Flutter Engine
The Flutter Engine
ProcessorTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 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
14#include "include/core/SkRect.h"
17#include "include/core/SkSize.h"
24#include "include/gpu/GrTypes.h"
30#include "src/base/SkRandom.h"
31#include "src/gpu/KeyBuilder.h"
33#include "src/gpu/Swizzle.h"
48#include "src/gpu/ganesh/SkGr.h"
56#include "tests/Test.h"
57#include "tests/TestHarness.h"
58#include "tests/TestUtils.h"
59#include "tools/EncodeUtils.h"
61
62#include <algorithm>
63#include <atomic>
64#include <cmath>
65#include <cstdint>
66#include <initializer_list>
67#include <memory>
68#include <random>
69#include <string>
70#include <tuple>
71#include <utility>
72#include <vector>
73
74using namespace skia_private;
75
76class GrDstProxyView;
78class GrOpFlushState;
79class GrProgramInfo;
82class SkArenaAlloc;
83enum class GrXferBarrierFlags;
84struct GrContextOptions;
85struct GrShaderCaps;
86
87namespace {
88class TestOp : public GrMeshDrawOp {
89public:
91 static GrOp::Owner Make(GrRecordingContext* rContext,
92 std::unique_ptr<GrFragmentProcessor> fp) {
93 return GrOp::Make<TestOp>(rContext, std::move(fp));
94 }
95
96 const char* name() const override { return "TestOp"; }
97
98 void visitProxies(const GrVisitProxyFunc& func) const override {
99 fProcessors.visitProxies(func);
100 }
101
102 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
103
104 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
105 GrClampType clampType) override {
106 static constexpr GrProcessorAnalysisColor kUnknownColor;
107 SkPMColor4f overrideColor;
108 return fProcessors.finalize(
110 &GrUserStencilSettings::kUnused, caps, clampType, &overrideColor);
111 }
112
113private:
114 friend class ::GrOp; // for ctor
115
116 TestOp(std::unique_ptr<GrFragmentProcessor> fp)
117 : INHERITED(ClassID()), fProcessors(std::move(fp)) {
118 this->setBounds(SkRect::MakeWH(100, 100), HasAABloat::kNo, IsHairline::kNo);
119 }
120
121 GrProgramInfo* programInfo() override { return nullptr; }
122 void onCreateProgramInfo(const GrCaps*,
124 const GrSurfaceProxyView& writeView,
125 bool usesMSAASurface,
127 const GrDstProxyView&,
128 GrXferBarrierFlags renderPassXferBarriers,
129 GrLoadOp colorLoadOp) override {}
130 void onPrePrepareDraws(GrRecordingContext*,
131 const GrSurfaceProxyView& writeView,
133 const GrDstProxyView&,
134 GrXferBarrierFlags renderPassXferBarriers,
135 GrLoadOp colorLoadOp) override {}
136 void onPrepareDraws(GrMeshDrawTarget*) override { return; }
137 void onExecute(GrOpFlushState*, const SkRect&) override { return; }
138
139 GrProcessorSet fProcessors;
140
141 using INHERITED = GrMeshDrawOp;
142};
143
144/**
145 * FP used to test ref counts on owned GrGpuResources. Can also be a parent FP to test counts
146 * of resources owned by child FPs.
147 */
148class TestFP : public GrFragmentProcessor {
149public:
150 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> child) {
151 return std::unique_ptr<GrFragmentProcessor>(new TestFP(std::move(child)));
152 }
153 static std::unique_ptr<GrFragmentProcessor> Make(const TArray<GrSurfaceProxyView>& views) {
154 return std::unique_ptr<GrFragmentProcessor>(new TestFP(views));
155 }
156
157 const char* name() const override { return "test"; }
158
159 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const override {
160 static std::atomic<int32_t> nextKey{0};
161 b->add32(nextKey++);
162 }
163
164 std::unique_ptr<GrFragmentProcessor> clone() const override {
165 return std::unique_ptr<GrFragmentProcessor>(new TestFP(*this));
166 }
167
168private:
169 TestFP(const TArray<GrSurfaceProxyView>& views)
170 : INHERITED(kTestFP_ClassID, kNone_OptimizationFlags) {
171 for (const GrSurfaceProxyView& view : views) {
172 this->registerChild(GrTextureEffect::Make(view, kUnknown_SkAlphaType));
173 }
174 }
175
176 TestFP(std::unique_ptr<GrFragmentProcessor> child)
177 : INHERITED(kTestFP_ClassID, kNone_OptimizationFlags) {
178 this->registerChild(std::move(child));
179 }
180
181 explicit TestFP(const TestFP& that) : INHERITED(that) {}
182
183 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
184 class Impl : public ProgramImpl {
185 public:
186 void emitCode(EmitArgs& args) override {
187 args.fFragBuilder->codeAppendf("return half4(1);");
188 }
189
190 private:
191 };
192 return std::make_unique<Impl>();
193 }
194
195 bool onIsEqual(const GrFragmentProcessor&) const override { return false; }
196
198};
199} // namespace
200
202 auto dContext = ctxInfo.directContext();
203 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
204
205 static constexpr SkISize kDims = {10, 10};
206
207 const GrBackendFormat format =
208 dContext->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
210 skgpu::Swizzle swizzle = dContext->priv().caps()->getReadSwizzle(format,
212
213 for (bool makeClone : {false, true}) {
214 for (int parentCnt = 0; parentCnt < 2; parentCnt++) {
215 auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(dContext,
217 nullptr,
219 {1, 1},
221 /*label=*/{});
222 {
224 proxyProvider->createProxy(format,
225 kDims,
227 1,
232 /*label=*/"ProcessorRefTest");
233
234 {
236 views.push_back({proxy, kTopLeft_GrSurfaceOrigin, swizzle});
237 auto fp = TestFP::Make(std::move(views));
238 for (int i = 0; i < parentCnt; ++i) {
239 fp = TestFP::Make(std::move(fp));
240 }
241 std::unique_ptr<GrFragmentProcessor> clone;
242 if (makeClone) {
243 clone = fp->clone();
244 }
245 GrOp::Owner op = TestOp::Make(dContext, std::move(fp));
246 sdc->addDrawOp(std::move(op));
247 if (clone) {
248 op = TestOp::Make(dContext, std::move(clone));
249 sdc->addDrawOp(std::move(op));
250 }
251 }
252
253 // If the fp is cloned the number of refs should increase by one (for the clone)
254 int expectedProxyRefs = makeClone ? 3 : 2;
255
256 CheckSingleThreadedProxyRefs(reporter, proxy.get(), expectedProxyRefs, -1);
257
258 dContext->flushAndSubmit();
259
260 // just one from the 'proxy' sk_sp
262 }
263 }
264 }
265}
266
267static DEFINE_bool(randomProcessorTest, false,
268 "Use non-deterministic seed for random processor tests?");
269static DEFINE_int(processorSeed, 0,
270 "Use specific seed for processor tests. Overridden by --randomProcessorTest.");
271
272#if defined(GR_TEST_UTILS)
273
274static GrColor input_texel_color(int x, int y, SkScalar delta) {
275 // Delta must be less than 0.5 to prevent over/underflow issues with the input color
276 SkASSERT(delta <= 0.5);
277
278 SkColor color = SkColorSetARGB((uint8_t)(x & 0xFF),
279 (uint8_t)(y & 0xFF),
280 (uint8_t)((x + y) & 0xFF),
281 (uint8_t)((2 * y - x) & 0xFF));
282 SkColor4f color4f = SkColor4f::FromColor(color);
283 // We only apply delta to the r,g, and b channels. This is because we're using this
284 // to test the canTweakAlphaForCoverage() optimization. A processor is allowed
285 // to use the input color's alpha in its calculation and report this optimization.
286 for (int i = 0; i < 3; i++) {
287 if (color4f[i] > 0.5) {
288 color4f[i] -= delta;
289 } else {
290 color4f[i] += delta;
291 }
292 }
293 return color4f.premul().toBytes_RGBA();
294}
295
296// The output buffer must be the same size as the render-target context.
297static void render_fp(GrDirectContext* dContext,
299 std::unique_ptr<GrFragmentProcessor> fp,
300 GrColor* outBuffer) {
301 sdc->fillWithFP(std::move(fp));
302 std::fill_n(outBuffer, sdc->width() * sdc->height(), 0);
304 GrPixmap resultPM(ii, outBuffer, sdc->width()*sizeof(uint32_t));
305 sdc->readPixels(dContext, resultPM, {0, 0});
306}
307
308// This class is responsible for reproducibly generating a random fragment processor.
309// An identical randomly-designed FP can be generated as many times as needed.
310class TestFPGenerator {
311 public:
312 TestFPGenerator() = delete;
313 TestFPGenerator(GrDirectContext* context, GrResourceProvider* resourceProvider)
314 : fContext(context)
315 , fResourceProvider(resourceProvider)
316 , fInitialSeed(synthesizeInitialSeed())
317 , fRandomSeed(fInitialSeed) {}
318
319 uint32_t initialSeed() { return fInitialSeed; }
320
321 bool init() {
322 // Initializes the two test texture proxies that are available to the FP test factories.
323 SkRandom random{fRandomSeed};
324 static constexpr int kTestTextureSize = 256;
325
326 {
327 // Put premul data into the RGBA texture that the test FPs can optionally use.
328 GrColor* rgbaData = new GrColor[kTestTextureSize * kTestTextureSize];
329 for (int y = 0; y < kTestTextureSize; ++y) {
330 for (int x = 0; x < kTestTextureSize; ++x) {
331 rgbaData[kTestTextureSize * y + x] = input_texel_color(
332 random.nextULessThan(256), random.nextULessThan(256), 0.0f);
333 }
334 }
335
336 SkImageInfo ii = SkImageInfo::Make(kTestTextureSize, kTestTextureSize,
339 bitmap.installPixels(
340 ii, rgbaData, ii.minRowBytes(),
341 [](void* addr, void* context) { delete[](GrColor*) addr; }, nullptr);
342 bitmap.setImmutable();
344 if (!view || !view.proxy()->instantiate(fResourceProvider)) {
345 SkDebugf("Unable to instantiate RGBA8888 test texture.");
346 return false;
347 }
348 fTestViews[0] = GrProcessorTestData::ViewInfo{view, GrColorType::kRGBA_8888,
350 }
351
352 {
353 // Put random values into the alpha texture that the test FPs can optionally use.
354 uint8_t* alphaData = new uint8_t[kTestTextureSize * kTestTextureSize];
355 for (int y = 0; y < kTestTextureSize; ++y) {
356 for (int x = 0; x < kTestTextureSize; ++x) {
357 alphaData[kTestTextureSize * y + x] = random.nextULessThan(256);
358 }
359 }
360
361 SkImageInfo ii = SkImageInfo::Make(kTestTextureSize, kTestTextureSize,
364 bitmap.installPixels(
365 ii, alphaData, ii.minRowBytes(),
366 [](void* addr, void* context) { delete[](uint8_t*) addr; }, nullptr);
367 bitmap.setImmutable();
369 if (!view || !view.proxy()->instantiate(fResourceProvider)) {
370 SkDebugf("Unable to instantiate A8 test texture.");
371 return false;
372 }
373 fTestViews[1] = GrProcessorTestData::ViewInfo{view, GrColorType::kAlpha_8,
375 }
376
377 return true;
378 }
379
380 void reroll() {
381 // Feed our current random seed into SkRandom to generate a new seed.
382 SkRandom random{fRandomSeed};
383 fRandomSeed = random.nextU();
384 }
385
386 std::unique_ptr<GrFragmentProcessor> make(int type, int randomTreeDepth,
387 std::unique_ptr<GrFragmentProcessor> inputFP) {
388 // This will generate the exact same randomized FP (of each requested type) each time
389 // it's called. Call `reroll` to get a different FP.
390 SkRandom random{fRandomSeed};
391 GrProcessorTestData testData{&random, fContext, randomTreeDepth,
392 static_cast<int>(std::size(fTestViews)), fTestViews,
393 std::move(inputFP)};
394 return GrFragmentProcessorTestFactory::MakeIdx(type, &testData);
395 }
396
397 std::unique_ptr<GrFragmentProcessor> make(int type, int randomTreeDepth,
400 return make(type, randomTreeDepth, GrTextureEffect::Make(std::move(view), alpha));
401 }
402
403 private:
404 static uint32_t synthesizeInitialSeed() {
405 if (FLAGS_randomProcessorTest) {
406 std::random_device rd;
407 return rd();
408 } else {
409 return FLAGS_processorSeed;
410 }
411 }
412
413 GrDirectContext* fContext; // owned by caller
414 GrResourceProvider* fResourceProvider; // owned by caller
415 const uint32_t fInitialSeed;
416 uint32_t fRandomSeed;
417 GrProcessorTestData::ViewInfo fTestViews[2];
418};
419
420// Creates an array of color values from input_texel_color(), to be used as an input texture.
421static std::vector<GrColor> make_input_pixels(int width, int height, SkScalar delta) {
422 std::vector<GrColor> pixel(width * height);
423 for (int y = 0; y < width; ++y) {
424 for (int x = 0; x < height; ++x) {
425 pixel[width * y + x] = input_texel_color(x, y, delta);
426 }
427 }
428
429 return pixel;
430}
431
432// Creates a texture of premul colors used as the output of the fragment processor that precedes
433// the fragment processor under test. An array of W*H colors are passed in as the texture data.
434static GrSurfaceProxyView make_input_texture(GrRecordingContext* context,
435 int width, int height, GrColor* pixel) {
438 bitmap.installPixels(ii, pixel, ii.minRowBytes());
439 bitmap.setImmutable();
441}
442
443// We tag logged data as unpremul to avoid conversion when encoding as PNG. The input texture
444// actually contains unpremul data. Also, even though we made the result data by rendering into
445// a "unpremul" SurfaceDrawContext, our input texture is unpremul and outside of the random
446// effect configuration, we didn't do anything to ensure the output is actually premul. We just
447// don't currently allow kUnpremul GrSurfaceDrawContexts.
448static constexpr auto kLogAlphaType = kUnpremul_SkAlphaType;
449
450static bool log_pixels(GrColor* pixels, int widthHeight, SkString* dst) {
452 SkImageInfo::Make(widthHeight, widthHeight, kRGBA_8888_SkColorType, kLogAlphaType);
453 SkBitmap bmp;
454 bmp.installPixels(info, pixels, widthHeight * sizeof(GrColor));
456}
457
458static bool log_texture_view(GrDirectContext* dContext, GrSurfaceProxyView src, SkString* dst) {
459 SkImageInfo ii = SkImageInfo::Make(src.proxy()->dimensions(), kRGBA_8888_SkColorType,
460 kLogAlphaType);
461
462 auto sContext = dContext->priv().makeSC(std::move(src), ii.colorInfo());
463 SkBitmap bm;
465 SkAssertResult(sContext->readPixels(dContext, bm.pixmap(), {0, 0}));
467}
468
469static bool fuzzy_color_equals(const SkPMColor4f& c1, const SkPMColor4f& c2) {
470 // With the loss of precision of rendering into 32-bit color, then estimating the FP's output
471 // from that, it is not uncommon for a valid output to differ from estimate by up to 0.01
472 // (really 1/128 ~ .0078, but frequently floating point issues make that tolerance a little
473 // too unforgiving).
474 static constexpr SkScalar kTolerance = 0.01f;
475 for (int i = 0; i < 4; i++) {
476 if (!SkScalarNearlyEqual(c1[i], c2[i], kTolerance)) {
477 return false;
478 }
479 }
480 return true;
481}
482
483// Given three input colors (color preceding the FP being tested) provided to the FP at the same
484// local coord and the three corresponding FP outputs, this ensures that either:
485// out[0] = fp * in[0].a, out[1] = fp * in[1].a, and out[2] = fp * in[2].a
486// where fp is the pre-modulated color that should not be changing across frames (FP's state doesn't
487// change), OR:
488// out[0] = fp * in[0], out[1] = fp * in[1], and out[2] = fp * in[2]
489// (per-channel modulation instead of modulation by just the alpha channel)
490// It does this by estimating the pre-modulated fp color from one of the input/output pairs and
491// confirms the conditions hold for the other two pairs.
492// It is required that the three input colors have the same alpha as fp is allowed to be a function
493// of the input alpha (but not r, g, or b).
494static bool legal_modulation(const GrColor inGr[3], const GrColor outGr[3]) {
495 // Convert to floating point, which is the number space the FP operates in (more or less)
496 SkPMColor4f inf[3], outf[3];
497 for (int i = 0; i < 3; ++i) {
498 inf[i] = SkPMColor4f::FromBytes_RGBA(inGr[i]);
499 outf[i] = SkPMColor4f::FromBytes_RGBA(outGr[i]);
500 }
501 // This test is only valid if all the input alphas are the same.
502 SkASSERT(inf[0].fA == inf[1].fA && inf[1].fA == inf[2].fA);
503
504 // Reconstruct the output of the FP before the shader modulated its color with the input value.
505 // When the original input is very small, it may cause the final output color to round
506 // to 0, in which case we estimate the pre-modulated color using one of the stepped frames that
507 // will then have a guaranteed larger channel value (since the offset will be added to it).
508 SkPMColor4f fpPreColorModulation = {0,0,0,0};
509 SkPMColor4f fpPreAlphaModulation = {0,0,0,0};
510 for (int i = 0; i < 4; i++) {
511 // Use the most stepped up frame
512 int maxInIdx = inf[0][i] > inf[1][i] ? 0 : 1;
513 maxInIdx = inf[maxInIdx][i] > inf[2][i] ? maxInIdx : 2;
514 const SkPMColor4f& in = inf[maxInIdx];
515 const SkPMColor4f& out = outf[maxInIdx];
516 if (in[i] > 0) {
517 fpPreColorModulation[i] = out[i] / in[i];
518 }
519 if (in[3] > 0) {
520 fpPreAlphaModulation[i] = out[i] / in[3];
521 }
522 }
523
524 // With reconstructed pre-modulated FP output, derive the expected value of fp * input for each
525 // of the transformed input colors.
526 SkPMColor4f expectedForAlphaModulation[3];
527 SkPMColor4f expectedForColorModulation[3];
528 for (int i = 0; i < 3; ++i) {
529 expectedForAlphaModulation[i] = fpPreAlphaModulation * inf[i].fA;
530 expectedForColorModulation[i] = fpPreColorModulation * inf[i];
531 // If the input alpha is 0 then the other channels should also be zero
532 // since the color is assumed to be premul. Modulating zeros by anything
533 // should produce zeros.
534 if (inf[i].fA == 0) {
535 SkASSERT(inf[i].fR == 0 && inf[i].fG == 0 && inf[i].fB == 0);
536 expectedForColorModulation[i] = expectedForAlphaModulation[i] = {0, 0, 0, 0};
537 }
538 }
539
540 bool isLegalColorModulation = fuzzy_color_equals(outf[0], expectedForColorModulation[0]) &&
541 fuzzy_color_equals(outf[1], expectedForColorModulation[1]) &&
542 fuzzy_color_equals(outf[2], expectedForColorModulation[2]);
543
544 bool isLegalAlphaModulation = fuzzy_color_equals(outf[0], expectedForAlphaModulation[0]) &&
545 fuzzy_color_equals(outf[1], expectedForAlphaModulation[1]) &&
546 fuzzy_color_equals(outf[2], expectedForAlphaModulation[2]);
547
548 // This can be enabled to print the values that caused this check to fail.
549 if ((false)) {
550 if (!isLegalColorModulation && !isLegalAlphaModulation) {
551 SkDebugf("Color modulation test\n\timplied mod color: (%.03f, %.03f, %.03f, %.03f)\n",
552 fpPreColorModulation[0],
553 fpPreColorModulation[1],
554 fpPreColorModulation[2],
555 fpPreColorModulation[3]);
556 for (int i = 0; i < 3; ++i) {
557 SkDebugf("\t(%.03f, %.03f, %.03f, %.03f) -> "
558 "(%.03f, %.03f, %.03f, %.03f) | "
559 "(%.03f, %.03f, %.03f, %.03f), ok: %d\n",
560 inf[i].fR, inf[i].fG, inf[i].fB, inf[i].fA,
561 outf[i].fR, outf[i].fG, outf[i].fB, outf[i].fA,
562 expectedForColorModulation[i].fR, expectedForColorModulation[i].fG,
563 expectedForColorModulation[i].fB, expectedForColorModulation[i].fA,
564 fuzzy_color_equals(outf[i], expectedForColorModulation[i]));
565 }
566 SkDebugf("Alpha modulation test\n\timplied mod color: (%.03f, %.03f, %.03f, %.03f)\n",
567 fpPreAlphaModulation[0],
568 fpPreAlphaModulation[1],
569 fpPreAlphaModulation[2],
570 fpPreAlphaModulation[3]);
571 for (int i = 0; i < 3; ++i) {
572 SkDebugf("\t(%.03f, %.03f, %.03f, %.03f) -> "
573 "(%.03f, %.03f, %.03f, %.03f) | "
574 "(%.03f, %.03f, %.03f, %.03f), ok: %d\n",
575 inf[i].fR, inf[i].fG, inf[i].fB, inf[i].fA,
576 outf[i].fR, outf[i].fG, outf[i].fB, outf[i].fA,
577 expectedForAlphaModulation[i].fR, expectedForAlphaModulation[i].fG,
578 expectedForAlphaModulation[i].fB, expectedForAlphaModulation[i].fA,
579 fuzzy_color_equals(outf[i], expectedForAlphaModulation[i]));
580 }
581 }
582 }
583 return isLegalColorModulation || isLegalAlphaModulation;
584}
585
586DEF_GANESH_TEST_FOR_GL_CONTEXT(ProcessorOptimizationValidationTest,
587 reporter,
588 ctxInfo,
590 GrDirectContext* context = ctxInfo.directContext();
591 GrResourceProvider* resourceProvider = context->priv().resourceProvider();
592 using FPFactory = GrFragmentProcessorTestFactory;
593
594 TestFPGenerator fpGenerator{context, resourceProvider};
595 if (!fpGenerator.init()) {
596 ERRORF(reporter, "Could not initialize TestFPGenerator");
597 return;
598 }
599
600 // Make the destination context for the test.
601 static constexpr int kRenderSize = 256;
604 nullptr,
606 {kRenderSize, kRenderSize},
608 /*label=*/{});
609
610 // Coverage optimization uses three frames with a linearly transformed input texture. The first
611 // frame has no offset, second frames add .2 and .4, which should then be present as a fixed
612 // difference between the frame outputs if the FP is properly following the modulation
613 // requirements of the coverage optimization.
614 static constexpr SkScalar kInputDelta = 0.2f;
615 std::vector<GrColor> inputPixels1 = make_input_pixels(kRenderSize, kRenderSize, 0.0f);
616 std::vector<GrColor> inputPixels2 =
617 make_input_pixels(kRenderSize, kRenderSize, 1 * kInputDelta);
618 std::vector<GrColor> inputPixels3 =
619 make_input_pixels(kRenderSize, kRenderSize, 2 * kInputDelta);
620 GrSurfaceProxyView inputTexture1 =
621 make_input_texture(context, kRenderSize, kRenderSize, inputPixels1.data());
622 GrSurfaceProxyView inputTexture2 =
623 make_input_texture(context, kRenderSize, kRenderSize, inputPixels2.data());
624 GrSurfaceProxyView inputTexture3 =
625 make_input_texture(context, kRenderSize, kRenderSize, inputPixels3.data());
626
627 // Encoded images are very verbose and this tests many potential images, so only export the
628 // first failure (subsequent failures have a reasonable chance of being related).
629 bool loggedFirstFailure = false;
630 bool loggedFirstWarning = false;
631
632 // Storage for the three frames required for coverage compatibility optimization testing.
633 // Each frame uses the correspondingly numbered inputTextureX.
634 std::vector<GrColor> readData1(kRenderSize * kRenderSize);
635 std::vector<GrColor> readData2(kRenderSize * kRenderSize);
636 std::vector<GrColor> readData3(kRenderSize * kRenderSize);
637
638 // Because processor factories configure themselves in random ways, this is not exhaustive.
639 for (int i = 0; i < FPFactory::Count(); ++i) {
640 int optimizedForOpaqueInput = 0;
641 int optimizedForCoverageAsAlpha = 0;
642 int optimizedForConstantOutputForInput = 0;
643
644#ifdef __MSVC_RUNTIME_CHECKS
645 // This test is infuriatingly slow with MSVC runtime checks enabled
646 static constexpr int kMinimumTrials = 1;
647 static constexpr int kMaximumTrials = 1;
648 static constexpr int kExpectedSuccesses = 1;
649#else
650 // We start by testing each fragment-processor 100 times, watching the optimization bits
651 // that appear. If we see an optimization bit appear in those first 100 trials, we keep
652 // running tests until we see at least five successful trials that have this optimization
653 // bit enabled. If we never see a particular optimization bit after 100 trials, we assume
654 // that this FP doesn't support that optimization at all.
655 static constexpr int kMinimumTrials = 100;
656 static constexpr int kMaximumTrials = 2000;
657 static constexpr int kExpectedSuccesses = 5;
658#endif
659
660 for (int trial = 0;; ++trial) {
661 // Create a randomly-configured FP.
662 fpGenerator.reroll();
663 std::unique_ptr<GrFragmentProcessor> fp =
664 fpGenerator.make(i, /*randomTreeDepth=*/1, inputTexture1);
665
666 // If we have iterated enough times and seen a sufficient number of successes on each
667 // optimization bit that can be returned, stop running trials.
668 if (trial >= kMinimumTrials) {
669 bool moreTrialsNeeded = (optimizedForOpaqueInput > 0 &&
670 optimizedForOpaqueInput < kExpectedSuccesses) ||
671 (optimizedForCoverageAsAlpha > 0 &&
672 optimizedForCoverageAsAlpha < kExpectedSuccesses) ||
673 (optimizedForConstantOutputForInput > 0 &&
674 optimizedForConstantOutputForInput < kExpectedSuccesses);
675 if (!moreTrialsNeeded) break;
676
677 if (trial >= kMaximumTrials) {
678 SkDebugf("Abandoning ProcessorOptimizationValidationTest after %d trials. "
679 "Seed: 0x%08x, processor:\n%s",
680 kMaximumTrials, fpGenerator.initialSeed(), fp->dumpTreeInfo().c_str());
681 break;
682 }
683 }
684
685 // Skip further testing if this trial has no optimization bits enabled.
686 if (!fp->hasConstantOutputForConstantInput() && !fp->preservesOpaqueInput() &&
687 !fp->compatibleWithCoverageAsAlpha()) {
688 continue;
689 }
690
691 // We can make identical copies of the test FP in order to test coverage-as-alpha.
692 if (fp->compatibleWithCoverageAsAlpha()) {
693 // Create and render two identical versions of this FP, but using different input
694 // textures, to check coverage optimization. We don't need to do this step for
695 // constant-output or preserving-opacity tests.
696 render_fp(context, sdc.get(),
697 fpGenerator.make(i, /*randomTreeDepth=*/1, inputTexture2),
698 readData2.data());
699 render_fp(context, sdc.get(),
700 fpGenerator.make(i, /*randomTreeDepth=*/1, inputTexture3),
701 readData3.data());
702 ++optimizedForCoverageAsAlpha;
703 }
704
705 if (fp->hasConstantOutputForConstantInput()) {
706 ++optimizedForConstantOutputForInput;
707 }
708
709 if (fp->preservesOpaqueInput()) {
710 ++optimizedForOpaqueInput;
711 }
712
713 // Draw base frame last so that rtc holds the original FP behavior if we need to dump
714 // the image to the log.
715 render_fp(context, sdc.get(), fpGenerator.make(i, /*randomTreeDepth=*/1, inputTexture1),
716 readData1.data());
717
718 // This test has a history of being flaky on a number of devices. If an FP is logically
719 // violating the optimizations, it's reasonable to expect it to violate requirements on
720 // a large number of pixels in the image. Sporadic pixel violations are more indicative
721 // of device errors and represents a separate problem.
722 static const int kMaxAcceptableFailedPixels =
723 CurrentTestHarnessIsSkQP() ? 0 : // Strict when running as SKQP
724 2 * kRenderSize; // ~0.7% of the image
725
726 // Collect first optimization failure message, to be output later as a warning or an
727 // error depending on whether the rendering "passed" or failed.
728 int failedPixelCount = 0;
729 SkString coverageMessage;
730 SkString opaqueMessage;
731 SkString constMessage;
732 for (int y = 0; y < kRenderSize; ++y) {
733 for (int x = 0; x < kRenderSize; ++x) {
734 bool passing = true;
735 GrColor input = inputPixels1[y * kRenderSize + x];
736 GrColor output = readData1[y * kRenderSize + x];
737
738 if (fp->compatibleWithCoverageAsAlpha()) {
739 GrColor ins[3];
740 ins[0] = input;
741 ins[1] = inputPixels2[y * kRenderSize + x];
742 ins[2] = inputPixels3[y * kRenderSize + x];
743
744 GrColor outs[3];
745 outs[0] = output;
746 outs[1] = readData2[y * kRenderSize + x];
747 outs[2] = readData3[y * kRenderSize + x];
748
749 if (!legal_modulation(ins, outs)) {
750 passing = false;
751 if (coverageMessage.isEmpty()) {
752 coverageMessage.printf(
753 "\"Modulating\" processor did not match alpha-modulation "
754 "nor color-modulation rules.\n"
755 "Input: 0x%08x, Output: 0x%08x, pixel (%d, %d).",
756 input, output, x, y);
757 }
758 }
759 }
760
763 SkPMColor4f expected4f;
764 if (fp->hasConstantOutputForConstantInput(input4f, &expected4f)) {
765 float rDiff = fabsf(output4f.fR - expected4f.fR);
766 float gDiff = fabsf(output4f.fG - expected4f.fG);
767 float bDiff = fabsf(output4f.fB - expected4f.fB);
768 float aDiff = fabsf(output4f.fA - expected4f.fA);
769 static constexpr float kTol = 4 / 255.f;
770 if (rDiff > kTol || gDiff > kTol || bDiff > kTol || aDiff > kTol) {
771 if (constMessage.isEmpty()) {
772 passing = false;
773
774 constMessage.printf(
775 "Processor claimed output for const input doesn't match "
776 "actual output.\n"
777 "Error: %f, Tolerance: %f, input: (%f, %f, %f, %f), "
778 "actual: (%f, %f, %f, %f), expected(%f, %f, %f, %f).",
779 std::max(rDiff, std::max(gDiff, std::max(bDiff, aDiff))),
780 kTol, input4f.fR, input4f.fG, input4f.fB, input4f.fA,
781 output4f.fR, output4f.fG, output4f.fB, output4f.fA,
782 expected4f.fR, expected4f.fG, expected4f.fB, expected4f.fA);
783 }
784 }
785 }
786 if (input4f.isOpaque() && fp->preservesOpaqueInput() && !output4f.isOpaque()) {
787 passing = false;
788
789 if (opaqueMessage.isEmpty()) {
790 opaqueMessage.printf(
791 "Processor claimed opaqueness is preserved but "
792 "it is not. Input: 0x%08x, Output: 0x%08x.",
793 input, output);
794 }
795 }
796
797 if (!passing) {
798 // Regardless of how many optimizations the pixel violates, count it as a
799 // single bad pixel.
800 failedPixelCount++;
801 }
802 }
803 }
804
805 // Finished analyzing the entire image, see if the number of pixel failures meets the
806 // threshold for an FP violating the optimization requirements.
807 if (failedPixelCount > kMaxAcceptableFailedPixels) {
809 "Processor violated %d of %d pixels, seed: 0x%08x.\n"
810 "Processor:\n%s\nFirst failing pixel details are below:",
811 failedPixelCount, kRenderSize * kRenderSize, fpGenerator.initialSeed(),
812 fp->dumpTreeInfo().c_str());
813
814 // Print first failing pixel's details.
815 if (!coverageMessage.isEmpty()) {
816 ERRORF(reporter, "%s", coverageMessage.c_str());
817 }
818 if (!constMessage.isEmpty()) {
819 ERRORF(reporter, "%s", constMessage.c_str());
820 }
821 if (!opaqueMessage.isEmpty()) {
822 ERRORF(reporter, "%s", opaqueMessage.c_str());
823 }
824
825 if (!loggedFirstFailure) {
826 // Print with ERRORF to make sure the encoded image is output
827 SkString input;
828 log_texture_view(context, inputTexture1, &input);
830 log_pixels(readData1.data(), kRenderSize, &output);
831 ERRORF(reporter, "Input image: %s\n\n"
832 "===========================================================\n\n"
833 "Output image: %s\n", input.c_str(), output.c_str());
834 loggedFirstFailure = true;
835 }
836 } else if (failedPixelCount > 0) {
837 // Don't trigger an error, but don't just hide the failures either.
838 INFOF(reporter, "Processor violated %d of %d pixels (below error threshold), seed: "
839 "0x%08x, processor: %s", failedPixelCount, kRenderSize * kRenderSize,
840 fpGenerator.initialSeed(), fp->dumpInfo().c_str());
841 if (!coverageMessage.isEmpty()) {
842 INFOF(reporter, "%s", coverageMessage.c_str());
843 }
844 if (!constMessage.isEmpty()) {
845 INFOF(reporter, "%s", constMessage.c_str());
846 }
847 if (!opaqueMessage.isEmpty()) {
848 INFOF(reporter, "%s", opaqueMessage.c_str());
849 }
850 if (!loggedFirstWarning) {
851 SkString input;
852 log_texture_view(context, inputTexture1, &input);
854 log_pixels(readData1.data(), kRenderSize, &output);
855 INFOF(reporter, "Input image: %s\n\n"
856 "===========================================================\n\n"
857 "Output image: %s\n", input.c_str(), output.c_str());
858 loggedFirstWarning = true;
859 }
860 }
861 }
862 }
863}
864
865static void assert_processor_equality(skiatest::Reporter* reporter,
866 const GrFragmentProcessor& fp,
867 const GrFragmentProcessor& clone) {
868 REPORTER_ASSERT(reporter, !strcmp(fp.name(), clone.name()),
869 "\n%s", fp.dumpTreeInfo().c_str());
870 REPORTER_ASSERT(reporter, fp.compatibleWithCoverageAsAlpha() ==
872 "\n%s", fp.dumpTreeInfo().c_str());
873 REPORTER_ASSERT(reporter, fp.isEqual(clone),
874 "\n%s", fp.dumpTreeInfo().c_str());
875 REPORTER_ASSERT(reporter, fp.preservesOpaqueInput() == clone.preservesOpaqueInput(),
876 "\n%s", fp.dumpTreeInfo().c_str());
877 REPORTER_ASSERT(reporter, fp.hasConstantOutputForConstantInput() ==
879 "\n%s", fp.dumpTreeInfo().c_str());
880 REPORTER_ASSERT(reporter, fp.numChildProcessors() == clone.numChildProcessors(),
881 "\n%s", fp.dumpTreeInfo().c_str());
882 REPORTER_ASSERT(reporter, fp.sampleUsage() == clone.sampleUsage(),
883 "\n%s", fp.dumpTreeInfo().c_str());
884 REPORTER_ASSERT(reporter, fp.usesSampleCoords() == clone.usesSampleCoords(),
885 "\n%s", fp.dumpTreeInfo().c_str());
886}
887
888static bool verify_identical_render(skiatest::Reporter* reporter, int renderSize,
889 const char* processorType,
890 const GrColor readData1[], const GrColor readData2[]) {
891 // The ProcessorClone test has a history of being flaky on a number of devices. If an FP clone
892 // is logically wrong, it's reasonable to expect it produce a large number of pixel differences
893 // in the image. Sporadic pixel violations are more indicative device errors and represents a
894 // separate problem.
895 static const int maxAcceptableFailedPixels =
896 CurrentTestHarnessIsSkQP() ? 0 : // Strict when running as SKQP
897 2 * renderSize; // ~0.002% of the pixels (size 1024*1024)
898
899 int failedPixelCount = 0;
900 int firstWrongX = 0;
901 int firstWrongY = 0;
902 int idx = 0;
903 for (int y = 0; y < renderSize; ++y) {
904 for (int x = 0; x < renderSize; ++x, ++idx) {
905 if (readData1[idx] != readData2[idx]) {
906 if (!failedPixelCount) {
907 firstWrongX = x;
908 firstWrongY = y;
909 }
910 ++failedPixelCount;
911 }
912 if (failedPixelCount > maxAcceptableFailedPixels) {
913 idx = firstWrongY * renderSize + firstWrongX;
915 "%s produced different output at (%d, %d). "
916 "Input color: 0x%08x, Original Output Color: 0x%08x, "
917 "Clone Output Color: 0x%08x.",
918 processorType, firstWrongX, firstWrongY, input_texel_color(x, y, 0.0f),
919 readData1[idx], readData2[idx]);
920
921 return false;
922 }
923 }
924 }
925
926 return true;
927}
928
929static void log_clone_failure(skiatest::Reporter* reporter, int renderSize,
930 GrDirectContext* context, const GrSurfaceProxyView& inputTexture,
931 GrColor pixelsFP[], GrColor pixelsClone[], GrColor pixelsRegen[]) {
932 // Write the images out as data URLs for inspection.
933 SkString inputURL, origURL, cloneURL, regenURL;
934 if (log_texture_view(context, inputTexture, &inputURL) &&
935 log_pixels(pixelsFP, renderSize, &origURL) &&
936 log_pixels(pixelsClone, renderSize, &cloneURL) &&
937 log_pixels(pixelsRegen, renderSize, &regenURL)) {
939 "\nInput image:\n%s\n\n"
940 "==========================================================="
941 "\n\n"
942 "Orig output image:\n%s\n"
943 "==========================================================="
944 "\n\n"
945 "Clone output image:\n%s\n"
946 "==========================================================="
947 "\n\n"
948 "Regen output image:\n%s\n",
949 inputURL.c_str(), origURL.c_str(), cloneURL.c_str(), regenURL.c_str());
950 }
951}
952
953// Tests that a fragment processor returned by GrFragmentProcessor::clone() is equivalent to its
954// progenitor.
956 GrDirectContext* context = ctxInfo.directContext();
957 GrResourceProvider* resourceProvider = context->priv().resourceProvider();
958
959 TestFPGenerator fpGenerator{context, resourceProvider};
960 if (!fpGenerator.init()) {
961 ERRORF(reporter, "Could not initialize TestFPGenerator");
962 return;
963 }
964
965 // Make the destination context for the test.
966 static constexpr int kRenderSize = 1024;
969 nullptr,
971 {kRenderSize, kRenderSize},
973 /*label=*/{});
974
975 std::vector<GrColor> inputPixels = make_input_pixels(kRenderSize, kRenderSize, 0.0f);
976 GrSurfaceProxyView inputTexture =
977 make_input_texture(context, kRenderSize, kRenderSize, inputPixels.data());
978
979 // On failure we write out images, but just write the first failing set as the print is very
980 // large.
981 bool loggedFirstFailure = false;
982
983 // Storage for the original frame's readback and the readback of its clone.
984 std::vector<GrColor> readDataFP(kRenderSize * kRenderSize);
985 std::vector<GrColor> readDataClone(kRenderSize * kRenderSize);
986 std::vector<GrColor> readDataRegen(kRenderSize * kRenderSize);
987
988 // Because processor factories configure themselves in random ways, this is not exhaustive.
989 for (int i = 0; i < GrFragmentProcessorTestFactory::Count(); ++i) {
990 static constexpr int kTimesToInvokeFactory = 10;
991 for (int j = 0; j < kTimesToInvokeFactory; ++j) {
992 fpGenerator.reroll();
993 std::unique_ptr<GrFragmentProcessor> fp =
994 fpGenerator.make(i, /*randomTreeDepth=*/1, /*inputFP=*/nullptr);
995 std::unique_ptr<GrFragmentProcessor> regen =
996 fpGenerator.make(i, /*randomTreeDepth=*/1, /*inputFP=*/nullptr);
997 std::unique_ptr<GrFragmentProcessor> clone = fp->clone();
998 if (!clone) {
999 ERRORF(reporter, "Clone of processor %s failed.", fp->dumpTreeInfo().c_str());
1000 continue;
1001 }
1002 assert_processor_equality(reporter, *fp, *clone);
1003
1004 // Draw with original and read back the results.
1005 render_fp(context, sdc.get(), std::move(fp), readDataFP.data());
1006
1007 // Draw with clone and read back the results.
1008 render_fp(context, sdc.get(), std::move(clone), readDataClone.data());
1009
1010 // Check that the results are the same.
1011 if (!verify_identical_render(reporter, kRenderSize, "Processor clone",
1012 readDataFP.data(), readDataClone.data())) {
1013 // Dump a description from the regenerated processor (since the original FP has
1014 // already been consumed).
1015 ERRORF(reporter, "FP hierarchy:\n%s", regen->dumpTreeInfo().c_str());
1016
1017 // Render and readback output from the regenerated FP. If this also mismatches, the
1018 // FP itself doesn't generate consistent output. This could happen if:
1019 // - the FP's TestCreate() does not always generate the same FP from a given seed
1020 // - the FP's Make() does not always generate the same FP when given the same inputs
1021 // - the FP itself generates inconsistent pixels (shader UB?)
1022 // - the driver has a bug
1023 render_fp(context, sdc.get(), std::move(regen), readDataRegen.data());
1024
1025 if (!verify_identical_render(reporter, kRenderSize, "Regenerated processor",
1026 readDataFP.data(), readDataRegen.data())) {
1027 ERRORF(reporter, "Output from regen did not match original!\n");
1028 } else {
1029 ERRORF(reporter, "Regenerated processor output matches original results.\n");
1030 }
1031
1032 // If this is the first time we've encountered a cloning failure, log the generated
1033 // images to the reporter as data URLs.
1034 if (!loggedFirstFailure) {
1035 log_clone_failure(reporter, kRenderSize, context, inputTexture,
1036 readDataFP.data(), readDataClone.data(),
1037 readDataRegen.data());
1038 loggedFirstFailure = true;
1039 }
1040 }
1041 }
1042 }
1043}
1044
1045#endif // defined(GR_TEST_UTILS)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
reporter
Definition: FontMgrTest.cpp:39
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
uint32_t GrColor
Definition: GrColor.h:25
#define DEFINE_OP_CLASS_ID
Definition: GrOp.h:64
static constexpr float kTolerance
Definition: GrQuadUtils.cpp:29
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrLoadOp
Definition: GrTypesPriv.h:155
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
GrXferBarrierFlags
static DEFINE_bool(randomProcessorTest, false, "Use non-deterministic seed for random processor tests?")
DEF_GANESH_TEST_FOR_ALL_CONTEXTS(ProcessorRefTest, reporter, ctxInfo, CtsEnforcement::kNever)
static DEFINE_int(processorSeed, 0, "Use specific seed for processor tests. Overridden by --randomProcessorTest.")
kUnpremul_SkAlphaType
SkAlphaType
Definition: SkAlphaType.h:26
@ kUnknown_SkAlphaType
uninitialized
Definition: SkAlphaType.h:27
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ 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
uint32_t SkColor
Definition: SkColor.h:37
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColor.h:49
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
std::tuple< GrSurfaceProxyView, GrColorType > GrMakeUncachedBitmapProxyView(GrRecordingContext *rContext, const SkBitmap &bitmap, skgpu::Mipmapped mipmapped, SkBackingFit fit, skgpu::Budgeted budgeted)
Definition: SkGr.cpp:253
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
const Context & fContext
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
bool CurrentTestHarnessIsSkQP()
Definition: TestHarness.cpp:10
void CheckSingleThreadedProxyRefs(skiatest::Reporter *reporter, GrSurfaceProxy *proxy, int32_t expectedProxyRefs, int32_t expectedBackingRefs)
Definition: TestUtils.cpp:231
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define DEF_GANESH_TEST_FOR_GL_CONTEXT(name, reporter, context_info, ctsEnforcement)
Definition: Test.h:442
#define INFOF(REPORTER,...)
Definition: Test.h:298
#define ERRORF(r,...)
Definition: Test.h:293
GLenum type
Definition: GrCaps.h:57
GrResourceProvider * resourceProvider()
GrDirectContextPriv priv()
bool preservesOpaqueInput() const
bool hasConstantOutputForConstantInput(SkPMColor4f inputColor, SkPMColor4f *outputColor) const
const SkSL::SampleUsage & sampleUsage() const
bool compatibleWithCoverageAsAlpha() const
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
virtual const char * name() const =0
sk_sp< GrTextureProxy > createProxy(const GrBackendFormat &, SkISize dimensions, GrRenderable, int renderTargetSampleCnt, skgpu::Mipmapped, SkBackingFit, skgpu::Budgeted, GrProtected, std::string_view label, GrInternalSurfaceFlags=GrInternalSurfaceFlags::kNone, UseAllocator useAllocator=UseAllocator::kYes)
std::unique_ptr< skgpu::ganesh::SurfaceContext > makeSC(GrSurfaceProxyView readView, const GrColorInfo &)
static std::unique_ptr< GrFragmentProcessor > Make(GrSurfaceProxyView, SkAlphaType, const SkMatrix &=SkMatrix::I(), GrSamplerState::Filter=GrSamplerState::Filter::kNearest, GrSamplerState::MipmapMode mipmapMode=GrSamplerState::MipmapMode::kNone)
bool installPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, void(*releaseProc)(void *addr, void *context), void *context)
Definition: SkBitmap.cpp:323
const SkPixmap & pixmap() const
Definition: SkBitmap.h:133
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:271
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
bool isEmpty() const
Definition: SkString.h:130
const char * c_str() const
Definition: SkString.h:133
T * get() const
Definition: SkRefCnt.h:303
bool readPixels(GrDirectContext *dContext, GrPixmap dst, SkIPoint srcPt)
static std::unique_ptr< SurfaceDrawContext > Make(GrRecordingContext *, GrColorType, sk_sp< GrSurfaceProxy >, sk_sp< SkColorSpace >, GrSurfaceOrigin, const SkSurfaceProps &)
void fillWithFP(std::unique_ptr< GrFragmentProcessor > fp)
DlColor color
float SkScalar
Definition: extension.cpp:12
static bool b
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t uint32_t * format
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
static bool init()
bool BitmapToBase64DataURI(const SkBitmap &bitmap, SkString *dst)
Definition: EncodeUtils.cpp:25
Definition: bitmap.py:1
const uint32_t fp
@ kNone
Definition: layer.h:53
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
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 running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
const myers::Point & get< 0 >(const myers::Segment &s)
Definition: Myers.h:80
static void make(SkBitmap *bitmap, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace)
Definition: encode_srgb.cpp:35
Definition: ref_ptr.h:256
int32_t height
int32_t width
static const GrUserStencilSettings & kUnused
Definition: SkSize.h:16
const SkColorInfo & colorInfo() const
Definition: SkImageInfo.h:404
size_t minRowBytes() const
Definition: SkImageInfo.h:517
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
float fB
blue component
Definition: SkColor.h:265
static SkRGBA4f FromBytes_RGBA(uint32_t color)
float fR
red component
Definition: SkColor.h:263
float fG
green component
Definition: SkColor.h:264
float fA
alpha component
Definition: SkColor.h:266
bool isOpaque() const
Definition: SkColor.h:344
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609