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 "GrSurfaceProxy.h"
9 #include "GrSurfaceProxyPriv.h"
10 
11 #include "GrCaps.h"
12 #include "GrContext.h"
13 #include "GrContextPriv.h"
14 #include "GrGpuResourcePriv.h"
15 #include "GrOpList.h"
16 #include "GrProxyProvider.h"
17 #include "GrSurfaceContext.h"
18 #include "GrSurfacePriv.h"
19 #include "GrTexturePriv.h"
20 #include "GrTextureRenderTargetProxy.h"
21 
22 #include "SkMathPriv.h"
23 #include "SkMipMap.h"
24 
25 #ifdef SK_DEBUG
26 #include "GrRenderTarget.h"
27 #include "GrRenderTargetPriv.h"
28 
29 static bool is_valid_fully_lazy(const GrSurfaceDesc& desc, SkBackingFit fit) {
30     return desc.fWidth <= 0 &&
31            desc.fHeight <= 0 &&
32            desc.fConfig != kUnknown_GrPixelConfig &&
33            desc.fSampleCnt == 1 &&
34            SkBackingFit::kApprox == fit;
35 }
36 
37 static bool is_valid_partially_lazy(const GrSurfaceDesc& desc) {
38     return ((desc.fWidth > 0 && desc.fHeight > 0) ||
39             (desc.fWidth <= 0 && desc.fHeight <= 0))  &&
40            desc.fConfig != kUnknown_GrPixelConfig;
41 }
42 
43 static bool is_valid_non_lazy(const GrSurfaceDesc& desc) {
44     return desc.fWidth > 0 &&
45            desc.fHeight > 0 &&
46            desc.fConfig != kUnknown_GrPixelConfig;
47 }
48 #endif
49 
50 // Lazy-callback version
51 GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback, LazyInstantiationType lazyType,
52                                const GrBackendFormat& format, const GrSurfaceDesc& desc,
53                                GrSurfaceOrigin origin, SkBackingFit fit,
54                                SkBudgeted budgeted, GrInternalSurfaceFlags surfaceFlags)
55         : fSurfaceFlags(surfaceFlags)
56         , fFormat(format)
57         , fConfig(desc.fConfig)
58         , fWidth(desc.fWidth)
59         , fHeight(desc.fHeight)
60         , fOrigin(origin)
61         , fFit(fit)
62         , fBudgeted(budgeted)
63         , fLazyInstantiateCallback(std::move(callback))
64         , fLazyInstantiationType(lazyType)
65         , fNeedsClear(SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag))
66         , fGpuMemorySize(kInvalidGpuMemorySize)
67         , fLastOpList(nullptr) {
68     SkASSERT(fFormat.isValid());
69     // NOTE: the default fUniqueID ctor pulls a value from the same pool as the GrGpuResources.
70     if (fLazyInstantiateCallback) {
71         SkASSERT(is_valid_fully_lazy(desc, fit) || is_valid_partially_lazy(desc));
72     } else {
73         SkASSERT(is_valid_non_lazy(desc));
74     }
75 
76     if (GrPixelConfigIsCompressed(desc.fConfig)) {
77         SkASSERT(!SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag));
78         fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
79     }
80 }
81 
82 // Wrapped version
83 GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, GrSurfaceOrigin origin, SkBackingFit fit)
84         : INHERITED(std::move(surface))
85         , fSurfaceFlags(fTarget->surfacePriv().flags())
86         , fFormat(fTarget->backendFormat())
87         , fConfig(fTarget->config())
88         , fWidth(fTarget->width())
89         , fHeight(fTarget->height())
90         , fOrigin(origin)
91         , fFit(fit)
92         , fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
93                             ? SkBudgeted::kYes
94                             : SkBudgeted::kNo)
95         , fUniqueID(fTarget->uniqueID())  // Note: converting from unique resource ID to a proxy ID!
96         , fNeedsClear(false)
97         , fGpuMemorySize(kInvalidGpuMemorySize)
98         , fLastOpList(nullptr) {
99     SkASSERT(fFormat.isValid());
100 }
101 
102 GrSurfaceProxy::~GrSurfaceProxy() {
103     if (fLazyInstantiateCallback) {
104         // We call the callback with a null GrResourceProvider to signal that the lambda should
105         // clean itself up if it is holding onto any captured objects.
106         this->fLazyInstantiateCallback(nullptr);
107     }
108     // For this to be deleted the opList that held a ref on it (if there was one) must have been
109     // deleted. Which would have cleared out this back pointer.
110     SkASSERT(!fLastOpList);
111 }
112 
113 bool GrSurfaceProxyPriv::AttachStencilIfNeeded(GrResourceProvider* resourceProvider,
114                                                GrSurface* surface, bool needsStencil) {
115     if (needsStencil) {
116         GrRenderTarget* rt = surface->asRenderTarget();
117         if (!rt) {
118             SkASSERT(0);
119             return false;
120         }
121 
122         if (!resourceProvider->attachStencilAttachment(rt)) {
123             return false;
124         }
125     }
126 
127     return true;
128 }
129 
130 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
131                                                    int sampleCnt, bool needsStencil,
132                                                    GrSurfaceDescFlags descFlags,
133                                                    GrMipMapped mipMapped) const {
134     SkASSERT(GrSurfaceProxy::LazyState::kNot == this->lazyInstantiationState());
135     SkASSERT(!fTarget);
136     GrSurfaceDesc desc;
137     desc.fFlags = descFlags;
138     if (fNeedsClear) {
139         desc.fFlags |= kPerformInitialClear_GrSurfaceFlag;
140     }
141     desc.fWidth = fWidth;
142     desc.fHeight = fHeight;
143     desc.fConfig = fConfig;
144     desc.fSampleCnt = sampleCnt;
145 
146     GrResourceProvider::Flags resourceProviderFlags = GrResourceProvider::Flags::kNone;
147     if ((fSurfaceFlags & GrInternalSurfaceFlags::kNoPendingIO) ||
148         resourceProvider->explicitlyAllocateGPUResources()) {
149         // The explicit resource allocator requires that any resources it pulls out of the
150         // cache have no pending IO.
151         resourceProviderFlags = GrResourceProvider::Flags::kNoPendingIO;
152     }
153 
154     sk_sp<GrSurface> surface;
155     if (GrMipMapped::kYes == mipMapped) {
156         SkASSERT(SkBackingFit::kExact == fFit);
157 
158         // SkMipMap doesn't include the base level in the level count so we have to add 1
159         int mipCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
160         // We should have caught the case where mipCount == 1 when making the proxy and instead
161         // created a non-mipmapped proxy.
162         SkASSERT(mipCount > 1);
163         std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipCount]);
164 
165         // We don't want to upload any texel data
166         for (int i = 0; i < mipCount; i++) {
167             texels[i].fPixels = nullptr;
168             texels[i].fRowBytes = 0;
169         }
170 
171         surface = resourceProvider->createTexture(desc, fBudgeted, texels.get(), mipCount);
172         if (surface) {
173             SkASSERT(surface->asTexture());
174             SkASSERT(GrMipMapped::kYes == surface->asTexture()->texturePriv().mipMapped());
175         }
176     } else {
177         if (SkBackingFit::kApprox == fFit) {
178             surface = resourceProvider->createApproxTexture(desc, resourceProviderFlags);
179         } else {
180             surface = resourceProvider->createTexture(desc, fBudgeted, resourceProviderFlags);
181         }
182     }
183     if (!surface) {
184         return nullptr;
185     }
186 
187     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(), needsStencil)) {
188         return nullptr;
189     }
190 
191     return surface;
192 }
193 
194 bool GrSurfaceProxy::canSkipResourceAllocator() const {
195     auto peek = this->peekSurface();
196     if (!peek) {
197         return false;
198     }
199     // If this resource is already allocated and not recyclable then the resource allocator does
200     // not need to do anything with it.
201     return !peek->resourcePriv().getScratchKey().isValid();
202 }
203 
204 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
205     SkASSERT(!fTarget && surface);
206 
207     SkDEBUGCODE(this->validateSurface(surface.get());)
208 
209     fTarget = surface.release();
210 
211     this->INHERITED::transferRefs();
212 
213 #ifdef SK_DEBUG
214     if (this->asRenderTargetProxy()) {
215         SkASSERT(fTarget->asRenderTarget());
216         if (this->asRenderTargetProxy()->needsStencil()) {
217            SkASSERT(fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment());
218         }
219     }
220 
221     if (kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
222         SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
223     }
224 #endif
225 }
226 
227 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
228                                      bool needsStencil, GrSurfaceDescFlags descFlags,
229                                      GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
230     SkASSERT(LazyState::kNot == this->lazyInstantiationState());
231     if (fTarget) {
232         if (uniqueKey && uniqueKey->isValid()) {
233             SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
234         }
235         return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget, needsStencil);
236     }
237 
238     sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, needsStencil,
239                                                        descFlags, mipMapped);
240     if (!surface) {
241         return false;
242     }
243 
244     // If there was an invalidation message pending for this key, we might have just processed it,
245     // causing the key (stored on this proxy) to become invalid.
246     if (uniqueKey && uniqueKey->isValid()) {
247         resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
248     }
249 
250     this->assign(std::move(surface));
251 
252     return true;
253 }
254 
255 void GrSurfaceProxy::deinstantiate() {
256     SkASSERT(this->isInstantiated());
257 
258     this->release();
259 }
260 
261 void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
262     SkASSERT(LazyState::kFully != this->lazyInstantiationState());
263     const GrRenderTargetProxy* rtp = this->asRenderTargetProxy();
264     int sampleCount = 1;
265     if (rtp) {
266         sampleCount = rtp->numStencilSamples();
267     }
268 
269     const GrTextureProxy* tp = this->asTextureProxy();
270     GrMipMapped mipMapped = GrMipMapped::kNo;
271     if (tp) {
272         mipMapped = tp->mipMapped();
273     }
274 
275     int width = this->worstCaseWidth();
276     int height = this->worstCaseHeight();
277 
278     GrTexturePriv::ComputeScratchKey(this->config(), width, height, SkToBool(rtp), sampleCount,
279                                      mipMapped, key);
280 }
281 
282 void GrSurfaceProxy::setLastOpList(GrOpList* opList) {
283 #ifdef SK_DEBUG
284     if (fLastOpList) {
285         SkASSERT(fLastOpList->isClosed());
286     }
287 #endif
288 
289     // Un-reffed
290     fLastOpList = opList;
291 }
292 
293 GrRenderTargetOpList* GrSurfaceProxy::getLastRenderTargetOpList() {
294     return fLastOpList ? fLastOpList->asRenderTargetOpList() : nullptr;
295 }
296 
297 GrTextureOpList* GrSurfaceProxy::getLastTextureOpList() {
298     return fLastOpList ? fLastOpList->asTextureOpList() : nullptr;
299 }
300 
301 int GrSurfaceProxy::worstCaseWidth() const {
302     SkASSERT(LazyState::kFully != this->lazyInstantiationState());
303     if (fTarget) {
304         return fTarget->width();
305     }
306 
307     if (SkBackingFit::kExact == fFit) {
308         return fWidth;
309     }
310     return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fWidth));
311 }
312 
313 int GrSurfaceProxy::worstCaseHeight() const {
314     SkASSERT(LazyState::kFully != this->lazyInstantiationState());
315     if (fTarget) {
316         return fTarget->height();
317     }
318 
319     if (SkBackingFit::kExact == fFit) {
320         return fHeight;
321     }
322     return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fHeight));
323 }
324 
325 #ifdef SK_DEBUG
326 void GrSurfaceProxy::validate(GrContext* context) const {
327     if (fTarget) {
328         SkASSERT(fTarget->getContext() == context);
329     }
330 
331     INHERITED::validate();
332 }
333 #endif
334 
335 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context,
336                                            GrSurfaceProxy* src,
337                                            GrMipMapped mipMapped,
338                                            SkIRect srcRect,
339                                            SkBackingFit fit,
340                                            SkBudgeted budgeted) {
341     SkASSERT(LazyState::kFully != src->lazyInstantiationState());
342     if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
343         return nullptr;
344     }
345 
346     GrSurfaceDesc dstDesc;
347     dstDesc.fWidth = srcRect.width();
348     dstDesc.fHeight = srcRect.height();
349     dstDesc.fConfig = src->config();
350 
351     GrBackendFormat format = src->backendFormat().makeTexture2D();
352     if (!format.isValid()) {
353         return nullptr;
354     }
355 
356     sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
357             format, dstDesc, src->origin(), mipMapped, fit, budgeted));
358     if (!dstContext) {
359         return nullptr;
360     }
361 
362     if (!dstContext->copy(src, srcRect, SkIPoint::Make(0, 0))) {
363         return nullptr;
364     }
365 
366     return dstContext->asTextureProxyRef();
367 }
368 
369 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context, GrSurfaceProxy* src,
370                                            GrMipMapped mipMapped, SkBackingFit fit,
371                                            SkBudgeted budgeted) {
372     SkASSERT(LazyState::kFully != src->lazyInstantiationState());
373     return Copy(context, src, mipMapped, SkIRect::MakeWH(src->width(), src->height()), fit,
374                 budgeted);
375 }
376 
377 sk_sp<GrSurfaceContext> GrSurfaceProxy::TestCopy(GrContext* context, const GrSurfaceDesc& dstDesc,
378                                                  GrSurfaceOrigin origin, GrSurfaceProxy* srcProxy) {
379     SkASSERT(LazyState::kFully != srcProxy->lazyInstantiationState());
380 
381     GrBackendFormat format = srcProxy->backendFormat().makeTexture2D();
382     if (!format.isValid()) {
383         return nullptr;
384     }
385 
386     sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
387             format, dstDesc, origin, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kYes));
388     if (!dstContext) {
389         return nullptr;
390     }
391 
392     if (!dstContext->copy(srcProxy)) {
393         return nullptr;
394     }
395 
396     return dstContext;
397 }
398 
399 void GrSurfaceProxyPriv::exactify() {
400     SkASSERT(GrSurfaceProxy::LazyState::kFully != fProxy->lazyInstantiationState());
401     if (this->isExact()) {
402         return;
403     }
404 
405     SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
406 
407     if (fProxy->fTarget) {
408         // The kApprox but already instantiated case. Setting the proxy's width & height to
409         // the instantiated width & height could have side-effects going forward, since we're
410         // obliterating the area of interest information. This call (exactify) only used
411         // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
412         // used for additional draws.
413         fProxy->fWidth = fProxy->fTarget->width();
414         fProxy->fHeight = fProxy->fTarget->height();
415         return;
416     }
417 
418     // The kApprox uninstantiated case. Making this proxy be exact should be okay.
419     // It could mess things up if prior decisions were based on the approximate size.
420     fProxy->fFit = SkBackingFit::kExact;
421     // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
422     // already been computed we want to leave it alone so that amount will be removed when
423     // the special image goes away. If it hasn't been computed yet it might as well compute the
424     // exact amount.
425 }
426 
427 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
428     SkASSERT(GrSurfaceProxy::LazyState::kNot != fProxy->lazyInstantiationState());
429 
430     sk_sp<GrSurface> surface;
431     if (fProxy->asTextureProxy() && fProxy->asTextureProxy()->getUniqueKey().isValid()) {
432         // First try to reattach to a cached version if the proxy is uniquely keyed
433         surface = resourceProvider->findByUniqueKey<GrSurface>(
434                                                         fProxy->asTextureProxy()->getUniqueKey());
435     }
436 
437     if (!surface) {
438         surface = fProxy->fLazyInstantiateCallback(resourceProvider);
439     }
440     if (GrSurfaceProxy::LazyInstantiationType::kSingleUse == fProxy->fLazyInstantiationType) {
441         fProxy->fLazyInstantiateCallback(nullptr);
442         fProxy->fLazyInstantiateCallback = nullptr;
443     }
444     if (!surface) {
445         fProxy->fWidth = 0;
446         fProxy->fHeight = 0;
447         return false;
448     }
449 
450     if (fProxy->fWidth <= 0 || fProxy->fHeight <= 0) {
451         // This was a fully lazy proxy. We need to fill in the width & height. For partially
452         // lazy proxies we must preserve the original width & height since that indicates
453         // the content area.
454         SkASSERT(fProxy->fWidth <= 0 && fProxy->fHeight <= 0);
455         fProxy->fWidth = surface->width();
456         fProxy->fHeight = surface->height();
457     }
458 
459     bool needsStencil = fProxy->asRenderTargetProxy()
460                                         ? fProxy->asRenderTargetProxy()->needsStencil()
461                                         : false;
462 
463     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(), needsStencil)) {
464         return false;
465     }
466 
467     if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
468         const GrUniqueKey& key = texProxy->getUniqueKey();
469         if (key.isValid()) {
470             if (!surface->asTexture()->getUniqueKey().isValid()) {
471                 // If 'surface' is newly created, attach the unique key
472                 resourceProvider->assignUniqueKeyToResource(key, surface.get());
473             } else {
474                 // otherwise we had better have reattached to a cached version
475                 SkASSERT(surface->asTexture()->getUniqueKey() == key);
476             }
477         }
478     }
479 
480     this->assign(std::move(surface));
481     return true;
482 }
483 
484 #ifdef SK_DEBUG
485 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
486     SkASSERT(surface->config() == fConfig);
487 
488     // Assert the flags are the same except for kNoPendingIO which is not passed onto the GrSurface.
489     GrInternalSurfaceFlags proxyFlags = fSurfaceFlags & ~GrInternalSurfaceFlags::kNoPendingIO;
490     GrInternalSurfaceFlags surfaceFlags = surface->surfacePriv().flags();
491     SkASSERT((proxyFlags & GrInternalSurfaceFlags::kSurfaceMask) ==
492              (surfaceFlags & GrInternalSurfaceFlags::kSurfaceMask));
493     this->onValidateSurface(surface);
494 }
495 #endif
496