Index: src/gpu/GrStencil.h |
diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..075eb1ef1d2af44e092ae5b48a987fd92d3993f2 |
--- /dev/null |
+++ b/src/gpu/GrStencil.h |
@@ -0,0 +1,369 @@ |
+/* |
+ * Copyright 2011 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+ |
+#ifndef GrStencil_DEFINED |
+#define GrStencil_DEFINED |
+ |
+#include "GrTypes.h" |
+#include "SkRegion.h" |
+ |
+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 |
+ * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits |
+ * provided by clients that overlap the bits used to implement clipping. |
+ * |
+ * 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 client bits. |
+ * > Client can assume all client 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 |
+ * 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 |
+}; |
+ |
+static const int kStencilFuncCnt = kLast_StencilFunc + 1; |
+static const int kClipStencilFuncCnt = |
+ kNonZeroIfInClip_StencilFunc - kAlwaysIfInClip_StencilFunc + 1; |
+static const int kBasicStencilFuncCnt = kStencilFuncCnt - kClipStencilFuncCnt; |
+ |
+/** |
+ * 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 |
+}; |
+static const int kStencilOpCnt = kLast_StencilOp + 1; |
+ |
+/** |
+ * Class representing stencil state. |
+ */ |
+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)) { |
+ } |
+ |
+ 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)) { |
+ } |
+ |
+ GrStencilSettings() { |
+ fPad0 = fPad1 = 0; |
+ this->setDisabled(); |
+ } |
+ |
+ 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; |
+ } |
+ |
+ void setDisabled() { |
+ memset(this, 0, sizeof(*this)); |
+ GR_STATIC_ASSERT(0 == kKeep_StencilOp); |
+ GR_STATIC_ASSERT(0 == kAlways_StencilFunc); |
+ fFlags = kIsDisabled_StencilFlag | kDoesNotWrite_StencilFlag; |
+ } |
+ |
+ 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]; |
+ } |
+ |
+ 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]; |
+ } |
+ |
+ 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; |
+ } |
+ |
+ 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; |
+ } |
+ |
+ void invalidate() { |
+ // write an illegal value to the first member |
+ fPassOps[0] = kStencilOpCnt; |
+ fFlags = 0; |
+ } |
+ |
+ 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); |
+ } |
+ |
+ bool operator!=(const GrStencilSettings& s) const { |
+ return !(*this == s); |
+ } |
+ |
+ GrStencilSettings& operator=(const GrStencilSettings& s) { |
+ memcpy(this, &s, sizeof(GrStencilSettings)); |
+ return *this; |
+ } |
+ |
+private: |
+ friend class GrClipMaskManager; |
+ |
+ enum { |
+ kMaxStencilClipPasses = 2 // maximum number of passes to add a clip |
+ // element to the stencil buffer. |
+ }; |
+ |
+ /** |
+ * Given a thing to draw into the stencil clip, a fill type, and a set op |
+ * this function determines: |
+ * 1. Whether the thing can be draw directly to the stencil clip or |
+ * 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 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 |
+ * |
+ * @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. |
+ */ |
+ 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, |
+ }; |
+ |
+ 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; |
+ |
+}; |
+ |
+// 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 |