Home | History | Annotate | Download | only in tests
      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