blob: 3c06406ea3245a0c0b3c85d3e03dacd84a2cc15e [file] [log] [blame]
/*
* 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