/* * Copyright 2024 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/graphite/ScratchResourceManager.h" #include "src/gpu/graphite/Resource.h" #include "src/gpu/graphite/ResourceProvider.h" #include "src/gpu/graphite/Texture.h" #include "src/gpu/graphite/TextureProxy.h" namespace skgpu::graphite { ScratchResourceManager::ScratchResourceManager(ResourceProvider* resourceProvider, std::unique_ptr proxyCounts) : fResourceProvider(resourceProvider) , fProxyReadCounts(std::move(proxyCounts)) { SkASSERT(resourceProvider); SkASSERT(fProxyReadCounts); } ScratchResourceManager::~ScratchResourceManager() = default; sk_sp ScratchResourceManager::getScratchTexture(SkISize dimensions, const TextureInfo& info, std::string_view label) { for (ScratchTexture& st : fScratchTextures) { if (st.fAvailable && st.fTexture->dimensions() == dimensions && st.fTexture->textureInfo() == info) { // An exact match, reuse it. st.fAvailable = false; return st.fTexture; } } // No texture was available so go out to the resource provider, which will hopefully find a // cached resource that was freed up from a previous recording (or create a new one, if not). // TODO(b/339496039): Always start with a fixed label like "ScratchTexture" and then concatenate // the proxy label that's passed in onto the texture's label, including when reusing a texture. sk_sp newScratchTexture = fResourceProvider->findOrCreateScratchTexture( dimensions, info, std::move(label), Budgeted::kYes); if (newScratchTexture) { fScratchTextures.push_back({newScratchTexture, /*fAvailable=*/false}); } return newScratchTexture; } void ScratchResourceManager::returnTexture(sk_sp texture) { for (ScratchTexture& st : fScratchTextures) { if (st.fTexture.get() == texture.get()) { SkASSERT(!st.fAvailable); st.fAvailable = true; return; } } // Trying to return a resource that didn't come from getScratchTexture(). SkASSERT(false); } void ScratchResourceManager::pushScope() { // Push a null pointer to mark the beginning of the list of listeners in the next depth fListenerStack.push_back(nullptr); } void ScratchResourceManager::popScope() { // Must have at least the null element to start the scope being popped SkASSERT(!fListenerStack.empty()); // TODO: Assert that the current sublist is empty (i.e. the back element is a null pointer) but // for now skip over them and leave them un-invoked to keep the unconsumed scratch resources // out of the pool so they remain valid in later recordings. int n = 0; while (fListenerStack.fromBack(n)) { n++; } SkASSERT(n < fListenerStack.size() && fListenerStack.fromBack(n) == nullptr); // Remove all non-null listeners after the most recent null entry AND the null entry fListenerStack.pop_back_n(n + 1); } void ScratchResourceManager::notifyResourcesConsumed() { // Should only be called inside a scope SkASSERT(!fListenerStack.empty()); int n = 0; while (PendingUseListener* listener = fListenerStack.fromBack(n)) { listener->onUseCompleted(this); n++; } SkASSERT(n < fListenerStack.size() && fListenerStack.fromBack(n) == nullptr); // Remove all non-null listeners that were just invoked, but do not remove the null entry that // marks the start of this scope boundary. if (n > 0) { fListenerStack.pop_back_n(n); } } void ScratchResourceManager::markResourceInUse(PendingUseListener* listener) { // Should only be called inside a scope SkASSERT(!fListenerStack.empty()); fListenerStack.push_back(listener); } } // namespace skgpu::graphite