1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "gm/gm.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkSize.h"
15 #include "include/core/SkString.h"
16 #include "include/core/SkSurface.h"
17 #include "include/effects/SkGradientShader.h"
18 #include "include/effects/SkImageFilters.h"
19 #include "include/effects/SkRuntimeEffect.h"
20 #include "include/utils/SkRandom.h"
21 #include "tools/Resources.h"
22 #include "tools/ToolUtils.h"
23 
24 static constexpr int kBoxSize     = 100;
25 static constexpr int kPadding     = 5;
26 static constexpr int kLabelHeight = 15;
27 
next_column(SkCanvas * canvas)28 static void next_column(SkCanvas* canvas) {
29     canvas->translate(kBoxSize + kPadding, 0);
30 }
31 
next_row(SkCanvas * canvas)32 static void next_row(SkCanvas* canvas) {
33     canvas->restore();
34     canvas->translate(0, kBoxSize + kPadding + kLabelHeight);
35     canvas->save();
36 }
37 
columns_to_width(int columns)38 static constexpr int columns_to_width(int columns) {
39     return (kPadding + kBoxSize) * columns + kPadding;
40 }
41 
rows_to_height(int rows)42 static constexpr int rows_to_height(int rows) {
43     return (kPadding + kLabelHeight + kBoxSize) * rows + kPadding;
44 }
45 
draw_label(SkCanvas * canvas,const char * label)46 static void draw_label(SkCanvas* canvas, const char* label) {
47     SkFont font(ToolUtils::create_portable_typeface());
48     SkPaint p(SkColors::kBlack);
49     SkRect bounds;
50     font.measureText(label, strlen(label), SkTextEncoding::kUTF8, &bounds);
51 
52     canvas->drawSimpleText(label, strlen(label), SkTextEncoding::kUTF8,
53                            (kBoxSize - bounds.width()) * 0.5f,
54                            (kLabelHeight + bounds.height()) * 0.5f, font, p);
55     canvas->translate(0, kLabelHeight);
56 }
57 
draw_shader(SkCanvas * canvas,sk_sp<SkShader> shader)58 static SkBitmap draw_shader(SkCanvas* canvas, sk_sp<SkShader> shader) {
59     SkPaint paint;
60     paint.setShader(std::move(shader));
61 
62     SkImageInfo info = SkImageInfo::MakeN32Premul({kBoxSize, kBoxSize});
63     auto surface = canvas->makeSurface(info);
64     if (!surface) {
65         surface = SkSurface::MakeRaster(info);
66     }
67 
68     surface->getCanvas()->clear(SK_ColorWHITE);
69     surface->getCanvas()->scale(kBoxSize, kBoxSize);
70     surface->getCanvas()->drawRect({0, 0, 1, 1}, paint);
71 
72     SkBitmap bitmap;
73     bitmap.allocPixels(info);
74     surface->readPixels(bitmap, 0, 0);
75 
76     canvas->drawImage(bitmap.asImage(), 0, 0);
77     return bitmap;
78 }
79 
80 /*
81   Test cases are inserted into the middle of this shader. The pasted expression is expected to
82   produce a single float. It can reference:
83 
84     'x'  : float  in [xMin, xMax]
85     'p'  : float2 in [xMin, xMax]  Lerps from (xMax, xMin) to (xMin, xMax)
86                                    (helpful for intrinsics with a mix of scalar/vector params)
87     'v1' : float2(1)
88     'v2' : float2(2)
89 */
make_unary_sksl_1d(const char * fn)90 static SkString make_unary_sksl_1d(const char* fn) {
91     return SkStringPrintf(
92             "uniform float xScale; uniform float xBias;"
93             "uniform float yScale; uniform float yBias;"
94             "half4 main(float2 p) {"
95             "    float2 v1 = float2(1);"
96             "    float2 v2 = float2(2);"
97             "    p = float2(p.x, 1 - p.x) * xScale + xBias;"
98             "    float x = p.x;"
99             "    float y = %s  * yScale + yBias;"
100             "    return y.xxx1;"
101             "}",
102             fn);
103 }
104 
105 // Draws one row of boxes, then advances the canvas translation vertically
plot(SkCanvas * canvas,const char * fn,float xMin,float xMax,float yMin,float yMax,const char * label=nullptr)106 static void plot(SkCanvas* canvas,
107                  const char* fn,
108                  float xMin,
109                  float xMax,
110                  float yMin,
111                  float yMax,
112                  const char* label = nullptr) {
113     canvas->save();
114 
115     draw_label(canvas, label ? label : fn);
116 
117     auto [effect, error] = SkRuntimeEffect::MakeForShader(make_unary_sksl_1d(fn));
118     if (!effect) {
119         SkDebugf("Error: %s\n", error.c_str());
120         return;
121     }
122 
123     SkRuntimeShaderBuilder builder(effect);
124     builder.uniform("xScale") = xMax - xMin;
125     builder.uniform("xBias")  = xMin;
126     builder.uniform("yScale") = 1.0f  / (yMax - yMin);
127     builder.uniform("yBias")  = -yMin / (yMax - yMin);
128 
129     SkBitmap bitmap =
130             draw_shader(canvas, builder.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false));
131 
132     // Plot...
133     SkPaint plotPaint({ 0.0f, 0.5f, 0.0f, 1.0f });
134     SkPoint pts[kBoxSize];
135     for (int x = 0; x < kBoxSize; ++x) {
136         SkColor c = bitmap.getColor(x, 0);
137         SkScalar y = (1 - (SkColorGetR(c) / 255.0f)) * kBoxSize;
138         pts[x].set(x + 0.5f, y);
139     }
140     canvas->drawPoints(SkCanvas::kPoints_PointMode, kBoxSize, pts, plotPaint);
141 
142     canvas->restore();
143     next_column(canvas);
144 }
145 
146 // The OpenGL ES Shading Language, Version 1.00, Section 8.1
147 DEF_SIMPLE_GM(runtime_intrinsics_trig,
148               canvas,
149               columns_to_width(3),
150               rows_to_height(5)) {
151     const float kPI = SK_FloatPI, kTwoPI = 2 * SK_FloatPI, kPIOverTwo = SK_FloatPI / 2;
152 
153     canvas->translate(kPadding, kPadding);
154     canvas->save();
155 
156     plot(canvas, "radians(x)", 0.0f, 360.0f, 0.0f, kTwoPI);
157     plot(canvas, "degrees(x)", 0.0f, kTwoPI, 0.0f, 360.0f);
158     next_row(canvas);
159 
160     plot(canvas, "sin(x)", 0.0f, kTwoPI,  -1.0f,  1.0f);
161     plot(canvas, "cos(x)", 0.0f, kTwoPI,  -1.0f,  1.0f);
162     plot(canvas, "tan(x)", 0.0f,    kPI, -10.0f, 10.0f);
163     next_row(canvas);
164 
165     plot(canvas, "asin(x)",  -1.0f,  1.0f, -kPIOverTwo, kPIOverTwo);
166     plot(canvas, "acos(x)",  -1.0f,  1.0f,        0.0f,        kPI);
167     plot(canvas, "atan(x)", -10.0f, 10.0f, -kPIOverTwo, kPIOverTwo);
168     next_row(canvas);
169 
170     plot(canvas, "atan(0.1,  x)", -1.0f, 1.0f,        0.0f,        kPI);
171     plot(canvas, "atan(-0.1, x)", -1.0f, 1.0f,        -kPI,       0.0f);
172     next_row(canvas);
173 
174     plot(canvas, "atan(x,  0.1)", -1.0f, 1.0f, -kPIOverTwo, kPIOverTwo);
175     plot(canvas, "atan(x, -0.1)", -1.0f, 1.0f,        -kPI,        kPI);
176     next_row(canvas);
177 }
178 
179 // The OpenGL ES Shading Language, Version 1.00, Section 8.2
180 DEF_SIMPLE_GM(runtime_intrinsics_exponential,
181               canvas,
182               columns_to_width(2),
183               rows_to_height(5)) {
184     canvas->translate(kPadding, kPadding);
185     canvas->save();
186 
187     plot(canvas, "pow(x, 3)",  0.0f, 8.0f, 0.0f, 500.0f);
188     plot(canvas, "pow(x, -3)", 0.0f, 4.0f, 0.0f,  10.0f);
189     next_row(canvas);
190 
191     plot(canvas, "pow(0.9, x)", -10.0f, 10.0f, 0.0f, 3.0f);
192     plot(canvas, "pow(1.1, x)", -10.0f, 10.0f, 0.0f, 3.0f);
193     next_row(canvas);
194 
195     plot(canvas, "exp(x)", -1.0f, 7.0f,  0.0f, 1000.0f);
196     plot(canvas, "log(x)",  0.0f, 2.5f, -4.0f,    1.0f);
197     next_row(canvas);
198 
199     plot(canvas, "exp2(x)", -1.0f, 7.0f,  0.0f, 130.0f);
200     plot(canvas, "log2(x)",  0.0f, 4.0f, -4.0f,   2.0f);
201     next_row(canvas);
202 
203     plot(canvas,        "sqrt(x)", 0.0f, 25.0f, 0.0f, 5.0f);
204     plot(canvas, "inversesqrt(x)", 0.0f, 25.0f, 0.2f, 4.0f);
205     next_row(canvas);
206 }
207 
208 // The OpenGL ES Shading Language, Version 1.00, Section 8.3
209 DEF_SIMPLE_GM(runtime_intrinsics_common,
210               canvas,
211               columns_to_width(6),
212               rows_to_height(7)) {
213     canvas->translate(kPadding, kPadding);
214     canvas->save();
215 
216     plot(canvas, "abs(x)",  -10.0f, 10.0f, 0.0f, 10.0f);
217     plot(canvas, "sign(x)",  -1.0f,  1.0f, -1.5f, 1.5f);
218     next_row(canvas);
219 
220     plot(canvas, "floor(x)",     -3.0f, 3.0f, -4.0f, 4.0f);
221     plot(canvas, "ceil(x)",      -3.0f, 3.0f, -4.0f, 4.0f);
222     plot(canvas, "fract(x)",     -3.0f, 3.0f,  0.0f, 1.0f);
223     plot(canvas, "mod(x, 2)",    -4.0f, 4.0f, -2.0f, 2.0f, "mod(scalar)");
224     plot(canvas, "mod(p, -2).x", -4.0f, 4.0f, -2.0f, 2.0f, "mod(mixed)" );
225     plot(canvas, "mod(p, v2).x", -4.0f, 4.0f, -2.0f, 2.0f, "mod(vector)");
226     next_row(canvas);
227 
228     plot(canvas, "min(x, 1)",    0.0f, 2.0f, 0.0f, 2.0f, "min(scalar)");
229     plot(canvas, "min(p, 1).x",  0.0f, 2.0f, 0.0f, 2.0f, "min(mixed)" );
230     plot(canvas, "min(p, v1).x", 0.0f, 2.0f, 0.0f, 2.0f, "min(vector)");
231     plot(canvas, "max(x, 1)",    0.0f, 2.0f, 0.0f, 2.0f, "max(scalar)");
232     plot(canvas, "max(p, 1).x",  0.0f, 2.0f, 0.0f, 2.0f, "max(mixed)" );
233     plot(canvas, "max(p, v1).x", 0.0f, 2.0f, 0.0f, 2.0f, "max(vector)");
234     next_row(canvas);
235 
236     plot(canvas, "clamp(x, 1, 2)",     0.0f, 3.0f, 0.0f, 3.0f, "clamp(scalar)");
237     plot(canvas, "clamp(p, 1, 2).x",   0.0f, 3.0f, 0.0f, 3.0f, "clamp(mixed)" );
238     plot(canvas, "clamp(p, v1, v2).x", 0.0f, 3.0f, 0.0f, 3.0f, "clamp(vector)");
239     plot(canvas, "saturate(x)", -1.0f, 2.0f, -0.5f, 1.5f);
240     next_row(canvas);
241 
242     plot(canvas, "mix(1, 2, x)",     -1.0f, 2.0f, 0.0f, 3.0f, "mix(scalar)");
243     plot(canvas, "mix(v1, v2, x).x", -1.0f, 2.0f, 0.0f, 3.0f, "mix(mixed)" );
244     plot(canvas, "mix(v1, v2, p).x", -1.0f, 2.0f, 0.0f, 3.0f, "mix(vector)");
245     next_row(canvas);
246 
247     plot(canvas, "step(1, x)",    0.0f, 2.0f, -0.5f, 1.5f, "step(scalar)");
248     plot(canvas, "step(1, p).x",  0.0f, 2.0f, -0.5f, 1.5f, "step(mixed)" );
249     plot(canvas, "step(v1, p).x", 0.0f, 2.0f, -0.5f, 1.5f, "step(vector)");
250     plot(canvas, "smoothstep(1, 2, x)",     0.5f, 2.5f, -0.5f, 1.5f, "smooth(scalar)");
251     plot(canvas, "smoothstep(1, 2, p).x",   0.5f, 2.5f, -0.5f, 1.5f, "smooth(mixed)" );
252     plot(canvas, "smoothstep(v1, v2, p).x", 0.5f, 2.5f, -0.5f, 1.5f, "smooth(vector)");
253     next_row(canvas);
254 
255     plot(canvas, "floor(p).x", -3.0f, 3.0f, -4.0f, 4.0f);
256     plot(canvas, "ceil(p).x",  -3.0f, 3.0f, -4.0f, 4.0f);
257     plot(canvas, "floor(p).y", -3.0f, 3.0f, -4.0f, 4.0f);
258     plot(canvas, "ceil(p).y",  -3.0f, 3.0f, -4.0f, 4.0f);
259     next_row(canvas);
260 }
261 
262 // The OpenGL ES Shading Language, Version 1.00, Section 8.4
263 DEF_SIMPLE_GM(runtime_intrinsics_geometric,
264               canvas,
265               columns_to_width(4),
266               rows_to_height(5)) {
267     canvas->translate(kPadding, kPadding);
268     canvas->save();
269 
270     plot(canvas, "length(x)",       -1.0f, 1.0f, -0.5f, 1.5f);
271     plot(canvas, "length(p)",        0.0f, 1.0f,  0.5f, 1.5f);
272     plot(canvas, "distance(x, 0)",  -1.0f, 1.0f, -0.5f, 1.5f);
273     plot(canvas, "distance(p, v1)",  0.0f, 1.0f,  0.5f, 1.5f);
274     next_row(canvas);
275 
276     plot(canvas, "dot(x, 2)",    -1.0f, 1.0f, -2.5f, 2.5f);
277     plot(canvas, "dot(p, p.y1)", -1.0f, 1.0f, -2.5f, 0.5f);
278     next_row(canvas);
279 
280     plot(canvas, "cross(p.xy1, p.y1x).x", 0.0f, 1.0f, -1.0f, 1.0f);
281     plot(canvas, "cross(p.xy1, p.y1x).y", 0.0f, 1.0f, -1.0f, 1.0f);
282     plot(canvas, "cross(p.xy1, p.y1x).z", 0.0f, 1.0f, -1.0f, 1.0f);
283     next_row(canvas);
284 
285     plot(canvas, "normalize(x)",   -2.0f, 2.0f, -1.5f, 1.5f);
286     plot(canvas, "normalize(p).x",  0.0f, 2.0f,  0.0f, 1.0f);
287     plot(canvas, "normalize(p).y",  0.0f, 2.0f,  0.0f, 1.0f);
288     plot(canvas, "faceforward(v1, p.x0, v1.x0).x", -1.0f, 1.0f, -1.5f, 1.5f, "faceforward");
289     next_row(canvas);
290 
291     plot(canvas, "reflect(p.x1, v1.0x).x",         -1.0f, 1.0f, -1.0f, 1.0f, "reflect(horiz)");
292     plot(canvas, "reflect(p.x1, normalize(v1)).y", -1.0f, 1.0f, -1.0f, 1.0f, "reflect(diag)" );
293     plot(canvas, "refract(v1.x0, v1.0x, x).x",      0.0f, 1.0f, -1.0f, 1.0f, "refract().x");
294     plot(canvas, "refract(v1.x0, v1.0x, x).y",      0.0f, 1.0f, -1.0f, 1.0f, "refract().y");
295     next_row(canvas);
296 }
297 
298 #define SKSL_MATRIX_SELECTORS               \
299     "inline float2 sel2(float x) {"         \
300     "    return float2("                    \
301     "      x <  0.5 ? 1 : 0,"               \
302     "      x >= 0.5 ? 1 : 0);"              \
303     "}"                                     \
304     "inline float3 sel3(float x) {"         \
305     "    return float3("                    \
306     "      x <  0.33             ? 1 : 0,"  \
307     "      x >= 0.33 && x < 0.66 ? 1 : 0,"  \
308     "      x >= 0.66             ? 1 : 0);" \
309     "}"                                     \
310     "inline float4 sel4(float x) {"         \
311     "    return float4("                    \
312     "      x <  0.25             ? 1 : 0,"  \
313     "      x >= 0.25 && x < 0.5  ? 1 : 0,"  \
314     "      x >= 0.5  && x < 0.75 ? 1 : 0,"  \
315     "      x >= 0.75             ? 1 : 0);" \
316     "}"
317 
318 // Shader for testing matrixCompMult intrinsic
make_matrix_comp_mult_sksl(int dim)319 static SkString make_matrix_comp_mult_sksl(int dim) {
320     return SkStringPrintf(
321             "uniform float%dx%d m1;"                              // dim, dim
322             "uniform float%dx%d m2;"                              // dim, dim
323             SKSL_MATRIX_SELECTORS
324             "half4 main(float2 p) {"
325             "    float%d colSel = sel%d(p.x);"                    // dim, dim
326             "    float%d rowSel = sel%d(p.y);"                    // dim, dim
327             "    float%d col = matrixCompMult(m1, m2) * colSel;"  // dim
328             "    float  v = dot(col, rowSel);"
329             "    return v.xxx1;"
330             "}", dim, dim, dim, dim, dim, dim, dim, dim, dim);
331 }
332 
333 template <int N>
plot_matrix_comp_mult(SkCanvas * canvas,std::array<float,N * N> mtx1,std::array<float,N * N> mtx2,const char * label)334 static void plot_matrix_comp_mult(SkCanvas* canvas,
335                                   std::array<float, N*N> mtx1,
336                                   std::array<float, N*N> mtx2,
337                                   const char* label) {
338     canvas->save();
339 
340     draw_label(canvas, label);
341 
342     auto [effect, error] = SkRuntimeEffect::MakeForShader(make_matrix_comp_mult_sksl(N));
343     if (!effect) {
344         SkDebugf("Error: %s\n", error.c_str());
345         return;
346     }
347 
348     SkRuntimeShaderBuilder builder(effect);
349     builder.uniform("m1") = mtx1;
350     builder.uniform("m2") = mtx2;
351 
352     draw_shader(canvas, builder.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false));
353 
354     canvas->restore();
355     next_column(canvas);
356 }
357 
358 // Shader for testing inverse() intrinsic
make_matrix_inverse_sksl(int dim)359 static SkString make_matrix_inverse_sksl(int dim) {
360     return SkStringPrintf(
361             "uniform float scale; uniform float bias;"
362             "uniform float%dx%d m;"                    // dim, dim
363             SKSL_MATRIX_SELECTORS
364             "half4 main(float2 p) {"
365             "    float%d colSel = sel%d(p.x);"         // dim, dim
366             "    float%d rowSel = sel%d(p.y);"         // dim, dim
367             "    float%d col = inverse(m) * colSel;"   // dim
368             "    float  v = dot(col, rowSel) * scale + bias;"
369             "    return v.xxx1;"
370             "}", dim, dim, dim, dim, dim, dim, dim);
371 }
372 
373 template <int N>
plot_matrix_inverse(SkCanvas * canvas,std::array<float,N * N> mtx,const char * label)374 static void plot_matrix_inverse(SkCanvas* canvas, std::array<float, N*N> mtx, const char* label) {
375     canvas->save();
376 
377     draw_label(canvas, label);
378 
379     auto [effect, error] = SkRuntimeEffect::MakeForShader(make_matrix_inverse_sksl(N));
380     if (!effect) {
381         SkDebugf("Error: %s\n", error.c_str());
382         return;
383     }
384 
385     SkRuntimeShaderBuilder builder(effect);
386     builder.uniform("scale") = 0.5f;
387     builder.uniform("bias")  = 0.5f;
388     builder.uniform("m")     = mtx;
389 
390     draw_shader(canvas, builder.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/false));
391 
392     canvas->restore();
393     next_column(canvas);
394 }
395 
396 // The OpenGL ES Shading Language, Version 1.00, Section 8.5
397 DEF_SIMPLE_GM(runtime_intrinsics_matrix,
398               canvas,
399               columns_to_width(3),
400               rows_to_height(2)) {
401     canvas->translate(kPadding, kPadding);
402     canvas->save();
403 
404     // Random pairs of matrices where the elements of matrixCompMult(m1, m2) lie in [0, 1]
405     plot_matrix_comp_mult<2>(canvas,
406                              {1.00f, 0.0f, 2.0f, 0.5f},
407                              {0.75f, 2.0f, 0.2f, 1.2f},
408                              "compMult(2x2)");
409 
410     plot_matrix_comp_mult<3>(canvas,
411                              {1.00f, 0.0f, 2.0f, 0.5f, -1.0f, -2.0f, -0.5f, 4.00f, 0.25f},
412                              {0.75f, 2.0f, 0.2f, 1.2f, -0.8f, -0.1f, -1.8f, 0.25f, 2.00f},
413                              "compMult(3x3)");
414 
415     plot_matrix_comp_mult<4>(canvas,
416                              {1.00f, 0.0f, 2.0f, 0.5f, -1.0f, -2.0f, -0.5f, 4.00f, 0.25f, 0.05f,
417                               10.00f, -0.66f, -1.0f, -0.5f, 0.5f, 0.66f},
418                              {0.75f, 2.0f, 0.2f, 1.2f, -0.8f, -0.1f, -1.8f, 0.25f, 2.00f, 2.00f,
419                               0.03f, -1.00f, -1.0f, -0.5f, 1.7f, 0.66f},
420                              "compMult(4x4)");
421     next_row(canvas);
422 
423     // Random, invertible matrices where the elements of inverse(m) lie in [-1, 1]
424     plot_matrix_inverse<2>(canvas,
425                            { 1.20f,  0.68f,
426                             -0.27f, -1.55f},
427                            "inverse(2x2)");
428 
429     plot_matrix_inverse<3>(canvas,
430                            {-1.13f, -2.96f, -0.14f,
431                              1.45f, -1.88f, -1.02f,
432                             -2.54f, -2.58f, -1.17f},
433                            "inverse(3x3)");
434 
435     plot_matrix_inverse<4>(canvas,
436                            {-1.51f, -3.95f, -0.19f,  1.93f,
437                             -2.51f, -1.35f, -3.39f, -3.45f,
438                             -1.56f,  1.61f, -0.22f, -1.08f,
439                             -2.81f, -2.14f, -0.09f,  3.00f},
440                            "inverse(4x4)");
441     next_row(canvas);
442 }
443 
444 /*
445   Specialized shader for testing relational operators.
446 */
make_bvec_sksl(const char * type,const char * fn)447 static SkString make_bvec_sksl(const char* type, const char* fn) {
448     // We use negative floats, to ensure that the integer variants are working with the correct
449     // interpretation of the data.
450     return SkStringPrintf(
451             "uniform %s2 v1;"
452             "half4 main(float2 p) {"
453             "    p.x = p.x < 0.33 ? -3.0 : (p.x < 0.66 ? -2.0 : -1.0);"
454             "    p.y = p.y < 0.33 ? -3.0 : (p.y < 0.66 ? -2.0 : -1.0);"
455             "    bool2 cmp = %s;"
456             "    return half4(cmp.x ? 1.0 : 0.0, cmp.y ? 1.0 : 0.0, 0, 1);"
457             "}",
458             type, fn);
459 }
460 
461 template <typename T = float>
plot_bvec(SkCanvas * canvas,const char * fn,const char * label)462 static void plot_bvec(SkCanvas* canvas, const char* fn, const char* label) {
463     canvas->save();
464 
465     draw_label(canvas, label);
466 
467     const char* type = std::is_integral<T>::value ? "int" : "float";
468     auto [effect, error] = SkRuntimeEffect::MakeForShader(make_bvec_sksl(type, fn));
469     if (!effect) {
470         SkDebugf("Error: %s\n", error.c_str());
471         return;
472     }
473 
474     T uniformData[2] = { -2, -2 };
475     sk_sp<SkData> uniforms = SkData::MakeWithCopy(uniformData, sizeof(uniformData));
476 
477     draw_shader(canvas,
478                 effect->makeShader(uniforms,
479                                    /*children=*/nullptr,
480                                    /*childCount=*/0,
481                                    /*localMatrix=*/nullptr,
482                                    /*isOpaque=*/false));
483 
484     canvas->restore();
485     next_column(canvas);
486 }
487 
488 // The OpenGL ES Shading Language, Version 1.00, Section 8.6
489 DEF_SIMPLE_GM(runtime_intrinsics_relational,
490               canvas,
491               columns_to_width(4),
492               rows_to_height(6)) {
493     canvas->translate(kPadding, kPadding);
494     canvas->save();
495 
496     plot_bvec<float>(canvas, "lessThan(p, v1)",            "lessThan");
497     plot_bvec<int>  (canvas, "lessThan(int2(p), v1)",      "lessThan(int)");
498     plot_bvec<float>(canvas, "lessThanEqual(p, v1)",       "lessThanEqual");
499     plot_bvec<int>  (canvas, "lessThanEqual(int2(p), v1)", "lessThanEqual(int)");
500     next_row(canvas);
501 
502     plot_bvec<float>(canvas, "greaterThan(p, v1)",            "greaterThan");
503     plot_bvec<int>  (canvas, "greaterThan(int2(p), v1)",      "greaterThan(int)");
504     plot_bvec<float>(canvas, "greaterThanEqual(p, v1)",       "greaterThanEqual");
505     plot_bvec<int>  (canvas, "greaterThanEqual(int2(p), v1)", "greaterThanEqual(int)");
506     next_row(canvas);
507 
508     plot_bvec<float>(canvas, "equal(p, v1)",          "equal");
509     plot_bvec<int>  (canvas, "equal(int2(p), v1)",    "equal(int)");
510     plot_bvec<float>(canvas, "notEqual(p, v1)",       "notEqual");
511     plot_bvec<int>  (canvas, "notEqual(int2(p), v1)", "notEqual(int)");
512     next_row(canvas);
513 
514     plot_bvec(canvas, "equal(   lessThanEqual(p, v1), greaterThanEqual(p, v1))", "equal(bvec)");
515     plot_bvec(canvas, "notEqual(lessThanEqual(p, v1), greaterThanEqual(p, v1))", "notequal(bvec)");
516     next_row(canvas);
517 
518     plot_bvec(canvas, "not(notEqual(p, v1))", "not(notEqual)");
519     plot_bvec(canvas, "not(equal(p, v1))",    "not(equal)");
520     next_row(canvas);
521 
522     plot_bvec(canvas, "bool2(any(equal(p, v1)))", "any(equal)");
523     plot_bvec(canvas, "bool2(all(equal(p, v1)))", "all(equal)");
524     next_row(canvas);
525 }
526