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