Index: src/gpu/GrUserStencilSettings.h |
diff --git a/src/gpu/GrUserStencilSettings.h b/src/gpu/GrUserStencilSettings.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a07a08300a72d5b8aa8441d6cb97511f87649217 |
--- /dev/null |
+++ b/src/gpu/GrUserStencilSettings.h |
@@ -0,0 +1,237 @@ |
+/* |
+ * 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 |
+ * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer |
+ * 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 fall outside the user range. |
+ * |
+ * When code outside the GrDrawTarget 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<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)) |
+}; |
+ |
+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<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>(); |
+ } |
+ |
+ 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>(); |
+ } |
+ |
+ // 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{(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<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; |
+}; |
+ |
+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)); |
+ |
+ 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 |