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