/* * Copyright 2023 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef skgpu_BlendFormula_DEFINED #define skgpu_BlendFormula_DEFINED #include "include/private/base/SkAssert.h" #include "include/private/base/SkMacros.h" #include "include/private/base/SkTo.h" #include "src/gpu/Blend.h" #include enum class SkBlendMode; namespace skgpu { /** * Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage. */ class BlendFormula { public: /** * Values the shader can write to primary and secondary outputs. These are all modulated by * coverage. We will ignore the multiplies when not using coverage. */ enum OutputType { kNone_OutputType, //(equation)) , fSrcCoeff(SkTo(srcCoeff)) , fDstCoeff(SkTo(dstCoeff)) , fProps(GetProperties(primaryOut, secondaryOut, equation, srcCoeff, dstCoeff)) {} BlendFormula(const BlendFormula&) = default; BlendFormula& operator=(const BlendFormula&) = default; bool operator==(const BlendFormula& that) const { return fPrimaryOutputType == that.fPrimaryOutputType && fSecondaryOutputType == that. fSecondaryOutputType && fBlendEquation == that.fBlendEquation && fSrcCoeff == that.fSrcCoeff && fDstCoeff == that.fDstCoeff && fProps == that.fProps; } bool hasSecondaryOutput() const { return kNone_OutputType != fSecondaryOutputType; } bool modifiesDst() const { return SkToBool(fProps & kModifiesDst_Property); } bool unaffectedByDst() const { return SkToBool(fProps & kUnaffectedByDst_Property); } // We don't always fully optimize the blend formula (e.g., for opaque src-over), so we include // an "IfOpaque" variant to help set AnalysisProperties::kUnaffectedByDstValue in those cases. bool unaffectedByDstIfOpaque() const { return SkToBool(fProps & kUnaffectedByDstIfOpaque_Property); } bool usesInputColor() const { return SkToBool(fProps & kUsesInputColor_Property); } bool canTweakAlphaForCoverage() const { return SkToBool(fProps & kCanTweakAlphaForCoverage_Property); } skgpu::BlendEquation equation() const { return static_cast(fBlendEquation); } skgpu::BlendCoeff srcCoeff() const { return static_cast(fSrcCoeff); } skgpu::BlendCoeff dstCoeff() const { return static_cast(fDstCoeff); } OutputType primaryOutput() const { return fPrimaryOutputType; } OutputType secondaryOutput() const { return fSecondaryOutputType; } private: enum Properties { kModifiesDst_Property = 1 << 0, kUnaffectedByDst_Property = 1 << 1, kUnaffectedByDstIfOpaque_Property = 1 << 2, kUsesInputColor_Property = 1 << 3, kCanTweakAlphaForCoverage_Property = 1 << 4, kLast_Property = kCanTweakAlphaForCoverage_Property }; SK_DECL_BITFIELD_OPS_FRIENDS(Properties) /** * Deduce the properties of a BlendFormula. */ constexpr BlendFormula::Properties GetProperties(OutputType PrimaryOut, OutputType SecondaryOut, skgpu::BlendEquation BlendEquation, skgpu::BlendCoeff SrcCoeff, skgpu::BlendCoeff DstCoeff) { return // The provided formula should already be optimized before a BlendFormula is constructed. // Assert that here while setting up the properties in the constexpr constructor. SkASSERT((kNone_OutputType == PrimaryOut) == !skgpu::BlendCoeffsUseSrcColor(SrcCoeff, DstCoeff)), SkASSERT(!skgpu::BlendCoeffRefsSrc2(SrcCoeff)), SkASSERT((kNone_OutputType == SecondaryOut) == !skgpu::BlendCoeffRefsSrc2(DstCoeff)), SkASSERT(PrimaryOut != SecondaryOut || kNone_OutputType == PrimaryOut), SkASSERT(kNone_OutputType != PrimaryOut || kNone_OutputType == SecondaryOut), static_cast( (skgpu::BlendModifiesDst(BlendEquation, SrcCoeff, DstCoeff) ? kModifiesDst_Property : 0) | (!skgpu::BlendCoeffsUseDstColor(SrcCoeff, DstCoeff, false/*srcColorIsOpaque*/) ? kUnaffectedByDst_Property : 0) | (!skgpu::BlendCoeffsUseDstColor(SrcCoeff, DstCoeff, true/*srcColorIsOpaque*/) ? kUnaffectedByDstIfOpaque_Property : 0) | ((PrimaryOut >= kModulate_OutputType && skgpu::BlendCoeffsUseSrcColor(SrcCoeff, DstCoeff)) || (SecondaryOut >= kModulate_OutputType && skgpu::BlendCoeffRefsSrc2(DstCoeff)) ? kUsesInputColor_Property : 0) | // We assert later that SrcCoeff doesn't ref src2. ((kModulate_OutputType == PrimaryOut || kNone_OutputType == PrimaryOut) && kNone_OutputType == SecondaryOut && skgpu::BlendAllowsCoverageAsAlpha(BlendEquation, SrcCoeff, DstCoeff) ? kCanTweakAlphaForCoverage_Property : 0)); } struct { // We allot the enums one more bit than they require because MSVC seems to sign-extend // them when the top bit is set. (This is in violation of the C++03 standard 9.6/4) OutputType fPrimaryOutputType : 4; OutputType fSecondaryOutputType : 4; uint32_t fBlendEquation : 6; uint32_t fSrcCoeff : 6; uint32_t fDstCoeff : 6; Properties fProps : 32 - (4 + 4 + 6 + 6 + 6); }; static_assert(kLast_OutputType < (1 << 3)); static_assert(static_cast(skgpu::BlendEquation::kLast) < (1 << 5)); static_assert(static_cast(skgpu::BlendCoeff::kLast) < (1 << 5)); static_assert(kLast_Property < (1 << 6)); }; static_assert(4 == sizeof(BlendFormula)); SK_MAKE_BITFIELD_OPS(BlendFormula::Properties) BlendFormula GetBlendFormula(bool isOpaque, bool hasCoverage, SkBlendMode xfermode); BlendFormula GetLCDBlendFormula(SkBlendMode xfermode); } // namespace skgpu #endif // skgpu_BlendFormula_DEFINED