| Index: tests/GLProgramsTest.cpp | 
| diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp | 
| index ce86c9bf38a7ff1807f07f18cab681edaa3fa521..ba64a7c9cb12f34d2943c2fc748679f25308a24f 100644 | 
| --- a/tests/GLProgramsTest.cpp | 
| +++ b/tests/GLProgramsTest.cpp | 
| @@ -47,7 +47,11 @@ public: | 
| 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 @@ const GrFragmentProcessor* BigKeyProcessor::TestCreate(GrProcessorTestData*) { | 
| 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 GrRenderTarget* random_render_target(GrTextureProvider* textureProvider, | 
| } | 
|  | 
| 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 @@ static const GrFragmentProcessor* create_random_proc_tree(GrProcessorTestData* d | 
| 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 @@ static void set_random_color_coverage_stages(GrPipelineBuilder* pipelineBuilder, | 
|  | 
| 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 @@ bool GrDrawTarget::programUnitTest(GrContext* context, int maxStages) { | 
|  | 
| 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; | 
| } | 
|  | 
|  |