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/GrDirectContextPriv.h"
12 #include "src/gpu/SkGr.h"
13 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
14 #include "src/gpu/ops/GrFillRectOp.h"
15 #include "tools/ToolUtils.h"
16 
17 // Samples child with a constant (literal) matrix
18 // Scales along X
19 class ConstantMatrixEffect : public GrFragmentProcessor {
20 public:
21     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 3;
22 
ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)23     ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
24             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
25         this->registerChild(std::move(child),
26                             SkSL::SampleUsage::UniformMatrix(
27                                 "float3x3(float3(0.5, 0.0, 0.0), "
28                                         "float3(0.0, 1.0, 0.0), "
29                                         "float3(0.0, 0.0, 1.0))"));
30     }
31 
name() const32     const char* name() const override { return "ConstantMatrixEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const33     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const34     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const35     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
36 
onMakeProgramImpl() const37     std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override {
38         class Impl : public GrGLSLFragmentProcessor {
39             void emitCode(EmitArgs& args) override {
40                 SkString sample = this->invokeChildWithMatrix(0, args);
41                 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
42             }
43         };
44         return std::make_unique<Impl>();
45     }
46 };
47 
48 // Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
49 // Scales along Y
50 class UniformMatrixEffect : public GrFragmentProcessor {
51 public:
52     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
53 
UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)54     UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
55             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
56         this->registerChild(std::move(child), SkSL::SampleUsage::UniformMatrix("matrix"));
57     }
58 
name() const59     const char* name() const override { return "UniformMatrixEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const60     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const61     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const62     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
63 
onMakeProgramImpl() const64     std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override {
65         class Impl : public GrGLSLFragmentProcessor {
66             void emitCode(EmitArgs& args) override {
67                 fMatrixVar = args.fUniformHandler->addUniform(&args.fFp, kFragment_GrShaderFlag,
68                                                               kFloat3x3_GrSLType, "matrix");
69                 SkString sample = this->invokeChildWithMatrix(0, args);
70                 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
71             }
72             void onSetData(const GrGLSLProgramDataManager& pdman,
73                            const GrFragmentProcessor& proc) override {
74                 pdman.setSkMatrix(fMatrixVar, SkMatrix::Scale(1, 0.5f));
75             }
76             UniformHandle fMatrixVar;
77         };
78         return std::make_unique<Impl>();
79     }
80 };
81 
82 // Samples child with explicit coords
83 // Translates along Y
84 class ExplicitCoordEffect : public GrFragmentProcessor {
85 public:
86     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
87 
ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)88     ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
89             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
90         this->registerChild(std::move(child), SkSL::SampleUsage::Explicit());
91         this->setUsesSampleCoordsDirectly();
92     }
93 
name() const94     const char* name() const override { return "ExplicitCoordEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const95     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const96     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const97     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
98 
onMakeProgramImpl() const99     std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override {
100         class Impl : public GrGLSLFragmentProcessor {
101             void emitCode(EmitArgs& args) override {
102                 args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);",
103                                                args.fSampleCoord);
104                 SkString sample = this->invokeChild(0, args, "coord");
105                 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
106             }
107         };
108         return std::make_unique<Impl>();
109     }
110 };
111 
112 // Generates test pattern
113 class TestPatternEffect : public GrFragmentProcessor {
114 public:
115     static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
116 
TestPatternEffect()117     TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
118         this->setUsesSampleCoordsDirectly();
119     }
120 
name() const121     const char* name() const override { return "TestPatternEffect"; }
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const122     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const123     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const124     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
125 
onMakeProgramImpl() const126     std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override {
127         class Impl : public GrGLSLFragmentProcessor {
128             void emitCode(EmitArgs& args) override {
129                 auto fb = args.fFragBuilder;
130                 fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord);
131                 fb->codeAppendf("coord = floor(coord * 4) / 3;");
132                 fb->codeAppendf("return half2(coord).rg01;\n");
133             }
134         };
135         return std::make_unique<Impl>();
136     }
137 };
138 
make_test_bitmap()139 SkBitmap make_test_bitmap() {
140     SkBitmap bitmap;
141     bitmap.allocN32Pixels(64, 64);
142     SkCanvas canvas(bitmap);
143 
144     SkFont font(ToolUtils::create_portable_typeface());
145     const char* alpha = "ABCDEFGHIJKLMNOP";
146 
147     for (int i = 0; i < 16; ++i) {
148         int tx = i % 4,
149             ty = i / 4;
150         int x = tx * 16,
151             y = ty * 16;
152         SkPaint paint;
153         paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f });
154         canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint);
155         paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f });
156         canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint);
157     }
158 
159     return bitmap;
160 }
161 
162 enum EffectType {
163     kConstant,
164     kUniform,
165     kExplicit,
166 };
167 
wrap(std::unique_ptr<GrFragmentProcessor> fp,EffectType effectType)168 static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp,
169                                                  EffectType effectType) {
170     switch (effectType) {
171         case kConstant:
172             return std::make_unique<ConstantMatrixEffect>(std::move(fp));
173         case kUniform:
174             return std::make_unique<UniformMatrixEffect>(std::move(fp));
175         case kExplicit:
176             return std::make_unique<ExplicitCoordEffect>(std::move(fp));
177     }
178     SkUNREACHABLE;
179 }
180 
181 DEF_SIMPLE_GPU_GM(fp_sample_chaining, ctx, rtCtx, canvas, 306, 232) {
182     SkBitmap bmp = make_test_bitmap();
183 
184     int x = 10, y = 10;
185 
__anonaf7915130102null186     auto nextCol = [&] { x += (64 + 10); };
__anonaf7915130202null187     auto nextRow = [&] { x = 10; y += (64 + 10); };
188 
__anonaf7915130302(std::initializer_list<EffectType> effects) 189     auto draw = [&](std::initializer_list<EffectType> effects) {
190         // Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice
191         // visually (no text labels in each box), but it avoids the extra GrMatrixEffect.
192         // Switching it on actually triggers *more* shader compilation failures.
193 #if 0
194         auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect());
195 #else
196         auto view = std::get<0>(GrMakeCachedBitmapProxyView(ctx, bmp, GrMipmapped::kNo));
197         auto fp = GrTextureEffect::Make(std::move(view), bmp.alphaType());
198 #endif
199         for (EffectType effectType : effects) {
200             fp = wrap(std::move(fp), effectType);
201         }
202         GrPaint paint;
203         paint.setColorFragmentProcessor(std::move(fp));
204         rtCtx->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y),
205                         SkRect::MakeIWH(64, 64));
206         nextCol();
207     };
208 
209     // Reminder, in every case, the chain is more complicated than it seems, because the
210     // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
211     // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
212 
213     // First row: no transform, then each one independently applied
214     draw({});             // Identity (4 rows and columns)
215     draw({ kConstant });  // Scale X axis by 2x (2 visible columns)
216     draw({ kUniform  });  // Scale Y axis by 2x (2 visible rows)
217     draw({ kExplicit });  // Translate up by 8px
218     nextRow();
219 
220     // Second row: transform duplicated
221     draw({ kConstant, kUniform  });  // Scale XY by 2x (2 rows and columns)
222     draw({ kConstant, kConstant });  // Scale X axis by 4x (1 visible column)
223     draw({ kUniform,  kUniform  });  // Scale Y axis by 4x (1 visible row)
224     draw({ kExplicit, kExplicit });  // Translate up by 16px
225     nextRow();
226 
227     // Remember, these are applied inside out:
228     draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
229     draw({ kUniform,  kExplicit }); // Scale Y by 2x and translate up by 8px
230     draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
231     draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
232 }
233