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 "SkExchange.h"
9 #include "Test.h"
10
11 #include "GrBackendSurface.h"
12 #include "GrContextPriv.h"
13 #include "GrGpu.h"
14 #include "GrTexture.h"
15 #include "SkImage_Gpu.h"
16 #include "SkPromiseImageTexture.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 GrBackendTexture fLastFulfilledTexture;
38
39 /**
40 * Replaces the backend texture that this checker will return from fulfill. Also, transfers
41 * ownership of the previous PromiseImageTexture to the caller, if they want to control when
42 * it is deleted. The default argument will remove the existing texture without installing a
43 * valid replacement.
44 */
replaceTexturePromiseTextureChecker45 sk_sp<const SkPromiseImageTexture> replaceTexture(
46 const GrBackendTexture& tex = GrBackendTexture()) {
47 return skstd::exchange(fTexture, SkPromiseImageTexture::Make(tex));
48 }
49
uniqueKeysPromiseTextureChecker50 SkTArray<GrUniqueKey> uniqueKeys() const {
51 return fTexture->testingOnly_uniqueKeysToInvalidate();
52 }
53
FulfillPromiseTextureChecker54 static sk_sp<SkPromiseImageTexture> Fulfill(void* self) {
55 auto checker = static_cast<PromiseTextureChecker*>(self);
56 checker->fFulfillCount++;
57 checker->fLastFulfilledTexture = checker->fTexture->backendTexture();
58 return checker->fTexture;
59 }
ReleasePromiseTextureChecker60 static void Release(void* self) {
61 auto checker = static_cast<PromiseTextureChecker*>(self);
62 checker->fReleaseCount++;
63 if (!checker->fShared) {
64 // This is only used in a single threaded fashion with a single promise image. So
65 // every fulfill should be balanced by a release before the next fulfill.
66 REPORTER_ASSERT(checker->fReporter, checker->fReleaseCount == checker->fFulfillCount);
67 }
68 }
DonePromiseTextureChecker69 static void Done(void* self) {
70 static_cast<PromiseTextureChecker*>(self)->fDoneCount++;
71 }
72 };
73
74 enum class ReleaseBalanceExpecation {
75 kBalanced,
76 kBalancedOrPlusOne,
77 kAny
78 };
79
check_fulfill_and_release_cnts(const PromiseTextureChecker & promiseChecker,ReleaseBalanceExpecation balanceExpecation,int expectedFulfillCnt,int expectedReleaseCnt,bool expectedRequired,int expectedDoneCnt,skiatest::Reporter * reporter)80 static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseChecker,
81 ReleaseBalanceExpecation balanceExpecation,
82 int expectedFulfillCnt,
83 int expectedReleaseCnt,
84 bool expectedRequired,
85 int expectedDoneCnt,
86 skiatest::Reporter* reporter) {
87 bool result = true;
88 int countDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
89 // FulfillCount should always equal ReleaseCount or be at most one higher
90 if (countDiff != 0) {
91 if (balanceExpecation == ReleaseBalanceExpecation::kBalanced) {
92 result = false;
93 REPORTER_ASSERT(reporter, 0 == countDiff);
94 } else if (countDiff != 1 &&
95 balanceExpecation == ReleaseBalanceExpecation::kBalancedOrPlusOne) {
96 result = false;
97 REPORTER_ASSERT(reporter, 0 == countDiff || 1 == countDiff);
98 } else if (countDiff < 0) {
99 result = false;
100 REPORTER_ASSERT(reporter, countDiff >= 0);
101 }
102 }
103
104 int fulfillDiff = expectedFulfillCnt - promiseChecker.fFulfillCount;
105 REPORTER_ASSERT(reporter, fulfillDiff >= 0);
106 if (fulfillDiff != 0) {
107 if (expectedRequired) {
108 result = false;
109 REPORTER_ASSERT(reporter, expectedFulfillCnt == promiseChecker.fFulfillCount);
110 } else if (fulfillDiff > 1) {
111 result = false;
112 REPORTER_ASSERT(reporter, fulfillDiff <= 1);
113 }
114 }
115
116 int releaseDiff = expectedReleaseCnt - promiseChecker.fReleaseCount;
117 REPORTER_ASSERT(reporter, releaseDiff >= 0);
118 if (releaseDiff != 0) {
119 if (expectedRequired) {
120 result = false;
121 REPORTER_ASSERT(reporter, expectedReleaseCnt == promiseChecker.fReleaseCount);
122 } else if (releaseDiff > 1) {
123 result = false;
124 REPORTER_ASSERT(reporter, releaseDiff <= 1);
125 }
126 }
127
128 if (expectedDoneCnt != promiseChecker.fDoneCount) {
129 result = false;
130 REPORTER_ASSERT(reporter, expectedDoneCnt == promiseChecker.fDoneCount);
131 }
132
133 return result;
134 }
135
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTestNoDelayedRelease,reporter,ctxInfo)136 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTestNoDelayedRelease, reporter, ctxInfo) {
137 const int kWidth = 10;
138 const int kHeight = 10;
139
140 GrContext* ctx = ctxInfo.grContext();
141 GrGpu* gpu = ctx->contextPriv().getGpu();
142
143 for (bool releaseImageEarly : {true, false}) {
144 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
145 nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
146 REPORTER_ASSERT(reporter, backendTex.isValid());
147
148 GrBackendFormat backendFormat = backendTex.getBackendFormat();
149 REPORTER_ASSERT(reporter, backendFormat.isValid());
150
151 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
152 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
153 sk_sp<SkImage> refImg(
154 SkImage_Gpu::MakePromiseTexture(
155 ctx, backendFormat, kWidth, kHeight,
156 GrMipMapped::kNo, texOrigin,
157 kRGBA_8888_SkColorType, kPremul_SkAlphaType,
158 nullptr,
159 PromiseTextureChecker::Fulfill,
160 PromiseTextureChecker::Release,
161 PromiseTextureChecker::Done,
162 &promiseChecker,
163 SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
164
165 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
166 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
167 SkCanvas* canvas = surface->getCanvas();
168
169 int expectedFulfillCnt = 0;
170 int expectedReleaseCnt = 0;
171 int expectedDoneCnt = 0;
172 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
173
174 canvas->drawImage(refImg, 0, 0);
175 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
176 balanceExpecation,
177 expectedFulfillCnt,
178 expectedReleaseCnt,
179 true,
180 expectedDoneCnt,
181 reporter));
182
183 bool isVulkan = GrBackendApi::kVulkan == ctx->backend();
184 canvas->flush();
185 expectedFulfillCnt++;
186 expectedReleaseCnt++;
187 if (isVulkan) {
188 balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
189 }
190 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
191 balanceExpecation,
192 expectedFulfillCnt,
193 expectedReleaseCnt,
194 !isVulkan,
195 expectedDoneCnt,
196 reporter));
197
198 gpu->testingOnly_flushGpuAndSync();
199 balanceExpecation = ReleaseBalanceExpecation::kBalanced;
200 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
201 balanceExpecation,
202 expectedFulfillCnt,
203 expectedReleaseCnt,
204 true,
205 expectedDoneCnt,
206 reporter));
207
208 canvas->drawImage(refImg, 0, 0);
209 canvas->drawImage(refImg, 0, 0);
210
211 canvas->flush();
212 expectedFulfillCnt++;
213 expectedReleaseCnt++;
214
215 gpu->testingOnly_flushGpuAndSync();
216 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
217 balanceExpecation,
218 expectedFulfillCnt,
219 expectedReleaseCnt,
220 true,
221 expectedDoneCnt,
222 reporter));
223
224 // Now test code path on Vulkan where we released the texture, but the GPU isn't done with
225 // resource yet and we do another draw. We should only call fulfill on the first draw and
226 // use the cached GrBackendTexture on the second. Release should only be called after the
227 // second draw is finished.
228 canvas->drawImage(refImg, 0, 0);
229 canvas->flush();
230 expectedFulfillCnt++;
231 expectedReleaseCnt++;
232 if (isVulkan) {
233 balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
234 }
235 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
236 balanceExpecation,
237 expectedFulfillCnt,
238 expectedReleaseCnt,
239 !isVulkan,
240 expectedDoneCnt,
241 reporter));
242
243 canvas->drawImage(refImg, 0, 0);
244
245 if (releaseImageEarly) {
246 refImg.reset();
247 }
248
249 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
250 balanceExpecation,
251 expectedFulfillCnt,
252 expectedReleaseCnt,
253 !isVulkan,
254 expectedDoneCnt,
255 reporter));
256
257 canvas->flush();
258 expectedFulfillCnt++;
259
260 gpu->testingOnly_flushGpuAndSync();
261 expectedReleaseCnt++;
262 if (releaseImageEarly) {
263 expectedDoneCnt++;
264 }
265 balanceExpecation = ReleaseBalanceExpecation::kBalanced;
266 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
267 balanceExpecation,
268 expectedFulfillCnt,
269 expectedReleaseCnt,
270 !isVulkan,
271 expectedDoneCnt,
272 reporter));
273 expectedFulfillCnt = promiseChecker.fFulfillCount;
274 expectedReleaseCnt = promiseChecker.fReleaseCount;
275
276 if (!releaseImageEarly) {
277 refImg.reset();
278 expectedDoneCnt++;
279 }
280
281 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
282 balanceExpecation,
283 expectedFulfillCnt,
284 expectedReleaseCnt,
285 true,
286 expectedDoneCnt,
287 reporter));
288
289 gpu->deleteTestingOnlyBackendTexture(backendTex);
290 }
291 }
292
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTestDelayedRelease,reporter,ctxInfo)293 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTestDelayedRelease, reporter, ctxInfo) {
294 const int kWidth = 10;
295 const int kHeight = 10;
296
297 GrContext* ctx = ctxInfo.grContext();
298 GrGpu* gpu = ctx->contextPriv().getGpu();
299
300 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
301 nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo);
302 REPORTER_ASSERT(reporter, backendTex.isValid());
303
304 GrBackendFormat backendFormat = backendTex.getBackendFormat();
305 REPORTER_ASSERT(reporter, backendFormat.isValid());
306
307 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
308 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
309 sk_sp<SkImage> refImg(
310 SkImage_Gpu::MakePromiseTexture(
311 ctx, backendFormat, kWidth, kHeight,
312 GrMipMapped::kNo, texOrigin,
313 kRGBA_8888_SkColorType, kPremul_SkAlphaType,
314 nullptr,
315 PromiseTextureChecker::Fulfill,
316 PromiseTextureChecker::Release,
317 PromiseTextureChecker::Done,
318 &promiseChecker,
319 SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes));
320
321 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
322 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
323 SkCanvas* canvas = surface->getCanvas();
324
325 int expectedFulfillCnt = 0;
326 int expectedReleaseCnt = 0;
327 int expectedDoneCnt = 0;
328 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
329
330 canvas->drawImage(refImg, 0, 0);
331 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
332 balanceExpecation,
333 expectedFulfillCnt,
334 expectedReleaseCnt,
335 true,
336 expectedDoneCnt,
337 reporter));
338
339 bool isVulkan = GrBackendApi::kVulkan == ctx->backend();
340 canvas->flush();
341 expectedFulfillCnt++;
342 // Because we've delayed release, we expect a +1 balance.
343 balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
344 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
345 balanceExpecation,
346 expectedFulfillCnt,
347 expectedReleaseCnt,
348 !isVulkan,
349 expectedDoneCnt,
350 reporter));
351
352 gpu->testingOnly_flushGpuAndSync();
353 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
354 balanceExpecation,
355 expectedFulfillCnt,
356 expectedReleaseCnt,
357 true,
358 expectedDoneCnt,
359 reporter));
360
361 canvas->drawImage(refImg, 0, 0);
362 canvas->drawImage(refImg, 0, 0);
363
364 canvas->flush();
365
366 gpu->testingOnly_flushGpuAndSync();
367 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
368 balanceExpecation,
369 expectedFulfillCnt,
370 expectedReleaseCnt,
371 true,
372 expectedDoneCnt,
373 reporter));
374
375 canvas->drawImage(refImg, 0, 0);
376 canvas->flush();
377 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
378 balanceExpecation,
379 expectedFulfillCnt,
380 expectedReleaseCnt,
381 !isVulkan,
382 expectedDoneCnt,
383 reporter));
384
385 canvas->drawImage(refImg, 0, 0);
386
387 refImg.reset();
388
389 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
390 balanceExpecation,
391 expectedFulfillCnt,
392 expectedReleaseCnt,
393 !isVulkan,
394 expectedDoneCnt,
395 reporter));
396
397 canvas->flush();
398 gpu->testingOnly_flushGpuAndSync();
399 // We released the image already and we flushed and synced.
400 balanceExpecation = ReleaseBalanceExpecation::kBalanced;
401 expectedReleaseCnt++;
402 expectedDoneCnt++;
403 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
404 balanceExpecation,
405 expectedFulfillCnt,
406 expectedReleaseCnt,
407 !isVulkan,
408 expectedDoneCnt,
409 reporter));
410
411 gpu->deleteTestingOnlyBackendTexture(backendTex);
412 }
413
414 // Tests replacing the backing texture for a promise image after a release and then refulfilling in
415 // the SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo case.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse,reporter,ctxInfo)416 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuse, reporter, ctxInfo) {
417 const int kWidth = 10;
418 const int kHeight = 10;
419
420 GrContext* ctx = ctxInfo.grContext();
421 GrGpu* gpu = ctx->contextPriv().getGpu();
422
423 GrBackendTexture backendTex1 = gpu->createTestingOnlyBackendTexture(
424 nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, false, GrMipMapped::kNo);
425 GrBackendTexture backendTex2 = gpu->createTestingOnlyBackendTexture(
426 nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, false, GrMipMapped::kNo);
427 GrBackendTexture backendTex3 = gpu->createTestingOnlyBackendTexture(
428 nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, false, GrMipMapped::kNo);
429 REPORTER_ASSERT(reporter, backendTex1.isValid());
430 REPORTER_ASSERT(reporter, backendTex2.isValid());
431 REPORTER_ASSERT(reporter, backendTex3.isValid());
432
433 GrBackendFormat backendFormat = backendTex1.getBackendFormat();
434 REPORTER_ASSERT(reporter, backendFormat.isValid());
435 REPORTER_ASSERT(reporter, backendFormat == backendTex2.getBackendFormat());
436 REPORTER_ASSERT(reporter, backendFormat == backendTex3.getBackendFormat());
437
438 PromiseTextureChecker promiseChecker(backendTex1, reporter, true);
439 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
440 sk_sp<SkImage> refImg(
441 SkImage_Gpu::MakePromiseTexture(
442 ctx, backendFormat, kWidth, kHeight,
443 GrMipMapped::kNo, texOrigin,
444 kRGBA_8888_SkColorType, kPremul_SkAlphaType,
445 nullptr,
446 PromiseTextureChecker::Fulfill,
447 PromiseTextureChecker::Release,
448 PromiseTextureChecker::Done,
449 &promiseChecker,
450 SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
451
452 SkImageInfo info =
453 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
454 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
455 SkCanvas* canvas = surface->getCanvas();
456
457 int expectedFulfillCnt = 0;
458 int expectedReleaseCnt = 0;
459 int expectedDoneCnt = 0;
460
461 canvas->drawImage(refImg, 0, 0);
462 canvas->drawImage(refImg, 5, 5);
463 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
464 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
465 balanceExpecation,
466 expectedFulfillCnt,
467 expectedReleaseCnt,
468 true,
469 expectedDoneCnt,
470 reporter));
471
472 bool isVulkan = GrBackendApi::kVulkan == ctx->backend();
473 canvas->flush();
474 expectedFulfillCnt++;
475 expectedReleaseCnt++;
476 if (isVulkan) {
477 balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
478 }
479 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
480 balanceExpecation,
481 expectedFulfillCnt,
482 expectedReleaseCnt,
483 !isVulkan,
484 expectedDoneCnt,
485 reporter));
486 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
487 promiseChecker.fLastFulfilledTexture, backendTex1));
488 // We should have put a GrTexture for this fulfillment into the cache.
489 auto keys = promiseChecker.uniqueKeys();
490 REPORTER_ASSERT(reporter, keys.count() == 1);
491 GrUniqueKey texKey1;
492 if (keys.count()) {
493 texKey1 = keys[0];
494 }
495 REPORTER_ASSERT(reporter, texKey1.isValid());
496 REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
497
498 gpu->testingOnly_flushGpuAndSync();
499 balanceExpecation = ReleaseBalanceExpecation::kBalanced;
500 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
501 balanceExpecation,
502 expectedFulfillCnt,
503 expectedReleaseCnt,
504 true,
505 expectedDoneCnt,
506 reporter));
507 REPORTER_ASSERT(reporter,
508 GrBackendTexture::TestingOnly_Equals(
509 promiseChecker.replaceTexture()->backendTexture(), backendTex1));
510 gpu->deleteTestingOnlyBackendTexture(backendTex1);
511
512 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
513 // We should have invalidated the key on the previously cached texture (after ensuring
514 // invalidation messages have been processed by calling purgeAsNeeded.)
515 REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey1));
516
517 promiseChecker.replaceTexture(backendTex2);
518
519 canvas->drawImage(refImg, 0, 0);
520 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
521 balanceExpecation,
522 expectedFulfillCnt,
523 expectedReleaseCnt,
524 true,
525 expectedDoneCnt,
526 reporter));
527
528 canvas->flush();
529 expectedFulfillCnt++;
530 expectedReleaseCnt++;
531 if (isVulkan) {
532 balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne;
533 }
534 // Second texture should be in the cache.
535 keys = promiseChecker.uniqueKeys();
536 REPORTER_ASSERT(reporter, keys.count() == 1);
537 GrUniqueKey texKey2;
538 if (keys.count()) {
539 texKey2 = keys[0];
540 }
541 REPORTER_ASSERT(reporter, texKey2.isValid() && texKey2 != texKey1);
542 REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
543
544 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
545 balanceExpecation,
546 expectedFulfillCnt,
547 expectedReleaseCnt,
548 !isVulkan,
549 expectedDoneCnt,
550 reporter));
551 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
552 promiseChecker.fLastFulfilledTexture, backendTex2));
553
554 gpu->testingOnly_flushGpuAndSync();
555 balanceExpecation = ReleaseBalanceExpecation::kBalanced;
556 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
557 balanceExpecation,
558 expectedFulfillCnt,
559 expectedReleaseCnt,
560 true,
561 expectedDoneCnt,
562 reporter));
563
564 // Because we have kept the SkPromiseImageTexture alive, we should be able to use it again and
565 // hit the cache.
566 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
567 REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
568
569 canvas->drawImage(refImg, 0, 0);
570
571 canvas->flush();
572 gpu->testingOnly_flushGpuAndSync();
573 expectedFulfillCnt++;
574 expectedReleaseCnt++;
575 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
576 balanceExpecation,
577 expectedFulfillCnt,
578 expectedReleaseCnt,
579 true,
580 expectedDoneCnt,
581 reporter));
582
583 // Make sure we didn't add another key and that the second texture is still alive in the cache.
584 keys = promiseChecker.uniqueKeys();
585 REPORTER_ASSERT(reporter, keys.count() == 1);
586 if (keys.count()) {
587 REPORTER_ASSERT(reporter, texKey2 == keys[0]);
588 }
589 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
590 REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
591
592 // Now we test keeping tex2 alive but fulfilling with a new texture.
593 sk_sp<const SkPromiseImageTexture> promiseImageTexture2 =
594 promiseChecker.replaceTexture(backendTex3);
595 REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(
596 promiseImageTexture2->backendTexture(), backendTex2));
597
598 canvas->drawImage(refImg, 0, 0);
599
600 canvas->flush();
601 gpu->testingOnly_flushGpuAndSync();
602 expectedFulfillCnt++;
603 expectedReleaseCnt++;
604 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
605 balanceExpecation,
606 expectedFulfillCnt,
607 expectedReleaseCnt,
608 true,
609 expectedDoneCnt,
610 reporter));
611
612 keys = promiseChecker.uniqueKeys();
613 REPORTER_ASSERT(reporter, keys.count() == 1);
614 GrUniqueKey texKey3;
615 if (keys.count()) {
616 texKey3 = keys[0];
617 }
618 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
619 REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey2));
620 REPORTER_ASSERT(reporter, ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
621 gpu->deleteTestingOnlyBackendTexture(promiseImageTexture2->backendTexture());
622
623 // Make a new promise image also backed by texture 3.
624 sk_sp<SkImage> refImg2(
625 SkImage_Gpu::MakePromiseTexture(
626 ctx, backendFormat, kWidth, kHeight,
627 GrMipMapped::kNo, texOrigin,
628 kRGBA_8888_SkColorType, kPremul_SkAlphaType,
629 nullptr,
630 PromiseTextureChecker::Fulfill,
631 PromiseTextureChecker::Release,
632 PromiseTextureChecker::Done,
633 &promiseChecker,
634 SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
635 canvas->drawImage(refImg, 0, 0);
636 canvas->drawImage(refImg2, 1, 1);
637
638 canvas->flush();
639 gpu->testingOnly_flushGpuAndSync();
640 expectedFulfillCnt += 2;
641 expectedReleaseCnt += 2;
642 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
643 balanceExpecation,
644 expectedFulfillCnt,
645 expectedReleaseCnt,
646 true,
647 expectedDoneCnt,
648 reporter));
649
650 // The two images should share a single GrTexture by using the same key. The key is only
651 // dependent on the pixel config and the PromiseImageTexture key.
652 keys = promiseChecker.uniqueKeys();
653 REPORTER_ASSERT(reporter, keys.count() == 1);
654 if (keys.count() > 0) {
655 REPORTER_ASSERT(reporter, texKey3 == keys[0]);
656 }
657 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
658
659 // If we delete the SkPromiseImageTexture we should trigger both key removals.
660 REPORTER_ASSERT(reporter,
661 GrBackendTexture::TestingOnly_Equals(
662 promiseChecker.replaceTexture()->backendTexture(), backendTex3));
663
664 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
665 REPORTER_ASSERT(reporter, !ctx->contextPriv().resourceProvider()->findByUniqueKey<>(texKey3));
666 gpu->deleteTestingOnlyBackendTexture(backendTex3);
667
668 // After deleting each image we should get a done call.
669 refImg.reset();
670 ++expectedDoneCnt;
671 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
672 balanceExpecation,
673 expectedFulfillCnt,
674 expectedReleaseCnt,
675 true,
676 expectedDoneCnt,
677 reporter));
678 refImg2.reset();
679 ++expectedDoneCnt;
680 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
681 balanceExpecation,
682 expectedFulfillCnt,
683 expectedReleaseCnt,
684 true,
685 expectedDoneCnt,
686 reporter));
687 }
688
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig,reporter,ctxInfo)689 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, reporter, ctxInfo) {
690 // Try making two promise SkImages backed by the same texture but with different configs.
691 // This will only be testable on backends where a single texture format (8bit red unorm) can
692 // be used for alpha and gray image color types.
693
694 const int kWidth = 10;
695 const int kHeight = 10;
696
697 GrContext* ctx = ctxInfo.grContext();
698 GrGpu* gpu = ctx->contextPriv().getGpu();
699
700 GrBackendTexture backendTex1 = gpu->createTestingOnlyBackendTexture(
701 nullptr, kWidth, kHeight, GrColorType::kGray_8, false, GrMipMapped::kNo);
702 REPORTER_ASSERT(reporter, backendTex1.isValid());
703
704 GrBackendTexture backendTex2 = gpu->createTestingOnlyBackendTexture(
705 nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo);
706 REPORTER_ASSERT(reporter, backendTex2.isValid());
707 if (backendTex1.getBackendFormat() != backendTex2.getBackendFormat()) {
708 gpu->deleteTestingOnlyBackendTexture(backendTex1);
709 return;
710 }
711 // We only needed this texture to check that alpha and gray color types use the same format.
712 gpu->deleteTestingOnlyBackendTexture(backendTex2);
713
714 SkImageInfo info =
715 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
716 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
717 SkCanvas* canvas = surface->getCanvas();
718
719 for (auto delayRelease : {SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo,
720 SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes}) {
721 PromiseTextureChecker promiseChecker(backendTex1, reporter, true);
722 sk_sp<SkImage> alphaImg(SkImage_Gpu::MakePromiseTexture(
723 ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
724 kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
725 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
726 PromiseTextureChecker::Done, &promiseChecker, delayRelease));
727 REPORTER_ASSERT(reporter, alphaImg);
728
729 sk_sp<SkImage> grayImg(SkImage_Gpu::MakePromiseTexture(
730 ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
731 kBottomLeft_GrSurfaceOrigin, kGray_8_SkColorType, kOpaque_SkAlphaType, nullptr,
732 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
733 PromiseTextureChecker::Done, &promiseChecker, delayRelease));
734 REPORTER_ASSERT(reporter, grayImg);
735
736 canvas->drawImage(alphaImg, 0, 0);
737 canvas->drawImage(grayImg, 1, 1);
738 canvas->flush();
739 gpu->testingOnly_flushGpuAndSync();
740
741 int expectedFulfillCnt = 2;
742 int expectedReleaseCnt = 0;
743 int expectedDoneCnt = 0;
744 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kAny;
745 if (delayRelease == SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo) {
746 expectedReleaseCnt = 2;
747 balanceExpecation = ReleaseBalanceExpecation::kBalanced;
748 }
749 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
750 balanceExpecation,
751 expectedFulfillCnt,
752 expectedReleaseCnt,
753 true,
754 expectedDoneCnt,
755 reporter));
756
757 // Because they use different configs, each image should have created a different GrTexture
758 // and they both should still be cached.
759 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
760
761 auto keys = promiseChecker.uniqueKeys();
762 REPORTER_ASSERT(reporter, keys.count() == 2);
763 for (const auto& key : keys) {
764 auto surf = ctx->contextPriv().resourceProvider()->findByUniqueKey<GrSurface>(key);
765 REPORTER_ASSERT(reporter, surf && surf->asTexture());
766 if (surf && surf->asTexture()) {
767 REPORTER_ASSERT(reporter,
768 !GrBackendTexture::TestingOnly_Equals(
769 backendTex1, surf->asTexture()->getBackendTexture()));
770 }
771 }
772
773 // Change the backing texture, this should invalidate the keys.
774 promiseChecker.replaceTexture();
775 ctx->contextPriv().getResourceCache()->purgeAsNeeded();
776
777 for (const auto& key : keys) {
778 auto surf = ctx->contextPriv().resourceProvider()->findByUniqueKey<GrSurface>(key);
779 REPORTER_ASSERT(reporter, !surf);
780 }
781 }
782 gpu->deleteTestingOnlyBackendTexture(backendTex1);
783 }
784
DEF_GPUTEST(PromiseImageTextureShutdown,reporter,ctxInfo)785 DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
786 const int kWidth = 10;
787 const int kHeight = 10;
788
789 // Different ways of killing contexts.
790 using DeathFn = std::function<void(sk_gpu_test::GrContextFactory*, GrContext*)>;
791 DeathFn destroy = [](sk_gpu_test::GrContextFactory* factory, GrContext* context) {
792 factory->destroyContexts();
793 };
794 DeathFn abandon = [](sk_gpu_test::GrContextFactory* factory, GrContext* context) {
795 context->abandonContext();
796 };
797 DeathFn releaseResourcesAndAbandon = [](sk_gpu_test::GrContextFactory* factory,
798 GrContext* context) {
799 context->releaseResourcesAndAbandonContext();
800 };
801
802 for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) {
803 auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type);
804 // These tests are difficult to get working with Vulkan. See http://skbug.com/8705
805 // and http://skbug.com/8275
806 GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType);
807 if (api == GrBackendApi::kVulkan) {
808 continue;
809 }
810 DeathFn contextKillers[] = {destroy, abandon, releaseResourcesAndAbandon};
811 for (auto contextDeath : contextKillers) {
812 sk_gpu_test::GrContextFactory factory;
813 auto ctx = factory.get(contextType);
814 if (!ctx) {
815 continue;
816 }
817 GrGpu* gpu = ctx->contextPriv().getGpu();
818
819 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
820 nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo);
821 REPORTER_ASSERT(reporter, backendTex.isValid());
822
823 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType,
824 kPremul_SkAlphaType);
825 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
826 SkCanvas* canvas = surface->getCanvas();
827
828 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
829 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(
830 ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
831 kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
832 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
833 PromiseTextureChecker::Done, &promiseChecker,
834 SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
835 REPORTER_ASSERT(reporter, image);
836
837 canvas->drawImage(image, 0, 0);
838 image.reset();
839 // If the surface still holds a ref to the context then the factory will not be able
840 // to destroy the context (and instead will release-all-and-abandon).
841 surface.reset();
842
843 ctx->flush();
844 contextDeath(&factory, ctx);
845
846 int expectedFulfillCnt = 1;
847 int expectedReleaseCnt = 1;
848 int expectedDoneCnt = 1;
849 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced;
850 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker,
851 balanceExpecation,
852 expectedFulfillCnt,
853 expectedReleaseCnt,
854 true,
855 expectedDoneCnt,
856 reporter));
857 }
858 }
859 }
860
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache,reporter,ctxInfo)861 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache, reporter, ctxInfo) {
862 const int kWidth = 10;
863 const int kHeight = 10;
864
865 GrContext* ctx = ctxInfo.grContext();
866 GrGpu* gpu = ctx->contextPriv().getGpu();
867
868 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
869 nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo);
870 REPORTER_ASSERT(reporter, backendTex.isValid());
871
872 SkImageInfo info =
873 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
874 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
875 SkCanvas* canvas = surface->getCanvas();
876
877 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
878 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(
879 ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo,
880 kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr,
881 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release,
882 PromiseTextureChecker::Done, &promiseChecker,
883 SkDeferredDisplayListRecorder::DelayReleaseCallback::kNo));
884 REPORTER_ASSERT(reporter, image);
885
886 // Make the cache full. This tests that we don't preemptively purge cached textures for
887 // fulfillment due to cache pressure.
888 static constexpr int kMaxResources = 10;
889 static constexpr int kMaxBytes = 100;
890 ctx->setResourceCacheLimits(kMaxResources, kMaxBytes);
891 sk_sp<GrTexture> textures[2 * kMaxResources];
892 for (int i = 0; i < 2 * kMaxResources; ++i) {
893 GrSurfaceDesc desc;
894 desc.fConfig = kRGBA_8888_GrPixelConfig;
895 desc.fWidth = desc.fHeight = 100;
896 textures[i] = ctx->contextPriv().resourceProvider()->createTexture(desc, SkBudgeted::kYes);
897 REPORTER_ASSERT(reporter, textures[i]);
898 }
899
900 // Relying on the asserts in the promiseImageChecker to ensure that fulfills and releases are
901 // properly ordered.
902 canvas->drawImage(image, 0, 0);
903 canvas->flush();
904 canvas->drawImage(image, 1, 0);
905 canvas->flush();
906 canvas->drawImage(image, 2, 0);
907 canvas->flush();
908 canvas->drawImage(image, 3, 0);
909 canvas->flush();
910 canvas->drawImage(image, 4, 0);
911 canvas->flush();
912 canvas->drawImage(image, 5, 0);
913 canvas->flush();
914 // Must call this to ensure that all callbacks are performed before the checker is destroyed.
915 gpu->testingOnly_flushGpuAndSync();
916 gpu->deleteTestingOnlyBackendTexture(backendTex);
917 }
918