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