Index: src/gpu/GrStencil.cpp |
diff --git a/src/gpu/GrStencil.cpp b/src/gpu/GrStencil.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..94559ab68eb6333ccd09692eb7e2df4acf8535ba |
--- /dev/null |
+++ b/src/gpu/GrStencil.cpp |
@@ -0,0 +1,403 @@ |
+/* |
+ * Copyright 2011 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+ |
+#include "GrStencil.h" |
+ |
+#include "GrProcessor.h" |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// Stencil Rules for Merging user stencil space into clip |
+ |
+// We can't include the clip bit in the ref or mask values because the division |
+// between user and clip bits in the stencil depends on the number of stencil |
+// bits in the runtime. Comments below indicate what the code should do to |
+// incorporate the clip bit into these settings. |
+ |
+/////// |
+// Replace |
+ |
+// set the ref to be the clip bit, but mask it out for the test |
+static constexpr GrStencilSettings gUserToClipReplace( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kLess_StencilFunc, |
+ 0xffff, // unset clip bit |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gInvUserToClipReplace( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, // unset clip bit |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+/////// |
+// Intersect |
+static constexpr GrStencilSettings gUserToClipIsect( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kLess_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gInvUserToClipIsect( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+/////// |
+// Difference |
+static constexpr GrStencilSettings gUserToClipDiff( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gInvUserToClipDiff( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kLess_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+/////// |
+// Union |
+ |
+// first pass makes all the passing cases >= just clip bit set. |
+static constexpr GrStencilSettings gUserToClipUnionPass0( |
+ kReplace_StencilOp, |
+ kKeep_StencilOp, |
+ kLEqual_StencilFunc, |
+ 0xffff, |
+ 0x0001, // set clip bit |
+ 0xffff); |
+ |
+// second pass allows anything greater than just clip bit set to pass |
+static constexpr GrStencilSettings gUserToClipUnionPass1( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kLEqual_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+// first pass finds zeros in the user bits and if found sets |
+// the clip bit to 1 |
+static constexpr GrStencilSettings gInvUserToClipUnionPass0( |
+ kReplace_StencilOp, |
+ kKeep_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0x0000 // set clip bit |
+); |
+ |
+// second pass zeros the user bits |
+static constexpr GrStencilSettings gInvUserToClipUnionPass1( |
+ kZero_StencilOp, |
+ kZero_StencilOp, |
+ kLess_StencilFunc, |
+ 0xffff, |
+ 0x0000, |
+ 0xffff // unset clip bit |
+); |
+ |
+/////// |
+// Xor |
+static constexpr GrStencilSettings gUserToClipXorPass0( |
+ kInvert_StencilOp, |
+ kKeep_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, // unset clip bit |
+ 0x0000, |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gUserToClipXorPass1( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kGreater_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gInvUserToClipXorPass0( |
+ kInvert_StencilOp, |
+ kKeep_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, // unset clip bit |
+ 0x0000, |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gInvUserToClipXorPass1( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kLess_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+/////// |
+// Reverse Diff |
+static constexpr GrStencilSettings gUserToClipRDiffPass0( |
+ kInvert_StencilOp, |
+ kZero_StencilOp, |
+ kLess_StencilFunc, |
+ 0xffff, // unset clip bit |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+static constexpr GrStencilSettings gUserToClipRDiffPass1( |
+ kReplace_StencilOp, |
+ kZero_StencilOp, |
+ kEqual_StencilFunc, |
+ 0x0000, // set clip bit |
+ 0x0000, // set clip bit |
+ 0xffff); |
+ |
+// We are looking for stencil values that are all zero. The first pass sets the |
+// clip bit if the stencil is all zeros. The second pass clears the user bits. |
+static constexpr GrStencilSettings gInvUserToClipRDiffPass0( |
+ kInvert_StencilOp, |
+ kZero_StencilOp, |
+ kEqual_StencilFunc, |
+ 0xffff, |
+ 0x0000, |
+ 0x0000 // set clip bit |
+); |
+ |
+static constexpr GrStencilSettings gInvUserToClipRDiffPass1( |
+ kZero_StencilOp, |
+ kZero_StencilOp, |
+ kAlways_StencilFunc, |
+ 0xffff, |
+ 0x0000, |
+ 0xffff // unset clip bit |
+); |
+ |
+/////// |
+// Direct to Stencil |
+ |
+// We can render a clip element directly without first writing to the client |
+// portion of the clip when the fill is not inverse and the set operation will |
+// only modify the in/out status of samples covered by the clip element. |
+ |
+// this one only works if used right after stencil clip was cleared. |
+// Our clip mask creation code doesn't allow midstream replace ops. |
+static constexpr GrStencilSettings gReplaceClip( |
+ kReplace_StencilOp, |
+ kReplace_StencilOp, |
+ kAlways_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0x0000 // set clipBit |
+); |
+ |
+static constexpr GrStencilSettings gUnionClip( |
+ kReplace_StencilOp, |
+ kReplace_StencilOp, |
+ kAlways_StencilFunc, |
+ 0xffff, |
+ 0x0000, // set clip bit |
+ 0x0000 // set clip bit |
+); |
+ |
+static constexpr GrStencilSettings gXorClip( |
+ kInvert_StencilOp, |
+ kInvert_StencilOp, |
+ kAlways_StencilFunc, |
+ 0xffff, |
+ 0x0000, |
+ 0x0000 // set clip bit |
+); |
+ |
+static constexpr GrStencilSettings gDiffClip( |
+ kZero_StencilOp, |
+ kZero_StencilOp, |
+ kAlways_StencilFunc, |
+ 0xffff, |
+ 0x0000, |
+ 0x0000 // set clip bit |
+); |
+ |
+bool GrStencilSettings::GetClipPasses( |
+ SkRegion::Op op, |
+ bool canBeDirect, |
+ unsigned int stencilClipMask, |
+ bool invertedFill, |
+ int* numPasses, |
+ GrStencilSettings settings[kMaxStencilClipPasses]) { |
+ if (canBeDirect && !invertedFill) { |
+ *numPasses = 0; |
+ switch (op) { |
+ case SkRegion::kReplace_Op: |
+ *numPasses = 1; |
+ settings[0] = gReplaceClip; |
+ break; |
+ case SkRegion::kUnion_Op: |
+ *numPasses = 1; |
+ settings[0] = gUnionClip; |
+ break; |
+ case SkRegion::kXOR_Op: |
+ *numPasses = 1; |
+ settings[0] = gXorClip; |
+ break; |
+ case SkRegion::kDifference_Op: |
+ *numPasses = 1; |
+ settings[0] = gDiffClip; |
+ break; |
+ default: // suppress warning |
+ break; |
+ } |
+ if (1 == *numPasses) { |
+ settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[0].fWriteMasks[kFront_Face] |= stencilClipMask; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ settings[0].fWriteMasks[kBack_Face] = |
+ settings[0].fWriteMasks[kFront_Face]; |
+ return true; |
+ } |
+ } |
+ switch (op) { |
+ // if we make the path renderer go to stencil we always give it a |
+ // non-inverted fill and we use the stencil rules on the client->clipbit |
+ // pass to select either the zeros or nonzeros. |
+ case SkRegion::kReplace_Op: |
+ *numPasses= 1; |
+ settings[0] = invertedFill ? gInvUserToClipReplace : |
+ gUserToClipReplace; |
+ settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[0].fFuncMasks[kBack_Face] = |
+ settings[0].fFuncMasks[kFront_Face]; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ break; |
+ case SkRegion::kIntersect_Op: |
+ *numPasses = 1; |
+ settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect; |
+ settings[0].fFuncRefs[kFront_Face] = stencilClipMask; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ break; |
+ case SkRegion::kUnion_Op: |
+ *numPasses = 2; |
+ if (invertedFill) { |
+ settings[0] = gInvUserToClipUnionPass0; |
+ settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[0].fFuncMasks[kBack_Face] = |
+ settings[0].fFuncMasks[kFront_Face]; |
+ settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ settings[0].fWriteMasks[kFront_Face] |= stencilClipMask; |
+ settings[0].fWriteMasks[kBack_Face] = |
+ settings[0].fWriteMasks[kFront_Face]; |
+ |
+ settings[1] = gInvUserToClipUnionPass1; |
+ settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[1].fWriteMasks[kBack_Face] &= |
+ settings[1].fWriteMasks[kFront_Face]; |
+ |
+ } else { |
+ settings[0] = gUserToClipUnionPass0; |
+ settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[0].fFuncMasks[kBack_Face] = |
+ settings[0].fFuncMasks[kFront_Face]; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ |
+ settings[1] = gUserToClipUnionPass1; |
+ settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[1].fFuncRefs[kBack_Face] = |
+ settings[1].fFuncRefs[kFront_Face]; |
+ } |
+ break; |
+ case SkRegion::kXOR_Op: |
+ *numPasses = 2; |
+ if (invertedFill) { |
+ settings[0] = gInvUserToClipXorPass0; |
+ settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[0].fFuncMasks[kBack_Face] = |
+ settings[0].fFuncMasks[kFront_Face]; |
+ |
+ settings[1] = gInvUserToClipXorPass1; |
+ settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[1].fFuncRefs[kBack_Face] = |
+ settings[1].fFuncRefs[kFront_Face]; |
+ } else { |
+ settings[0] = gUserToClipXorPass0; |
+ settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[0].fFuncMasks[kBack_Face] = |
+ settings[0].fFuncMasks[kFront_Face]; |
+ |
+ settings[1] = gUserToClipXorPass1; |
+ settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[1].fFuncRefs[kBack_Face] = |
+ settings[1].fFuncRefs[kFront_Face]; |
+ } |
+ break; |
+ case SkRegion::kDifference_Op: |
+ *numPasses = 1; |
+ settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff; |
+ settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ break; |
+ case SkRegion::kReverseDifference_Op: |
+ if (invertedFill) { |
+ *numPasses = 2; |
+ settings[0] = gInvUserToClipRDiffPass0; |
+ settings[0].fWriteMasks[kFront_Face] |= stencilClipMask; |
+ settings[0].fWriteMasks[kBack_Face] = |
+ settings[0].fWriteMasks[kFront_Face]; |
+ settings[1] = gInvUserToClipRDiffPass1; |
+ settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[1].fWriteMasks[kBack_Face] = |
+ settings[1].fWriteMasks[kFront_Face]; |
+ } else { |
+ *numPasses = 2; |
+ settings[0] = gUserToClipRDiffPass0; |
+ settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; |
+ settings[0].fFuncMasks[kBack_Face] = |
+ settings[0].fFuncMasks[kFront_Face]; |
+ settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[0].fFuncRefs[kBack_Face] = |
+ settings[0].fFuncRefs[kFront_Face]; |
+ |
+ settings[1] = gUserToClipRDiffPass1; |
+ settings[1].fFuncMasks[kFront_Face] |= stencilClipMask; |
+ settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; |
+ settings[1].fFuncMasks[kBack_Face] = |
+ settings[1].fFuncMasks[kFront_Face]; |
+ settings[1].fFuncRefs[kBack_Face] = |
+ settings[1].fFuncRefs[kFront_Face]; |
+ } |
+ break; |
+ default: |
+ SkFAIL("Unknown set op"); |
+ } |
+ return false; |
+} |
+ |
+void GrStencilSettings::genKey(GrProcessorKeyBuilder* b) const { |
+ static const int kCount = sizeof(GrStencilSettings) / sizeof(uint32_t); |
+ GR_STATIC_ASSERT(0 == sizeof(GrStencilSettings) % sizeof(uint32_t)); |
+ uint32_t* key = b->add32n(kCount); |
+ memcpy(key, this, sizeof(GrStencilSettings)); |
+} |