/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/GrXferProcessor.h" #include "src/gpu/GrCaps.h" #include "src/gpu/GrPipeline.h" #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" #include "src/gpu/glsl/GrGLSLProgramDataManager.h" GrXferProcessor::GrXferProcessor(ClassID classID) : INHERITED(classID) , fWillReadDstColor(false) , fIsLCD(false) {} GrXferProcessor::GrXferProcessor(ClassID classID, bool willReadDstColor, GrProcessorAnalysisCoverage coverage) : INHERITED(classID) , fWillReadDstColor(willReadDstColor) , fIsLCD(GrProcessorAnalysisCoverage::kLCD == coverage) {} bool GrXferProcessor::hasSecondaryOutput() const { if (!this->willReadDstColor()) { return this->onHasSecondaryOutput(); } return false; } void GrXferProcessor::addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b, const GrSurfaceOrigin* originIfDstTexture, bool usesInputAttachmentForDstRead) const { uint32_t key = this->willReadDstColor() ? 0x1 : 0x0; if (key) { if (originIfDstTexture) { key |= 0x2; if (kTopLeft_GrSurfaceOrigin == *originIfDstTexture) { key |= 0x4; } if (usesInputAttachmentForDstRead) { key |= 0x8; } } } if (fIsLCD) { key |= 0x10; } b->add32(key); this->onAddToKey(caps, b); } #ifdef SK_DEBUG static const char* equation_string(GrBlendEquation eq) { switch (eq) { case kAdd_GrBlendEquation: return "add"; case kSubtract_GrBlendEquation: return "subtract"; case kReverseSubtract_GrBlendEquation: return "reverse_subtract"; case kScreen_GrBlendEquation: return "screen"; case kOverlay_GrBlendEquation: return "overlay"; case kDarken_GrBlendEquation: return "darken"; case kLighten_GrBlendEquation: return "lighten"; case kColorDodge_GrBlendEquation: return "color_dodge"; case kColorBurn_GrBlendEquation: return "color_burn"; case kHardLight_GrBlendEquation: return "hard_light"; case kSoftLight_GrBlendEquation: return "soft_light"; case kDifference_GrBlendEquation: return "difference"; case kExclusion_GrBlendEquation: return "exclusion"; case kMultiply_GrBlendEquation: return "multiply"; case kHSLHue_GrBlendEquation: return "hsl_hue"; case kHSLSaturation_GrBlendEquation: return "hsl_saturation"; case kHSLColor_GrBlendEquation: return "hsl_color"; case kHSLLuminosity_GrBlendEquation: return "hsl_luminosity"; case kIllegal_GrBlendEquation: SkASSERT(false); return ""; } return ""; } static const char* coeff_string(GrBlendCoeff coeff) { switch (coeff) { case kZero_GrBlendCoeff: return "zero"; case kOne_GrBlendCoeff: return "one"; case kSC_GrBlendCoeff: return "src_color"; case kISC_GrBlendCoeff: return "inv_src_color"; case kDC_GrBlendCoeff: return "dst_color"; case kIDC_GrBlendCoeff: return "inv_dst_color"; case kSA_GrBlendCoeff: return "src_alpha"; case kISA_GrBlendCoeff: return "inv_src_alpha"; case kDA_GrBlendCoeff: return "dst_alpha"; case kIDA_GrBlendCoeff: return "inv_dst_alpha"; case kConstC_GrBlendCoeff: return "const_color"; case kIConstC_GrBlendCoeff: return "inv_const_color"; case kS2C_GrBlendCoeff: return "src2_color"; case kIS2C_GrBlendCoeff: return "inv_src2_color"; case kS2A_GrBlendCoeff: return "src2_alpha"; case kIS2A_GrBlendCoeff: return "inv_src2_alpha"; case kIllegal_GrBlendCoeff: SkASSERT(false); return ""; } return ""; } SkString GrXferProcessor::BlendInfo::dump() const { SkString out; out.printf("write_color(%d) equation(%s) src_coeff(%s) dst_coeff:(%s) const(0x%08x)", fWriteColor, equation_string(fEquation), coeff_string(fSrcBlend), coeff_string(fDstBlend), fBlendConstant.toBytes_RGBA()); return out; } #endif /////////////////////////////////////////////////////////////////////////////// GrXPFactory::AnalysisProperties GrXPFactory::GetAnalysisProperties( const GrXPFactory* factory, const GrProcessorAnalysisColor& color, const GrProcessorAnalysisCoverage& coverage, const GrCaps& caps, GrClampType clampType) { AnalysisProperties result; if (factory) { result = factory->analysisProperties(color, coverage, caps, clampType); } else { result = GrPorterDuffXPFactory::SrcOverAnalysisProperties(color, coverage, caps, clampType); } if (coverage == GrProcessorAnalysisCoverage::kNone) { result |= AnalysisProperties::kCompatibleWithCoverageAsAlpha; } SkASSERT(!(result & AnalysisProperties::kRequiresDstTexture)); if ((result & AnalysisProperties::kReadsDstInShader) && !caps.shaderCaps()->dstReadInShaderSupport()) { result |= AnalysisProperties::kRequiresDstTexture | AnalysisProperties::kRequiresNonOverlappingDraws; } return result; } sk_sp GrXPFactory::MakeXferProcessor(const GrXPFactory* factory, const GrProcessorAnalysisColor& color, GrProcessorAnalysisCoverage coverage, const GrCaps& caps, GrClampType clampType) { if (factory) { return factory->makeXferProcessor(color, coverage, caps, clampType); } else { return GrPorterDuffXPFactory::MakeSrcOverXferProcessor(color, coverage, caps); } } ////////////////////////////////////////////////////////////////////////////// using ProgramImpl = GrXferProcessor::ProgramImpl; // This is only called for cases where we are doing LCD coverage and not using in shader blending. // For these cases we assume the the src alpha is 1, thus we can just use the max for the alpha // coverage since src alpha will always be greater than or equal to dst alpha. static void adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder* fragBuilder, const char* srcCoverage, const GrXferProcessor& proc) { if (srcCoverage && proc.isLCD()) { fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);", srcCoverage, srcCoverage, srcCoverage, srcCoverage); } } void ProgramImpl::emitCode(const EmitArgs& args) { if (!args.fXP.willReadDstColor()) { adjust_for_lcd_coverage(args.fXPFragBuilder, args.fInputCoverage, args.fXP); this->emitOutputsForBlendState(args); } else { GrGLSLXPFragmentBuilder* fragBuilder = args.fXPFragBuilder; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; const char* dstColor = fragBuilder->dstColor(); bool needsLocalOutColor = false; if (args.fDstTextureSamplerHandle.isValid()) { if (args.fInputCoverage) { // We don't think any shaders actually output negative coverage, but just as a // safety check for floating point precision errors, we compare with <= here. We // just check the RGB values of the coverage, since the alpha may not have been set // when using LCD. If we are using single-channel coverage, alpha will be equal to // RGB anyway. // // The discard here also helps for batching text-draws together, which need to read // from a dst copy for blends. However, this only helps the case where the outer // bounding boxes of each letter overlap and not two actually parts of the text. fragBuilder->codeAppendf("if (all(lessThanEqual(%s.rgb, half3(0)))) {" " discard;" "}", args.fInputCoverage); } } else { needsLocalOutColor = args.fShaderCaps->requiresLocalOutputColorForFBFetch(); } const char* outColor = "_localColorOut"; if (!needsLocalOutColor) { outColor = args.fOutputPrimary; } else { fragBuilder->codeAppendf("half4 %s;", outColor); } this->emitBlendCodeForDstRead(fragBuilder, uniformHandler, args.fInputColor, args.fInputCoverage, dstColor, outColor, args.fOutputSecondary, args.fXP); if (needsLocalOutColor) { fragBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, outColor); } } // Swizzle the fragment shader outputs if necessary. this->emitWriteSwizzle(args.fXPFragBuilder, args.fWriteSwizzle, args.fOutputPrimary, args.fOutputSecondary); } void ProgramImpl::emitWriteSwizzle(GrGLSLXPFragmentBuilder* x, const GrSwizzle& swizzle, const char* outColor, const char* outColorSecondary) const { if (GrSwizzle::RGBA() != swizzle) { x->codeAppendf("%s = %s.%s;", outColor, outColor, swizzle.asString().c_str()); if (outColorSecondary) { x->codeAppendf("%s = %s.%s;", outColorSecondary, outColorSecondary, swizzle.asString().c_str()); } } } void ProgramImpl::setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp) { this->onSetData(pdm, xp); } void ProgramImpl::DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fragBuilder, const char* srcCoverage, const char* dstColor, const char* outColor, const char* outColorSecondary, const GrXferProcessor& proc) { if (srcCoverage) { if (proc.isLCD()) { fragBuilder->codeAppendf("half3 lerpRGB = mix(%s.aaa, %s.aaa, %s.rgb);", dstColor, outColor, srcCoverage); } fragBuilder->codeAppendf("%s = %s * %s + (half4(1.0) - %s) * %s;", outColor, srcCoverage, outColor, srcCoverage, dstColor); if (proc.isLCD()) { fragBuilder->codeAppendf("%s.a = max(max(lerpRGB.r, lerpRGB.b), lerpRGB.g);", outColor); } } }