• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/core/SkCanvasPriv.h"
12 #include "src/gpu/GrDirectContextPriv.h"
13 #include "src/gpu/GrPaint.h"
14 #include "src/gpu/SkGr.h"
15 #include "src/gpu/effects/GrMatrixEffect.h"
16 #include "src/gpu/effects/GrTextureEffect.h"
17 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
18 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
19 #include "tools/ToolUtils.h"
20 
21 namespace {
22 
23 // Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
24 // Scales along Y
25 class UniformMatrixEffect : public GrFragmentProcessor {
26 public:
27     inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
28 
UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)29     UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
30             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
31         this->registerChild(std::move(child),
32                             SkSL::SampleUsage::UniformMatrix(/*hasPerspective=*/false));
33     }
34 
name() const35     const char* name() const override { return "UniformMatrixEffect"; }
onAddToKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const36     void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const37     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const38     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
39 
onMakeProgramImpl() const40     std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
41         class Impl : public ProgramImpl {
42         public:
43             void emitCode(EmitArgs& args) override {
44                 fMatrixVar =
45                         args.fUniformHandler->addUniform(&args.fFp,
46                                                          kFragment_GrShaderFlag,
47                                                          kFloat3x3_GrSLType,
48                                                          SkSL::SampleUsage::MatrixUniformName());
49                 SkString sample = this->invokeChildWithMatrix(0, args);
50                 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
51             }
52 
53         private:
54             void onSetData(const GrGLSLProgramDataManager& pdman,
55                            const GrFragmentProcessor& proc) override {
56                 pdman.setSkMatrix(fMatrixVar, SkMatrix::Scale(1, 0.5f));
57             }
58             UniformHandle fMatrixVar;
59         };
60         return std::make_unique<Impl>();
61     }
62 };
63 
64 // Samples child with explicit coords
65 // Translates along Y
66 class ExplicitCoordEffect : public GrFragmentProcessor {
67 public:
68     inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
69 
ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)70     ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
71             : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
72         this->registerChild(std::move(child), SkSL::SampleUsage::Explicit());
73         this->setUsesSampleCoordsDirectly();
74     }
75 
name() const76     const char* name() const override { return "ExplicitCoordEffect"; }
onAddToKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const77     void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const78     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const79     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
80 
onMakeProgramImpl() const81     std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
82         class Impl : public ProgramImpl {
83         public:
84             void emitCode(EmitArgs& args) override {
85                 args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);",
86                                                args.fSampleCoord);
87                 SkString sample = this->invokeChild(0, args, "coord");
88                 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
89             }
90         };
91 
92         return std::make_unique<Impl>();
93     }
94 };
95 
96 // Generates test pattern
97 class TestPatternEffect : public GrFragmentProcessor {
98 public:
99     inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
100 
TestPatternEffect()101     TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
102         this->setUsesSampleCoordsDirectly();
103     }
104 
name() const105     const char* name() const override { return "TestPatternEffect"; }
onAddToKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const106     void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
onIsEqual(const GrFragmentProcessor & that) const107     bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
clone() const108     std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
109 
onMakeProgramImpl() const110     std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
111         class Impl : public ProgramImpl {
112         public:
113             void emitCode(EmitArgs& args) override {
114                 auto fb = args.fFragBuilder;
115                 fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord);
116                 fb->codeAppendf("coord = floor(coord * 4) / 3;");
117                 fb->codeAppendf("return half2(coord).rg01;\n");
118             }
119         };
120 
121         return std::make_unique<Impl>();
122     }
123 };
124 
make_test_bitmap()125 SkBitmap make_test_bitmap() {
126     SkBitmap bitmap;
127     bitmap.allocN32Pixels(64, 64);
128     SkCanvas canvas(bitmap);
129 
130     SkFont font(ToolUtils::create_portable_typeface());
131     const char* alpha = "ABCDEFGHIJKLMNOP";
132 
133     for (int i = 0; i < 16; ++i) {
134         int tx = i % 4,
135             ty = i / 4;
136         int x = tx * 16,
137             y = ty * 16;
138         SkPaint paint;
139         paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f });
140         canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint);
141         paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f });
142         canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint);
143     }
144 
145     return bitmap;
146 }
147 
148 enum EffectType {
149     kUniform,
150     kExplicit,
151     kDevice,
152 };
153 
wrap(std::unique_ptr<GrFragmentProcessor> fp,EffectType effectType,int drawX,int drawY)154 static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp,
155                                                  EffectType effectType,
156                                                  int drawX, int drawY) {
157     switch (effectType) {
158         case kUniform:
159             return std::make_unique<UniformMatrixEffect>(std::move(fp));
160         case kExplicit:
161             return std::make_unique<ExplicitCoordEffect>(std::move(fp));
162         case kDevice:
163             // Subtract out upper-left corner of draw so that device is effectively identity.
164             fp = GrMatrixEffect::Make(SkMatrix::Translate(-drawX, -drawY), std::move(fp));
165             return GrFragmentProcessor::DeviceSpace(std::move(fp));
166     }
167     SkUNREACHABLE;
168 }
169 
170 } // namespace
171 
172 namespace skiagm {
173 
174 DEF_SIMPLE_GPU_GM_CAN_FAIL(fp_sample_chaining, rContext, canvas, errorMsg, 232, 306) {
175     auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
176     if (!sdc) {
177         *errorMsg = GM::kErrorMsg_DrawSkippedGpuOnly;
178         return DrawResult::kSkip;
179     }
180 
181     SkBitmap bmp = make_test_bitmap();
182 
183     int x = 10, y = 10;
184 
__anonecd163110202null185     auto nextCol = [&] { x += (64 + 10); };
__anonecd163110302null186     auto nextRow = [&] { x = 10; y += (64 + 10); };
187 
__anonecd163110402(std::initializer_list<EffectType> effects) 188     auto draw = [&](std::initializer_list<EffectType> effects) {
189         // Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice
190         // visually (no text labels in each box), but it avoids the extra GrMatrixEffect.
191         // Switching it on actually triggers *more* shader compilation failures.
192 #if 0
193         auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect());
194 #else
195         auto view = std::get<0>(GrMakeCachedBitmapProxyView(rContext, bmp, GrMipmapped::kNo));
196         auto fp = GrTextureEffect::Make(std::move(view), bmp.alphaType());
197 #endif
198         for (EffectType effectType : effects) {
199             fp = wrap(std::move(fp), effectType, x, y);
200         }
201         GrPaint paint;
202         paint.setColorFragmentProcessor(std::move(fp));
203         sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y),
204                       SkRect::MakeIWH(64, 64));
205         nextCol();
206     };
207 
208     // Reminder, in every case, the chain is more complicated than it seems, because the
209     // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
210     // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
211 
212     // First row: no transform, then each one independently applied
213     draw({});             // Identity (4 rows and columns)
214     draw({ kUniform  });  // Scale Y axis by 2x (2 visible rows)
215     draw({ kExplicit });  // Translate up by 8px
216     nextRow();
217 
218     // Second row: transform duplicated
219     draw({ kUniform,  kUniform  });  // Scale Y axis by 4x (1 visible row)
220     draw({ kExplicit, kExplicit });  // Translate up by 16px
221     nextRow();
222 
223     // Third row: Remember, these are applied inside out:
224     draw({ kUniform,  kExplicit }); // Scale Y by 2x and translate up by 8px
225     draw({ kExplicit, kUniform });  // Scale Y by 2x and translate up by 16px
226     nextRow();
227 
228     // Fourth row: device space.
229     draw({ kDevice, kUniform });                     // Same as identity (uniform applied *before*
230                                                      // device so ignored).
231     draw({ kExplicit, kUniform, kDevice });          // Scale Y by 2x and translate up by 16px
232     draw({ kDevice, kExplicit, kUniform, kDevice }); // Identity, again.
233 
234     return DrawResult::kOk;
235 }
236 
237 } // namespace skiagm
238