blob: af12445bed0c1d0884d9b7ad84fc8be07c1e13dd [file] [log] [blame]
/*
* 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/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/base/SkAssert.h"
#include "include/private/base/SkPoint_impl.h"
#include "include/private/base/SkTo.h"
#include "src/core/SkChecksum.h"
#include "src/gpu/RefCntedCallback.h"
#include "src/gpu/graphite/CommandBuffer.h"
#include "src/gpu/graphite/Log.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();
}
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->backingTextureProxy();
SkASSERT(surfaceTexture->isInstantiated());
const TextureProxy* targetProxy = fRecording->fTargetProxyData->lazyProxy();
if (surfaceTexture->mipmapped() != targetProxy->mipmapped()) {
SKGPU_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()) {
SKGPU_LOG_E(
"Deferred canvas dimensions don't match instantiating target's dimensions.");
return nullptr;
}
if (!targetTranslation.isZero()) {
SKGPU_LOG_E(
"Replay translation is not allowed when replaying draws to a mipmapped "
"deferred canvas.");
return nullptr;
}
if (!targetClip.isEmpty()) {
SKGPU_LOG_E(
"Replay clip is not allowed when replaying draws to a mipmapped deferred "
"canvas.");
return nullptr;
}
}
if (!fRecording->fTargetProxyData->lazyInstantiate(resourceProvider,
surfaceTexture->refTexture())) {
SKGPU_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;
});
}
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;
}
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> 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