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