|  | /* | 
|  | * Copyright 2019 Google LLC. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "gm/gm.h" | 
|  | #include "include/core/SkBitmap.h" | 
|  | #include "include/core/SkFont.h" | 
|  | #include "include/effects/SkRuntimeEffect.h" | 
|  | #include "src/core/SkCanvasPriv.h" | 
|  | #include "src/gpu/ganesh/GrDirectContextPriv.h" | 
|  | #include "src/gpu/ganesh/GrPaint.h" | 
|  | #include "src/gpu/ganesh/SkGr.h" | 
|  | #include "src/gpu/ganesh/effects/GrMatrixEffect.h" | 
|  | #include "src/gpu/ganesh/effects/GrTextureEffect.h" | 
|  | #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h" | 
|  | #include "src/gpu/ganesh/v1/SurfaceDrawContext_v1.h" | 
|  | #include "tools/ToolUtils.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Samples child with a uniform matrix (functionally identical to GrMatrixEffect) | 
|  | // Scales along Y | 
|  | class UniformMatrixEffect : public GrFragmentProcessor { | 
|  | public: | 
|  | inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4; | 
|  |  | 
|  | UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child) | 
|  | : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) { | 
|  | this->registerChild(std::move(child), | 
|  | SkSL::SampleUsage::UniformMatrix(/*hasPerspective=*/false)); | 
|  | } | 
|  |  | 
|  | const char* name() const override { return "UniformMatrixEffect"; } | 
|  | void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {} | 
|  | bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; } | 
|  | std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; } | 
|  |  | 
|  | std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override { | 
|  | class Impl : public ProgramImpl { | 
|  | public: | 
|  | void emitCode(EmitArgs& args) override { | 
|  | fMatrixVar = | 
|  | args.fUniformHandler->addUniform(&args.fFp, | 
|  | kFragment_GrShaderFlag, | 
|  | SkSLType::kFloat3x3, | 
|  | SkSL::SampleUsage::MatrixUniformName()); | 
|  | SkString sample = this->invokeChildWithMatrix(0, args); | 
|  | args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void onSetData(const GrGLSLProgramDataManager& pdman, | 
|  | const GrFragmentProcessor& proc) override { | 
|  | pdman.setSkMatrix(fMatrixVar, SkMatrix::Scale(1, 0.5f)); | 
|  | } | 
|  | UniformHandle fMatrixVar; | 
|  | }; | 
|  | return std::make_unique<Impl>(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Samples child with explicit coords | 
|  | // Translates along Y | 
|  | class ExplicitCoordEffect : public GrFragmentProcessor { | 
|  | public: | 
|  | inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6; | 
|  |  | 
|  | ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child) | 
|  | : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) { | 
|  | this->registerChild(std::move(child), SkSL::SampleUsage::Explicit()); | 
|  | this->setUsesSampleCoordsDirectly(); | 
|  | } | 
|  |  | 
|  | const char* name() const override { return "ExplicitCoordEffect"; } | 
|  | void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {} | 
|  | bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; } | 
|  | std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; } | 
|  |  | 
|  | std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override { | 
|  | class Impl : public ProgramImpl { | 
|  | public: | 
|  | void emitCode(EmitArgs& args) override { | 
|  | args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);", | 
|  | args.fSampleCoord); | 
|  | SkString sample = this->invokeChild(0, args, "coord"); | 
|  | args.fFragBuilder->codeAppendf("return %s;\n", sample.c_str()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | return std::make_unique<Impl>(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Generates test pattern | 
|  | class TestPatternEffect : public GrFragmentProcessor { | 
|  | public: | 
|  | inline static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7; | 
|  |  | 
|  | TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) { | 
|  | this->setUsesSampleCoordsDirectly(); | 
|  | } | 
|  |  | 
|  | const char* name() const override { return "TestPatternEffect"; } | 
|  | void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {} | 
|  | bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; } | 
|  | std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; } | 
|  |  | 
|  | std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override { | 
|  | class Impl : public ProgramImpl { | 
|  | public: | 
|  | void emitCode(EmitArgs& args) override { | 
|  | auto fb = args.fFragBuilder; | 
|  | fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord); | 
|  | fb->codeAppendf("coord = floor(coord * 4) / 3;"); | 
|  | fb->codeAppendf("return half2(coord).rg01;\n"); | 
|  | } | 
|  | }; | 
|  |  | 
|  | return std::make_unique<Impl>(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | SkBitmap make_test_bitmap() { | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(64, 64); | 
|  | SkCanvas canvas(bitmap); | 
|  |  | 
|  | SkFont font(ToolUtils::create_portable_typeface()); | 
|  | const char* alpha = "ABCDEFGHIJKLMNOP"; | 
|  |  | 
|  | for (int i = 0; i < 16; ++i) { | 
|  | int tx = i % 4, | 
|  | ty = i / 4; | 
|  | int x = tx * 16, | 
|  | y = ty * 16; | 
|  | SkPaint paint; | 
|  | paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f }); | 
|  | canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint); | 
|  | paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f }); | 
|  | canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint); | 
|  | } | 
|  |  | 
|  | return bitmap; | 
|  | } | 
|  |  | 
|  | enum EffectType { | 
|  | kUniform, | 
|  | kExplicit, | 
|  | kDevice, | 
|  | }; | 
|  |  | 
|  | static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp, | 
|  | EffectType effectType, | 
|  | int drawX, int drawY) { | 
|  | switch (effectType) { | 
|  | case kUniform: | 
|  | return std::make_unique<UniformMatrixEffect>(std::move(fp)); | 
|  | case kExplicit: | 
|  | return std::make_unique<ExplicitCoordEffect>(std::move(fp)); | 
|  | case kDevice: | 
|  | // Subtract out upper-left corner of draw so that device is effectively identity. | 
|  | fp = GrMatrixEffect::Make(SkMatrix::Translate(-drawX, -drawY), std::move(fp)); | 
|  | return GrFragmentProcessor::DeviceSpace(std::move(fp)); | 
|  | } | 
|  | SkUNREACHABLE; | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | namespace skiagm { | 
|  |  | 
|  | DEF_SIMPLE_GPU_GM_CAN_FAIL(fp_sample_chaining, rContext, canvas, errorMsg, 232, 306) { | 
|  | auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas); | 
|  | if (!sdc) { | 
|  | *errorMsg = GM::kErrorMsg_DrawSkippedGpuOnly; | 
|  | return DrawResult::kSkip; | 
|  | } | 
|  |  | 
|  | SkBitmap bmp = make_test_bitmap(); | 
|  |  | 
|  | int x = 10, y = 10; | 
|  |  | 
|  | auto nextCol = [&] { x += (64 + 10); }; | 
|  | auto nextRow = [&] { x = 10; y += (64 + 10); }; | 
|  |  | 
|  | auto draw = [&](std::initializer_list<EffectType> effects) { | 
|  | // Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice | 
|  | // visually (no text labels in each box), but it avoids the extra GrMatrixEffect. | 
|  | // Switching it on actually triggers *more* shader compilation failures. | 
|  | #if 0 | 
|  | auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect()); | 
|  | #else | 
|  | auto view = std::get<0>(GrMakeCachedBitmapProxyView(rContext, bmp, GrMipmapped::kNo)); | 
|  | auto fp = GrTextureEffect::Make(std::move(view), bmp.alphaType()); | 
|  | #endif | 
|  | for (EffectType effectType : effects) { | 
|  | fp = wrap(std::move(fp), effectType, x, y); | 
|  | } | 
|  | GrPaint paint; | 
|  | paint.setColorFragmentProcessor(std::move(fp)); | 
|  | sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y), | 
|  | SkRect::MakeIWH(64, 64)); | 
|  | nextCol(); | 
|  | }; | 
|  |  | 
|  | // Reminder, in every case, the chain is more complicated than it seems, because the | 
|  | // GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that | 
|  | // we're testing (particularly the bug about owner/base in UniformMatrixEffect). | 
|  |  | 
|  | // First row: no transform, then each one independently applied | 
|  | draw({});             // Identity (4 rows and columns) | 
|  | draw({ kUniform  });  // Scale Y axis by 2x (2 visible rows) | 
|  | draw({ kExplicit });  // Translate up by 8px | 
|  | nextRow(); | 
|  |  | 
|  | // Second row: transform duplicated | 
|  | draw({ kUniform,  kUniform  });  // Scale Y axis by 4x (1 visible row) | 
|  | draw({ kExplicit, kExplicit });  // Translate up by 16px | 
|  | nextRow(); | 
|  |  | 
|  | // Third row: Remember, these are applied inside out: | 
|  | draw({ kUniform,  kExplicit }); // Scale Y by 2x and translate up by 8px | 
|  | draw({ kExplicit, kUniform });  // Scale Y by 2x and translate up by 16px | 
|  | nextRow(); | 
|  |  | 
|  | // Fourth row: device space. | 
|  | draw({ kDevice, kUniform });                     // Same as identity (uniform applied *before* | 
|  | // device so ignored). | 
|  | draw({ kExplicit, kUniform, kDevice });          // Scale Y by 2x and translate up by 16px | 
|  | draw({ kDevice, kExplicit, kUniform, kDevice }); // Identity, again. | 
|  |  | 
|  | return DrawResult::kOk; | 
|  | } | 
|  |  | 
|  | } // namespace skiagm |