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
__anon83543c1a0102null186 auto nextCol = [&] { x += (64 + 10); };
__anon83543c1a0202null187 auto nextRow = [&] { x = 10; y += (64 + 10); };
188
__anon83543c1a0302(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