2 * Copyright 2018 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "tests/Test.h"
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/ganesh/GrDirectContextPriv.h"
15 #include "src/gpu/ganesh/GrGpu.h"
16 #include "src/gpu/ganesh/GrResourceProvider.h"
17 #include "src/gpu/ganesh/GrTexture.h"
18 #include "src/image/SkImage_Gpu.h"
19 #include "tools/gpu/ManagedBackendTexture.h"
21 using namespace sk_gpu_test;
23 struct PromiseTextureChecker {
24 // shared indicates whether the backend texture is used to fulfill more than one promise
26 explicit PromiseTextureChecker(const GrBackendTexture& tex,
27 skiatest::Reporter* reporter,
29 : fTexture(SkPromiseImageTexture::Make(tex)), fReporter(reporter), fShared(shared) {}
30 sk_sp<SkPromiseImageTexture> fTexture;
31 skiatest::Reporter* fReporter;
33 int fFulfillCount = 0;
34 int fReleaseCount = 0;
36 static sk_sp<SkPromiseImageTexture> Fulfill(void* self) {
37 auto checker = static_cast<PromiseTextureChecker*>(self);
38 checker->fFulfillCount++;
39 return checker->fTexture;
41 static void Release(void* self) { static_cast<PromiseTextureChecker*>(self)->fReleaseCount++; }
44 enum class ReleaseBalanceExpectation {
52 static void check_fulfill_and_release_cnts(skiatest::Reporter* reporter,
53 const PromiseTextureChecker& promiseChecker,
54 int expectedFulfillCnt,
55 ReleaseBalanceExpectation releaseBalanceExpecation) {
56 REPORTER_ASSERT(reporter, promiseChecker.fFulfillCount == expectedFulfillCnt);
57 if (!expectedFulfillCnt) {
58 // Release and Done should only ever be called after Fulfill.
59 REPORTER_ASSERT(reporter, !promiseChecker.fReleaseCount);
62 int releaseDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
63 switch (releaseBalanceExpecation) {
64 case ReleaseBalanceExpectation::kBalanced:
65 REPORTER_ASSERT(reporter, !releaseDiff);
67 case ReleaseBalanceExpectation::kAllUnbalanced:
68 REPORTER_ASSERT(reporter, releaseDiff == promiseChecker.fFulfillCount);
70 case ReleaseBalanceExpectation::kUnknown:
71 REPORTER_ASSERT(reporter,
72 releaseDiff >= 0 && releaseDiff <= promiseChecker.fFulfillCount);
74 case ReleaseBalanceExpectation::kUnbalancedByOne:
75 REPORTER_ASSERT(reporter, releaseDiff == 1);
77 case ReleaseBalanceExpectation::kBalancedOrOffByOne:
78 REPORTER_ASSERT(reporter, releaseDiff == 0 || releaseDiff == 1);
83 static void check_unfulfilled(const PromiseTextureChecker& promiseChecker,
84 skiatest::Reporter* reporter) {
85 check_fulfill_and_release_cnts(reporter, promiseChecker, 0,
86 ReleaseBalanceExpectation::kBalanced);
89 static void check_only_fulfilled(skiatest::Reporter* reporter,
90 const PromiseTextureChecker& promiseChecker,
91 int expectedFulfillCnt = 1) {
92 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
93 ReleaseBalanceExpectation::kAllUnbalanced);
96 static void check_all_flushed_but_not_synced(skiatest::Reporter* reporter,
97 const PromiseTextureChecker& promiseChecker,
99 int expectedFulfillCnt = 1) {
100 ReleaseBalanceExpectation releaseBalanceExpectation = ReleaseBalanceExpectation::kBalanced;
101 // On Vulkan and D3D Done isn't guaranteed to be called until a sync has occurred.
102 if (api == GrBackendApi::kVulkan || api == GrBackendApi::kDirect3D) {
103 releaseBalanceExpectation = expectedFulfillCnt == 1
104 ? ReleaseBalanceExpectation::kBalancedOrOffByOne
105 : ReleaseBalanceExpectation::kUnknown;
107 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
108 releaseBalanceExpectation);
111 static void check_all_done(skiatest::Reporter* reporter,
112 const PromiseTextureChecker& promiseChecker,
113 int expectedFulfillCnt = 1) {
114 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
115 ReleaseBalanceExpectation::kBalanced);
118 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) {
119 const int kWidth = 10;
120 const int kHeight = 10;
122 auto ctx = ctxInfo.directContext();
124 GrBackendTexture backendTex = ctx->createBackendTexture(
125 kWidth, kHeight, kRGBA_8888_SkColorType,
126 SkColors::kTransparent, GrMipmapped::kNo, GrRenderable::kYes, GrProtected::kNo);
127 REPORTER_ASSERT(reporter, backendTex.isValid());
129 GrBackendFormat backendFormat = backendTex.getBackendFormat();
130 REPORTER_ASSERT(reporter, backendFormat.isValid());
132 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
133 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
134 sk_sp<SkImage> refImg(SkImage_Gpu::MakePromiseTexture(ctx->threadSafeProxy(),
139 kRGBA_8888_SkColorType,
142 PromiseTextureChecker::Fulfill,
143 PromiseTextureChecker::Release,
146 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
147 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
148 SkCanvas* canvas = surface->getCanvas();
150 canvas->drawImage(refImg, 0, 0);
151 check_unfulfilled(promiseChecker, reporter);
153 surface->flushAndSubmit();
154 // We still own the image so we should not have called Release or Done.
155 check_only_fulfilled(reporter, promiseChecker);
158 check_only_fulfilled(reporter, promiseChecker);
160 canvas->drawImage(refImg, 0, 0);
161 canvas->drawImage(refImg, 0, 0);
163 surface->flushAndSubmit(true);
165 // Image should still be fulfilled from the first time we drew/flushed it.
166 check_only_fulfilled(reporter, promiseChecker);
168 canvas->drawImage(refImg, 0, 0);
169 surface->flushAndSubmit();
170 check_only_fulfilled(reporter, promiseChecker);
172 canvas->drawImage(refImg, 0, 0);
174 // We no longer own the image but the last draw is still unflushed.
175 check_only_fulfilled(reporter, promiseChecker);
177 surface->flushAndSubmit();
178 // Flushing should have called Release. Depending on the backend and timing it may have called
180 check_all_flushed_but_not_synced(reporter, promiseChecker, ctx->backend());
182 // Now Done should definitely have been called.
183 check_all_done(reporter, promiseChecker);
185 ctx->deleteBackendTexture(backendTex);
188 DEF_GPUTEST(PromiseImageTextureShutdown, reporter, ctxInfo) {
189 const int kWidth = 10;
190 const int kHeight = 10;
192 // Different ways of killing contexts.
193 using DeathFn = std::function<void(sk_gpu_test::GrContextFactory*, GrDirectContext*)>;
194 DeathFn destroy = [](sk_gpu_test::GrContextFactory* factory, GrDirectContext*) {
195 factory->destroyContexts();
197 DeathFn abandon = [](sk_gpu_test::GrContextFactory* factory, GrDirectContext* dContext) {
198 dContext->abandonContext();
200 DeathFn releaseResourcesAndAbandon = [](sk_gpu_test::GrContextFactory* factory,
201 GrDirectContext* dContext) {
202 dContext->releaseResourcesAndAbandonContext();
205 for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) {
206 auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type);
207 // These tests are difficult to get working with Vulkan. See http://skbug.com/8705
208 // and http://skbug.com/8275
209 // Also problematic on Dawn; see http://skbug.com/10326
210 // And Direct3D, for similar reasons.
211 GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType);
212 if (api == GrBackendApi::kVulkan || api == GrBackendApi::kDawn ||
213 api == GrBackendApi::kDirect3D) {
216 DeathFn contextKillers[] = {destroy, abandon, releaseResourcesAndAbandon};
217 for (const DeathFn& contextDeath : contextKillers) {
218 sk_gpu_test::GrContextFactory factory;
219 auto ctx = factory.get(contextType);
224 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(ctx,
227 kAlpha_8_SkColorType,
231 ERRORF(reporter, "Could not create texture alpha texture.");
235 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType,
236 kPremul_SkAlphaType);
237 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
238 SkCanvas* canvas = surface->getCanvas();
240 PromiseTextureChecker promiseChecker(mbet->texture(), reporter, false);
241 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(ctx->threadSafeProxy(),
242 mbet->texture().getBackendFormat(),
245 kTopLeft_GrSurfaceOrigin,
246 kAlpha_8_SkColorType,
248 /*color space*/ nullptr,
249 PromiseTextureChecker::Fulfill,
250 PromiseTextureChecker::Release,
252 REPORTER_ASSERT(reporter, image);
254 canvas->drawImage(image, 0, 0);
256 // If the surface still holds a ref to the context then the factory will not be able
257 // to destroy the context (and instead will release-all-and-abandon).
260 ctx->flushAndSubmit();
261 contextDeath(&factory, ctx);
263 check_all_done(reporter, promiseChecker);
268 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache, reporter, ctxInfo) {
269 const int kWidth = 10;
270 const int kHeight = 10;
272 auto dContext = ctxInfo.directContext();
274 GrBackendTexture backendTex = dContext->createBackendTexture(
275 kWidth, kHeight, kAlpha_8_SkColorType,
276 SkColors::kTransparent, GrMipmapped::kNo, GrRenderable::kNo, GrProtected::kNo);
277 REPORTER_ASSERT(reporter, backendTex.isValid());
280 SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
281 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info);
282 SkCanvas* canvas = surface->getCanvas();
284 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
285 sk_sp<SkImage> image(SkImage_Gpu::MakePromiseTexture(dContext->threadSafeProxy(),
286 backendTex.getBackendFormat(),
289 kTopLeft_GrSurfaceOrigin,
290 kAlpha_8_SkColorType,
293 PromiseTextureChecker::Fulfill,
294 PromiseTextureChecker::Release,
296 REPORTER_ASSERT(reporter, image);
298 // Make the cache full. This tests that we don't preemptively purge cached textures for
299 // fulfillment due to cache pressure.
300 static constexpr int kMaxBytes = 1;
301 dContext->setResourceCacheLimit(kMaxBytes);
302 SkTArray<sk_sp<GrTexture>> textures;
303 for (int i = 0; i < 5; ++i) {
304 auto format = dContext->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
306 textures.emplace_back(dContext->priv().resourceProvider()->createTexture({100, 100},
315 REPORTER_ASSERT(reporter, textures[i]);
320 dContext->getResourceCacheUsage(nullptr, &bytesUsed);
321 REPORTER_ASSERT(reporter, bytesUsed > kMaxBytes);
323 // Relying on the asserts in the promiseImageChecker to ensure that fulfills and releases are
325 canvas->drawImage(image, 0, 0);
326 surface->flushAndSubmit();
327 canvas->drawImage(image, 1, 0);
328 surface->flushAndSubmit();
329 canvas->drawImage(image, 2, 0);
330 surface->flushAndSubmit();
331 canvas->drawImage(image, 3, 0);
332 surface->flushAndSubmit();
333 canvas->drawImage(image, 4, 0);
334 surface->flushAndSubmit();
335 canvas->drawImage(image, 5, 0);
336 surface->flushAndSubmit();
337 // Must call these to ensure that all callbacks are performed before the checker is destroyed.
339 dContext->flushAndSubmit(true);
341 dContext->deleteBackendTexture(backendTex);
344 // Test case where promise image fulfill returns nullptr.
345 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageNullFulfill, reporter, ctxInfo) {
346 const int kWidth = 10;
347 const int kHeight = 10;
349 auto dContext = ctxInfo.directContext();
351 GrBackendFormat backendFormat =
352 dContext->defaultBackendFormat(kRGBA_8888_SkColorType, GrRenderable::kYes);
353 if (!backendFormat.isValid()) {
354 ERRORF(reporter, "No valid default kRGBA_8888 texture format.");
359 int fFulfillCount = 0;
360 int fReleaseCount = 0;
362 auto fulfill = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) {
363 ++static_cast<Counts*>(ctx)->fFulfillCount;
364 return sk_sp<SkPromiseImageTexture>();
366 auto release = [](SkDeferredDisplayListRecorder::PromiseImageTextureContext ctx) {
367 ++static_cast<Counts*>(ctx)->fReleaseCount;
369 GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
370 sk_sp<SkImage> refImg(SkImage_Gpu::MakePromiseTexture(dContext->threadSafeProxy(),
375 kRGBA_8888_SkColorType,
382 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
383 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info);
384 SkCanvas* canvas = surface->getCanvas();
385 // Draw the image a few different ways.
386 canvas->drawImage(refImg, 0, 0);
388 paint.setColorFilter(SkColorFilters::LinearToSRGBGamma());
389 canvas->drawImage(refImg, 0, 0, SkSamplingOptions(), &paint);
390 auto shader = refImg->makeShader(SkSamplingOptions());
391 REPORTER_ASSERT(reporter, shader);
392 paint.setShader(std::move(shader));
393 canvas->drawRect(SkRect::MakeWH(1,1), paint);
394 paint.setShader(nullptr);
396 surface->flushAndSubmit();
397 // We should only call each callback once and we should have made all the calls by this point.
398 REPORTER_ASSERT(reporter, counts.fFulfillCount == 1);
399 REPORTER_ASSERT(reporter, counts.fReleaseCount == 1);