blob: 94559ab68eb6333ccd09692eb7e2df4acf8535ba [file] [log] [blame]
/*
* 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));
}