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