diff options
Diffstat (limited to 'src/gpu/GrStencilSettings.cpp')
-rw-r--r-- | src/gpu/GrStencilSettings.cpp | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/src/gpu/GrStencilSettings.cpp b/src/gpu/GrStencilSettings.cpp new file mode 100644 index 0000000000..ae8b03b976 --- /dev/null +++ b/src/gpu/GrStencilSettings.cpp @@ -0,0 +1,489 @@ +/* + * 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 "GrStencilSettings.h" + +#include "GrProcessor.h" + +constexpr const GrUserStencilSettings gUnused( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kAlwaysIfInClip, + 0xffff, + GrUserStencilOp::kKeep, + GrUserStencilOp::kKeep, + 0x0000>() +); + +GR_STATIC_ASSERT(kAll_StencilFlags == (gUnused.fFrontFlags[0] & gUnused.fBackFlags[0])); + +const GrUserStencilSettings& GrUserStencilSettings::kUnused = gUnused; + +void GrStencilSettings::reset(const GrUserStencilSettings& user, bool hasStencilClip, + int numStencilBits) { + uint16_t frontFlags = user.fFrontFlags[hasStencilClip]; + if (frontFlags & kSingleSided_StencilFlag) { + fFlags = frontFlags; + if (!this->isDisabled()) { + fFront.reset(user.fFront, hasStencilClip, numStencilBits); + } + return; + } + + uint16_t backFlags = user.fBackFlags[hasStencilClip]; + fFlags = frontFlags & backFlags; + if (this->isDisabled()) { + return; + } + if (!(frontFlags & kDisabled_StencilFlag)) { + fFront.reset(user.fFront, hasStencilClip, numStencilBits); + } else { + fFront.setDisabled(); + } + if (!(backFlags & kDisabled_StencilFlag)) { + fBack.reset(user.fBack, hasStencilClip, numStencilBits); + } else { + fBack.setDisabled(); + } +} + +void GrStencilSettings::reset(const GrStencilSettings& that) { + fFlags = that.fFlags; + if ((kInvalid_PrivateFlag | kDisabled_StencilFlag) & fFlags) { + return; + } + if (!this->isTwoSided()) { + memcpy(&fFront, &that.fFront, sizeof(Face)); + } else { + memcpy(&fFront, &that.fFront, 2 * sizeof(Face)); + GR_STATIC_ASSERT(sizeof(Face) == + offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront)); + } +} + +bool GrStencilSettings::operator==(const GrStencilSettings& that) const { + if ((kInvalid_PrivateFlag | kDisabled_StencilFlag) & (fFlags | that.fFlags)) { + // At least one is invalid and/or disabled. + if (kInvalid_PrivateFlag & (fFlags | that.fFlags)) { + return false; // We never allow invalid stencils to be equal. + } + // They're only equal if both are disabled. + return kDisabled_StencilFlag & (fFlags & that.fFlags); + } + if (kSingleSided_StencilFlag & (fFlags & that.fFlags)) { + return 0 == memcmp(&fFront, &that.fFront, sizeof(Face)); // Both are single sided. + } else { + return 0 == memcmp(&fFront, &that.fFront, 2 * sizeof(Face)); + GR_STATIC_ASSERT(sizeof(Face) == + offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront)); + } + // memcmp relies on GrStencilSettings::Face being tightly packed. + GR_STATIC_ASSERT(0 == offsetof(Face, fRef)); + GR_STATIC_ASSERT(2 == sizeof(Face::fRef)); + GR_STATIC_ASSERT(2 == offsetof(Face, fTest)); + GR_STATIC_ASSERT(2 == sizeof(Face::fTest)); + GR_STATIC_ASSERT(4 == offsetof(Face, fTestMask)); + GR_STATIC_ASSERT(2 == sizeof(Face::fTestMask)); + GR_STATIC_ASSERT(6 == offsetof(Face, fPassOp)); + GR_STATIC_ASSERT(1 == sizeof(Face::fPassOp)); + GR_STATIC_ASSERT(7 == offsetof(Face, fFailOp)); + GR_STATIC_ASSERT(1 == sizeof(Face::fFailOp)); + GR_STATIC_ASSERT(8 == offsetof(Face, fWriteMask)); + GR_STATIC_ASSERT(2 == sizeof(Face::fWriteMask)); + GR_STATIC_ASSERT(10 == sizeof(Face)); +} + +static constexpr GrStencilTest gUserStencilTestToRaw[kGrUserStencilTestCount] = { + // Tests that respect the clip. + GrStencilTest::kAlways, // kAlwaysIfInClip (This is only for when there is not a stencil clip). + GrStencilTest::kEqual, // kEqualIfInClip. + GrStencilTest::kLess, // kLessIfInClip. + GrStencilTest::kLEqual, // kLEqualIfInClip. + + // Tests that ignore the clip. + GrStencilTest::kAlways, + GrStencilTest::kNever, + GrStencilTest::kGreater, + GrStencilTest::kGEqual, + GrStencilTest::kLess, + GrStencilTest::kLEqual, + GrStencilTest::kEqual, + GrStencilTest::kNotEqual +}; + +GR_STATIC_ASSERT(0 == (int)GrUserStencilTest::kAlwaysIfInClip); +GR_STATIC_ASSERT(1 == (int)GrUserStencilTest::kEqualIfInClip); +GR_STATIC_ASSERT(2 == (int)GrUserStencilTest::kLessIfInClip); +GR_STATIC_ASSERT(3 == (int)GrUserStencilTest::kLEqualIfInClip); +GR_STATIC_ASSERT(4 == (int)GrUserStencilTest::kAlways); +GR_STATIC_ASSERT(5 == (int)GrUserStencilTest::kNever); +GR_STATIC_ASSERT(6 == (int)GrUserStencilTest::kGreater); +GR_STATIC_ASSERT(7 == (int)GrUserStencilTest::kGEqual); +GR_STATIC_ASSERT(8 == (int)GrUserStencilTest::kLess); +GR_STATIC_ASSERT(9 == (int)GrUserStencilTest::kLEqual); +GR_STATIC_ASSERT(10 == (int)GrUserStencilTest::kEqual); +GR_STATIC_ASSERT(11 == (int)GrUserStencilTest::kNotEqual); + +static constexpr GrStencilOp gUserStencilOpToRaw[kGrUserStencilOpCount] = { + GrStencilOp::kKeep, + + // Ops that only modify user bits. + GrStencilOp::kZero, + GrStencilOp::kReplace, + GrStencilOp::kInvert, + GrStencilOp::kIncWrap, + GrStencilOp::kDecWrap, + GrStencilOp::kIncClamp, // kIncMaybeClamp. + GrStencilOp::kDecClamp, // kDecMaybeClamp. + + // Ops that only modify the clip bit. + GrStencilOp::kZero, // kZeroClipBit. + GrStencilOp::kReplace, // kSetClipBit. + GrStencilOp::kInvert, // kInvertClipBit. + + // Ops that modify clip and user bits. + GrStencilOp::kReplace, // kSetClipAndReplaceUserBits. + GrStencilOp::kZero // kZeroClipAndUserBits. +}; + +GR_STATIC_ASSERT(0 == (int)GrUserStencilOp::kKeep); +GR_STATIC_ASSERT(1 == (int)GrUserStencilOp::kZero); +GR_STATIC_ASSERT(2 == (int)GrUserStencilOp::kReplace); +GR_STATIC_ASSERT(3 == (int)GrUserStencilOp::kInvert); +GR_STATIC_ASSERT(4 == (int)GrUserStencilOp::kIncWrap); +GR_STATIC_ASSERT(5 == (int)GrUserStencilOp::kDecWrap); +GR_STATIC_ASSERT(6 == (int)GrUserStencilOp::kIncMaybeClamp); +GR_STATIC_ASSERT(7 == (int)GrUserStencilOp::kDecMaybeClamp); +GR_STATIC_ASSERT(8 == (int)GrUserStencilOp::kZeroClipBit); +GR_STATIC_ASSERT(9 == (int)GrUserStencilOp::kSetClipBit); +GR_STATIC_ASSERT(10 == (int)GrUserStencilOp::kInvertClipBit); +GR_STATIC_ASSERT(11 == (int)GrUserStencilOp::kSetClipAndReplaceUserBits); +GR_STATIC_ASSERT(12 == (int)GrUserStencilOp::kZeroClipAndUserBits); + +void GrStencilSettings::Face::reset(const GrUserStencilSettings::Face& user, bool hasStencilClip, + int numStencilBits) { + SkASSERT(user.fTest < (GrUserStencilTest)kGrUserStencilTestCount); + SkASSERT(user.fPassOp < (GrUserStencilOp)kGrUserStencilOpCount); + SkASSERT(user.fFailOp < (GrUserStencilOp)kGrUserStencilOpCount); + SkASSERT(numStencilBits <= 16); + int clipBit = 1 << (numStencilBits - 1); + int userMask = clipBit - 1; + + GrUserStencilOp maxOp = SkTMax(user.fPassOp, user.fFailOp); + SkDEBUGCODE(GrUserStencilOp otherOp = SkTMin(user.fPassOp, user.fFailOp);) + if (maxOp <= kLastUserOnlyStencilOp) { + // Ops that only modify user bits. + fWriteMask = user.fWriteMask & userMask; + SkASSERT(otherOp <= kLastUserOnlyStencilOp); + } else if (maxOp <= kLastClipOnlyStencilOp) { + // Ops that only modify the clip bit. + fWriteMask = clipBit; + SkASSERT(GrUserStencilOp::kKeep == otherOp || + (otherOp > kLastUserOnlyStencilOp && otherOp <= kLastClipOnlyStencilOp)); + } else { + // Ops that modify both clip and user bits. + fWriteMask = clipBit | (user.fWriteMask & userMask); + SkASSERT(GrUserStencilOp::kKeep == otherOp || otherOp > kLastClipOnlyStencilOp); + } + + fFailOp = gUserStencilOpToRaw[(int)user.fFailOp]; + fPassOp = gUserStencilOpToRaw[(int)user.fPassOp]; + + if (!hasStencilClip || user.fTest > kLastClippedStencilTest) { + // Ignore the clip. + fTestMask = user.fTestMask & userMask; + fTest = gUserStencilTestToRaw[(int)user.fTest]; + } else if (GrUserStencilTest::kAlwaysIfInClip != user.fTest) { + // Respect the clip. + fTestMask = clipBit | (user.fTestMask & userMask); + fTest = gUserStencilTestToRaw[(int)user.fTest]; + } else { + // Test only for clip. + fTestMask = clipBit; + fTest = GrStencilTest::kEqual; + } + + fRef = (clipBit | user.fRef) & (fTestMask | fWriteMask); +} + +void GrStencilSettings::Face::setDisabled() { + memset(this, 0, sizeof(*this)); + GR_STATIC_ASSERT(0 == (int)GrStencilTest::kAlways); + GR_STATIC_ASSERT(0 == (int)GrStencilOp::kKeep); +} + +//////////////////////////////////////////////////////////////////////////////// +// Stencil Rules for Merging user stencil space into clip +// + +/////// +// Replace +static constexpr GrUserStencilSettings gUserToClipReplace( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kNotEqual, + 0xffff, + GrUserStencilOp::kSetClipAndReplaceUserBits, + GrUserStencilOp::kZeroClipAndUserBits, + 0xffff>() +); + +static constexpr GrUserStencilSettings gInvUserToClipReplace( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kEqual, + 0xffff, + GrUserStencilOp::kSetClipAndReplaceUserBits, + GrUserStencilOp::kZeroClipAndUserBits, + 0xffff>() +); + +/////// +// Intersect +static constexpr GrUserStencilSettings gUserToClipIsect( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kLessIfInClip, // "0 < userBits" is equivalent to "0 != userBits". + 0xffff, + GrUserStencilOp::kSetClipAndReplaceUserBits, + GrUserStencilOp::kZeroClipAndUserBits, + 0xffff>() +); + +/////// +// Difference +static constexpr GrUserStencilSettings gUserToClipDiff( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kEqualIfInClip, + 0xffff, + GrUserStencilOp::kSetClipAndReplaceUserBits, + GrUserStencilOp::kZeroClipAndUserBits, + 0xffff>() +); + +/////// +// Union +static constexpr GrUserStencilSettings gUserToClipUnion( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kNotEqual, + 0xffff, + GrUserStencilOp::kSetClipAndReplaceUserBits, + GrUserStencilOp::kKeep, + 0xffff>() +); + +static constexpr GrUserStencilSettings gInvUserToClipUnionPass0( // Does not zero user bits. + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kEqual, + 0xffff, + GrUserStencilOp::kSetClipBit, + GrUserStencilOp::kKeep, + 0x0000>() +); + +/////// +// Xor +static constexpr GrUserStencilSettings gUserToClipXorPass0( // Does not zero user bits. + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kNotEqual, + 0xffff, + GrUserStencilOp::kInvertClipBit, + GrUserStencilOp::kKeep, + 0x0000>() +); + +static constexpr GrUserStencilSettings gInvUserToClipXorPass0( // Does not zero user bits. + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kEqual, + 0xffff, + GrUserStencilOp::kInvertClipBit, + GrUserStencilOp::kKeep, + 0x0000>() +); + +/////// +// Reverse Diff +static constexpr GrUserStencilSettings gUserToClipRDiffPass0( // Does not zero user bits. + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kNotEqual, + 0xffff, + GrUserStencilOp::kInvertClipBit, + GrUserStencilOp::kZeroClipBit, + 0x0000>() +); + +static constexpr GrUserStencilSettings gInvUserToClipRDiffPass0( // Does not zero user bits. + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kEqual, + 0xffff, + GrUserStencilOp::kInvertClipBit, + GrUserStencilOp::kZeroClipBit, + 0x0000>() +); + +/////// +// Second pass to clear user bits (only needed sometimes) +static constexpr GrUserStencilSettings gZeroUserBits( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kNotEqual, + 0xffff, + GrUserStencilOp::kZero, + GrUserStencilOp::kKeep, + 0xffff>() +); + +static constexpr const GrUserStencilSettings* gUserToClipTable[2][1 + SkRegion::kLastOp][3] = { + { /* Normal fill. */ + {&gUserToClipDiff, nullptr, nullptr}, // kDifference_Op. + {&gUserToClipIsect, nullptr, nullptr}, // kIntersect_Op. + {&gUserToClipUnion, nullptr, nullptr}, // kUnion_Op. + {&gUserToClipXorPass0, &gZeroUserBits, nullptr}, // kXOR_Op. + {&gUserToClipRDiffPass0, &gZeroUserBits, nullptr}, // kReverseDifference_Op. + {&gUserToClipReplace, nullptr, nullptr} // kReplace_Op. + + }, /* Inverse fill. */ { + {&gUserToClipIsect, nullptr, nullptr}, // ~diff (aka isect). + {&gUserToClipDiff, nullptr, nullptr}, // ~isect (aka diff). + {&gInvUserToClipUnionPass0, &gZeroUserBits, nullptr}, // ~union. + {&gInvUserToClipXorPass0, &gZeroUserBits, nullptr}, // ~xor. + {&gInvUserToClipRDiffPass0, &gZeroUserBits, nullptr}, // ~reverse diff. + {&gInvUserToClipReplace, nullptr, nullptr} // ~replace. + } +}; + +GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op); +GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op); +GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op); +GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op); +GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op); +GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op); + +/////// +// 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 GrUserStencilSettings gReplaceClip( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kAlways, + 0xffff, + GrUserStencilOp::kSetClipBit, + GrUserStencilOp::kSetClipBit, + 0x0000>() +); + +static constexpr GrUserStencilSettings gUnionClip( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kAlwaysIfInClip, + 0xffff, + GrUserStencilOp::kKeep, + GrUserStencilOp::kSetClipBit, + 0x0000>() +); + +static constexpr GrUserStencilSettings gXorClip( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kAlways, + 0xffff, + GrUserStencilOp::kInvertClipBit, + GrUserStencilOp::kInvertClipBit, + 0x0000>() +); + +static constexpr GrUserStencilSettings gDiffClip( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kAlwaysIfInClip, + 0xffff, + GrUserStencilOp::kZeroClipBit, + GrUserStencilOp::kKeep, + 0x0000>() +); + +static constexpr const GrUserStencilSettings* gDirectDrawTable[1 + SkRegion::kLastOp][2] = { + {&gDiffClip, nullptr}, // kDifference_Op. + {nullptr, nullptr}, // kIntersect_Op. + {&gUnionClip, nullptr}, // kUnion_Op. + {&gXorClip, nullptr}, // kXOR_Op. + {nullptr, nullptr}, // kReverseDifference_Op. + {&gReplaceClip, nullptr} // kReplace_Op. +}; + +GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op); +GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op); +GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op); +GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op); +GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op); +GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op); + +GrUserStencilSettings const* const* GrStencilSettings::GetClipPasses(SkRegion::Op op, + bool canBeDirect, + bool invertedFill, + bool* drawDirectToClip) { + SkASSERT((unsigned)op <= SkRegion::kLastOp); + if (canBeDirect && !invertedFill) { // TODO: inverse fill + intersect op can be direct. + GrUserStencilSettings const* const* directPass = gDirectDrawTable[op]; + if (directPass[0]) { + *drawDirectToClip = true; + return directPass; + } + } + *drawDirectToClip = false; + return gUserToClipTable[invertedFill][op]; +} + +void GrStencilSettings::genKey(GrProcessorKeyBuilder* b) const { + b->add32(fFlags); + if (this->isDisabled()) { + return; + } + if (!this->isTwoSided()) { + constexpr int kCount16 = sizeof(Face) / sizeof(uint16_t); + GR_STATIC_ASSERT(0 == sizeof(Face) % sizeof(uint16_t)); + uint16_t* key = reinterpret_cast<uint16_t*>(b->add32n((kCount16 + 1) / 2)); + memcpy(key, &fFront, sizeof(Face)); + key[kCount16] = 0; + GR_STATIC_ASSERT(1 == kCount16 % 2); + } else { + constexpr int kCount32 = (2 * sizeof(Face)) / sizeof(uint32_t); + GR_STATIC_ASSERT(0 == (2 * sizeof(Face)) % sizeof(uint32_t)); + uint32_t* key = b->add32n(kCount32); + memcpy(key, &fFront, 2 * sizeof(Face)); + GR_STATIC_ASSERT(sizeof(Face) == + offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront)); + } + // We rely on GrStencilSettings::Face being tightly packed for the key to be reliable. + GR_STATIC_ASSERT(0 == offsetof(Face, fRef)); + GR_STATIC_ASSERT(2 == sizeof(Face::fRef)); + GR_STATIC_ASSERT(2 == offsetof(Face, fTest)); + GR_STATIC_ASSERT(2 == sizeof(Face::fTest)); + GR_STATIC_ASSERT(4 == offsetof(Face, fTestMask)); + GR_STATIC_ASSERT(2 == sizeof(Face::fTestMask)); + GR_STATIC_ASSERT(6 == offsetof(Face, fPassOp)); + GR_STATIC_ASSERT(1 == sizeof(Face::fPassOp)); + GR_STATIC_ASSERT(7 == offsetof(Face, fFailOp)); + GR_STATIC_ASSERT(1 == sizeof(Face::fFailOp)); + GR_STATIC_ASSERT(8 == offsetof(Face, fWriteMask)); + GR_STATIC_ASSERT(2 == sizeof(Face::fWriteMask)); + GR_STATIC_ASSERT(10 == sizeof(Face)); +} |