| /* | 
 |  * 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 > 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]; | 
 | } | 
 |  | 
 | 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)); | 
 | } |