diff options
Diffstat (limited to 'src/gpu/GrStencil.h')
-rw-r--r-- | src/gpu/GrStencil.h | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h new file mode 100644 index 0000000..ae81840 --- /dev/null +++ b/src/gpu/GrStencil.h @@ -0,0 +1,323 @@ + +/* + * 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" +/** + * 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 + + // counts + kStencilFuncCount, + kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc - + kAlwaysIfInClip_StencilFunc + 1, + kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount +}; + +/** + * 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 + + kStencilOpCount +}; + +/** + * GrStencilState needs to be a class with accessors and setters so that it + * can maintain flags related to its current state. However, we also want to + * be able to declare pre-made stencil settings at compile time (without + * inserting static initializer code). So all the data members are in this + * struct. A macro defined after the class can be used to jam an instance of + * this struct that is created from an initializer list into a + * GrStencilSettings. (We hang our heads in shame.) + */ +struct GrStencilSettingsStruct { + GrStencilOp fFrontPassOp : 8; // op to perform when front faces pass + GrStencilOp fBackPassOp : 8; // op to perform when back faces pass + GrStencilOp fFrontFailOp : 8; // op to perform when front faces fail + GrStencilOp fBackFailOp : 8; // op to perform when back faces fail + GrStencilFunc fFrontFunc : 8; // test function for front faces + GrStencilFunc fBackFunc : 8; // test function for back faces + int fPad0 : 8; + int fPad1 : 8; + unsigned short fFrontFuncMask; // mask for front face test + unsigned short fBackFuncMask; // mask for back face test + unsigned short fFrontFuncRef; // reference value for front face test + unsigned short fBackFuncRef; // reference value for back face test + unsigned short fFrontWriteMask; // stencil write mask for front faces + unsigned short fBackWriteMask; // stencil write mask for back faces + mutable uint32_t fFlags; +}; +// We rely on this being packed and aligned (memcmp'ed and memcpy'ed) +GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) % 4 == 0); +GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) == + 4*sizeof(uint8_t) + // ops + 2*sizeof(uint8_t) + // funcs + 2*sizeof(uint8_t) + // pads + 2*sizeof(unsigned short) + // func masks + 2*sizeof(unsigned short) + // ref values + 2*sizeof(unsigned short) + // write masks + sizeof(uint32_t)); // flags + +/** + * Class representing stencil state. + */ +class GrStencilSettings : private GrStencilSettingsStruct { + +public: + GrStencilSettings() { + fPad0 = fPad1 = 0; + this->setDisabled(); + } + + GrStencilOp frontPassOp() const { return fFrontPassOp; } + GrStencilOp backPassOp() const { return fBackPassOp; } + GrStencilOp frontFailOp() const { return fFrontFailOp; } + GrStencilOp backFailOp() const { return fBackFailOp; } + GrStencilFunc frontFunc() const { return fFrontFunc; } + GrStencilFunc backFunc() const { return fBackFunc; } + unsigned short frontFuncMask() const { return fFrontFuncMask; } + unsigned short backFuncMask() const { return fBackFuncMask; } + unsigned short frontFuncRef() const { return fFrontFuncRef; } + unsigned short backFuncRef() const { return fBackFuncRef; } + unsigned short frontWriteMask() const {return fFrontWriteMask; } + unsigned short backWriteMask() const { return fBackWriteMask; } + + void setFrontPassOp(GrStencilOp op) { fFrontPassOp = op; fFlags = 0;} + void setBackPassOp(GrStencilOp op) { fBackPassOp = op; fFlags = 0;} + void setFrontFailOp(GrStencilOp op) {fFrontFailOp = op; fFlags = 0;} + void setBackFailOp(GrStencilOp op) { fBackFailOp = op; fFlags = 0;} + void setFrontFunc(GrStencilFunc func) { fFrontFunc = func; fFlags = 0;} + void setBackFunc(GrStencilFunc func) { fBackFunc = func; fFlags = 0;} + void setFrontFuncMask(unsigned short mask) { fFrontFuncMask = mask; } + void setBackFuncMask(unsigned short mask) { fBackFuncMask = mask; } + void setFrontFuncRef(unsigned short ref) { fFrontFuncRef = ref; } + void setBackFuncRef(unsigned short ref) { fBackFuncRef = ref; } + void setFrontWriteMask(unsigned short writeMask) { fFrontWriteMask = writeMask; } + void setBackWriteMask(unsigned short writeMask) { fBackWriteMask = writeMask; } + + void setSame(GrStencilOp passOp, + GrStencilOp failOp, + GrStencilFunc func, + unsigned short funcMask, + unsigned short funcRef, + unsigned short writeMask) { + fFrontPassOp = passOp; + fBackPassOp = passOp; + fFrontFailOp = failOp; + fBackFailOp = failOp; + fFrontFunc = func; + fBackFunc = func; + fFrontFuncMask = funcMask; + fBackFuncMask = funcMask; + fFrontFuncRef = funcRef; + fBackFuncRef = funcRef; + fFrontWriteMask = writeMask; + fBackWriteMask = writeMask; + fFlags = 0; + } + + void setDisabled() { + memset(this, 0, sizeof(*this)); + GR_STATIC_ASSERT(0 == kKeep_StencilOp); + GR_STATIC_ASSERT(0 == kAlways_StencilFunc); + fFlags = kIsDisabled_Flag | kDoesNotWrite_Flag; + } + + bool isDisabled() const { + if (fFlags & kIsDisabled_Flag) { + return true; + } + if (fFlags & kNotDisabled_Flag) { + return false; + } + bool disabled = kKeep_StencilOp == fFrontPassOp && + kKeep_StencilOp == fBackPassOp && + kKeep_StencilOp == fFrontFailOp && + kKeep_StencilOp == fBackFailOp && + kAlways_StencilFunc == fFrontFunc && + kAlways_StencilFunc == fBackFunc; + fFlags |= disabled ? kIsDisabled_Flag : kNotDisabled_Flag; + return disabled; + } + + bool doesWrite() const { + if (fFlags & kDoesWrite_Flag) { + return true; + } + if (fFlags & kDoesNotWrite_Flag) { + return false; + } + bool writes = !((kNever_StencilFunc == fFrontFunc || + kKeep_StencilOp == fFrontPassOp) && + (kNever_StencilFunc == fBackFunc || + kKeep_StencilOp == fBackPassOp) && + (kAlways_StencilFunc == fFrontFunc || + kKeep_StencilOp == fFrontFailOp) && + (kAlways_StencilFunc == fBackFunc || + kKeep_StencilOp == fBackFailOp)); + fFlags |= writes ? kDoesWrite_Flag : kDoesNotWrite_Flag; + return writes; + } + + void invalidate() { + // write an illegal value to the first member + fFrontPassOp = (GrStencilOp)(uint8_t)-1; + fFlags = 0; + } + + bool operator == (const GrStencilSettings& s) const { + static const size_t gCompareSize = sizeof(GrStencilSettings) - + sizeof(fFlags); + GrAssert((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 GrGpu; + enum { + kIsDisabled_Flag = 0x1, + kNotDisabled_Flag = 0x2, + kDoesWrite_Flag = 0x4, + kDoesNotWrite_Flag = 0x8, + }; + + 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(GrSetOp op, + bool canBeDirect, + unsigned int stencilClipMask, + bool invertedFill, + int* numPasses, + GrStencilSettings settings[kMaxStencilClipPasses]); +}; + +GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) == sizeof(GrStencilSettings)); + +#define GR_STATIC_CONST_STENCIL(NAME, \ + FRONT_PASS_OP, BACK_PASS_OP, \ + FRONT_FAIL_OP, BACK_FAIL_OP, \ + FRONT_FUNC, BACK_FUNC, \ + FRONT_MASK, BACK_MASK, \ + FRONT_REF, BACK_REF, \ + FRONT_WRITE_MASK, BACK_WRITE_MASK) \ + static const GrStencilSettingsStruct NAME ## _STRUCT = { \ + (FRONT_PASS_OP), (BACK_PASS_OP), \ + (FRONT_FAIL_OP), (BACK_FAIL_OP), \ + (FRONT_FUNC), (BACK_FUNC), \ + (0), (0), \ + (FRONT_MASK), (BACK_MASK), \ + (FRONT_REF), (BACK_REF), \ + (FRONT_WRITE_MASK), (BACK_WRITE_MASK), \ + 0 \ + }; \ + static const GrStencilSettings& NAME = \ + *reinterpret_cast<const GrStencilSettings*>(&(NAME ## _STRUCT)) +#endif + +#define GR_STATIC_CONST_SAME_STENCIL(NAME, \ + PASS_OP, FAIL_OP, FUNC, MASK, REF, WRITE_MASK) \ + GR_STATIC_CONST_STENCIL(NAME, (PASS_OP), (PASS_OP), (FAIL_OP), \ + (FAIL_OP), (FUNC), (FUNC), (MASK), (MASK), (REF), (REF), (WRITE_MASK), \ + (WRITE_MASK)) |