|  | /* | 
|  | * 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 <cstdint> | 
|  |  | 
|  | 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,        //<! 0 | 
|  | kCoverage_OutputType,    //<! inputCoverage | 
|  | kModulate_OutputType,    //<! inputColor * inputCoverage | 
|  | kSAModulate_OutputType,  //<! inputColor.a * inputCoverage | 
|  | kISAModulate_OutputType, //<! (1 - inputColor.a) * inputCoverage | 
|  | kISCModulate_OutputType, //<! (1 - inputColor) * inputCoverage | 
|  |  | 
|  | kLast_OutputType = kISCModulate_OutputType | 
|  | }; | 
|  |  | 
|  | constexpr BlendFormula(OutputType primaryOut, | 
|  | OutputType secondaryOut, | 
|  | skgpu::BlendEquation equation, | 
|  | skgpu::BlendCoeff srcCoeff, | 
|  | skgpu::BlendCoeff dstCoeff) | 
|  | : fPrimaryOutputType(primaryOut) | 
|  | , fSecondaryOutputType(secondaryOut) | 
|  | , fBlendEquation(SkTo<uint8_t>(equation)) | 
|  | , fSrcCoeff(SkTo<uint8_t>(srcCoeff)) | 
|  | , fDstCoeff(SkTo<uint8_t>(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<skgpu::BlendEquation>(fBlendEquation); | 
|  | } | 
|  |  | 
|  | skgpu::BlendCoeff srcCoeff() const { | 
|  | return static_cast<skgpu::BlendCoeff>(fSrcCoeff); | 
|  | } | 
|  |  | 
|  | skgpu::BlendCoeff dstCoeff() const { | 
|  | return static_cast<skgpu::BlendCoeff>(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<Properties>( | 
|  | (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<int>(skgpu::BlendEquation::kLast) < (1 << 5)); | 
|  | static_assert(static_cast<int>(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 |