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