/* * 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) { SkASSERT(frontFlags == user.fBackFlags[hasStencilClip]); 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 if (kSingleSided_StencilFlag & (fFlags | that.fFlags)) { return false; } 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 > 0 && 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]; } static constexpr GrUserStencilSettings gZeroStencilClipBit( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kAlways, 0xffff, GrUserStencilOp::kZeroClipBit, GrUserStencilOp::kZeroClipBit, 0x0000>() ); const GrUserStencilSettings* GrStencilSettings::SetClipBitSettings(bool setToInside) { return setToInside ? &gReplaceClip : &gZeroStencilClipBit; } 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)); }