123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "src/gpu/ccpr/GrCCPathCache.h"
- #include "include/private/SkNx.h"
- #include "src/gpu/GrOnFlushResourceProvider.h"
- #include "src/gpu/GrProxyProvider.h"
- static constexpr int kMaxKeyDataCountU32 = 256; // 1kB of uint32_t's.
- DECLARE_SKMESSAGEBUS_MESSAGE(sk_sp<GrCCPathCache::Key>);
- static inline uint32_t next_path_cache_id() {
- static std::atomic<uint32_t> gNextID(1);
- for (;;) {
- uint32_t id = gNextID.fetch_add(+1, std::memory_order_acquire);
- if (SK_InvalidUniqueID != id) {
- return id;
- }
- }
- }
- static inline bool SkShouldPostMessageToBus(
- const sk_sp<GrCCPathCache::Key>& key, uint32_t msgBusUniqueID) {
- return key->pathCacheUniqueID() == msgBusUniqueID;
- }
- // The maximum number of cache entries we allow in our own cache.
- static constexpr int kMaxCacheCount = 1 << 16;
- GrCCPathCache::MaskTransform::MaskTransform(const SkMatrix& m, SkIVector* shift)
- : fMatrix2x2{m.getScaleX(), m.getSkewX(), m.getSkewY(), m.getScaleY()} {
- SkASSERT(!m.hasPerspective());
- Sk2f translate = Sk2f(m.getTranslateX(), m.getTranslateY());
- Sk2f transFloor;
- #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
- // On Android framework we pre-round view matrix translates to integers for better caching.
- transFloor = translate;
- #else
- transFloor = translate.floor();
- (translate - transFloor).store(fSubpixelTranslate);
- #endif
- shift->set((int)transFloor[0], (int)transFloor[1]);
- SkASSERT((float)shift->fX == transFloor[0]); // Make sure transFloor had integer values.
- SkASSERT((float)shift->fY == transFloor[1]);
- }
- inline static bool fuzzy_equals(const GrCCPathCache::MaskTransform& a,
- const GrCCPathCache::MaskTransform& b) {
- if ((Sk4f::Load(a.fMatrix2x2) != Sk4f::Load(b.fMatrix2x2)).anyTrue()) {
- return false;
- }
- #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
- if (((Sk2f::Load(a.fSubpixelTranslate) -
- Sk2f::Load(b.fSubpixelTranslate)).abs() > 1.f/256).anyTrue()) {
- return false;
- }
- #endif
- return true;
- }
- sk_sp<GrCCPathCache::Key> GrCCPathCache::Key::Make(uint32_t pathCacheUniqueID,
- int dataCountU32, const void* data) {
- void* memory = ::operator new (sizeof(Key) + dataCountU32 * sizeof(uint32_t));
- sk_sp<GrCCPathCache::Key> key(new (memory) Key(pathCacheUniqueID, dataCountU32));
- if (data) {
- memcpy(key->data(), data, key->dataSizeInBytes());
- }
- return key;
- }
- const uint32_t* GrCCPathCache::Key::data() const {
- // The shape key is a variable-length footer to the entry allocation.
- return reinterpret_cast<const uint32_t*>(reinterpret_cast<const char*>(this) + sizeof(Key));
- }
- uint32_t* GrCCPathCache::Key::data() {
- // The shape key is a variable-length footer to the entry allocation.
- return reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(this) + sizeof(Key));
- }
- void GrCCPathCache::Key::onChange() {
- // Our key's corresponding path was invalidated. Post a thread-safe eviction message.
- SkMessageBus<sk_sp<Key>>::Post(sk_ref_sp(this));
- }
- GrCCPathCache::GrCCPathCache(uint32_t contextUniqueID)
- : fContextUniqueID(contextUniqueID)
- , fInvalidatedKeysInbox(next_path_cache_id())
- , fScratchKey(Key::Make(fInvalidatedKeysInbox.uniqueID(), kMaxKeyDataCountU32)) {
- }
- GrCCPathCache::~GrCCPathCache() {
- while (!fLRU.isEmpty()) {
- this->evict(*fLRU.tail()->fCacheKey, fLRU.tail());
- }
- SkASSERT(0 == fHashTable.count()); // Ensure the hash table and LRU list were coherent.
- // Now take all the atlas textures we just invalidated and purge them from the GrResourceCache.
- // We just purge via message bus since we don't have any access to the resource cache right now.
- for (sk_sp<GrTextureProxy>& proxy : fInvalidatedProxies) {
- SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
- GrUniqueKeyInvalidatedMessage(proxy->getUniqueKey(), fContextUniqueID));
- }
- for (const GrUniqueKey& key : fInvalidatedProxyUniqueKeys) {
- SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
- GrUniqueKeyInvalidatedMessage(key, fContextUniqueID));
- }
- }
- namespace {
- // Produces a key that accounts both for a shape's path geometry, as well as any stroke/style.
- class WriteKeyHelper {
- public:
- static constexpr int kStrokeWidthIdx = 0;
- static constexpr int kStrokeMiterIdx = 1;
- static constexpr int kStrokeCapJoinIdx = 2;
- static constexpr int kShapeUnstyledKeyIdx = 3;
- WriteKeyHelper(const GrShape& shape) : fShapeUnstyledKeyCount(shape.unstyledKeySize()) {}
- // Returns the total number of uint32_t's to allocate for the key.
- int allocCountU32() const { return kShapeUnstyledKeyIdx + fShapeUnstyledKeyCount; }
- // Writes the key data to out[].
- void write(const GrShape& shape, uint32_t* out) {
- // Stroke key.
- // We don't use GrStyle::WriteKey() because it does not account for hairlines.
- // http://skbug.com/8273
- SkASSERT(!shape.style().hasPathEffect());
- const SkStrokeRec& stroke = shape.style().strokeRec();
- if (stroke.isFillStyle()) {
- // Use a value for width that won't collide with a valid fp32 value >= 0.
- out[kStrokeWidthIdx] = ~0;
- out[kStrokeMiterIdx] = out[kStrokeCapJoinIdx] = 0;
- } else {
- float width = stroke.getWidth(), miterLimit = stroke.getMiter();
- memcpy(&out[kStrokeWidthIdx], &width, sizeof(float));
- memcpy(&out[kStrokeMiterIdx], &miterLimit, sizeof(float));
- out[kStrokeCapJoinIdx] = (stroke.getCap() << 16) | stroke.getJoin();
- GR_STATIC_ASSERT(sizeof(out[kStrokeWidthIdx]) == sizeof(float));
- }
- // Shape unstyled key.
- shape.writeUnstyledKey(&out[kShapeUnstyledKeyIdx]);
- }
- private:
- int fShapeUnstyledKeyCount;
- };
- }
- GrCCPathCache::OnFlushEntryRef GrCCPathCache::find(
- GrOnFlushResourceProvider* onFlushRP, const GrShape& shape,
- const SkIRect& clippedDrawBounds, const SkMatrix& viewMatrix, SkIVector* maskShift) {
- if (!shape.hasUnstyledKey()) {
- return OnFlushEntryRef();
- }
- WriteKeyHelper writeKeyHelper(shape);
- if (writeKeyHelper.allocCountU32() > kMaxKeyDataCountU32) {
- return OnFlushEntryRef();
- }
- SkASSERT(fScratchKey->unique());
- fScratchKey->resetDataCountU32(writeKeyHelper.allocCountU32());
- writeKeyHelper.write(shape, fScratchKey->data());
- MaskTransform m(viewMatrix, maskShift);
- GrCCPathCacheEntry* entry = nullptr;
- if (HashNode* node = fHashTable.find(*fScratchKey)) {
- entry = node->entry();
- SkASSERT(fLRU.isInList(entry));
- if (!fuzzy_equals(m, entry->fMaskTransform)) {
- // The path was reused with an incompatible matrix.
- if (entry->unique()) {
- // This entry is unique: recycle it instead of deleting and malloc-ing a new one.
- SkASSERT(0 == entry->fOnFlushRefCnt); // Because we are unique.
- entry->fMaskTransform = m;
- entry->fHitCount = 0;
- entry->fHitRect = SkIRect::MakeEmpty();
- entry->releaseCachedAtlas(this);
- } else {
- this->evict(*fScratchKey);
- entry = nullptr;
- }
- }
- }
- if (!entry) {
- if (fHashTable.count() >= kMaxCacheCount) {
- SkDEBUGCODE(HashNode* node = fHashTable.find(*fLRU.tail()->fCacheKey));
- SkASSERT(node && node->entry() == fLRU.tail());
- this->evict(*fLRU.tail()->fCacheKey); // We've exceeded our limit.
- }
- // Create a new entry in the cache.
- sk_sp<Key> permanentKey = Key::Make(fInvalidatedKeysInbox.uniqueID(),
- writeKeyHelper.allocCountU32(), fScratchKey->data());
- SkASSERT(*permanentKey == *fScratchKey);
- SkASSERT(!fHashTable.find(*permanentKey));
- entry = fHashTable.set(HashNode(this, std::move(permanentKey), m, shape))->entry();
- SkASSERT(fHashTable.count() <= kMaxCacheCount);
- } else {
- fLRU.remove(entry); // Will be re-added at head.
- }
- SkDEBUGCODE(HashNode* node = fHashTable.find(*fScratchKey));
- SkASSERT(node && node->entry() == entry);
- fLRU.addToHead(entry);
- if (0 == entry->fOnFlushRefCnt) {
- // Only update the time stamp and hit count if we haven't seen this entry yet during the
- // current flush.
- entry->fTimestamp = this->quickPerFlushTimestamp();
- ++entry->fHitCount;
- if (entry->fCachedAtlas) {
- SkASSERT(SkToBool(entry->fCachedAtlas->peekOnFlushRefCnt()) ==
- SkToBool(entry->fCachedAtlas->getOnFlushProxy()));
- if (!entry->fCachedAtlas->getOnFlushProxy()) {
- if (sk_sp<GrTextureProxy> onFlushProxy = onFlushRP->findOrCreateProxyByUniqueKey(
- entry->fCachedAtlas->textureKey(), GrCCAtlas::kTextureOrigin)) {
- onFlushProxy->priv().setIgnoredByResourceAllocator();
- entry->fCachedAtlas->setOnFlushProxy(std::move(onFlushProxy));
- }
- }
- if (!entry->fCachedAtlas->getOnFlushProxy()) {
- // Our atlas's backing texture got purged from the GrResourceCache. Release the
- // cached atlas.
- entry->releaseCachedAtlas(this);
- }
- }
- }
- entry->fHitRect.join(clippedDrawBounds.makeOffset(-maskShift->x(), -maskShift->y()));
- SkASSERT(!entry->fCachedAtlas || entry->fCachedAtlas->getOnFlushProxy());
- return OnFlushEntryRef::OnFlushRef(entry);
- }
- void GrCCPathCache::evict(const GrCCPathCache::Key& key, GrCCPathCacheEntry* entry) {
- if (!entry) {
- HashNode* node = fHashTable.find(key);
- SkASSERT(node);
- entry = node->entry();
- }
- SkASSERT(*entry->fCacheKey == key);
- SkASSERT(!entry->hasBeenEvicted());
- entry->fCacheKey->markShouldUnregisterFromPath(); // Unregister the path listener.
- entry->releaseCachedAtlas(this);
- fLRU.remove(entry);
- fHashTable.remove(key);
- }
- void GrCCPathCache::doPreFlushProcessing() {
- this->evictInvalidatedCacheKeys();
- // Mark the per-flush timestamp as needing to be updated with a newer clock reading.
- fPerFlushTimestamp = GrStdSteadyClock::time_point::min();
- }
- void GrCCPathCache::purgeEntriesOlderThan(GrProxyProvider* proxyProvider,
- const GrStdSteadyClock::time_point& purgeTime) {
- this->evictInvalidatedCacheKeys();
- #ifdef SK_DEBUG
- auto lastTimestamp = (fLRU.isEmpty())
- ? GrStdSteadyClock::time_point::max()
- : fLRU.tail()->fTimestamp;
- #endif
- // Evict every entry from our local path cache whose timestamp is older than purgeTime.
- while (!fLRU.isEmpty() && fLRU.tail()->fTimestamp < purgeTime) {
- #ifdef SK_DEBUG
- // Verify that fLRU is sorted by timestamp.
- auto timestamp = fLRU.tail()->fTimestamp;
- SkASSERT(timestamp >= lastTimestamp);
- lastTimestamp = timestamp;
- #endif
- this->evict(*fLRU.tail()->fCacheKey);
- }
- // Now take all the atlas textures we just invalidated and purge them from the GrResourceCache.
- this->purgeInvalidatedAtlasTextures(proxyProvider);
- }
- void GrCCPathCache::purgeInvalidatedAtlasTextures(GrOnFlushResourceProvider* onFlushRP) {
- for (sk_sp<GrTextureProxy>& proxy : fInvalidatedProxies) {
- onFlushRP->removeUniqueKeyFromProxy(proxy.get());
- }
- fInvalidatedProxies.reset();
- for (const GrUniqueKey& key : fInvalidatedProxyUniqueKeys) {
- onFlushRP->processInvalidUniqueKey(key);
- }
- fInvalidatedProxyUniqueKeys.reset();
- }
- void GrCCPathCache::purgeInvalidatedAtlasTextures(GrProxyProvider* proxyProvider) {
- for (sk_sp<GrTextureProxy>& proxy : fInvalidatedProxies) {
- proxyProvider->removeUniqueKeyFromProxy(proxy.get());
- }
- fInvalidatedProxies.reset();
- for (const GrUniqueKey& key : fInvalidatedProxyUniqueKeys) {
- proxyProvider->processInvalidUniqueKey(key, nullptr,
- GrProxyProvider::InvalidateGPUResource::kYes);
- }
- fInvalidatedProxyUniqueKeys.reset();
- }
- void GrCCPathCache::evictInvalidatedCacheKeys() {
- SkTArray<sk_sp<Key>> invalidatedKeys;
- fInvalidatedKeysInbox.poll(&invalidatedKeys);
- for (const sk_sp<Key>& key : invalidatedKeys) {
- bool isInCache = !key->shouldUnregisterFromPath(); // Gets set upon exiting the cache.
- if (isInCache) {
- this->evict(*key);
- }
- }
- }
- GrCCPathCache::OnFlushEntryRef
- GrCCPathCache::OnFlushEntryRef::OnFlushRef(GrCCPathCacheEntry* entry) {
- entry->ref();
- ++entry->fOnFlushRefCnt;
- if (entry->fCachedAtlas) {
- entry->fCachedAtlas->incrOnFlushRefCnt();
- }
- return OnFlushEntryRef(entry);
- }
- GrCCPathCache::OnFlushEntryRef::~OnFlushEntryRef() {
- if (!fEntry) {
- return;
- }
- --fEntry->fOnFlushRefCnt;
- SkASSERT(fEntry->fOnFlushRefCnt >= 0);
- if (fEntry->fCachedAtlas) {
- fEntry->fCachedAtlas->decrOnFlushRefCnt();
- }
- fEntry->unref();
- }
- void GrCCPathCacheEntry::setCoverageCountAtlas(
- GrOnFlushResourceProvider* onFlushRP, GrCCAtlas* atlas, const SkIVector& atlasOffset,
- const GrOctoBounds& octoBounds, const SkIRect& devIBounds, const SkIVector& maskShift) {
- SkASSERT(fOnFlushRefCnt > 0);
- SkASSERT(!fCachedAtlas); // Otherwise we would need to call releaseCachedAtlas().
- if (this->hasBeenEvicted()) {
- // This entry will never be found in the path cache again. Don't bother trying to save an
- // atlas texture for it in the GrResourceCache.
- return;
- }
- fCachedAtlas = atlas->refOrMakeCachedAtlas(onFlushRP);
- fCachedAtlas->incrOnFlushRefCnt(fOnFlushRefCnt);
- fCachedAtlas->addPathPixels(devIBounds.height() * devIBounds.width());
- fAtlasOffset = atlasOffset + maskShift;
- fOctoBounds.setOffset(octoBounds, -maskShift.fX, -maskShift.fY);
- fDevIBounds = devIBounds.makeOffset(-maskShift.fX, -maskShift.fY);
- }
- GrCCPathCacheEntry::ReleaseAtlasResult GrCCPathCacheEntry::upgradeToLiteralCoverageAtlas(
- GrCCPathCache* pathCache, GrOnFlushResourceProvider* onFlushRP, GrCCAtlas* atlas,
- const SkIVector& newAtlasOffset) {
- SkASSERT(!this->hasBeenEvicted());
- SkASSERT(fOnFlushRefCnt > 0);
- SkASSERT(fCachedAtlas);
- SkASSERT(GrCCAtlas::CoverageType::kA8_LiteralCoverage != fCachedAtlas->coverageType());
- ReleaseAtlasResult releaseAtlasResult = this->releaseCachedAtlas(pathCache);
- fCachedAtlas = atlas->refOrMakeCachedAtlas(onFlushRP);
- fCachedAtlas->incrOnFlushRefCnt(fOnFlushRefCnt);
- fCachedAtlas->addPathPixels(this->height() * this->width());
- fAtlasOffset = newAtlasOffset;
- return releaseAtlasResult;
- }
- GrCCPathCacheEntry::ReleaseAtlasResult GrCCPathCacheEntry::releaseCachedAtlas(
- GrCCPathCache* pathCache) {
- ReleaseAtlasResult result = ReleaseAtlasResult::kNone;
- if (fCachedAtlas) {
- result = fCachedAtlas->invalidatePathPixels(pathCache, this->height() * this->width());
- if (fOnFlushRefCnt) {
- SkASSERT(fOnFlushRefCnt > 0);
- fCachedAtlas->decrOnFlushRefCnt(fOnFlushRefCnt);
- }
- fCachedAtlas = nullptr;
- }
- return result;
- }
- GrCCPathCacheEntry::ReleaseAtlasResult GrCCCachedAtlas::invalidatePathPixels(
- GrCCPathCache* pathCache, int numPixels) {
- // Mark the pixels invalid in the cached atlas texture.
- fNumInvalidatedPathPixels += numPixels;
- SkASSERT(fNumInvalidatedPathPixels <= fNumPathPixels);
- if (!fIsInvalidatedFromResourceCache && fNumInvalidatedPathPixels >= fNumPathPixels / 2) {
- // Too many invalidated pixels: purge the atlas texture from the resource cache.
- if (fOnFlushProxy) {
- // Don't clear (or std::move) fOnFlushProxy. Other path cache entries might still have a
- // reference on this atlas and expect to use our proxy during the current flush.
- // fOnFlushProxy will be cleared once fOnFlushRefCnt decrements to zero.
- pathCache->fInvalidatedProxies.push_back(fOnFlushProxy);
- } else {
- pathCache->fInvalidatedProxyUniqueKeys.push_back(fTextureKey);
- }
- fIsInvalidatedFromResourceCache = true;
- return ReleaseAtlasResult::kDidInvalidateFromCache;
- }
- return ReleaseAtlasResult::kNone;
- }
- void GrCCCachedAtlas::decrOnFlushRefCnt(int count) const {
- SkASSERT(count > 0);
- fOnFlushRefCnt -= count;
- SkASSERT(fOnFlushRefCnt >= 0);
- if (0 == fOnFlushRefCnt) {
- // Don't hold the actual proxy past the end of the current flush.
- SkASSERT(fOnFlushProxy);
- fOnFlushProxy = nullptr;
- }
- }
|