| /* |
| * 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/ganesh/GrThreadSafeCache.h" |
| |
| #include "include/gpu/GrDirectContext.h" |
| #include "src/gpu/ganesh/GrCaps.h" |
| #include "src/gpu/ganesh/GrDirectContextPriv.h" |
| #include "src/gpu/ganesh/GrGpuBuffer.h" |
| #include "src/gpu/ganesh/GrProxyProvider.h" |
| #include "src/gpu/ganesh/GrRenderTargetProxy.h" |
| #include "src/gpu/ganesh/GrResourceCache.h" |
| #include "src/gpu/ganesh/GrTexture.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<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::internalFind( |
| const skgpu::UniqueKey& key) { |
| Entry* tmp = fUniquelyKeyedEntryMap.find(key); |
| if (tmp) { |
| this->makeExistingEntryMRU(tmp); |
| return { tmp->view(), tmp->refCustomData() }; |
| } |
| |
| return {}; |
| } |
| |
| #ifdef SK_DEBUG |
| bool GrThreadSafeCache::has(const skgpu::UniqueKey& key) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| Entry* tmp = fUniquelyKeyedEntryMap.find(key); |
| return SkToBool(tmp); |
| } |
| #endif |
| |
| GrSurfaceProxyView GrThreadSafeCache::find(const skgpu::UniqueKey& key) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| GrSurfaceProxyView view; |
| std::tie(view, std::ignore) = this->internalFind(key); |
| return view; |
| } |
| |
| std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::findWithData( |
| const skgpu::UniqueKey& key) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| return this->internalFind(key); |
| } |
| |
| GrThreadSafeCache::Entry* GrThreadSafeCache::getEntry(const skgpu::UniqueKey& key, |
| const GrSurfaceProxyView& view) { |
| Entry* entry; |
| |
| if (fFreeEntryList) { |
| entry = fFreeEntryList; |
| fFreeEntryList = entry->fNext; |
| entry->fNext = nullptr; |
| |
| entry->set(key, view); |
| } else { |
| entry = fEntryAllocator.make<Entry>(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 skgpu::UniqueKey& key, |
| sk_sp<VertexData> vertData) { |
| Entry* entry; |
| |
| if (fFreeEntryList) { |
| entry = fFreeEntryList; |
| fFreeEntryList = entry->fNext; |
| entry->fNext = nullptr; |
| |
| entry->set(key, std::move(vertData)); |
| } else { |
| entry = fEntryAllocator.make<Entry>(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<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::internalAdd( |
| const skgpu::UniqueKey& 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 skgpu::UniqueKey& key, |
| const GrSurfaceProxyView& view) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| GrSurfaceProxyView newView; |
| std::tie(newView, std::ignore) = this->internalAdd(key, view); |
| return newView; |
| } |
| |
| std::tuple<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::addWithData( |
| const skgpu::UniqueKey& key, |
| const GrSurfaceProxyView& view) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| return this->internalAdd(key, view); |
| } |
| |
| GrSurfaceProxyView GrThreadSafeCache::findOrAdd(const skgpu::UniqueKey& 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<GrSurfaceProxyView, sk_sp<SkData>> GrThreadSafeCache::findOrAddWithData( |
| const skgpu::UniqueKey& 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::VertexData> GrThreadSafeCache::MakeVertexData(const void* vertices, |
| int vertexCount, |
| size_t vertexSize) { |
| return sk_sp<VertexData>(new VertexData(vertices, vertexCount, vertexSize)); |
| } |
| |
| sk_sp<GrThreadSafeCache::VertexData> GrThreadSafeCache::MakeVertexData(sk_sp<GrGpuBuffer> buffer, |
| int vertexCount, |
| size_t vertexSize) { |
| return sk_sp<VertexData>(new VertexData(std::move(buffer), vertexCount, vertexSize)); |
| } |
| |
| std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> |
| GrThreadSafeCache::internalFindVerts(const skgpu::UniqueKey& key) { |
| Entry* tmp = fUniquelyKeyedEntryMap.find(key); |
| if (tmp) { |
| this->makeExistingEntryMRU(tmp); |
| return { tmp->vertexData(), tmp->refCustomData() }; |
| } |
| |
| return {}; |
| } |
| |
| std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> |
| GrThreadSafeCache::findVertsWithData(const skgpu::UniqueKey& key) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| return this->internalFindVerts(key); |
| } |
| |
| std::tuple<sk_sp<GrThreadSafeCache::VertexData>, sk_sp<SkData>> GrThreadSafeCache::internalAddVerts( |
| const skgpu::UniqueKey& key, |
| sk_sp<VertexData> 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::VertexData>, sk_sp<SkData>> GrThreadSafeCache::addVertsWithData( |
| const skgpu::UniqueKey& key, |
| sk_sp<VertexData> vertData, |
| IsNewerBetter isNewerBetter) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| return this->internalAddVerts(key, std::move(vertData), isNewerBetter); |
| } |
| |
| void GrThreadSafeCache::remove(const skgpu::UniqueKey& key) { |
| SkAutoSpinlock lock{fSpinLock}; |
| |
| Entry* tmp = fUniquelyKeyedEntryMap.find(key); |
| if (tmp) { |
| fUniquelyKeyedEntryMap.remove(key); |
| fUniquelyKeyedEntryList.remove(tmp); |
| this->recycleEntry(tmp); |
| } |
| } |
| |
| std::tuple<GrSurfaceProxyView, sk_sp<GrThreadSafeCache::Trampoline>> |
| GrThreadSafeCache::CreateLazyView(GrDirectContext* dContext, |
| GrColorType origCT, |
| SkISize dimensions, |
| GrSurfaceOrigin origin, |
| SkBackingFit fit) { |
| GrProxyProvider* proxyProvider = dContext->priv().proxyProvider(); |
| const GrCaps* caps = dContext->priv().caps(); |
| |
| constexpr int kSampleCnt = 1; |
| auto [newCT, format] = caps->getFallbackColorTypeAndFormat(origCT, kSampleCnt); |
| |
| if (newCT == GrColorType::kUnknown) { |
| return {GrSurfaceProxyView(nullptr), nullptr}; |
| } |
| |
| sk_sp<Trampoline> trampoline(new Trampoline); |
| |
| GrProxyProvider::TextureInfo texInfo{ GrMipmapped::kNo, GrTextureType::k2D }; |
| |
| sk_sp<GrRenderTargetProxy> 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, |
| skgpu::Budgeted::kYes, |
| GrProtected::kNo, |
| /* wrapsVkSecondaryCB */ false, |
| GrSurfaceProxy::UseAllocator::kYes); |
| |
| // TODO: It seems like this 'newCT' usage should be 'origCT' but this is |
| // what skgpu::v1::SurfaceDrawContext::MakeWithFallback does |
| skgpu::Swizzle swizzle = dContext->priv().caps()->getReadSwizzle(format, newCT); |
| |
| return {{std::move(proxy), origin, swizzle}, std::move(trampoline)}; |
| } |