1 /*
2  * Copyright 2018 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 "SkImage_GpuBase.h"
9 #include "GrBackendSurface.h"
10 #include "GrClip.h"
11 #include "GrContext.h"
12 #include "GrContextPriv.h"
13 #include "GrRenderTargetContext.h"
14 #include "GrTexture.h"
15 #include "GrTextureAdjuster.h"
16 #include "SkBitmapCache.h"
17 #include "SkImage_Gpu.h"
18 #include "SkPromiseImageTexture.h"
19 #include "SkReadPixelsRec.h"
20 #include "SkTLList.h"
21 #include "effects/GrYUVtoRGBEffect.h"
22 
SkImage_GpuBase(sk_sp<GrContext> context,int width,int height,uint32_t uniqueID,SkAlphaType at,sk_sp<SkColorSpace> cs)23 SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
24                                  SkAlphaType at, sk_sp<SkColorSpace> cs)
25         : INHERITED(width, height, uniqueID)
26         , fContext(std::move(context))
27         , fAlphaType(at)
28         , fColorSpace(std::move(cs)) {}
29 
~SkImage_GpuBase()30 SkImage_GpuBase::~SkImage_GpuBase() {}
31 
32 //////////////////////////////////////////////////////////////////////////////////////////////////
33 
34 #if GR_TEST_UTILS
resetContext(sk_sp<GrContext> newContext)35 void SkImage_GpuBase::resetContext(sk_sp<GrContext> newContext) {
36     SkASSERT(fContext->contextPriv().contextID() == newContext->contextPriv().contextID());
37     fContext = newContext;
38 }
39 #endif
40 
ValidateBackendTexture(GrContext * ctx,const GrBackendTexture & tex,GrPixelConfig * config,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)41 bool SkImage_GpuBase::ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
42                                              GrPixelConfig* config, SkColorType ct, SkAlphaType at,
43                                              sk_sp<SkColorSpace> cs) {
44     if (!tex.isValid()) {
45         return false;
46     }
47     // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
48     // create a fake image info here.
49     SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
50     if (!SkImageInfoIsValid(info)) {
51         return false;
52     }
53     GrBackendFormat backendFormat = tex.getBackendFormat();
54     if (!backendFormat.isValid()) {
55         return false;
56     }
57     *config = ctx->contextPriv().caps()->getConfigFromBackendFormat(backendFormat, ct);
58     return *config != kUnknown_GrPixelConfig;
59 }
60 
61 //////////////////////////////////////////////////////////////////////////////////////////////////
62 
contextID() const63 uint32_t SkImage_GpuBase::contextID() const {
64     return fContext->contextPriv().contextID();
65 }
66 
getROPixels(SkBitmap * dst,CachingHint chint) const67 bool SkImage_GpuBase::getROPixels(SkBitmap* dst, CachingHint chint) const {
68     if (!fContext->contextPriv().resourceProvider()) {
69         // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
70         return false;
71     }
72 
73     const auto desc = SkBitmapCacheDesc::Make(this);
74     if (SkBitmapCache::Find(desc, dst)) {
75         SkASSERT(dst->isImmutable());
76         SkASSERT(dst->getPixels());
77         return true;
78     }
79 
80     SkBitmapCache::RecPtr rec = nullptr;
81     SkPixmap pmap;
82     if (kAllow_CachingHint == chint) {
83         rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
84         if (!rec) {
85             return false;
86         }
87     } else {
88         if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
89             return false;
90         }
91     }
92 
93     sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
94         this->asTextureProxyRef(),
95         fColorSpace);
96     if (!sContext) {
97         return false;
98     }
99 
100     if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
101         return false;
102     }
103 
104     if (rec) {
105         SkBitmapCache::Add(std::move(rec), dst);
106         this->notifyAddedToRasterCache();
107     }
108     return true;
109 }
110 
onMakeSubset(const SkIRect & subset) const111 sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset) const {
112     sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();
113 
114     GrSurfaceDesc desc;
115     desc.fWidth = subset.width();
116     desc.fHeight = subset.height();
117     desc.fConfig = proxy->config();
118 
119     GrBackendFormat format = proxy->backendFormat().makeTexture2D();
120     if (!format.isValid()) {
121         return nullptr;
122     }
123 
124     // TODO: Should this inherit our proxy's budgeted status?
125     sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
126             format, desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact,
127             proxy->isBudgeted()));
128     if (!sContext) {
129         return nullptr;
130     }
131 
132     if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
133         return nullptr;
134     }
135 
136     // MDB: this call is okay bc we know 'sContext' was kExact
137     return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, fAlphaType,
138                                    sContext->asTextureProxyRef(), fColorSpace);
139 }
140 
apply_premul(const SkImageInfo & info,void * pixels,size_t rowBytes)141 static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
142     switch (info.colorType()) {
143     case kRGBA_8888_SkColorType:
144     case kBGRA_8888_SkColorType:
145         break;
146     default:
147         return; // nothing to do
148     }
149 
150     // SkColor is not necessarily RGBA or BGRA, but it is one of them on little-endian,
151     // and in either case, the alpha-byte is always in the same place, so we can safely call
152     // SkPreMultiplyColor()
153     //
154     SkColor* row = (SkColor*)pixels;
155     for (int y = 0; y < info.height(); ++y) {
156         for (int x = 0; x < info.width(); ++x) {
157             row[x] = SkPreMultiplyColor(row[x]);
158         }
159         row = (SkColor*)((char*)(row)+rowBytes);
160     }
161 }
162 
onReadPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const163 bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
164                                    int srcX, int srcY, CachingHint) const {
165     if (!fContext->contextPriv().resourceProvider()) {
166         // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
167         return false;
168     }
169 
170     if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
171         return false;
172     }
173 
174     SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
175     if (!rec.trim(this->width(), this->height())) {
176         return false;
177     }
178 
179     // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
180     // GrRenderTargetContext::onReadPixels
181     uint32_t flags = 0;
182     if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
183         // let the GPU perform this transformation for us
184         flags = GrContextPriv::kUnpremul_PixelOpsFlag;
185     }
186 
187     sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
188         this->asTextureProxyRef(), fColorSpace);
189     if (!sContext) {
190         return false;
191     }
192 
193     if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
194         return false;
195     }
196 
197     // do we have to manually fix-up the alpha channel?
198     //      src         dst
199     //      unpremul    premul      fix manually
200     //      premul      unpremul    done by kUnpremul_PixelOpsFlag
201     // all other combos need to change.
202     //
203     // Should this be handled by Ganesh? todo:?
204     //
205     if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
206         apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
207     }
208     return true;
209 }
210 
asTextureProxyRef(GrContext * context,const GrSamplerState & params,SkScalar scaleAdjust[2]) const211 sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
212                                                          const GrSamplerState& params,
213                                                          SkScalar scaleAdjust[2]) const {
214     if (context->contextPriv().contextID() != fContext->contextPriv().contextID()) {
215         SkASSERT(0);
216         return nullptr;
217     }
218 
219     GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(), fAlphaType,
220                                this->uniqueID(), fColorSpace.get());
221     return adjuster.refTextureProxyForParams(params, scaleAdjust);
222 }
223 
onGetBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const224 GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
225                                                       GrSurfaceOrigin* origin) const {
226     sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();
227     SkASSERT(proxy);
228 
229     if (!fContext->contextPriv().resourceProvider() && !proxy->isInstantiated()) {
230         // This image was created with a DDL context and cannot be instantiated.
231         return GrBackendTexture();
232 }
233 
234     if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
235         return GrBackendTexture(); // invalid
236     }
237 
238     GrTexture* texture = proxy->peekTexture();
239 
240     if (texture) {
241         if (flushPendingGrContextIO) {
242             fContext->contextPriv().prepareSurfaceForExternalIO(proxy.get());
243         }
244         if (origin) {
245             *origin = proxy->origin();
246         }
247         return texture->getBackendTexture();
248     }
249     return GrBackendTexture(); // invalid
250 }
251 
onGetTexture() const252 GrTexture* SkImage_GpuBase::onGetTexture() const {
253     GrTextureProxy* proxy = this->peekProxy();
254     if (!proxy) {
255         return nullptr;
256     }
257 
258     sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef();
259     if (!fContext->contextPriv().resourceProvider() && !proxyRef->isInstantiated()) {
260         // This image was created with a DDL context and cannot be instantiated.
261         return nullptr;
262     }
263 
264     if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
265         return nullptr;
266     }
267 
268     return proxy->peekTexture();
269 }
270 
onIsValid(GrContext * context) const271 bool SkImage_GpuBase::onIsValid(GrContext* context) const {
272     // The base class has already checked that context isn't abandoned (if it's not nullptr)
273     if (fContext->abandoned()) {
274         return false;
275     }
276 
277     if (context && context != fContext.get()) {
278         return false;
279     }
280 
281     return true;
282 }
283 
MakeTempTextureProxies(GrContext * ctx,const GrBackendTexture yuvaTextures[],int numTextures,const SkYUVAIndex yuvaIndices[4],GrSurfaceOrigin imageOrigin,sk_sp<GrTextureProxy> tempTextureProxies[4])284 bool SkImage_GpuBase::MakeTempTextureProxies(GrContext* ctx, const GrBackendTexture yuvaTextures[],
285                                              int numTextures, const SkYUVAIndex yuvaIndices[4],
286                                              GrSurfaceOrigin imageOrigin,
287                                              sk_sp<GrTextureProxy> tempTextureProxies[4]) {
288     GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
289 
290     // We need to make a copy of the input backend textures because we need to preserve the result
291     // of validate_backend_texture.
292     GrBackendTexture yuvaTexturesCopy[4];
293     for (int textureIndex = 0; textureIndex < numTextures; ++textureIndex) {
294         yuvaTexturesCopy[textureIndex] = yuvaTextures[textureIndex];
295         GrBackendFormat backendFormat = yuvaTexturesCopy[textureIndex].getBackendFormat();
296         if (!backendFormat.isValid()) {
297             return false;
298         }
299         yuvaTexturesCopy[textureIndex].fConfig =
300                 ctx->contextPriv().caps()->getYUVAConfigFromBackendFormat(backendFormat);
301         if (yuvaTexturesCopy[textureIndex].fConfig == kUnknown_GrPixelConfig) {
302             return false;
303         }
304         SkASSERT(yuvaTexturesCopy[textureIndex].isValid());
305 
306         tempTextureProxies[textureIndex] = proxyProvider->wrapBackendTexture(
307                 yuvaTexturesCopy[textureIndex], imageOrigin, kBorrow_GrWrapOwnership,
308                 GrWrapCacheable::kNo, kRead_GrIOType);
309         if (!tempTextureProxies[textureIndex]) {
310             return false;
311         }
312 
313         // Check that each texture contains the channel data for the corresponding YUVA index
314         GrPixelConfig config = yuvaTexturesCopy[textureIndex].fConfig;
315         for (int yuvaIndex = 0; yuvaIndex < SkYUVAIndex::kIndexCount; ++yuvaIndex) {
316             if (yuvaIndices[yuvaIndex].fIndex == textureIndex) {
317                 switch (yuvaIndices[yuvaIndex].fChannel) {
318                     case SkColorChannel::kR:
319                         if (kAlpha_8_as_Alpha_GrPixelConfig == config) {
320                             return false;
321                         }
322                         break;
323                     case SkColorChannel::kG:
324                     case SkColorChannel::kB:
325                         if (kAlpha_8_as_Alpha_GrPixelConfig == config ||
326                             kAlpha_8_as_Red_GrPixelConfig == config) {
327                             return false;
328                         }
329                         break;
330                     case SkColorChannel::kA:
331                     default:
332                         if (kRGB_888_GrPixelConfig == config) {
333                             return false;
334                         }
335                         break;
336                 }
337             }
338         }
339     }
340 
341     return true;
342 }
343 
RenderYUVAToRGBA(GrContext * ctx,GrRenderTargetContext * renderTargetContext,const SkRect & rect,SkYUVColorSpace yuvColorSpace,const sk_sp<GrTextureProxy> proxies[4],const SkYUVAIndex yuvaIndices[4])344 bool SkImage_GpuBase::RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
345                                        const SkRect& rect, SkYUVColorSpace yuvColorSpace,
346                                        const sk_sp<GrTextureProxy> proxies[4],
347                                        const SkYUVAIndex yuvaIndices[4]) {
348     SkASSERT(renderTargetContext);
349     if (!renderTargetContext->asSurfaceProxy()) {
350         return false;
351     }
352 
353     GrPaint paint;
354     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
355 
356     paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
357                                                            yuvColorSpace,
358                                                            GrSamplerState::Filter::kNearest));
359 
360     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
361 
362     // DDL TODO: in the promise image version we must not flush here
363     ctx->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy());
364 
365     return true;
366 }
367 
MakePromiseImageLazyProxy(GrContext * context,int width,int height,GrSurfaceOrigin origin,GrPixelConfig config,GrBackendFormat backendFormat,GrMipMapped mipMapped,PromiseImageTextureFulfillProc fulfillProc,PromiseImageTextureReleaseProc releaseProc,PromiseImageTextureDoneProc doneProc,PromiseImageTextureContext textureContext,DelayReleaseCallback delayReleaseCallback)368 sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
369         GrContext* context, int width, int height, GrSurfaceOrigin origin, GrPixelConfig config,
370         GrBackendFormat backendFormat, GrMipMapped mipMapped,
371         PromiseImageTextureFulfillProc fulfillProc,
372         PromiseImageTextureReleaseProc releaseProc,
373         PromiseImageTextureDoneProc doneProc,
374         PromiseImageTextureContext textureContext,
375         DelayReleaseCallback delayReleaseCallback) {
376     SkASSERT(context);
377     SkASSERT(width > 0 && height > 0);
378     SkASSERT(doneProc);
379     SkASSERT(config != kUnknown_GrPixelConfig);
380 
381     if (!fulfillProc || !releaseProc) {
382         doneProc(textureContext);
383         return nullptr;
384     }
385 
386     if (mipMapped == GrMipMapped::kYes &&
387         GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
388         // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
389         // well.
390         doneProc(textureContext);
391         return nullptr;
392     }
393 
394     /**
395      * This class is the lazy instantiation callback for promise images. It manages calling the
396      * client's Fulfill, Release, and Done procs. It attempts to reuse a GrTexture instance in
397      * cases where the client provides the same SkPromiseImageTexture for successive Fulfill calls.
398      * The created GrTexture is given a key based on a unique ID associated with the
399      * SkPromiseImageTexture. When the texture enters "idle" state (meaning it is not being used by
400      * the GPU and is at rest in the resource cache) the client's Release proc is called
401      * using GrTexture's idle proc mechanism. If the same SkPromiseImageTexture is provided for
402      * another fulfill we find the cached GrTexture. If the proxy, and therefore this object,
403      * is destroyed, we invalidate the GrTexture's key. Also if the client overwrites or
404      * destroys their SkPromiseImageTexture we invalidate the key.
405      *
406      * Currently a GrTexture is only reused for a given SkPromiseImageTexture if the
407      * SkPromiseImageTexture is reused in Fulfill for the same promise SkImage. However, we'd
408      * like to relax that so that a SkPromiseImageTexture can be reused with different promise
409      * SkImages that will reuse a single GrTexture.
410      */
411     class PromiseLazyInstantiateCallback {
412     public:
413         PromiseLazyInstantiateCallback(PromiseImageTextureFulfillProc fulfillProc,
414                                        PromiseImageTextureReleaseProc releaseProc,
415                                        PromiseImageTextureDoneProc doneProc,
416                                        PromiseImageTextureContext context,
417                                        DelayReleaseCallback delayReleaseCallback,
418                                        GrPixelConfig config)
419                 : fFulfillProc(fulfillProc)
420                 , fConfig(config)
421                 , fDelayReleaseCallback(delayReleaseCallback) {
422             auto doneHelper = sk_make_sp<GrReleaseProcHelper>(doneProc, context);
423             fReleaseContext = sk_make_sp<IdleContext::PromiseImageReleaseContext>(
424                     releaseProc, context, std::move(doneHelper));
425         }
426 
427         ~PromiseLazyInstantiateCallback() = default;
428 
429         sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
430             if (!resourceProvider) {
431                 if (fDelayedReleaseTexture) {
432                     fDelayedReleaseTexture.reset();
433                 }
434                 return nullptr;
435             }
436             if (fDelayedReleaseTexture) {
437                 return fDelayedReleaseTexture;
438             }
439 
440             sk_sp<GrTexture> cachedTexture;
441             SkASSERT(fLastFulfilledKey.isValid() == (fLastFulfillID > 0));
442             if (fLastFulfilledKey.isValid()) {
443                 auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey);
444                 if (surf) {
445                     cachedTexture = sk_ref_sp(surf->asTexture());
446                     SkASSERT(cachedTexture);
447                 }
448             }
449             // If the release callback hasn't been called already by releasing the GrTexture
450             // then we can be sure that won't happen so long as we have a ref to the texture.
451             if (cachedTexture && !fReleaseContext->isReleased()) {
452                 return std::move(cachedTexture);
453             }
454             GrBackendTexture backendTexture;
455             sk_sp<SkPromiseImageTexture> promiseTexture =
456                     fFulfillProc(fReleaseContext->textureContext());
457             fReleaseContext->notifyWasFulfilled();
458             if (!promiseTexture) {
459                 fReleaseContext->release();
460                 return sk_sp<GrTexture>();
461             }
462             bool same = promiseTexture->uniqueID() == fLastFulfillID;
463             SkASSERT(!same || fLastFulfilledKey.isValid());
464             if (same && cachedTexture) {
465                 SkASSERT(fReleaseContext->unique());
466                 this->addToIdleContext(cachedTexture.get());
467                 return std::move(cachedTexture);
468             } else if (cachedTexture) {
469                 cachedTexture->resourcePriv().removeUniqueKey();
470             }
471             fLastFulfillID = promiseTexture->uniqueID();
472 
473             backendTexture = promiseTexture->backendTexture();
474             backendTexture.fConfig = fConfig;
475             if (!backendTexture.isValid()) {
476                 // Even though the GrBackendTexture is not valid, we must call the release
477                 // proc to keep our contract of always calling Fulfill and Release in pairs.
478                 fReleaseContext->release();
479                 return sk_sp<GrTexture>();
480             }
481 
482             sk_sp<GrTexture> tex;
483             static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
484             GrUniqueKey::Builder builder(&fLastFulfilledKey, kDomain, 2, "promise");
485             builder[0] = promiseTexture->uniqueID();
486             builder[1] = fConfig;
487             builder.finish();
488             // A texture with this key may already exist from a different instance of this lazy
489             // callback. This could happen if the client fulfills a promise image with a texture
490             // that was previously used to fulfill a different promise image.
491             if (auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey)) {
492                 tex = sk_ref_sp(surf->asTexture());
493                 SkASSERT(tex);
494             } else {
495                 if ((tex = resourceProvider->wrapBackendTexture(
496                              backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kYes,
497                              kRead_GrIOType))) {
498                     tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
499                 } else {
500                     // Even though we failed to wrap the backend texture, we must call the release
501                     // proc to keep our contract of always calling Fulfill and Release in pairs.
502                     fReleaseContext->release();
503                     return sk_sp<GrTexture>();
504                 }
505             }
506             this->addToIdleContext(tex.get());
507             if (fDelayReleaseCallback == DelayReleaseCallback::kYes) {
508                 fDelayedReleaseTexture = tex;
509             }
510             tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
511             SkASSERT(fContextID == SK_InvalidUniqueID ||
512                      fContextID == tex->getContext()->contextPriv().contextID());
513             fContextID = tex->getContext()->contextPriv().contextID();
514             promiseTexture->addKeyToInvalidate(fContextID, fLastFulfilledKey);
515             return std::move(tex);
516         }
517 
518     private:
519         // The GrTexture's idle callback mechanism is used to call the client's Release proc via
520         // this class. This also owns a ref counted helper that calls the client's ReleaseProc when
521         // the ref count reaches zero. The callback and any Fulfilled but un-Released texture share
522         // ownership of the IdleContext. Thus, the IdleContext is destroyed and calls the Done proc
523         // after the last fulfilled texture goes idle and calls the Release proc or the proxy's
524         // destructor destroys the lazy callback, whichever comes last.
525         class IdleContext {
526         public:
527             class PromiseImageReleaseContext;
528 
529             IdleContext() = default;
530 
531             ~IdleContext() = default;
532 
533             void addImageReleaseContext(sk_sp<PromiseImageReleaseContext> context) {
534                 fReleaseContexts.addToHead(std::move(context));
535             }
536 
537             static void IdleProc(void* context) {
538                 IdleContext* idleContext = static_cast<IdleContext*>(context);
539                 for (ReleaseContextList::Iter iter = idleContext->fReleaseContexts.headIter();
540                      iter.get();
541                      iter.next()) {
542                     (*iter.get())->release();
543                 }
544                 idleContext->fReleaseContexts.reset();
545                 delete idleContext;
546             }
547 
548             class PromiseImageReleaseContext : public SkNVRefCnt<PromiseImageReleaseContext> {
549             public:
550                 PromiseImageReleaseContext(PromiseImageTextureReleaseProc releaseProc,
551                                            PromiseImageTextureContext textureContext,
552                                            sk_sp<GrReleaseProcHelper> doneHelper)
553                         : fReleaseProc(releaseProc)
554                         , fTextureContext(textureContext)
555                         , fDoneHelper(std::move(doneHelper)) {}
556 
557                 ~PromiseImageReleaseContext() { SkASSERT(fIsReleased); }
558 
559                 void release() {
560                     SkASSERT(!fIsReleased);
561                     fReleaseProc(fTextureContext);
562                     fIsReleased = true;
563                 }
564 
565                 void notifyWasFulfilled() { fIsReleased = false; }
566                 bool isReleased() const { return fIsReleased; }
567 
568                 PromiseImageTextureContext textureContext() const { return fTextureContext; }
569 
570             private:
571                 PromiseImageTextureReleaseProc fReleaseProc;
572                 PromiseImageTextureContext fTextureContext;
573                 sk_sp<GrReleaseProcHelper> fDoneHelper;
574                 bool fIsReleased = true;
575             };
576 
577         private:
578             using ReleaseContextList = SkTLList<sk_sp<PromiseImageReleaseContext>, 4>;
579             ReleaseContextList fReleaseContexts;
580         };
581 
582         void addToIdleContext(GrTexture* texture) {
583             SkASSERT(!fReleaseContext->isReleased());
584             IdleContext* idleContext = static_cast<IdleContext*>(texture->idleContext());
585             if (!idleContext) {
586                 idleContext = new IdleContext();
587                 texture->setIdleProc(IdleContext::IdleProc, idleContext);
588             }
589             idleContext->addImageReleaseContext(fReleaseContext);
590         }
591 
592         sk_sp<IdleContext::PromiseImageReleaseContext> fReleaseContext;
593         sk_sp<GrTexture> fDelayedReleaseTexture;
594         PromiseImageTextureFulfillProc fFulfillProc;
595         GrPixelConfig fConfig;
596         DelayReleaseCallback fDelayReleaseCallback;
597 
598         // ID of the last SkPromiseImageTexture given to us by the client.
599         uint32_t fLastFulfillID = 0;
600         // ID of the GrContext that we are interacting with.
601         uint32_t fContextID = SK_InvalidUniqueID;
602         GrUniqueKey fLastFulfilledKey;
603     } callback(fulfillProc, releaseProc, doneProc, textureContext, delayReleaseCallback, config);
604 
605     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
606 
607     GrSurfaceDesc desc;
608     desc.fWidth = width;
609     desc.fHeight = height;
610     desc.fConfig = config;
611 
612     // We pass kReadOnly here since we should treat content of the client's texture as immutable.
613     return proxyProvider->createLazyProxy(std::move(callback), backendFormat, desc, origin,
614                                           mipMapped, GrInternalSurfaceFlags::kReadOnly,
615                                           SkBackingFit::kExact, SkBudgeted::kNo,
616                                           GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
617 }
618