Test that GrFragmentProcessors work without input colors.
authorbsalomon <bsalomon@google.com>
Mon, 14 Sep 2015 19:26:33 +0000 (12:26 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 14 Sep 2015 19:26:34 +0000 (12:26 -0700)
Committed: https://skia.googlesource.com/skia/+/72c58e7052af2a0855412ce4b249f977069db751

Review URL: https://codereview.chromium.org/1341853002

include/gpu/GrProcessorUnitTest.h
src/gpu/effects/GrConstColorProcessor.cpp
src/gpu/effects/GrExtractAlphaFragmentProcessor.cpp
src/gpu/effects/GrXfermodeFragmentProcessor.cpp
tests/GLProgramsTest.cpp

index 66ba2396029724925e1214c0f4729b23098e3855..8a17521bfce2bfd6f92f77c992301759de70285f 100644 (file)
@@ -68,10 +68,19 @@ public:
         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);
     }
index 95a4081d4f7df2421435f51cf9d362f7e15c07ee..627139fa413d2f717c7e77cb2149f5060b3b0583 100644 (file)
@@ -19,7 +19,11 @@ public:
         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;
index c5ee9b8571c285c9b44572fe252c343dbf8fcd8b..8f6af65ca77be81034ab62d8c8f906f5b107a435 100644 (file)
@@ -14,9 +14,13 @@ public:
     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:
index 3cb56d07f7ea2ce74d3a5d08f0118781293a3958..c039db343315b6610ee1d4a62048becd2ffa2ba3 100644 (file)
@@ -78,12 +78,12 @@ const GrFragmentProcessor* GrComposeTwoFragmentProcessor::TestCreate(GrProcessor
     // 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 @@ void GrGLComposeTwoFragmentProcessor::emitCode(EmitArgs& args) {
     // 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 @@ void GrGLComposeTwoFragmentProcessor::emitCode(EmitArgs& args) {
     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) {
index ce86c9bf38a7ff1807f07f18cab681edaa3fa521..ba64a7c9cb12f34d2943c2fc748679f25308a24f 100644 (file)
@@ -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;
 }