Chromium Code Reviews| Index: src/gpu/GrStencil.h |
| diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h |
| index 075eb1ef1d2af44e092ae5b48a987fd92d3993f2..d65e2a8c59b76333338ffff5d59db2d4be6ca0de 100644 |
| --- a/src/gpu/GrStencil.h |
| +++ b/src/gpu/GrStencil.h |
| @@ -17,9 +17,9 @@ class GrProcessorKeyBuilder; |
| /** |
| * Gr uses the stencil buffer to implement complex clipping inside the |
| * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer |
| - * bits available for other uses by external code (clients). Client code can |
| + * bits available for other uses by external code (user bits). Client code can |
| * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits |
| - * provided by clients that overlap the bits used to implement clipping. |
| + * provided by clients that fall outside the user range. |
| * |
| * When code outside the GrDrawTarget class uses the stencil buffer the contract |
| * is as follows: |
| @@ -27,235 +27,286 @@ class GrProcessorKeyBuilder; |
| * > Normal stencil funcs allow the client to pass / fail regardless of the |
| * reserved clip bits. |
| * > Additional functions allow a test against the clip along with a limited |
| - * set of tests against the client bits. |
| - * > Client can assume all client bits are zero initially. |
| + * set of tests against the user bits. |
| + * > Client can assume all user bits are zero initially. |
| * > Client must ensure that after all its passes are finished it has only |
| * written to the color buffer in the region inside the clip. Furthermore, it |
| - * must zero all client bits that were modifed (both inside and outside the |
| + * must zero all user bits that were modifed (both inside and outside the |
| * clip). |
| */ |
| -/** |
| - * Determines which pixels pass / fail the stencil test. |
| - * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true |
| - */ |
| -enum GrStencilFunc { |
| - kAlways_StencilFunc = 0, |
| - kNever_StencilFunc, |
| - kGreater_StencilFunc, |
| - kGEqual_StencilFunc, |
| - kLess_StencilFunc, |
| - kLEqual_StencilFunc, |
| - kEqual_StencilFunc, |
| - kNotEqual_StencilFunc, |
| - |
| - // Gr stores the current clip in the |
| - // stencil buffer in the high bits that |
| - // are not directly accessible modifiable |
| - // via the GrDrawTarget interface. The below |
| - // stencil funcs test against the current |
| - // clip in addition to the GrDrawTarget |
| - // client's stencil bits. |
| - |
| - // pass if inside the clip |
| - kAlwaysIfInClip_StencilFunc, |
| - kEqualIfInClip_StencilFunc, |
| - kLessIfInClip_StencilFunc, |
| - kLEqualIfInClip_StencilFunc, |
| - kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0 |
| - |
| - kLast_StencilFunc = kNonZeroIfInClip_StencilFunc |
| +enum GrStencilFlags { |
| + kDisabled_StencilFlag = 0x1, |
| + kNoModifyStencil_StencilFlag = 0x2, |
| + kNoWrapOps_StencilFlag = 0x4, |
| + kSingleSided_StencilFlag = 0x8, |
| + |
| + kLast_StencilFlag = kSingleSided_StencilFlag, |
| + kAll_StencilFlags = kLast_StencilFlag | (kLast_StencilFlag - 1) |
| }; |
| -static const int kStencilFuncCnt = kLast_StencilFunc + 1; |
| -static const int kClipStencilFuncCnt = |
| - kNonZeroIfInClip_StencilFunc - kAlwaysIfInClip_StencilFunc + 1; |
| -static const int kBasicStencilFuncCnt = kStencilFuncCnt - kClipStencilFuncCnt; |
| +template<typename TTest, typename TOp> struct GrTStencilFaceSettings { |
| + uint16_t fRef; // Reference value for stencil test and ops. |
| + TTest fTest; // Stencil test function, where fRef is on the left side. |
| + uint16_t fTestMask; // Bitwise "and" to perform on fRef and stencil values before testing. |
| + // (e.g. (fRef & fTestMask) < (stencil & fTestMask)) |
| + TOp fPassOp; // Op to perform when the test passes. |
| + TOp fFailOp; // Op to perform when the test fails. |
| + uint16_t fWriteMask; // Indicates which bits in the stencil buffer should be updated. |
| + // (e.g. stencil = (newValue & fWriteMask) | (stencil & ~fWriteMask)) |
| +}; |
| -/** |
| - * Operations to perform based on whether stencil test passed failed. |
| - */ |
| -enum GrStencilOp { |
| - kKeep_StencilOp = 0, // preserve existing stencil value |
| - kReplace_StencilOp, // replace with reference value from stencl test |
| - kIncWrap_StencilOp, // increment and wrap at max |
| - kIncClamp_StencilOp, // increment and clamp at max |
| - kDecWrap_StencilOp, // decrement and wrap at 0 |
| - kDecClamp_StencilOp, // decrement and clamp at 0 |
| - kZero_StencilOp, // zero stencil bits |
| - kInvert_StencilOp, // invert stencil bits |
| - kLast_StencilOp = kInvert_StencilOp |
| +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +enum class GrUserStencilTest : uint16_t { |
| + // Tests that respect the clip bit (if in use). |
|
bsalomon
2016/05/10 14:06:21
Maybe say that these tests "always fail when the c
Chris Dalton
2016/05/10 16:39:30
How's the reworded comment?
bsalomon
2016/05/10 20:10:58
better, thanks.
|
| + kAlwaysIfInClip, |
| + kEqualIfInClip, |
| + kLessIfInClip, |
| + kLEqualIfInClip, |
| + |
| + // Tests that ignore the clip bit. The client is responsible to ensure no color writes occur |
| + // outside the clip when it is in use. |
| + kAlways, |
| + kNever, |
| + kGreater, |
| + kGEqual, |
| + kLess, |
| + kLEqual, |
| + kEqual, |
| + kNotEqual |
| +}; |
| +static constexpr GrUserStencilTest kLastClippedStencilTest = GrUserStencilTest::kLEqualIfInClip; |
| +static constexpr int kGrUserStencilTestCount = 1 + (int)GrUserStencilTest::kNotEqual; |
| + |
| +enum class GrUserStencilOp : uint8_t { |
| + kKeep, |
| + |
| + // Ops that only modify user bits. These must not be paired with ops that modify the clip bit. |
| + kZero, |
| + kReplace, // Replace stencil value with fRef (only the bits enabled in fWriteMask). |
| + kInvert, |
| + kIncWrap, |
| + kDecWrap, |
| + // These two should only be used if wrap ops are not supported, or if the math is guaranteed |
| + // to not overflow. The user bits may or may not clamp, depending on the state of non-user bits. |
| + kIncMaybeClamp, |
| + kDecMaybeClamp, |
| + |
| + // Ops that only modify the clip bit. These must not be paired with ops that modify user bits. |
| + kZeroClipBit, |
| + kSetClipBit, |
| + kInvertClipBit, |
| + |
| + // Ops that modify both clip and user bits. These can only be paired with kKeep or each other. |
| + kSetClipAndReplaceUserBits, |
| + kZeroClipAndUserBits |
| }; |
| -static const int kStencilOpCnt = kLast_StencilOp + 1; |
| +static constexpr GrUserStencilOp kLastUserOnlyStencilOp = GrUserStencilOp::kDecMaybeClamp; |
| +static constexpr GrUserStencilOp kLastClipOnlyStencilOp = GrUserStencilOp::kInvertClipBit; |
| +static constexpr int kGrUserStencilOpCount = 1 + (int)GrUserStencilOp::kZeroClipAndUserBits; |
| /** |
| - * Class representing stencil state. |
| + * This struct is a compile-time constant representation of user stencil settings. It describes in |
| + * abstract terms how a draw will use the stencil buffer. It gets ODR-used at runtime to define a |
| + * draw's stencil settings, and is later translated into concrete settings when the pipeline is |
| + * finalized. |
| */ |
| -class GrStencilSettings { |
| -public: |
| - enum Face { |
| - kFront_Face = 0, |
| - kBack_Face = 1, |
| - }; |
| - |
| - constexpr GrStencilSettings(GrStencilOp passOp, |
| - GrStencilOp failOp, |
| - GrStencilFunc func, |
| - unsigned short funcMask, |
| - unsigned short funcRef, |
| - unsigned short writeMask) |
| - : fPassOps{(uint8_t)passOp, (uint8_t)passOp} |
| - , fFailOps{(uint8_t)failOp, (uint8_t)failOp} |
| - , fFuncs{(uint8_t)func, (uint8_t)func} |
| - , fPad0(0) |
| - , fPad1(0) |
| - , fFuncMasks{funcMask, funcMask} |
| - , fFuncRefs{funcRef, funcRef} |
| - , fWriteMasks{writeMask, writeMask} |
| - , fFlags(ComputeFlags(passOp, passOp, |
| - failOp, failOp, |
| - func, func, |
| - writeMask, writeMask)) { |
| +struct GrUserStencilSettings { |
| + typedef GrTStencilFaceSettings<GrUserStencilTest, GrUserStencilOp> Face; |
| + |
| + template<GrUserStencilTest, GrUserStencilOp PassOp, GrUserStencilOp FailOp> struct Attrs; |
| + |
| + // Unfortunately, this is the only way to pass template arguments to a constructor. |
| + template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask, |
| + GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask> struct Init {}; |
| + |
| + template<uint16_t FtRef, uint16_t BkRef, |
| + GrUserStencilTest FtTest, GrUserStencilTest BkTest, |
| + uint16_t FtTestMask, uint16_t BkTestMask, |
| + GrUserStencilOp FtPassOp, GrUserStencilOp BkPassOp, |
| + GrUserStencilOp FtFailOp, GrUserStencilOp BkFailOp, |
| + uint16_t FtWriteMask, uint16_t BkWriteMask> struct InitSeparate {}; |
| + |
| + template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask, |
| + GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask> |
| + constexpr static Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask> StaticInit() { |
| + return Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask>(); |
| } |
| - constexpr GrStencilSettings(GrStencilOp frontPassOp, GrStencilOp backPassOp, |
| - GrStencilOp frontFailOp, GrStencilOp backFailOp, |
| - GrStencilFunc frontFunc, GrStencilFunc backFunc, |
| - uint16_t frontFuncMask, uint16_t backFuncMask, |
| - uint16_t frontFuncRef, uint16_t backFuncRef, |
| - uint16_t frontWriteMask, uint16_t backWriteMask) |
| - : fPassOps{(uint8_t)frontPassOp, (uint8_t)backPassOp} |
| - , fFailOps{(uint8_t)frontFailOp, (uint8_t)backFailOp} |
| - , fFuncs{(uint8_t)frontFunc, (uint8_t)backFunc} |
| - , fPad0(0) |
| - , fPad1(0) |
| - , fFuncMasks{frontFuncMask, backFuncMask} |
| - , fFuncRefs{frontFuncRef, backFuncRef} |
| - , fWriteMasks{frontWriteMask, backWriteMask} |
| - , fFlags(ComputeFlags(frontPassOp, backPassOp, |
| - frontFailOp, backFailOp, |
| - frontFunc, backFunc, |
| - frontWriteMask, backWriteMask)) { |
| + template<uint16_t FtRef, uint16_t BkRef, |
| + GrUserStencilTest FtTest, GrUserStencilTest BkTest, |
| + uint16_t FtTestMask, uint16_t BkTestMask, |
| + GrUserStencilOp FtPassOp, GrUserStencilOp BkPassOp, |
| + GrUserStencilOp FtFailOp, GrUserStencilOp BkFailOp, |
| + uint16_t FtWriteMask, uint16_t BkWriteMask> |
| + constexpr static InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask, |
| + FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, |
| + BkWriteMask> StaticInitSeparate() { |
| + return InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask, |
| + FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, BkWriteMask>(); |
| } |
| - GrStencilSettings() { |
| - fPad0 = fPad1 = 0; |
| - this->setDisabled(); |
| + // We construct with template arguments in order to enforce that the struct be compile-time |
| + // constant and to make use of static asserts. |
| + template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask, |
| + GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask, |
| + typename Attrs = Attrs<Test, PassOp, FailOp> > |
| + constexpr explicit GrUserStencilSettings( |
| + const Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask>&) |
| + : fFrontFlags{Attrs::Flags(false) | kSingleSided_StencilFlag, |
| + Attrs::Flags(true) | kSingleSided_StencilFlag} |
| + , fFront{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp, |
| + Attrs::EffectiveWriteMask(WriteMask)} |
| + , fBackFlags{Attrs::Flags(false) | kSingleSided_StencilFlag, |
| + Attrs::Flags(true) | kSingleSided_StencilFlag} |
| + , fBack{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp, |
| + Attrs::EffectiveWriteMask(WriteMask)} { |
| } |
| - GrStencilOp passOp(Face f) const { return static_cast<GrStencilOp>(fPassOps[f]); } |
| - GrStencilOp failOp(Face f) const { return static_cast<GrStencilOp>(fFailOps[f]); } |
| - GrStencilFunc func(Face f) const { return static_cast<GrStencilFunc>(fFuncs[f]); } |
| - uint16_t funcMask(Face f) const { return fFuncMasks[f]; } |
| - uint16_t funcRef(Face f) const { return fFuncRefs[f]; } |
| - uint16_t writeMask(Face f) const { return fWriteMasks[f]; } |
| - |
| - void setPassOp(Face f, GrStencilOp op) { fPassOps[f] = op; fFlags = 0;} |
| - void setFailOp(Face f, GrStencilOp op) { fFailOps[f] = op; fFlags = 0;} |
| - void setFunc(Face f, GrStencilFunc func) { fFuncs[f] = func; fFlags = 0;} |
| - void setFuncMask(Face f, unsigned short mask) { fFuncMasks[f] = mask; } |
| - void setFuncRef(Face f, unsigned short ref) { fFuncRefs[f] = ref; } |
| - void setWriteMask(Face f, unsigned short writeMask) { fWriteMasks[f] = writeMask; } |
| - |
| - void copyFrontSettingsToBack() { |
| - fPassOps[kBack_Face] = fPassOps[kFront_Face]; |
| - fFailOps[kBack_Face] = fFailOps[kFront_Face]; |
| - fFuncs[kBack_Face] = fFuncs[kFront_Face]; |
| - fFuncMasks[kBack_Face] = fFuncMasks[kFront_Face]; |
| - fFuncRefs[kBack_Face] = fFuncRefs[kFront_Face]; |
| - fWriteMasks[kBack_Face] = fWriteMasks[kFront_Face]; |
| - fFlags = 0; |
| - } |
| + template<uint16_t FtRef, uint16_t BkRef, |
| + GrUserStencilTest FtTest, GrUserStencilTest BkTest, |
| + uint16_t FtTestMask, uint16_t BkTestMask, |
| + GrUserStencilOp FtPassOp, GrUserStencilOp BkPassOp, |
| + GrUserStencilOp FtFailOp, GrUserStencilOp BkFailOp, |
| + uint16_t FtWriteMask, uint16_t BkWriteMask, |
| + typename FtAttrs = Attrs<FtTest, FtPassOp, FtFailOp>, |
| + typename BkAttrs = Attrs<BkTest, BkPassOp, BkFailOp> > |
| + constexpr explicit GrUserStencilSettings( |
| + const InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask, |
| + FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, BkWriteMask>&) |
| + : fFrontFlags{FtAttrs::Flags(false), FtAttrs::Flags(true)} |
| + , fFront{FtRef, FtTest, FtAttrs::EffectiveTestMask(FtTestMask), FtPassOp, FtFailOp, |
| + FtAttrs::EffectiveWriteMask(FtWriteMask)} |
| + , fBackFlags{BkAttrs::Flags(false), BkAttrs::Flags(true)} |
| + , fBack{BkRef, BkTest, BkAttrs::EffectiveTestMask(BkTestMask), BkPassOp, BkFailOp, |
| + BkAttrs::EffectiveWriteMask(BkWriteMask)} {} |
| + |
| + // This struct can only be constructed with static initializers. |
| + GrUserStencilSettings() = delete; |
| + GrUserStencilSettings(const GrUserStencilSettings&) = delete; |
| + |
| + const uint16_t fFrontFlags[2]; // frontFlagsForDraw = fFrontFlags[hasStencilClip]. |
| + const Face fFront; |
| + const uint16_t fBackFlags[2]; // backFlagsForDraw = fBackFlags[hasStencilClip]. |
| + const Face fBack; |
| + |
| + static const GrUserStencilSettings& kUnused; |
| +}; |
| - void setDisabled() { |
| - memset(this, 0, sizeof(*this)); |
| - GR_STATIC_ASSERT(0 == kKeep_StencilOp); |
| - GR_STATIC_ASSERT(0 == kAlways_StencilFunc); |
| - fFlags = kIsDisabled_StencilFlag | kDoesNotWrite_StencilFlag; |
| +template<GrUserStencilTest Test, GrUserStencilOp PassOp, GrUserStencilOp FailOp> |
| +struct GrUserStencilSettings::Attrs { |
| + // Ensure an op that only modifies user bits isn't paired with one that modifies clip bits. |
| + GR_STATIC_ASSERT(GrUserStencilOp::kKeep == PassOp || GrUserStencilOp::kKeep == FailOp || |
| + (PassOp <= kLastUserOnlyStencilOp) == (FailOp <= kLastUserOnlyStencilOp)); |
| + // Ensure an op that only modifies clip bits isn't paired with one that modifies clip and user. |
| + GR_STATIC_ASSERT(GrUserStencilOp::kKeep == PassOp || GrUserStencilOp::kKeep == FailOp || |
| + (PassOp <= kLastClipOnlyStencilOp) == (FailOp <= kLastClipOnlyStencilOp)); |
| + |
| + static constexpr bool TestAlwaysPasses(bool hasStencilClip) { |
| + return (!hasStencilClip && GrUserStencilTest::kAlwaysIfInClip == Test) || |
| + GrUserStencilTest::kAlways == Test; |
| } |
| - |
| - bool isTwoSided() const { |
| - return fPassOps[kFront_Face] != fPassOps[kBack_Face] || |
| - fFailOps[kFront_Face] != fFailOps[kBack_Face] || |
| - fFuncs[kFront_Face] != fFuncs[kBack_Face] || |
| - fFuncMasks[kFront_Face] != fFuncMasks[kBack_Face] || |
| - fFuncRefs[kFront_Face] != fFuncRefs[kBack_Face] || |
| - fWriteMasks[kFront_Face] != fWriteMasks[kBack_Face]; |
| + static constexpr bool DoesNotModifyStencil(bool hasStencilClip) { |
| + return (GrUserStencilTest::kNever == Test || GrUserStencilOp::kKeep == PassOp) && |
| + (TestAlwaysPasses(hasStencilClip) || GrUserStencilOp::kKeep == FailOp); |
| } |
| - |
| - bool usesWrapOp() const { |
| - return kIncWrap_StencilOp == fPassOps[kFront_Face] || |
| - kDecWrap_StencilOp == fPassOps[kFront_Face] || |
| - kIncWrap_StencilOp == fPassOps[kBack_Face] || |
| - kDecWrap_StencilOp == fPassOps[kBack_Face] || |
| - kIncWrap_StencilOp == fFailOps[kFront_Face] || |
| - kDecWrap_StencilOp == fFailOps[kFront_Face] || |
| - kIncWrap_StencilOp == fFailOps[kBack_Face] || |
| - kDecWrap_StencilOp == fFailOps[kBack_Face]; |
| + static constexpr bool IsDisabled(bool hasStencilClip) { |
| + return TestAlwaysPasses(hasStencilClip) && DoesNotModifyStencil(hasStencilClip); |
| } |
| - |
| - bool isDisabled() const { |
| - if (fFlags & kIsDisabled_StencilFlag) { |
| - return true; |
| - } |
| - if (fFlags & kNotDisabled_StencilFlag) { |
| - return false; |
| - } |
| - bool disabled = this->computeIsDisabled(); |
| - fFlags |= disabled ? kIsDisabled_StencilFlag : kNotDisabled_StencilFlag; |
| - return disabled; |
| + static constexpr bool UsesWrapOps() { |
| + return GrUserStencilOp::kIncWrap == PassOp || GrUserStencilOp::kDecWrap == PassOp || |
| + GrUserStencilOp::kIncWrap == FailOp || GrUserStencilOp::kDecWrap == FailOp; |
| } |
| - |
| - bool doesWrite() const { |
| - if (fFlags & kDoesWrite_StencilFlag) { |
| - return true; |
| - } |
| - if (fFlags & kDoesNotWrite_StencilFlag) { |
| - return false; |
| - } |
| - bool writes = this->computeDoesWrite(); |
| - fFlags |= writes ? kDoesWrite_StencilFlag : kDoesNotWrite_StencilFlag; |
| - return writes; |
| + static constexpr bool TestIgnoresRef() { |
| + return (GrUserStencilTest::kAlwaysIfInClip == Test || GrUserStencilTest::kAlways == Test || |
| + GrUserStencilTest::kNever == Test); |
| } |
| - |
| - void invalidate() { |
| - // write an illegal value to the first member |
| - fPassOps[0] = kStencilOpCnt; |
| - fFlags = 0; |
| + static constexpr uint16_t Flags(bool hasStencilClip) { |
| + return (IsDisabled(hasStencilClip) ? kDisabled_StencilFlag : 0) | |
| + (DoesNotModifyStencil(hasStencilClip) ? kNoModifyStencil_StencilFlag : 0) | |
| + (UsesWrapOps() ? 0 : kNoWrapOps_StencilFlag); |
| } |
| - |
| - bool isValid() const { return fPassOps[0] < kStencilOpCnt; } |
| - |
| - void genKey(GrProcessorKeyBuilder* b) const; |
| - |
| - bool operator==(const GrStencilSettings& s) const { |
| - static const size_t gCompareSize = sizeof(GrStencilSettings) - |
| - sizeof(fFlags); |
| - SkASSERT((const char*)&fFlags + sizeof(fFlags) == |
| - (const char*)this + sizeof(GrStencilSettings)); |
| - if (this->isDisabled() & s.isDisabled()) { // using & not && |
| - return true; |
| - } |
| - return 0 == memcmp(this, &s, gCompareSize); |
| + static constexpr uint16_t EffectiveTestMask(uint16_t testMask) { |
| + return TestIgnoresRef() ? 0 : testMask; |
| } |
| - |
| - bool operator!=(const GrStencilSettings& s) const { |
| - return !(*this == s); |
| + static constexpr uint16_t EffectiveWriteMask(uint16_t writeMask) { |
| + // We don't modify the mask differently when hasStencilClip=false because either the entire |
| + // face gets disabled in that case (e.g. Test=kAlwaysIfInClip, PassOp=kKeep), or the |
| + // effective mask stays the same either way. |
| + return DoesNotModifyStencil(true) ? 0 : writeMask; |
| } |
| +}; |
| - GrStencilSettings& operator=(const GrStencilSettings& s) { |
| - memcpy(this, &s, sizeof(GrStencilSettings)); |
| - return *this; |
| +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +enum class GrStencilTest : uint16_t { |
|
bsalomon
2016/05/10 14:06:21
I'm wondering if this could be in another file tha
Chris Dalton
2016/05/10 16:39:30
Done. Good idea.
|
| + kAlways, |
| + kNever, |
| + kGreater, |
| + kGEqual, |
| + kLess, |
| + kLEqual, |
| + kEqual, |
| + kNotEqual |
| +}; |
| +static constexpr int kGrStencilTestCount = 1 + (int)GrStencilTest::kNotEqual; |
| + |
| +enum class GrStencilOp : uint8_t { |
| + kKeep, |
| + kZero, |
| + kReplace, // Replace stencil value with fRef (only the bits enabled in fWriteMask). |
| + kInvert, |
| + kIncWrap, |
| + kDecWrap, |
| + // NOTE: clamping occurs before the write mask. So if the MSB is zero and masked out, stencil |
| + // values will still wrap when using clamping ops. |
| + kIncClamp, |
| + kDecClamp |
| +}; |
| +static constexpr int kGrStencilOpCount = 1 + (int)GrStencilOp::kDecClamp; |
| + |
| +/** |
| + * This class defines concrete stencil settings that map directly to the underlying hardware. It |
| + * is deduced from user stencil settings, stencil clip status, and the number of bits in the |
| + * target stencil buffer. |
| + */ |
| +class GrStencilSettings { |
| +public: |
| + GrStencilSettings() { this->setDisabled(); } |
| + GrStencilSettings(const GrUserStencilSettings& user, bool hasStencilClip, int numStencilBits) { |
| + this->reset(user, hasStencilClip, numStencilBits); |
| } |
| + GrStencilSettings(const GrStencilSettings& that) { this->reset(that); } |
| + GrStencilSettings& operator=(const GrStencilSettings& that) { this->reset(that); return *this; } |
| + |
| + void invalidate() { fFlags |= kInvalid_PrivateFlag; } |
| + void setDisabled() { fFlags = kAll_StencilFlags; } |
| + void reset(const GrUserStencilSettings&, bool hasStencilClip, int numStencilBits); |
| + void reset(const GrStencilSettings&); |
| + |
| + bool isValid() const { return !(fFlags & kInvalid_PrivateFlag); } |
| + bool isDisabled() const { SkASSERT(this->isValid()); return fFlags & kDisabled_StencilFlag; } |
| + bool doesWrite() const { SkASSERT(this->isValid()); |
| + return !(fFlags & kNoModifyStencil_StencilFlag); } |
| + bool isTwoSided() const { SkASSERT(this->isValid()); |
| + return !(fFlags & kSingleSided_StencilFlag); } |
| + bool usesWrapOp() const { SkASSERT(this->isValid()); |
| + return !(fFlags & kNoWrapOps_StencilFlag); } |
| -private: |
| - friend class GrClipMaskManager; |
| + void genKey(GrProcessorKeyBuilder* b) const; |
| + |
| + bool operator!=(const GrStencilSettings& that) const { return !(*this == that); } |
| + bool operator==(const GrStencilSettings&) const; |
| - enum { |
| - kMaxStencilClipPasses = 2 // maximum number of passes to add a clip |
| - // element to the stencil buffer. |
| + struct Face : public GrTStencilFaceSettings<GrStencilTest, GrStencilOp> { |
| + void reset(const GrUserStencilSettings::Face&, bool useStencilClip, int numStencilBits); |
| + void setDisabled(); |
| }; |
| + const Face& front() const { SkASSERT(!this->isDisabled()); return fFront; } |
| + const Face& back() const { SkASSERT(this->isTwoSided()); return fBack; } |
| + |
| /** |
| * Given a thing to draw into the stencil clip, a fill type, and a set op |
| * this function determines: |
| @@ -263,107 +314,34 @@ private: |
| * needs to be drawn to the client portion of the stencil first. |
| * 2. How many passes are needed. |
| * 3. What those passes are. |
| - * 4. The fill rule that should actually be used to render (will |
| - * always be non-inverted). |
| * |
| - * @param op the set op to combine this element with the |
| - * existing clip |
| - * @param stencilClipMask mask with just the stencil bit used for clipping |
| - * enabled. |
| + * @param op the set op to combine this element with the existing clip |
| + * @param canBeDirect can the caller draw this element directly (without using stencil)? |
| * @param invertedFill is this path inverted |
| - * @param numPasses out: the number of passes needed to add the |
| - * element to the clip. |
| - * @param settings out: the stencil settings to use for each pass |
| + * @param drawDirectToClip out: true if caller should draw the element directly, false if it |
| + * should draw it into the user stencil bits first. |
| + * |
| + * @return a null-terminated array of settings for stencil passes. |
| + * |
| + * If drawDirectToClip is false, the caller must first draw the element into the user |
| + * stencil bits, and then cover the clip area with multiple passes using the returned |
| + * stencil settings. |
| * |
| - * @return true if the clip element's geometry can be drawn directly to the |
| - * stencil clip bit. Will only be true if canBeDirect is true. |
| - * numPasses will be 1 if return value is true. |
| + * If drawDirectToClip is true, the returned array will only have one pass and the |
| + * caller should use those stencil settings while drawing the element directly. |
| */ |
| - static bool GetClipPasses(SkRegion::Op op, |
| - bool canBeDirect, |
| - unsigned int stencilClipMask, |
| - bool invertedFill, |
| - int* numPasses, |
| - GrStencilSettings settings[kMaxStencilClipPasses]); |
| - |
| - constexpr static bool IsDisabled(GrStencilOp frontPassOp, GrStencilOp backPassOp, |
| - GrStencilOp frontFailOp, GrStencilOp backFailOp, |
| - GrStencilFunc frontFunc, GrStencilFunc backFunc) { |
| - return (((frontPassOp == kKeep_StencilOp && frontFailOp == kKeep_StencilOp)) && |
| - ((backPassOp == kKeep_StencilOp && backFailOp == kKeep_StencilOp)) && |
| - frontFunc == kAlways_StencilFunc && |
| - backFunc == kAlways_StencilFunc); |
| - } |
| - |
| - constexpr static bool DoesWrite(GrStencilOp frontPassOp, GrStencilOp backPassOp, |
| - GrStencilOp frontFailOp, GrStencilOp backFailOp, |
| - GrStencilFunc frontFunc, GrStencilFunc backFunc, |
| - uint16_t frontWriteMask, uint16_t backWriteMask) { |
| - return (0 != (frontWriteMask | backWriteMask)) && |
| - // Can we write due to a front face passing the stencil test? |
| - ((frontFunc != kNever_StencilFunc && frontPassOp != kKeep_StencilOp) || |
| - // Can we write due to a back face passing the stencil test? |
| - (backFunc != kNever_StencilFunc && backPassOp != kKeep_StencilOp) || |
| - // Can we write due to a front face failing the stencil test? |
| - (frontFunc != kAlways_StencilFunc && frontFailOp != kKeep_StencilOp) || |
| - // Can we write due to a back face failing the stencil test? |
| - (backFunc != kAlways_StencilFunc && backFailOp != kKeep_StencilOp)); |
| - } |
| - |
| - constexpr static uint32_t ComputeFlags(GrStencilOp frontPassOp, GrStencilOp backPassOp, |
| - GrStencilOp frontFailOp, GrStencilOp backFailOp, |
| - GrStencilFunc frontFunc, GrStencilFunc backFunc, |
| - uint16_t frontWriteMask, uint16_t backWriteMask) { |
| - return (IsDisabled(frontPassOp, backPassOp, frontFailOp, backFailOp, |
| - frontFunc, backFunc) |
| - ? kIsDisabled_StencilFlag |
| - : kNotDisabled_StencilFlag) | |
| - (DoesWrite(frontPassOp, backPassOp, frontFailOp, backFailOp, |
| - frontFunc, backFunc, frontWriteMask, backWriteMask) |
| - ? kDoesWrite_StencilFlag |
| - : kDoesNotWrite_StencilFlag); |
| - } |
| - |
| - bool computeIsDisabled() const { |
| - return IsDisabled((GrStencilOp) fPassOps[kFront_Face], (GrStencilOp) fPassOps[kBack_Face], |
| - (GrStencilOp) fFailOps[kFront_Face], (GrStencilOp) fFailOps[kBack_Face], |
| - (GrStencilFunc) fFuncs[kFront_Face], (GrStencilFunc) fFuncs[kBack_Face]); |
| - } |
| - bool computeDoesWrite() const { |
| - return DoesWrite((GrStencilOp)fPassOps[kFront_Face], (GrStencilOp)fPassOps[kBack_Face], |
| - (GrStencilOp)fFailOps[kFront_Face], (GrStencilOp)fFailOps[kBack_Face], |
| - (GrStencilFunc)fFuncs[kFront_Face], (GrStencilFunc)fFuncs[kBack_Face], |
| - fWriteMasks[kFront_Face], fWriteMasks[kBack_Face]); |
| - } |
| - |
| - enum GrStencilFlags { |
| - kIsDisabled_StencilFlag = 0x1, |
| - kNotDisabled_StencilFlag = 0x2, |
| - kDoesWrite_StencilFlag = 0x4, |
| - kDoesNotWrite_StencilFlag = 0x8, |
| - }; |
| + static GrUserStencilSettings const* const* GetClipPasses(SkRegion::Op op, |
| + bool canBeDirect, |
| + bool invertedFill, |
| + bool* drawDirectToClip); |
| - uint8_t fPassOps[2]; // op to perform when faces pass (GrStencilOp) |
| - uint8_t fFailOps[2]; // op to perform when faces fail (GrStencilOp) |
| - uint8_t fFuncs[2]; // test function for faces (GrStencilFunc) |
| - uint8_t fPad0; |
| - uint8_t fPad1; |
| - uint16_t fFuncMasks[2]; // mask for face tests |
| - uint16_t fFuncRefs[2]; // reference values for face tests |
| - uint16_t fWriteMasks[2]; // stencil write masks |
| - mutable uint32_t fFlags; |
| +private: |
| + // Internal flag for backends to optionally mark their tracked stencil state as invalid. |
| + enum { kInvalid_PrivateFlag = (kLast_StencilFlag << 1) }; |
| + uint32_t fFlags; |
| + Face fFront; |
| + Face fBack; |
| }; |
| -// We rely on this being packed and aligned (memcmp'ed and memcpy'ed) |
| -GR_STATIC_ASSERT(sizeof(GrStencilSettings) % 4 == 0); |
| -GR_STATIC_ASSERT(sizeof(GrStencilSettings) == |
| - 4*sizeof(uint8_t) + // ops |
| - 2*sizeof(uint8_t) + // funcs |
| - 2*sizeof(uint8_t) + // pads |
| - 2*sizeof(uint16_t) + // func masks |
| - 2*sizeof(uint16_t) + // ref values |
| - 2*sizeof(uint16_t) + // write masks |
| - sizeof(uint32_t)); // flags |
| - |
| #endif |