blob: 602742894c76e61171e5737ce347857594cc5e20 [file] [log] [blame]
/*
* 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<ProxyReadCountMap> proxyCounts)
: fResourceProvider(resourceProvider)
, fProxyReadCounts(std::move(proxyCounts)) {
SkASSERT(resourceProvider);
SkASSERT(fProxyReadCounts);
}
ScratchResourceManager::~ScratchResourceManager() = default;
sk_sp<Texture> 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<Texture> 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> 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