| /* | |
| * 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 "include/core/SkPoint.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/gpu/GpuTypes.h" | |
| #include "include/gpu/graphite/GraphiteTypes.h" | |
| #include "include/gpu/graphite/TextureInfo.h" | |
| #include "include/private/SkAssert.h" | |
| #include "include/private/SkLog.h" | |
| #include "include/private/SkTo.h" | |
| #include "src/core/SkChecksum.h" | |
| #include "src/gpu/RefCntedCallback.h" | |
| #include "src/gpu/graphite/CommandBuffer.h" | |
| #include "src/gpu/graphite/RecordingPriv.h" | |
| #include "src/gpu/graphite/Resource.h" | |
| #include "src/gpu/graphite/RuntimeEffectDictionary.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/Task.h" | |
| #include "src/gpu/graphite/task/TaskList.h" | |
| #include <functional> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| namespace skgpu::graphite { class Context; } | |
| using namespace skia_private; | |
| namespace skgpu::graphite { | |
| Recording::Recording(uint32_t uniqueID, | |
| uint32_t recorderID, | |
| std::unique_ptr<LazyProxyData> targetProxyData, | |
| TArray<sk_sp<RefCntedCallback>>&& finishedProcs) | |
| : fUniqueID(uniqueID) | |
| , fRecorderID(recorderID) | |
| , fRootTaskList(new TaskList) | |
| , 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<TextureProxy> &proxy) const { | |
| return SkGoodHash()(proxy.get()); | |
| } | |
| Recording::LazyProxyData::LazyProxyData(const Caps* caps, | |
| SkISize dimensions, | |
| const TextureInfo& textureInfo) { | |
| auto onInstantiate = [this](ResourceProvider*) { | |
| SkASSERT(SkToBool(fTarget)); | |
| return std::move(fTarget); | |
| }; | |
| // If the texture info specifies that mipmapping is required, that implies that the final | |
| // surface used to instantiate this proxy will be mipmapped, and that the dimensions of that | |
| // surface are known already. | |
| fTargetProxy = textureInfo.mipmapped() == Mipmapped::kYes | |
| ? TextureProxy::MakeLazy(caps, | |
| dimensions, | |
| textureInfo, | |
| skgpu::Budgeted::kNo, | |
| Volatile::kYes, | |
| std::move(onInstantiate)) | |
| : TextureProxy::MakeFullyLazy(textureInfo, | |
| skgpu::Budgeted::kNo, | |
| Volatile::kYes, | |
| std::move(onInstantiate)); | |
| } | |
| Recording::LazyProxyData::~LazyProxyData() = default; | |
| TextureProxy* Recording::LazyProxyData::lazyProxy() { return fTargetProxy.get(); } | |
| sk_sp<TextureProxy> Recording::LazyProxyData::refLazyProxy() { return fTargetProxy; } | |
| bool Recording::LazyProxyData::lazyInstantiate(ResourceProvider* resourceProvider, | |
| sk_sp<Texture> 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(); | |
| } | |
| void RecordingPriv::moveFinishedProcsTo(skia_private::TArray<sk_sp<RefCntedCallback>>& dst) { | |
| dst.move_back(fRecording->fFinishedProcs); | |
| } | |
| TextureProxy* RecordingPriv::deferredTargetProxy() { | |
| return fRecording->fTargetProxyData ? fRecording->fTargetProxyData->lazyProxy() : nullptr; | |
| } | |
| const Texture* RecordingPriv::setupDeferredTarget(ResourceProvider* resourceProvider, | |
| Surface* targetSurface, | |
| SkIVector targetTranslation, | |
| SkIRect targetClip) { | |
| SkASSERT(targetSurface && fRecording->fTargetProxyData); | |
| TextureProxy* surfaceTexture = targetSurface->target().proxy(); | |
| SkASSERT(surfaceTexture->isInstantiated()); | |
| const TextureProxy* targetProxy = fRecording->fTargetProxyData->lazyProxy(); | |
| if (surfaceTexture->mipmapped() != targetProxy->mipmapped()) { | |
| SKIA_LOG_E("Deferred canvas mipmap settings don't match instantiating target's."); | |
| return nullptr; | |
| } | |
| // If the deferred canvas's texture proxy is not fully lazy, that means we used it for draws | |
| // that require specific dimensions and no translation. The only time this happens is when a | |
| // client requests a mipmapped deferred canvas and we automatically insert commands to | |
| // regenerate mipmaps. | |
| if (!targetProxy->isFullyLazy()) { | |
| SkASSERT(targetProxy->mipmapped() == skgpu::Mipmapped::kYes); | |
| if (targetProxy->dimensions() != surfaceTexture->dimensions()) { | |
| SKIA_LOG_E( | |
| "Deferred canvas dimensions don't match instantiating target's dimensions."); | |
| return nullptr; | |
| } | |
| if (!targetTranslation.isZero()) { | |
| SKIA_LOG_E( | |
| "Replay translation is not allowed when replaying draws to a mipmapped " | |
| "deferred canvas."); | |
| return nullptr; | |
| } | |
| if (!targetClip.isEmpty()) { | |
| SKIA_LOG_E( | |
| "Replay clip is not allowed when replaying draws to a mipmapped deferred " | |
| "canvas."); | |
| return nullptr; | |
| } | |
| } | |
| if (!fRecording->fTargetProxyData->lazyInstantiate(resourceProvider, | |
| surfaceTexture->refTexture())) { | |
| SKIA_LOG_E("Could not instantiate deferred texture proxy."); | |
| return nullptr; | |
| } | |
| return surfaceTexture->texture(); | |
| } | |
| bool RecordingPriv::prepareResources(ResourceProvider* resourceProvider, | |
| ScratchResourceManager* scratchManager, | |
| sk_sp<const RuntimeEffectDictionary> rteDict) { | |
| Task::Status status = fRecording->fRootTaskList->prepareResources( | |
| resourceProvider, scratchManager, rteDict); | |
| if (status == Task::Status::kSuccess) { | |
| fRecording->fRootTaskList->visitProxies([&](const TextureProxy* proxy) { | |
| if (proxy->isLazy()) { | |
| if (proxy->isVolatile()) { | |
| fRecording->fVolatileLazyProxies.insert(sk_ref_sp(proxy)); | |
| } else { | |
| fRecording->fNonVolatileLazyProxies.insert(sk_ref_sp(proxy)); | |
| } | |
| } | |
| return true; | |
| }, /*readsOnly=*/false); | |
| } | |
| return status != Task::Status::kFail; | |
| } | |
| bool RecordingPriv::addCommands(Context* context, | |
| CommandBuffer* commandBuffer, | |
| const Texture* replayTarget, | |
| SkIVector targetTranslation, | |
| SkIRect targetClip) { | |
| 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, targetClip}) == | |
| Task::Status::kFail) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| void RecordingPriv::addResourceRef(sk_sp<Resource> resource) { | |
| fRecording->fExtraResourceRefs.push_back(std::move(resource)); | |
| } | |
| #if defined(GPU_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 |