• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "tests/Test.h"
9 
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkPromiseImageTexture.h"
12 #include "include/gpu/GrBackendSurface.h"
13 #include "include/gpu/GrTexture.h"
14 #include "src/gpu/GrContextPriv.h"
15 #include "src/gpu/GrGpu.h"
16 #include "src/image/SkImage_Gpu.h"
17 
18 using namespace sk_gpu_test;
19 
20 struct PromiseTextureChecker {
21     // shared indicates whether the backend texture is used to fulfill more than one promise
22     // image.
PromiseTextureCheckerPromiseTextureChecker23     explicit PromiseTextureChecker(const GrBackendTexture& tex, skiatest::Reporter* reporter,
24                                    bool shared)
25             : fTexture(SkPromiseImageTexture::Make(tex))
26             , fReporter(reporter)
27             , fShared(shared)
28             , fFulfillCount(0)
29             , fReleaseCount(0)
30             , fDoneCount(0) {}
31     sk_sp<SkPromiseImageTexture> fTexture;
32     skiatest::Reporter* fReporter;
33     bool fShared;
34     int fFulfillCount;
35     int fReleaseCount;
36     int fDoneCount;
37 
38     /**
39      * Releases the SkPromiseImageTexture. Used to test that cached GrTexture representations
40      * in the cache are freed.
41      */
releaseTexturePromiseTextureChecker42     void releaseTexture() { fTexture.reset(); }
43 
uniqueKeysPromiseTextureChecker44     SkTArray<GrUniqueKey> uniqueKeys() const {
45         return fTexture->testingOnly_uniqueKeysToInvalidate();
46     }
47 
FulfillPromiseTextureChecker48     static sk_sp<SkPromiseImageTexture> Fulfill(void* self) {
49         auto checker = static_cast<PromiseTextureChecker*>(self);
50         checker->fFulfillCount++;
51         return checker->fTexture;
52     }
ReleasePromiseTextureChecker53     static void Release(void* self) {
54         auto checker = static_cast<PromiseTextureChecker*>(self);
55         checker->fReleaseCount++;
56         if (!checker->fShared) {
57             // This is only used in a single threaded fashion with a single promise image. So
58             // every fulfill should be balanced by a release before the next fulfill.
59             REPORTER_ASSERT(checker->fReporter, checker->fReleaseCount == checker->fFulfillCount);
60         }
61     }
DonePromiseTextureChecker62     static void Done(void* self) {
63         static_cast<PromiseTextureChecker*>(self)->fDoneCount++;
64     }
65 };
66 
67 enum class ReleaseBalanceExpectation {
68     kBalanced,
69     kAllUnbalanced,
70     kUnbalancedByOne,
71 };
72 
73 enum class DoneBalanceExpectation {
74     kBalanced,
75     kAllUnbalanced,
76     kUnknown,
77     kUnbalancedByOne,
78     kBalancedOrOffByOne,
79 };
80 
check_fulfill_and_release_cnts(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,int expectedFulfillCnt,ReleaseBalanceExpectation releaseBalanceExpecation,DoneBalanceExpectation doneBalanceExpecation)81 static void check_fulfill_and_release_cnts(skiatest::Reporter* reporter,
82                                            const PromiseTextureChecker& promiseChecker,
83                                            int expectedFulfillCnt,
84                                            ReleaseBalanceExpectation releaseBalanceExpecation,
85                                            DoneBalanceExpectation doneBalanceExpecation) {
86     REPORTER_ASSERT(reporter, promiseChecker.fFulfillCount == expectedFulfillCnt);
87     if (!expectedFulfillCnt) {
88         // Release and Done should only ever be called after Fulfill.
89         REPORTER_ASSERT(reporter, !promiseChecker.fReleaseCount);
90         REPORTER_ASSERT(reporter, !promiseChecker.fDoneCount);
91         return;
92     }
93     int releaseDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
94     switch (releaseBalanceExpecation) {
95         case ReleaseBalanceExpectation::kBalanced:
96             REPORTER_ASSERT(reporter, !releaseDiff);
97             break;
98         case ReleaseBalanceExpectation::kAllUnbalanced:
99             REPORTER_ASSERT(reporter, releaseDiff == promiseChecker.fFulfillCount);
100             break;
101         case ReleaseBalanceExpectation::kUnbalancedByOne:
102             REPORTER_ASSERT(reporter, releaseDiff == 1);
103             break;
104     }
105     int doneDiff = promiseChecker.fFulfillCount - promiseChecker.fDoneCount;
106     switch (doneBalanceExpecation) {
107         case DoneBalanceExpectation::kBalanced:
108             REPORTER_ASSERT(reporter, !doneDiff);
109             break;
110         case DoneBalanceExpectation::kAllUnbalanced:
111             REPORTER_ASSERT(reporter, doneDiff == promiseChecker.fFulfillCount);
112             break;
113         case DoneBalanceExpectation::kUnknown:
114             REPORTER_ASSERT(reporter, doneDiff >= 0 && doneDiff <= promiseChecker.fFulfillCount);
115             break;
116         case DoneBalanceExpectation::kUnbalancedByOne:
117             REPORTER_ASSERT(reporter, doneDiff == 1);
118             break;
119         case DoneBalanceExpectation::kBalancedOrOffByOne:
120             REPORTER_ASSERT(reporter, doneDiff == 0 || doneDiff == 1);
121             break;
122     }
123 }
124 
check_unfulfilled(const PromiseTextureChecker & promiseChecker,skiatest::Reporter * reporter)125 static void check_unfulfilled(const PromiseTextureChecker& promiseChecker,
126                               skiatest::Reporter* reporter) {
127     check_fulfill_and_release_cnts(reporter, promiseChecker, 0,
128                                    ReleaseBalanceExpectation::kBalanced,
129                                    DoneBalanceExpectation::kBalanced);
130 }
131 
check_only_fulfilled(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,int expectedFulfillCnt=1)132 static void check_only_fulfilled(skiatest::Reporter* reporter,
133                                  const PromiseTextureChecker& promiseChecker,
134                                  int expectedFulfillCnt = 1) {
135     check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
136                                    ReleaseBalanceExpectation::kAllUnbalanced,
137                                    DoneBalanceExpectation::kAllUnbalanced);
138 }
139 
check_all_flushed_but_not_synced(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,GrBackendApi api,int expectedFulfillCnt=1)140 static void check_all_flushed_but_not_synced(skiatest::Reporter* reporter,
141                                              const PromiseTextureChecker& promiseChecker,
142                                              GrBackendApi api,
143                                              int expectedFulfillCnt = 1) {
144     DoneBalanceExpectation doneBalanceExpectation = DoneBalanceExpectation::kBalanced;
145     // On Vulkan Done isn't guaranteed to be called until a sync has occurred.
146     if (api == GrBackendApi::kVulkan) {
147         doneBalanceExpectation = expectedFulfillCnt == 1
148                                          ? DoneBalanceExpectation::kBalancedOrOffByOne
149                                          : DoneBalanceExpectation::kUnknown;
150     }
151     check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
152                                    ReleaseBalanceExpectation::kBalanced, doneBalanceExpectation);
153 }
154 
check_all_done(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,int expectedFulfillCnt=1)155 static void check_all_done(skiatest::Reporter* reporter,
156                            const PromiseTextureChecker& promiseChecker,
157                            int expectedFulfillCnt = 1) {
158     check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
159                                    ReleaseBalanceExpectation::kBalanced,
160                                    DoneBalanceExpectation::kBalanced);
161 }
162 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest,reporter,ctxInfo)163 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
164     const int kWidth = 10;
165     const int kHeight = 10;
166 
167     GrContext* ctx = ctxInfo.grContext();
168     GrGpu* gpu = ctx->priv().getGpu();
169 
170     GrBackendTexture backendTex = ctx->createBackendTexture(
171             kWidth, kHeight, kRGBA_8888_SkColorType,
172             SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes, GrProtected::kNo);
173     REPORTER_ASSERT(reporter, backendTex.isValid());
174 
175     GrBackendFormat backendFormat = backendTex.getBackendFormat();
176     REPORTER_ASSERT(reporter, backendFormat.isValid());
177 
178     PromiseTextureChecker promiseChecker(backendTex, reporter, false);
179     GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
180     sk_sp<SkImage> refImg(
181             SkImage_Gpu::MakePromiseTexture(
182                     ctx, backendFormat, kWidth, kHeight,
183                     GrMipMapped::kNo, texOrigin,
184                     kRGBA_8888_SkColorType, kPremul_SkAlphaType,
185                     nullptr,
186                     PromiseTextureChecker::Fulfill,
187                     PromiseTextureChecker::Release,
188                     PromiseTextureChecker::Done,
189                     &promiseChecker,
190                     SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
191 
192     SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
193     sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
194     SkCanvas* canvas = surface->getCanvas();
195 
196     canvas->drawImage(refImg, 0, 0);
197     check_unfulfilled(promiseChecker, reporter);
198 
199     surface->flush();
200     // We still own the image so we should not have called Release or Done.
201     check_only_fulfilled(reporter, promiseChecker);
202 
203     gpu->testingOnly_flushGpuAndSync();
204     check_only_fulfilled(reporter, promiseChecker);
205 
206     canvas->drawImage(refImg, 0, 0);
207     canvas->drawImage(refImg, 0, 0);
208 
209     surface->flush();
210 
211     gpu->testingOnly_flushGpuAndSync();
212     // Image should still be fulfilled from the first time we drew/flushed it.
213     check_only_fulfilled(reporter, promiseChecker);
214 
215     canvas->drawImage(refImg, 0, 0);
216     surface->flush();
217     check_only_fulfilled(reporter, promiseChecker);
218 
219     canvas->drawImage(refImg, 0, 0);
220     refImg.reset();
221     // We no longer own the image but the last draw is still unflushed.
222     check_only_fulfilled(reporter, promiseChecker);
223 
224     surface->flush();
225     // Flushing should have called Release. Depending on the backend and timing it may have called
226     // done.
227     check_all_flushed_but_not_synced(reporter, promiseChecker, ctx->backend());
228     gpu->testingOnly_flushGpuAndSync();
229     // Now Done should definitely have been called.
230     check_all_done(reporter, promiseChecker);
231 
232     ctx->deleteBackendTexture(backendTex);
233 }
234 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig,reporter,ctxInfo)235 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, reporter, ctxInfo) {
236     // Try making two promise SkImages backed by the same texture but with different uses/views.
237     // This will only be testable on backends where a single texture format (8bit red unorm) can
238     // be used for both alpha and gray image color types.
239 
240     const int kWidth = 10;
241     const int kHeight = 10;
242 
243     GrContext* ctx = ctxInfo.grContext();
244     GrGpu* gpu = ctx->priv().getGpu();
245 
246     GrBackendFormat gray8Format  = ctx->defaultBackendFormat(kGray_8_SkColorType,
247                                                              GrRenderable::kNo);
248     GrBackendFormat alpha8Format = ctx->defaultBackendFormat(kAlpha_8_SkColorType,
249                                                              GrRenderable::kNo);
250     if (gray8Format != alpha8Format) {
251         // kGray_8 and kAlpha_8 won't share the same backend texture
252         return;
253     }
254 
255     GrBackendTexture grayBackendTex = ctx->createBackendTexture(
256             kWidth, kHeight, gray8Format,
257             SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo, GrProtected::kNo);
258     REPORTER_ASSERT(reporter, grayBackendTex.isValid());
259 
260     SkImageInfo info =
261             SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
262     sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
263     SkCanvas* canvas = surface->getCanvas();
264 
265     PromiseTextureChecker promiseChecker(grayBackendTex, reporter, true);
266 
267     sk_sp<SkImage> alphaImg(SkImage_Gpu::MakePromiseTexture(
268             ctx, alpha8Format, kWidth, kHeight, GrMipMapped::kNo,
269             kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
270             PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
271             PromiseTextureChecker::Done, &promiseChecker,
272             SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
273     REPORTER_ASSERT(reporter, alphaImg);
274 
275     sk_sp<SkImage> grayImg(SkImage_Gpu::MakePromiseTexture(
276             ctx, gray8Format, kWidth, kHeight, GrMipMapped::kNo,
277             kBottomLeft_GrSurfaceOrigin, kGray_8_SkColorType, kOpaque_SkAlphaType, nullptr,
278             PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
279             PromiseTextureChecker::Done, &promiseChecker,
280             SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
281     REPORTER_ASSERT(reporter, grayImg);
282 
283     canvas->drawImage(alphaImg, 0, 0);
284     canvas->drawImage(grayImg, 1, 1);
285     surface->flush();
286     gpu->testingOnly_flushGpuAndSync();
287     check_only_fulfilled(reporter, promiseChecker, 2);
288 
289     // Because they use different backend formats, each image should have created a different
290     // GrTexture and they both should still be cached.
291     ctx->priv().getResourceCache()->purgeAsNeeded();
292 
293     auto keys = promiseChecker.uniqueKeys();
294     REPORTER_ASSERT(reporter, keys.count() == 2);
295     for (const auto& key : keys) {
296         auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key);
297         REPORTER_ASSERT(reporter, surf && surf->asTexture());
298         if (surf && surf->asTexture()) {
299             GrTexture* texture = surf->asTexture();
300 
301             // The backend texture should be shared between the two uses
302             REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
303                                                 grayBackendTex, texture->getBackendTexture()));
304 
305             // but the view of them from the GrTexture should've been transmuted into the
306             // specific pixel configs
307             REPORTER_ASSERT(reporter, texture->config() == kAlpha_8_as_Red_GrPixelConfig ||
308                                       texture->config() == kGray_8_as_Red_GrPixelConfig);
309         }
310     }
311 
312     // Invalidate the backing texture, this should invalidate the keys.
313     promiseChecker.releaseTexture();
314     ctx->priv().getResourceCache()->purgeAsNeeded();
315 
316     for (const auto& key : keys) {
317         auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key);
318         REPORTER_ASSERT(reporter, !surf);
319     }
320     alphaImg.reset();
321     ctx->flush(); // We do this to pick up any unref messages that are sent by unref'ing the image.
322     check_fulfill_and_release_cnts(reporter, promiseChecker, 2,
323                                    ReleaseBalanceExpectation::kUnbalancedByOne,
324                                    DoneBalanceExpectation::kUnbalancedByOne);
325     grayImg.reset();
326     ctx->flush(); // We do this to pick up any unref messages that are sent by unref'ing the image.
327     check_all_done(reporter, promiseChecker, 2);
328     ctx->deleteBackendTexture(grayBackendTex);
329 }
330 
DEF_GPUTEST(PromiseImageTextureShutdown,reporter,ctxInfo)331 DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
332     const int kWidth = 10;
333     const int kHeight = 10;
334 
335     // Different ways of killing contexts.
336     using DeathFn = std::function<void(sk_gpu_test::GrContextFactory*, GrContext*)>;
337     DeathFn destroy = [](sk_gpu_test::GrContextFactory* factory, GrContext* context) {
338         factory->destroyContexts();
339     };
340     DeathFn abandon = [](sk_gpu_test::GrContextFactory* factory, GrContext* context) {
341         context->abandonContext();
342     };
343     DeathFn releaseResourcesAndAbandon = [](sk_gpu_test::GrContextFactory* factory,
344                                             GrContext* context) {
345         context->releaseResourcesAndAbandonContext();
346     };
347 
348     for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) {
349         auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type);
350         // These tests are difficult to get working with Vulkan. See http://skbug.com/8705
351         // and http://skbug.com/8275
352         GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType);
353         if (api == GrBackendApi::kVulkan) {
354             continue;
355         }
356         DeathFn contextKillers[] = {destroy, abandon, releaseResourcesAndAbandon};
357         for (auto contextDeath : contextKillers) {
358             sk_gpu_test::GrContextFactory factory;
359             auto ctx = factory.get(contextType);
360             if (!ctx) {
361                 continue;
362             }
363 
364             GrBackendTexture backendTex = ctx->createBackendTexture(
365                     kWidth, kHeight, kAlpha_8_SkColorType,
366                     SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo, GrProtected::kNo);
367             REPORTER_ASSERT(reporter, backendTex.isValid());
368 
369             SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType,
370                                                  kPremul_SkAlphaType);
371             sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
372             SkCanvas* canvas = surface->getCanvas();
373 
374             PromiseTextureChecker promiseChecker(backendTex, reporter, false);
375             sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(
376                     ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
377                     kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
378                     PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
379                     PromiseTextureChecker::Done, &promiseChecker,
380                     SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
381             REPORTER_ASSERT(reporter, image);
382 
383             canvas->drawImage(image, 0, 0);
384             image.reset();
385             // If the surface still holds a ref to the context then the factory will not be able
386             // to destroy the context (and instead will release-all-and-abandon).
387             surface.reset();
388 
389             ctx->flush();
390             contextDeath(&factory, ctx);
391 
392             check_all_done(reporter, promiseChecker);
393         }
394     }
395 }
396 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache,reporter,ctxInfo)397 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache, reporter, ctxInfo) {
398     const int kWidth = 10;
399     const int kHeight = 10;
400 
401     GrContext* ctx = ctxInfo.grContext();
402 
403     GrBackendTexture backendTex = ctx->createBackendTexture(
404             kWidth, kHeight, kAlpha_8_SkColorType,
405             SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo, GrProtected::kNo);
406     REPORTER_ASSERT(reporter, backendTex.isValid());
407 
408     SkImageInfo info =
409             SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
410     sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
411     SkCanvas* canvas = surface->getCanvas();
412 
413     PromiseTextureChecker promiseChecker(backendTex, reporter, false);
414     sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(
415             ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
416             kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
417             PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
418             PromiseTextureChecker::Done, &promiseChecker,
419             SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
420     REPORTER_ASSERT(reporter, image);
421 
422     // Make the cache full. This tests that we don't preemptively purge cached textures for
423     // fulfillment due to cache pressure.
424     static constexpr int kMaxResources = 10;
425     static constexpr int kMaxBytes = 100;
426     ctx->setResourceCacheLimits(kMaxResources, kMaxBytes);
427     sk_sp<GrTexture> textures[2 * kMaxResources];
428     for (int i = 0; i < 2 * kMaxResources; ++i) {
429         GrSurfaceDesc desc;
430         desc.fConfig = kRGBA_8888_GrPixelConfig;
431         desc.fWidth = desc.fHeight = 100;
432         auto format = ctx->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
433                                                                   GrRenderable::kNo);
434         textures[i] = ctx->priv().resourceProvider()->createTexture(
435                 desc, format, GrRenderable::kNo, 1, SkBudgeted::kYes, GrProtected::kNo,
436                 GrResourceProvider::Flags::kNoPendingIO);
437         REPORTER_ASSERT(reporter, textures[i]);
438     }
439 
440     // Relying on the asserts in the promiseImageChecker to ensure that fulfills and releases are
441     // properly ordered.
442     canvas->drawImage(image, 0, 0);
443     surface->flush();
444     canvas->drawImage(image, 1, 0);
445     surface->flush();
446     canvas->drawImage(image, 2, 0);
447     surface->flush();
448     canvas->drawImage(image, 3, 0);
449     surface->flush();
450     canvas->drawImage(image, 4, 0);
451     surface->flush();
452     canvas->drawImage(image, 5, 0);
453     surface->flush();
454     // Must call these to ensure that all callbacks are performed before the checker is destroyed.
455     image.reset();
456     ctx->flush();
457     ctx->priv().getGpu()->testingOnly_flushGpuAndSync();
458 
459     ctx->deleteBackendTexture(backendTex);
460 }
461 
462 // Test case where promise image fulfill returns nullptr.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageNullFulfill,reporter,ctxInfo)463 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageNullFulfill, reporter, ctxInfo) {
464     const int kWidth = 10;
465     const int kHeight = 10;
466 
467     GrContext* ctx = ctxInfo.grContext();
468 
469     // Do all this just to get a valid backend format for the image.
470     GrBackendTexture backendTex = ctx->createBackendTexture(
471             kWidth, kHeight, kRGBA_8888_SkColorType,
472             SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes, GrProtected::kNo);
473     REPORTER_ASSERT(reporter, backendTex.isValid());
474     GrBackendFormat backendFormat = backendTex.getBackendFormat();
475     REPORTER_ASSERT(reporter, backendFormat.isValid());
476     ctx->deleteBackendTexture(backendTex);
477 
478     struct Counts {
479         int fFulfillCount = 0;
480         int fReleaseCount = 0;
481         int fDoneCount = 0;
482     } counts;
483     auto fulfill = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) {
484         ++static_cast<Counts*>(ctx)->fFulfillCount;
485         return sk_sp<SkPromiseImageTexture>();
486     };
487     auto release = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) {
488         ++static_cast<Counts*>(ctx)->fReleaseCount;
489     };
490     auto done = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) {
491         ++static_cast<Counts*>(ctx)->fDoneCount;
492     };
493     GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
494     sk_sp<SkImage> refImg(SkImage_Gpu::MakePromiseTexture(
495             ctx, backendFormat, kWidth, kHeight, GrMipMapped::kNo, texOrigin,
496             kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr, fulfill, release, done, &counts,
497             SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew));
498 
499     SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
500     sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
501     SkCanvas* canvas = surface->getCanvas();
502     // Draw the image a few different ways.
503     canvas->drawImage(refImg, 0, 0);
504     SkPaint paint;
505     paint.setColorFilter(SkColorFilters::LinearToSRGBGamma());
506     canvas->drawImage(refImg, 0, 0, &paint);
507     auto shader = refImg->makeShader(SkTileMode::kClamp, SkTileMode::kClamp);
508     REPORTER_ASSERT(reporter, shader);
509     paint.setShader(std::move(shader));
510     canvas->drawRect(SkRect::MakeWH(1,1), paint);
511     paint.setShader(nullptr);
512     refImg.reset();
513     surface->flush();
514     // We should only call each callback once and we should have made all the calls by this point.
515     REPORTER_ASSERT(reporter, counts.fFulfillCount == 1);
516     REPORTER_ASSERT(reporter, counts.fReleaseCount == 1);
517     REPORTER_ASSERT(reporter, counts.fDoneCount == 1);
518 }
519