1 /*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/gpu/graphite/Recording.h"
9
10 #include "src/core/SkChecksum.h"
11 #include "src/gpu/RefCntedCallback.h"
12 #include "src/gpu/graphite/CommandBuffer.h"
13 #include "src/gpu/graphite/ContextPriv.h"
14 #include "src/gpu/graphite/Log.h"
15 #include "src/gpu/graphite/RecordingPriv.h"
16 #include "src/gpu/graphite/Resource.h"
17 #include "src/gpu/graphite/ResourceProvider.h"
18 #include "src/gpu/graphite/Surface_Graphite.h"
19 #include "src/gpu/graphite/Texture.h"
20 #include "src/gpu/graphite/TextureProxy.h"
21 #include "src/gpu/graphite/task/TaskList.h"
22
23 #include <unordered_set>
24
25 using namespace skia_private;
26
27 namespace skgpu::graphite {
28
Recording(uint32_t uniqueID,uint32_t recorderID,std::unordered_set<sk_sp<TextureProxy>,ProxyHash> && nonVolatileLazyProxies,std::unordered_set<sk_sp<TextureProxy>,ProxyHash> && volatileLazyProxies,std::unique_ptr<LazyProxyData> targetProxyData,TArray<sk_sp<RefCntedCallback>> && finishedProcs)29 Recording::Recording(uint32_t uniqueID,
30 uint32_t recorderID,
31 std::unordered_set<sk_sp<TextureProxy>, ProxyHash>&& nonVolatileLazyProxies,
32 std::unordered_set<sk_sp<TextureProxy>, ProxyHash>&& volatileLazyProxies,
33 std::unique_ptr<LazyProxyData> targetProxyData,
34 TArray<sk_sp<RefCntedCallback>>&& finishedProcs)
35 : fUniqueID(uniqueID)
36 , fRecorderID(recorderID)
37 , fRootTaskList(new TaskList)
38 , fNonVolatileLazyProxies(std::move(nonVolatileLazyProxies))
39 , fVolatileLazyProxies(std::move(volatileLazyProxies))
40 , fTargetProxyData(std::move(targetProxyData))
41 , fFinishedProcs(std::move(finishedProcs)) {}
42
~Recording()43 Recording::~Recording() {
44 // Any finished procs that haven't been passed to a CommandBuffer fail
45 this->priv().setFailureResultForFinishedProcs();
46 }
47
operator ()(const sk_sp<TextureProxy> & proxy) const48 std::size_t Recording::ProxyHash::operator()(const sk_sp<TextureProxy> &proxy) const {
49 return SkGoodHash()(proxy.get());
50 }
51
LazyProxyData(const Caps * caps,SkISize dimensions,const TextureInfo & textureInfo)52 Recording::LazyProxyData::LazyProxyData(const Caps* caps,
53 SkISize dimensions,
54 const TextureInfo& textureInfo) {
55 auto onInstantiate = [this](ResourceProvider*) {
56 SkASSERT(SkToBool(fTarget));
57 return std::move(fTarget);
58 };
59
60 // If the texture info specifies that mipmapping is required, that implies that the final
61 // surface used to instantiate this proxy will be mipmapped, and that the dimensions of that
62 // surface are known already.
63 fTargetProxy = textureInfo.mipmapped() == Mipmapped::kYes
64 ? TextureProxy::MakeLazy(caps,
65 dimensions,
66 textureInfo,
67 skgpu::Budgeted::kNo,
68 Volatile::kYes,
69 std::move(onInstantiate))
70 : TextureProxy::MakeFullyLazy(textureInfo,
71 skgpu::Budgeted::kNo,
72 Volatile::kYes,
73 std::move(onInstantiate));
74 }
75
lazyProxy()76 TextureProxy* Recording::LazyProxyData::lazyProxy() { return fTargetProxy.get(); }
77
refLazyProxy()78 sk_sp<TextureProxy> Recording::LazyProxyData::refLazyProxy() { return fTargetProxy; }
79
lazyInstantiate(ResourceProvider * resourceProvider,sk_sp<Texture> texture)80 bool Recording::LazyProxyData::lazyInstantiate(ResourceProvider* resourceProvider,
81 sk_sp<Texture> texture) {
82 fTarget = std::move(texture);
83 return fTargetProxy->lazyInstantiate(resourceProvider);
84 }
85
86 ////////////////////////////////////////////////////////////////////////////////
hasNonVolatileLazyProxies() const87 bool RecordingPriv::hasNonVolatileLazyProxies() const {
88 return !fRecording->fNonVolatileLazyProxies.empty();
89 }
90
instantiateNonVolatileLazyProxies(ResourceProvider * resourceProvider)91 bool RecordingPriv::instantiateNonVolatileLazyProxies(ResourceProvider* resourceProvider) {
92 SkASSERT(this->hasNonVolatileLazyProxies());
93
94 for (const auto& proxy : fRecording->fNonVolatileLazyProxies) {
95 if (!proxy->lazyInstantiate(resourceProvider)) {
96 return false;
97 }
98 }
99
100 // Note: once all the lazy proxies have been instantiated, that's it - there are no more
101 // chances to instantiate.
102 fRecording->fNonVolatileLazyProxies.clear();
103 return true;
104 }
105
hasVolatileLazyProxies() const106 bool RecordingPriv::hasVolatileLazyProxies() const {
107 return !fRecording->fVolatileLazyProxies.empty();
108 }
109
instantiateVolatileLazyProxies(ResourceProvider * resourceProvider)110 bool RecordingPriv::instantiateVolatileLazyProxies(ResourceProvider* resourceProvider) {
111 SkASSERT(this->hasVolatileLazyProxies());
112
113 for (const auto& proxy : fRecording->fVolatileLazyProxies) {
114 if (!proxy->lazyInstantiate(resourceProvider)) {
115 return false;
116 }
117 }
118
119 return true;
120 }
121
deinstantiateVolatileLazyProxies()122 void RecordingPriv::deinstantiateVolatileLazyProxies() {
123 if (!this->hasVolatileLazyProxies()) {
124 return;
125 }
126
127 for (const auto& proxy : fRecording->fVolatileLazyProxies) {
128 SkASSERT(proxy->isVolatile());
129 proxy->deinstantiate();
130 }
131 }
132
setFailureResultForFinishedProcs()133 void RecordingPriv::setFailureResultForFinishedProcs() {
134 for (int i = 0; i < fRecording->fFinishedProcs.size(); ++i) {
135 fRecording->fFinishedProcs[i]->setFailureResult();
136 }
137 fRecording->fFinishedProcs.clear();
138 }
139
deferredTargetProxy()140 TextureProxy* RecordingPriv::deferredTargetProxy() {
141 return fRecording->fTargetProxyData ? fRecording->fTargetProxyData->lazyProxy() : nullptr;
142 }
143
setupDeferredTarget(ResourceProvider * resourceProvider,Surface * targetSurface,SkIVector targetTranslation,SkIRect targetClip)144 const Texture* RecordingPriv::setupDeferredTarget(ResourceProvider* resourceProvider,
145 Surface* targetSurface,
146 SkIVector targetTranslation,
147 SkIRect targetClip) {
148 SkASSERT(targetSurface && fRecording->fTargetProxyData);
149
150 TextureProxy* surfaceTexture = targetSurface->backingTextureProxy();
151 SkASSERT(surfaceTexture->isInstantiated());
152
153 const TextureProxy* targetProxy = fRecording->fTargetProxyData->lazyProxy();
154 if (surfaceTexture->mipmapped() != targetProxy->mipmapped()) {
155 SKGPU_LOG_E("Deferred canvas mipmap settings don't match instantiating target's.");
156 return nullptr;
157 }
158
159 // If the deferred canvas's texture proxy is not fully lazy, that means we used it for draws
160 // that require specific dimensions and no translation. The only time this happens is when a
161 // client requests a mipmapped deferred canvas and we automatically insert commands to
162 // regenerate mipmaps.
163 if (!targetProxy->isFullyLazy()) {
164 SkASSERT(targetProxy->mipmapped() == skgpu::Mipmapped::kYes);
165 if (targetProxy->dimensions() != surfaceTexture->dimensions()) {
166 SKGPU_LOG_E(
167 "Deferred canvas dimensions don't match instantiating target's dimensions.");
168 return nullptr;
169 }
170 if (!targetTranslation.isZero()) {
171 SKGPU_LOG_E(
172 "Replay translation is not allowed when replaying draws to a mipmapped "
173 "deferred canvas.");
174 return nullptr;
175 }
176 if (!targetClip.isEmpty()) {
177 SKGPU_LOG_E(
178 "Replay clip is not allowed when replaying draws to a mipmapped deferred "
179 "canvas.");
180 return nullptr;
181 }
182 }
183
184 if (!fRecording->fTargetProxyData->lazyInstantiate(resourceProvider,
185 surfaceTexture->refTexture())) {
186 SKGPU_LOG_E("Could not instantiate deferred texture proxy.");
187 return nullptr;
188 }
189 return surfaceTexture->texture();
190 }
191
addCommands(Context * context,CommandBuffer * commandBuffer,const Texture * replayTarget,SkIVector targetTranslation,SkIRect targetClip)192 bool RecordingPriv::addCommands(Context* context,
193 CommandBuffer* commandBuffer,
194 const Texture* replayTarget,
195 SkIVector targetTranslation,
196 SkIRect targetClip) {
197 for (size_t i = 0; i < fRecording->fExtraResourceRefs.size(); ++i) {
198 commandBuffer->trackResource(fRecording->fExtraResourceRefs[i]);
199 }
200
201 // There's no need to differentiate kSuccess and kDiscard at the root list level; if every task
202 // is discarded, the Recording will automatically be a no-op on replay while still correctly
203 // notifying any finish procs the client may have added.
204 if (fRecording->fRootTaskList->addCommands(
205 context, commandBuffer, {replayTarget, targetTranslation, targetClip}) ==
206 Task::Status::kFail) {
207 return false;
208 }
209 for (int i = 0; i < fRecording->fFinishedProcs.size(); ++i) {
210 commandBuffer->addFinishedProc(std::move(fRecording->fFinishedProcs[i]));
211 }
212 fRecording->fFinishedProcs.clear();
213
214 return true;
215 }
216
addResourceRef(sk_sp<Resource> resource)217 void RecordingPriv::addResourceRef(sk_sp<Resource> resource) {
218 fRecording->fExtraResourceRefs.push_back(std::move(resource));
219 }
220
221 #if defined(GPU_TEST_UTILS)
isTargetProxyInstantiated() const222 bool RecordingPriv::isTargetProxyInstantiated() const {
223 return fRecording->fTargetProxyData->lazyProxy()->isInstantiated();
224 }
225
numVolatilePromiseImages() const226 int RecordingPriv::numVolatilePromiseImages() const {
227 return fRecording->fVolatileLazyProxies.size();
228 }
229
numNonVolatilePromiseImages() const230 int RecordingPriv::numNonVolatilePromiseImages() const {
231 return fRecording->fNonVolatileLazyProxies.size();
232 }
233
hasTasks() const234 bool RecordingPriv::hasTasks() const {
235 return fRecording->fRootTaskList->hasTasks();
236 }
237 #endif
238
239 } // namespace skgpu::graphite
240