Test that GrFragmentProcessors work without input colors.

Committed: https://skia.googlesource.com/skia/+/72c58e7052af2a0855412ce4b249f977069db751

Review URL: https://codereview.chromium.org/1341853002
diff --git a/include/gpu/GrProcessorUnitTest.h b/include/gpu/GrProcessorUnitTest.h
index 66ba239..8a17521 100644
--- a/include/gpu/GrProcessorUnitTest.h
+++ b/include/gpu/GrProcessorUnitTest.h
@@ -68,10 +68,19 @@
         GetFactories()->push_back(this);
     }
 
-    static const Processor* CreateStage(GrProcessorTestData* data) {
+    /** Pick a random factory function and create a processor.  */
+    static const Processor* Create(GrProcessorTestData* data) {
         VerifyFactoryCount();
         SkASSERT(GetFactories()->count());
         uint32_t idx = data->fRandom->nextRangeU(0, GetFactories()->count() - 1);
+        return CreateIdx(idx, data);
+    }
+
+    /** Number of registered factory functions */
+    static int Count() { return GetFactories()->count(); }
+
+    /** Use factory function at Index idx to create a processor. */
+    static const Processor* CreateIdx(int idx, GrProcessorTestData* data) {
         GrProcessorTestFactory<Processor>* factory = (*GetFactories())[idx];
         return factory->fCreateProc(data);
     }
diff --git a/src/gpu/effects/GrConstColorProcessor.cpp b/src/gpu/effects/GrConstColorProcessor.cpp
index 95a4081..627139f 100644
--- a/src/gpu/effects/GrConstColorProcessor.cpp
+++ b/src/gpu/effects/GrConstColorProcessor.cpp
@@ -19,7 +19,11 @@
         fColorUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                             kVec4f_GrSLType, kMedium_GrSLPrecision, "constantColor",
                                             &colorUni);
-        switch (args.fFp.cast<GrConstColorProcessor>().inputMode()) {
+        GrConstColorProcessor::InputMode mode = args.fFp.cast<GrConstColorProcessor>().inputMode();
+        if (!args.fInputColor) {
+            mode = GrConstColorProcessor::kIgnore_InputMode;
+        }
+        switch (mode) {
             case GrConstColorProcessor::kIgnore_InputMode:
                 fsBuilder->codeAppendf("%s = %s;", args.fOutputColor, colorUni);
                 break;
diff --git a/src/gpu/effects/GrExtractAlphaFragmentProcessor.cpp b/src/gpu/effects/GrExtractAlphaFragmentProcessor.cpp
index c5ee9b8..8f6af65 100644
--- a/src/gpu/effects/GrExtractAlphaFragmentProcessor.cpp
+++ b/src/gpu/effects/GrExtractAlphaFragmentProcessor.cpp
@@ -14,9 +14,13 @@
     GLExtractAlphaFragmentProcessor() {}
 
     void emitCode(EmitArgs& args) override {
-        GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
-        fsBuilder->codeAppendf("vec4 alpha4 = %s.aaaa;", args.fInputColor);
-        this->emitChild(0, "alpha4", args.fOutputColor, args);
+        if (args.fInputColor) {
+            GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
+            fsBuilder->codeAppendf("vec4 alpha4 = %s.aaaa;", args.fInputColor);
+            this->emitChild(0, "alpha4", args.fOutputColor, args);
+        } else {
+            this->emitChild(0, nullptr, args.fOutputColor, args);
+        }
     }
 
 private:
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index 3cb56d0..c039db3 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -78,12 +78,12 @@
     // possibility of an arbitrarily large tree of procs.
     SkAutoTUnref<const GrFragmentProcessor> fpA;
     do {
-        fpA.reset(GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(d));
+        fpA.reset(GrProcessorTestFactory<GrFragmentProcessor>::Create(d));
         SkASSERT(fpA);
     } while (fpA->numChildProcessors() != 0);
     SkAutoTUnref<const GrFragmentProcessor> fpB;
     do {
-        fpB.reset(GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(d));
+        fpB.reset(GrProcessorTestFactory<GrFragmentProcessor>::Create(d));
         SkASSERT(fpB);
     } while (fpB->numChildProcessors() != 0);
 
@@ -112,20 +112,26 @@
     // This is because we don't want the paint's alpha to affect either child proc's output
     // before the blend; we want to apply the paint's alpha AFTER the blend. This mirrors the
     // software implementation of SkComposeShader.
-    SkString inputAlpha("inputAlpha");
-    fsBuilder->codeAppendf("float %s = %s.a;", inputAlpha.c_str(), args.fInputColor);
-    fsBuilder->codeAppendf("%s /= %s.a;", args.fInputColor, args.fInputColor);
+    const char* opaqueInput = nullptr;
+    const char* inputAlpha = nullptr;
+    if (args.fInputColor) {
+        inputAlpha = "inputAlpha";
+        opaqueInput = "opaqueInput";
+        fsBuilder->codeAppendf("float inputAlpha = %s.a;", args.fInputColor);
+        fsBuilder->codeAppendf("vec4 opaqueInput = vec4(%s.rgb / inputAlpha, 1);",
+                               args.fInputColor);
+    }
 
     // declare outputColor and emit the code for each of the two children
     SkString outputColorSrc(args.fOutputColor);
     outputColorSrc.append("_src");
     fsBuilder->codeAppendf("vec4 %s;\n", outputColorSrc.c_str());
-    this->emitChild(0, args.fInputColor, outputColorSrc.c_str(), args);
+    this->emitChild(0, opaqueInput, outputColorSrc.c_str(), args);
 
     SkString outputColorDst(args.fOutputColor);
     outputColorDst.append("_dst");
     fsBuilder->codeAppendf("vec4 %s;\n", outputColorDst.c_str());
-    this->emitChild(1, args.fInputColor, outputColorDst.c_str(), args);
+    this->emitChild(1, opaqueInput, outputColorDst.c_str(), args);
 
     // emit blend code
     SkXfermode::Mode mode = cs.getMode();
@@ -136,10 +142,11 @@
     fsBuilder->codeAppend("}");
 
     // re-multiply the output color by the input color's alpha
-    fsBuilder->codeAppendf("%s *= %s;", args.fOutputColor, inputAlpha.c_str());
+    if (inputAlpha) {
+        fsBuilder->codeAppendf("%s *= %s;", args.fOutputColor, inputAlpha);
+    }
 }
 
-
 const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromTwoProcessors(
          const GrFragmentProcessor* src, const GrFragmentProcessor* dst, SkXfermode::Mode mode) {
     if (SkXfermode::kLastCoeffMode < mode) {
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index ce86c9b..ba64a7c 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -47,7 +47,11 @@
     virtual void emitCode(EmitArgs& args) override {
         // pass through
         GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
-        fsBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, args.fInputColor);
+        if (args.fInputColor) {
+            fsBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, args.fInputColor);
+        } else {
+            fsBuilder->codeAppendf("%s = vec4(1.0);\n", args.fOutputColor);
+        }
     }
 
     static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
@@ -95,6 +99,49 @@
     return BigKeyProcessor::Create();
 }
 
