Flutter Engine
The Flutter Engine
SkRuntimeEffectTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2019 Google LLC
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
16#include "include/core/SkData.h"
23#include "include/core/SkSize.h"
24#include "include/core/SkSpan.h"
41#include "src/base/SkTLazy.h"
44#include "src/gpu/KeyBuilder.h"
54#include "src/sksl/SkSLString.h"
56#include "tests/Test.h"
57
58#include <array>
59#include <cstdint>
60#include <functional>
61#include <initializer_list>
62#include <memory>
63#include <string>
64#include <thread>
65#include <utility>
66
67using namespace skia_private;
68
70struct GrContextOptions;
71struct SkIPoint;
72
73#if defined(SK_GRAPHITE)
79
80struct GraphiteInfo {
83};
84#else
86 void* context = nullptr;
87 void* recorder = nullptr;
88};
89#endif
90
91void test_invalid_effect(skiatest::Reporter* r, const char* src, const char* expected) {
92 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(src));
93 REPORTER_ASSERT(r, !effect);
94 REPORTER_ASSERT(r, errorText.contains(expected),
95 "Expected error message to contain \"%s\". Actual message: \"%s\"",
96 expected, errorText.c_str());
97}
98
99#define EMPTY_MAIN "half4 main(float2 p) { return half4(0); }"
100
101DEF_TEST(SkRuntimeEffectInvalid_NoInVariables, r) {
102 // 'in' variables aren't allowed at all:
103 test_invalid_effect(r, "in bool b;" EMPTY_MAIN, "'in'");
104 test_invalid_effect(r, "in float f;" EMPTY_MAIN, "'in'");
105 test_invalid_effect(r, "in float2 v;" EMPTY_MAIN, "'in'");
106 test_invalid_effect(r, "in half3x3 m;" EMPTY_MAIN, "'in'");
107}
108
109DEF_TEST(SkRuntimeEffectInvalid_UndefinedFunction, r) {
110 test_invalid_effect(r, "half4 missing(); half4 main(float2 p) { return missing(); }",
111 "function 'half4 missing()' is not defined");
112}
113
114DEF_TEST(SkRuntimeEffectInvalid_UndefinedMain, r) {
115 // Shouldn't be possible to create an SkRuntimeEffect without "main"
116 test_invalid_effect(r, "", "main");
117}
118
119DEF_TEST(SkRuntimeEffectInvalid_SkCapsDisallowed, r) {
120 // sk_Caps is an internal system. It should not be visible to runtime effects
122 r,
123 "half4 main(float2 p) { return sk_Caps.floatIs32Bits ? half4(1) : half4(0); }",
124 "name 'sk_Caps' is reserved");
125}
126
127DEF_TEST(SkRuntimeEffect_DeadCodeEliminationStackOverflow, r) {
128 // Verify that a deeply-nested loop does not cause stack overflow during dead-code elimination.
129 auto [effect, errorText] = SkRuntimeEffect::MakeForColorFilter(SkString(R"(
130 half4 main(half4 color) {
131 half value = color.r;
132
133 for (int a=0; a<10; ++a) { // 10
134 for (int b=0; b<10; ++b) { // 100
135 for (int c=0; c<10; ++c) { // 1000
136 for (int d=0; d<10; ++d) { // 10000
137 ++value;
138 }}}}
139
140 return value.xxxx;
141 }
142 )"));
143 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
144}
145
146DEF_TEST(SkRuntimeEffectCanDisableES2Restrictions, r) {
147 auto test_valid_es3 = [](skiatest::Reporter* r, const char* sksl) {
149 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl), opt);
150 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
151 };
152
153 test_invalid_effect(r, "float f[2] = float[2](0, 1);" EMPTY_MAIN, "construction of array type");
154 test_valid_es3 (r, "float f[2] = float[2](0, 1);" EMPTY_MAIN);
155}
156
157DEF_TEST(SkRuntimeEffectCanEnableVersion300, r) {
158 auto test_valid = [](skiatest::Reporter* r, const char* sksl) {
159 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl));
160 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
161 };
162
163 test_invalid_effect(r, "#version 100\nfloat f[2] = float[2](0, 1);" EMPTY_MAIN,
164 "construction of array type");
165 test_valid (r, "#version 300\nfloat f[2] = float[2](0, 1);" EMPTY_MAIN);
166}
167
168DEF_TEST(SkRuntimeEffectUniformFlags, r) {
169 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(R"(
170 uniform int simple; // should have no flags
171 uniform float arrayOfOne[1]; // should have kArray_Flag
172 uniform float arrayOfMultiple[2]; // should have kArray_Flag
173 layout(color) uniform float4 color; // should have kColor_Flag
174 uniform half3 halfPrecisionFloat; // should have kHalfPrecision_Flag
175 layout(color) uniform half4 allFlags[2]; // should have Array | Color | HalfPrecision
176 )" EMPTY_MAIN));
177 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
178
179 SkSpan<const SkRuntimeEffect::Uniform> uniforms = effect->uniforms();
180 REPORTER_ASSERT(r, uniforms.size() == 6);
181
182 REPORTER_ASSERT(r, uniforms[0].flags == 0);
190}
191
192DEF_TEST(SkRuntimeEffectValidation, r) {
193 auto es2Effect = SkRuntimeEffect::MakeForShader(SkString("#version 100\n" EMPTY_MAIN)).effect;
194 auto es3Effect = SkRuntimeEffect::MakeForShader(SkString("#version 300\n" EMPTY_MAIN)).effect;
195 REPORTER_ASSERT(r, es2Effect && es3Effect);
196
197 auto es2Caps = SkCapabilities::RasterBackend();
198 REPORTER_ASSERT(r, es2Caps->skslVersion() == SkSL::Version::k100);
199
200 REPORTER_ASSERT(r, SkRuntimeEffectPriv::CanDraw(es2Caps.get(), es2Effect.get()));
201 REPORTER_ASSERT(r, !SkRuntimeEffectPriv::CanDraw(es2Caps.get(), es3Effect.get()));
202}
203
204DEF_TEST(SkRuntimeEffectForColorFilter, r) {
205 // Tests that the color filter factory rejects or accepts certain SkSL constructs
206 auto test_valid = [r](const char* sksl) {
207 auto [effect, errorText] = SkRuntimeEffect::MakeForColorFilter(SkString(sksl));
208 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
209 };
210
211 auto test_invalid = [r](const char* sksl, const char* expected) {
212 auto [effect, errorText] = SkRuntimeEffect::MakeForColorFilter(SkString(sksl));
213 REPORTER_ASSERT(r, !effect);
215 errorText.contains(expected),
216 "Expected error message to contain \"%s\". Actual message: \"%s\"",
217 expected,
218 errorText.c_str());
219 };
220
221 // Color filters must use the 'half4 main(half4)' signature. Either color can be float4/vec4
222 test_valid("half4 main(half4 c) { return c; }");
223 test_valid("float4 main(half4 c) { return c; }");
224 test_valid("half4 main(float4 c) { return c; }");
225 test_valid("float4 main(float4 c) { return c; }");
226 test_valid("vec4 main(half4 c) { return c; }");
227 test_valid("half4 main(vec4 c) { return c; }");
228 test_valid("vec4 main(vec4 c) { return c; }");
229
230 // Invalid return types
231 test_invalid("void main(half4 c) {}", "'main' must return");
232 test_invalid("half3 main(half4 c) { return c.rgb; }", "'main' must return");
233
234 // Invalid argument types (some are valid as shaders, but not color filters)
235 test_invalid("half4 main() { return half4(1); }", "'main' parameter");
236 test_invalid("half4 main(float2 p) { return half4(1); }", "'main' parameter");
237 test_invalid("half4 main(float2 p, half4 c) { return c; }", "'main' parameter");
238
239 // sk_FragCoord should not be available
240 test_invalid("half4 main(half4 c) { return sk_FragCoord.xy01; }", "unknown identifier");
241
242 // Sampling a child shader requires that we pass explicit coords
243 test_valid("uniform shader child;"
244 "half4 main(half4 c) { return child.eval(c.rg); }");
245
246 // Sampling a colorFilter requires a color
247 test_valid("uniform colorFilter child;"
248 "half4 main(half4 c) { return child.eval(c); }");
249
250 // Sampling a blender requires two colors
251 test_valid("uniform blender child;"
252 "half4 main(half4 c) { return child.eval(c, c); }");
253}
254
255DEF_TEST(SkRuntimeEffectForBlender, r) {
256 // Tests that the blender factory rejects or accepts certain SkSL constructs
257 auto test_valid = [r](const char* sksl) {
258 auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(SkString(sksl));
259 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
260 };
261
262 auto test_invalid = [r](const char* sksl, const char* expected) {
263 auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(SkString(sksl));
264 REPORTER_ASSERT(r, !effect);
266 errorText.contains(expected),
267 "Expected error message to contain \"%s\". Actual message: \"%s\"",
268 expected,
269 errorText.c_str());
270 };
271
272 // Blenders must use the 'half4 main(half4, half4)' signature. Any mixture of float4/vec4/half4
273 // is allowed.
274 test_valid("half4 main(half4 s, half4 d) { return s; }");
275 test_valid("float4 main(float4 s, float4 d) { return d; }");
276 test_valid("float4 main(half4 s, float4 d) { return s; }");
277 test_valid("half4 main(float4 s, half4 d) { return d; }");
278 test_valid("vec4 main(half4 s, half4 d) { return s; }");
279 test_valid("half4 main(vec4 s, vec4 d) { return d; }");
280 test_valid("vec4 main(vec4 s, vec4 d) { return s; }");
281
282 // Invalid return types
283 test_invalid("void main(half4 s, half4 d) {}", "'main' must return");
284 test_invalid("half3 main(half4 s, half4 d) { return s.rgb; }", "'main' must return");
285
286 // Invalid argument types (some are valid as shaders/color filters)
287 test_invalid("half4 main() { return half4(1); }", "'main' parameter");
288 test_invalid("half4 main(half4 c) { return c; }", "'main' parameter");
289 test_invalid("half4 main(float2 p) { return half4(1); }", "'main' parameter");
290 test_invalid("half4 main(float2 p, half4 c) { return c; }", "'main' parameter");
291 test_invalid("half4 main(float2 p, half4 a, half4 b) { return a; }", "'main' parameter");
292 test_invalid("half4 main(half4 a, half4 b, half4 c) { return a; }", "'main' parameter");
293
294 // sk_FragCoord should not be available
295 test_invalid("half4 main(half4 s, half4 d) { return sk_FragCoord.xy01; }",
296 "unknown identifier");
297
298 // Sampling a child shader requires that we pass explicit coords
299 test_valid("uniform shader child;"
300 "half4 main(half4 s, half4 d) { return child.eval(s.rg); }");
301
302 // Sampling a colorFilter requires a color
303 test_valid("uniform colorFilter child;"
304 "half4 main(half4 s, half4 d) { return child.eval(d); }");
305
306 // Sampling a blender requires two colors
307 test_valid("uniform blender child;"
308 "half4 main(half4 s, half4 d) { return child.eval(s, d); }");
309}
310
311DEF_TEST(SkRuntimeEffectForShader, r) {
312 // Tests that the shader factory rejects or accepts certain SkSL constructs
313 auto test_valid = [r](const char* sksl, SkRuntimeEffect::Options options = {}) {
314 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl), options);
315 REPORTER_ASSERT(r, effect, "%s", errorText.c_str());
316 };
317
318 auto test_invalid = [r](const char* sksl,
319 const char* expected,
321 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(sksl));
322 REPORTER_ASSERT(r, !effect);
324 errorText.contains(expected),
325 "Expected error message to contain \"%s\". Actual message: \"%s\"",
326 expected,
327 errorText.c_str());
328 };
329
330 // Shaders must use the 'half4 main(float2)' signature
331 // Either color can be half4/float4/vec4, but the coords must be float2/vec2
332 test_valid("half4 main(float2 p) { return p.xyxy; }");
333 test_valid("float4 main(float2 p) { return p.xyxy; }");
334 test_valid("vec4 main(float2 p) { return p.xyxy; }");
335 test_valid("half4 main(vec2 p) { return p.xyxy; }");
336 test_valid("vec4 main(vec2 p) { return p.xyxy; }");
337
338 // The 'half4 main(float2, half4|float4)' signature is disallowed on both public and private
339 // runtime effects.
342 test_invalid("half4 main(float2 p, half4 c) { return c; }", "'main' parameter");
343 test_invalid("half4 main(float2 p, half4 c) { return c; }", "'main' parameter", options);
344
345 test_invalid("half4 main(float2 p, float4 c) { return c; }", "'main' parameter");
346 test_invalid("half4 main(float2 p, float4 c) { return c; }", "'main' parameter", options);
347
348 test_invalid("half4 main(float2 p, vec4 c) { return c; }", "'main' parameter");
349 test_invalid("half4 main(float2 p, vec4 c) { return c; }", "'main' parameter", options);
350
351 test_invalid("float4 main(float2 p, half4 c) { return c; }", "'main' parameter");
352 test_invalid("float4 main(float2 p, half4 c) { return c; }", "'main' parameter", options);
353
354 test_invalid("vec4 main(float2 p, half4 c) { return c; }", "'main' parameter");
355 test_invalid("vec4 main(float2 p, half4 c) { return c; }", "'main' parameter", options);
356
357 test_invalid("vec4 main(vec2 p, vec4 c) { return c; }", "'main' parameter");
358 test_invalid("vec4 main(vec2 p, vec4 c) { return c; }", "'main' parameter", options);
359
360 // Invalid return types
361 test_invalid("void main(float2 p) {}", "'main' must return");
362 test_invalid("half3 main(float2 p) { return p.xy1; }", "'main' must return");
363
364 // Invalid argument types (some are valid as color filters, but not shaders)
365 test_invalid("half4 main() { return half4(1); }", "'main' parameter");
366 test_invalid("half4 main(half4 c) { return c; }", "'main' parameter");
367
368 // sk_FragCoord should be available, but only if we've enabled it via Options
369 test_invalid("half4 main(float2 p) { return sk_FragCoord.xy01; }",
370 "unknown identifier 'sk_FragCoord'");
371
372 test_valid("half4 main(float2 p) { return sk_FragCoord.xy01; }", options);
373
374 // Sampling a child shader requires that we pass explicit coords
375 test_valid("uniform shader child;"
376 "half4 main(float2 p) { return child.eval(p); }");
377
378 // Sampling a colorFilter requires a color
379 test_valid("uniform colorFilter child;"
380 "half4 main(float2 p) { return child.eval(half4(1)); }");
381
382 // Sampling a blender requires two colors
383 test_valid("uniform blender child;"
384 "half4 main(float2 p) { return child.eval(half4(0.5), half4(0.6)); }");
385}
386
388
389void paint_canvas(SkCanvas* canvas, SkPaint* paint, const PreTestFn& preTestCallback) {
390 canvas->save();
391 if (preTestCallback) {
392 preTestCallback(canvas, paint);
393 }
394 canvas->drawPaint(*paint);
395 canvas->restore();
396}
397
399 GrColor* pixels) {
400 SkImageInfo info = surface->imageInfo();
401 SkPixmap dest{info, pixels, info.minRowBytes()};
402 return surface->readPixels(dest, /*srcX=*/0, /*srcY=*/0);
403}
404
406 const SkRuntimeEffect* effect,
408 std::array<GrColor, 4> expected) {
409 std::array<GrColor, 4> actual;
410 SkImageInfo info = surface->imageInfo();
411 if (!read_pixels(surface, actual.data())) {
412 REPORT_FAILURE(r, "readPixels", SkString("readPixels failed"));
413 return;
414 }
415
416 if (actual != expected) {
417 REPORT_FAILURE(r, "Runtime effect didn't match expectations",
418 SkStringPrintf("\n"
419 "Expected: [ %08x %08x %08x %08x ]\n"
420 "Got : [ %08x %08x %08x %08x ]\n"
421 "SkSL:\n%s\n",
422 expected[0], expected[1], expected[2], expected[3],
423 actual[0], actual[1], actual[2], actual[3],
424 effect->source().c_str()));
425 }
426}
427
429 const GraphiteInfo* graphite,
430 SkISize size) {
433 if (graphite) {
434#if defined(SK_GRAPHITE)
436#endif
437 } else if (grContext) {
439 } else {
441 }
443 return surface;
444}
445
447public:
449 GrRecordingContext* grContext,
450 const GraphiteInfo* graphite,
451 SkISize size = {2, 2})
452 : fReporter(r), fGrContext(grContext), fGraphite(graphite), fSize(size) {
453 fSurface = make_surface(fGrContext, fGraphite, fSize);
454 }
455
456 void build(const char* src) {
459 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(SkString(src), options);
460 if (!effect) {
461 ERRORF(fReporter, "Effect didn't compile: %s", errorText.c_str());
462 return;
463 }
464 fBuilder.init(std::move(effect));
465 }
466
468 return fBuilder->uniform(name);
469 }
470
472 return fBuilder->child(name);
473 }
474
475 void test(std::array<GrColor, 4> expected, PreTestFn preTestCallback = nullptr) {
476 auto shader = fBuilder->makeShader();
477 if (!shader) {
478 ERRORF(fReporter, "Effect didn't produce a shader");
479 return;
480 }
481
482 SkCanvas* canvas = fSurface->getCanvas();
483
484 // We shouldn't need to clear the canvas, because we are about to paint over the whole thing
485 // with a `source` blend mode. However, there are a few devices where the background can
486 // leak through when we paint with MSAA on. (This seems to be a driver/hardware bug.)
487 // Graphite, at present, uses MSAA to do `drawPaint`. To avoid flakiness in this test on
488 // those devices, we explicitly clear the canvas here. (skia:13761)
489 canvas->clear(SK_ColorBLACK);
490
492 paint.setShader(std::move(shader));
493 paint.setBlendMode(SkBlendMode::kSrc);
494
495 paint_canvas(canvas, &paint, preTestCallback);
496
497 verify_2x2_surface_results(fReporter, fBuilder->effect(), fSurface.get(), expected);
498 }
499
500 std::string trace(const SkIPoint& traceCoord) {
501 sk_sp<SkShader> shader = fBuilder->makeShader();
502 if (!shader) {
503 ERRORF(fReporter, "Effect didn't produce a shader");
504 return {};
505 }
506
507 auto [debugShader, debugTrace] = SkRuntimeEffect::MakeTraced(std::move(shader), traceCoord);
508
509 SkCanvas* canvas = fSurface->getCanvas();
511 paint.setShader(std::move(debugShader));
512 paint.setBlendMode(SkBlendMode::kSrc);
513
514 paint_canvas(canvas, &paint, /*preTestCallback=*/nullptr);
515
517 debugTrace->dump(&wstream);
518 sk_sp<SkData> streamData = wstream.detachAsData();
519 return std::string(static_cast<const char*>(streamData->data()), streamData->size());
520 }
521
522 void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
523 this->test({expected, expected, expected, expected}, preTestCallback);
524 }
525
526private:
527 skiatest::Reporter* fReporter;
528 sk_sp<SkSurface> fSurface;
529 GrRecordingContext* fGrContext;
530 const GraphiteInfo* fGraphite;
531 SkISize fSize;
533};
534
536public:
538 : fReporter(r), fGrContext(grContext), fGraphite(graphite) {
539 fSurface = make_surface(fGrContext, fGraphite, /*size=*/{2, 2});
540 }
541
542 void build(const char* src) {
543 auto [effect, errorText] = SkRuntimeEffect::MakeForBlender(SkString(src));
544 if (!effect) {
545 ERRORF(fReporter, "Effect didn't compile: %s", errorText.c_str());
546 return;
547 }
548 fBuilder.init(std::move(effect));
549 }
550
552 return fSurface.get();
553 }
554
556 return fBuilder->uniform(name);
557 }
558
560 return fBuilder->child(name);
561 }
562
563 void test(std::array<GrColor, 4> expected, PreTestFn preTestCallback = nullptr) {
564 auto blender = fBuilder->makeBlender();
565 if (!blender) {
566 ERRORF(fReporter, "Effect didn't produce a blender");
567 return;
568 }
569
570 SkCanvas* canvas = fSurface->getCanvas();
572 paint.setBlender(std::move(blender));
573 paint.setColor(SK_ColorGRAY);
574
575 paint_canvas(canvas, &paint, preTestCallback);
576
577 verify_2x2_surface_results(fReporter, fBuilder->effect(), fSurface.get(), expected);
578 }
579
580 void test(GrColor expected, PreTestFn preTestCallback = nullptr) {
581 this->test({expected, expected, expected, expected}, preTestCallback);
582 }
583
584private:
585 skiatest::Reporter* fReporter;
586 sk_sp<SkSurface> fSurface;
587 GrRecordingContext* fGrContext;
588 const GraphiteInfo* fGraphite;
590};
591
592// Produces a shader which will paint these opaque colors in a 2x2 rectangle:
593// [ Red, Green ]
594// [ Blue, White ]
596 static constexpr SkColor colors[] = {SK_ColorWHITE, SK_ColorWHITE,
600 static constexpr SkScalar pos[] = { 0, .25f, .25f, .50f, .50f, .75, .75, 1 };
601 static_assert(std::size(colors) == std::size(pos), "size mismatch");
603}
604
606 GrRecordingContext* grContext,
607 const GraphiteInfo* graphite) {
608 TestEffect effect(r, grContext, graphite);
609 using float4 = std::array<float, 4>;
610 using int4 = std::array<int, 4>;
611
612 // Local coords
613 effect.build("half4 main(float2 p) { return half4(half2(p - 0.5), 0, 1); }");
614 effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
615
616 // Use of a simple uniform. (Draw twice with two values to ensure it's updated).
617 effect.build("uniform float4 gColor; half4 main(float2 p) { return half4(gColor); }");
618 effect.uniform("gColor") = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
619 effect.test(0xFFBF4000);
620 effect.uniform("gColor") = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
621 effect.test(0x7F0000FF); // Tests that we don't clamp to valid premul
622
623 // Same, with integer uniforms
624 effect.build("uniform int4 gColor; half4 main(float2 p) { return half4(gColor) / 255.0; }");
625 effect.uniform("gColor") = int4{ 0x00, 0x40, 0xBF, 0xFF };
626 effect.test(0xFFBF4000);
627 effect.uniform("gColor") = int4{ 0xFF, 0x00, 0x00, 0x7F };
628 effect.test(0x7F0000FF); // Tests that we don't clamp to valid premul
629
630 // Test sk_FragCoord (device coords). Rotate the canvas to be sure we're seeing device coords.
631 // Since the surface is 2x2, we should see (0,0), (1,0), (0,1), (1,1). Multiply by 0.498 to
632 // make sure we're not saturating unexpectedly.
633 effect.build(
634 "half4 main(float2 p) { return half4(0.498 * (half2(sk_FragCoord.xy) - 0.5), 0, 1); }");
635 effect.test({0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F},
636 [](SkCanvas* canvas, SkPaint*) { canvas->rotate(45.0f); });
637
638 // Runtime effects should use relaxed precision rules by default
639 effect.build("half4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
640 effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
641
642 // ... and support *returning* float4 (aka vec4), not just half4
643 effect.build("float4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
644 effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
645 effect.build("vec4 main(float2 p) { return float4(p - 0.5, 0, 1); }");
646 effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
647
648 // Mutating coords should work. (skbug.com/10918)
649 effect.build("vec4 main(vec2 p) { p -= 0.5; return vec4(p, 0, 1); }");
650 effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
651 effect.build("void moveCoords(inout vec2 p) { p -= 0.5; }"
652 "vec4 main(vec2 p) { moveCoords(p); return vec4(p, 0, 1); }");
653 effect.test({0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF});
654
655 //
656 // Sampling children
657 //
658
659 // Sampling a null shader should return transparent black
660 if (!graphite) {
661 // TODO: Graphite does not yet pass this test.
662 effect.build("uniform shader child;"
663 "half4 main(float2 p) { return child.eval(p); }");
664 effect.child("child") = nullptr;
665 effect.test(0x00000000,
666 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
667 }
668
669 // Sampling a null color-filter should return the passed-in color
670 effect.build("uniform colorFilter child;"
671 "half4 main(float2 p) { return child.eval(half4(1, 1, 0, 1)); }");
672 effect.child("child") = nullptr;
673 effect.test(0xFF00FFFF);
674
675 // Sampling a null blender should return blend_src_over(src, dest).
676 effect.build("uniform blender child;"
677 "half4 main(float2 p) {"
678 " float4 src = float4(p - 0.5, 0, 1) * 0.498;"
679 " return child.eval(src, half4(0, 0, 0, 1));"
680 "}");
681 effect.child("child") = nullptr;
682 effect.test({0xFF000000, 0xFF00007F, 0xFF007F00, 0xFF007F7F});
683
684 // Sampling a simple child at our coordinates
685 sk_sp<SkShader> rgbwShader = make_RGBW_shader();
686
687 effect.build("uniform shader child;"
688 "half4 main(float2 p) { return child.eval(p); }");
689 effect.child("child") = rgbwShader;
690 effect.test({0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF});
691
692 // Sampling with explicit coordinates (reflecting about the diagonal)
693 effect.build("uniform shader child;"
694 "half4 main(float2 p) { return child.eval(p.yx); }");
695 effect.child("child") = rgbwShader;
696 effect.test({0xFF0000FF, 0xFFFF0000, 0xFF00FF00, 0xFFFFFFFF});
697
698 // Bind an image shader, but don't use it - ensure that we don't assert or generate bad shaders.
699 // (skbug.com/12429)
700 effect.build("uniform shader child;"
701 "half4 main(float2 p) { return half4(0, 1, 0, 1); }");
702 effect.child("child") = rgbwShader;
703 effect.test(0xFF00FF00);
704
705 //
706 // Helper functions
707 //
708
709 // Test case for inlining in the pipeline-stage and fragment-shader passes (skbug.com/10526):
710 effect.build("float2 helper(float2 x) { return x + 1; }"
711 "half4 main(float2 p) { float2 v = helper(p); return half4(half2(v), 0, 1); }");
712 effect.test(0xFF00FFFF);
713}
714
715DEF_TEST(SkRuntimeEffectSimple, r) {
716 test_RuntimeEffect_Shaders(r, /*grContext=*/nullptr, /*graphite=*/nullptr);
717}
718
719#if defined(SK_GRAPHITE)
720DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffectSimple_Graphite, r, context,
722 std::unique_ptr<skgpu::graphite::Recorder> recorder = context->makeRecorder();
723 GraphiteInfo graphite = {context, recorder.get()};
724 test_RuntimeEffect_Shaders(r, /*grContext=*/nullptr, &graphite);
725}
726#endif
727
729 r,
730 ctxInfo,
732 test_RuntimeEffect_Shaders(r, ctxInfo.directContext(), /*graphite=*/nullptr);
733}
734
736 const SkRuntimeEffect* effect,
738 const SkPaint& paint) {
739 // We expect the draw to do something if-and-only-if expectSuccess is true:
740 const bool expectSuccess = surface->capabilities()->skslVersion() >= SkSL::Version::k300;
741
742 constexpr GrColor kGreen = 0xFF00FF00;
743 constexpr GrColor kRed = 0xFF0000FF;
744 const GrColor kExpected = expectSuccess ? kGreen : kRed;
745
746 surface->getCanvas()->clear(SK_ColorRED);
747 surface->getCanvas()->drawPaint(paint);
748 verify_2x2_surface_results(r, effect, surface, {kExpected, kExpected, kExpected, kExpected});
749}
750
752 // This test creates shaders and blenders that target `#version 300`. If a user validates an
753 // effect like this against a particular device, and later draws that effect to a device with
754 // insufficient capabilities -- we want to fail gracefully (drop the draw entirely).
755 // If the capabilities indicate that the effect is supported, we expect it to work.
756 //
757 // We test two different scenarios here:
758 // 1) An effect flagged as #version 300, but actually compatible with #version 100.
759 // 2) An effect flagged as #version 300, and using features not available in ES2.
760 //
761 // We expect both cases to fail cleanly on ES2-only devices -- nothing should be drawn, and
762 // there should be no asserts or driver shader-compilation errors.
763 //
764 // In all tests, we first clear the canvas to RED, then draw an effect that (if it renders)
765 // will fill the canvas with GREEN. We check that the final colors match our expectations,
766 // based on the device capabilities.
767
768 // Effect that would actually work on CPU/ES2, but should still fail on those devices:
769 {
771 #version 300
772 half4 main(float2 xy) { return half4(0, 1, 0, 1); }
773 )")).effect;
774 REPORTER_ASSERT(r, effect);
776 paint.setShader(effect->makeShader(/*uniforms=*/nullptr, /*children=*/{}));
777 REPORTER_ASSERT(r, paint.getShader());
779 }
780
781 // Effect that won't work on CPU/ES2 at all, and should fail gracefully on those devices.
782 // We choose to use bit-pun intrinsics because SkSL doesn't automatically inject an extension
783 // to enable them (like it does for derivatives). We pass a non-literal value so that SkSL's
784 // constant folding doesn't elide them entirely before the driver sees the shader.
785 {
787 #version 300
788 half4 main(float2 xy) {
789 half4 result = half4(0, 1, 0, 1);
790 result.g = intBitsToFloat(floatBitsToInt(result.g));
791 return result;
792 }
793 )")).effect;
794 REPORTER_ASSERT(r, effect);
796 paint.setShader(effect->makeShader(/*uniforms=*/nullptr, /*children=*/{}));
797 REPORTER_ASSERT(r, paint.getShader());
799 }
800
801 //
802 // As above, but with a blender
803 //
804
805 {
807 #version 300
808 half4 main(half4 src, half4 dst) { return half4(0, 1, 0, 1); }
809 )")).effect;
810 REPORTER_ASSERT(r, effect);
812 paint.setBlender(effect->makeBlender(/*uniforms=*/nullptr, /*children=*/{}));
813 REPORTER_ASSERT(r, paint.getBlender());
815 }
816
817 {
819 #version 300
820 half4 main(half4 src, half4 dst) {
821 half4 result = half4(0, 1, 0, 1);
822 result.g = intBitsToFloat(floatBitsToInt(result.g));
823 return result;
824 }
825 )")).effect;
826 REPORTER_ASSERT(r, effect);
828 paint.setBlender(effect->makeBlender(/*uniforms=*/nullptr, /*children=*/{}));
829 REPORTER_ASSERT(r, paint.getBlender());
831 }
832}
833
834DEF_TEST(SkRuntimeEffectObeysCapabilities_CPU, r) {
839}
840
841DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffectObeysCapabilities_GPU,
842 r,
843 ctxInfo,
847 SkSurfaces::RenderTarget(ctxInfo.directContext(), skgpu::Budgeted::kNo, info);
850}
851
852DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeColorFilterReturningInvalidAlpha_GPU,
853 r,
854 ctxInfo,
858 SkSurfaces::RenderTarget(ctxInfo.directContext(), skgpu::Budgeted::kNo, info);
860
862 half4 main(half4 color) { return half4(2); }
863 )")).effect;
864 REPORTER_ASSERT(r, effect);
866 paint.setColorFilter(effect->makeColorFilter(/*uniforms=*/nullptr));
867 REPORTER_ASSERT(r, paint.getColorFilter());
868 surface->getCanvas()->drawPaint(paint);
869}
870
871DEF_TEST(SkRuntimeColorFilterLimitedToES2, r) {
872 // Verify that SkSL requesting #version 300 can't be used to create a color-filter effect.
873 // This restriction could be removed if we can find a way to implement filterColor4f for these
874 // color filters.
875 {
877 #version 300
878 half4 main(half4 inColor) { return half4(1, 0, 0, 1); }
879 )")).effect;
880 REPORTER_ASSERT(r, !effect);
881 }
882
883 {
885 #version 300
886 uniform int loops;
887 half4 main(half4 inColor) {
888 half4 result = half4(1, 0, 0, 1);
889 for (int i = 0; i < loops; i++) {
890 result = result.argb;
891 }
892 return result;
893 }
894 )")).effect;
895 REPORTER_ASSERT(r, !effect);
896 }
897}
898
899DEF_TEST(SkRuntimeEffectTraceShader, r) {
900 for (int imageSize : {2, 80}) {
901 TestEffect effect(r, /*grContext=*/nullptr, /*graphite=*/nullptr,
902 SkISize{imageSize, imageSize});
903 effect.build(R"(
904 half4 main(float2 p) {
905 float2 val = p - 0.5;
906 return val.0y01;
907 }
908 )");
909 int center = imageSize / 2;
910 std::string dump = effect.trace({center, 1});
911 static constexpr char kSkRPSlotDump[] =
912R"($0 = p (float2 : slot 1/2, L0)
913$1 = p (float2 : slot 2/2, L0)
914$2 = [main].result (float4 : slot 1/4, L0)
915$3 = [main].result (float4 : slot 2/4, L0)
916$4 = [main].result (float4 : slot 3/4, L0)
917$5 = [main].result (float4 : slot 4/4, L0)
918$6 = val (float2 : slot 1/2, L0)
919$7 = val (float2 : slot 2/2, L0)
920F0 = half4 main(float2 p)
921)";
922 auto expectedTrace = SkSL::String::printf(R"(
923enter half4 main(float2 p)
924 p.x = %d.5
925 p.y = 1.5
926 scope +1
927 line 3
928 val.x = %d
929 val.y = 1
930 line 4
931 [main].result.x = 0
932 [main].result.y = 1
933 [main].result.z = 0
934 [main].result.w = 1
935 scope -1
936exit half4 main(float2 p)
939 r,
940 skstd::starts_with(dump, kSkRPSlotDump) && skstd::ends_with(dump, expectedTrace),
941 "Trace does not match expectation for %dx%d:\n%.*s\n",
942 imageSize, imageSize, (int)dump.size(), dump.data());
943 }
944}
945
946DEF_TEST(SkRuntimeEffectTracesAreUnoptimized, r) {
947 TestEffect effect(r, /*grContext=*/nullptr, /*graphite=*/nullptr);
948
949 effect.build(R"(
950 int globalUnreferencedVar = 7;
951 half inlinableFunction() {
952 return 1;
953 }
954 half4 main(float2 p) {
955 if (true) {
956 int localUnreferencedVar = 7;
957 }
958 return inlinableFunction().xxxx;
959 }
960 )");
961 std::string dump = effect.trace({1, 1});
962 static constexpr char kSkRPSlotDump[] =
963R"($0 = p (float2 : slot 1/2, L0)
964$1 = p (float2 : slot 2/2, L0)
965$2 = globalUnreferencedVar (int, L0)
966$3 = [main].result (float4 : slot 1/4, L0)
967$4 = [main].result (float4 : slot 2/4, L0)
968$5 = [main].result (float4 : slot 3/4, L0)
969$6 = [main].result (float4 : slot 4/4, L0)
970$7 = localUnreferencedVar (int, L0)
971$8 = [inlinableFunction].result (float, L0)
972F0 = half4 main(float2 p)
973F1 = half inlinableFunction()
974)";
975 static constexpr char kExpectedTrace[] = R"(
976globalUnreferencedVar = 7
977enter half4 main(float2 p)
978 p.x = 1.5
979 p.y = 1.5
980 scope +1
981 line 7
982 scope +1
983 line 8
984 localUnreferencedVar = 7
985 scope -1
986 line 10
987 enter half inlinableFunction()
988 scope +1
989 line 4
990 [inlinableFunction].result = 1
991 scope -1
992 exit half inlinableFunction()
993 [main].result.x = 1
994 [main].result.y = 1
995 [main].result.z = 1
996 [main].result.w = 1
997 scope -1
998exit half4 main(float2 p)
999)";
1001 r,
1002 skstd::starts_with(dump, kSkRPSlotDump) && skstd::ends_with(dump, kExpectedTrace),
1003 "Trace output does not match expectation:\n%.*s\n", (int)dump.size(), dump.data());
1004}
1005
1006DEF_TEST(SkRuntimeEffectTraceCodeThatCannotBeUnoptimized, r) {
1007 TestEffect effect(r, /*grContext=*/nullptr, /*graphite=*/nullptr);
1008
1009 effect.build(R"(
1010 half4 main(float2 p) {
1011 int variableThatGetsOptimizedAway = 7;
1012 if (true) {
1013 return half4(1);
1014 }
1015 // This (unreachable) path doesn't return a value.
1016 // Without optimization, SkSL thinks this code doesn't return a value on every path.
1017 }
1018 )");
1019 std::string dump = effect.trace({1, 1});
1020 static constexpr char kSkRPSlotDump[] =
1021R"($0 = p (float2 : slot 1/2, L0)
1022$1 = p (float2 : slot 2/2, L0)
1023$2 = [main].result (float4 : slot 1/4, L0)
1024$3 = [main].result (float4 : slot 2/4, L0)
1025$4 = [main].result (float4 : slot 3/4, L0)
1026$5 = [main].result (float4 : slot 4/4, L0)
1027F0 = half4 main(float2 p)
1028)";
1029 static constexpr char kExpectedTrace[] = R"(
1030enter half4 main(float2 p)
1031 p.x = 1.5
1032 p.y = 1.5
1033 scope +1
1034 scope +1
1035 line 5
1036 [main].result.x = 1
1037 [main].result.y = 1
1038 [main].result.z = 1
1039 [main].result.w = 1
1040 scope -1
1041 scope -1
1042exit half4 main(float2 p)
1043)";
1045 r,
1046 skstd::starts_with(dump, kSkRPSlotDump) && skstd::ends_with(dump, kExpectedTrace),
1047 "Trace output does not match expectation:\n%.*s\n", (int)dump.size(), dump.data());
1048}
1049
1051 GrRecordingContext* grContext,
1052 const GraphiteInfo* graphite) {
1053 TestBlend effect(r, grContext, graphite);
1054
1055 using float2 = std::array<float, 2>;
1056 using float4 = std::array<float, 4>;
1057 using int4 = std::array<int, 4>;
1058
1059 // Use of a simple uniform. (Draw twice with two values to ensure it's updated).
1060 effect.build("uniform float4 gColor; half4 main(half4 s, half4 d) { return half4(gColor); }");
1061 effect.uniform("gColor") = float4{ 0.0f, 0.25f, 0.75f, 1.0f };
1062 effect.test(0xFFBF4000);
1063 effect.uniform("gColor") = float4{ 1.0f, 0.0f, 0.0f, 0.498f };
1064 effect.test(0x7F0000FF); // We don't clamp here either
1065
1066 // Same, with integer uniforms
1067 effect.build("uniform int4 gColor;"
1068 "half4 main(half4 s, half4 d) { return half4(gColor) / 255.0; }");
1069 effect.uniform("gColor") = int4{ 0x00, 0x40, 0xBF, 0xFF };
1070 effect.test(0xFFBF4000);
1071 effect.uniform("gColor") = int4{ 0xFF, 0x00, 0x00, 0x7F };
1072 effect.test(0x7F0000FF); // We don't clamp here either
1073
1074 // Verify that mutating the source and destination colors is allowed
1075 effect.build("half4 main(half4 s, half4 d) { s += d; d += s; return half4(1); }");
1076 effect.test(0xFFFFFFFF);
1077
1078 // Verify that we can write out the source color (ignoring the dest color)
1079 // This is equivalent to the kSrc blend mode.
1080 effect.build("half4 main(half4 s, half4 d) { return s; }");
1081 effect.test(0xFF888888);
1082
1083 // Fill the destination with a variety of colors (using the RGBW shader)
1084 SkPaint rgbwPaint;
1085 rgbwPaint.setShader(make_RGBW_shader());
1087 effect.surface()->getCanvas()->drawPaint(rgbwPaint);
1088
1089 // Verify that we can read back the dest color exactly as-is (ignoring the source color)
1090 // This is equivalent to the kDst blend mode.
1091 effect.build("half4 main(half4 s, half4 d) { return d; }");
1092 effect.test({0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF});
1093
1094 // Verify that we can invert the destination color (including the alpha channel).
1095 // The expected outputs are the exact inverse of the previous test.
1096 effect.build("half4 main(half4 s, half4 d) { return half4(1) - d; }");
1097 effect.test({0x00FFFF00, 0x00FF00FF, 0x0000FFFF, 0x00000000});
1098
1099 // Verify that color values are clamped to 0 and 1.
1100 effect.build("half4 main(half4 s, half4 d) { return half4(-1); }");
1101 effect.test(0x00000000);
1102 effect.build("half4 main(half4 s, half4 d) { return half4(2); }");
1103 effect.test(0xFFFFFFFF);
1104
1105 //
1106 // Sampling children
1107 //
1108
1109 // Sampling a null shader should return transparent black.
1110 effect.build("uniform shader child;"
1111 "half4 main(half4 s, half4 d) { return child.eval(s.rg); }");
1112 effect.child("child") = nullptr;
1113 effect.test(0x00000000,
1114 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
1115
1116 effect.build("uniform colorFilter child;"
1117 "half4 main(half4 s, half4 d) { return child.eval(s); }");
1118 effect.child("child") = nullptr;
1119 effect.test(0xFF00FFFF,
1120 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
1121
1122 // Sampling a null blender should do a src-over blend. Draw 50% black over RGBW to verify this.
1123 effect.surface()->getCanvas()->drawPaint(rgbwPaint);
1124 effect.build("uniform blender child;"
1125 "half4 main(half4 s, half4 d) { return child.eval(s, d); }");
1126 effect.child("child") = nullptr;
1127 effect.test({0xFF000080, 0xFF008000, 0xFF800000, 0xFF808080},
1128 [](SkCanvas*, SkPaint* paint) { paint->setColor4f({0.0f, 0.0f, 0.0f, 0.497f}); });
1129
1130 // Sampling a shader at various coordinates
1131 effect.build("uniform shader child;"
1132 "uniform half2 pos;"
1133 "half4 main(half4 s, half4 d) { return child.eval(pos); }");
1134 effect.child("child") = make_RGBW_shader();
1135 effect.uniform("pos") = float2{0.5, 0.5};
1136 effect.test(0xFF0000FF);
1137
1138 effect.uniform("pos") = float2{1.5, 0.5};
1139 effect.test(0xFF00FF00);
1140
1141 effect.uniform("pos") = float2{0.5, 1.5};
1142 effect.test(0xFFFF0000);
1144 effect.uniform("pos") = float2{1.5, 1.5};
1145 effect.test(0xFFFFFFFF);
1146
1147 // Sampling a color filter
1148 effect.build("uniform colorFilter child;"
1149 "half4 main(half4 s, half4 d) { return child.eval(half4(1)); }");
1150 effect.child("child") = SkColorFilters::Blend(0xFF012345, SkBlendMode::kSrc);
1151 effect.test(0xFF452301);
1152
1153 // Sampling a built-in blender
1154 effect.surface()->getCanvas()->drawPaint(rgbwPaint);
1155 effect.build("uniform blender child;"
1156 "half4 main(half4 s, half4 d) { return child.eval(s, d); }");
1157 effect.child("child") = SkBlender::Mode(SkBlendMode::kPlus);
1158 effect.test({0xFF4523FF, 0xFF45FF01, 0xFFFF2301, 0xFFFFFFFF},
1159 [](SkCanvas*, SkPaint* paint) { paint->setColor(0xFF012345); });
1160
1161 // Sampling a runtime-effect blender
1162 effect.surface()->getCanvas()->drawPaint(rgbwPaint);
1163 effect.build("uniform blender child;"
1164 "half4 main(half4 s, half4 d) { return child.eval(s, d); }");
1165 effect.child("child") = SkBlenders::Arithmetic(0, 1, 1, 0, /*enforcePremul=*/false);
1166 effect.test({0xFF4523FF, 0xFF45FF01, 0xFFFF2301, 0xFFFFFFFF},
1167 [](SkCanvas*, SkPaint* paint) { paint->setColor(0xFF012345); });
1168}
1169
1170DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {
1171 test_RuntimeEffect_Blenders(r, /*grContext=*/nullptr, /*graphite=*/nullptr);
1173
1174DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffect_Blender_GPU,
1175 r,
1176 ctxInfo,
1178 test_RuntimeEffect_Blenders(r, ctxInfo.directContext(), /*graphite=*/nullptr);
1179}
1180
1181DEF_TEST(SkRuntimeShaderBuilderReuse, r) {
1182 const char* kSource = R"(
1183 uniform half x;
1184 half4 main(float2 p) { return half4(x); }
1185 )";
1186
1188 REPORTER_ASSERT(r, effect);
1190 // Test passes if this sequence doesn't assert. skbug.com/10667
1191 SkRuntimeShaderBuilder b(std::move(effect));
1192 b.uniform("x") = 0.0f;
1193 auto shader_0 = b.makeShader();
1194
1195 b.uniform("x") = 1.0f;
1196 auto shader_1 = b.makeShader();
1197}
1198
1199DEF_TEST(SkRuntimeBlendBuilderReuse, r) {
1200 const char* kSource = R"(
1201 uniform half x;
1202 half4 main(half4 s, half4 d) { return half4(x); }
1203 )";
1204
1206 REPORTER_ASSERT(r, effect);
1207
1208 // We should be able to construct multiple SkBlenders in a row without asserting.
1209 SkRuntimeBlendBuilder b(std::move(effect));
1210 for (float x = 0.0f; x <= 2.0f; x += 2.0f) {
1211 b.uniform("x") = x;
1212 sk_sp<SkBlender> blender = b.makeBlender();
1213 }
1214}
1215
1216DEF_TEST(SkRuntimeShaderBuilderSetUniforms, r) {
1217 const char* kSource = R"(
1218 uniform half x;
1219 uniform vec2 offset;
1220 half4 main(float2 p) { return half4(x); }
1221 )";
1222
1224 REPORTER_ASSERT(r, effect);
1225
1226 SkRuntimeShaderBuilder b(std::move(effect));
1227
1228 // Test passes if this sequence doesn't assert.
1229 float x = 1.0f;
1230 REPORTER_ASSERT(r, b.uniform("x").set(&x, 1));
1231
1232 // add extra value to ensure that set doesn't try to use sizeof(array)
1233 float origin[] = { 2.0f, 3.0f, 4.0f };
1234 REPORTER_ASSERT(r, b.uniform("offset").set<float>(origin, 2));
1235
1236#ifndef SK_DEBUG
1237 REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 1));
1238 REPORTER_ASSERT(r, !b.uniform("offset").set<float>(origin, 3));
1239#endif
1240
1241 auto shader = b.makeShader();
1242}
1243
1244DEF_TEST(SkRuntimeEffectThreaded, r) {
1245 // This tests that we can safely use SkRuntimeEffect::MakeForShader from more than one thread,
1246 // and also that programs don't refer to shared structures owned by the compiler.
1247 // skbug.com/10589
1248 static constexpr char kSource[] = "half4 main(float2 p) { return sk_FragCoord.xyxy; }";
1249
1250 std::thread threads[16];
1251 for (auto& thread : threads) {
1252 thread = std::thread([r]() {
1255 auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(kSource), options);
1256 REPORTER_ASSERT(r, effect);
1257 });
1258 }
1259
1260 for (auto& thread : threads) {
1261 thread.join();
1262 }
1263}
1264
1265DEF_TEST(SkRuntimeEffectAllowsPrivateAccess, r) {
1266 SkRuntimeEffect::Options defaultOptions;
1267 SkRuntimeEffect::Options optionsWithAccess;
1268 SkRuntimeEffectPriv::AllowPrivateAccess(&optionsWithAccess);
1269
1270 // Confirm that shaders can only access $private_functions when private access is allowed.
1271 {
1272 static constexpr char kShader[] =
1273 "half4 main(float2 p) { return $hsl_to_rgb(p.xxx, p.y); }";
1275 SkRuntimeEffect::MakeForShader(SkString(kShader), defaultOptions);
1276 REPORTER_ASSERT(r, !normal.effect);
1277 SkRuntimeEffect::Result privileged =
1278 SkRuntimeEffect::MakeForShader(SkString(kShader), optionsWithAccess);
1279 REPORTER_ASSERT(r, privileged.effect, "%s", privileged.errorText.c_str());
1281
1282 // Confirm that color filters can only access $private_functions when private access is allowed.
1283 {
1284 static constexpr char kColorFilter[] =
1285 "half4 main(half4 c) { return $hsl_to_rgb(c.rgb, c.a); }";
1288 REPORTER_ASSERT(r, !normal.effect);
1289 SkRuntimeEffect::Result privileged =
1291 REPORTER_ASSERT(r, privileged.effect, "%s", privileged.errorText.c_str());
1292 }
1293
1294 // Confirm that blenders can only access $private_functions when private access is allowed.
1295 {
1296 static constexpr char kBlender[] =
1297 "half4 main(half4 s, half4 d) { return $hsl_to_rgb(s.rgb, d.a); }";
1299 SkRuntimeEffect::MakeForBlender(SkString(kBlender), defaultOptions);
1300 REPORTER_ASSERT(r, !normal.effect);
1301 SkRuntimeEffect::Result privileged =
1302 SkRuntimeEffect::MakeForBlender(SkString(kBlender), optionsWithAccess);
1303 REPORTER_ASSERT(r, privileged.effect, "%s", privileged.errorText.c_str());
1304 }
1305}
1306
1307DEF_TEST(SkRuntimeColorFilterSingleColor, r) {
1308 // Test runtime colorfilters support filterColor4f().
1309 auto [effect, err] =
1310 SkRuntimeEffect::MakeForColorFilter(SkString{"half4 main(half4 c) { return c*c; }"});
1311 REPORTER_ASSERT(r, effect);
1312 REPORTER_ASSERT(r, err.isEmpty());
1313
1315 REPORTER_ASSERT(r, cf);
1316
1317 SkColor4f c = cf->filterColor4f({0.25, 0.5, 0.75, 1.0},
1319 REPORTER_ASSERT(r, c.fR == 0.0625f);
1320 REPORTER_ASSERT(r, c.fG == 0.25f);
1321 REPORTER_ASSERT(r, c.fB == 0.5625f);
1322 REPORTER_ASSERT(r, c.fA == 1.0f);
1323}
1326 // Test that two different runtime effects can reuse struct names in a single paint operation
1327 auto [childEffect, err] = SkRuntimeEffect::MakeForShader(SkString(
1328 "uniform shader paint;"
1329 "struct S { half4 rgba; };"
1330 "void process(inout S s) { s.rgba.rgb *= 0.5; }"
1331 "half4 main(float2 p) { S s; s.rgba = paint.eval(p); process(s); return s.rgba; }"
1332 ));
1333 REPORTER_ASSERT(r, childEffect, "%s\n", err.c_str());
1334 sk_sp<SkShader> sourceColor = SkShaders::Color({0.99608f, 0.50196f, 0.0f, 1.0f}, nullptr);
1335 const GrColor kExpected = 0xFF00407F;
1336 sk_sp<SkShader> child = childEffect->makeShader(/*uniforms=*/nullptr,
1337 &sourceColor,
1338 /*childCount=*/1);
1339
1340 TestEffect effect(r, /*grContext=*/nullptr, /*graphite=*/nullptr);
1341 effect.build(
1342 "uniform shader child;"
1343 "struct S { float2 coord; };"
1344 "void process(inout S s) { s.coord = s.coord.yx; }"
1345 "half4 main(float2 p) { S s; s.coord = p; process(s); return child.eval(s.coord); "
1346 "}");
1347 effect.child("child") = child;
1348 effect.test(kExpected, [](SkCanvas*, SkPaint* paint) {});
1349}
1350
1351DEF_TEST(SkRuntimeStructNameReuse, r) {
1353}
1354
1355DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeStructNameReuse_GPU,
1356 r,
1357 ctxInfo,
1359 test_RuntimeEffectStructNameReuse(r, ctxInfo.directContext());
1360}
1361
1362DEF_TEST(SkRuntimeColorFilterFlags, r) {
1363 auto expectAlphaUnchanged = [&](const char* shader) {
1364 auto [effect, err] = SkRuntimeEffect::MakeForColorFilter(SkString{shader});
1365 REPORTER_ASSERT(r, effect && err.isEmpty(), "%s", shader);
1367 REPORTER_ASSERT(r, filter && filter->isAlphaUnchanged(), "%s", shader);
1368 };
1369
1370 auto expectAlphaChanged = [&](const char* shader) {
1371 auto [effect, err] = SkRuntimeEffect::MakeForColorFilter(SkString{shader});
1372 REPORTER_ASSERT(r, effect && err.isEmpty(), "%s", shader);
1374 REPORTER_ASSERT(r, filter && !filter->isAlphaUnchanged(), "%s", shader);
1375 };
1376
1377 // We expect these patterns to be detected as alpha-unchanged.
1378 expectAlphaUnchanged("half4 main(half4 color) { return color; }");
1379 expectAlphaUnchanged("half4 main(half4 color) { return color.aaaa; }");
1380 expectAlphaUnchanged("half4 main(half4 color) { return color.bgra; }");
1381 expectAlphaUnchanged("half4 main(half4 color) { return color.rraa; }");
1382 expectAlphaUnchanged("half4 main(half4 color) { return color.010a; }");
1383 expectAlphaUnchanged("half4 main(half4 color) { return half4(0, 0, 0, color.a); }");
1384 expectAlphaUnchanged("half4 main(half4 color) { return half4(half2(1), color.ba); }");
1385 expectAlphaUnchanged("half4 main(half4 color) { return half4(half2(1), half2(color.a)); }");
1386 expectAlphaUnchanged("half4 main(half4 color) { return half4(color.a); }");
1387 expectAlphaUnchanged("half4 main(half4 color) { return half4(float4(color.baba)); }");
1388 expectAlphaUnchanged("half4 main(half4 color) { return color.r != color.g ? color :"
1389 " color.000a; }");
1390 expectAlphaUnchanged("half4 main(half4 color) { return color.a == color.r ? color.rrra : "
1391 "color.g == color.b ? color.ggga : "
1392 " color.bbba; }");
1393 // Modifying the input color invalidates the check.
1394 expectAlphaChanged("half4 main(half4 color) { color.a = 0; return color; }");
1395
1396 // These swizzles don't end in alpha.
1397 expectAlphaChanged("half4 main(half4 color) { return color.argb; }");
1398 expectAlphaChanged("half4 main(half4 color) { return color.rrrr; }");
1399
1400 // This compound constructor doesn't end in alpha.
1401 expectAlphaChanged("half4 main(half4 color) { return half4(1, 1, 1, color.r); }");
1402
1403 // This splat constructor doesn't use alpha.
1404 expectAlphaChanged("half4 main(half4 color) { return half4(color.r); }");
1405
1406 // These ternaries don't return alpha on both sides
1407 expectAlphaChanged("half4 main(half4 color) { return color.a > 0 ? half4(0) : color; }");
1408 expectAlphaChanged("half4 main(half4 color) { return color.g < 1 ? color.bgra : color.abgr; }");
1409 expectAlphaChanged("half4 main(half4 color) { return color.b > 0.5 ? half4(0) : half4(1); }");
1410
1411 // Performing arithmetic on the input causes it to report as "alpha changed" even if the
1412 // arithmetic is a no-op; we aren't smart enough to see through it.
1413 expectAlphaChanged("half4 main(half4 color) { return color + half4(1,1,1,0); }");
1414 expectAlphaChanged("half4 main(half4 color) { return color + half4(0,0,0,4); }");
1415
1416 // All exit paths are checked.
1417 expectAlphaChanged("half4 main(half4 color) { "
1418 " if (color.r > 0.5) { return color; }"
1419 " return half4(0);"
1420 "}");
1421 expectAlphaChanged("half4 main(half4 color) { "
1422 " if (color.r > 0.5) { return half4(0); }"
1423 " return color;"
1424 "}");
1425}
1426
1427DEF_TEST(SkRuntimeShaderSampleCoords, r) {
1428 // This test verifies that we detect calls to sample where the coords are the same as those
1429 // passed to main. In those cases, it's safe to turn the "explicit" sampling into "passthrough"
1430 // sampling. This optimization is implemented very conservatively.
1431 //
1432 // It also checks that we correctly set the "referencesSampleCoords" bit on the runtime effect
1433 // FP, depending on how the coords parameter to main is used.
1434
1435 auto test = [&](const char* src, bool expectExplicit, bool expectReferencesSampleCoords) {
1436 auto [effect, err] =
1437 SkRuntimeEffect::MakeForShader(SkStringPrintf("uniform shader child; %s", src));
1438 REPORTER_ASSERT(r, effect);
1439
1440 auto child = GrFragmentProcessor::MakeColor({ 1, 1, 1, 1 });
1441 auto fp = GrSkSLFP::Make(effect.get(), "test_fp", /*inputFP=*/nullptr,
1442 GrSkSLFP::OptFlags::kNone, "child", std::move(child));
1443 REPORTER_ASSERT(r, fp);
1444
1445 REPORTER_ASSERT(r, fp->childProcessor(0)->sampleUsage().isExplicit() == expectExplicit);
1446 REPORTER_ASSERT(r, fp->usesSampleCoords() == expectReferencesSampleCoords);
1447 };
1448
1449 // Cases where our optimization is valid, and works:
1450
1451 // Direct use of passed-in coords. Here, the only use of sample coords is for a sample call
1452 // converted to passthrough, so referenceSampleCoords is *false*, despite appearing in main.
1453 test("half4 main(float2 xy) { return child.eval(xy); }", false, false);
1454 // Sample with passed-in coords, read (but don't write) sample coords elsewhere
1455 test("half4 main(float2 xy) { return child.eval(xy) + sin(xy.x); }", false, true);
1456
1457 // Cases where our optimization is not valid, and does not happen:
1458
1459 // Sampling with values completely unrelated to passed-in coords
1460 test("half4 main(float2 xy) { return child.eval(float2(0, 0)); }", true, false);
1461 // Use of expression involving passed in coords
1462 test("half4 main(float2 xy) { return child.eval(xy * 0.5); }", true, true);
1463 // Use of coords after modification
1464 test("half4 main(float2 xy) { xy *= 2; return child.eval(xy); }", true, true);
1465 // Use of coords after modification via out-param call
1466 test("void adjust(inout float2 xy) { xy *= 2; }"
1467 "half4 main(float2 xy) { adjust(xy); return child.eval(xy); }", true, true);
1468
1469 // There should (must) not be any false-positive cases. There are false-negatives.
1470 // In all of these cases, our optimization would be valid, but does not happen:
1471
1472 // Direct use of passed-in coords, modified after use
1473 test("half4 main(float2 xy) { half4 c = child.eval(xy); xy *= 2; return c; }", true, true);
1474 // Passed-in coords copied to a temp variable
1475 test("half4 main(float2 xy) { float2 p = xy; return child.eval(p); }", true, true);
1476 // Use of coords passed to helper function
1477 test("half4 helper(float2 xy) { return child.eval(xy); }"
1478 "half4 main(float2 xy) { return helper(xy); }", true, true);
1479}
1480
1481DEF_TEST(SkRuntimeShaderIsOpaque, r) {
1482 // This test verifies that we detect certain simple patterns in runtime shaders, and can deduce
1483 // (via code in SkSL::Analysis::ReturnsOpaqueColor) that the resulting shader is always opaque.
1484 // That logic is conservative, and the tests below reflect this.
1485
1486 auto test = [&](const char* body, bool expectOpaque) {
1487 auto [effect, err] = SkRuntimeEffect::MakeForShader(SkStringPrintf(R"(
1488 uniform shader cOnes;
1489 uniform shader cZeros;
1490 uniform float4 uOnes;
1491 uniform float4 uZeros;
1492 half4 main(float2 xy) {
1493 %s
1494 })", body));
1495 REPORTER_ASSERT(r, effect);
1496
1497 auto cOnes = SkShaders::Color(SK_ColorWHITE);
1499 SkASSERT(cOnes->isOpaque());
1500 SkASSERT(!cZeros->isOpaque());
1501
1503 builder.child("cOnes") = std::move(cOnes);
1504 builder.child("cZeros") = std::move(cZeros);
1505 builder.uniform("uOnes") = SkColors::kWhite;
1506 builder.uniform("uZeros") = SkColors::kTransparent;
1507
1508 auto shader = builder.makeShader();
1509 REPORTER_ASSERT(r, shader->isOpaque() == expectOpaque);
1510 };
1511
1512 // Cases where our optimization is valid, and works:
1513
1514 // Returning opaque literals
1515 test("return half4(1);", true);
1516 test("return half4(0, 1, 0, 1);", true);
1517 test("return half4(0, 0, 0, 1);", true);
1518
1519 // Simple expressions involving uniforms
1520 test("return uZeros.rgb1;", true);
1521 test("return uZeros.bgra.rgb1;", true);
1522 test("return half4(uZeros.rgb, 1);", true);
1523
1524 // Simple expressions involving child.eval
1525 test("return cZeros.eval(xy).rgb1;", true);
1526 test("return cZeros.eval(xy).bgra.rgb1;", true);
1527 test("return half4(cZeros.eval(xy).rgb, 1);", true);
1528
1529 // Multiple returns
1530 test("if (xy.x < 100) { return uZeros.rgb1; } else { return cZeros.eval(xy).rgb1; }", true);
1531
1532 // More expression cases:
1533 test("return (cZeros.eval(xy) * uZeros).rgb1;", true);
1534 test("return half4(1, 1, 1, 0.5 + 0.5);", true);
1535
1536 // Constant variable propagation
1537 test("const half4 kWhite = half4(1); return kWhite;", true);
1538
1539 // Cases where our optimization is not valid, and does not happen:
1540
1541 // Returning non-opaque literals
1542 test("return half4(0);", false);
1543 test("return half4(1, 1, 1, 0);", false);
1544
1545 // Returning non-opaque uniforms or children
1546 test("return uZeros;", false);
1547 test("return cZeros.eval(xy);", false);
1548
1549 // Multiple returns
1550 test("if (xy.x < 100) { return uZeros; } else { return cZeros.eval(xy).rgb1; }", false);
1551 test("if (xy.x < 100) { return uZeros.rgb1; } else { return cZeros.eval(xy); }", false);
1552
1553 // There should (must) not be any false-positive cases. There are false-negatives.
1554 // In these cases, our optimization would be valid, but does not happen:
1555
1556 // More complex expressions that can't be simplified
1557 test("return xy.x < 100 ? uZeros.rgb1 : cZeros.eval(xy).rgb1;", false);
1558
1559 // Finally, there are cases that are conditional on the uniforms and children. These *could*
1560 // determine dynamically if the uniform and/or child being referenced is opaque, and use that
1561 // information. Today, we don't do this, so we pessimistically assume they're transparent:
1562 test("return uOnes;", false);
1563 test("return cOnes.eval(xy);", false);
1564}
1565
1566DEF_GANESH_TEST_FOR_ALL_CONTEXTS(GrSkSLFP_Specialized, r, ctxInfo, CtsEnforcement::kApiLevel_T) {
1567 struct FpAndKey {
1568 std::unique_ptr<GrFragmentProcessor> fp;
1570 };
1571
1572 // Constant color, but with an 'specialize' option that decides if the color is inserted in the
1573 // SkSL as a literal, or left as a uniform
1574 auto make_color_fp = [&](SkPMColor4f color, bool specialize) {
1576 "uniform half4 color;"
1577 "half4 main(float2 xy) { return color; }"
1578 );
1579 FpAndKey result;
1580 result.fp = GrSkSLFP::Make(effect, "color_fp", /*inputFP=*/nullptr,
1582 "color", GrSkSLFP::SpecializeIf(specialize, color));
1584 result.fp->addToKey(*ctxInfo.directContext()->priv().caps()->shaderCaps(), &builder);
1585 builder.flush();
1586 return result;
1587 };
1588
1589 FpAndKey uRed = make_color_fp({1, 0, 0, 1}, false),
1590 uGreen = make_color_fp({0, 1, 0, 1}, false),
1591 sRed = make_color_fp({1, 0, 0, 1}, true),
1592 sGreen = make_color_fp({0, 1, 0, 1}, true);
1593
1594 // uRed and uGreen should have the same key - they just have different uniforms
1595 SkASSERT(uRed.key == uGreen.key);
1596 // sRed and sGreen should have keys that are different from the uniform case, and each other
1597 SkASSERT(sRed.key != uRed.key);
1598 SkASSERT(sGreen.key != uRed.key);
1599 SkASSERT(sRed.key != sGreen.key);
1600}
1601
1602DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrSkSLFP_UniformArray,
1603 r,
1604 ctxInfo,
1606 // Make a fill-context to draw into.
1607 GrDirectContext* directContext = ctxInfo.directContext();
1609 std::unique_ptr<skgpu::ganesh::SurfaceFillContext> testCtx =
1610 directContext->priv().makeSFC(info, /*label=*/{}, SkBackingFit::kExact);
1611
1612 // Make an effect that takes a uniform array as input.
1613 static constexpr std::array<float, 4> kRed {1.0f, 0.0f, 0.0f, 1.0f};
1614 static constexpr std::array<float, 4> kGreen{0.0f, 1.0f, 0.0f, 1.0f};
1615 static constexpr std::array<float, 4> kBlue {0.0f, 0.0f, 1.0f, 1.0f};
1616 static constexpr std::array<float, 4> kGray {0.499f, 0.499f, 0.499f, 1.0f};
1617
1618 for (const auto& colorArray : {kRed, kGreen, kBlue, kGray}) {
1619 // Compile our runtime effect.
1621 "uniform half color[4];"
1622 "half4 main(float2 xy) { return half4(color[0], color[1], color[2], color[3]); }"
1623 );
1624 // Render our shader into the fill-context with our various input colors.
1625 testCtx->fillWithFP(GrSkSLFP::Make(effect, "test_fp", /*inputFP=*/nullptr,
1627 "color", SkSpan(colorArray)));
1628 // Read our color back and ensure it matches.
1629 GrColor actual;
1630 GrPixmap pixmap(info, &actual, sizeof(GrColor));
1631 if (!testCtx->readPixels(directContext, pixmap, /*srcPt=*/{0, 0})) {
1632 REPORT_FAILURE(r, "readPixels", SkString("readPixels failed"));
1633 break;
1634 }
1635 if (actual != GrColorPackRGBA(255 * colorArray[0], 255 * colorArray[1],
1636 255 * colorArray[2], 255 * colorArray[3])) {
1637 REPORT_FAILURE(r, "Uniform array didn't match expectations",
1638 SkStringPrintf("\n"
1639 "Expected: [ %g %g %g %g ]\n"
1640 "Got : [ %08x ]\n",
1641 colorArray[0], colorArray[1],
1642 colorArray[2], colorArray[3],
1643 actual));
1644 break;
1645 }
1646 }
1647}
static void test_invalid(skiatest::Reporter *r, const char path[])
Definition: CodecTest.cpp:704
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define test(name)
static GrColor GrColorPackRGBA(unsigned r, unsigned g, unsigned b, unsigned a)
Definition: GrColor.h:46
uint32_t GrColor
Definition: GrColor.h:25
SkPoint pos
static const uint64_t kGreen
static const uint64_t kBlue
static const uint64_t kRed
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kPlus
r = min(s + d, 1)
SkColorSpace * sk_srgb_singleton()
@ 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
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
constexpr SkColor SK_ColorGRAY
Definition: SkColor.h:113
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
SkRuntimeEffect * SkMakeRuntimeEffect(SkRuntimeEffect::Result(*make)(SkString, const SkRuntimeEffect::Options &), const char *sksl, SkRuntimeEffect::Options options=SkRuntimeEffect::Options{})
static void test_RuntimeEffect_Blenders(skiatest::Reporter *r, GrRecordingContext *grContext, const GraphiteInfo *graphite)
void test_invalid_effect(skiatest::Reporter *r, const char *src, const char *expected)
static bool read_pixels(SkSurface *surface, GrColor *pixels)
static void verify_2x2_surface_results(skiatest::Reporter *r, const SkRuntimeEffect *effect, SkSurface *surface, std::array< GrColor, 4 > expected)
std::function< void(SkCanvas *, SkPaint *)> PreTestFn
DEF_TEST(SkRuntimeEffectInvalid_NoInVariables, r)
static void test_RuntimeEffectObeysCapabilities(skiatest::Reporter *r, SkSurface *surface)
#define EMPTY_MAIN
static sk_sp< SkShader > make_RGBW_shader()
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffectSimple_GPU, r, ctxInfo, CtsEnforcement::kApiLevel_T)
static sk_sp< SkSurface > make_surface(GrRecordingContext *grContext, const GraphiteInfo *graphite, SkISize size)
DEF_GANESH_TEST_FOR_ALL_CONTEXTS(GrSkSLFP_Specialized, r, ctxInfo, CtsEnforcement::kApiLevel_T)
void paint_canvas(SkCanvas *canvas, SkPaint *paint, const PreTestFn &preTestCallback)
static void verify_draw_obeys_capabilities(skiatest::Reporter *r, const SkRuntimeEffect *effect, SkSurface *surface, const SkPaint &paint)
static void test_RuntimeEffectStructNameReuse(skiatest::Reporter *r, GrRecordingContext *rContext)
static void test_RuntimeEffect_Shaders(skiatest::Reporter *r, GrRecordingContext *grContext, const GraphiteInfo *graphite)
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv)
Definition: SkYUVMath.cpp:629
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(name, reporter, graphite_context, ctsEnforcement)
Definition: Test.h:377
#define ERRORF(r,...)
Definition: Test.h:293
#define REPORT_FAILURE(reporter, cond, message)
Definition: Test.h:90
static SkScalar center(float pos0, float pos1)
GrDirectContextPriv priv()
static std::unique_ptr< GrFragmentProcessor > MakeColor(SkPMColor4f color)
std::unique_ptr< skgpu::ganesh::SurfaceFillContext > makeSFC(GrImageInfo, std::string_view label, SkBackingFit=SkBackingFit::kExact, int sampleCount=1, skgpu::Mipmapped=skgpu::Mipmapped::kNo, skgpu::Protected=skgpu::Protected::kNo, GrSurfaceOrigin=kTopLeft_GrSurfaceOrigin, skgpu::Budgeted=skgpu::Budgeted::kYes)
static GrSpecializedUniform< T > SpecializeIf(bool condition, const T &value)
Definition: GrSkSLFP.h:81
static std::unique_ptr< GrSkSLFP > Make(const SkRuntimeEffect *effect, const char *name, std::unique_ptr< GrFragmentProcessor > inputFP, OptFlags optFlags, Args &&... args)
Definition: GrSkSLFP.h:159
static sk_sp< SkBlender > Mode(SkBlendMode mode)
static sk_sp< SkBlender > Arithmetic(float k1, float k2, float k3, float k4, bool enforcePremul)
Definition: SkBlenders.cpp:20
void restore()
Definition: SkCanvas.cpp:461
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
void clear(SkColor color)
Definition: SkCanvas.h:1199
void rotate(SkScalar degrees)
Definition: SkCanvas.cpp:1300
int save()
Definition: SkCanvas.cpp:447
static sk_sp< const SkCapabilities > RasterBackend()
bool isAlphaUnchanged() const
SkColor4f filterColor4f(const SkColor4f &srcColor, SkColorSpace *srcCS, SkColorSpace *dstCS) const
static sk_sp< SkColorFilter > Blend(const SkColor4f &c, sk_sp< SkColorSpace >, SkBlendMode mode)
const void * data() const
Definition: SkData.h:37
static sk_sp< SkData > MakeEmpty()
Definition: SkData.cpp:94
size_t size() const
Definition: SkData.h:30
sk_sp< SkData > detachAsData()
Definition: SkStream.cpp:707
static sk_sp< SkShader > MakeSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, SkScalar startAngle, SkScalar endAngle, uint32_t flags, const SkMatrix *localMatrix)
void setShader(sk_sp< SkShader > shader)
void setBlendMode(SkBlendMode mode)
Definition: SkPaint.cpp:151
sk_sp< SkBlender > makeBlender() const
BuilderUniform uniform(std::string_view name)
BuilderChild child(std::string_view name)
const SkRuntimeEffect * effect() const
static void AllowPrivateAccess(SkRuntimeEffect::Options *options)
static bool CanDraw(const SkCapabilities *, const SkSL::Program *)
static SkRuntimeEffect::Options ES3Options()
static Result MakeForColorFilter(SkString sksl, const Options &)
static Result MakeForBlender(SkString sksl, const Options &)
static Result MakeForShader(SkString sksl, const Options &)
static TracedShader MakeTraced(sk_sp< SkShader > shader, const SkIPoint &traceCoord)
sk_sp< SkColorFilter > makeColorFilter(sk_sp< const SkData > uniforms) const
const std::string & source() const
sk_sp< SkShader > makeShader(const SkMatrix *localMatrix=nullptr) const
constexpr size_t size() const
Definition: SkSpan_impl.h:95
const char * c_str() const
Definition: SkString.h:133
SkCanvas * getCanvas()
Definition: SkSurface.cpp:82
T * init(Args &&... args)
Definition: SkTLazy.h:45
void build(const char *src)
TestBlend(skiatest::Reporter *r, GrRecordingContext *grContext, const GraphiteInfo *graphite)
void test(GrColor expected, PreTestFn preTestCallback=nullptr)
SkRuntimeBlendBuilder::BuilderChild child(const char *name)
SkRuntimeBlendBuilder::BuilderUniform uniform(const char *name)
SkSurface * surface()
void test(std::array< GrColor, 4 > expected, PreTestFn preTestCallback=nullptr)
void test(std::array< GrColor, 4 > expected, PreTestFn preTestCallback=nullptr)
void build(const char *src)
SkRuntimeShaderBuilder::BuilderUniform uniform(const char *name)
SkRuntimeShaderBuilder::BuilderChild child(const char *name)
TestEffect(skiatest::Reporter *r, GrRecordingContext *grContext, const GraphiteInfo *graphite, SkISize size={2, 2})
void test(GrColor expected, PreTestFn preTestCallback=nullptr)
std::string trace(const SkIPoint &traceCoord)
T * get() const
Definition: SkRefCnt.h:303
const Paint & paint
Definition: color_source.cc:38
DlColor color
VkSurfaceKHR surface
Definition: main.cc:49
float SkScalar
Definition: extension.cpp:12
static bool b
FlutterSemanticsFlag flags
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
double x
constexpr SkColor4f kWhite
Definition: SkColor.h:439
constexpr SkColor4f kTransparent
Definition: SkColor.h:434
constexpr SkColor4f kGray
Definition: SkColor.h:437
PODArray< SkColor > colors
Definition: SkRecords.h:276
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
SK_API sk_sp< SkShader > Color(SkColor)
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
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)
const uint32_t fp
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
constexpr bool starts_with(std::string_view str, std::string_view prefix)
Definition: SkStringView.h:17
constexpr bool ends_with(std::string_view str, std::string_view suffix)
Definition: SkStringView.h:28
Vec< 4, int32_t > int4
Definition: SkVx.h:1159
dest
Definition: zip.py:79
Definition: SkSize.h:16
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
sk_sp< SkRuntimeEffect > effect
Definition: SkVx.h:83