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 "Test.h" 9 10 #include "GrBackendSurface.h" 11 #include "GrContextPriv.h" 12 #include "GrGpu.h" 13 #include "GrTexture.h" 14 #include "SkImage_Gpu.h" 15 #include "SkPromiseImageTexture.h" 16 17 using namespace sk_gpu_test; 18 19 struct PromiseTextureChecker { 20 // shared indicates whether the backend texture is used to fulfill more than one promise 21 // image. 22 explicit PromiseTextureChecker(const GrBackendTexture& tex, skiatest::Reporter* reporter, 23 bool shared) 24 : fTexture(SkPromiseImageTexture::Make(tex)) 25 , fReporter(reporter) 26 , fShared(shared) 27 , fFulfillCount(0) 28 , fReleaseCount(0) 29 , fDoneCount(0) {} 30 sk_sp<SkPromiseImageTexture> fTexture; 31 skiatest::Reporter* fReporter; 32 bool fShared; 33 int fFulfillCount; 34 int fReleaseCount; 35 int fDoneCount; 36 37 /** 38 * Releases the SkPromiseImageTexture. Used to test that cached GrTexture representations 39 * in the cache are freed. 40 */ 41 void releaseTexture() { fTexture.reset(); } 42 43 SkTArray<GrUniqueKey> uniqueKeys() const { 44 return fTexture->testingOnly_uniqueKeysToInvalidate(); 45 } 46 47 static sk_sp<SkPromiseImageTexture> Fulfill(void* self) { 48 auto checker = static_cast<PromiseTextureChecker*>(self); 49 checker->fFulfillCount++; 50 return checker->fTexture; 51 } 52 static void Release(void* self) { 53 auto checker = static_cast<PromiseTextureChecker*>(self); 54 checker->fReleaseCount++; 55 if (!checker->fShared) { 56 // This is only used in a single threaded fashion with a single promise image. So 57 // every fulfill should be balanced by a release before the next fulfill. 58 REPORTER_ASSERT(checker->fReporter, checker->fReleaseCount == checker->fFulfillCount); 59 } 60 } 61 static void Done(void* self) { 62 static_cast<PromiseTextureChecker*>(self)->fDoneCount++; 63 } 64 }; 65 66 enum class ReleaseBalanceExpecation { 67 kBalanced, 68 kBalancedOrPlusOne, 69 kAny 70 }; 71 72 static bool check_fulfill_and_release_cnts(const PromiseTextureChecker& promiseChecker, 73 ReleaseBalanceExpecation balanceExpecation, 74 int expectedFulfillCnt, 75 int expectedReleaseCnt, 76 bool expectedRequired, 77 int expectedDoneCnt, 78 skiatest::Reporter* reporter) { 79 bool result = true; 80 int countDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount; 81 // FulfillCount should always equal ReleaseCount or be at most one higher 82 if (countDiff != 0) { 83 if (balanceExpecation == ReleaseBalanceExpecation::kBalanced) { 84 result = false; 85 REPORTER_ASSERT(reporter, 0 == countDiff); 86 } else if (countDiff != 1 && 87 balanceExpecation == ReleaseBalanceExpecation::kBalancedOrPlusOne) { 88 result = false; 89 REPORTER_ASSERT(reporter, 0 == countDiff || 1 == countDiff); 90 } else if (countDiff < 0) { 91 result = false; 92 REPORTER_ASSERT(reporter, countDiff >= 0); 93 } 94 } 95 96 int fulfillDiff = expectedFulfillCnt - promiseChecker.fFulfillCount; 97 REPORTER_ASSERT(reporter, fulfillDiff >= 0); 98 if (fulfillDiff != 0) { 99 if (expectedRequired) { 100 result = false; 101 REPORTER_ASSERT(reporter, expectedFulfillCnt == promiseChecker.fFulfillCount); 102 } else if (fulfillDiff > 1) { 103 result = false; 104 REPORTER_ASSERT(reporter, fulfillDiff <= 1); 105 } 106 } 107 108 int releaseDiff = expectedReleaseCnt - promiseChecker.fReleaseCount; 109 REPORTER_ASSERT(reporter, releaseDiff >= 0); 110 if (releaseDiff != 0) { 111 if (expectedRequired) { 112 result = false; 113 REPORTER_ASSERT(reporter, expectedReleaseCnt == promiseChecker.fReleaseCount); 114 } else if (releaseDiff > 1) { 115 result = false; 116 REPORTER_ASSERT(reporter, releaseDiff <= 1); 117 } 118 } 119 120 if (expectedDoneCnt != promiseChecker.fDoneCount) { 121 result = false; 122 REPORTER_ASSERT(reporter, expectedDoneCnt == promiseChecker.fDoneCount); 123 } 124 125 return result; 126 } 127 128 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) { 129 const int kWidth = 10; 130 const int kHeight = 10; 131 132 GrContext* ctx = ctxInfo.grContext(); 133 GrGpu* gpu = ctx->priv().getGpu(); 134 135 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture( 136 nullptr, kWidth, kHeight, GrColorType::kRGBA_8888, true, GrMipMapped::kNo); 137 REPORTER_ASSERT(reporter, backendTex.isValid()); 138 139 GrBackendFormat backendFormat = backendTex.getBackendFormat(); 140 REPORTER_ASSERT(reporter, backendFormat.isValid()); 141 142 PromiseTextureChecker promiseChecker(backendTex, reporter, false); 143 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin; 144 sk_sp<SkImage> refImg( 145 SkImage_Gpu::MakePromiseTexture( 146 ctx, backendFormat, kWidth, kHeight, 147 GrMipMapped::kNo, texOrigin, 148 kRGBA_8888_SkColorType, kPremul_SkAlphaType, 149 nullptr, 150 PromiseTextureChecker::Fulfill, 151 PromiseTextureChecker::Release, 152 PromiseTextureChecker::Done, 153 &promiseChecker)); 154 155 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 156 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); 157 SkCanvas* canvas = surface->getCanvas(); 158 159 int expectedFulfillCnt = 0; 160 int expectedReleaseCnt = 0; 161 int expectedDoneCnt = 0; 162 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced; 163 164 canvas->drawImage(refImg, 0, 0); 165 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 166 balanceExpecation, 167 expectedFulfillCnt, 168 expectedReleaseCnt, 169 true, 170 expectedDoneCnt, 171 reporter)); 172 173 bool isVulkan = GrBackendApi::kVulkan == ctx->backend(); 174 surface->flush(); 175 expectedFulfillCnt++; 176 // Because we've delayed release, we expect a +1 balance. 177 balanceExpecation = ReleaseBalanceExpecation::kBalancedOrPlusOne; 178 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 179 balanceExpecation, 180 expectedFulfillCnt, 181 expectedReleaseCnt, 182 !isVulkan, 183 expectedDoneCnt, 184 reporter)); 185 186 gpu->testingOnly_flushGpuAndSync(); 187 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 188 balanceExpecation, 189 expectedFulfillCnt, 190 expectedReleaseCnt, 191 true, 192 expectedDoneCnt, 193 reporter)); 194 195 canvas->drawImage(refImg, 0, 0); 196 canvas->drawImage(refImg, 0, 0); 197 198 surface->flush(); 199 200 gpu->testingOnly_flushGpuAndSync(); 201 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 202 balanceExpecation, 203 expectedFulfillCnt, 204 expectedReleaseCnt, 205 true, 206 expectedDoneCnt, 207 reporter)); 208 209 canvas->drawImage(refImg, 0, 0); 210 surface->flush(); 211 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 212 balanceExpecation, 213 expectedFulfillCnt, 214 expectedReleaseCnt, 215 !isVulkan, 216 expectedDoneCnt, 217 reporter)); 218 219 canvas->drawImage(refImg, 0, 0); 220 221 refImg.reset(); 222 223 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 224 balanceExpecation, 225 expectedFulfillCnt, 226 expectedReleaseCnt, 227 !isVulkan, 228 expectedDoneCnt, 229 reporter)); 230 231 surface->flush(); 232 gpu->testingOnly_flushGpuAndSync(); 233 // We released the image already and we flushed and synced. 234 balanceExpecation = ReleaseBalanceExpecation::kBalanced; 235 expectedReleaseCnt++; 236 expectedDoneCnt++; 237 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 238 balanceExpecation, 239 expectedFulfillCnt, 240 expectedReleaseCnt, 241 !isVulkan, 242 expectedDoneCnt, 243 reporter)); 244 245 gpu->deleteTestingOnlyBackendTexture(backendTex); 246 } 247 248 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureReuseDifferentConfig, reporter, ctxInfo) { 249 // Try making two promise SkImages backed by the same texture but with different configs. 250 // This will only be testable on backends where a single texture format (8bit red unorm) can 251 // be used for alpha and gray image color types. 252 253 const int kWidth = 10; 254 const int kHeight = 10; 255 256 GrContext* ctx = ctxInfo.grContext(); 257 GrGpu* gpu = ctx->priv().getGpu(); 258 259 GrBackendTexture backendTex1 = gpu->createTestingOnlyBackendTexture( 260 nullptr, kWidth, kHeight, GrColorType::kGray_8, false, GrMipMapped::kNo); 261 REPORTER_ASSERT(reporter, backendTex1.isValid()); 262 263 GrBackendTexture backendTex2 = gpu->createTestingOnlyBackendTexture( 264 nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo); 265 REPORTER_ASSERT(reporter, backendTex2.isValid()); 266 if (backendTex1.getBackendFormat() != backendTex2.getBackendFormat()) { 267 gpu->deleteTestingOnlyBackendTexture(backendTex1); 268 return; 269 } 270 // We only needed this texture to check that alpha and gray color types use the same format. 271 gpu->deleteTestingOnlyBackendTexture(backendTex2); 272 273 SkImageInfo info = 274 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 275 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); 276 SkCanvas* canvas = surface->getCanvas(); 277 278 PromiseTextureChecker promiseChecker(backendTex1, reporter, true); 279 sk_sp<SkImage> alphaImg(SkImage_Gpu::MakePromiseTexture( 280 ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo, 281 kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr, 282 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release, 283 PromiseTextureChecker::Done, &promiseChecker)); 284 REPORTER_ASSERT(reporter, alphaImg); 285 286 sk_sp<SkImage> grayImg(SkImage_Gpu::MakePromiseTexture( 287 ctx, backendTex1.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo, 288 kBottomLeft_GrSurfaceOrigin, kGray_8_SkColorType, kOpaque_SkAlphaType, nullptr, 289 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release, 290 PromiseTextureChecker::Done, &promiseChecker)); 291 REPORTER_ASSERT(reporter, grayImg); 292 293 canvas->drawImage(alphaImg, 0, 0); 294 canvas->drawImage(grayImg, 1, 1); 295 surface->flush(); 296 gpu->testingOnly_flushGpuAndSync(); 297 298 int expectedFulfillCnt = 2; 299 int expectedReleaseCnt = 0; 300 int expectedDoneCnt = 0; 301 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kAny; 302 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 303 balanceExpecation, 304 expectedFulfillCnt, 305 expectedReleaseCnt, 306 true, 307 expectedDoneCnt, 308 reporter)); 309 310 // Because they use different configs, each image should have created a different GrTexture 311 // and they both should still be cached. 312 ctx->priv().getResourceCache()->purgeAsNeeded(); 313 314 auto keys = promiseChecker.uniqueKeys(); 315 REPORTER_ASSERT(reporter, keys.count() == 2); 316 for (const auto& key : keys) { 317 auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key); 318 REPORTER_ASSERT(reporter, surf && surf->asTexture()); 319 if (surf && surf->asTexture()) { 320 REPORTER_ASSERT(reporter, 321 !GrBackendTexture::TestingOnly_Equals( 322 backendTex1, surf->asTexture()->getBackendTexture())); 323 } 324 } 325 326 // Change the backing texture, this should invalidate the keys. 327 promiseChecker.releaseTexture(); 328 ctx->priv().getResourceCache()->purgeAsNeeded(); 329 330 for (const auto& key : keys) { 331 auto surf = ctx->priv().resourceProvider()->findByUniqueKey<GrSurface>(key); 332 REPORTER_ASSERT(reporter, !surf); 333 } 334 335 // Must do this to ensure all callbacks occur before the PromiseImageChecker goes out of scope. 336 alphaImg.reset(); 337 grayImg.reset(); 338 ctx->flush(); 339 ctx->priv().getGpu()->testingOnly_flushGpuAndSync(); 340 341 gpu->deleteTestingOnlyBackendTexture(backendTex1); 342 } 343 344 DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) { 345 const int kWidth = 10; 346 const int kHeight = 10; 347 348 // Different ways of killing contexts. 349 using DeathFn = std::function<void(sk_gpu_test::GrContextFactory*, GrContext*)>; 350 DeathFn destroy = [](sk_gpu_test::GrContextFactory* factory, GrContext* context) { 351 factory->destroyContexts(); 352 }; 353 DeathFn abandon = [](sk_gpu_test::GrContextFactory* factory, GrContext* context) { 354 context->abandonContext(); 355 }; 356 DeathFn releaseResourcesAndAbandon = [](sk_gpu_test::GrContextFactory* factory, 357 GrContext* context) { 358 context->releaseResourcesAndAbandonContext(); 359 }; 360 361 for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) { 362 auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type); 363 // These tests are difficult to get working with Vulkan. See http://skbug.com/8705 364 // and http://skbug.com/8275 365 GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType); 366 if (api == GrBackendApi::kVulkan) { 367 continue; 368 } 369 DeathFn contextKillers[] = {destroy, abandon, releaseResourcesAndAbandon}; 370 for (auto contextDeath : contextKillers) { 371 sk_gpu_test::GrContextFactory factory; 372 auto ctx = factory.get(contextType); 373 if (!ctx) { 374 continue; 375 } 376 GrGpu* gpu = ctx->priv().getGpu(); 377 378 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture( 379 nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo); 380 REPORTER_ASSERT(reporter, backendTex.isValid()); 381 382 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, 383 kPremul_SkAlphaType); 384 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); 385 SkCanvas* canvas = surface->getCanvas(); 386 387 PromiseTextureChecker promiseChecker(backendTex, reporter, false); 388 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture( 389 ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo, 390 kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr, 391 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release, 392 PromiseTextureChecker::Done, &promiseChecker)); 393 REPORTER_ASSERT(reporter, image); 394 395 canvas->drawImage(image, 0, 0); 396 image.reset(); 397 // If the surface still holds a ref to the context then the factory will not be able 398 // to destroy the context (and instead will release-all-and-abandon). 399 surface.reset(); 400 401 ctx->flush(); 402 contextDeath(&factory, ctx); 403 404 int expectedFulfillCnt = 1; 405 int expectedReleaseCnt = 1; 406 int expectedDoneCnt = 1; 407 ReleaseBalanceExpecation balanceExpecation = ReleaseBalanceExpecation::kBalanced; 408 REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, 409 balanceExpecation, 410 expectedFulfillCnt, 411 expectedReleaseCnt, 412 true, 413 expectedDoneCnt, 414 reporter)); 415 } 416 } 417 } 418 419 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache, reporter, ctxInfo) { 420 const int kWidth = 10; 421 const int kHeight = 10; 422 423 GrContext* ctx = ctxInfo.grContext(); 424 GrGpu* gpu = ctx->priv().getGpu(); 425 426 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture( 427 nullptr, kWidth, kHeight, GrColorType::kAlpha_8, false, GrMipMapped::kNo); 428 REPORTER_ASSERT(reporter, backendTex.isValid()); 429 430 SkImageInfo info = 431 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 432 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); 433 SkCanvas* canvas = surface->getCanvas(); 434 435 PromiseTextureChecker promiseChecker(backendTex, reporter, false); 436 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture( 437 ctx, backendTex.getBackendFormat(), kWidth, kHeight, GrMipMapped::kNo, 438 kTopLeft_GrSurfaceOrigin, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr, 439 PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release, 440 PromiseTextureChecker::Done, &promiseChecker)); 441 REPORTER_ASSERT(reporter, image); 442 443 // Make the cache full. This tests that we don't preemptively purge cached textures for 444 // fulfillment due to cache pressure. 445 static constexpr int kMaxResources = 10; 446 static constexpr int kMaxBytes = 100; 447 ctx->setResourceCacheLimits(kMaxResources, kMaxBytes); 448 sk_sp<GrTexture> textures[2 * kMaxResources]; 449 for (int i = 0; i < 2 * kMaxResources; ++i) { 450 GrSurfaceDesc desc; 451 desc.fConfig = kRGBA_8888_GrPixelConfig; 452 desc.fWidth = desc.fHeight = 100; 453 textures[i] = ctx->priv().resourceProvider()->createTexture(desc, SkBudgeted::kYes); 454 REPORTER_ASSERT(reporter, textures[i]); 455 } 456 457 // Relying on the asserts in the promiseImageChecker to ensure that fulfills and releases are 458 // properly ordered. 459 canvas->drawImage(image, 0, 0); 460 surface->flush(); 461 canvas->drawImage(image, 1, 0); 462 surface->flush(); 463 canvas->drawImage(image, 2, 0); 464 surface->flush(); 465 canvas->drawImage(image, 3, 0); 466 surface->flush(); 467 canvas->drawImage(image, 4, 0); 468 surface->flush(); 469 canvas->drawImage(image, 5, 0); 470 surface->flush(); 471 // Must call these to ensure that all callbacks are performed before the checker is destroyed. 472 image.reset(); 473 ctx->flush(); 474 gpu->testingOnly_flushGpuAndSync(); 475 476 gpu->deleteTestingOnlyBackendTexture(backendTex); 477 } 478