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