Flutter Engine
The Flutter Engine
PromiseImageTest.cpp
Go to the documentation of this file.
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
17#include "include/core/SkRect.h"
26#include "include/gpu/GrTypes.h"
38#include "tests/Test.h"
40#include "tools/gpu/FenceSync.h"
42
43#include <cstddef>
44#include <functional>
45#include <utility>
46
47using namespace skia_private;
48
49struct GrContextOptions;
50
51using namespace sk_gpu_test;
52
54 // shared indicates whether the backend texture is used to fulfill more than one promise
55 // image.
58 bool shared)
62 bool fShared;
65
67 auto checker = static_cast<PromiseTextureChecker*>(self);
68 checker->fFulfillCount++;
69 return checker->fTexture;
70 }
71 static void Release(void* self) { static_cast<PromiseTextureChecker*>(self)->fReleaseCount++; }
72};
73
75 kBalanced,
77 kUnknown,
80};
81
83 const PromiseTextureChecker& promiseChecker,
84 int expectedFulfillCnt,
85 ReleaseBalanceExpectation releaseBalanceExpecation) {
86 REPORTER_ASSERT(reporter, promiseChecker.fFulfillCount == expectedFulfillCnt);
87 if (!expectedFulfillCnt) {
88 // Release and Done should only ever be called after Fulfill.
89 REPORTER_ASSERT(reporter, !promiseChecker.fReleaseCount);
90 return;
91 }
92 int releaseDiff = promiseChecker.fFulfillCount - promiseChecker.fReleaseCount;
93 switch (releaseBalanceExpecation) {
95 REPORTER_ASSERT(reporter, !releaseDiff);
96 break;
98 REPORTER_ASSERT(reporter, releaseDiff == promiseChecker.fFulfillCount);
99 break;
102 releaseDiff >= 0 && releaseDiff <= promiseChecker.fFulfillCount);
103 break;
105 REPORTER_ASSERT(reporter, releaseDiff == 1);
106 break;
108 REPORTER_ASSERT(reporter, releaseDiff == 0 || releaseDiff == 1);
109 break;
110 }
111}
112
113static void check_unfulfilled(const PromiseTextureChecker& promiseChecker,
115 check_fulfill_and_release_cnts(reporter, promiseChecker, 0,
117}
118
120 const PromiseTextureChecker& promiseChecker,
121 int expectedFulfillCnt = 1) {
122 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
124}
125
127 const PromiseTextureChecker& promiseChecker,
128 GrBackendApi api,
129 int expectedFulfillCnt = 1) {
131 // On Vulkan and D3D Done isn't guaranteed to be called until a sync has occurred.
132 if (api == GrBackendApi::kVulkan || api == GrBackendApi::kDirect3D) {
133 releaseBalanceExpectation = expectedFulfillCnt == 1
136 }
137 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
138 releaseBalanceExpectation);
139}
140
142 const PromiseTextureChecker& promiseChecker,
143 int expectedFulfillCnt = 1) {
144 check_fulfill_and_release_cnts(reporter, promiseChecker, expectedFulfillCnt,
146}
147
149 reporter,
150 ctxInfo,
152 using namespace skgpu;
153 const int kWidth = 10;
154 const int kHeight = 10;
155
156 auto ctx = ctxInfo.directContext();
157
158 Protected isProtected = Protected(ctx->priv().caps()->supportsProtectedContent());
159
160 GrBackendTexture backendTex = ctx->createBackendTexture(kWidth,
161 kHeight,
166 isProtected);
167 REPORTER_ASSERT(reporter, backendTex.isValid());
168
169 GrBackendFormat backendFormat = backendTex.getBackendFormat();
170 REPORTER_ASSERT(reporter, backendFormat.isValid());
171
172 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
174 sk_sp<SkImage> refImg(SkImages::PromiseTextureFrom(ctx->threadSafeProxy(),
175 backendFormat,
176 {kWidth, kHeight},
178 texOrigin,
181 nullptr,
182 PromiseTextureChecker::Fulfill,
183 PromiseTextureChecker::Release,
184 &promiseChecker));
185
188 SkCanvas* canvas = surface->getCanvas();
189
190 canvas->drawImage(refImg, 0, 0);
191 check_unfulfilled(promiseChecker, reporter);
192
193 ctx->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
194 // We still own the image so we should not have called Release or Done.
195 check_only_fulfilled(reporter, promiseChecker);
196
197 ctx->submit(GrSyncCpu::kYes);
198 check_only_fulfilled(reporter, promiseChecker);
199
200 canvas->drawImage(refImg, 0, 0);
201 canvas->drawImage(refImg, 0, 0);
202
203 ctx->flushAndSubmit(surface.get(), GrSyncCpu::kYes);
204
205 // Image should still be fulfilled from the first time we drew/flushed it.
206 check_only_fulfilled(reporter, promiseChecker);
207
208 canvas->drawImage(refImg, 0, 0);
209 ctx->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
210 check_only_fulfilled(reporter, promiseChecker);
211
212 canvas->drawImage(refImg, 0, 0);
213 refImg.reset();
214 // We no longer own the image but the last draw is still unflushed.
215 check_only_fulfilled(reporter, promiseChecker);
216
217 ctx->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
218 // Flushing should have called Release. Depending on the backend and timing it may have called
219 // done.
220 check_all_flushed_but_not_synced(reporter, promiseChecker, ctx->backend());
221 ctx->submit(GrSyncCpu::kYes);
222 // Now Done should definitely have been called.
223 check_all_done(reporter, promiseChecker);
224
225 ctx->deleteBackendTexture(backendTex);
226}
227
228DEF_GANESH_TEST(PromiseImageTextureShutdown, reporter, ctxInfo, CtsEnforcement::kNever) {
229 const int kWidth = 10;
230 const int kHeight = 10;
231
232 // Different ways of killing contexts.
234 DeathFn destroy = [](sk_gpu_test::GrContextFactory* factory, GrDirectContext*) {
235 factory->destroyContexts();
236 };
237 DeathFn abandon = [](sk_gpu_test::GrContextFactory* factory, GrDirectContext* dContext) {
238 dContext->abandonContext();
239 };
240 DeathFn releaseResourcesAndAbandon = [](sk_gpu_test::GrContextFactory* factory,
241 GrDirectContext* dContext) {
242 dContext->releaseResourcesAndAbandonContext();
243 };
244
245 for (int type = 0; type < skgpu::kContextTypeCount; ++type) {
246 auto contextType = static_cast<skgpu::ContextType>(type);
247 // These tests are difficult to get working with Vulkan. See http://skbug.com/8705
248 // and http://skbug.com/8275
249 // And Direct3D, for similar reasons.
253 continue;
254 }
255 DeathFn contextKillers[] = {destroy, abandon, releaseResourcesAndAbandon};
256 for (const DeathFn& contextDeath : contextKillers) {
258 auto ctx = factory.get(contextType);
259 if (!ctx) {
260 continue;
261 }
262
263 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(ctx,
264 kWidth,
265 kHeight,
269 if (!mbet) {
270 ERRORF(reporter, "Could not create texture alpha texture.");
271 continue;
272 }
273
277 SkCanvas* canvas = surface->getCanvas();
278
279 PromiseTextureChecker promiseChecker(mbet->texture(), reporter, false);
281 mbet->texture().getBackendFormat(),
282 {kWidth, kHeight},
287 /*color space*/ nullptr,
288 PromiseTextureChecker::Fulfill,
289 PromiseTextureChecker::Release,
290 &promiseChecker));
292
293 canvas->drawImage(image, 0, 0);
294 image.reset();
295 // If the surface still holds a ref to the context then the factory will not be able
296 // to destroy the context (and instead will release-all-and-abandon).
297 surface.reset();
298
299 ctx->flushAndSubmit();
300 contextDeath(&factory, ctx);
301
302 check_all_done(reporter, promiseChecker);
303 }
304 }
305}
306
307DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(PromiseImageTextureFullCache,
308 reporter,
309 ctxInfo,
311 using namespace skgpu;
312
313 const int kWidth = 10;
314 const int kHeight = 10;
315
316 auto dContext = ctxInfo.directContext();
317
318 Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
319
320 GrBackendTexture backendTex = dContext->createBackendTexture(kWidth,
321 kHeight,
326 isProtected);
327 REPORTER_ASSERT(reporter, backendTex.isValid());
328
332 SkCanvas* canvas = surface->getCanvas();
333
334 PromiseTextureChecker promiseChecker(backendTex, reporter, false);
335 sk_sp<SkImage> image(SkImages::PromiseTextureFrom(dContext->threadSafeProxy(),
336 backendTex.getBackendFormat(),
337 {kWidth, kHeight},
342 nullptr,
343 PromiseTextureChecker::Fulfill,
344 PromiseTextureChecker::Release,
345 &promiseChecker));
347
348 // Make the cache full. This tests that we don't preemptively purge cached textures for
349 // fulfillment due to cache pressure.
350 static constexpr int kMaxBytes = 1;
351 dContext->setResourceCacheLimit(kMaxBytes);
352 TArray<sk_sp<GrTexture>> textures;
353 for (int i = 0; i < 5; ++i) {
354 auto format = dContext->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
356 textures.emplace_back(dContext->priv().resourceProvider()->createTexture(
357 {100, 100},
358 format,
361 1,
364 isProtected,
365 /*label=*/"PromiseImageTextureFullCacheTest"));
366 REPORTER_ASSERT(reporter, textures[i]);
367 }
368
369 size_t bytesUsed;
370
371 dContext->getResourceCacheUsage(nullptr, &bytesUsed);
372 REPORTER_ASSERT(reporter, bytesUsed > kMaxBytes);
373
374 // Relying on the asserts in the promiseImageChecker to ensure that fulfills and releases are
375 // properly ordered.
376 canvas->drawImage(image, 0, 0);
377 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
378 canvas->drawImage(image, 1, 0);
379 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
380 canvas->drawImage(image, 2, 0);
381 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
382 canvas->drawImage(image, 3, 0);
383 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
384 canvas->drawImage(image, 4, 0);
385 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
386 canvas->drawImage(image, 5, 0);
387 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
388 // Must call these to ensure that all callbacks are performed before the checker is destroyed.
389 image.reset();
390 dContext->flushAndSubmit(GrSyncCpu::kYes);
391
392 dContext->deleteBackendTexture(backendTex);
393}
394
395// Test case where promise image fulfill returns nullptr.
397 reporter,
398 ctxInfo,
400 const int kWidth = 10;
401 const int kHeight = 10;
402
403 auto dContext = ctxInfo.directContext();
404
405 GrBackendFormat backendFormat =
406 dContext->defaultBackendFormat(kRGBA_8888_SkColorType, GrRenderable::kYes);
407 if (!backendFormat.isValid()) {
408 ERRORF(reporter, "No valid default kRGBA_8888 texture format.");
409 return;
410 }
411
412 struct Counts {
413 int fFulfillCount = 0;
414 int fReleaseCount = 0;
415 } counts;
416 auto fulfill = [](SkImages::PromiseImageTextureContext ctx) {
417 ++static_cast<Counts*>(ctx)->fFulfillCount;
419 };
420 auto release = [](SkImages::PromiseImageTextureContext ctx) {
421 ++static_cast<Counts*>(ctx)->fReleaseCount;
422 };
424 sk_sp<SkImage> refImg(SkImages::PromiseTextureFrom(dContext->threadSafeProxy(),
425 backendFormat,
426 {kWidth, kHeight},
428 texOrigin,
431 nullptr,
432 fulfill,
433 release,
434 &counts));
435
438 SkCanvas* canvas = surface->getCanvas();
439 // Draw the image a few different ways.
440 canvas->drawImage(refImg, 0, 0);
443 canvas->drawImage(refImg, 0, 0, SkSamplingOptions(), &paint);
444 auto shader = refImg->makeShader(SkSamplingOptions());
445 REPORTER_ASSERT(reporter, shader);
446 paint.setShader(std::move(shader));
447 canvas->drawRect(SkRect::MakeWH(1,1), paint);
448 paint.setShader(nullptr);
449 refImg.reset();
450 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
451 // We should only call each callback once and we should have made all the calls by this point.
452 REPORTER_ASSERT(reporter, counts.fFulfillCount == 1);
453 REPORTER_ASSERT(reporter, counts.fReleaseCount == 1);
454}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
reporter
Definition: FontMgrTest.cpp:39
GrSurfaceOrigin
Definition: GrTypes.h:147
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
GrBackendApi
Definition: GrTypes.h:95
skgpu::Protected Protected
ReleaseBalanceExpectation
static void check_fulfill_and_release_cnts(skiatest::Reporter *reporter, const PromiseTextureChecker &promiseChecker, int expectedFulfillCnt, ReleaseBalanceExpectation releaseBalanceExpecation)
static void check_unfulfilled(const PromiseTextureChecker &promiseChecker, skiatest::Reporter *reporter)
static void check_only_fulfilled(skiatest::Reporter *reporter, const PromiseTextureChecker &promiseChecker, int expectedFulfillCnt=1)
DEF_GANESH_TEST(PromiseImageTextureShutdown, reporter, ctxInfo, CtsEnforcement::kNever)
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo, CtsEnforcement::kNever)
static void check_all_done(skiatest::Reporter *reporter, const PromiseTextureChecker &promiseChecker, int expectedFulfillCnt=1)
static void check_all_flushed_but_not_synced(skiatest::Reporter *reporter, const PromiseTextureChecker &promiseChecker, GrBackendApi api, int expectedFulfillCnt=1)
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition: SkColorType.h:21
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
GLenum type
bool isValid() const
GrBackendFormat getBackendFormat() const
bool isValid() const
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
static sk_sp< SkColorFilter > LinearToSRGBGamma()
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix=nullptr) const
Definition: SkImage.cpp:179
GrDirectContext * get(ContextType type, ContextOverrides overrides=ContextOverrides::kNone)
void reset(T *ptr=nullptr)
Definition: SkRefCnt.h:310
T & emplace_back(Args &&... args)
Definition: SkTArray.h:248
const Paint & paint
Definition: color_source.cc:38
VkSurfaceKHR surface
Definition: main.cc:49
uint32_t uint32_t * format
Dart_NativeFunction function
Definition: fuchsia.cc:51
constexpr SkColor4f kTransparent
Definition: SkColor.h:434
SK_API sk_sp< SkImage > PromiseTextureFrom(skgpu::graphite::Recorder *, SkISize dimensions, const skgpu::graphite::TextureInfo &, const SkColorInfo &, skgpu::Origin origin, skgpu::graphite::Volatile, GraphitePromiseTextureFulfillProc, GraphitePromiseImageReleaseProc, GraphitePromiseTextureReleaseProc, GraphitePromiseImageContext, std::string_view label={})
void * PromiseImageTextureContext
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
sk_sp< const SkImage > image
Definition: SkRecords.h:269
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core counts
Definition: switches.h:239
GrBackendApi ContextTypeBackend(skgpu::ContextType type)
Definition: ContextType.cpp:92
Definition: GpuTools.h:21
static const int kContextTypeCount
Definition: ContextType.h:42
ContextType
Definition: ContextType.h:19
Protected
Definition: GpuTypes.h:61
SkSamplingOptions(SkFilterMode::kLinear))
static sk_sp< GrPromiseImageTexture > Fulfill(void *self)
static void Release(void *self)
sk_sp< GrPromiseImageTexture > fTexture
PromiseTextureChecker(const GrBackendTexture &tex, skiatest::Reporter *reporter, bool shared)
skiatest::Reporter * fReporter
static SkImageInfo MakeN32Premul(int width, int height)
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
constexpr size_t kHeight
constexpr size_t kWidth
int_closure destroy