| /* | |
| * Copyright 2014 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "effects/GrCoverageSetOpXP.h" | |
| #include "GrCaps.h" | |
| #include "GrColor.h" | |
| #include "GrDrawContext.h" | |
| #include "GrPipeline.h" | |
| #include "GrProcessor.h" | |
| #include "GrProcOptInfo.h" | |
| #include "glsl/GrGLSLBlend.h" | |
| #include "glsl/GrGLSLFragmentShaderBuilder.h" | |
| #include "glsl/GrGLSLUniformHandler.h" | |
| #include "glsl/GrGLSLXferProcessor.h" | |
| class CoverageSetOpXP : public GrXferProcessor { | |
| public: | |
| static GrXferProcessor* Create(SkRegion::Op regionOp, bool invertCoverage) { | |
| return new CoverageSetOpXP(regionOp, invertCoverage); | |
| } | |
| ~CoverageSetOpXP() override; | |
| const char* name() const override { return "Coverage Set Op"; } | |
| GrGLSLXferProcessor* createGLSLInstance() const override; | |
| bool invertCoverage() const { return fInvertCoverage; } | |
| private: | |
| CoverageSetOpXP(SkRegion::Op regionOp, bool fInvertCoverage); | |
| GrXferProcessor::OptFlags onGetOptimizations(const GrPipelineOptimizations& optimizations, | |
| bool doesStencilWrite, | |
| GrColor* color, | |
| const GrCaps& caps) const override; | |
| void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; | |
| void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override; | |
| bool onIsEqual(const GrXferProcessor& xpBase) const override { | |
| const CoverageSetOpXP& xp = xpBase.cast<CoverageSetOpXP>(); | |
| return (fRegionOp == xp.fRegionOp && | |
| fInvertCoverage == xp.fInvertCoverage); | |
| } | |
| SkRegion::Op fRegionOp; | |
| bool fInvertCoverage; | |
| typedef GrXferProcessor INHERITED; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| class GLCoverageSetOpXP : public GrGLSLXferProcessor { | |
| public: | |
| GLCoverageSetOpXP(const GrProcessor&) {} | |
| ~GLCoverageSetOpXP() override {} | |
| static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, | |
| GrProcessorKeyBuilder* b) { | |
| const CoverageSetOpXP& xp = processor.cast<CoverageSetOpXP>(); | |
| uint32_t key = xp.invertCoverage() ? 0x0 : 0x1; | |
| b->add32(key); | |
| }; | |
| private: | |
| void emitOutputsForBlendState(const EmitArgs& args) override { | |
| const CoverageSetOpXP& xp = args.fXP.cast<CoverageSetOpXP>(); | |
| GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder; | |
| if (xp.invertCoverage()) { | |
| fragBuilder->codeAppendf("%s = 1.0 - %s;", args.fOutputPrimary, args.fInputCoverage); | |
| } else { | |
| fragBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage); | |
| } | |
| } | |
| void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) override {}; | |
| typedef GrGLSLXferProcessor INHERITED; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| CoverageSetOpXP::CoverageSetOpXP(SkRegion::Op regionOp, bool invertCoverage) | |
| : fRegionOp(regionOp) | |
| , fInvertCoverage(invertCoverage) { | |
| this->initClassID<CoverageSetOpXP>(); | |
| } | |
| CoverageSetOpXP::~CoverageSetOpXP() { | |
| } | |
| void CoverageSetOpXP::onGetGLSLProcessorKey(const GrGLSLCaps& caps, | |
| GrProcessorKeyBuilder* b) const { | |
| GLCoverageSetOpXP::GenKey(*this, caps, b); | |
| } | |
| GrGLSLXferProcessor* CoverageSetOpXP::createGLSLInstance() const { | |
| return new GLCoverageSetOpXP(*this); | |
| } | |
| GrXferProcessor::OptFlags | |
| CoverageSetOpXP::onGetOptimizations(const GrPipelineOptimizations& optimizations, | |
| bool doesStencilWrite, | |
| GrColor* color, | |
| const GrCaps& caps) const { | |
| // We never look at the color input | |
| return GrXferProcessor::kIgnoreColor_OptFlag; | |
| } | |
| void CoverageSetOpXP::onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const { | |
| switch (fRegionOp) { | |
| case SkRegion::kReplace_Op: | |
| blendInfo->fSrcBlend = kOne_GrBlendCoeff; | |
| blendInfo->fDstBlend = kZero_GrBlendCoeff; | |
| break; | |
| case SkRegion::kIntersect_Op: | |
| blendInfo->fSrcBlend = kDC_GrBlendCoeff; | |
| blendInfo->fDstBlend = kZero_GrBlendCoeff; | |
| break; | |
| case SkRegion::kUnion_Op: | |
| blendInfo->fSrcBlend = kOne_GrBlendCoeff; | |
| blendInfo->fDstBlend = kISC_GrBlendCoeff; | |
| break; | |
| case SkRegion::kXOR_Op: | |
| blendInfo->fSrcBlend = kIDC_GrBlendCoeff; | |
| blendInfo->fDstBlend = kISC_GrBlendCoeff; | |
| break; | |
| case SkRegion::kDifference_Op: | |
| blendInfo->fSrcBlend = kZero_GrBlendCoeff; | |
| blendInfo->fDstBlend = kISC_GrBlendCoeff; | |
| break; | |
| case SkRegion::kReverseDifference_Op: | |
| blendInfo->fSrcBlend = kIDC_GrBlendCoeff; | |
| blendInfo->fDstBlend = kZero_GrBlendCoeff; | |
| break; | |
| } | |
| blendInfo->fBlendConstant = 0; | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| class ShaderCSOXferProcessor : public GrXferProcessor { | |
| public: | |
| ShaderCSOXferProcessor(const DstTexture* dstTexture, | |
| bool hasMixedSamples, | |
| SkRegion::Op regionOp, | |
| bool invertCoverage) | |
| : INHERITED(dstTexture, true, hasMixedSamples) | |
| , fRegionOp(regionOp) | |
| , fInvertCoverage(invertCoverage) { | |
| this->initClassID<ShaderCSOXferProcessor>(); | |
| } | |
| const char* name() const override { return "Coverage Set Op Shader"; } | |
| GrGLSLXferProcessor* createGLSLInstance() const override; | |
| SkRegion::Op regionOp() const { return fRegionOp; } | |
| bool invertCoverage() const { return fInvertCoverage; } | |
| private: | |
| GrXferProcessor::OptFlags onGetOptimizations(const GrPipelineOptimizations&, bool, GrColor*, | |
| const GrCaps&) const override { | |
| // We never look at the color input | |
| return GrXferProcessor::kIgnoreColor_OptFlag; | |
| } | |
| void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; | |
| bool onIsEqual(const GrXferProcessor& xpBase) const override { | |
| const ShaderCSOXferProcessor& xp = xpBase.cast<ShaderCSOXferProcessor>(); | |
| return (fRegionOp == xp.fRegionOp && | |
| fInvertCoverage == xp.fInvertCoverage); | |
| } | |
| SkRegion::Op fRegionOp; | |
| bool fInvertCoverage; | |
| typedef GrXferProcessor INHERITED; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| class GLShaderCSOXferProcessor : public GrGLSLXferProcessor { | |
| public: | |
| static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) { | |
| const ShaderCSOXferProcessor& xp = processor.cast<ShaderCSOXferProcessor>(); | |
| b->add32(xp.regionOp()); | |
| uint32_t key = xp.invertCoverage() ? 0x0 : 0x1; | |
| b->add32(key); | |
| } | |
| private: | |
| void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder, | |
| GrGLSLUniformHandler* uniformHandler, | |
| const char* srcColor, | |
| const char* srcCoverage, | |
| const char* dstColor, | |
| const char* outColor, | |
| const char* outColorSecondary, | |
| const GrXferProcessor& proc) override { | |
| const ShaderCSOXferProcessor& xp = proc.cast<ShaderCSOXferProcessor>(); | |
| if (xp.invertCoverage()) { | |
| fragBuilder->codeAppendf("%s = 1.0 - %s;", outColor, srcCoverage); | |
| } else { | |
| fragBuilder->codeAppendf("%s = %s;", outColor, srcCoverage); | |
| } | |
| GrGLSLBlend::AppendRegionOp(fragBuilder, outColor, dstColor, outColor, xp.regionOp()); | |
| } | |
| void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) override {} | |
| typedef GrGLSLXferProcessor INHERITED; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| void ShaderCSOXferProcessor::onGetGLSLProcessorKey(const GrGLSLCaps&, | |
| GrProcessorKeyBuilder* b) const { | |
| GLShaderCSOXferProcessor::GenKey(*this, b); | |
| } | |
| GrGLSLXferProcessor* ShaderCSOXferProcessor::createGLSLInstance() const { | |
| return new GLShaderCSOXferProcessor; | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // | |
| GrCoverageSetOpXPFactory::GrCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage) | |
| : fRegionOp(regionOp) | |
| , fInvertCoverage(invertCoverage) { | |
| this->initClassID<GrCoverageSetOpXPFactory>(); | |
| } | |
| sk_sp<GrXPFactory> GrCoverageSetOpXPFactory::Make(SkRegion::Op regionOp, bool invertCoverage) { | |
| switch (regionOp) { | |
| case SkRegion::kReplace_Op: { | |
| if (invertCoverage) { | |
| static GrCoverageSetOpXPFactory gReplaceCDXPFI(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gReplaceCDXPFI)); | |
| } else { | |
| static GrCoverageSetOpXPFactory gReplaceCDXPF(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gReplaceCDXPF)); | |
| } | |
| break; | |
| } | |
| case SkRegion::kIntersect_Op: { | |
| if (invertCoverage) { | |
| static GrCoverageSetOpXPFactory gIntersectCDXPFI(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gIntersectCDXPFI)); | |
| } else { | |
| static GrCoverageSetOpXPFactory gIntersectCDXPF(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gIntersectCDXPF)); | |
| } | |
| break; | |
| } | |
| case SkRegion::kUnion_Op: { | |
| if (invertCoverage) { | |
| static GrCoverageSetOpXPFactory gUnionCDXPFI(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gUnionCDXPFI)); | |
| } else { | |
| static GrCoverageSetOpXPFactory gUnionCDXPF(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gUnionCDXPF)); | |
| } | |
| break; | |
| } | |
| case SkRegion::kXOR_Op: { | |
| if (invertCoverage) { | |
| static GrCoverageSetOpXPFactory gXORCDXPFI(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gXORCDXPFI)); | |
| } else { | |
| static GrCoverageSetOpXPFactory gXORCDXPF(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gXORCDXPF)); | |
| } | |
| break; | |
| } | |
| case SkRegion::kDifference_Op: { | |
| if (invertCoverage) { | |
| static GrCoverageSetOpXPFactory gDifferenceCDXPFI(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gDifferenceCDXPFI)); | |
| } else { | |
| static GrCoverageSetOpXPFactory gDifferenceCDXPF(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gDifferenceCDXPF)); | |
| } | |
| break; | |
| } | |
| case SkRegion::kReverseDifference_Op: { | |
| if (invertCoverage) { | |
| static GrCoverageSetOpXPFactory gRevDiffCDXPFI(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gRevDiffCDXPFI)); | |
| } else { | |
| static GrCoverageSetOpXPFactory gRevDiffCDXPF(regionOp, invertCoverage); | |
| return sk_sp<GrXPFactory>(SkRef(&gRevDiffCDXPF)); | |
| } | |
| break; | |
| } | |
| default: | |
| return nullptr; | |
| } | |
| } | |
| GrXferProcessor* | |
| GrCoverageSetOpXPFactory::onCreateXferProcessor(const GrCaps& caps, | |
| const GrPipelineOptimizations& optimizations, | |
| bool hasMixedSamples, | |
| const DstTexture* dst) const { | |
| // We don't support inverting coverage with mixed samples. We don't expect to ever want this in | |
| // the future, however we could at some point make this work using an inverted coverage | |
| // modulation table. Note that an inverted table still won't work if there are coverage procs. | |
| if (fInvertCoverage && hasMixedSamples) { | |
| SkASSERT(false); | |
| return nullptr; | |
| } | |
| if (optimizations.fOverrides.fUsePLSDstRead) { | |
| return new ShaderCSOXferProcessor(dst, hasMixedSamples, fRegionOp, fInvertCoverage); | |
| } | |
| return CoverageSetOpXP::Create(fRegionOp, fInvertCoverage); | |
| } | |
| void GrCoverageSetOpXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI, | |
| InvariantBlendedColor* blendedColor) const { | |
| blendedColor->fWillBlendWithDst = SkRegion::kReplace_Op != fRegionOp; | |
| blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags; | |
| } | |
| GR_DEFINE_XP_FACTORY_TEST(GrCoverageSetOpXPFactory); | |
| sk_sp<GrXPFactory> GrCoverageSetOpXPFactory::TestCreate(GrProcessorTestData* d) { | |
| SkRegion::Op regionOp = SkRegion::Op(d->fRandom->nextULessThan(SkRegion::kLastOp + 1)); | |
| bool invertCoverage = !d->fDrawContext->hasMixedSamples() && d->fRandom->nextBool(); | |
| return GrCoverageSetOpXPFactory::Make(regionOp, invertCoverage); | |
| } |