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