• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 using namespace sk_gpu_test;
22 
23 struct PromiseTextureChecker {
24     // shared indicates whether the backend texture is used to fulfill more than one promise
25     // image.
PromiseTextureCheckerPromiseTextureChecker26     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      */
releaseTexturePromiseTextureChecker40     void releaseTexture() { fTexture.reset(); }
41 
uniqueKeysPromiseTextureChecker42     SkTArray<GrUniqueKey> uniqueKeys() const {
43         return fTexture->testingOnly_uniqueKeysToInvalidate();
44     }
45 
FulfillPromiseTextureChecker46     static sk_sp<SkPromiseImageTexture> Fulfill(void* self) {
47         auto checker = static_cast<PromiseTextureChecker*>(self);
48         checker->fFulfillCount++;
49         return checker->fTexture;
50     }
ReleasePromiseTextureChecker51     static void Release(void* self) { static_cast<PromiseTextureChecker*>(self)->fReleaseCount++; }
52 };
53 
54 enum class ReleaseBalanceExpectation {
55     kBalanced,
56     kAllUnbalanced,
57     kUnknown,
58     kUnbalancedByOne,
59     kBalancedOrOffByOne,
60 };
61 
check_fulfill_and_release_cnts(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,int expectedFulfillCnt,ReleaseBalanceExpectation releaseBalanceExpecation)62 static 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 
check_unfulfilled(const PromiseTextureChecker & promiseChecker,skiatest::Reporter * reporter)93 static void check_unfulfilled(const PromiseTextureChecker& promiseChecker,
94                               skiatest::Reporter* reporter) {
95     check_fulfill_and_release_cnts(reporter, promiseChecker, 0,
96                                    ReleaseBalanceExpectation::kBalanced);
97 }
98 
check_only_fulfilled(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,int expectedFulfillCnt=1)99 static 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 
check_all_flushed_but_not_synced(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,GrBackendApi api,int expectedFulfillCnt=1)106 static 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 
check_all_done(skiatest::Reporter * reporter,const PromiseTextureChecker & promiseChecker,int expectedFulfillCnt=1)121 static 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 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest,reporter,ctxInfo)128 DEF_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 
DEF_GPUTEST(PromiseImageTextureShutdown,reporter,ctxInfo)198 DEF_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 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache,reporter,ctxInfo)278 DEF_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.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageNullFulfill,reporter,ctxInfo)349 DEF_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