+//////////////////////////////////////////////////////////////////////////////
+
+class BlockInputFragmentProcessor : public GrFragmentProcessor {
+public:
+    static GrFragmentProcessor* Create(const GrFragmentProcessor* fp) {
+        return new BlockInputFragmentProcessor(fp);
+    }
+
+    const char* name() const override { return "Block Input"; }
+
+    GrGLFragmentProcessor* onCreateGLInstance() const override { return new GLFP; }
+
+private:
+    class GLFP : public GrGLFragmentProcessor {
+    public:
+        void emitCode(EmitArgs& args) override {
+            this->emitChild(0, nullptr, args.fOutputColor, args);
+        }
+
+    private:
+        typedef GrGLFragmentProcessor INHERITED;
+    };
+
+    BlockInputFragmentProcessor(const GrFragmentProcessor* child) {
+        this->initClassID<BlockInputFragmentProcessor>();
+        this->registerChildProcessor(child);
+    }
+
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {}
+
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
+
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
+        inout->setToOther(kRGBA_GrColorComponentFlags, GrColor_WHITE,
+                          GrInvariantOutput::kWillNot_ReadInput);
+        this->childProcessor(0).computeInvariantOutput(inout);
+    }
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
 /*
  * Begin test code
  */
@@ -132,7 +179,7 @@
 }
 
 static void set_random_xpf(GrPipelineBuilder* pipelineBuilder, GrProcessorTestData* d) {
-    SkAutoTUnref<const GrXPFactory> xpf(GrProcessorTestFactory<GrXPFactory>::CreateStage(d));
+    SkAutoTUnref<const GrXPFactory> xpf(GrProcessorTestFactory<GrXPFactory>::Create(d));
     SkASSERT(xpf);
     pipelineBuilder->setXPFactory(xpf.get());
 }
@@ -151,7 +198,7 @@
         if (terminate) {
             const GrFragmentProcessor* fp;
             while (true) {
-                fp = GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(d);
+                fp = GrProcessorTestFactory<GrFragmentProcessor>::Create(d);
                 SkASSERT(fp);
                 if (0 == fp->numChildProcessors()) {
                     break;
@@ -201,7 +248,7 @@
 
         for (int s = 0; s < numProcs;) {
             SkAutoTUnref<const GrFragmentProcessor> fp(
-                    GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(d));
+                    GrProcessorTestFactory<GrFragmentProcessor>::Create(d));
             SkASSERT(fp);
 
             // finally add the stage to the correct pipeline in the drawstate
@@ -309,9 +356,42 @@
 
         this->drawBatch(pipelineBuilder, batch);
     }
-
     // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes)
     this->flush();
+
+    // Validate that GrFPs work correctly without an input.
+    GrSurfaceDesc rtDesc;
+    rtDesc.fWidth = kRenderTargetWidth;
+    rtDesc.fHeight = kRenderTargetHeight;
+    rtDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    rtDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    SkAutoTUnref<GrRenderTarget> rt(
+        fContext->textureProvider()->createTexture(rtDesc, false)->asRenderTarget());
+    int fpFactoryCnt = GrProcessorTestFactory<GrFragmentProcessor>::Count();
+    for (int i = 0; i < fpFactoryCnt; ++i) {
+        // Since FP factories internally randomize, call each 10 times.
+        for (int j = 0; j < 10; ++j) {
+            SkAutoTUnref<GrDrawBatch> batch(GrRandomDrawBatch(&random, context));
+            SkASSERT(batch);
+            GrProcessorDataManager procDataManager;
+            GrProcessorTestData ptd(&random, context, &procDataManager, this->caps(),
+                                    dummyTextures);
+            GrPipelineBuilder builder;
+            builder.setXPFactory(GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref();
+            builder.setRenderTarget(rt);
+            builder.setClip(clip);
+
+            SkAutoTUnref<const GrFragmentProcessor> fp(
+                GrProcessorTestFactory<GrFragmentProcessor>::CreateIdx(i, &ptd));
+            SkAutoTUnref<const GrFragmentProcessor> blockFP(
+                BlockInputFragmentProcessor::Create(fp));
+            builder.addColorFragmentProcessor(blockFP);
+
+            this->drawBatch(builder, batch);
+            this->flush();
+        }
+    }
+
     return true;
 }