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/GrStencilAttachment.h"
23 #include "src/gpu/GrSurfacePriv.h"
24 #include "src/gpu/GrTextureContext.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 GrSurfaceDesc & desc,SkBackingFit fit)32 static bool is_valid_lazy(const GrSurfaceDesc& desc, 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 desc.fConfig != kUnknown_GrPixelConfig &&
38            ((desc.fWidth < 0 && desc.fHeight < 0 && SkBackingFit::kApprox == fit) ||
39             (desc.fWidth > 0 && desc.fHeight > 0));
40 }
41 
is_valid_non_lazy(const GrSurfaceDesc & desc)42 static bool is_valid_non_lazy(const GrSurfaceDesc& desc) {
43     return desc.fWidth > 0 && desc.fHeight > 0 && desc.fConfig != kUnknown_GrPixelConfig;
44 }
45 #endif
46 
47 // Deferred version
GrSurfaceProxy(const GrBackendFormat & format,const GrSurfaceDesc & desc,GrRenderable renderable,GrSurfaceOrigin origin,const GrSwizzle & textureSwizzle,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags,UseAllocator useAllocator)48 GrSurfaceProxy::GrSurfaceProxy(const GrBackendFormat& format,
49                                const GrSurfaceDesc& desc,
50                                GrRenderable renderable,
51                                GrSurfaceOrigin origin,
52                                const GrSwizzle& textureSwizzle,
53                                SkBackingFit fit,
54                                SkBudgeted budgeted,
55                                GrProtected isProtected,
56                                GrInternalSurfaceFlags surfaceFlags,
57                                UseAllocator useAllocator)
58         : fSurfaceFlags(surfaceFlags)
59         , fFormat(format)
60         , fConfig(desc.fConfig)
61         , fWidth(desc.fWidth)
62         , fHeight(desc.fHeight)
63         , fOrigin(origin)
64         , fTextureSwizzle(textureSwizzle)
65         , fFit(fit)
66         , fBudgeted(budgeted)
67         , fUseAllocator(useAllocator)
68         , fIsProtected(isProtected)
69         , fGpuMemorySize(kInvalidGpuMemorySize) {
70     SkASSERT(fFormat.isValid());
71     SkASSERT(is_valid_non_lazy(desc));
72     if (GrPixelConfigIsCompressed(desc.fConfig)) {
73         SkASSERT(renderable == GrRenderable::kNo);
74         fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
75     }
76 }
77 
78 // Lazy-callback version
GrSurfaceProxy(LazyInstantiateCallback && callback,const GrBackendFormat & format,const GrSurfaceDesc & desc,GrRenderable renderable,GrSurfaceOrigin origin,const GrSwizzle & textureSwizzle,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags,UseAllocator useAllocator)79 GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback,
80                                const GrBackendFormat& format,
81                                const GrSurfaceDesc& desc,
82                                GrRenderable renderable,
83                                GrSurfaceOrigin origin,
84                                const GrSwizzle& textureSwizzle,
85                                SkBackingFit fit,
86                                SkBudgeted budgeted,
87                                GrProtected isProtected,
88                                GrInternalSurfaceFlags surfaceFlags,
89                                UseAllocator useAllocator)
90         : fSurfaceFlags(surfaceFlags)
91         , fFormat(format)
92         , fConfig(desc.fConfig)
93         , fWidth(desc.fWidth)
94         , fHeight(desc.fHeight)
95         , fOrigin(origin)
96         , fTextureSwizzle(textureSwizzle)
97         , fFit(fit)
98         , fBudgeted(budgeted)
99         , fUseAllocator(useAllocator)
100         , fLazyInstantiateCallback(std::move(callback))
101         , fIsProtected(isProtected)
102         , fGpuMemorySize(kInvalidGpuMemorySize) {
103     SkASSERT(fFormat.isValid());
104     SkASSERT(fLazyInstantiateCallback);
105     SkASSERT(is_valid_lazy(desc, fit));
106     if (GrPixelConfigIsCompressed(desc.fConfig)) {
107         SkASSERT(renderable == GrRenderable::kNo);
108         fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
109     }
110 }
111 
112 // Wrapped version
GrSurfaceProxy(sk_sp<GrSurface> surface,GrSurfaceOrigin origin,const GrSwizzle & textureSwizzle,SkBackingFit fit,UseAllocator useAllocator)113 GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface,
114                                GrSurfaceOrigin origin,
115                                const GrSwizzle& textureSwizzle,
116                                SkBackingFit fit,
117                                UseAllocator useAllocator)
118         : fTarget(std::move(surface))
119         , fSurfaceFlags(fTarget->surfacePriv().flags())
120         , fFormat(fTarget->backendFormat())
121         , fConfig(fTarget->config())
122         , fWidth(fTarget->width())
123         , fHeight(fTarget->height())
124         , fOrigin(origin)
125         , fTextureSwizzle(textureSwizzle)
126         , fFit(fit)
127         , fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
128                             ? SkBudgeted::kYes
129                             : SkBudgeted::kNo)
130         , fUseAllocator(useAllocator)
131         , fUniqueID(fTarget->uniqueID())  // Note: converting from unique resource ID to a proxy ID!
132         , fIsProtected(fTarget->isProtected() ? GrProtected::kYes : GrProtected::kNo)
133         , fGpuMemorySize(kInvalidGpuMemorySize) {
134     SkASSERT(fFormat.isValid());
135 }
136 
~GrSurfaceProxy()137 GrSurfaceProxy::~GrSurfaceProxy() {
138     // For this to be deleted the opsTask that held a ref on it (if there was one) must have been
139     // deleted. Which would have cleared out this back pointer.
140     SkASSERT(!fLastRenderTask);
141 }
142 
AttachStencilIfNeeded(GrResourceProvider * resourceProvider,GrSurface * surface,int minStencilSampleCount)143 bool GrSurfaceProxyPriv::AttachStencilIfNeeded(GrResourceProvider* resourceProvider,
144                                                GrSurface* surface, int minStencilSampleCount) {
145     if (minStencilSampleCount) {
146         GrRenderTarget* rt = surface->asRenderTarget();
147         if (!rt) {
148             SkASSERT(0);
149             return false;
150         }
151 
152         if (!resourceProvider->attachStencilAttachment(rt, minStencilSampleCount)) {
153             return false;
154         }
155     }
156 
157     return true;
158 }
159 
createSurfaceImpl(GrResourceProvider * resourceProvider,int sampleCnt,int minStencilSampleCount,GrRenderable renderable,GrMipMapped mipMapped) const160 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
161                                                    int sampleCnt,
162                                                    int minStencilSampleCount,
163                                                    GrRenderable renderable,
164                                                    GrMipMapped mipMapped) const {
165     SkASSERT(mipMapped == GrMipMapped::kNo || fFit == SkBackingFit::kExact);
166     SkASSERT(!this->isLazy());
167     SkASSERT(!fTarget);
168     GrSurfaceDesc desc;
169     desc.fWidth = fWidth;
170     desc.fHeight = fHeight;
171     desc.fConfig = fConfig;
172 
173     sk_sp<GrSurface> surface;
174     if (SkBackingFit::kApprox == fFit) {
175         surface = resourceProvider->createApproxTexture(desc, fFormat, renderable, sampleCnt,
176                                                         fIsProtected);
177     } else {
178         surface = resourceProvider->createTexture(desc, fFormat, renderable, sampleCnt, mipMapped,
179                                                   fBudgeted, fIsProtected);
180     }
181     if (!surface) {
182         return nullptr;
183     }
184 
185     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(),
186                                                    minStencilSampleCount)) {
187         return nullptr;
188     }
189 
190     return surface;
191 }
192 
canSkipResourceAllocator() const193 bool GrSurfaceProxy::canSkipResourceAllocator() const {
194     if (fUseAllocator == UseAllocator::kNo) {
195         // Usually an atlas or onFlush proxy
196         return true;
197     }
198 
199     auto peek = this->peekSurface();
200     if (!peek) {
201         return false;
202     }
203     // If this resource is already allocated and not recyclable then the resource allocator does
204     // not need to do anything with it.
205     return !peek->resourcePriv().getScratchKey().isValid();
206 }
207 
assign(sk_sp<GrSurface> surface)208 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
209     SkASSERT(!fTarget && surface);
210 
211     SkDEBUGCODE(this->validateSurface(surface.get());)
212 
213     fTarget = std::move(surface);
214 
215 #ifdef SK_DEBUG
216     if (this->asRenderTargetProxy()) {
217         SkASSERT(fTarget->asRenderTarget());
218         if (int minStencilSampleCount = this->asRenderTargetProxy()->numStencilSamples()) {
219             auto* stencil = fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment();
220             SkASSERT(stencil);
221             SkASSERT(stencil->numSamples() >= minStencilSampleCount);
222         }
223     }
224 
225     if (kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
226         SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
227     }
228 #endif
229 }
230 
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,int minStencilSampleCount,GrRenderable renderable,GrMipMapped mipMapped,const GrUniqueKey * uniqueKey)231 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
232                                      int minStencilSampleCount, GrRenderable renderable,
233                                      GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
234     SkASSERT(!this->isLazy());
235     if (fTarget) {
236         if (uniqueKey && uniqueKey->isValid()) {
237             SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
238         }
239         return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget.get(),
240                                                          minStencilSampleCount);
241     }
242 
243     sk_sp<GrSurface> surface = this->createSurfaceImpl(
244             resourceProvider, sampleCnt, minStencilSampleCount, renderable, mipMapped);
245     if (!surface) {
246         return false;
247     }
248 
249     // If there was an invalidation message pending for this key, we might have just processed it,
250     // causing the key (stored on this proxy) to become invalid.
251     if (uniqueKey && uniqueKey->isValid()) {
252         resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
253     }
254 
255     this->assign(std::move(surface));
256 
257     return true;
258 }
259 
deinstantiate()260 void GrSurfaceProxy::deinstantiate() {
261     SkASSERT(this->isInstantiated());
262     fTarget = nullptr;
263 }
264 
computeScratchKey(GrScratchKey * key) const265 void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
266     SkASSERT(!this->isFullyLazy());
267     GrRenderable renderable = GrRenderable::kNo;
268     int sampleCount = 1;
269     if (const auto* rtp = this->asRenderTargetProxy()) {
270         renderable = GrRenderable::kYes;
271         sampleCount = rtp->numSamples();
272     }
273 
274     const GrTextureProxy* tp = this->asTextureProxy();
275     GrMipMapped mipMapped = GrMipMapped::kNo;
276     if (tp) {
277         mipMapped = tp->mipMapped();
278     }
279 
280     int width = this->worstCaseWidth();
281     int height = this->worstCaseHeight();
282 
283     GrTexturePriv::ComputeScratchKey(this->config(), width, height, renderable, sampleCount,
284                                      mipMapped, fIsProtected, key);
285 }
286 
setLastRenderTask(GrRenderTask * renderTask)287 void GrSurfaceProxy::setLastRenderTask(GrRenderTask* renderTask) {
288 #ifdef SK_DEBUG
289     if (fLastRenderTask) {
290         SkASSERT(fLastRenderTask->isClosed());
291     }
292 #endif
293 
294     // Un-reffed
295     fLastRenderTask = renderTask;
296 }
297 
getLastOpsTask()298 GrOpsTask* GrSurfaceProxy::getLastOpsTask() {
299     return fLastRenderTask ? fLastRenderTask->asOpsTask() : nullptr;
300 }
301 
worstCaseWidth() const302 int GrSurfaceProxy::worstCaseWidth() const {
303     SkASSERT(!this->isFullyLazy());
304     if (fTarget) {
305         return fTarget->width();
306     }
307 
308     if (SkBackingFit::kExact == fFit) {
309         return fWidth;
310     }
311     return GrResourceProvider::MakeApprox(fWidth);
312 }
313 
worstCaseHeight() const314 int GrSurfaceProxy::worstCaseHeight() const {
315     SkASSERT(!this->isFullyLazy());
316     if (fTarget) {
317         return fTarget->height();
318     }
319 
320     if (SkBackingFit::kExact == fFit) {
321         return fHeight;
322     }
323     return GrResourceProvider::MakeApprox(fHeight);
324 }
325 
326 #ifdef SK_DEBUG
validate(GrContext_Base * context) const327 void GrSurfaceProxy::validate(GrContext_Base* context) const {
328     if (fTarget) {
329         SkASSERT(fTarget->getContext() == context);
330     }
331 }
332 #endif
333 
Copy(GrRecordingContext * context,GrSurfaceProxy * src,GrColorType srcColorType,GrMipMapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted,RectsMustMatch rectsMustMatch)334 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
335                                            GrSurfaceProxy* src,
336                                            GrColorType srcColorType,
337                                            GrMipMapped mipMapped,
338                                            SkIRect srcRect,
339                                            SkBackingFit fit,
340                                            SkBudgeted budgeted,
341                                            RectsMustMatch rectsMustMatch) {
342     SkASSERT(!src->isFullyLazy());
343     GrProtected isProtected = src->isProtected() ? GrProtected::kYes : GrProtected::kNo;
344     int width;
345     int height;
346 
347     SkIPoint dstPoint;
348     if (rectsMustMatch == RectsMustMatch::kYes) {
349         width = src->width();
350         height = src->height();
351         dstPoint = {srcRect.fLeft, srcRect.fTop};
352     } else {
353         width = srcRect.width();
354         height = srcRect.height();
355         dstPoint = {0, 0};
356     }
357 
358     if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
359         return nullptr;
360     }
361     auto colorType = GrPixelConfigToColorType(src->config());
362     if (src->backendFormat().textureType() != GrTextureType::kExternal) {
363         auto dstContext = context->priv().makeDeferredTextureContext(
364                 fit, width, height, colorType, kUnknown_SkAlphaType, nullptr, mipMapped,
365                 src->origin(), budgeted, isProtected);
366         if (!dstContext) {
367             return nullptr;
368         }
369         if (dstContext->copy(src, srcRect, dstPoint)) {
370             return dstContext->asTextureProxyRef();
371         }
372     }
373     if (src->asTextureProxy()) {
374         auto dstContext = context->priv().makeDeferredRenderTargetContext(
375                 fit, width, height, colorType, nullptr, 1, mipMapped, src->origin(), nullptr,
376                 budgeted);
377 
378         if (dstContext && dstContext->blitTexture(src->asTextureProxy(), srcColorType, srcRect,
379                                                   dstPoint)) {
380             return dstContext->asTextureProxyRef();
381         }
382     }
383     // Can't use backend copies or draws.
384     return nullptr;
385 }
386 
Copy(GrRecordingContext * context,GrSurfaceProxy * src,GrColorType srcColorType,GrMipMapped mipMapped,SkBackingFit fit,SkBudgeted budgeted)387 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context, GrSurfaceProxy* src,
388                                            GrColorType srcColorType, GrMipMapped mipMapped,
389                                            SkBackingFit fit, SkBudgeted budgeted) {
390     SkASSERT(!src->isFullyLazy());
391     return Copy(context, src, srcColorType, mipMapped, SkIRect::MakeWH(src->width(), src->height()),
392                 fit, budgeted);
393 }
394 
395 #if GR_TEST_UTILS
testingOnly_getBackingRefCnt() const396 int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
397     if (fTarget) {
398         return fTarget->testingOnly_getRefCnt();
399     }
400 
401     return -1; // no backing GrSurface
402 }
403 
testingOnly_getFlags() const404 GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
405     return fSurfaceFlags;
406 }
407 #endif
408 
exactify(bool allocatedCaseOnly)409 void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) {
410     SkASSERT(!fProxy->isFullyLazy());
411     if (this->isExact()) {
412         return;
413     }
414 
415     SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
416 
417     if (fProxy->fTarget) {
418         // The kApprox but already instantiated case. Setting the proxy's width & height to
419         // the instantiated width & height could have side-effects going forward, since we're
420         // obliterating the area of interest information. This call (exactify) only used
421         // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
422         // used for additional draws.
423         fProxy->fWidth = fProxy->fTarget->width();
424         fProxy->fHeight = fProxy->fTarget->height();
425         return;
426     }
427 
428 #ifndef SK_CRIPPLE_TEXTURE_REUSE
429     // In the post-implicit-allocation world we can't convert this proxy to be exact fit
430     // at this point. With explicit allocation switching this to exact will result in a
431     // different allocation at flush time. With implicit allocation, allocation would occur
432     // at draw time (rather than flush time) so this pathway was encountered less often (if
433     // at all).
434     if (allocatedCaseOnly) {
435         return;
436     }
437 #endif
438 
439     // The kApprox uninstantiated case. Making this proxy be exact should be okay.
440     // It could mess things up if prior decisions were based on the approximate size.
441     fProxy->fFit = SkBackingFit::kExact;
442     // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
443     // already been computed we want to leave it alone so that amount will be removed when
444     // the special image goes away. If it hasn't been computed yet it might as well compute the
445     // exact amount.
446 }
447 
doLazyInstantiation(GrResourceProvider * resourceProvider)448 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
449     SkASSERT(fProxy->isLazy());
450 
451     sk_sp<GrSurface> surface;
452     if (fProxy->asTextureProxy() && fProxy->asTextureProxy()->getUniqueKey().isValid()) {
453         // First try to reattach to a cached version if the proxy is uniquely keyed
454         surface = resourceProvider->findByUniqueKey<GrSurface>(
455                                                         fProxy->asTextureProxy()->getUniqueKey());
456     }
457 
458     bool syncKey = true;
459     bool releaseCallback = false;
460     if (!surface) {
461         auto result = fProxy->fLazyInstantiateCallback(resourceProvider);
462         surface = std::move(result.fSurface);
463         syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
464         releaseCallback = surface && result.fReleaseCallback;
465     }
466     if (!surface) {
467         fProxy->fWidth = 0;
468         fProxy->fHeight = 0;
469         return false;
470     }
471 
472     if (fProxy->isFullyLazy()) {
473         // This was a fully lazy proxy. We need to fill in the width & height. For partially
474         // lazy proxies we must preserve the original width & height since that indicates
475         // the content area.
476         fProxy->fWidth = surface->width();
477         fProxy->fHeight = surface->height();
478     }
479 
480     SkASSERT(fProxy->fWidth <= surface->width());
481     SkASSERT(fProxy->fHeight <= surface->height());
482 
483     auto rt = fProxy->asRenderTargetProxy();
484     int minStencilSampleCount = rt ? rt->numSamples() : 0;
485 
486     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
487             resourceProvider, surface.get(), minStencilSampleCount)) {
488         return false;
489     }
490 
491     if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
492         texProxy->setTargetKeySync(syncKey);
493         if (syncKey) {
494             const GrUniqueKey& key = texProxy->getUniqueKey();
495             if (key.isValid()) {
496                 if (!surface->asTexture()->getUniqueKey().isValid()) {
497                     // If 'surface' is newly created, attach the unique key
498                     resourceProvider->assignUniqueKeyToResource(key, surface.get());
499                 } else {
500                     // otherwise we had better have reattached to a cached version
501                     SkASSERT(surface->asTexture()->getUniqueKey() == key);
502                 }
503             } else {
504                 SkASSERT(!surface->getUniqueKey().isValid());
505             }
506         }
507     }
508 
509     this->assign(std::move(surface));
510     if (releaseCallback) {
511         fProxy->fLazyInstantiateCallback = nullptr;
512     }
513 
514     return true;
515 }
516 
517 #ifdef SK_DEBUG
validateSurface(const GrSurface * surface)518 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
519     SkASSERT(surface->config() == fConfig);
520 
521     this->onValidateSurface(surface);
522 }
523 #endif
524