Rename GrThreadSafeUniquelyKeyedProxyViewCache
Once triangulated paths are added this will no longer just be storing proxy views.
Bug: 1108408
Change-Id: I82fa47b0b85f738d9a25330c29bc2892c9bfeda4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323999
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Adlai Holler <adlai@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrThreadSafeCache.h b/src/gpu/GrThreadSafeCache.h
new file mode 100644
index 0000000..27a990e
--- /dev/null
+++ b/src/gpu/GrThreadSafeCache.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrThreadSafeCache_DEFINED
+#define GrThreadSafeCache_DEFINED
+
+#include "include/private/SkSpinlock.h"
+#include "src/core/SkArenaAlloc.h"
+#include "src/core/SkTDynamicHash.h"
+#include "src/core/SkTInternalLList.h"
+#include "src/gpu/GrSurfaceProxyView.h"
+
+// Ganesh creates a lot of utility textures (e.g., blurred-rrect masks) that need to be shared
+// between the direct context and all the DDL recording contexts. This thread-safe cache
+// allows this sharing.
+//
+// In operation, each thread will first check if the threaded cache possesses the required texture.
+//
+// If a DDL thread doesn't find a needed texture it will go off and create it on the cpu and then
+// attempt to add it to the cache. If another thread had added it in the interim, the losing thread
+// will discard its work and use the texture the winning thread had created.
+//
+// If the thread in possession of the direct context doesn't find the needed texture it should
+// add a place holder view and then queue up the draw calls to complete it. In this way the
+// gpu-thread has precedence over the recording threads.
+//
+// The invariants for this cache differ a bit from those of the proxy and resource caches.
+// For this cache:
+//
+// only this cache knows the unique key - neither the proxy nor backing resource should
+// be discoverable in any other cache by the unique key
+// if a backing resource resides in the resource cache then there should be an entry in this
+// cache
+// an entry in this cache, however, doesn't guarantee that there is a corresponding entry in
+// the resource cache - although the entry here should be able to generate that entry
+// (i.e., be a lazy proxy)
+//
+// Wrt interactions w/ GrContext/GrResourceCache purging, we have:
+//
+// Both GrContext::abandonContext and GrContext::releaseResourcesAndAbandonContext will cause
+// all the refs held in this cache to be dropped prior to clearing out the resource cache.
+//
+// For the size_t-variant of GrContext::purgeUnlockedResources, after an initial attempt
+// to purge the requested amount of resources fails, uniquely held resources in this cache
+// will be dropped in LRU to MRU order until the cache is under budget. Note that this
+// prioritizes the survival of resources in this cache over those just in the resource cache.
+//
+// For the 'scratchResourcesOnly' variant of GrContext::purgeUnlockedResources, this cache
+// won't be modified in the scratch-only case unless the resource cache is over budget (in
+// which case it will purge uniquely-held resources in LRU to MRU order to get
+// back under budget). In the non-scratch-only case, all uniquely held resources in this cache
+// will be released prior to the resource cache being cleared out.
+//
+// For GrContext::setResourceCacheLimit, if an initial pass through the resource cache doesn't
+// reach the budget, uniquely held resources in this cache will be released in LRU to MRU order.
+//
+// For GrContext::performDeferredCleanup, any uniquely held resources that haven't been accessed
+// w/in 'msNotUsed' will be released from this cache prior to the resource cache being cleaned.
+class GrThreadSafeCache {
+public:
+ GrThreadSafeCache();
+ ~GrThreadSafeCache();
+
+#if GR_TEST_UTILS
+ int numEntries() const SK_EXCLUDES(fSpinLock);
+
+ size_t approxBytesUsedForHash() const SK_EXCLUDES(fSpinLock);
+#endif
+
+ void dropAllRefs() SK_EXCLUDES(fSpinLock);
+
+ // Drop uniquely held refs until under the resource cache's budget.
+ // A null parameter means drop all uniquely held refs.
+ void dropUniqueRefs(GrResourceCache* resourceCache) SK_EXCLUDES(fSpinLock);
+
+ // Drop uniquely held refs that were last accessed before 'purgeTime'
+ void dropUniqueRefsOlderThan(GrStdSteadyClock::time_point purgeTime) SK_EXCLUDES(fSpinLock);
+
+ GrSurfaceProxyView find(const GrUniqueKey&) SK_EXCLUDES(fSpinLock);
+ std::tuple<GrSurfaceProxyView, sk_sp<SkData>> findWithData(
+ const GrUniqueKey&) SK_EXCLUDES(fSpinLock);
+
+ GrSurfaceProxyView add(const GrUniqueKey&, const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
+ std::tuple<GrSurfaceProxyView, sk_sp<SkData>> addWithData(
+ const GrUniqueKey&, const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
+
+ GrSurfaceProxyView findOrAdd(const GrUniqueKey&,
+ const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
+ std::tuple<GrSurfaceProxyView, sk_sp<SkData>> findOrAddWithData(
+ const GrUniqueKey&, const GrSurfaceProxyView&) SK_EXCLUDES(fSpinLock);
+
+ void remove(const GrUniqueKey&) SK_EXCLUDES(fSpinLock);
+
+ // To allow gpu-created resources to have priority, we pre-emptively place a lazy proxy
+ // in the thread-safe cache (with findOrAdd). The Trampoline object allows that lazy proxy to
+ // be instantiated with some later generated rendering result.
+ class Trampoline : public SkRefCnt {
+ public:
+ sk_sp<GrTextureProxy> fProxy;
+ };
+
+ static std::tuple<GrSurfaceProxyView, sk_sp<Trampoline>> CreateLazyView(GrDirectContext*,
+ GrColorType,
+ SkISize dimensions,
+ GrSurfaceOrigin,
+ SkBackingFit);
+private:
+ struct Entry {
+ Entry(const GrUniqueKey& key, const GrSurfaceProxyView& view) : fKey(key), fView(view) {}
+
+ // Note: the unique key is stored here bc it is never attached to a proxy or a GrTexture
+ GrUniqueKey fKey;
+ GrSurfaceProxyView fView;
+ GrStdSteadyClock::time_point fLastAccess;
+
+ SK_DECLARE_INTERNAL_LLIST_INTERFACE(Entry);
+
+ // for SkTDynamicHash
+ static const GrUniqueKey& GetKey(const Entry& e) { return e.fKey; }
+ static uint32_t Hash(const GrUniqueKey& key) { return key.hash(); }
+ };
+
+ Entry* getEntry(const GrUniqueKey&, const GrSurfaceProxyView&) SK_REQUIRES(fSpinLock);
+ void recycleEntry(Entry*) SK_REQUIRES(fSpinLock);
+
+ std::tuple<GrSurfaceProxyView, sk_sp<SkData>> internalFind(
+ const GrUniqueKey&) SK_REQUIRES(fSpinLock);
+ std::tuple<GrSurfaceProxyView, sk_sp<SkData>> internalAdd(
+ const GrUniqueKey&, const GrSurfaceProxyView&) SK_REQUIRES(fSpinLock);
+
+ mutable SkSpinlock fSpinLock;
+
+ SkTDynamicHash<Entry, GrUniqueKey> fUniquelyKeyedProxyViewMap SK_GUARDED_BY(fSpinLock);
+ // The head of this list is the MRU
+ SkTInternalLList<Entry> fUniquelyKeyedProxyViewList SK_GUARDED_BY(fSpinLock);
+
+ // TODO: empirically determine this from the skps
+ static const int kInitialArenaSize = 64 * sizeof(Entry);
+
+ char fStorage[kInitialArenaSize];
+ SkArenaAlloc fEntryAllocator{fStorage, kInitialArenaSize, kInitialArenaSize};
+ Entry* fFreeEntryList SK_GUARDED_BY(fSpinLock);
+};
+
+#endif // GrThreadSafeCache_DEFINED