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