123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #ifndef GrCCPathCache_DEFINED
- #define GrCCPathCache_DEFINED
- #include "include/private/SkTHash.h"
- #include "src/core/SkExchange.h"
- #include "src/core/SkTInternalLList.h"
- #include "src/gpu/ccpr/GrCCAtlas.h"
- #include "src/gpu/ccpr/GrCCPathProcessor.h"
- #include "src/gpu/geometry/GrShape.h"
- class GrCCPathCacheEntry;
- class GrShape;
- /**
- * This class implements an LRU cache that maps from GrShape to GrCCPathCacheEntry objects. Shapes
- * are only given one entry in the cache, so any time they are accessed with a different matrix, the
- * old entry gets evicted.
- */
- class GrCCPathCache {
- public:
- GrCCPathCache(uint32_t contextUniqueID);
- ~GrCCPathCache();
- class Key : public SkPathRef::GenIDChangeListener {
- public:
- static sk_sp<Key> Make(uint32_t pathCacheUniqueID, int dataCountU32,
- const void* data = nullptr);
- uint32_t pathCacheUniqueID() const { return fPathCacheUniqueID; }
- int dataSizeInBytes() const { return fDataSizeInBytes; }
- const uint32_t* data() const;
- void resetDataCountU32(int dataCountU32) {
- SkASSERT(dataCountU32 <= fDataReserveCountU32);
- fDataSizeInBytes = dataCountU32 * sizeof(uint32_t);
- }
- uint32_t* data();
- bool operator==(const Key& that) const {
- return fDataSizeInBytes == that.fDataSizeInBytes &&
- !memcmp(this->data(), that.data(), fDataSizeInBytes);
- }
- // Called when our corresponding path is modified or deleted. Not threadsafe.
- void onChange() override;
- private:
- Key(uint32_t pathCacheUniqueID, int dataCountU32)
- : fPathCacheUniqueID(pathCacheUniqueID)
- , fDataSizeInBytes(dataCountU32 * sizeof(uint32_t))
- SkDEBUGCODE(, fDataReserveCountU32(dataCountU32)) {
- SkASSERT(SK_InvalidUniqueID != fPathCacheUniqueID);
- }
- const uint32_t fPathCacheUniqueID;
- int fDataSizeInBytes;
- SkDEBUGCODE(const int fDataReserveCountU32);
- // The GrShape's unstyled key is stored as a variable-length footer to this class. GetKey
- // provides access to it.
- };
- // Stores the components of a transformation that affect a path mask (i.e. everything but
- // integer translation). During construction, any integer portions of the matrix's translate are
- // shaved off and returned to the caller. The caller is responsible for those integer shifts.
- struct MaskTransform {
- MaskTransform(const SkMatrix& m, SkIVector* shift);
- float fMatrix2x2[4];
- #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
- // Except on AOSP, cache hits must have matching subpixel portions of their view matrix.
- // On AOSP we follow after HWUI and ignore the subpixel translate.
- float fSubpixelTranslate[2];
- #endif
- };
- // Represents a ref on a GrCCPathCacheEntry that should only be used during the current flush.
- class OnFlushEntryRef : SkNoncopyable {
- public:
- static OnFlushEntryRef OnFlushRef(GrCCPathCacheEntry*);
- OnFlushEntryRef() = default;
- OnFlushEntryRef(OnFlushEntryRef&& ref) : fEntry(skstd::exchange(ref.fEntry, nullptr)) {}
- ~OnFlushEntryRef();
- GrCCPathCacheEntry* get() const { return fEntry; }
- GrCCPathCacheEntry* operator->() const { return fEntry; }
- GrCCPathCacheEntry& operator*() const { return *fEntry; }
- explicit operator bool() const { return fEntry; }
- void operator=(OnFlushEntryRef&& ref) { fEntry = skstd::exchange(ref.fEntry, nullptr); }
- private:
- OnFlushEntryRef(GrCCPathCacheEntry* entry) : fEntry(entry) {}
- GrCCPathCacheEntry* fEntry = nullptr;
- };
- // Finds an entry in the cache that matches the given shape and transformation matrix.
- // 'maskShift' is filled with an integer post-translate that the caller must apply when drawing
- // the entry's mask to the device.
- //
- // NOTE: Shapes are only given one entry, so any time they are accessed with a new
- // transformation, the old entry gets evicted.
- OnFlushEntryRef find(GrOnFlushResourceProvider*, const GrShape&,
- const SkIRect& clippedDrawBounds, const SkMatrix& viewMatrix,
- SkIVector* maskShift);
- void doPreFlushProcessing();
- void purgeEntriesOlderThan(GrProxyProvider*, const GrStdSteadyClock::time_point& purgeTime);
- // As we evict entries from our local path cache, we accumulate a list of invalidated atlas
- // textures. This call purges the invalidated atlas textures from the mainline GrResourceCache.
- // This call is available with two different "provider" objects, to accomodate whatever might
- // be available at the callsite.
- void purgeInvalidatedAtlasTextures(GrOnFlushResourceProvider*);
- void purgeInvalidatedAtlasTextures(GrProxyProvider*);
- private:
- // This is a special ref ptr for GrCCPathCacheEntry, used by the hash table. It provides static
- // methods for SkTHash, and can only be moved. This guarantees the hash table holds exactly one
- // reference for each entry. Also, when a HashNode goes out of scope, that means it is exiting
- // the hash table. We take that opportunity to remove it from the LRU list and do some cleanup.
- class HashNode : SkNoncopyable {
- public:
- static const Key& GetKey(const HashNode&);
- inline static uint32_t Hash(const Key& key) {
- return GrResourceKeyHash(key.data(), key.dataSizeInBytes());
- }
- HashNode() = default;
- HashNode(GrCCPathCache*, sk_sp<Key>, const MaskTransform&, const GrShape&);
- HashNode(HashNode&& node)
- : fPathCache(node.fPathCache), fEntry(std::move(node.fEntry)) {
- SkASSERT(!node.fEntry);
- }
- ~HashNode();
- void operator=(HashNode&& node);
- GrCCPathCacheEntry* entry() const { return fEntry.get(); }
- private:
- GrCCPathCache* fPathCache = nullptr;
- sk_sp<GrCCPathCacheEntry> fEntry;
- };
- GrStdSteadyClock::time_point quickPerFlushTimestamp() {
- // time_point::min() means it's time to update fPerFlushTimestamp with a newer clock read.
- if (GrStdSteadyClock::time_point::min() == fPerFlushTimestamp) {
- fPerFlushTimestamp = GrStdSteadyClock::now();
- }
- return fPerFlushTimestamp;
- }
- void evict(const GrCCPathCache::Key&, GrCCPathCacheEntry* = nullptr);
- // Evicts all the cache entries whose keys have been queued up in fInvalidatedKeysInbox via
- // SkPath listeners.
- void evictInvalidatedCacheKeys();
- const uint32_t fContextUniqueID;
- SkTHashTable<HashNode, const Key&> fHashTable;
- SkTInternalLList<GrCCPathCacheEntry> fLRU;
- SkMessageBus<sk_sp<Key>>::Inbox fInvalidatedKeysInbox;
- sk_sp<Key> fScratchKey; // Reused for creating a temporary key in the find() method.
- // We only read the clock once per flush, and cache it in this variable. This prevents us from
- // excessive clock reads for cache timestamps that might degrade performance.
- GrStdSteadyClock::time_point fPerFlushTimestamp = GrStdSteadyClock::time_point::min();
- // As we evict entries from our local path cache, we accumulate lists of invalidated atlas
- // textures in these two members. We hold these until we purge them from the GrResourceCache
- // (e.g. via purgeInvalidatedAtlasTextures().)
- SkSTArray<4, sk_sp<GrTextureProxy>> fInvalidatedProxies;
- SkSTArray<4, GrUniqueKey> fInvalidatedProxyUniqueKeys;
- friend class GrCCCachedAtlas; // To append to fInvalidatedProxies, fInvalidatedProxyUniqueKeys.
- public:
- const SkTHashTable<HashNode, const Key&>& testingOnly_getHashTable() const;
- const SkTInternalLList<GrCCPathCacheEntry>& testingOnly_getLRU() const;
- };
- /**
- * This class stores all the data necessary to draw a specific path + matrix combination from their
- * corresponding cached atlas.
- */
- class GrCCPathCacheEntry : public GrNonAtomicRef<GrCCPathCacheEntry> {
- public:
- SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrCCPathCacheEntry);
- ~GrCCPathCacheEntry() {
- SkASSERT(this->hasBeenEvicted()); // Should have called GrCCPathCache::evict().
- SkASSERT(!fCachedAtlas);
- SkASSERT(0 == fOnFlushRefCnt);
- }
- const GrCCPathCache::Key& cacheKey() const { SkASSERT(fCacheKey); return *fCacheKey; }
- // The number of flushes during which this specific entry (path + matrix combination) has been
- // pulled from the path cache. If a path is pulled from the cache more than once in a single
- // flush, the hit count is only incremented once.
- //
- // If the entry did not previously exist, its hit count will be 1.
- int hitCount() const { return fHitCount; }
- // The accumulative region of the path that has been drawn during the lifetime of this cache
- // entry (as defined by the 'clippedDrawBounds' parameter for GrCCPathCache::find).
- const SkIRect& hitRect() const { return fHitRect; }
- const GrCCCachedAtlas* cachedAtlas() const { return fCachedAtlas.get(); }
- const SkIRect& devIBounds() const { return fDevIBounds; }
- int width() const { return fDevIBounds.width(); }
- int height() const { return fDevIBounds.height(); }
- enum class ReleaseAtlasResult : bool {
- kNone,
- kDidInvalidateFromCache
- };
- // Called once our path has been rendered into the mainline CCPR (fp16, coverage count) atlas.
- // The caller will stash this atlas texture away after drawing, and during the next flush,
- // recover it and attempt to copy any paths that got reused into permanent 8-bit atlases.
- void setCoverageCountAtlas(
- GrOnFlushResourceProvider*, GrCCAtlas*, const SkIVector& atlasOffset,
- const GrOctoBounds& octoBounds, const SkIRect& devIBounds, const SkIVector& maskShift);
- // Called once our path mask has been copied into a permanent, 8-bit atlas. This method points
- // the entry at the new atlas and updates the GrCCCCachedAtlas data.
- ReleaseAtlasResult upgradeToLiteralCoverageAtlas(GrCCPathCache*, GrOnFlushResourceProvider*,
- GrCCAtlas*, const SkIVector& newAtlasOffset);
- private:
- using MaskTransform = GrCCPathCache::MaskTransform;
- GrCCPathCacheEntry(sk_sp<GrCCPathCache::Key> cacheKey, const MaskTransform& maskTransform)
- : fCacheKey(std::move(cacheKey)), fMaskTransform(maskTransform) {
- }
- bool hasBeenEvicted() const { return fCacheKey->shouldUnregisterFromPath(); }
- // Resets this entry back to not having an atlas, and purges its previous atlas texture from the
- // resource cache if needed.
- ReleaseAtlasResult releaseCachedAtlas(GrCCPathCache*);
- sk_sp<GrCCPathCache::Key> fCacheKey;
- GrStdSteadyClock::time_point fTimestamp;
- int fHitCount = 0;
- SkIRect fHitRect = SkIRect::MakeEmpty();
- sk_sp<GrCCCachedAtlas> fCachedAtlas;
- SkIVector fAtlasOffset;
- MaskTransform fMaskTransform;
- GrOctoBounds fOctoBounds;
- SkIRect fDevIBounds;
- int fOnFlushRefCnt = 0;
- friend class GrCCPathCache;
- friend void GrCCPathProcessor::Instance::set(const GrCCPathCacheEntry&, const SkIVector&,
- uint64_t color, GrFillRule); // To access data.
- public:
- int testingOnly_peekOnFlushRefCnt() const;
- };
- /**
- * Encapsulates the data for an atlas whose texture is stored in the mainline GrResourceCache. Many
- * instances of GrCCPathCacheEntry will reference the same GrCCCachedAtlas.
- *
- * We use this object to track the percentage of the original atlas pixels that could still ever
- * potentially be reused (i.e., those which still represent an extant path). When the percentage
- * of useful pixels drops below 50%, we purge the entire texture from the resource cache.
- *
- * This object also holds a ref on the atlas's actual texture proxy during flush. When
- * fOnFlushRefCnt decrements back down to zero, we release fOnFlushProxy and reset it back to null.
- */
- class GrCCCachedAtlas : public GrNonAtomicRef<GrCCCachedAtlas> {
- public:
- using ReleaseAtlasResult = GrCCPathCacheEntry::ReleaseAtlasResult;
- GrCCCachedAtlas(GrCCAtlas::CoverageType type, const GrUniqueKey& textureKey,
- sk_sp<GrTextureProxy> onFlushProxy)
- : fCoverageType(type)
- , fTextureKey(textureKey)
- , fOnFlushProxy(std::move(onFlushProxy)) {}
- ~GrCCCachedAtlas() {
- SkASSERT(!fOnFlushProxy);
- SkASSERT(!fOnFlushRefCnt);
- }
- GrCCAtlas::CoverageType coverageType() const { return fCoverageType; }
- const GrUniqueKey& textureKey() const { return fTextureKey; }
- GrTextureProxy* getOnFlushProxy() const { return fOnFlushProxy.get(); }
- void setOnFlushProxy(sk_sp<GrTextureProxy> proxy) {
- SkASSERT(!fOnFlushProxy);
- fOnFlushProxy = std::move(proxy);
- }
- void addPathPixels(int numPixels) { fNumPathPixels += numPixels; }
- ReleaseAtlasResult invalidatePathPixels(GrCCPathCache*, int numPixels);
- int peekOnFlushRefCnt() const { return fOnFlushRefCnt; }
- void incrOnFlushRefCnt(int count = 1) const {
- SkASSERT(count > 0);
- SkASSERT(fOnFlushProxy);
- fOnFlushRefCnt += count;
- }
- void decrOnFlushRefCnt(int count = 1) const;
- private:
- const GrCCAtlas::CoverageType fCoverageType;
- const GrUniqueKey fTextureKey;
- int fNumPathPixels = 0;
- int fNumInvalidatedPathPixels = 0;
- bool fIsInvalidatedFromResourceCache = false;
- mutable sk_sp<GrTextureProxy> fOnFlushProxy;
- mutable int fOnFlushRefCnt = 0;
- public:
- int testingOnly_peekOnFlushRefCnt() const;
- };
- inline GrCCPathCache::HashNode::HashNode(GrCCPathCache* pathCache, sk_sp<Key> key,
- const MaskTransform& m, const GrShape& shape)
- : fPathCache(pathCache)
- , fEntry(new GrCCPathCacheEntry(key, m)) {
- SkASSERT(shape.hasUnstyledKey());
- shape.addGenIDChangeListener(std::move(key));
- }
- inline const GrCCPathCache::Key& GrCCPathCache::HashNode::GetKey(
- const GrCCPathCache::HashNode& node) {
- return *node.entry()->fCacheKey;
- }
- inline GrCCPathCache::HashNode::~HashNode() {
- SkASSERT(!fEntry || fEntry->hasBeenEvicted()); // Should have called GrCCPathCache::evict().
- }
- inline void GrCCPathCache::HashNode::operator=(HashNode&& node) {
- SkASSERT(!fEntry || fEntry->hasBeenEvicted()); // Should have called GrCCPathCache::evict().
- fEntry = skstd::exchange(node.fEntry, nullptr);
- }
- inline void GrCCPathProcessor::Instance::set(
- const GrCCPathCacheEntry& entry, const SkIVector& shift, uint64_t color,
- GrFillRule fillRule) {
- float dx = (float)shift.fX, dy = (float)shift.fY;
- this->set(entry.fOctoBounds.makeOffset(dx, dy), entry.fAtlasOffset - shift, color, fillRule);
- }
- #endif
|