Flutter Engine
The Flutter Engine
GrThreadSafeCache.h
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google Inc.
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#ifndef GrThreadSafeCache_DEFINED
9#define GrThreadSafeCache_DEFINED
10
13#include "src/base/SkSpinlock.h"
19
20// Ganesh creates a lot of utility textures (e.g., blurred-rrect masks) that need to be shared
21// between the direct context and all the DDL recording contexts. This thread-safe cache
22// allows this sharing.
23//
24// In operation, each thread will first check if the threaded cache possesses the required texture.
25//
26// If a DDL thread doesn't find a needed texture it will go off and create it on the cpu and then
27// attempt to add it to the cache. If another thread had added it in the interim, the losing thread
28// will discard its work and use the texture the winning thread had created.
29//
30// If the thread in possession of the direct context doesn't find the needed texture it should
31// add a place holder view and then queue up the draw calls to complete it. In this way the
32// gpu-thread has precedence over the recording threads.
33//
34// The invariants for this cache differ a bit from those of the proxy and resource caches.
35// For this cache:
36//
37// only this cache knows the unique key - neither the proxy nor backing resource should
38// be discoverable in any other cache by the unique key
39// if a backing resource resides in the resource cache then there should be an entry in this
40// cache
41// an entry in this cache, however, doesn't guarantee that there is a corresponding entry in
42// the resource cache - although the entry here should be able to generate that entry
43// (i.e., be a lazy proxy)
44//
45// Wrt interactions w/ GrContext/GrResourceCache purging, we have:
46//
47// Both GrContext::abandonContext and GrContext::releaseResourcesAndAbandonContext will cause
48// all the refs held in this cache to be dropped prior to clearing out the resource cache.
49//
50// For the size_t-variant of GrContext::purgeUnlockedResources, after an initial attempt
51// to purge the requested amount of resources fails, uniquely held resources in this cache
52// will be dropped in LRU to MRU order until the cache is under budget. Note that this
53// prioritizes the survival of resources in this cache over those just in the resource cache.
54//
55// For the 'scratchResourcesOnly' variant of GrContext::purgeUnlockedResources, this cache
56// won't be modified in the scratch-only case unless the resource cache is over budget (in
57// which case it will purge uniquely-held resources in LRU to MRU order to get
58// back under budget). In the non-scratch-only case, all uniquely held resources in this cache
59// will be released prior to the resource cache being cleared out.
60//
61// For GrContext::setResourceCacheLimit, if an initial pass through the resource cache doesn't
62// reach the budget, uniquely held resources in this cache will be released in LRU to MRU order.
63//
64// For GrContext::performDeferredCleanup, any uniquely held resources that haven't been accessed
65// w/in 'msNotUsed' will be released from this cache prior to the resource cache being cleaned.
67public:
70
71#if defined(GR_TEST_UTILS)
72 int numEntries() const SK_EXCLUDES(fSpinLock);
73
74 size_t approxBytesUsedForHash() const SK_EXCLUDES(fSpinLock);
75#endif
76
77 void dropAllRefs() SK_EXCLUDES(fSpinLock);
78
79 // Drop uniquely held refs until under the resource cache's budget.
80 // A null parameter means drop all uniquely held refs.
81 void dropUniqueRefs(GrResourceCache* resourceCache) SK_EXCLUDES(fSpinLock);
82
83 // Drop uniquely held refs that were last accessed before 'purgeTime'
85 skgpu::StdSteadyClock::time_point purgeTime) SK_EXCLUDES(fSpinLock);
86
87 SkDEBUGCODE(bool has(const skgpu::UniqueKey&) SK_EXCLUDES(fSpinLock);)
88
90 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> findWithData(
91 const skgpu::UniqueKey&) SK_EXCLUDES(fSpinLock);
92
94 const skgpu::UniqueKey&, const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
95 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> addWithData(
96 const skgpu::UniqueKey&, const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
97
99 const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
100 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> findOrAddWithData(
101 const skgpu::UniqueKey&, const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
102
103 // To hold vertex data in the cache and have it transparently transition from cpu-side to
104 // gpu-side while being shared between all the threads we need a ref counted object that
105 // keeps hold of the cpu-side data but allows deferred filling in of the mirroring gpu buffer.
106 class VertexData : public SkNVRefCnt<VertexData> {
107 public:
108 ~VertexData();
109
110 const void* vertices() const { return fVertices; }
111 size_t size() const { return fNumVertices * fVertexSize; }
112
113 int numVertices() const { return fNumVertices; }
114 size_t vertexSize() const { return fVertexSize; }
115
116 // TODO: make these return const GrGpuBuffers?
117 GrGpuBuffer* gpuBuffer() { return fGpuBuffer.get(); }
118 sk_sp<GrGpuBuffer> refGpuBuffer() { return fGpuBuffer; }
119
121 // TODO: once we add the gpuBuffer we could free 'fVertices'. Deinstantiable
122 // DDLs could throw a monkey wrench into that plan though.
123 SkASSERT(!fGpuBuffer);
124 fGpuBuffer = std::move(gpuBuffer);
125 }
126
127 void reset() {
128 sk_free(const_cast<void*>(fVertices));
129 fVertices = nullptr;
130 fNumVertices = 0;
131 fVertexSize = 0;
132 fGpuBuffer.reset();
133 }
134
135 private:
136 friend class GrThreadSafeCache; // for access to ctor
137
138 VertexData(const void* vertices, int numVertices, size_t vertexSize)
139 : fVertices(vertices)
140 , fNumVertices(numVertices)
141 , fVertexSize(vertexSize) {
142 }
143
145 : fVertices(nullptr)
146 , fNumVertices(numVertices)
147 , fVertexSize(vertexSize)
148 , fGpuBuffer(std::move(gpuBuffer)) {
149 }
150
151 const void* fVertices;
152 int fNumVertices;
153 size_t fVertexSize;
154
155 sk_sp<GrGpuBuffer> fGpuBuffer;
156 };
157
158 // The returned VertexData object takes ownership of 'vertices' which had better have been
159 // allocated with malloc!
160 static sk_sp<VertexData> MakeVertexData(const void* vertices,
161 int vertexCount,
162 size_t vertexSize);
164 int vertexCount,
165 size_t vertexSize);
166
167 std::tuple<sk_sp<VertexData>, sk_sp<SkData>> findVertsWithData(
168 const skgpu::UniqueKey&) SK_EXCLUDES(fSpinLock);
169
170 typedef bool (*IsNewerBetter)(SkData* incumbent, SkData* challenger);
171
172 std::tuple<sk_sp<VertexData>, sk_sp<SkData>> addVertsWithData(
173 const skgpu::UniqueKey&,
175 IsNewerBetter) SK_EXCLUDES(fSpinLock);
176
177 void remove(const skgpu::UniqueKey&) SK_EXCLUDES(fSpinLock);
178
179 // To allow gpu-created resources to have priority, we pre-emptively place a lazy proxy
180 // in the thread-safe cache (with findOrAdd). The Trampoline object allows that lazy proxy to
181 // be instantiated with some later generated rendering result.
182 class Trampoline : public SkRefCnt {
183 public:
185 };
186
187 static std::tuple<GrSurfaceProxyView, sk_sp<Trampoline>> CreateLazyView(GrDirectContext*,
189 SkISize dimensions,
192private:
193 struct Entry {
194 Entry(const skgpu::UniqueKey& key, const GrSurfaceProxyView& view)
195 : fKey(key)
196 , fView(view)
197 , fTag(Entry::kView) {
198 }
199
201 : fKey(key)
202 , fVertData(std::move(vertData))
203 , fTag(Entry::kVertData) {
204 }
205
206 ~Entry() {
207 this->makeEmpty();
208 }
209
210 bool uniquelyHeld() const {
211 SkASSERT(fTag != kEmpty);
212
213 if (fTag == kView && fView.proxy()->unique()) {
214 return true;
215 } else if (fTag == kVertData && fVertData->unique()) {
216 return true;
217 }
218
219 return false;
220 }
221
222 const skgpu::UniqueKey& key() const {
223 SkASSERT(fTag != kEmpty);
224 return fKey;
225 }
226
227 SkData* getCustomData() const {
228 SkASSERT(fTag != kEmpty);
229 return fKey.getCustomData();
230 }
231
232 sk_sp<SkData> refCustomData() const {
233 SkASSERT(fTag != kEmpty);
234 return fKey.refCustomData();
235 }
236
237 GrSurfaceProxyView view() {
238 SkASSERT(fTag == kView);
239 return fView;
240 }
241
242 sk_sp<VertexData> vertexData() {
243 SkASSERT(fTag == kVertData);
244 return fVertData;
245 }
246
247 void set(const skgpu::UniqueKey& key, const GrSurfaceProxyView& view) {
248 SkASSERT(fTag == kEmpty);
249 fKey = key;
250 fView = view;
251 fTag = kView;
252 }
253
254 void makeEmpty() {
255 fKey.reset();
256 if (fTag == kView) {
257 fView.reset();
258 } else if (fTag == kVertData) {
259 fVertData.reset();
260 }
261 fTag = kEmpty;
262 }
263
264 void set(const skgpu::UniqueKey& key, sk_sp<VertexData> vertData) {
265 SkASSERT(fTag == kEmpty || fTag == kVertData);
266 fKey = key;
267 fVertData = std::move(vertData);
268 fTag = kVertData;
269 }
270
271 // The thread-safe cache gets to directly manipulate the llist and last-access members
272 skgpu::StdSteadyClock::time_point fLastAccess;
274
275 // for SkTDynamicHash
276 static const skgpu::UniqueKey& GetKey(const Entry& e) {
277 SkASSERT(e.fTag != kEmpty);
278 return e.fKey;
279 }
280 static uint32_t Hash(const skgpu::UniqueKey& key) { return key.hash(); }
281
282 private:
283 // Note: the unique key is stored here bc it is never attached to a proxy or a GrTexture
284 skgpu::UniqueKey fKey;
285 union {
286 GrSurfaceProxyView fView;
287 sk_sp<VertexData> fVertData;
288 };
289
290 enum {
291 kEmpty,
292 kView,
293 kVertData,
294 } fTag { kEmpty };
295 };
296
297 void makeExistingEntryMRU(Entry*) SK_REQUIRES(fSpinLock);
298 Entry* makeNewEntryMRU(Entry*) SK_REQUIRES(fSpinLock);
299
300 Entry* getEntry(const skgpu::UniqueKey&, const GrSurfaceProxyView&) SK_REQUIRES(fSpinLock);
301 Entry* getEntry(const skgpu::UniqueKey&, sk_sp<VertexData>) SK_REQUIRES(fSpinLock);
302
303 void recycleEntry(Entry*) SK_REQUIRES(fSpinLock);
304
305 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> internalFind(
306 const skgpu::UniqueKey&) SK_REQUIRES(fSpinLock);
307 std::tuple<GrSurfaceProxyView, sk_sp<SkData>> internalAdd(
308 const skgpu::UniqueKey&, const GrSurfaceProxyView&) SK_REQUIRES(fSpinLock);
309
310 std::tuple<sk_sp<VertexData>, sk_sp<SkData>> internalFindVerts(
311 const skgpu::UniqueKey&) SK_REQUIRES(fSpinLock);
312 std::tuple<sk_sp<VertexData>, sk_sp<SkData>> internalAddVerts(
314
315 mutable SkSpinlock fSpinLock;
316
317 SkTDynamicHash<Entry, skgpu::UniqueKey> fUniquelyKeyedEntryMap SK_GUARDED_BY(fSpinLock);
318 // The head of this list is the MRU
319 SkTInternalLList<Entry> fUniquelyKeyedEntryList SK_GUARDED_BY(fSpinLock);
320
321 // TODO: empirically determine this from the skps
322 static const int kInitialArenaSize = 64 * sizeof(Entry);
323
324 char fStorage[kInitialArenaSize];
325 SkArenaAlloc fEntryAllocator{fStorage, kInitialArenaSize, kInitialArenaSize};
326 Entry* fFreeEntryList SK_GUARDED_BY(fSpinLock);
327};
328
329#endif // GrThreadSafeCache_DEFINED
GrColorType
Definition: GrTypesPriv.h:540
GrSurfaceOrigin
Definition: GrTypes.h:147
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBackingFit
Definition: SkBackingFit.h:16
SK_API void sk_free(void *)
#define SK_DECLARE_INTERNAL_LLIST_INTERFACE(ClassName)
#define SK_EXCLUDES(...)
#define SK_REQUIRES(...)
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
int find(T *array, int N, T item)
GrSurfaceProxy * proxy() const
sk_sp< GrTextureProxy > fProxy
void setGpuBuffer(sk_sp< GrGpuBuffer > gpuBuffer)
sk_sp< GrGpuBuffer > refGpuBuffer()
bool(* IsNewerBetter)(SkData *incumbent, SkData *challenger)
static std::tuple< GrSurfaceProxyView, sk_sp< Trampoline > > CreateLazyView(GrDirectContext *, GrColorType, SkISize dimensions, GrSurfaceOrigin, SkBackingFit)
std::tuple< sk_sp< VertexData >, sk_sp< SkData > > addVertsWithData(const skgpu::UniqueKey &, sk_sp< VertexData >, IsNewerBetter) SK_EXCLUDES(fSpinLock)
SkDEBUGCODE(bool has(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock);) GrSurfaceProxyView find(const skgpu std::tuple< GrSurfaceProxyView, sk_sp< SkData > > findWithData(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock)
std::tuple< sk_sp< VertexData >, sk_sp< SkData > > findVertsWithData(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock)
GrSurfaceProxyView add(const skgpu::UniqueKey &, const GrSurfaceProxyView &) SK_EXCLUDES(fSpinLock)
void dropUniqueRefsOlderThan(skgpu::StdSteadyClock::time_point purgeTime) SK_EXCLUDES(fSpinLock)
void remove(const skgpu::UniqueKey &) SK_EXCLUDES(fSpinLock)
std::tuple< GrSurfaceProxyView, sk_sp< SkData > > findOrAddWithData(const skgpu::UniqueKey &, const GrSurfaceProxyView &) SK_EXCLUDES(fSpinLock)
std::tuple< GrSurfaceProxyView, sk_sp< SkData > > addWithData(const skgpu::UniqueKey &, const GrSurfaceProxyView &) SK_EXCLUDES(fSpinLock)
GrSurfaceProxyView findOrAdd(const skgpu::UniqueKey &, const GrSurfaceProxyView &) SK_EXCLUDES(fSpinLock)
void dropUniqueRefs(GrResourceCache *resourceCache) SK_EXCLUDES(fSpinLock)
static sk_sp< VertexData > MakeVertexData(const void *vertices, int vertexCount, size_t vertexSize)
void dropAllRefs() SK_EXCLUDES(fSpinLock)
Definition: SkData.h:25
bool unique() const
Definition: SkRefCnt.h:175
T * get() const
Definition: SkRefCnt.h:303
void reset(T *ptr=nullptr)
Definition: SkRefCnt.h:310
static uint32_t Hash(uint32_t key)
Definition: hashmap_test.cc:65
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition: switches.h:76
Definition: ref_ptr.h:256
Definition: SkSize.h:16