blob: 4ac66137ac166a0baa9d38ce476e9a3f066eca66 [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"
12#include "src/gpu/GrContextPriv.h"
13#include "src/gpu/GrRenderTargetContextPriv.h"
14#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15#include "src/gpu/ops/GrFillRectOp.h"
16#include "tools/ToolUtils.h"
17
18// Samples child with a constant (literal) matrix
19// Scales along X
20class ConstantMatrixEffect : public GrFragmentProcessor {
21public:
22 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 3;
23
24 ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
25 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
26 this->registerChild(std::move(child),
Brian Osman1298bc42020-06-30 13:39:35 -040027 SkSL::SampleUsage::UniformMatrix(
Michael Ludwige88320b2020-06-24 09:04:56 -040028 "float3x3(float3(0.5, 0.0, 0.0), "
29 "float3(0.0, 1.0, 0.0), "
30 "float3(0.0, 0.0, 1.0))"));
31 }
32
33 const char* name() const override { return "ConstantMatrixEffect"; }
34 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
35 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
36 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
37
38 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
39 class Impl : public GrGLSLFragmentProcessor {
40 void emitCode(EmitArgs& args) override {
41 SkString sample = this->invokeChildWithMatrix(0, args);
42 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
43 }
44 };
45 return new Impl;
46 }
47};
48
49// Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
50// Scales along Y
51class UniformMatrixEffect : public GrFragmentProcessor {
52public:
53 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
54
55 UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
56 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Brian Osman1298bc42020-06-30 13:39:35 -040057 this->registerChild(std::move(child), SkSL::SampleUsage::UniformMatrix("matrix"));
Michael Ludwige88320b2020-06-24 09:04:56 -040058 }
59
60 const char* name() const override { return "UniformMatrixEffect"; }
61 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
62 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
63 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
64
65 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
66 class Impl : public GrGLSLFragmentProcessor {
67 void emitCode(EmitArgs& args) override {
68 fMatrixVar = args.fUniformHandler->addUniform(&args.fFp, kFragment_GrShaderFlag,
69 kFloat3x3_GrSLType, "matrix");
70 SkString sample = this->invokeChildWithMatrix(0, args);
71 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
72 }
73 void onSetData(const GrGLSLProgramDataManager& pdman,
74 const GrFragmentProcessor& proc) override {
75 pdman.setSkMatrix(fMatrixVar, SkMatrix::Scale(1, 0.5f));
76 }
77 UniformHandle fMatrixVar;
78 };
79 return new Impl;
80 }
81};
82
83// Samples child with a variable matrix
84// Translates along X
85// Typically, kVariable would be due to multiple sample(matrix) invocations, but this artificially
86// uses kVariable with a single (constant) matrix.
87class VariableMatrixEffect : public GrFragmentProcessor {
88public:
89 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 5;
90
91 VariableMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
92 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Brian Osman1298bc42020-06-30 13:39:35 -040093 this->registerChild(std::move(child), SkSL::SampleUsage::VariableMatrix());
Michael Ludwige88320b2020-06-24 09:04:56 -040094 }
95
96 const char* name() const override { return "VariableMatrixEffect"; }
97 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
98 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
99 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
100
101 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
102 class Impl : public GrGLSLFragmentProcessor {
103 void emitCode(EmitArgs& args) override {
104 SkString sample = this->invokeChildWithMatrix(
105 0, args, "float3x3(1, 0, 0, 0, 1, 0, 8, 0, 1)");
106 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
107 }
108 };
109 return new Impl;
110 }
111};
112
113// Samples child with explicit coords
114// Translates along Y
115class ExplicitCoordEffect : public GrFragmentProcessor {
116public:
117 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
118
119 ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
120 : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Brian Osman1298bc42020-06-30 13:39:35 -0400121 this->registerChild(std::move(child), SkSL::SampleUsage::Explicit());
Michael Ludwige88320b2020-06-24 09:04:56 -0400122 this->setUsesSampleCoordsDirectly();
123 }
124
125 const char* name() const override { return "ExplicitCoordEffect"; }
126 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
127 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
128 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
129
130 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
131 class Impl : public GrGLSLFragmentProcessor {
132 void emitCode(EmitArgs& args) override {
133 args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);",
134 args.fSampleCoord);
135 SkString sample = this->invokeChild(0, args, "coord");
136 args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
137 }
138 };
139 return new Impl;
140 }
141};
142
143// Generates test pattern
144class TestPatternEffect : public GrFragmentProcessor {
145public:
146 static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
147
148 TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
Michael Ludwigfbe28592020-06-26 16:02:15 -0400149 this->setUsesSampleCoordsDirectly();
Michael Ludwige88320b2020-06-24 09:04:56 -0400150 }
151
152 const char* name() const override { return "TestPatternEffect"; }
153 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
154 bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
155 std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
156
157 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
158 class Impl : public GrGLSLFragmentProcessor {
159 void emitCode(EmitArgs& args) override {
160 auto fb = args.fFragBuilder;
161 fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord);
162 fb->codeAppendf("coord = floor(coord * 4) / 3;");
163 fb->codeAppendf("%s = half4(half2(coord.rg), 0, 1);\n", args.fOutputColor);
164 }
165 };
166 return new Impl;
167 }
Michael Ludwige88320b2020-06-24 09:04:56 -0400168};
169
170SkBitmap make_test_bitmap() {
171 SkBitmap bitmap;
172 bitmap.allocN32Pixels(64, 64);
173 SkCanvas canvas(bitmap);
174
175 SkFont font(ToolUtils::create_portable_typeface());
176 const char* alpha = "ABCDEFGHIJKLMNOP";
177
178 for (int i = 0; i < 16; ++i) {
179 int tx = i % 4,
180 ty = i / 4;
181 int x = tx * 16,
182 y = ty * 16;
183 SkPaint paint;
184 paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f });
185 canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint);
186 paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f });
187 canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint);
188 }
189
190 return bitmap;
191}
192
193enum EffectType {
194 kConstant,
195 kUniform,
196 kVariable,
197 kExplicit,
198};
199
200static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp,
201 EffectType effectType) {
202 switch (effectType) {
203 case kConstant:
204 return std::unique_ptr<GrFragmentProcessor>(new ConstantMatrixEffect(std::move(fp)));
205 case kUniform:
206 return std::unique_ptr<GrFragmentProcessor>(new UniformMatrixEffect(std::move(fp)));
207 case kVariable:
208 return std::unique_ptr<GrFragmentProcessor>(new VariableMatrixEffect(std::move(fp)));
209 case kExplicit:
210 return std::unique_ptr<GrFragmentProcessor>(new ExplicitCoordEffect(std::move(fp)));
211 }
212 SkUNREACHABLE;
213}
214
215DEF_SIMPLE_GPU_GM(fp_sample_chaining, ctx, rtCtx, canvas, 380, 306) {
216 SkBitmap bmp = make_test_bitmap();
217
218 GrBitmapTextureMaker maker(ctx, bmp, GrImageTexGenPolicy::kDraw);
219 int x = 10, y = 10;
220
221 auto nextCol = [&] { x += (64 + 10); };
222 auto nextRow = [&] { x = 10; y += (64 + 10); };
223
224 auto draw = [&](std::initializer_list<EffectType> effects) {
225 // Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice
226 // visually (no text labels in each box), but it avoids the extra GrMatrixEffect.
227 // Switching it on actually triggers *more* shader compilation failures.
228#if 0
229 auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect());
230#else
231 auto view = maker.view(GrMipMapped::kNo);
232 auto fp = GrTextureEffect::Make(std::move(view), maker.alphaType());
233#endif
234 for (EffectType effectType : effects) {
235 fp = wrap(std::move(fp), effectType);
236 }
237 GrPaint paint;
238 paint.addColorFragmentProcessor(std::move(fp));
239 rtCtx->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y),
240 SkRect::MakeIWH(64, 64));
241 nextCol();
242 };
243
244 // Reminder, in every case, the chain is more complicated than it seems, because the
245 // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
246 // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
247
248 // First row: no transform, then each one independently applied
249 draw({}); // Identity (4 rows and columns)
250 draw({ kConstant }); // Scale X axis by 2x (2 visible columns)
251 draw({ kUniform }); // Scale Y axis by 2x (2 visible rows)
252 draw({ kVariable }); // Translate left by 8px
253 draw({ kExplicit }); // Translate up by 8px
254 nextRow();
255
256 // Second row: transform duplicated
257 draw({ kConstant, kUniform }); // Scale XY by 2x (2 rows and columns)
258 draw({ kConstant, kConstant }); // Scale X axis by 4x (1 visible column)
259 draw({ kUniform, kUniform }); // Scale Y axis by 4x (1 visible row)
260 draw({ kVariable, kVariable }); // Translate left by 16px
261 draw({ kExplicit, kExplicit }); // Translate up by 16px
262 nextRow();
263
264 // Remember, these are applied inside out:
265 draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
266 draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
267 draw({ kUniform, kVariable }); // Scale Y by 2x and translate left by 8px
268 draw({ kUniform, kExplicit }); // Scale Y by 2x and translate up by 8px
269 draw({ kVariable, kExplicit }); // Translate left and up by 8px
270 nextRow();
271
272 draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
273 draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
274 draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
275 draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
276 draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
277}
Brian Osmanbe1b8372020-06-18 13:40:26 -0400278
279const char* gConstantMatrixSkSL = R"(
280 in shader child;
281 void main(float2 xy, inout half4 color) {
282 color = sample(child, float3x3(0.5, 0.0, 0.0,
283 0.0, 1.0, 0.0,
284 0.0, 0.0, 1.0));
285 }
286)";
287
288const char* gUniformMatrixSkSL = R"(
289 in shader child;
290 uniform float3x3 matrix;
291 void main(float2 xy, inout half4 color) {
292 color = sample(child, matrix);
293 }
294)";
295
296// This form (uniform * constant) is currently detected as variable, thanks to our limited analysis
Brian Osman10b75412020-06-29 16:48:46 -0400297// when scanning for sample matrices. With that pulled into a separate local, it's highly unlikely
298// we'll ever treat this as anything else.
Brian Osmanbe1b8372020-06-18 13:40:26 -0400299const char* gVariableMatrixSkSL = R"(
300 in shader child;
301 uniform float3x3 matrix;
302 void main(float2 xy, inout half4 color) {
Brian Osman10b75412020-06-29 16:48:46 -0400303 float3x3 varMatrix = matrix * 0.5;
304 color = sample(child, varMatrix);
Brian Osmanbe1b8372020-06-18 13:40:26 -0400305 }
306)";
307
308const char* gExplicitCoordSkSL = R"(
309 in shader child;
310 void main(float2 xy, inout half4 color) {
311 color = sample(child, xy + float2(0, 8));
312 }
313)";
314
315// Version of fp_sample_chaining that uses SkRuntimeEffect
316DEF_SIMPLE_GM(sksl_sample_chaining, canvas, 380, 306) {
317 SkBitmap bmp = make_test_bitmap();
318
319 sk_sp<SkRuntimeEffect> effects[4] = {
320 std::get<0>(SkRuntimeEffect::Make(SkString(gConstantMatrixSkSL))),
321 std::get<0>(SkRuntimeEffect::Make(SkString(gUniformMatrixSkSL))),
322 std::get<0>(SkRuntimeEffect::Make(SkString(gVariableMatrixSkSL))),
323 std::get<0>(SkRuntimeEffect::Make(SkString(gExplicitCoordSkSL))),
324 };
325
326 canvas->translate(10, 10);
327 canvas->save();
328 auto nextCol = [&] { canvas->translate(64 + 10, 0); };
329 auto nextRow = [&] { canvas->restore(); canvas->translate(0, 64 + 10); canvas->save(); };
330
331 auto draw = [&](std::initializer_list<EffectType> effectTypes) {
332 auto shader = bmp.makeShader();
333
334 for (EffectType effectType : effectTypes) {
335 SkRuntimeShaderBuilder builder(effects[effectType]);
336 builder.child("child") = shader;
337 switch (effectType) {
338 case kUniform:
339 builder.input("matrix") = SkMatrix::Scale(1.0f, 0.5f);
340 break;
341 case kVariable:
342 builder.input("matrix") = SkMatrix::Translate(8, 0);
343 break;
344 default:
345 break;
346 }
347 shader = builder.makeShader(nullptr, true);
348 }
349 SkPaint paint;
350 paint.setShader(shader);
351 canvas->drawRect(SkRect::MakeWH(64, 64), paint);
352 nextCol();
353 };
354
355 // Reminder, in every case, the chain is more complicated than it seems, because the
356 // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
357 // we're testing (particularly the bug about owner/base in UniformMatrixEffect).
358
359 // First row: no transform, then each one independently applied
360 draw({}); // Identity (4 rows and columns)
361 draw({ kConstant }); // Scale X axis by 2x (2 visible columns)
362 draw({ kUniform }); // Scale Y axis by 2x (2 visible rows)
363 draw({ kVariable }); // Translate left by 8px
364 draw({ kExplicit }); // Translate up by 8px
365 nextRow();
366
367 // Second row: transform duplicated
368 draw({ kConstant, kUniform }); // Scale XY by 2x (2 rows and columns)
369 draw({ kConstant, kConstant }); // Scale X axis by 4x (1 visible column)
370 draw({ kUniform, kUniform }); // Scale Y axis by 4x (1 visible row)
371 draw({ kVariable, kVariable }); // Translate left by 16px
372 draw({ kExplicit, kExplicit }); // Translate up by 16px
373 nextRow();
374
375 // Remember, these are applied inside out:
376 draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
377 draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
378 draw({ kUniform, kVariable }); // Scale Y by 2x and translate left by 8px
379 draw({ kUniform, kExplicit }); // Scale Y by 2x and translate up by 8px
380 draw({ kVariable, kExplicit }); // Translate left and up by 8px
381 nextRow();
382
383 draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
384 draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
385 draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
386 draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
387 draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
388}