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/GrDirectContext.h" 14#include "src/gpu/GrDirectContextPriv.h" 15#include "src/gpu/GrGpu.h" 16#include "src/gpu/GrResourceProvider.h" 17#include "src/gpu/GrTexture.h" 18#include "src/image/SkImage_Gpu.h" 19#include "tools/gpu/ManagedBackendTexture.h" 20 21using namespace sk_gpu_test; 22 23struct PromiseTextureChecker { 24 // shared indicates whether the backend texture is used to fulfill more than one promise 25 // image. 26 explicit PromiseTextureChecker(const GrBackendTexture& tex, 27 skiatest::Reporter* reporter, 28 bool shared) 29 : fTexture(SkPromiseImageTexture::Make(tex)), fReporter(reporter), fShared(shared) {} 30 sk_sp<SkPromiseImageTexture> fTexture; 31 skiatest::Reporter* fReporter; 32 bool fShared; 33 int fFulfillCount = 0; 34 int fReleaseCount = 0; 35 36 /** 37 * Releases the SkPromiseImageTexture. Used to test that cached GrTexture representations 38 * in the cache are freed. 39 */ 40 void releaseTexture() { fTexture.reset(); } 41 42 SkTArray<GrUniqueKey> uniqueKeys() const { 43 return fTexture->testingOnly_uniqueKeysToInvalidate(); 44 } 45 46 static sk_sp<SkPromiseImageTexture> Fulfill(void* self) { 47 auto checker = static_cast<PromiseTextureChecker*>(self); 48 checker->fFulfillCount++; 49 return checker->fTexture; 50 } 51 static void Release(void* self) { static_cast<PromiseTextureChecker*>(self)->fReleaseCount++; } 52}; 53 54enum class ReleaseBalanceExpectation { 55 kBalanced, 56 kAllUnbalanced, 57 kUnknown, 58 kUnbalancedByOne, 59 kBalancedOrOffByOne, 60}; 61 62static void check_fulfill_and_release_cnts(skiatest::Reporter* reporter, 63 const PromiseTextureChecker& promiseChecker, 64 int expectedFulfillCnt, 65 ReleaseBalanceExpectation releaseBalanceExpecation) { 66 REPORTER_ASSERT(reporter, promiseChecker.fFulfillCount == expectedFulfillCnt); 67 if (!expectedFulfillCnt) { 68 // Release and Done should only ever be called after Fulfill. 69 REPORTER_ASSERT(reporter, !promiseChecker.fReleaseCount); 70 return; 71 } 72 int releaseDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount; 73 switch (releaseBalanceExpecation) { 74 case ReleaseBalanceExpectation::kBalanced: 75 REPORTER_ASSERT(reporter, !releaseDiff); 76 break; 77 case ReleaseBalanceExpectation::kAllUnbalanced: 78 REPORTER_ASSERT(reporter, releaseDiff == promiseChecker.fFulfillCount); 79 break; 80 case ReleaseBalanceExpectation::kUnknown: 81 REPORTER_ASSERT(reporter, 82 releaseDiff >= 0 && releaseDiff <= promiseChecker.fFulfillCount); 83 break; 84 case ReleaseBalanceExpectation::kUnbalancedByOne: 85 REPORTER_ASSERT(reporter, releaseDiff == 1); 86 break; 87 case ReleaseBalanceExpectation::kBalancedOrOffByOne: 88 REPORTER_ASSERT(reporter, releaseDiff == 0 || releaseDiff == 1); 89 break; 90 } 91} 92 93static void check_unfulfilled(const PromiseTextureChecker& promiseChecker, 94 skiatest::Reporter* reporter) { 95 check_fulfill_and_release_cnts(reporter, promiseChecker, 0, 96 ReleaseBalanceExpectation::kBalanced); 97} 98 99static void check_only_fulfilled(skiatest::Reporter* reporter, 100 const PromiseTextureChecker& promiseChecker, 101 int expectedFulfillCnt = 1) { 102 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt, 103 ReleaseBalanceExpectation::kAllUnbalanced); 104} 105 106static void check_all_flushed_but_not_synced(skiatest::Reporter* reporter, 107 const PromiseTextureChecker& promiseChecker, 108 GrBackendApi api, 109 int expectedFulfillCnt = 1) { 110 ReleaseBalanceExpectation releaseBalanceExpectation = ReleaseBalanceExpectation::kBalanced; 111 // On Vulkan and D3D Done isn't guaranteed to be called until a sync has occurred. 112 if (api == GrBackendApi::kVulkan || api == GrBackendApi::kDirect3D) { 113 releaseBalanceExpectation = expectedFulfillCnt == 1 114 ? ReleaseBalanceExpectation::kBalancedOrOffByOne 115 : ReleaseBalanceExpectation::kUnknown; 116 } 117 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt, 118 releaseBalanceExpectation); 119} 120 121static void check_all_done(skiatest::Reporter* reporter, 122 const PromiseTextureChecker& promiseChecker, 123 int expectedFulfillCnt = 1) { 124 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt, 125 ReleaseBalanceExpectation::kBalanced); 126} 127 128DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) { 129 const int kWidth = 10; 130 const int kHeight = 10; 131 132 auto ctx = ctxInfo.directContext(); 133 134 GrBackendTexture backendTex = ctx->createBackendTexture( 135 kWidth, kHeight, kRGBA_8888_SkColorType, 136 SkColors::kTransparent, GrMipmapped::kNo, GrRenderable::kYes, GrProtected::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(SkImage_Gpu::MakePromiseTexture(ctx->threadSafeProxy(), 145 backendFormat, 146 {kWidth, kHeight}, 147 GrMipmapped::kNo, 148 texOrigin, 149 kRGBA_8888_SkColorType, 150 kPremul_SkAlphaType, 151 nullptr, 152 PromiseTextureChecker::Fulfill, 153 PromiseTextureChecker::Release, 154 &promiseChecker)); 155 156 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 157 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); 158 SkCanvas* canvas = surface->getCanvas(); 159 160 canvas->drawImage(refImg, 0, 0); 161 check_unfulfilled(promiseChecker, reporter); 162 163 surface->flushAndSubmit(); 164 // We still own the image so we should not have called Release or Done. 165 check_only_fulfilled(reporter, promiseChecker); 166 167 ctx->submit(true); 168 check_only_fulfilled(reporter, promiseChecker); 169 170 canvas->drawImage(refImg, 0, 0); 171 canvas->drawImage(refImg, 0, 0); 172 173 surface->flushAndSubmit(true); 174 175 // Image should still be fulfilled from the first time we drew/flushed it. 176 check_only_fulfilled(reporter, promiseChecker); 177 178 canvas->drawImage(refImg, 0, 0); 179 surface->flushAndSubmit(); 180 check_only_fulfilled(reporter, promiseChecker); 181 182 canvas->drawImage(refImg, 0, 0); 183 refImg.reset(); 184 // We no longer own the image but the last draw is still unflushed. 185 check_only_fulfilled(reporter, promiseChecker); 186 187 surface->flushAndSubmit(); 188 // Flushing should have called Release. Depending on the backend and timing it may have called 189 // done. 190 check_all_flushed_but_not_synced(reporter, promiseChecker, ctx->backend()); 191 ctx->submit(true); 192 // Now Done should definitely have been called. 193 check_all_done(reporter, promiseChecker); 194 195 ctx->deleteBackendTexture(backendTex); 196} 197 198DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) { 199 const int kWidth = 10; 200 const int kHeight = 10; 201 202 // Different ways of killing contexts. 203 using DeathFn = std::function<void(sk_gpu_test::GrContextFactory*, GrDirectContext*)>; 204 DeathFn destroy = [](sk_gpu_test::GrContextFactory* factory, GrDirectContext*) { 205 factory->destroyContexts(); 206 }; 207 DeathFn abandon = [](sk_gpu_test::GrContextFactory* factory, GrDirectContext* dContext) { 208 dContext->abandonContext(); 209 }; 210 DeathFn releaseResourcesAndAbandon = [](sk_gpu_test::GrContextFactory* factory, 211 GrDirectContext* dContext) { 212 dContext->releaseResourcesAndAbandonContext(); 213 }; 214 215 for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) { 216 auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type); 217 // These tests are difficult to get working with Vulkan. See http://skbug.com/8705 218 // and http://skbug.com/8275 219 // Also problematic on Dawn; see http://skbug.com/10326 220 // And Direct3D, for similar reasons. 221 GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType); 222 if (api == GrBackendApi::kVulkan || api == GrBackendApi::kDawn || 223 api == GrBackendApi::kDirect3D) { 224 continue; 225 } 226 DeathFn contextKillers[] = {destroy, abandon, releaseResourcesAndAbandon}; 227 for (const DeathFn& contextDeath : contextKillers) { 228 sk_gpu_test::GrContextFactory factory; 229 auto ctx = factory.get(contextType); 230 if (!ctx) { 231 continue; 232 } 233 234 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(ctx, 235 kWidth, 236 kHeight, 237 kAlpha_8_SkColorType, 238 GrMipmapped::kNo, 239 GrRenderable::kNo); 240 if (!mbet) { 241 ERRORF(reporter, "Could not create texture alpha texture."); 242 continue; 243 } 244 245 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, 246 kPremul_SkAlphaType); 247 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); 248 SkCanvas* canvas = surface->getCanvas(); 249 250 PromiseTextureChecker promiseChecker(mbet->texture(), reporter, false); 251 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(ctx->threadSafeProxy(), 252 mbet->texture().getBackendFormat(), 253 {kWidth, kHeight}, 254 GrMipmapped::kNo, 255 kTopLeft_GrSurfaceOrigin, 256 kAlpha_8_SkColorType, 257 kPremul_SkAlphaType, 258 /*color space*/ nullptr, 259 PromiseTextureChecker::Fulfill, 260 PromiseTextureChecker::Release, 261 &promiseChecker)); 262 REPORTER_ASSERT(reporter, image); 263 264 canvas->drawImage(image, 0, 0); 265 image.reset(); 266 // If the surface still holds a ref to the context then the factory will not be able 267 // to destroy the context (and instead will release-all-and-abandon). 268 surface.reset(); 269 270 ctx->flushAndSubmit(); 271 contextDeath(&factory, ctx); 272 273 check_all_done(reporter, promiseChecker); 274 } 275 } 276} 277 278DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache, reporter, ctxInfo) { 279 const int kWidth = 10; 280 const int kHeight = 10; 281 282 auto dContext = ctxInfo.directContext(); 283 284 GrBackendTexture backendTex = dContext->createBackendTexture( 285 kWidth, kHeight, kAlpha_8_SkColorType, 286 SkColors::kTransparent, GrMipmapped::kNo, GrRenderable::kNo, GrProtected::kNo); 287 REPORTER_ASSERT(reporter, backendTex.isValid()); 288 289 SkImageInfo info = 290 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType); 291 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info); 292 SkCanvas* canvas = surface->getCanvas(); 293 294 PromiseTextureChecker promiseChecker(backendTex, reporter, false); 295 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(dContext->threadSafeProxy(), 296 backendTex.getBackendFormat(), 297 {kWidth, kHeight}, 298 GrMipmapped::kNo, 299 kTopLeft_GrSurfaceOrigin, 300 kAlpha_8_SkColorType, 301 kPremul_SkAlphaType, 302 nullptr, 303 PromiseTextureChecker::Fulfill, 304 PromiseTextureChecker::Release, 305 &promiseChecker)); 306 REPORTER_ASSERT(reporter, image); 307 308 // Make the cache full. This tests that we don't preemptively purge cached textures for 309 // fulfillment due to cache pressure. 310 static constexpr int kMaxBytes = 1; 311 dContext->setResourceCacheLimit(kMaxBytes); 312 SkTArray<sk_sp<GrTexture>> textures; 313 for (int i = 0; i < 5; ++i) { 314 auto format = dContext->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, 315 GrRenderable::kNo); 316 textures.emplace_back(dContext->priv().resourceProvider()->createTexture( 317 {100, 100}, format, GrTextureType::k2D, GrRenderable::kNo, 1, GrMipmapped::kNo, 318 SkBudgeted::kYes, GrProtected::kNo)); 319 REPORTER_ASSERT(reporter, textures[i]); 320 } 321 322 size_t bytesUsed; 323 324 dContext->getResourceCacheUsage(nullptr, &bytesUsed); 325 REPORTER_ASSERT(reporter, bytesUsed > kMaxBytes); 326 327 // Relying on the asserts in the promiseImageChecker to ensure that fulfills and releases are 328 // properly ordered. 329 canvas->drawImage(image, 0, 0); 330 surface->flushAndSubmit(); 331 canvas->drawImage(image, 1, 0); 332 surface->flushAndSubmit(); 333 canvas->drawImage(image, 2, 0); 334 surface->flushAndSubmit(); 335 canvas->drawImage(image, 3, 0); 336 surface->flushAndSubmit(); 337 canvas->drawImage(image, 4, 0); 338 surface->flushAndSubmit(); 339 canvas->drawImage(image, 5, 0); 340 surface->flushAndSubmit(); 341 // Must call these to ensure that all callbacks are performed before the checker is destroyed. 342 image.reset(); 343 dContext->flushAndSubmit(true); 344 345 dContext->deleteBackendTexture(backendTex); 346} 347 348// Test case where promise image fulfill returns nullptr. 349DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageNullFulfill, reporter, ctxInfo) { 350 const int kWidth = 10; 351 const int kHeight = 10; 352 353 auto dContext = ctxInfo.directContext(); 354 355 GrBackendFormat backendFormat = 356 dContext->defaultBackendFormat(kRGBA_8888_SkColorType, GrRenderable::kYes); 357 if (!backendFormat.isValid()) { 358 ERRORF(reporter, "No valid default kRGBA_8888 texture format."); 359 return; 360 } 361 362 struct Counts { 363 int fFulfillCount = 0; 364 int fReleaseCount = 0; 365 } counts; 366 auto fulfill = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) { 367 ++static_cast<Counts*>(ctx)->fFulfillCount; 368 return sk_sp<SkPromiseImageTexture>(); 369 }; 370 auto release = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) { 371 ++static_cast<Counts*>(ctx)->fReleaseCount; 372 }; 373 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin; 374 sk_sp<SkImage> refImg(SkImage_Gpu::MakePromiseTexture(dContext->threadSafeProxy(), 375 backendFormat, 376 {kWidth, kHeight}, 377 GrMipmapped::kNo, 378 texOrigin, 379 kRGBA_8888_SkColorType, 380 kPremul_SkAlphaType, 381 nullptr, 382 fulfill, 383 release, 384 &counts)); 385 386 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 387 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info); 388 SkCanvas* canvas = surface->getCanvas(); 389 // Draw the image a few different ways. 390 canvas->drawImage(refImg, 0, 0); 391 SkPaint paint; 392 paint.setColorFilter(SkColorFilters::LinearToSRGBGamma()); 393 canvas->drawImage(refImg, 0, 0, SkSamplingOptions(), &paint); 394 auto shader = refImg->makeShader(SkSamplingOptions()); 395 REPORTER_ASSERT(reporter, shader); 396 paint.setShader(std::move(shader)); 397 canvas->drawRect(SkRect::MakeWH(1,1), paint); 398 paint.setShader(nullptr); 399 refImg.reset(); 400 surface->flushAndSubmit(); 401 // We should only call each callback once and we should have made all the calls by this point. 402 REPORTER_ASSERT(reporter, counts.fFulfillCount == 1); 403 REPORTER_ASSERT(reporter, counts.fReleaseCount == 1); 404} 405