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