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
21namespace {
22
23// Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
24// Scales along Y
25class UniformMatrixEffect : public GrFragmentProcessor {
26public:
27    inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
28
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
35    const char* name() const override { return "UniformMatrixEffect"; }
36    void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
37    bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
38    std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
39
40    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
66class ExplicitCoordEffect : public GrFragmentProcessor {
67public:
68    inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
69
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
76    const char* name() const override { return "ExplicitCoordEffect"; }
77    void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
78    bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
79    std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
80
81    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
97class TestPatternEffect : public GrFragmentProcessor {
98public:
99    inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
100
101    TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
102        this->setUsesSampleCoordsDirectly();
103    }
104
105    const char* name() const override { return "TestPatternEffect"; }
106    void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
107    bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
108    std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
109
110    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
125SkBitmap 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
148enum EffectType {
149    kUniform,
150    kExplicit,
151    kDevice,
152};
153
154static 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
172namespace skiagm {
173
174DEF_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
185    auto nextCol = [&] { x += (64 + 10); };
186    auto nextRow = [&] { x = 10; y += (64 + 10); };
187
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