• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/GlobalCache.h"
9 
10 #include "src/core/SkTraceEvent.h"
11 #include "src/gpu/graphite/ComputePipeline.h"
12 #include "src/gpu/graphite/ContextUtils.h"
13 #include "src/gpu/graphite/GraphicsPipeline.h"
14 #include "src/gpu/graphite/Resource.h"
15 
16 namespace {
17 
next_compilation_id()18 uint32_t next_compilation_id() {
19     static std::atomic<uint32_t> nextId{0};
20     // Not worried about overflow since we don't expect that many GraphicsPipelines.
21     // Even if it wraps around to 0, this is solely for debug logging.
22     return nextId.fetch_add(1, std::memory_order_relaxed);
23 }
24 
25 } // anonymous namespce
26 
27 namespace skgpu::graphite {
28 
GlobalCache()29 GlobalCache::GlobalCache()
30         : fGraphicsPipelineCache(256)  // TODO: find a good value for these limits
31         , fComputePipelineCache(256) {}
32 
~GlobalCache()33 GlobalCache::~GlobalCache() {
34     // These should have been cleared out earlier by deleteResources().
35     SkDEBUGCODE(SkAutoSpinlock lock{ fSpinLock });
36     SkASSERT(fGraphicsPipelineCache.count() == 0);
37     SkASSERT(fComputePipelineCache.count() == 0);
38     SkASSERT(fStaticResource.empty());
39 }
40 
deleteResources()41 void GlobalCache::deleteResources() {
42     SkAutoSpinlock lock{ fSpinLock };
43 
44     fGraphicsPipelineCache.reset();
45     fComputePipelineCache.reset();
46     fStaticResource.clear();
47 }
48 
LogPurge(const UniqueKey & key,sk_sp<GraphicsPipeline> * p)49 void GlobalCache::LogPurge(const UniqueKey& key, sk_sp<GraphicsPipeline>* p) {
50 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
51     // A "Bad" Purge is one where the Pipeline was never retrieved from the Cache (i.e., unused
52     // overgeneration).
53     static const char* kNames[2][2] = { { "BadPurgedN", "BadPurgedP" },
54                                         { "PurgedN",    "PurgedP"} };
55 
56     TRACE_EVENT_INSTANT2("skia.gpu",
57                          TRACE_STR_STATIC(kNames[(*p)->wasUsed()][(*p)->fromPrecompile()]),
58                          TRACE_EVENT_SCOPE_THREAD,
59                          "key", key.hash(),
60                          "compilationID", (*p)->getPipelineInfo().fCompilationID);
61 #endif
62 }
63 
findGraphicsPipeline(const UniqueKey & key,SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags,uint32_t * compilationID)64 sk_sp<GraphicsPipeline> GlobalCache::findGraphicsPipeline(
65         const UniqueKey& key,
66         SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags,
67         uint32_t *compilationID) {
68 
69     [[maybe_unused]] bool forPrecompile =
70             SkToBool(pipelineCreationFlags & PipelineCreationFlags::kForPrecompilation);
71 
72     SkAutoSpinlock lock{fSpinLock};
73 
74     sk_sp<GraphicsPipeline>* entry = fGraphicsPipelineCache.find(key);
75     if (entry) {
76 #if defined(GPU_TEST_UTILS)
77         ++fStats.fGraphicsCacheHits;
78 #endif
79 
80         (*entry)->markUsed();
81 
82 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
83         static const char* kNames[2] = { "CacheHitForN", "CacheHitForP" };
84         TRACE_EVENT_INSTANT2("skia.gpu",
85                              TRACE_STR_STATIC(kNames[forPrecompile]),
86                              TRACE_EVENT_SCOPE_THREAD,
87                              "key", key.hash(),
88                              "compilationID", (*entry)->getPipelineInfo().fCompilationID);
89 #endif
90 
91         return *entry;
92     } else {
93 #if defined(GPU_TEST_UTILS)
94         ++fStats.fGraphicsCacheMisses;
95 #endif
96 
97         if (compilationID) {
98             // This is a cache miss so we know the next step is going to be a Pipeline
99             // creation. Create the compilationID here so we can use it in the "CacheMissFor"
100             // trace event.
101             *compilationID = next_compilation_id();
102 
103 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
104             static const char* kNames[2] = { "CacheMissForN", "CacheMissForP" };
105             TRACE_EVENT_INSTANT2("skia.gpu",
106                                  TRACE_STR_STATIC(kNames[forPrecompile]),
107                                  TRACE_EVENT_SCOPE_THREAD,
108                                  "key", key.hash(),
109                                  "compilationID", *compilationID);
110 #endif
111         }
112 
113         return nullptr;
114     }
115 }
116 
117 #if SK_HISTOGRAMS_ENABLED
118 // These values are persisted to logs. Entries should not be renumbered and
119 // numeric values should never be reused.
120 //
121 // LINT.IfChange(PipelineCreationRace)
122 enum class PipelineCreationRace {
123     // The <First>Over<Second> enum names mean the first type of compilation won a compilation race
124     // over the second type of compilation and ended up in the cache.
125     kNormalOverNormal                 = 0, // can happen w/ multiple Recorders on different threads
126     kNormalOverPrecompilation         = 1,
127     kPrecompilationOverNormal         = 2,
128     kPrecompilationOverPrecompilation = 3, // can happen with multiple threaded precompilation calls
129 
130     kMaxValue = kPrecompilationOverPrecompilation,
131 };
132 // LINT.ThenChange(//tools/metrics/histograms/enums.xml:SkiaPipelineCreationRace)
133 
134 [[maybe_unused]] static constexpr int kPipelineCreationRaceCount =
135         static_cast<int>(PipelineCreationRace::kMaxValue) + 1;
136 #endif // SK_HISTOGRAMS_ENABLED
137 
addGraphicsPipeline(const UniqueKey & key,sk_sp<GraphicsPipeline> pipeline)138 sk_sp<GraphicsPipeline> GlobalCache::addGraphicsPipeline(const UniqueKey& key,
139                                                          sk_sp<GraphicsPipeline> pipeline) {
140     SkAutoSpinlock lock{fSpinLock};
141 
142     sk_sp<GraphicsPipeline>* entry = fGraphicsPipelineCache.find(key);
143     if (!entry) {
144         // No equivalent pipeline was stored in the cache between a previous call to
145         // findGraphicsPipeline() that returned null (triggering the pipeline creation) and this
146         // later adding to the cache.
147         entry = fGraphicsPipelineCache.insert(key, std::move(pipeline));
148 
149 #if defined(GPU_TEST_UTILS)
150         ++fStats.fGraphicsCacheAdditions;
151 #endif
152 
153         // Precompile Pipelines are only marked as used when they get a cache hit in
154         // findGraphicsPipeline
155         if (!(*entry)->fromPrecompile()) {
156             (*entry)->markUsed();
157         }
158 
159 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
160         static const char* kNames[2] = { "AddedN", "AddedP" };
161         TRACE_EVENT_INSTANT2("skia.gpu",
162                              TRACE_STR_STATIC(kNames[(*entry)->fromPrecompile()]),
163                              TRACE_EVENT_SCOPE_THREAD,
164                              "key", key.hash(),
165                              "compilationID", (*entry)->getPipelineInfo().fCompilationID);
166 #endif
167     } else {
168 #if defined(GPU_TEST_UTILS)
169         // else there was a race creating the same pipeline and this thread lost, so return
170         // the winner
171         ++fStats.fGraphicsRaces;
172 #endif
173 
174         [[maybe_unused]] int race = (*entry)->fromPrecompile() * 2 + pipeline->fromPrecompile();
175 
176 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
177         static const char* kNames[4] = {
178                 "NWonRaceOverN",
179                 "NWonRaceOverP",
180                 "PWonRaceOverN",
181                 "PWonRaceOverP"
182         };
183         TRACE_EVENT_INSTANT2("skia.gpu",
184                              TRACE_STR_STATIC(kNames[race]),
185                              TRACE_EVENT_SCOPE_THREAD,
186                              "key", key.hash(),
187                              // The losing compilation
188                              "compilationID", pipeline->getPipelineInfo().fCompilationID);
189 #endif
190 
191 #if SK_HISTOGRAMS_ENABLED
192         SK_HISTOGRAM_ENUMERATION("Graphite.PipelineCreationRace",
193                                  race,
194                                  kPipelineCreationRaceCount);
195 #endif
196     }
197     return *entry;
198 }
199 
200 #if defined(GPU_TEST_UTILS)
numGraphicsPipelines() const201 int GlobalCache::numGraphicsPipelines() const {
202     SkAutoSpinlock lock{fSpinLock};
203 
204     return fGraphicsPipelineCache.count();
205 }
206 
resetGraphicsPipelines()207 void GlobalCache::resetGraphicsPipelines() {
208     SkAutoSpinlock lock{fSpinLock};
209 
210     fGraphicsPipelineCache.reset();
211 }
212 
forEachGraphicsPipeline(const std::function<void (const UniqueKey &,const GraphicsPipeline *)> & fn)213 void GlobalCache::forEachGraphicsPipeline(
214         const std::function<void(const UniqueKey&, const GraphicsPipeline*)>& fn) {
215     SkAutoSpinlock lock{fSpinLock};
216 
217     fGraphicsPipelineCache.foreach([&](const UniqueKey* k, const sk_sp<GraphicsPipeline>* v) {
218         fn(*k, v->get());
219     });
220 }
221 
getStats() const222 GlobalCache::PipelineStats GlobalCache::getStats() const {
223     SkAutoSpinlock lock{fSpinLock};
224 
225     return fStats;
226 }
227 #endif // defined(GPU_TEST_UTILS)
228 
findComputePipeline(const UniqueKey & key)229 sk_sp<ComputePipeline> GlobalCache::findComputePipeline(const UniqueKey& key) {
230     SkAutoSpinlock lock{fSpinLock};
231     sk_sp<ComputePipeline>* entry = fComputePipelineCache.find(key);
232     return entry ? *entry : nullptr;
233 }
234 
addComputePipeline(const UniqueKey & key,sk_sp<ComputePipeline> pipeline)235 sk_sp<ComputePipeline> GlobalCache::addComputePipeline(const UniqueKey& key,
236                                                        sk_sp<ComputePipeline> pipeline) {
237     SkAutoSpinlock lock{fSpinLock};
238     sk_sp<ComputePipeline>* entry = fComputePipelineCache.find(key);
239     if (!entry) {
240         entry = fComputePipelineCache.insert(key, std::move(pipeline));
241     }
242     return *entry;
243 }
244 
addStaticResource(sk_sp<Resource> resource)245 void GlobalCache::addStaticResource(sk_sp<Resource> resource) {
246     SkAutoSpinlock lock{fSpinLock};
247     fStaticResource.push_back(std::move(resource));
248 }
249 
250 } // namespace skgpu::graphite
251