/*
* 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 = (1 << 0),
kTestAlwaysPasses_StencilFlag = (1 << 1),
kNoModifyStencil_StencilFlag = (1 << 2),
kNoWrapOps_StencilFlag = (1 << 3),
kSingleSided_StencilFlag = (1 << 4),
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;
uint16_t flags(bool hasStencilClip) const {
return fFrontFlags[hasStencilClip] & fBackFlags[hasStencilClip];
}
bool isDisabled(bool hasStencilClip) const {
return this->flags(hasStencilClip) & kDisabled_StencilFlag;
}
bool testAlwaysPasses(bool hasStencilClip) const {
return this->flags(hasStencilClip) & kTestAlwaysPasses_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<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) |
(TestAlwaysPasses(hasStencilClip) ? kTestAlwaysPasses_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