| 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
|
|
|