/* * Copyright 2021 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/gpu/graphite/Recording.h" #include "src/core/SkChecksum.h" #include "src/gpu/RefCntedCallback.h" #include "src/gpu/graphite/CommandBuffer.h" #include "src/gpu/graphite/ContextPriv.h" #include "src/gpu/graphite/Log.h" #include "src/gpu/graphite/RecordingPriv.h" #include "src/gpu/graphite/Resource.h" #include "src/gpu/graphite/ResourceProvider.h" #include "src/gpu/graphite/Surface_Graphite.h" #include "src/gpu/graphite/Texture.h" #include "src/gpu/graphite/TextureProxy.h" #include "src/gpu/graphite/task/TaskList.h" #include using namespace skia_private; namespace skgpu::graphite { Recording::Recording(uint32_t uniqueID, uint32_t recorderID, std::unordered_set, ProxyHash>&& nonVolatileLazyProxies, std::unordered_set, ProxyHash>&& volatileLazyProxies, std::unique_ptr targetProxyData, TArray>&& finishedProcs) : fUniqueID(uniqueID) , fRecorderID(recorderID) , fRootTaskList(new TaskList) , fNonVolatileLazyProxies(std::move(nonVolatileLazyProxies)) , fVolatileLazyProxies(std::move(volatileLazyProxies)) , fTargetProxyData(std::move(targetProxyData)) , fFinishedProcs(std::move(finishedProcs)) {} Recording::~Recording() { // Any finished procs that haven't been passed to a CommandBuffer fail this->priv().setFailureResultForFinishedProcs(); } std::size_t Recording::ProxyHash::operator()(const sk_sp &proxy) const { return SkGoodHash()(proxy.get()); } Recording::LazyProxyData::LazyProxyData(const TextureInfo& textureInfo) { fTargetProxy = TextureProxy::MakeFullyLazy( textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, [this](ResourceProvider*) { SkASSERT(SkToBool(fTarget)); return std::move(fTarget); }); } TextureProxy* Recording::LazyProxyData::lazyProxy() { return fTargetProxy.get(); } sk_sp Recording::LazyProxyData::refLazyProxy() { return fTargetProxy; } bool Recording::LazyProxyData::lazyInstantiate(ResourceProvider* resourceProvider, sk_sp texture) { fTarget = std::move(texture); return fTargetProxy->lazyInstantiate(resourceProvider); } //////////////////////////////////////////////////////////////////////////////// bool RecordingPriv::hasNonVolatileLazyProxies() const { return !fRecording->fNonVolatileLazyProxies.empty(); } bool RecordingPriv::instantiateNonVolatileLazyProxies(ResourceProvider* resourceProvider) { SkASSERT(this->hasNonVolatileLazyProxies()); for (const auto& proxy : fRecording->fNonVolatileLazyProxies) { if (!proxy->lazyInstantiate(resourceProvider)) { return false; } } // Note: once all the lazy proxies have been instantiated, that's it - there are no more // chances to instantiate. fRecording->fNonVolatileLazyProxies.clear(); return true; } bool RecordingPriv::hasVolatileLazyProxies() const { return !fRecording->fVolatileLazyProxies.empty(); } bool RecordingPriv::instantiateVolatileLazyProxies(ResourceProvider* resourceProvider) { SkASSERT(this->hasVolatileLazyProxies()); for (const auto& proxy : fRecording->fVolatileLazyProxies) { if (!proxy->lazyInstantiate(resourceProvider)) { return false; } } return true; } void RecordingPriv::deinstantiateVolatileLazyProxies() { if (!this->hasVolatileLazyProxies()) { return; } for (const auto& proxy : fRecording->fVolatileLazyProxies) { SkASSERT(proxy->isVolatile()); proxy->deinstantiate(); } } void RecordingPriv::setFailureResultForFinishedProcs() { for (int i = 0; i < fRecording->fFinishedProcs.size(); ++i) { fRecording->fFinishedProcs[i]->setFailureResult(); } fRecording->fFinishedProcs.clear(); } bool RecordingPriv::addCommands(Context* context, CommandBuffer* commandBuffer, Surface* targetSurface, SkIVector targetTranslation) { AutoDeinstantiateTextureProxy autoDeinstantiateTargetProxy( fRecording->fTargetProxyData ? fRecording->fTargetProxyData->lazyProxy() : nullptr); const Texture* replayTarget = nullptr; ResourceProvider* resourceProvider = context->priv().resourceProvider(); SkASSERT(!SkToBool(fRecording->fTargetProxyData) || SkToBool(targetSurface)); if (fRecording->fTargetProxyData) { if (!targetSurface) { SKGPU_LOG_E("No surface provided to instantiate target texture proxy."); return false; } TextureProxy* surfaceTexture = targetSurface->backingTextureProxy(); if (!surfaceTexture->instantiate(resourceProvider)) { SKGPU_LOG_E("Could not instantiate target texture proxy."); return false; } if (!fRecording->fTargetProxyData->lazyInstantiate(resourceProvider, surfaceTexture->refTexture())) { SKGPU_LOG_E("Could not instantiate deferred texture proxy."); return false; } replayTarget = surfaceTexture->texture(); } for (size_t i = 0; i < fRecording->fExtraResourceRefs.size(); ++i) { commandBuffer->trackResource(fRecording->fExtraResourceRefs[i]); } // There's no need to differentiate kSuccess and kDiscard at the root list level; if every task // is discarded, the Recording will automatically be a no-op on replay while still correctly // notifying any finish procs the client may have added. if (fRecording->fRootTaskList->addCommands( context, commandBuffer, {replayTarget, targetTranslation}) == Task::Status::kFail) { return false; } for (int i = 0; i < fRecording->fFinishedProcs.size(); ++i) { commandBuffer->addFinishedProc(std::move(fRecording->fFinishedProcs[i])); } fRecording->fFinishedProcs.clear(); return true; } void RecordingPriv::addResourceRef(sk_sp resource) { fRecording->fExtraResourceRefs.push_back(std::move(resource)); } void RecordingPriv::addTask(sk_sp task) { fRecording->fRootTaskList->add(std::move(task)); } void RecordingPriv::addTasks(TaskList&& tasks) { fRecording->fRootTaskList->add(std::move(tasks)); } #if defined(GRAPHITE_TEST_UTILS) bool RecordingPriv::isTargetProxyInstantiated() const { return fRecording->fTargetProxyData->lazyProxy()->isInstantiated(); } int RecordingPriv::numVolatilePromiseImages() const { return fRecording->fVolatileLazyProxies.size(); } int RecordingPriv::numNonVolatilePromiseImages() const { return fRecording->fNonVolatileLazyProxies.size(); } bool RecordingPriv::hasTasks() const { return fRecording->fRootTaskList->hasTasks(); } #endif } // namespace skgpu::graphite