/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrUserStencilSettings_DEFINED #define GrUserStencilSettings_DEFINED #include "GrTypes.h" /** * Gr uses the stencil buffer to implement complex clipping inside the * GrOpList class. The GrOpList makes a subset of the stencil buffer * bits available for other uses by external code (user bits). Client code can * modify these bits. GrOpList will ignore ref, mask, and writemask bits * provided by clients that fall outside the user range. * * When code outside the GrOpList class uses the stencil buffer the contract * is as follows: * * > 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 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 user bits that were modifed (both inside and outside the * clip). */ 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) }; template 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)) }; enum class GrUserStencilTest : uint16_t { // Tests that respect the clip bit. If a stencil clip is not in effect, the "IfInClip" is // ignored and these only act on user bits. kAlwaysIfInClip, kEqualIfInClip, kLessIfInClip, kLEqualIfInClip, // Tests that ignore the clip bit. The client is responsible to ensure no color write occurs // outside the clip if it is in use. kAlways, kNever, kGreater, kGEqual, kLess, kLEqual, kEqual, kNotEqual }; constexpr static GrUserStencilTest kLastClippedStencilTest = GrUserStencilTest::kLEqualIfInClip; constexpr static 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 }; constexpr static GrUserStencilOp kLastUserOnlyStencilOp = GrUserStencilOp::kDecMaybeClamp; constexpr static GrUserStencilOp kLastClipOnlyStencilOp = GrUserStencilOp::kInvertClipBit; constexpr static int kGrUserStencilOpCount = 1 + (int)GrUserStencilOp::kZeroClipAndUserBits; /** * 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. */ struct GrUserStencilSettings { typedef GrTStencilFaceSettings Face; template struct Attrs; // Unfortunately, this is the only way to pass template arguments to a constructor. template struct Init {}; template struct InitSeparate {}; template constexpr static Init StaticInit() { return Init(); } template constexpr static InitSeparate StaticInitSeparate() { return InitSeparate(); } // We construct with template arguments in order to enforce that the struct be compile-time // constant and to make use of static asserts. template > constexpr explicit GrUserStencilSettings( const Init&) : fFrontFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag), (uint16_t)(Attrs::Flags(true) | kSingleSided_StencilFlag)} , fFront{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp, Attrs::EffectiveWriteMask(WriteMask)} , fBackFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag), (uint16_t)(Attrs::Flags(true) | kSingleSided_StencilFlag)} , fBack{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp, Attrs::EffectiveWriteMask(WriteMask)} { } template, typename BkAttrs = Attrs > constexpr explicit GrUserStencilSettings( const InitSeparate&) : 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; uint16_t flags(bool hasStencilClip) const { return fFrontFlags[hasStencilClip] & fBackFlags[hasStencilClip]; } bool isDisabled(bool hasStencilClip) const { return this->flags(hasStencilClip) & kDisabled_StencilFlag; } bool isTwoSided(bool hasStencilClip) const { return !(this->flags(hasStencilClip) & kSingleSided_StencilFlag); } bool usesWrapOp(bool hasStencilClip) const { return !(this->flags(hasStencilClip) & kNoWrapOps_StencilFlag); } 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; bool isUnused() const { return this == &kUnused; } }; template 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)); constexpr static bool TestAlwaysPasses(bool hasStencilClip) { return (!hasStencilClip && GrUserStencilTest::kAlwaysIfInClip == Test) || GrUserStencilTest::kAlways == Test; } constexpr static bool DoesNotModifyStencil(bool hasStencilClip) { return (GrUserStencilTest::kNever == Test || GrUserStencilOp::kKeep == PassOp) && (TestAlwaysPasses(hasStencilClip) || GrUserStencilOp::kKeep == FailOp); } constexpr static bool IsDisabled(bool hasStencilClip) { return TestAlwaysPasses(hasStencilClip) && DoesNotModifyStencil(hasStencilClip); } constexpr static bool UsesWrapOps() { return GrUserStencilOp::kIncWrap == PassOp || GrUserStencilOp::kDecWrap == PassOp || GrUserStencilOp::kIncWrap == FailOp || GrUserStencilOp::kDecWrap == FailOp; } constexpr static bool TestIgnoresRef() { return (GrUserStencilTest::kAlwaysIfInClip == Test || GrUserStencilTest::kAlways == Test || GrUserStencilTest::kNever == Test); } constexpr static uint16_t Flags(bool hasStencilClip) { return (IsDisabled(hasStencilClip) ? kDisabled_StencilFlag : 0) | (DoesNotModifyStencil(hasStencilClip) ? kNoModifyStencil_StencilFlag : 0) | (UsesWrapOps() ? 0 : kNoWrapOps_StencilFlag); } constexpr static uint16_t EffectiveTestMask(uint16_t testMask) { return TestIgnoresRef() ? 0 : testMask; } constexpr static 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 else the // effective mask stays the same either way. return DoesNotModifyStencil(true) ? 0 : writeMask; } }; #endif