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/SkFont.h"
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "src/gpu/GrBitmapTextureMaker.h"
12 #include "src/gpu/GrDirectContextPriv.h"
13 #include "src/gpu/GrRenderTargetContextPriv.h"
14 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "src/gpu/ops/GrFillRectOp.h"
16 #include "tools/ToolUtils.h"
17 
18 // Samples child with a constant (literal) matrix
19 // Scales along X
20 class ConstantMatrixEffect : public GrFragmentProcessor {
21 public:
22     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 3;
23 
ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)24     ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
25             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
26         this->registerChild(std::move(child),
27                             SkSL::SampleUsage::UniformMatrix(
28                                 "float3x3(float3(0.5, 0.0, 0.0), "
29                                         "float3(0.0, 1.0, 0.0), "
30                                         "float3(0.0, 0.0, 1.0))"));
31     }
32 
name() const33     const char* name() const override { return "ConstantMatrixEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const34     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const35     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const36     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
37 
onCreateGLSLInstance() const38     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
39         class Impl : public GrGLSLFragmentProcessor {
40             void emitCode(EmitArgs& args) override {
41                 SkString sample = this->invokeChildWithMatrix(0, args);
42                 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
43             }
44         };
45         return new Impl;
46     }
47 };
48 
49 // Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
50 // Scales along Y
51 class UniformMatrixEffect : public GrFragmentProcessor {
52 public:
53     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
54 
UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)55     UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
56             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
57         this->registerChild(std::move(child), SkSL::SampleUsage::UniformMatrix("matrix"));
58     }
59 
name() const60     const char* name() const override { return "UniformMatrixEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const61     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const62     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const63     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
64 
onCreateGLSLInstance() const65     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
66         class Impl : public GrGLSLFragmentProcessor {
67             void emitCode(EmitArgs& args) override {
68                 fMatrixVar = args.fUniformHandler->addUniform(&args.fFp, kFragment_GrShaderFlag,
69                                                               kFloat3x3_GrSLType, "matrix");
70                 SkString sample = this->invokeChildWithMatrix(0, args);
71                 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
72             }
73             void onSetData(const GrGLSLProgramDataManager& pdman,
74                            const GrFragmentProcessor& proc) override {
75                 pdman.setSkMatrix(fMatrixVar, SkMatrix::Scale(1, 0.5f));
76             }
77             UniformHandle fMatrixVar;
78         };
79         return new Impl;
80     }
81 };
82 
83 // Samples child with a variable matrix
84 // Translates along X
85 // Typically, kVariable would be due to multiple sample(matrix) invocations, but this artificially
86 // uses kVariable with a single (constant) matrix.
87 class VariableMatrixEffect : public GrFragmentProcessor {
88 public:
89     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 5;
90 
VariableMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)91     VariableMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
92             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
93         this->registerChild(std::move(child), SkSL::SampleUsage::VariableMatrix());
94     }
95 
name() const96     const char* name() const override { return "VariableMatrixEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const97     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const98     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const99     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
100 
onCreateGLSLInstance() const101     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
102         class Impl : public GrGLSLFragmentProcessor {
103             void emitCode(EmitArgs& args) override {
104                 SkString sample = this->invokeChildWithMatrix(
105                         0, args, "float3x3(1, 0, 0, 0, 1, 0, 8, 0, 1)");
106                 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
107             }
108         };
109         return new Impl;
110     }
111 };
112 
113 // Samples child with explicit coords
114 // Translates along Y
115 class ExplicitCoordEffect : public GrFragmentProcessor {
116 public:
117     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
118 
ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)119     ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
120             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
121         this->registerChild(std::move(child), SkSL::SampleUsage::Explicit());
122         this->setUsesSampleCoordsDirectly();
123     }
124 
name() const125     const char* name() const override { return "ExplicitCoordEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const126     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const127     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const128     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
129 
onCreateGLSLInstance() const130     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
131         class Impl : public GrGLSLFragmentProcessor {
132             void emitCode(EmitArgs& args) override {
133                 args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);",
134                                                args.fSampleCoord);
135                 SkString sample = this->invokeChild(0, args, "coord");
136                 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
137             }
138         };
139         return new Impl;
140     }
141 };
142 
143 // Generates test pattern
144 class TestPatternEffect : public GrFragmentProcessor {
145 public:
146     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
147 
TestPatternEffect()148     TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
149         this->setUsesSampleCoordsDirectly();
150     }
151 
name() const152     const char* name() const override { return "TestPatternEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const153     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const154     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const155     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
156 
onCreateGLSLInstance() const157     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
158         class Impl : public GrGLSLFragmentProcessor {
159             void emitCode(EmitArgs& args) override {
160                 auto fb = args.fFragBuilder;
161                 fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord);
162                 fb->codeAppendf("coord = floor(coord * 4) / 3;");
163                 fb->codeAppendf("%s = half4(half2(coord.rg), 0, 1);\n", args.fOutputColor);
164             }
165         };
166         return new Impl;
167     }
168 };
169 
make_test_bitmap()170 SkBitmap make_test_bitmap() {
171     SkBitmap bitmap;
172     bitmap.allocN32Pixels(64, 64);
173     SkCanvas canvas(bitmap);
174 
175     SkFont font(ToolUtils::create_portable_typeface());
176     const char* alpha = "ABCDEFGHIJKLMNOP";
177 
178     for (int i = 0; i < 16; ++i) {
179         int tx = i % 4,
180             ty = i / 4;
181         int x = tx * 16,
182             y = ty * 16;
183         SkPaint paint;
184         paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f });
185         canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint);
186         paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f });
187         canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint);
188     }
189 
190     return bitmap;
191 }
192 
193 enum EffectType {
194     kConstant,
195     kUniform,
196     kVariable,
197     kExplicit,
198 };
199 
wrap(std::unique_ptr<GrFragmentProcessor> fp,EffectType effectType)200 static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp,
201                                                  EffectType effectType) {
202     switch (effectType) {
203         case kConstant:
204             return std::make_unique<ConstantMatrixEffect>(std::move(fp));
205         case kUniform:
206             return std::make_unique<UniformMatrixEffect>(std::move(fp));
207         case kVariable:
208             return std::make_unique<VariableMatrixEffect>(std::move(fp));
209         case kExplicit:
210             return std::make_unique<ExplicitCoordEffect>(std::move(fp));
211     }
212     SkUNREACHABLE;
213 }
214 
215 DEF_SIMPLE_GPU_GM(fp_sample_chaining, ctx, rtCtx, canvas, 380, 306) {
216     SkBitmap bmp = make_test_bitmap();
217 
218     GrBitmapTextureMaker maker(ctx, bmp, GrImageTexGenPolicy::kDraw);
219     int x = 10, y = 10;
220 
__anonf2daa6e00102null221     auto nextCol = [&] { x += (64 + 10); };
__anonf2daa6e00202null222     auto nextRow = [&] { x = 10; y += (64 + 10); };
223 
__anonf2daa6e00302(std::initializer_list<EffectType> effects) 224     auto draw = [&](std::initializer_list<EffectType> effects) {
225         // Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice
226         // visually (no text labels in each box), but it avoids the extra GrMatrixEffect.
227         // Switching it on actually triggers *more* shader compilation failures.
228 #if 0
229         auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect());
230 #else
231         auto view = maker.view(GrMipmapped::kNo);
232         auto fp = GrTextureEffect::Make(std::move(view), maker.alphaType());
233 #endif
234         for (EffectType effectType : effects) {
235             fp = wrap(std::move(fp), effectType);
236         }
237         GrPaint paint;
238         paint.setColorFragmentProcessor(std::move(fp));
239         rtCtx->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y),
240                         SkRect::MakeIWH(64, 64));
241         nextCol();
242     };
243 
244     // Reminder, in every case, the chain is more complicated than it seems, because the
245     // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
246     // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
247 
248     // First row: no transform, then each one independently applied
249     draw({});             // Identity (4 rows and columns)
250     draw({ kConstant });  // Scale X axis by 2x (2 visible columns)
251     draw({ kUniform  });  // Scale Y axis by 2x (2 visible rows)
252     draw({ kVariable });  // Translate left by 8px
253     draw({ kExplicit });  // Translate up by 8px
254     nextRow();
255 
256     // Second row: transform duplicated
257     draw({ kConstant, kUniform  });  // Scale XY by 2x (2 rows and columns)
258     draw({ kConstant, kConstant });  // Scale X axis by 4x (1 visible column)
259     draw({ kUniform,  kUniform  });  // Scale Y axis by 4x (1 visible row)
260     draw({ kVariable, kVariable });  // Translate left by 16px
261     draw({ kExplicit, kExplicit });  // Translate up by 16px
262     nextRow();
263 
264     // Remember, these are applied inside out:
265     draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
266     draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
267     draw({ kUniform,  kVariable }); // Scale Y by 2x and translate left by 8px
268     draw({ kUniform,  kExplicit }); // Scale Y by 2x and translate up by 8px
269     draw({ kVariable, kExplicit }); // Translate left and up by 8px
270     nextRow();
271 
272     draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
273     draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
274     draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
275     draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
276     draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
277 }
278 
279 const char* gConstantMatrixSkSL = R"(
280     uniform shader child;
281     half4 main(float2 xy) {
282         return sample(child, float3x3(0.5, 0.0, 0.0,
283                                       0.0, 1.0, 0.0,
284                                       0.0, 0.0, 1.0));
285     }
286 )";
287 
288 const char* gUniformMatrixSkSL = R"(
289     uniform shader child;
290     uniform float3x3 matrix;
291     half4 main(float2 xy) {
292         return sample(child, matrix);
293     }
294 )";
295 
296 // This form (uniform * constant) is currently detected as variable, thanks to our limited analysis
297 // when scanning for sample matrices. With that pulled into a separate local, it's highly unlikely
298 // we'll ever treat this as anything else.
299 const char* gVariableMatrixSkSL = R"(
300     uniform shader child;
301     uniform float3x3 matrix;
302     half4 main(float2 xy) {
303         float3x3 varMatrix = matrix * 0.5;
304         return sample(child, varMatrix);
305     }
306 )";
307 
308 const char* gExplicitCoordSkSL = R"(
309     uniform shader child;
310     half4 main(float2 xy) {
311         return sample(child, xy + float2(0, 8));
312     }
313 )";
314 
315 // Version of fp_sample_chaining that uses SkRuntimeEffect
316 DEF_SIMPLE_GM(sksl_sample_chaining, canvas, 380, 306) {
317     SkBitmap bmp = make_test_bitmap();
318 
319     sk_sp<SkRuntimeEffect> effects[4] = {
320         std::get<0>(SkRuntimeEffect::Make(SkString(gConstantMatrixSkSL))),
321         std::get<0>(SkRuntimeEffect::Make(SkString(gUniformMatrixSkSL))),
322         std::get<0>(SkRuntimeEffect::Make(SkString(gVariableMatrixSkSL))),
323         std::get<0>(SkRuntimeEffect::Make(SkString(gExplicitCoordSkSL))),
324     };
325 
326     canvas->translate(10, 10);
327     canvas->save();
__anonf2daa6e00402null328     auto nextCol = [&] { canvas->translate(64 + 10, 0); };
__anonf2daa6e00502null329     auto nextRow = [&] { canvas->restore(); canvas->translate(0, 64 + 10); canvas->save(); };
330 
__anonf2daa6e00602(std::initializer_list<EffectType> effectTypes) 331     auto draw = [&](std::initializer_list<EffectType> effectTypes) {
332         auto shader = bmp.makeShader();
333 
334         for (EffectType effectType : effectTypes) {
335             SkRuntimeShaderBuilder builder(effects[effectType]);
336             builder.child("child") = shader;
337             switch (effectType) {
338                 case kUniform:
339                     builder.uniform("matrix") = SkMatrix::Scale(1.0f, 0.5f);
340                     break;
341                 case kVariable:
342                     builder.uniform("matrix") = SkMatrix::Translate(8, 0);
343                     break;
344                 default:
345                     break;
346             }
347             shader = builder.makeShader(nullptr, true);
348         }
349         SkPaint paint;
350         paint.setShader(shader);
351         canvas->drawRect(SkRect::MakeWH(64, 64), paint);
352         nextCol();
353     };
354 
355     // Reminder, in every case, the chain is more complicated than it seems, because the
356     // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
357     // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
358 
359     // First row: no transform, then each one independently applied
360     draw({});             // Identity (4 rows and columns)
361     draw({ kConstant });  // Scale X axis by 2x (2 visible columns)
362     draw({ kUniform  });  // Scale Y axis by 2x (2 visible rows)
363     draw({ kVariable });  // Translate left by 8px
364     draw({ kExplicit });  // Translate up by 8px
365     nextRow();
366 
367     // Second row: transform duplicated
368     draw({ kConstant, kUniform  });  // Scale XY by 2x (2 rows and columns)
369     draw({ kConstant, kConstant });  // Scale X axis by 4x (1 visible column)
370     draw({ kUniform,  kUniform  });  // Scale Y axis by 4x (1 visible row)
371     draw({ kVariable, kVariable });  // Translate left by 16px
372     draw({ kExplicit, kExplicit });  // Translate up by 16px
373     nextRow();
374 
375     // Remember, these are applied inside out:
376     draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
377     draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
378     draw({ kUniform,  kVariable }); // Scale Y by 2x and translate left by 8px
379     draw({ kUniform,  kExplicit }); // Scale Y by 2x and translate up by 8px
380     draw({ kVariable, kExplicit }); // Translate left and up by 8px
381     nextRow();
382 
383     draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
384     draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
385     draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
386     draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
387     draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
388 }
389