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