Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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.
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
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
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,
164 skgpu::Mipmapped::kNo,
165 GrRenderable::kYes,
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},
177 skgpu::Mipmapped::kNo,
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.
233 using DeathFn = std::function<void(sk_gpu_test::GrContextFactory*, GrDirectContext*)>;
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,
267 skgpu::Mipmapped::kNo,
268 GrRenderable::kNo);
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},
283 skgpu::Mipmapped::kNo,
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,
324 skgpu::Mipmapped::kNo,
325 GrRenderable::kNo,
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},
338 skgpu::Mipmapped::kNo,
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);
353 for (int i = 0; i < 5; ++i) {
354 auto format = dContext->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
355 GrRenderable::kNo);
356 textures.emplace_back(dContext->priv().resourceProvider()->createTexture(
357 {100, 100},
358 format,
360 GrRenderable::kNo,
361 1,
362 skgpu::Mipmapped::kNo,
364 isProtected,
365 /*label=*/"PromiseImageTextureFullCacheTest"));
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},
427 skgpu::Mipmapped::kNo,
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
GrSurfaceOrigin
Definition GrTypes.h:147
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
GrBackendApi
Definition GrTypes.h:95
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)
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
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
#define DEF_GANESH_TEST(name, reporter, options, ctsEnforcement)
Definition Test.h:393
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define ERRORF(r,...)
Definition Test.h:293
#define DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(name, reporter, context_info, ctsEnforcement)
Definition Test.h:434
bool isValid() const
GrBackendFormat getBackendFormat() const
void drawRect(const SkRect &rect, const SkPaint &paint)
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
static sk_sp< SkColorFilter > LinearToSRGBGamma()
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:243
const Paint & paint
std::vector< std::shared_ptr< FakeTexture > > textures
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
uint32_t uint32_t * format
constexpr SkColor4f kTransparent
Definition SkColor.h:434
void * PromiseImageTextureContext
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)
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)
skgpu::Protected Protected
GrBackendApi ContextTypeBackend(skgpu::ContextType type)
static const int kContextTypeCount
Definition ContextType.h:42
Protected
Definition GpuTypes.h:61
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