/* * Copyright 2020 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/GrThreadSafeCache.h" #include "include/gpu/GrDirectContext.h" #include "src/gpu/GrDirectContextPriv.h" #include "src/gpu/GrProxyProvider.h" #include "src/gpu/GrResourceCache.h" #include "src/gpu/GrSurfaceDrawContext.h" GrThreadSafeCache::VertexData::~VertexData () { this->reset(); } GrThreadSafeCache::GrThreadSafeCache() : fFreeEntryList(nullptr) { } GrThreadSafeCache::~GrThreadSafeCache() { this->dropAllRefs(); } #if GR_TEST_UTILS int GrThreadSafeCache::numEntries() const { SkAutoSpinlock lock{fSpinLock}; return fUniquelyKeyedEntryMap.count(); } size_t GrThreadSafeCache::approxBytesUsedForHash() const { SkAutoSpinlock lock{fSpinLock}; return fUniquelyKeyedEntryMap.approxBytesUsed(); } #endif void GrThreadSafeCache::dropAllRefs() { SkAutoSpinlock lock{fSpinLock}; fUniquelyKeyedEntryMap.reset(); while (auto tmp = fUniquelyKeyedEntryList.head()) { fUniquelyKeyedEntryList.remove(tmp); this->recycleEntry(tmp); } // TODO: should we empty out the fFreeEntryList and reset fEntryAllocator? } // TODO: If iterating becomes too expensive switch to using something like GrIORef for the // GrSurfaceProxy void GrThreadSafeCache::dropUniqueRefs(GrResourceCache* resourceCache) { SkAutoSpinlock lock{fSpinLock}; // Iterate from LRU to MRU Entry* cur = fUniquelyKeyedEntryList.tail(); Entry* prev = cur ? cur->fPrev : nullptr; while (cur) { if (resourceCache && !resourceCache->overBudget()) { return; } if (cur->uniquelyHeld()) { fUniquelyKeyedEntryMap.remove(cur->key()); fUniquelyKeyedEntryList.remove(cur); this->recycleEntry(cur); } cur = prev; prev = cur ? cur->fPrev : nullptr; } } void GrThreadSafeCache::dropUniqueRefsOlderThan(GrStdSteadyClock::time_point purgeTime) { SkAutoSpinlock lock{fSpinLock}; // Iterate from LRU to MRU Entry* cur = fUniquelyKeyedEntryList.tail(); Entry* prev = cur ? cur->fPrev : nullptr; while (cur) { if (cur->fLastAccess >= purgeTime) { // This entry and all the remaining ones in the list will be newer than 'purgeTime' return; } if (cur->uniquelyHeld()) { fUniquelyKeyedEntryMap.remove(cur->key()); fUniquelyKeyedEntryList.remove(cur); this->recycleEntry(cur); } cur = prev; prev = cur ? cur->fPrev : nullptr; } } void GrThreadSafeCache::makeExistingEntryMRU(Entry* entry) { SkASSERT(fUniquelyKeyedEntryList.isInList(entry)); entry->fLastAccess = GrStdSteadyClock::now(); fUniquelyKeyedEntryList.remove(entry); fUniquelyKeyedEntryList.addToHead(entry); } std::tuple> GrThreadSafeCache::internalFind( const GrUniqueKey& key) { Entry* tmp = fUniquelyKeyedEntryMap.find(key); if (tmp) { this->makeExistingEntryMRU(tmp); return { tmp->view(), tmp->refCustomData() }; } return {}; } #ifdef SK_DEBUG bool GrThreadSafeCache::has(const GrUniqueKey& key) { SkAutoSpinlock lock{fSpinLock}; Entry* tmp = fUniquelyKeyedEntryMap.find(key); return SkToBool(tmp); } #endif GrSurfaceProxyView GrThreadSafeCache::find(const GrUniqueKey& key) { SkAutoSpinlock lock{fSpinLock}; GrSurfaceProxyView view; std::tie(view, std::ignore) = this->internalFind(key); return view; } std::tuple> GrThreadSafeCache::findWithData( const GrUniqueKey& key) { SkAutoSpinlock lock{fSpinLock}; return this->internalFind(key); } GrThreadSafeCache::Entry* GrThreadSafeCache::getEntry(const GrUniqueKey& key, const GrSurfaceProxyView& view) { Entry* entry; if (fFreeEntryList) { entry = fFreeEntryList; fFreeEntryList = entry->fNext; entry->fNext = nullptr; entry->set(key, view); } else { entry = fEntryAllocator.make(key, view); } return this->makeNewEntryMRU(entry); } GrThreadSafeCache::Entry* GrThreadSafeCache::makeNewEntryMRU(Entry* entry) { entry->fLastAccess = GrStdSteadyClock::now(); fUniquelyKeyedEntryList.addToHead(entry); fUniquelyKeyedEntryMap.add(entry); return entry; } GrThreadSafeCache::Entry* GrThreadSafeCache::getEntry(const GrUniqueKey& key, sk_sp vertData) { Entry* entry; if (fFreeEntryList) { entry = fFreeEntryList; fFreeEntryList = entry->fNext; entry->fNext = nullptr; entry->set(key, std::move(vertData)); } else { entry = fEntryAllocator.make(key, std::move(vertData)); } return this->makeNewEntryMRU(entry); } void GrThreadSafeCache::recycleEntry(Entry* dead) { SkASSERT(!dead->fPrev && !dead->fNext && !dead->fList); dead->makeEmpty(); dead->fNext = fFreeEntryList; fFreeEntryList = dead; } std::tuple> GrThreadSafeCache::internalAdd( const GrUniqueKey& key, const GrSurfaceProxyView& view) { Entry* tmp = fUniquelyKeyedEntryMap.find(key); if (!tmp) { tmp = this->getEntry(key, view); SkASSERT(fUniquelyKeyedEntryMap.find(key)); } return { tmp->view(), tmp->refCustomData() }; } GrSurfaceProxyView GrThreadSafeCache::add(const GrUniqueKey& key, const GrSurfaceProxyView& view) { SkAutoSpinlock lock{fSpinLock}; GrSurfaceProxyView newView; std::tie(newView, std::ignore) = this->internalAdd(key, view); return newView; } std::tuple> GrThreadSafeCache::addWithData( const GrUniqueKey& key, const GrSurfaceProxyView& view) { SkAutoSpinlock lock{fSpinLock}; return this->internalAdd(key, view); } GrSurfaceProxyView GrThreadSafeCache::findOrAdd(const GrUniqueKey& key, const GrSurfaceProxyView& v) { SkAutoSpinlock lock{fSpinLock}; GrSurfaceProxyView view; std::tie(view, std::ignore) = this->internalFind(key); if (view) { return view; } std::tie(view, std::ignore) = this->internalAdd(key, v); return view; } std::tuple> GrThreadSafeCache::findOrAddWithData( const GrUniqueKey& key, const GrSurfaceProxyView& v) { SkAutoSpinlock lock{fSpinLock}; auto [view, data] = this->internalFind(key); if (view) { return { std::move(view), std::move(data) }; } return this->internalAdd(key, v); } sk_sp GrThreadSafeCache::MakeVertexData(const void* vertices, int vertexCount, size_t vertexSize) { return sk_sp(new VertexData(vertices, vertexCount, vertexSize)); } sk_sp GrThreadSafeCache::MakeVertexData(sk_sp buffer, int vertexCount, size_t vertexSize) { return sk_sp(new VertexData(std::move(buffer), vertexCount, vertexSize)); } std::tuple, sk_sp> GrThreadSafeCache::internalFindVerts( const GrUniqueKey& key) { Entry* tmp = fUniquelyKeyedEntryMap.find(key); if (tmp) { this->makeExistingEntryMRU(tmp); return { tmp->vertexData(), tmp->refCustomData() }; } return {}; } std::tuple, sk_sp> GrThreadSafeCache::findVertsWithData( const GrUniqueKey& key) { SkAutoSpinlock lock{fSpinLock}; return this->internalFindVerts(key); } std::tuple, sk_sp> GrThreadSafeCache::internalAddVerts( const GrUniqueKey& key, sk_sp vertData, IsNewerBetter isNewerBetter) { Entry* tmp = fUniquelyKeyedEntryMap.find(key); if (!tmp) { tmp = this->getEntry(key, std::move(vertData)); SkASSERT(fUniquelyKeyedEntryMap.find(key)); } else if (isNewerBetter(tmp->getCustomData(), key.getCustomData())) { // This orphans any existing uses of the prior vertex data but ensures the best // version is in the cache. tmp->set(key, std::move(vertData)); } return { tmp->vertexData(), tmp->refCustomData() }; } std::tuple, sk_sp> GrThreadSafeCache::addVertsWithData( const GrUniqueKey& key, sk_sp vertData, IsNewerBetter isNewerBetter) { SkAutoSpinlock lock{fSpinLock}; return this->internalAddVerts(key, std::move(vertData), isNewerBetter); } void GrThreadSafeCache::remove(const GrUniqueKey& key) { SkAutoSpinlock lock{fSpinLock}; Entry* tmp = fUniquelyKeyedEntryMap.find(key); if (tmp) { fUniquelyKeyedEntryMap.remove(key); fUniquelyKeyedEntryList.remove(tmp); this->recycleEntry(tmp); } } std::tuple> GrThreadSafeCache::CreateLazyView(GrDirectContext* dContext, GrColorType origCT, SkISize dimensions, GrSurfaceOrigin origin, SkBackingFit fit) { GrProxyProvider* proxyProvider = dContext->priv().proxyProvider(); constexpr int kSampleCnt = 1; auto [newCT, format] = GrSurfaceFillContext::GetFallbackColorTypeAndFormat(dContext, origCT, kSampleCnt); if (newCT == GrColorType::kUnknown) { return {GrSurfaceProxyView(nullptr), nullptr}; } sk_sp trampoline(new Trampoline); GrProxyProvider::TextureInfo texInfo{ GrMipMapped::kNo, GrTextureType::k2D }; sk_sp proxy = proxyProvider->createLazyRenderTargetProxy( [trampoline]( GrResourceProvider* resourceProvider, const GrSurfaceProxy::LazySurfaceDesc&) -> GrSurfaceProxy::LazyCallbackResult { if (!resourceProvider || !trampoline->fProxy || !trampoline->fProxy->isInstantiated()) { return GrSurfaceProxy::LazyCallbackResult(nullptr, true); } SkASSERT(!trampoline->fProxy->peekTexture()->getUniqueKey().isValid()); return GrSurfaceProxy::LazyCallbackResult( sk_ref_sp(trampoline->fProxy->peekTexture())); }, format, dimensions, kSampleCnt, GrInternalSurfaceFlags::kNone, &texInfo, GrMipmapStatus::kNotAllocated, fit, SkBudgeted::kYes, GrProtected::kNo, /* wrapsVkSecondaryCB */ false, GrSurfaceProxy::UseAllocator::kYes); // TODO: It seems like this 'newCT' usage should be 'origCT' but this is // what GrSurfaceDrawContext::MakeWithFallback does GrSwizzle swizzle = dContext->priv().caps()->getReadSwizzle(format, newCT); return {{std::move(proxy), origin, swizzle}, std::move(trampoline)}; }