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;
}