1 /*
2 * Copyright 2016 Google Inc.
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 "src/gpu/GrSurfaceProxy.h"
9 #include "src/gpu/GrSurfaceProxyPriv.h"
10
11 #include "include/gpu/GrRecordingContext.h"
12 #include "src/core/SkMathPriv.h"
13 #include "src/core/SkMipmap.h"
14 #include "src/gpu/GrAttachment.h"
15 #include "src/gpu/GrCaps.h"
16 #include "src/gpu/GrClip.h"
17 #include "src/gpu/GrGpuResourcePriv.h"
18 #include "src/gpu/GrOpsTask.h"
19 #include "src/gpu/GrProxyProvider.h"
20 #include "src/gpu/GrRecordingContextPriv.h"
21 #include "src/gpu/GrSurface.h"
22 #include "src/gpu/GrSurfaceDrawContext.h"
23 #include "src/gpu/GrTexture.h"
24 #include "src/gpu/GrTextureRenderTargetProxy.h"
25
26 #ifdef SK_DEBUG
27 #include "include/gpu/GrDirectContext.h"
28 #include "src/gpu/GrDirectContextPriv.h"
29 #include "src/gpu/GrRenderTarget.h"
30
is_valid_lazy(const SkISize & dimensions,SkBackingFit fit)31 static bool is_valid_lazy(const SkISize& dimensions, SkBackingFit fit) {
32 // A "fully" lazy proxy's width and height are not known until instantiation time.
33 // So fully lazy proxies are created with width and height < 0. Regular lazy proxies must be
34 // created with positive widths and heights. The width and height are set to 0 only after a
35 // failed instantiation. The former must be "approximate" fit while the latter can be either.
36 return ((dimensions.fWidth < 0 && dimensions.fHeight < 0 && SkBackingFit::kApprox == fit) ||
37 (dimensions.fWidth > 0 && dimensions.fHeight > 0));
38 }
39
is_valid_non_lazy(SkISize dimensions)40 static bool is_valid_non_lazy(SkISize dimensions) {
41 return dimensions.fWidth > 0 && dimensions.fHeight > 0;
42 }
43 #endif
44
45 // Deferred version
GrSurfaceProxy(const GrBackendFormat & format,SkISize dimensions,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags,UseAllocator useAllocator)46 GrSurfaceProxy::GrSurfaceProxy(const GrBackendFormat& format,
47 SkISize dimensions,
48 SkBackingFit fit,
49 SkBudgeted budgeted,
50 GrProtected isProtected,
51 GrInternalSurfaceFlags surfaceFlags,
52 UseAllocator useAllocator)
53 : fSurfaceFlags(surfaceFlags)
54 , fFormat(format)
55 , fDimensions(dimensions)
56 , fFit(fit)
57 , fBudgeted(budgeted)
58 , fUseAllocator(useAllocator)
59 , fIsProtected(isProtected) {
60 SkASSERT(fFormat.isValid());
61 SkASSERT(is_valid_non_lazy(dimensions));
62 }
63
64 // Lazy-callback version
GrSurfaceProxy(LazyInstantiateCallback && callback,const GrBackendFormat & format,SkISize dimensions,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags,UseAllocator useAllocator)65 GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback,
66 const GrBackendFormat& format,
67 SkISize dimensions,
68 SkBackingFit fit,
69 SkBudgeted budgeted,
70 GrProtected isProtected,
71 GrInternalSurfaceFlags surfaceFlags,
72 UseAllocator useAllocator)
73 : fSurfaceFlags(surfaceFlags)
74 , fFormat(format)
75 , fDimensions(dimensions)
76 , fFit(fit)
77 , fBudgeted(budgeted)
78 , fUseAllocator(useAllocator)
79 , fLazyInstantiateCallback(std::move(callback))
80 , fIsProtected(isProtected) {
81 SkASSERT(fFormat.isValid());
82 SkASSERT(fLazyInstantiateCallback);
83 SkASSERT(is_valid_lazy(dimensions, fit));
84 }
85
86 // Wrapped version
GrSurfaceProxy(sk_sp<GrSurface> surface,SkBackingFit fit,UseAllocator useAllocator)87 GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface,
88 SkBackingFit fit,
89 UseAllocator useAllocator)
90 : fTarget(std::move(surface))
91 , fSurfaceFlags(fTarget->flags())
92 , fFormat(fTarget->backendFormat())
93 , fDimensions(fTarget->dimensions())
94 , fFit(fit)
95 , fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
96 ? SkBudgeted::kYes
97 : SkBudgeted::kNo)
98 , fUseAllocator(useAllocator)
99 , fUniqueID(fTarget->uniqueID()) // Note: converting from unique resource ID to a proxy ID!
100 , fIsProtected(fTarget->isProtected() ? GrProtected::kYes : GrProtected::kNo) {
101 SkASSERT(fFormat.isValid());
102 }
103
~GrSurfaceProxy()104 GrSurfaceProxy::~GrSurfaceProxy() {
105 }
106
createSurfaceImpl(GrResourceProvider * resourceProvider,int sampleCnt,GrRenderable renderable,GrMipmapped mipMapped) const107 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
108 int sampleCnt,
109 GrRenderable renderable,
110 GrMipmapped mipMapped) const {
111 SkASSERT(mipMapped == GrMipmapped::kNo || fFit == SkBackingFit::kExact);
112 SkASSERT(!this->isLazy());
113 SkASSERT(!fTarget);
114
115 sk_sp<GrSurface> surface;
116 if (SkBackingFit::kApprox == fFit) {
117 surface = resourceProvider->createApproxTexture(fDimensions, fFormat, renderable, sampleCnt,
118 fIsProtected);
119 } else {
120 surface = resourceProvider->createTexture(fDimensions, fFormat, renderable, sampleCnt,
121 mipMapped, fBudgeted, fIsProtected);
122 }
123 if (!surface) {
124 return nullptr;
125 }
126
127 return surface;
128 }
129
canSkipResourceAllocator() const130 bool GrSurfaceProxy::canSkipResourceAllocator() const {
131 if (fUseAllocator == UseAllocator::kNo) {
132 // Usually an atlas or onFlush proxy
133 return true;
134 }
135
136 auto peek = this->peekSurface();
137 if (!peek) {
138 return false;
139 }
140 // If this resource is already allocated and not recyclable then the resource allocator does
141 // not need to do anything with it.
142 return !peek->resourcePriv().getScratchKey().isValid();
143 }
144
assign(sk_sp<GrSurface> surface)145 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
146 SkASSERT(!fTarget && surface);
147
148 SkDEBUGCODE(this->validateSurface(surface.get());)
149
150 fTarget = std::move(surface);
151
152 #ifdef SK_DEBUG
153 if (this->asRenderTargetProxy()) {
154 SkASSERT(fTarget->asRenderTarget());
155 }
156
157 // In order to give DDL users some flexibility in the destination of there DDLs,
158 // a DDL's target proxy can be more conservative (and thus require less memory)
159 // than the actual GrSurface used to fulfill it.
160 if (!this->isDDLTarget() && kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
161 // TODO(11373): Can this check be exact?
162 SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
163 }
164 #endif
165 }
166
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,GrRenderable renderable,GrMipmapped mipMapped,const GrUniqueKey * uniqueKey)167 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
168 GrRenderable renderable, GrMipmapped mipMapped,
169 const GrUniqueKey* uniqueKey) {
170 SkASSERT(!this->isLazy());
171 if (fTarget) {
172 if (uniqueKey && uniqueKey->isValid()) {
173 SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
174 }
175 return true;
176 }
177
178 sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, renderable,
179 mipMapped);
180 if (!surface) {
181 return false;
182 }
183
184 // If there was an invalidation message pending for this key, we might have just processed it,
185 // causing the key (stored on this proxy) to become invalid.
186 if (uniqueKey && uniqueKey->isValid()) {
187 resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
188 }
189
190 this->assign(std::move(surface));
191
192 return true;
193 }
194
deinstantiate()195 void GrSurfaceProxy::deinstantiate() {
196 SkASSERT(this->isInstantiated());
197 fTarget = nullptr;
198 }
199
computeScratchKey(const GrCaps & caps,GrScratchKey * key) const200 void GrSurfaceProxy::computeScratchKey(const GrCaps& caps, GrScratchKey* key) const {
201 SkASSERT(!this->isFullyLazy());
202 GrRenderable renderable = GrRenderable::kNo;
203 int sampleCount = 1;
204 if (const auto* rtp = this->asRenderTargetProxy()) {
205 renderable = GrRenderable::kYes;
206 sampleCount = rtp->numSamples();
207 }
208
209 const GrTextureProxy* tp = this->asTextureProxy();
210 GrMipmapped mipMapped = GrMipmapped::kNo;
211 if (tp) {
212 mipMapped = tp->mipmapped();
213 }
214
215 GrTexture::ComputeScratchKey(caps, this->backendFormat(), this->backingStoreDimensions(),
216 renderable, sampleCount, mipMapped, fIsProtected, key);
217 }
218
backingStoreDimensions() const219 SkISize GrSurfaceProxy::backingStoreDimensions() const {
220 SkASSERT(!this->isFullyLazy());
221 if (fTarget) {
222 return fTarget->dimensions();
223 }
224
225 if (SkBackingFit::kExact == fFit) {
226 return fDimensions;
227 }
228 return GrResourceProvider::MakeApprox(fDimensions);
229 }
230
isFunctionallyExact() const231 bool GrSurfaceProxy::isFunctionallyExact() const {
232 SkASSERT(!this->isFullyLazy());
233 return fFit == SkBackingFit::kExact ||
234 fDimensions == GrResourceProvider::MakeApprox(fDimensions);
235 }
236
isFormatCompressed(const GrCaps * caps) const237 bool GrSurfaceProxy::isFormatCompressed(const GrCaps* caps) const {
238 return caps->isFormatCompressed(this->backendFormat());
239 }
240
241 #ifdef SK_DEBUG
validate(GrContext_Base * context) const242 void GrSurfaceProxy::validate(GrContext_Base* context) const {
243 if (fTarget) {
244 SkASSERT(fTarget->getContext()->priv().matches(context));
245 }
246 }
247 #endif
248
Copy(GrRecordingContext * context,sk_sp<GrSurfaceProxy> src,GrSurfaceOrigin origin,GrMipmapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted,RectsMustMatch rectsMustMatch,sk_sp<GrRenderTask> * outTask)249 sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
250 sk_sp<GrSurfaceProxy> src,
251 GrSurfaceOrigin origin,
252 GrMipmapped mipMapped,
253 SkIRect srcRect,
254 SkBackingFit fit,
255 SkBudgeted budgeted,
256 RectsMustMatch rectsMustMatch,
257 sk_sp<GrRenderTask>* outTask) {
258 SkASSERT(!src->isFullyLazy());
259 int width;
260 int height;
261
262 SkIPoint dstPoint;
263 if (rectsMustMatch == RectsMustMatch::kYes) {
264 width = src->width();
265 height = src->height();
266 dstPoint = {srcRect.fLeft, srcRect.fTop};
267 } else {
268 width = srcRect.width();
269 height = srcRect.height();
270 dstPoint = {0, 0};
271 }
272
273 if (!srcRect.intersect(SkIRect::MakeSize(src->dimensions()))) {
274 return {};
275 }
276 auto format = src->backendFormat().makeTexture2D();
277 SkASSERT(format.isValid());
278
279 if (src->backendFormat().textureType() != GrTextureType::kExternal) {
280 GrImageInfo info(GrColorType::kUnknown, kUnknown_SkAlphaType, nullptr, {width, height});
281 auto dstContext = GrSurfaceContext::Make(context,
282 info,
283 format,
284 fit,
285 origin,
286 GrRenderable::kNo,
287 1,
288 mipMapped,
289 src->isProtected(),
290 budgeted);
291 sk_sp<GrRenderTask> copyTask;
292 if (dstContext && (copyTask = dstContext->copy(src, srcRect, dstPoint))) {
293 if (outTask) {
294 *outTask = std::move(copyTask);
295 }
296 return dstContext->asSurfaceProxyRef();
297 }
298 }
299 if (src->asTextureProxy()) {
300 auto dstContext = GrSurfaceFillContext::Make(context,
301 kUnknown_SkAlphaType,
302 nullptr,
303 {width, height},
304 fit,
305 format,
306 1,
307 mipMapped,
308 src->isProtected(),
309 GrSwizzle::RGBA(),
310 GrSwizzle::RGBA(),
311 origin,
312 budgeted);
313 GrSurfaceProxyView view(std::move(src), origin, GrSwizzle::RGBA());
314 if (dstContext && dstContext->blitTexture(std::move(view), srcRect, dstPoint)) {
315 if (outTask) {
316 *outTask = sk_ref_sp(dstContext->getOpsTask());
317 }
318 return dstContext->asSurfaceProxyRef();
319 }
320 }
321 // Can't use backend copies or draws.
322 return nullptr;
323 }
324
Copy(GrRecordingContext * context,sk_sp<GrSurfaceProxy> src,GrSurfaceOrigin origin,GrMipmapped mipMapped,SkBackingFit fit,SkBudgeted budgeted,sk_sp<GrRenderTask> * outTask)325 sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
326 sk_sp<GrSurfaceProxy> src,
327 GrSurfaceOrigin origin,
328 GrMipmapped mipMapped,
329 SkBackingFit fit,
330 SkBudgeted budgeted,
331 sk_sp<GrRenderTask>* outTask) {
332 SkASSERT(!src->isFullyLazy());
333 auto rect = SkIRect::MakeSize(src->dimensions());
334 return Copy(context,
335 std::move(src),
336 origin,
337 mipMapped,
338 rect,
339 fit,
340 budgeted,
341 RectsMustMatch::kNo,
342 outTask);
343 }
344
345 #if GR_TEST_UTILS
testingOnly_getBackingRefCnt() const346 int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
347 if (fTarget) {
348 return fTarget->testingOnly_getRefCnt();
349 }
350
351 return -1; // no backing GrSurface
352 }
353
testingOnly_getFlags() const354 GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
355 return fSurfaceFlags;
356 }
357
dump() const358 SkString GrSurfaceProxy::dump() const {
359 SkString tmp;
360
361 tmp.appendf("proxyID: %d - surfaceID: %d",
362 this->uniqueID().asUInt(),
363 this->peekSurface() ? this->peekSurface()->uniqueID().asUInt()
364 : -1);
365 return tmp;
366 }
367
368 #endif
369
exactify(bool allocatedCaseOnly)370 void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) {
371 SkASSERT(!fProxy->isFullyLazy());
372 if (this->isExact()) {
373 return;
374 }
375
376 SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
377
378 if (fProxy->fTarget) {
379 // The kApprox but already instantiated case. Setting the proxy's width & height to
380 // the instantiated width & height could have side-effects going forward, since we're
381 // obliterating the area of interest information. This call (exactify) only used
382 // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
383 // used for additional draws.
384 fProxy->fDimensions = fProxy->fTarget->dimensions();
385 return;
386 }
387
388 #ifndef SK_CRIPPLE_TEXTURE_REUSE
389 // In the post-implicit-allocation world we can't convert this proxy to be exact fit
390 // at this point. With explicit allocation switching this to exact will result in a
391 // different allocation at flush time. With implicit allocation, allocation would occur
392 // at draw time (rather than flush time) so this pathway was encountered less often (if
393 // at all).
394 if (allocatedCaseOnly) {
395 return;
396 }
397 #endif
398
399 // The kApprox uninstantiated case. Making this proxy be exact should be okay.
400 // It could mess things up if prior decisions were based on the approximate size.
401 fProxy->fFit = SkBackingFit::kExact;
402 // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
403 // already been computed we want to leave it alone so that amount will be removed when
404 // the special image goes away. If it hasn't been computed yet it might as well compute the
405 // exact amount.
406 }
407
doLazyInstantiation(GrResourceProvider * resourceProvider)408 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
409 SkASSERT(fProxy->isLazy());
410
411 sk_sp<GrSurface> surface;
412 if (const auto& uniqueKey = fProxy->getUniqueKey(); uniqueKey.isValid()) {
413 // First try to reattach to a cached version if the proxy is uniquely keyed
414 surface = resourceProvider->findByUniqueKey<GrSurface>(uniqueKey);
415 }
416
417 bool syncKey = true;
418 bool releaseCallback = false;
419 if (!surface) {
420 auto result = fProxy->fLazyInstantiateCallback(resourceProvider, fProxy->callbackDesc());
421 surface = std::move(result.fSurface);
422 syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
423 releaseCallback = surface && result.fReleaseCallback;
424 }
425 if (!surface) {
426 fProxy->fDimensions.setEmpty();
427 return false;
428 }
429
430 if (fProxy->isFullyLazy()) {
431 // This was a fully lazy proxy. We need to fill in the width & height. For partially
432 // lazy proxies we must preserve the original width & height since that indicates
433 // the content area.
434 fProxy->fDimensions = surface->dimensions();
435 }
436
437 SkASSERT(fProxy->width() <= surface->width());
438 SkASSERT(fProxy->height() <= surface->height());
439
440 if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
441 texProxy->setTargetKeySync(syncKey);
442 if (syncKey) {
443 const GrUniqueKey& key = texProxy->getUniqueKey();
444 if (key.isValid()) {
445 if (!surface->asTexture()->getUniqueKey().isValid()) {
446 // If 'surface' is newly created, attach the unique key
447 resourceProvider->assignUniqueKeyToResource(key, surface.get());
448 } else {
449 // otherwise we had better have reattached to a cached version
450 SkASSERT(surface->asTexture()->getUniqueKey() == key);
451 }
452 } else {
453 SkASSERT(!surface->getUniqueKey().isValid());
454 }
455 }
456 }
457
458 this->assign(std::move(surface));
459 if (releaseCallback) {
460 fProxy->fLazyInstantiateCallback = nullptr;
461 }
462
463 return true;
464 }
465
466 #ifdef SK_DEBUG
validateSurface(const GrSurface * surface)467 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
468 SkASSERTF(surface->backendFormat() == fFormat, "%s != %s",
469 surface->backendFormat().toStr().c_str(), fFormat.toStr().c_str());
470
471 this->onValidateSurface(surface);
472 }
473 #endif
474