blob: 5af060629b99308f1188c7d87e544952454ad032 [file] [log] [blame]
Michael Ludwige88320b2020-06-24 09:04:56 -04001/*
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"
Brian Osmanbe1b8372020-06-18 13:40:26 -040010#include "include/effects/SkRuntimeEffect.h"
Michael Ludwige88320b2020-06-24 09:04:56 -040011#include "src/gpu/GrBitmapTextureMaker.h"
Adlai Hollera0693042020-10-14 11:23:11 -040012#include "src/gpu/GrDirectContextPriv.h"
Michael Ludwige88320b2020-06-24 09:04:56 -040013#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
19class ConstantMatrixEffect : public GrFragmentProcessor {
20public:
21 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 3;
22
23 ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
24 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
25 this->registerChild(std::move(child),
Brian Osman1298bc42020-06-30 13:39:35 -040026 SkSL::SampleUsage::UniformMatrix(
Michael Ludwige88320b2020-06-24 09:04:56 -040027 "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
32 const char* name() const override { return "ConstantMatrixEffect"; }
33 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
34 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
35 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
36
37 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
38 class Impl : public GrGLSLFragmentProcessor {
39 void emitCode(EmitArgs& args) override {
40 SkString sample = this->invokeChildWithMatrix(0, args);
John Stiles4bdc1212020-12-14 18:04:13 -050041 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
Michael Ludwige88320b2020-06-24 09:04:56 -040042 }
43 };
44 return new Impl;
45 }
46};
47
48// Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
49// Scales along Y
50class UniformMatrixEffect : public GrFragmentProcessor {
51public:
52 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
53
54 UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
55 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Brian Osman1298bc42020-06-30 13:39:35 -040056 this->registerChild(std::move(child), SkSL::SampleUsage::UniformMatrix("matrix"));
Michael Ludwige88320b2020-06-24 09:04:56 -040057 }
58
59 const char* name() const override { return "UniformMatrixEffect"; }
60 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
61 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
62 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
63
64 GrGLSLFragmentProcessor* onCreateGLSLInstance() 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);
John Stiles4bdc1212020-12-14 18:04:13 -050070 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
Michael Ludwige88320b2020-06-24 09:04:56 -040071 }
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 new Impl;
79 }
80};
81
82// Samples child with a variable matrix
83// Translates along X
84// Typically, kVariable would be due to multiple sample(matrix) invocations, but this artificially
85// uses kVariable with a single (constant) matrix.
86class VariableMatrixEffect : public GrFragmentProcessor {
87public:
88 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 5;
89
90 VariableMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
91 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Brian Osman1298bc42020-06-30 13:39:35 -040092 this->registerChild(std::move(child), SkSL::SampleUsage::VariableMatrix());
Michael Ludwige88320b2020-06-24 09:04:56 -040093 }
94
95 const char* name() const override { return "VariableMatrixEffect"; }
96 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
97 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
98 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
99
100 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
101 class Impl : public GrGLSLFragmentProcessor {
102 void emitCode(EmitArgs& args) override {
103 SkString sample = this->invokeChildWithMatrix(
104 0, args, "float3x3(1, 0, 0, 0, 1, 0, 8, 0, 1)");
John Stiles4bdc1212020-12-14 18:04:13 -0500105 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
Michael Ludwige88320b2020-06-24 09:04:56 -0400106 }
107 };
108 return new Impl;
109 }
110};
111
112// Samples child with explicit coords
113// Translates along Y
114class ExplicitCoordEffect : public GrFragmentProcessor {
115public:
116 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
117
118 ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
119 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Brian Osman1298bc42020-06-30 13:39:35 -0400120 this->registerChild(std::move(child), SkSL::SampleUsage::Explicit());
Michael Ludwige88320b2020-06-24 09:04:56 -0400121 this->setUsesSampleCoordsDirectly();
122 }
123
124 const char* name() const override { return "ExplicitCoordEffect"; }
125 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
126 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
127 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
128
129 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
130 class Impl : public GrGLSLFragmentProcessor {
131 void emitCode(EmitArgs& args) override {
132 args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);",
133 args.fSampleCoord);
134 SkString sample = this->invokeChild(0, args, "coord");
John Stiles4bdc1212020-12-14 18:04:13 -0500135 args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str());
Michael Ludwige88320b2020-06-24 09:04:56 -0400136 }
137 };
138 return new Impl;
139 }
140};
141
142// Generates test pattern
143class TestPatternEffect : public GrFragmentProcessor {
144public:
145 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
146
147 TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Michael Ludwigfbe28592020-06-26 16:02:15 -0400148 this->setUsesSampleCoordsDirectly();
Michael Ludwige88320b2020-06-24 09:04:56 -0400149 }
150
151 const char* name() const override { return "TestPatternEffect"; }
152 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
153 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
154 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
155
156 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
157 class Impl : public GrGLSLFragmentProcessor {
158 void emitCode(EmitArgs& args) override {
159 auto fb = args.fFragBuilder;
160 fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord);
161 fb->codeAppendf("coord = floor(coord * 4) / 3;");
John Stiles4bdc1212020-12-14 18:04:13 -0500162 fb->codeAppendf("return half2(coord).rg01;\n");
Michael Ludwige88320b2020-06-24 09:04:56 -0400163 }
164 };
165 return new Impl;
166 }
Michael Ludwige88320b2020-06-24 09:04:56 -0400167};
168
169SkBitmap make_test_bitmap() {
170 SkBitmap bitmap;
171 bitmap.allocN32Pixels(64, 64);
172 SkCanvas canvas(bitmap);
173
174 SkFont font(ToolUtils::create_portable_typeface());
175 const char* alpha = "ABCDEFGHIJKLMNOP";
176
177 for (int i = 0; i < 16; ++i) {
178 int tx = i % 4,
179 ty = i / 4;
180 int x = tx * 16,
181 y = ty * 16;
182 SkPaint paint;
183 paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f });
184 canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint);
185 paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f });
186 canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint);
187 }
188
189 return bitmap;
190}
191
192enum EffectType {
193 kConstant,
194 kUniform,
195 kVariable,
196 kExplicit,
197};
198
199static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp,
200 EffectType effectType) {
201 switch (effectType) {
202 case kConstant:
John Stilesfbd050b2020-08-03 13:21:46 -0400203 return std::make_unique<ConstantMatrixEffect>(std::move(fp));
Michael Ludwige88320b2020-06-24 09:04:56 -0400204 case kUniform:
John Stilesfbd050b2020-08-03 13:21:46 -0400205 return std::make_unique<UniformMatrixEffect>(std::move(fp));
Michael Ludwige88320b2020-06-24 09:04:56 -0400206 case kVariable:
John Stilesfbd050b2020-08-03 13:21:46 -0400207 return std::make_unique<VariableMatrixEffect>(std::move(fp));
Michael Ludwige88320b2020-06-24 09:04:56 -0400208 case kExplicit:
John Stilesfbd050b2020-08-03 13:21:46 -0400209 return std::make_unique<ExplicitCoordEffect>(std::move(fp));
Michael Ludwige88320b2020-06-24 09:04:56 -0400210 }
211 SkUNREACHABLE;
212}
213
214DEF_SIMPLE_GPU_GM(fp_sample_chaining, ctx, rtCtx, canvas, 380, 306) {
215 SkBitmap bmp = make_test_bitmap();
216
217 GrBitmapTextureMaker maker(ctx, bmp, GrImageTexGenPolicy::kDraw);
218 int x = 10, y = 10;
219
220 auto nextCol = [&] { x += (64 + 10); };
221 auto nextRow = [&] { x = 10; y += (64 + 10); };
222
223 auto draw = [&](std::initializer_list<EffectType> effects) {
224 // Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice
225 // visually (no text labels in each box), but it avoids the extra GrMatrixEffect.
226 // Switching it on actually triggers *more* shader compilation failures.
227#if 0
228 auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect());
229#else
Brian Salomon7e67dca2020-07-21 09:27:25 -0400230 auto view = maker.view(GrMipmapped::kNo);
Michael Ludwige88320b2020-06-24 09:04:56 -0400231 auto fp = GrTextureEffect::Make(std::move(view), maker.alphaType());
232#endif
233 for (EffectType effectType : effects) {
234 fp = wrap(std::move(fp), effectType);
235 }
236 GrPaint paint;
John Stiles5933d7d2020-07-21 12:28:35 -0400237 paint.setColorFragmentProcessor(std::move(fp));
Michael Ludwige88320b2020-06-24 09:04:56 -0400238 rtCtx->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y),
239 SkRect::MakeIWH(64, 64));
240 nextCol();
241 };
242
243 // Reminder, in every case, the chain is more complicated than it seems, because the
244 // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
245 // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
246
247 // First row: no transform, then each one independently applied
248 draw({}); // Identity (4 rows and columns)
249 draw({ kConstant }); // Scale X axis by 2x (2 visible columns)
250 draw({ kUniform }); // Scale Y axis by 2x (2 visible rows)
251 draw({ kVariable }); // Translate left by 8px
252 draw({ kExplicit }); // Translate up by 8px
253 nextRow();
254
255 // Second row: transform duplicated
256 draw({ kConstant, kUniform }); // Scale XY by 2x (2 rows and columns)
257 draw({ kConstant, kConstant }); // Scale X axis by 4x (1 visible column)
258 draw({ kUniform, kUniform }); // Scale Y axis by 4x (1 visible row)
259 draw({ kVariable, kVariable }); // Translate left by 16px
260 draw({ kExplicit, kExplicit }); // Translate up by 16px
261 nextRow();
262
263 // Remember, these are applied inside out:
264 draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
265 draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
266 draw({ kUniform, kVariable }); // Scale Y by 2x and translate left by 8px
267 draw({ kUniform, kExplicit }); // Scale Y by 2x and translate up by 8px
268 draw({ kVariable, kExplicit }); // Translate left and up by 8px
269 nextRow();
270
271 draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
272 draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
273 draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
274 draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
275 draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
276}
Brian Osmanbe1b8372020-06-18 13:40:26 -0400277
278const char* gConstantMatrixSkSL = R"(
Brian Osman91292e92020-11-04 15:40:50 -0500279 uniform shader child;
Brian Osman767f4442020-08-13 16:59:48 -0400280 half4 main(float2 xy) {
281 return sample(child, float3x3(0.5, 0.0, 0.0,
282 0.0, 1.0, 0.0,
283 0.0, 0.0, 1.0));
Brian Osmanbe1b8372020-06-18 13:40:26 -0400284 }
285)";
286
287const char* gUniformMatrixSkSL = R"(
Brian Osman91292e92020-11-04 15:40:50 -0500288 uniform shader child;
Brian Osmanbe1b8372020-06-18 13:40:26 -0400289 uniform float3x3 matrix;
Brian Osman767f4442020-08-13 16:59:48 -0400290 half4 main(float2 xy) {
291 return sample(child, matrix);
Brian Osmanbe1b8372020-06-18 13:40:26 -0400292 }
293)";
294
295// This form (uniform * constant) is currently detected as variable, thanks to our limited analysis
Brian Osman10b75412020-06-29 16:48:46 -0400296// when scanning for sample matrices. With that pulled into a separate local, it's highly unlikely
297// we'll ever treat this as anything else.
Brian Osmanbe1b8372020-06-18 13:40:26 -0400298const char* gVariableMatrixSkSL = R"(
Brian Osman91292e92020-11-04 15:40:50 -0500299 uniform shader child;
Brian Osmanbe1b8372020-06-18 13:40:26 -0400300 uniform float3x3 matrix;
Brian Osman767f4442020-08-13 16:59:48 -0400301 half4 main(float2 xy) {
Brian Osman10b75412020-06-29 16:48:46 -0400302 float3x3 varMatrix = matrix * 0.5;
Brian Osman767f4442020-08-13 16:59:48 -0400303 return sample(child, varMatrix);
Brian Osmanbe1b8372020-06-18 13:40:26 -0400304 }
305)";
306
307const char* gExplicitCoordSkSL = R"(
Brian Osman91292e92020-11-04 15:40:50 -0500308 uniform shader child;
Brian Osman767f4442020-08-13 16:59:48 -0400309 half4 main(float2 xy) {
310 return sample(child, xy + float2(0, 8));
Brian Osmanbe1b8372020-06-18 13:40:26 -0400311 }
312)";
313
314// Version of fp_sample_chaining that uses SkRuntimeEffect
315DEF_SIMPLE_GM(sksl_sample_chaining, canvas, 380, 306) {
316 SkBitmap bmp = make_test_bitmap();
317
318 sk_sp<SkRuntimeEffect> effects[4] = {
319 std::get<0>(SkRuntimeEffect::Make(SkString(gConstantMatrixSkSL))),
320 std::get<0>(SkRuntimeEffect::Make(SkString(gUniformMatrixSkSL))),
321 std::get<0>(SkRuntimeEffect::Make(SkString(gVariableMatrixSkSL))),
322 std::get<0>(SkRuntimeEffect::Make(SkString(gExplicitCoordSkSL))),
323 };
324
325 canvas->translate(10, 10);
326 canvas->save();
327 auto nextCol = [&] { canvas->translate(64 + 10, 0); };
328 auto nextRow = [&] { canvas->restore(); canvas->translate(0, 64 + 10); canvas->save(); };
329
330 auto draw = [&](std::initializer_list<EffectType> effectTypes) {
Mike Reedb41bd152020-12-12 11:18:31 -0500331 auto shader = bmp.makeShader(SkSamplingOptions());
Brian Osmanbe1b8372020-06-18 13:40:26 -0400332
333 for (EffectType effectType : effectTypes) {
334 SkRuntimeShaderBuilder builder(effects[effectType]);
335 builder.child("child") = shader;
336 switch (effectType) {
337 case kUniform:
Brian Osmana4b91692020-08-10 14:26:16 -0400338 builder.uniform("matrix") = SkMatrix::Scale(1.0f, 0.5f);
Brian Osmanbe1b8372020-06-18 13:40:26 -0400339 break;
340 case kVariable:
Brian Osmana4b91692020-08-10 14:26:16 -0400341 builder.uniform("matrix") = SkMatrix::Translate(8, 0);
Brian Osmanbe1b8372020-06-18 13:40:26 -0400342 break;
343 default:
344 break;
345 }
346 shader = builder.makeShader(nullptr, true);
347 }
348 SkPaint paint;
349 paint.setShader(shader);
350 canvas->drawRect(SkRect::MakeWH(64, 64), paint);
351 nextCol();
352 };
353
354 // Reminder, in every case, the chain is more complicated than it seems, because the
355 // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
356 // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
357
358 // First row: no transform, then each one independently applied
359 draw({}); // Identity (4 rows and columns)
360 draw({ kConstant }); // Scale X axis by 2x (2 visible columns)
361 draw({ kUniform }); // Scale Y axis by 2x (2 visible rows)
362 draw({ kVariable }); // Translate left by 8px
363 draw({ kExplicit }); // Translate up by 8px
364 nextRow();
365
366 // Second row: transform duplicated
367 draw({ kConstant, kUniform }); // Scale XY by 2x (2 rows and columns)
368 draw({ kConstant, kConstant }); // Scale X axis by 4x (1 visible column)
369 draw({ kUniform, kUniform }); // Scale Y axis by 4x (1 visible row)
370 draw({ kVariable, kVariable }); // Translate left by 16px
371 draw({ kExplicit, kExplicit }); // Translate up by 16px
372 nextRow();
373
374 // Remember, these are applied inside out:
375 draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
376 draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
377 draw({ kUniform, kVariable }); // Scale Y by 2x and translate left by 8px
378 draw({ kUniform, kExplicit }); // Scale Y by 2x and translate up by 8px
379 draw({ kVariable, kExplicit }); // Translate left and up by 8px
380 nextRow();
381
382 draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
383 draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
384 draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
385 draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
386 draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
387}