Separate user and raw stencil settings

Adds a new GrUserStencilSettings class that describes in abstract terms
how a draw will use the stencil (e.g. kAlwaysIfInClip, kSetClipBit,
etc.). GrPipelineBuilder now only defines the GrUserStencilSettings.
When the GrPipeline is finalized, the user stencil settings are then
translated into concrete GrStencilSettings.

At this point, GrClipMaskManager only needs to tell the GrAppliedClip
whether or not there is a stencil clip. It does not need to modify
stencil settings and GrPipelineBuilder does not need
AutoRestoreStencil.

This is one step of the stencil overhaul. In the future it will also
allow us to clean up the special case handling for nvpr and the
stateful fClipMode member of GrClipMaskManager.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1962243002

Committed: https://skia.googlesource.com/skia/+/12dbb3947e1aaf205b4fcf13b40e54e50650eb37

Review-Url: https://codereview.chromium.org/1962243002
This commit is contained in:
cdalton 2016-05-11 13:58:08 -07:00 committed by Commit bot
parent f8237781d2
commit 93a379bd4d
33 changed files with 1365 additions and 1479 deletions

View File

@ -170,10 +170,10 @@
'<(skia_src_path)/gpu/GrResourceProvider.h',
'<(skia_src_path)/gpu/GrShape.cpp',
'<(skia_src_path)/gpu/GrShape.h',
'<(skia_src_path)/gpu/GrStencil.cpp',
'<(skia_src_path)/gpu/GrStencil.h',
'<(skia_src_path)/gpu/GrStencilAttachment.cpp',
'<(skia_src_path)/gpu/GrStencilAttachment.h',
'<(skia_src_path)/gpu/GrStencilSettings.cpp',
'<(skia_src_path)/gpu/GrStencilSettings.h',
'<(skia_src_path)/gpu/GrStyle.cpp',
'<(skia_src_path)/gpu/GrStyle.h',
'<(skia_src_path)/gpu/GrTessellator.cpp',
@ -200,6 +200,7 @@
'<(skia_src_path)/gpu/GrTextureToYUVPlanes.h',
'<(skia_src_path)/gpu/GrTextureAccess.cpp',
'<(skia_src_path)/gpu/GrTRecorder.h',
'<(skia_src_path)/gpu/GrUserStencilSettings.h',
'<(skia_src_path)/gpu/GrXferProcessor.cpp',
'<(skia_src_path)/gpu/GrYUVProvider.cpp',
'<(skia_src_path)/gpu/GrYUVProvider.h',

View File

@ -62,7 +62,7 @@ static void draw_non_aa_rect(GrDrawTarget* drawTarget,
// optionally, set 'prOut' to NULL. If not, return false (and, optionally, set
// 'prOut' to the non-SW path renderer that will do the job).
bool GrClipMaskManager::PathNeedsSWRenderer(GrContext* context,
bool isStencilDisabled,
bool hasUserStencilSettings,
const GrRenderTarget* rt,
const SkMatrix& viewMatrix,
const Element* element,
@ -104,7 +104,7 @@ bool GrClipMaskManager::PathNeedsSWRenderer(GrContext* context,
canDrawArgs.fPath = &path;
canDrawArgs.fStyle = &GrStyle::SimpleFill();
canDrawArgs.fAntiAlias = element->isAA();
canDrawArgs.fIsStencilDisabled = isStencilDisabled;
canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
canDrawArgs.fIsStencilBufferMSAA = rt->isStencilBufferMultisampled();
// the 'false' parameter disallows use of the SW path renderer
@ -124,10 +124,10 @@ GrPathRenderer* GrClipMaskManager::GetPathRenderer(GrContext* context,
const SkMatrix& viewMatrix,
const SkClipStack::Element* element) {
GrPathRenderer* pr;
static const bool kNeedsStencil = true;
static const bool kStencilIsDisabled = true;
constexpr bool kNeedsStencil = true;
constexpr bool kHasUserStencilSettings = false;
PathNeedsSWRenderer(context,
kStencilIsDisabled,
kHasUserStencilSettings,
texture->asRenderTarget(),
viewMatrix,
element,
@ -179,7 +179,7 @@ bool GrClipMaskManager::UseSWOnlyPath(GrContext* context,
bool needsStencil = invert ||
SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op;
if (PathNeedsSWRenderer(context, pipelineBuilder.getStencil().isDisabled(),
if (PathNeedsSWRenderer(context, pipelineBuilder.hasUserStencilSettings(),
rt, translate, element, nullptr, needsStencil)) {
return true;
}
@ -317,13 +317,11 @@ static void add_rect_to_clip(const GrClip& clip, const SkRect& devRect, GrClip*
}
bool GrClipMaskManager::setupScissorClip(const GrPipelineBuilder& pipelineBuilder,
GrPipelineBuilder::AutoRestoreStencil* ars,
const SkIRect& clipScissor,
const SkRect* devBounds,
GrAppliedClip* out) {
if (kRespectClip_StencilClipMode == fClipMode) {
fClipMode = kIgnoreClip_StencilClipMode;
}
SkASSERT(kModifyClip_StencilClipMode != fClipMode); // TODO: Remove fClipMode.
fClipMode = kIgnoreClip_StencilClipMode;
GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
@ -340,13 +338,11 @@ bool GrClipMaskManager::setupScissorClip(const GrPipelineBuilder& pipelineBuilde
if (scissor->contains(clipSpaceRTIBounds)) {
// This counts as wide open
this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
if (clipSpaceRTIBounds.intersect(*scissor)) {
out->fScissorState.set(clipSpaceRTIBounds);
this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
return false;
@ -356,7 +352,6 @@ bool GrClipMaskManager::setupScissorClip(const GrPipelineBuilder& pipelineBuilde
// sort out what kind of clip mask needs to be created: alpha, stencil,
// scissor, or entirely software
bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
GrPipelineBuilder::AutoRestoreStencil* ars,
const SkRect* devBounds,
GrAppliedClip* out) {
if (kRespectClip_StencilClipMode == fClipMode) {
@ -382,7 +377,6 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
const GrClip& clip = doDevBoundsClip ? devBoundsClip : pipelineBuilder.clip();
if (clip.isWideOpen(clipSpaceRTIBounds)) {
this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
@ -396,7 +390,7 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
SkIRect scissor = clip.irect();
if (scissor.intersect(clipSpaceRTIBounds)) {
out->fScissorState.set(scissor);
this->setPipelineBuilderStencil(pipelineBuilder, ars);
out->fHasStencilClip = kIgnoreClip_StencilClipMode != fClipMode;
return true;
}
return false;
@ -424,7 +418,6 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
if (elements.isEmpty()) {
if (GrReducedClip::kAllIn_InitialState == initialState) {
if (clipSpaceIBounds == clipSpaceRTIBounds) {
this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
} else {
@ -434,6 +427,8 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
} break;
}
SkASSERT(kIgnoreClip_StencilClipMode == fClipMode); // TODO: Remove fClipMode.
// An element count of 4 was chosen because of the common pattern in Blink of:
// isect RR
// diff RR
@ -453,7 +448,7 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
// color buffer anyway, so we may as well use coverage AA if nothing else in the pipe
// is multisampled.
disallowAnalyticAA = pipelineBuilder.isHWAntialias() ||
!pipelineBuilder.getStencil().isDisabled();
pipelineBuilder.hasUserStencilSettings();
}
const GrFragmentProcessor* clipFP = nullptr;
if (elements.isEmpty() ||
@ -466,7 +461,6 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
!SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) {
out->fScissorState.set(scissorSpaceIBounds);
}
this->setPipelineBuilderStencil(pipelineBuilder, ars);
out->fClipCoverageFP.reset(clipFP);
return true;
}
@ -508,7 +502,6 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
rtSpaceMaskBounds.offset(-clip.origin());
out->fClipCoverageFP.reset(create_fp_for_mask(result, rtSpaceMaskBounds));
this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
// if alpha clip mask creation fails fall through to the non-AA code paths
@ -529,13 +522,14 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
SkIRect scissorSpaceIBounds(clipSpaceIBounds);
scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
out->fScissorState.set(scissorSpaceIBounds);
this->setPipelineBuilderStencil(pipelineBuilder, ars);
SkASSERT(kRespectClip_StencilClipMode == fClipMode); // TODO: Remove fClipMode.
out->fHasStencilClip = true;
return true;
}
static bool stencil_element(GrDrawContext* dc,
const SkIRect* scissorRect,
const GrStencilSettings& ss,
const GrUserStencilSettings* ss,
const SkMatrix& viewMatrix,
const SkClipStack::Element* element) {
@ -681,28 +675,32 @@ GrTexture* GrClipMaskManager::CreateAlphaClipMask(GrContext* context,
// draw directly into the result with the stencil set to make the pixels affected
// by the clip shape be non-zero.
static constexpr GrStencilSettings kStencilInElement(
kReplace_StencilOp,
kReplace_StencilOp,
kAlways_StencilFunc,
0xffff,
0xffff,
0xffff);
if (!stencil_element(dc.get(), &maskSpaceIBounds, kStencilInElement,
static constexpr GrUserStencilSettings kStencilInElement(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlways,
0xffff,
GrUserStencilOp::kReplace,
GrUserStencilOp::kReplace,
0xffff>()
);
if (!stencil_element(dc.get(), &maskSpaceIBounds, &kStencilInElement,
translate, element)) {
texture->resourcePriv().removeUniqueKey();
return nullptr;
}
// Draw to the exterior pixels (those with a zero stencil value).
static constexpr GrStencilSettings kDrawOutsideElement(
kZero_StencilOp,
kZero_StencilOp,
kEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
if (!dc->drawContextPriv().drawAndStencilRect(&maskSpaceIBounds, kDrawOutsideElement,
static constexpr GrUserStencilSettings kDrawOutsideElement(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kEqual,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kZero,
0xffff>()
);
if (!dc->drawContextPriv().drawAndStencilRect(&maskSpaceIBounds, &kDrawOutsideElement,
op, !invert, false,
translate,
SkRect::Make(clipSpaceIBounds))) {
@ -753,10 +751,6 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
GrClip clip(stencilSpaceIBounds);
int clipBit = stencilAttachment->bits();
SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
clipBit = (1 << (clipBit-1));
fDrawTarget->cmmAccess().clearStencilClip(stencilSpaceIBounds,
GrReducedClip::kAllIn_InitialState == initialState, rt);
@ -798,7 +792,7 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
clipPath.toggleInverseFillType();
}
SkASSERT(pipelineBuilder.getStencil().isDisabled());
SkASSERT(!pipelineBuilder.hasUserStencilSettings());
GrPathRenderer::CanDrawPathArgs canDrawArgs;
canDrawArgs.fShaderCaps = this->getContext()->caps()->shaderCaps();
@ -806,7 +800,7 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
canDrawArgs.fPath = &clipPath;
canDrawArgs.fStyle = &GrStyle::SimpleFill();
canDrawArgs.fAntiAlias = false;
canDrawArgs.fIsStencilDisabled = pipelineBuilder.getStencil().isDisabled();
canDrawArgs.fHasUserStencilSettings = pipelineBuilder.hasUserStencilSettings();
canDrawArgs.fIsStencilBufferMSAA = rt->isStencilBufferMultisampled();
pr = this->getContext()->drawingManager()->getPathRenderer(canDrawArgs, false,
@ -817,40 +811,36 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
}
}
int passes;
GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
bool canRenderDirectToStencil =
GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
bool canDrawDirectToClip; // Given the renderer, the element,
// fill rule, and set operation can
// we render the element directly to
// stencil bit used for clipping.
canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,
canRenderDirectToStencil,
clipBit,
fillInverted,
&passes,
stencilSettings);
bool drawDirectToClip; // Given the renderer, the element,
// fill rule, and set operation should
// we render the element directly to
// stencil bit used for clipping.
GrUserStencilSettings const* const* stencilPasses =
GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
&drawDirectToClip);
// draw the element to the client stencil bits if necessary
if (!canDrawDirectToClip) {
static constexpr GrStencilSettings kDrawToStencil(
kIncClamp_StencilOp,
kIncClamp_StencilOp,
kAlways_StencilFunc,
0xffff,
0x0000,
0xffff);
if (!drawDirectToClip) {
static constexpr GrUserStencilSettings kDrawToStencil(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kAlways,
0xffff,
GrUserStencilOp::kIncMaybeClamp,
GrUserStencilOp::kIncMaybeClamp,
0xffff>()
);
if (Element::kRect_Type == element->getType()) {
*pipelineBuilder.stencil() = kDrawToStencil;
pipelineBuilder.setUserStencil(&kDrawToStencil);
draw_non_aa_rect(fDrawTarget, pipelineBuilder, GrColor_WHITE, viewMatrix,
element->getRect());
} else {
if (!clipPath.isEmpty()) {
if (canRenderDirectToStencil) {
*pipelineBuilder.stencil() = kDrawToStencil;
pipelineBuilder.setUserStencil(&kDrawToStencil);
GrPathRenderer::DrawPathArgs args;
args.fTarget = fDrawTarget;
@ -879,10 +869,10 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
// now we modify the clip bit by rendering either the clip
// element directly or a bounding rect of the entire clip.
fClipMode = kModifyClip_StencilClipMode;
for (int p = 0; p < passes; ++p) {
*pipelineBuilder.stencil() = stencilSettings[p];
for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
pipelineBuilder.setUserStencil(*pass);
if (canDrawDirectToClip) {
if (drawDirectToClip) {
if (Element::kRect_Type == element->getType()) {
draw_non_aa_rect(fDrawTarget, pipelineBuilder, GrColor_WHITE, viewMatrix,
element->getRect());
@ -912,165 +902,6 @@ bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
return true;
}
// mapping of clip-respecting stencil funcs to normal stencil funcs
// mapping depends on whether stencil-clipping is in effect.
static const GrStencilFunc
gSpecialToBasicStencilFunc[2][kClipStencilFuncCnt] = {
{// Stencil-Clipping is DISABLED, we are effectively always inside the clip
// In the Clip Funcs
kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc
kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
kLess_StencilFunc, // kLessIfInClip_StencilFunc
kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
// Special in the clip func that forces user's ref to be 0.
kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc
// make ref 0 and do normal nequal.
},
{// Stencil-Clipping is ENABLED
// In the Clip Funcs
kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc
// eq stencil clip bit, mask
// out user bits.
kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
// add stencil bit to mask and ref
kLess_StencilFunc, // kLessIfInClip_StencilFunc
kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
// for both of these we can add
// the clip bit to the mask and
// ref and compare as normal
// Special in the clip func that forces user's ref to be 0.
kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc
// make ref have only the clip bit set
// and make comparison be less
// 10..0 < 1..user_bits..
}
};
void GrClipMaskManager::setPipelineBuilderStencil(const GrPipelineBuilder& pipelineBuilder,
GrPipelineBuilder::AutoRestoreStencil* ars) {
// We make two copies of the StencilSettings here (except in the early
// exit scenario. One copy from draw state to the stack var. Then another
// from the stack var to the gpu. We could make this class hold a ptr to
// GrGpu's fStencilSettings and eliminate the stack copy here.
// use stencil for clipping if clipping is enabled and the clip
// has been written into the stencil.
GrStencilSettings settings;
// The GrGpu client may not be using the stencil buffer but we may need to
// enable it in order to respect a stencil clip.
if (pipelineBuilder.getStencil().isDisabled()) {
if (GrClipMaskManager::kRespectClip_StencilClipMode == fClipMode) {
static constexpr GrStencilSettings kBasicApplyClipSettings(
kKeep_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0x0000,
0x0000,
0x0000);
settings = kBasicApplyClipSettings;
} else {
return;
}
} else {
settings = pipelineBuilder.getStencil();
}
int stencilBits = 0;
GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
GrStencilAttachment* stencilAttachment = this->resourceProvider()->attachStencilAttachment(rt);
if (stencilAttachment) {
stencilBits = stencilAttachment->bits();
}
SkASSERT(this->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp());
SkASSERT(this->caps()->twoSidedStencilSupport() || !settings.isTwoSided());
this->adjustStencilParams(&settings, fClipMode, stencilBits);
ars->set(&pipelineBuilder);
ars->setStencil(settings);
}
void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings,
StencilClipMode mode,
int stencilBitCnt) {
SkASSERT(stencilBitCnt > 0);
if (kModifyClip_StencilClipMode == mode) {
// We assume that this clip manager itself is drawing to the GrGpu and
// has already setup the correct values.
return;
}
unsigned int clipBit = (1 << (stencilBitCnt - 1));
unsigned int userBits = clipBit - 1;
GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
bool twoSided = this->caps()->twoSidedStencilSupport();
bool finished = false;
while (!finished) {
GrStencilFunc func = settings->func(face);
uint16_t writeMask = settings->writeMask(face);
uint16_t funcMask = settings->funcMask(face);
uint16_t funcRef = settings->funcRef(face);
SkASSERT((unsigned) func < kStencilFuncCnt);
writeMask &= userBits;
if (func >= kBasicStencilFuncCnt) {
int respectClip = kRespectClip_StencilClipMode == mode;
if (respectClip) {
switch (func) {
case kAlwaysIfInClip_StencilFunc:
funcMask = clipBit;
funcRef = clipBit;
break;
case kEqualIfInClip_StencilFunc:
case kLessIfInClip_StencilFunc:
case kLEqualIfInClip_StencilFunc:
funcMask = (funcMask & userBits) | clipBit;
funcRef = (funcRef & userBits) | clipBit;
break;
case kNonZeroIfInClip_StencilFunc:
funcMask = (funcMask & userBits) | clipBit;
funcRef = clipBit;
break;
default:
SkFAIL("Unknown stencil func");
}
} else {
funcMask &= userBits;
funcRef &= userBits;
}
const GrStencilFunc* table =
gSpecialToBasicStencilFunc[respectClip];
func = table[func - kBasicStencilFuncCnt];
SkASSERT(func >= 0 && func < kBasicStencilFuncCnt);
} else {
funcMask &= userBits;
funcRef &= userBits;
}
settings->setFunc(face, func);
settings->setWriteMask(face, writeMask);
settings->setFuncMask(face, funcMask);
settings->setFuncRef(face, funcRef);
if (GrStencilSettings::kFront_Face == face) {
face = GrStencilSettings::kBack_Face;
finished = !twoSided;
} else {
finished = true;
}
}
if (!twoSided) {
settings->copyFrontSettingsToBack();
}
}
////////////////////////////////////////////////////////////////////////////////
GrTexture* GrClipMaskManager::CreateSoftwareClipMask(GrContext* context,
int32_t elementsGenID,
@ -1148,13 +979,3 @@ GrTexture* GrClipMaskManager::CreateSoftwareClipMask(GrContext* context,
return result;
}
////////////////////////////////////////////////////////////////////////////////
void GrClipMaskManager::adjustPathStencilParams(const GrStencilAttachment* stencilAttachment,
GrStencilSettings* settings) {
if (stencilAttachment) {
int stencilBits = stencilAttachment->bits();
this->adjustStencilParams(settings, fClipMode, stencilBits);
}
}

View File

@ -9,7 +9,6 @@
#include "GrPipelineBuilder.h"
#include "GrReducedClip.h"
#include "GrStencil.h"
#include "GrTexture.h"
#include "SkClipStack.h"
#include "SkDeque.h"
@ -33,13 +32,15 @@ class SkPath;
*/
class GrAppliedClip : public SkNoncopyable {
public:
GrAppliedClip() {}
GrAppliedClip() : fHasStencilClip(false) {}
const GrFragmentProcessor* clipCoverageFragmentProcessor() const { return fClipCoverageFP; }
const GrScissorState& scissorState() const { return fScissorState; }
bool hasStencilClip() const { return fHasStencilClip; }
private:
SkAutoTUnref<const GrFragmentProcessor> fClipCoverageFP;
GrScissorState fScissorState;
bool fHasStencilClip;
friend class GrClipMaskManager;
typedef SkNoncopyable INHERITED;
@ -60,30 +61,23 @@ public:
/**
* Creates a clip mask if necessary as a stencil buffer or alpha texture
* and sets the GrGpu's scissor and stencil state. If the return is false
* then the draw can be skipped. The AutoRestoreEffects is initialized by
* the manager when it must install additional effects to implement the
* clip. devBounds is optional but can help optimize clipping.
* then the draw can be skipped. devBounds is optional but can help optimize
* clipping.
*/
bool setupClipping(const GrPipelineBuilder&,
GrPipelineBuilder::AutoRestoreStencil*,
const SkRect* devBounds,
GrAppliedClip*);
bool setupClipping(const GrPipelineBuilder&, const SkRect* devBounds, GrAppliedClip*);
bool setupScissorClip(const GrPipelineBuilder& pipelineBuilder,
GrPipelineBuilder::AutoRestoreStencil* ars,
const SkIRect& scissor,
const SkRect* devBounds,
GrAppliedClip* out);
void adjustPathStencilParams(const GrStencilAttachment*, GrStencilSettings*);
private:
inline GrContext* getContext();
inline const GrCaps* caps() const;
inline GrResourceProvider* resourceProvider();
static bool PathNeedsSWRenderer(GrContext* context,
bool isStencilDisabled,
bool hasUserStencilSettings,
const GrRenderTarget* rt,
const SkMatrix& viewMatrix,
const SkClipStack::Element* element,
@ -150,21 +144,6 @@ private:
const SkVector& clipToMaskOffset,
const GrReducedClip::ElementList& elements);
/**
* Called prior to return control back the GrGpu in setupClipping. It updates the
* GrPipelineBuilder with stencil settings that account for stencil-based clipping.
*/
void setPipelineBuilderStencil(const GrPipelineBuilder&,
GrPipelineBuilder::AutoRestoreStencil*);
/**
* Adjusts the stencil settings to account for interaction with stencil
* clipping.
*/
void adjustStencilParams(GrStencilSettings* settings,
StencilClipMode mode,
int stencilBitCnt);
GrTexture* createCachedMask(int width, int height, const GrUniqueKey& key, bool renderTarget);
static const int kMaxAnalyticElements = 4;

View File

@ -375,7 +375,7 @@ void GrDrawContext::drawRect(const GrClip& clip,
}
bool GrDrawContextPriv::drawAndStencilRect(const SkIRect* scissorRect,
const GrStencilSettings& ss,
const GrUserStencilSettings* ss,
SkRegion::Op op,
bool invert,
bool doAA,
@ -397,7 +397,7 @@ bool GrDrawContextPriv::drawAndStencilRect(const SkIRect* scissorRect,
GrPipelineBuilder pipelineBuilder(paint,
fDrawContext->accessRenderTarget(),
GrClip::WideOpen());
pipelineBuilder.setStencil(ss);
pipelineBuilder.setUserStencil(ss);
fDrawContext->getDrawTarget()->drawBatch(pipelineBuilder, batch, scissorRect);
return true;
@ -855,7 +855,7 @@ void GrDrawContext::drawPath(const GrClip& clip,
}
bool GrDrawContextPriv::drawAndStencilPath(const SkIRect* scissorRect,
const GrStencilSettings& ss,
const GrUserStencilSettings* ss,
SkRegion::Op op,
bool invert,
bool doAA,
@ -880,7 +880,7 @@ bool GrDrawContextPriv::drawAndStencilPath(const SkIRect* scissorRect,
// aa. If we have some future driver-mojo path AA that can do the right
// thing WRT to the blend then we'll need some query on the PR.
bool useCoverageAA = doAA && !fDrawContext->fRenderTarget->isUnifiedMultisampled();
bool isStencilDisabled = true;
bool hasUserStencilSettings = (&GrUserStencilSettings::kUnused != ss);
bool isStencilBufferMSAA = fDrawContext->fRenderTarget->isStencilBufferMultisampled();
const GrPathRendererChain::DrawType type =
@ -893,7 +893,7 @@ bool GrDrawContextPriv::drawAndStencilPath(const SkIRect* scissorRect,
canDrawArgs.fPath = &path;
canDrawArgs.fStyle = &GrStyle::SimpleFill();
canDrawArgs.fAntiAlias = useCoverageAA;
canDrawArgs.fIsStencilDisabled = isStencilDisabled;
canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
canDrawArgs.fIsStencilBufferMSAA = isStencilBufferMSAA;
// Don't allow the SW renderer
@ -913,7 +913,7 @@ bool GrDrawContextPriv::drawAndStencilPath(const SkIRect* scissorRect,
}
GrPipelineBuilder pipelineBuilder(paint, fDrawContext->accessRenderTarget(), clip);
pipelineBuilder.setStencil(ss);
pipelineBuilder.setUserStencil(ss);
GrPathRenderer::DrawPathArgs args;
args.fTarget = fDrawContext->getDrawTarget();
@ -939,7 +939,7 @@ void GrDrawContext::internalDrawPath(const GrClip& clip,
SkASSERT(!origPath.isEmpty());
bool useCoverageAA = should_apply_coverage_aa(paint, fRenderTarget.get());
const bool isStencilDisabled = true;
constexpr bool kHasUserStencilSettings = false;
bool isStencilBufferMSAA = fRenderTarget->isStencilBufferMultisampled();
const GrPathRendererChain::DrawType type =
@ -955,7 +955,7 @@ void GrDrawContext::internalDrawPath(const GrClip& clip,
canDrawArgs.fPath = &origPath;
canDrawArgs.fStyle = &origStyle;
canDrawArgs.fAntiAlias = useCoverageAA;
canDrawArgs.fIsStencilDisabled = isStencilDisabled;
canDrawArgs.fHasUserStencilSettings = kHasUserStencilSettings;
canDrawArgs.fIsStencilBufferMSAA = isStencilBufferMSAA;
// Try a 1st time without applying any of the style to the geometry (and barring sw)

View File

@ -10,7 +10,7 @@
#include "GrDrawContext.h"
class GrStencilSettings;
struct GrUserStencilSettings;
/** Class that adds methods to GrDrawContext that are only intended for use internal to Skia.
This class is purely a privileged window into GrDrawContext. It should never have additional
@ -18,7 +18,7 @@ class GrStencilSettings;
class GrDrawContextPriv {
public:
bool drawAndStencilRect(const SkIRect* scissorRect,
const GrStencilSettings&,
const GrUserStencilSettings*,
SkRegion::Op op,
bool invert,
bool doAA,
@ -26,7 +26,7 @@ public:
const SkRect&);
bool drawAndStencilPath(const SkIRect* scissorRect,
const GrStencilSettings&,
const GrUserStencilSettings*,
SkRegion::Op op,
bool invert,
bool doAA,

View File

@ -16,6 +16,7 @@
#include "GrRenderTarget.h"
#include "GrResourceProvider.h"
#include "GrRenderTargetPriv.h"
#include "GrStencilAttachment.h"
#include "GrSurfacePriv.h"
#include "GrTexture.h"
#include "gl/GrGLRenderTarget.h"
@ -235,17 +236,16 @@ void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder,
GrDrawBatch* batch,
const SkIRect* scissorRect) {
// Setup clip
GrPipelineBuilder::AutoRestoreStencil ars;
GrAppliedClip clip;
if (scissorRect) {
SkASSERT(GrClip::kWideOpen_ClipType == pipelineBuilder.clip().clipType());
if (!fClipMaskManager->setupScissorClip(pipelineBuilder, &ars, *scissorRect,
if (!fClipMaskManager->setupScissorClip(pipelineBuilder, *scissorRect,
&batch->bounds(), &clip)) {
return;
}
} else {
if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &batch->bounds(), &clip)) {
if (!fClipMaskManager->setupClipping(pipelineBuilder, &batch->bounds(), &clip)) {
return;
}
}
@ -257,7 +257,8 @@ void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder,
}
GrPipeline::CreateArgs args;
if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(), batch)) {
if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(),
clip.hasStencilClip(), batch)) {
return;
}
@ -269,34 +270,36 @@ void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder,
this->recordBatch(batch);
}
void GrDrawTarget::getPathStencilSettingsForFilltype(GrPathRendering::FillType fill,
const GrStencilAttachment* sb,
GrStencilSettings* outStencilSettings) {
static constexpr GrStencilSettings kWindingStencilSettings(
kIncClamp_StencilOp,
kIncClamp_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xFFFF, 0xFFFF, 0xFFFF
inline static const GrUserStencilSettings& get_path_stencil_settings_for_fill(
GrPathRendering::FillType fill) {
static constexpr GrUserStencilSettings kWindingStencilSettings(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlwaysIfInClip,
0xffff,
GrUserStencilOp::kIncMaybeClamp, // TODO: Use wrap ops for NVPR.
GrUserStencilOp::kIncMaybeClamp,
0xffff>()
);
static constexpr GrStencilSettings kEvenODdStencilSettings(
kInvert_StencilOp,
kInvert_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xFFFF, 0xFFFF, 0xFFFF
static constexpr GrUserStencilSettings kEvenOddStencilSettings(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlwaysIfInClip,
0xffff,
GrUserStencilOp::kInvert,
GrUserStencilOp::kInvert,
0xffff>()
);
switch (fill) {
default:
SkFAIL("Unexpected path fill.");
case GrPathRendering::kWinding_FillType:
*outStencilSettings = kWindingStencilSettings;
break;
return kWindingStencilSettings;
case GrPathRendering::kEvenOdd_FillType:
*outStencilSettings = kEvenODdStencilSettings;
break;
return kEvenOddStencilSettings;
}
fClipMaskManager->adjustPathStencilParams(sb, outStencilSettings);
}
void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder,
@ -308,9 +311,8 @@ void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder,
SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
// Setup clip
GrPipelineBuilder::AutoRestoreStencil ars;
GrAppliedClip clip;
if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, nullptr, &clip)) {
if (!fClipMaskManager->setupClipping(pipelineBuilder, nullptr, &clip)) {
return;
}
@ -320,15 +322,15 @@ void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder,
arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
}
// set stencil settings for path
GrStencilSettings stencilSettings;
GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt);
this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings);
GrStencilAttachment* stencilAttachment = fResourceProvider->attachStencilAttachment(rt);
GrBatch* batch = GrStencilPathBatch::Create(viewMatrix,
pipelineBuilder.isHWAntialias(),
stencilSettings, clip.scissorState(),
get_path_stencil_settings_for_fill(fill),
clip.hasStencilClip(),
stencilAttachment->bits(),
clip.scissorState(),
pipelineBuilder.getRenderTarget(),
path);
this->recordBatch(batch);
@ -343,9 +345,8 @@ void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder,
// batches.
SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
GrPipelineBuilder::AutoRestoreStencil ars;
GrAppliedClip clip;
if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &batch->bounds(), &clip)) {
if (!fClipMaskManager->setupClipping(pipelineBuilder, &batch->bounds(), &clip)) {
return;
}
@ -356,14 +357,16 @@ void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder,
}
// Ensure the render target has a stencil buffer and get the stencil settings.
GrStencilSettings stencilSettings;
GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt);
this->getPathStencilSettingsForFilltype(batch->fillType(), sb, &stencilSettings);
batch->setStencilSettings(stencilSettings);
// TODO: Move this step into GrDrawPathPath::onPrepare().
batch->setStencilSettings(get_path_stencil_settings_for_fill(batch->fillType()),
clip.hasStencilClip(),
sb->bits());
GrPipeline::CreateArgs args;
if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(), batch)) {
if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(),
clip.hasStencilClip(), batch)) {
return;
}
@ -547,11 +550,20 @@ void GrDrawTarget::forwardCombine() {
bool GrDrawTarget::installPipelineInDrawBatch(const GrPipelineBuilder* pipelineBuilder,
const GrScissorState* scissor,
bool hasStencilClip,
GrDrawBatch* batch) {
GrPipeline::CreateArgs args;
args.fPipelineBuilder = pipelineBuilder;
args.fCaps = this->caps();
args.fScissor = scissor;
if (pipelineBuilder->hasUserStencilSettings() || hasStencilClip) {
GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt);
args.fNumStencilBits = sb->bits();
} else {
args.fNumStencilBits = 0;
}
args.fHasStencilClip = hasStencilClip;
batch->getPipelineOptimizations(&args.fOpts);
GrScissorState finalScissor;
if (args.fOpts.fOverrides.fUsePLSDstRead) {

View File

@ -223,6 +223,7 @@ private:
void forwardCombine();
bool installPipelineInDrawBatch(const GrPipelineBuilder* pipelineBuilder,
const GrScissorState* scissor,
bool hasStencilClip,
GrDrawBatch* batch);
// Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required
@ -233,11 +234,6 @@ private:
GrXferProcessor::DstTexture*,
const SkRect& batchBounds);
// Check to see if this set of draw commands has been sent out
void getPathStencilSettingsForFilltype(GrPathRendering::FillType,
const GrStencilAttachment*,
GrStencilSettings*);
void addDependency(GrDrawTarget* dependedOn);
// Used only by CMM.

View File

@ -10,7 +10,6 @@
#include "GrPipelineBuilder.h"
#include "GrProgramDesc.h"
#include "GrStencil.h"
#include "GrSwizzle.h"
#include "GrAllocator.h"
#include "GrTextureParamsAdjuster.h"
@ -34,6 +33,7 @@ class GrPipeline;
class GrPrimitiveProcessor;
class GrRenderTarget;
class GrStencilAttachment;
class GrStencilSettings;
class GrSurface;
class GrTexture;
@ -485,17 +485,6 @@ public:
virtual void resetShaderCacheForTesting() const {}
protected:
// Functions used to map clip-respecting stencil tests into normal
// stencil funcs supported by GPUs.
static GrStencilFunc ConvertStencilFunc(bool stencilInClip,
GrStencilFunc func);
static void ConvertStencilFuncAndMask(GrStencilFunc func,
bool clipInStencil,
unsigned int clipBit,
unsigned int userBits,
unsigned int* ref,
unsigned int* mask);
static void ElevateDrawPreference(GrGpu::DrawPreference* preference,
GrGpu::DrawPreference elevation) {
GR_STATIC_ASSERT(GrGpu::kCallerPrefersDraw_DrawPreference > GrGpu::kNoDraw_DrawPreference);

View File

@ -9,7 +9,6 @@
#define GrPathRenderer_DEFINED
#include "GrDrawTarget.h"
#include "GrStencil.h"
#include "GrStyle.h"
#include "SkDrawProcs.h"
@ -82,7 +81,7 @@ public:
bool fAntiAlias;
// These next two are only used by GrStencilAndCoverPathRenderer
bool fIsStencilDisabled;
bool fHasUserStencilSettings;
bool fIsStencilBufferMSAA;
void validate() const {
@ -155,11 +154,11 @@ public:
canArgs.fStyle = args.fStyle;
canArgs.fAntiAlias = args.fAntiAlias;
canArgs.fIsStencilDisabled = args.fPipelineBuilder->getStencil().isDisabled();
canArgs.fHasUserStencilSettings = args.fPipelineBuilder->hasUserStencilSettings();
canArgs.fIsStencilBufferMSAA =
args.fPipelineBuilder->getRenderTarget()->isStencilBufferMultisampled();
SkASSERT(this->canDrawPath(canArgs));
if (!args.fPipelineBuilder->getStencil().isDisabled()) {
if (args.fPipelineBuilder->hasUserStencilSettings()) {
SkASSERT(kNoRestriction_StencilSupport == this->getStencilSupport(*args.fPath));
SkASSERT(args.fStyle->isSimpleFill());
}
@ -260,14 +259,16 @@ private:
* kStencilOnly in onGetStencilSupport().
*/
virtual void onStencilPath(const StencilPathArgs& args) {
static constexpr GrStencilSettings kIncrementStencil(
kReplace_StencilOp,
kReplace_StencilOp,
kAlways_StencilFunc,
0xffff,
0xffff,
0xffff);
args.fPipelineBuilder->setStencil(kIncrementStencil);
static constexpr GrUserStencilSettings kIncrementStencil(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlways,
0xffff,
GrUserStencilOp::kReplace,
GrUserStencilOp::kReplace,
0xffff>()
);
args.fPipelineBuilder->setUserStencil(&kIncrementStencil);
args.fPipelineBuilder->setDisableColorXPFactory();
DrawPathArgs drawArgs;
drawArgs.fTarget = args.fTarget;

View File

@ -20,9 +20,35 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
GrXPOverridesForBatch* overrides) {
const GrPipelineBuilder& builder = *args.fPipelineBuilder;
GrPipeline* pipeline = new (memory) GrPipeline;
pipeline->fRenderTarget.reset(builder.fRenderTarget.get());
SkASSERT(pipeline->fRenderTarget);
pipeline->fScissorState = *args.fScissor;
if (builder.hasUserStencilSettings() || args.fHasStencilClip) {
SkASSERT(args.fNumStencilBits);
pipeline->fStencilSettings.reset(*builder.getUserStencil(), args.fHasStencilClip,
args.fNumStencilBits);
SkASSERT(!pipeline->fStencilSettings.usesWrapOp() || args.fCaps->stencilWrapOpsSupport());
}
pipeline->fDrawFace = builder.getDrawFace();
pipeline->fFlags = 0;
if (builder.isHWAntialias()) {
pipeline->fFlags |= kHWAA_Flag;
}
if (builder.snapVerticesToPixelCenters()) {
pipeline->fFlags |= kSnapVertices_Flag;
}
if (builder.getDisableOutputConversionToSRGB()) {
pipeline->fFlags |= kDisableOutputConversionToSRGB_Flag;
}
if (builder.getAllowSRGBInputs()) {
pipeline->fFlags |= kAllowSRGBInputs_Flag;
}
// Create XferProcessor from DS's XPFactory
bool hasMixedSamples = builder.getRenderTarget()->hasMixedSamples() &&
(builder.isHWAntialias() || !builder.getStencil().isDisabled());
(builder.isHWAntialias() || !pipeline->fStencilSettings.isDisabled());
const GrXPFactory* xpFactory = builder.getXPFactory();
SkAutoTUnref<GrXferProcessor> xferProcessor;
if (xpFactory) {
@ -31,6 +57,7 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
&args.fDstTexture,
*args.fCaps));
if (!xferProcessor) {
pipeline->~GrPipeline();
return nullptr;
}
} else {
@ -51,7 +78,7 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
const GrXferProcessor* xpForOpts = xferProcessor ? xferProcessor.get() :
&GrPorterDuffXPFactory::SimpleSrcOverXP();
optFlags = xpForOpts->getOptimizations(args.fOpts,
builder.getStencil().doesWrite(),
pipeline->fStencilSettings.doesWrite(),
&overrideColor,
*args.fCaps);
@ -59,6 +86,7 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
// so we must check the draw type. In cases where we will skip drawing we simply return a
// null GrPipeline.
if (GrXferProcessor::kSkipDraw_OptFlag & optFlags) {
pipeline->~GrPipeline();
return nullptr;
}
@ -67,29 +95,8 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
overrideColor = GrColor_ILLEGAL;
}
GrPipeline* pipeline = new (memory) GrPipeline;
pipeline->fXferProcessor.reset(xferProcessor);
pipeline->fRenderTarget.reset(builder.fRenderTarget.get());
SkASSERT(pipeline->fRenderTarget);
pipeline->fScissorState = *args.fScissor;
pipeline->fStencilSettings = builder.getStencil();
pipeline->fDrawFace = builder.getDrawFace();
pipeline->fFlags = 0;
if (builder.isHWAntialias()) {
pipeline->fFlags |= kHWAA_Flag;
}
if (builder.snapVerticesToPixelCenters()) {
pipeline->fFlags |= kSnapVertices_Flag;
}
if (builder.getDisableOutputConversionToSRGB()) {
pipeline->fFlags |= kDisableOutputConversionToSRGB_Flag;
}
if (builder.getAllowSRGBInputs()) {
pipeline->fFlags |= kAllowSRGBInputs_Flag;
}
int firstColorProcessorIdx = args.fOpts.fColorPOI.firstEffectiveProcessorIndex();
// TODO: Once we can handle single or four channel input into coverage GrFragmentProcessors

View File

@ -15,7 +15,7 @@
#include "GrPendingProgramElement.h"
#include "GrPrimitiveProcessor.h"
#include "GrProgramDesc.h"
#include "GrStencil.h"
#include "GrStencilSettings.h"
#include "GrTypesPriv.h"
#include "SkMatrix.h"
#include "SkRefCnt.h"
@ -51,6 +51,8 @@ public:
const GrCaps* fCaps;
GrPipelineOptimizations fOpts;
const GrScissorState* fScissor;
int fNumStencilBits;
bool fHasStencilClip;
GrXferProcessor::DstTexture fDstTexture;
};

View File

@ -16,11 +16,14 @@
#include "effects/GrPorterDuffXferProcessor.h"
GrPipelineBuilder::GrPipelineBuilder()
: fFlags(0x0), fDrawFace(kBoth_DrawFace) {
: fFlags(0x0),
fUserStencilSettings(&GrUserStencilSettings::kUnused),
fDrawFace(kBoth_DrawFace) {
SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
}
GrPipelineBuilder::GrPipelineBuilder(const GrPaint& paint, GrRenderTarget* rt, const GrClip& clip) {
GrPipelineBuilder::GrPipelineBuilder(const GrPaint& paint, GrRenderTarget* rt, const GrClip& clip)
: GrPipelineBuilder() {
SkDEBUGCODE(fBlockEffectRemovalCnt = 0;)
for (int i = 0; i < paint.numColorFragmentProcessors(); ++i) {
@ -35,11 +38,6 @@ GrPipelineBuilder::GrPipelineBuilder(const GrPaint& paint, GrRenderTarget* rt, c
this->setRenderTarget(rt);
// These have no equivalent in GrPaint, set them to defaults
fDrawFace = kBoth_DrawFace;
fStencilSettings.setDisabled();
fFlags = 0;
fClip = clip;
this->setState(GrPipelineBuilder::kHWAntialias_Flag,

View File

@ -14,7 +14,7 @@
#include "GrGpuResourceRef.h"
#include "GrProcOptInfo.h"
#include "GrRenderTarget.h"
#include "GrStencil.h"
#include "GrUserStencilSettings.h"
#include "GrXferProcessor.h"
#include "SkMatrix.h"
#include "effects/GrCoverageSetOpXP.h"
@ -199,57 +199,20 @@ public:
/// @name Stencil
////
const GrStencilSettings& getStencil() const { return fStencilSettings; }
bool hasUserStencilSettings() const {
return &GrUserStencilSettings::kUnused != fUserStencilSettings;
}
const GrUserStencilSettings* getUserStencil() const { return fUserStencilSettings; }
/**
* Sets the stencil settings to use for the next draw.
* Changing the clip has the side-effect of possibly zeroing
* out the client settable stencil bits. So multipass algorithms
* using stencil should not change the clip between passes.
* Sets the user stencil settings for the next draw.
* This class only stores pointers to stencil settings objects.
* The caller guarantees the pointer will remain valid until it
* changes or goes out of scope.
* @param settings the stencil settings to use.
*/
void setStencil(const GrStencilSettings& settings) { fStencilSettings = settings; }
GrStencilSettings* stencil() { return &fStencilSettings; }
/**
* AutoRestoreStencil
*
* This simple struct saves and restores the stencil settings
* This class can transiently modify its "const" GrPipelineBuilder object but will restore it
* when done - so it is notionally "const" correct.
*/
class AutoRestoreStencil : public ::SkNoncopyable {
public:
AutoRestoreStencil() : fPipelineBuilder(nullptr) {}
AutoRestoreStencil(const GrPipelineBuilder& ds) : fPipelineBuilder(nullptr) { this->set(&ds); }
~AutoRestoreStencil() { this->set(nullptr); }
void set(const GrPipelineBuilder* ds) {
if (fPipelineBuilder) {
fPipelineBuilder->setStencil(fStencilSettings);
}
fPipelineBuilder = const_cast<GrPipelineBuilder*>(ds);
if (ds) {
fStencilSettings = ds->getStencil();
}
}
bool isSet() const { return SkToBool(fPipelineBuilder); }
void setStencil(const GrStencilSettings& settings) {
SkASSERT(this->isSet());
fPipelineBuilder->setStencil(settings);
}
private:
// notionally const (as marginalia)
GrPipelineBuilder* fPipelineBuilder;
GrStencilSettings fStencilSettings;
};
void setUserStencil(const GrUserStencilSettings* settings) { fUserStencilSettings = settings; }
void disableUserStencil() { fUserStencilSettings = &GrUserStencilSettings::kUnused; }
/// @}
@ -371,7 +334,7 @@ private:
SkAutoTUnref<GrRenderTarget> fRenderTarget;
uint32_t fFlags;
GrStencilSettings fStencilSettings;
const GrUserStencilSettings* fUserStencilSettings;
DrawFace fDrawFace;
mutable SkAutoTUnref<const GrXPFactory> fXPFactory;
FragmentProcessorArray fColorFragmentProcessors;

View File

@ -1,403 +0,0 @@
/*
* 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));
}

View File

@ -1,369 +0,0 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrStencil_DEFINED
#define GrStencil_DEFINED
#include "GrTypes.h"
#include "SkRegion.h"
class GrProcessorKeyBuilder;
/**
* Gr uses the stencil buffer to implement complex clipping inside the
* GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer
* bits available for other uses by external code (clients). Client code can
* modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits
* provided by clients that overlap the bits used to implement clipping.
*
* When code outside the GrDrawTarget class uses the stencil buffer the contract
* is as follows:
*
* > Normal stencil funcs allow the client to pass / fail regardless of the
* reserved clip bits.
* > Additional functions allow a test against the clip along with a limited
* set of tests against the client bits.
* > Client can assume all client bits are zero initially.
* > Client must ensure that after all its passes are finished it has only
* written to the color buffer in the region inside the clip. Furthermore, it
* must zero all client bits that were modifed (both inside and outside the
* clip).
*/
/**
* Determines which pixels pass / fail the stencil test.
* Stencil test passes if (ref & mask) FUNC (stencil & mask) is true
*/
enum GrStencilFunc {
kAlways_StencilFunc = 0,
kNever_StencilFunc,
kGreater_StencilFunc,
kGEqual_StencilFunc,
kLess_StencilFunc,
kLEqual_StencilFunc,
kEqual_StencilFunc,
kNotEqual_StencilFunc,
// Gr stores the current clip in the
// stencil buffer in the high bits that
// are not directly accessible modifiable
// via the GrDrawTarget interface. The below
// stencil funcs test against the current
// clip in addition to the GrDrawTarget
// client's stencil bits.
// pass if inside the clip
kAlwaysIfInClip_StencilFunc,
kEqualIfInClip_StencilFunc,
kLessIfInClip_StencilFunc,
kLEqualIfInClip_StencilFunc,
kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0
kLast_StencilFunc = kNonZeroIfInClip_StencilFunc
};
static const int kStencilFuncCnt = kLast_StencilFunc + 1;
static const int kClipStencilFuncCnt =
kNonZeroIfInClip_StencilFunc - kAlwaysIfInClip_StencilFunc + 1;
static const int kBasicStencilFuncCnt = kStencilFuncCnt - kClipStencilFuncCnt;
/**
* Operations to perform based on whether stencil test passed failed.
*/
enum GrStencilOp {
kKeep_StencilOp = 0, // preserve existing stencil value
kReplace_StencilOp, // replace with reference value from stencl test
kIncWrap_StencilOp, // increment and wrap at max
kIncClamp_StencilOp, // increment and clamp at max
kDecWrap_StencilOp, // decrement and wrap at 0
kDecClamp_StencilOp, // decrement and clamp at 0
kZero_StencilOp, // zero stencil bits
kInvert_StencilOp, // invert stencil bits
kLast_StencilOp = kInvert_StencilOp
};
static const int kStencilOpCnt = kLast_StencilOp + 1;
/**
* Class representing stencil state.
*/
class GrStencilSettings {
public:
enum Face {
kFront_Face = 0,
kBack_Face = 1,
};
constexpr GrStencilSettings(GrStencilOp passOp,
GrStencilOp failOp,
GrStencilFunc func,
unsigned short funcMask,
unsigned short funcRef,
unsigned short writeMask)
: fPassOps{(uint8_t)passOp, (uint8_t)passOp}
, fFailOps{(uint8_t)failOp, (uint8_t)failOp}
, fFuncs{(uint8_t)func, (uint8_t)func}
, fPad0(0)
, fPad1(0)
, fFuncMasks{funcMask, funcMask}
, fFuncRefs{funcRef, funcRef}
, fWriteMasks{writeMask, writeMask}
, fFlags(ComputeFlags(passOp, passOp,
failOp, failOp,
func, func,
writeMask, writeMask)) {
}
constexpr GrStencilSettings(GrStencilOp frontPassOp, GrStencilOp backPassOp,
GrStencilOp frontFailOp, GrStencilOp backFailOp,
GrStencilFunc frontFunc, GrStencilFunc backFunc,
uint16_t frontFuncMask, uint16_t backFuncMask,
uint16_t frontFuncRef, uint16_t backFuncRef,
uint16_t frontWriteMask, uint16_t backWriteMask)
: fPassOps{(uint8_t)frontPassOp, (uint8_t)backPassOp}
, fFailOps{(uint8_t)frontFailOp, (uint8_t)backFailOp}
, fFuncs{(uint8_t)frontFunc, (uint8_t)backFunc}
, fPad0(0)
, fPad1(0)
, fFuncMasks{frontFuncMask, backFuncMask}
, fFuncRefs{frontFuncRef, backFuncRef}
, fWriteMasks{frontWriteMask, backWriteMask}
, fFlags(ComputeFlags(frontPassOp, backPassOp,
frontFailOp, backFailOp,
frontFunc, backFunc,
frontWriteMask, backWriteMask)) {
}
GrStencilSettings() {
fPad0 = fPad1 = 0;
this->setDisabled();
}
GrStencilOp passOp(Face f) const { return static_cast<GrStencilOp>(fPassOps[f]); }
GrStencilOp failOp(Face f) const { return static_cast<GrStencilOp>(fFailOps[f]); }
GrStencilFunc func(Face f) const { return static_cast<GrStencilFunc>(fFuncs[f]); }
uint16_t funcMask(Face f) const { return fFuncMasks[f]; }
uint16_t funcRef(Face f) const { return fFuncRefs[f]; }
uint16_t writeMask(Face f) const { return fWriteMasks[f]; }
void setPassOp(Face f, GrStencilOp op) { fPassOps[f] = op; fFlags = 0;}
void setFailOp(Face f, GrStencilOp op) { fFailOps[f] = op; fFlags = 0;}
void setFunc(Face f, GrStencilFunc func) { fFuncs[f] = func; fFlags = 0;}
void setFuncMask(Face f, unsigned short mask) { fFuncMasks[f] = mask; }
void setFuncRef(Face f, unsigned short ref) { fFuncRefs[f] = ref; }
void setWriteMask(Face f, unsigned short writeMask) { fWriteMasks[f] = writeMask; }
void copyFrontSettingsToBack() {
fPassOps[kBack_Face] = fPassOps[kFront_Face];
fFailOps[kBack_Face] = fFailOps[kFront_Face];
fFuncs[kBack_Face] = fFuncs[kFront_Face];
fFuncMasks[kBack_Face] = fFuncMasks[kFront_Face];
fFuncRefs[kBack_Face] = fFuncRefs[kFront_Face];
fWriteMasks[kBack_Face] = fWriteMasks[kFront_Face];
fFlags = 0;
}
void setDisabled() {
memset(this, 0, sizeof(*this));
GR_STATIC_ASSERT(0 == kKeep_StencilOp);
GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
fFlags = kIsDisabled_StencilFlag | kDoesNotWrite_StencilFlag;
}
bool isTwoSided() const {
return fPassOps[kFront_Face] != fPassOps[kBack_Face] ||
fFailOps[kFront_Face] != fFailOps[kBack_Face] ||
fFuncs[kFront_Face] != fFuncs[kBack_Face] ||
fFuncMasks[kFront_Face] != fFuncMasks[kBack_Face] ||
fFuncRefs[kFront_Face] != fFuncRefs[kBack_Face] ||
fWriteMasks[kFront_Face] != fWriteMasks[kBack_Face];
}
bool usesWrapOp() const {
return kIncWrap_StencilOp == fPassOps[kFront_Face] ||
kDecWrap_StencilOp == fPassOps[kFront_Face] ||
kIncWrap_StencilOp == fPassOps[kBack_Face] ||
kDecWrap_StencilOp == fPassOps[kBack_Face] ||
kIncWrap_StencilOp == fFailOps[kFront_Face] ||
kDecWrap_StencilOp == fFailOps[kFront_Face] ||
kIncWrap_StencilOp == fFailOps[kBack_Face] ||
kDecWrap_StencilOp == fFailOps[kBack_Face];
}
bool isDisabled() const {
if (fFlags & kIsDisabled_StencilFlag) {
return true;
}
if (fFlags & kNotDisabled_StencilFlag) {
return false;
}
bool disabled = this->computeIsDisabled();
fFlags |= disabled ? kIsDisabled_StencilFlag : kNotDisabled_StencilFlag;
return disabled;
}
bool doesWrite() const {
if (fFlags & kDoesWrite_StencilFlag) {
return true;
}
if (fFlags & kDoesNotWrite_StencilFlag) {
return false;
}
bool writes = this->computeDoesWrite();
fFlags |= writes ? kDoesWrite_StencilFlag : kDoesNotWrite_StencilFlag;
return writes;
}
void invalidate() {
// write an illegal value to the first member
fPassOps[0] = kStencilOpCnt;
fFlags = 0;
}
bool isValid() const { return fPassOps[0] < kStencilOpCnt; }
void genKey(GrProcessorKeyBuilder* b) const;
bool operator==(const GrStencilSettings& s) const {
static const size_t gCompareSize = sizeof(GrStencilSettings) -
sizeof(fFlags);
SkASSERT((const char*)&fFlags + sizeof(fFlags) ==
(const char*)this + sizeof(GrStencilSettings));
if (this->isDisabled() & s.isDisabled()) { // using & not &&
return true;
}
return 0 == memcmp(this, &s, gCompareSize);
}
bool operator!=(const GrStencilSettings& s) const {
return !(*this == s);
}
GrStencilSettings& operator=(const GrStencilSettings& s) {
memcpy(this, &s, sizeof(GrStencilSettings));
return *this;
}
private:
friend class GrClipMaskManager;
enum {
kMaxStencilClipPasses = 2 // maximum number of passes to add a clip
// element to the stencil buffer.
};
/**
* Given a thing to draw into the stencil clip, a fill type, and a set op
* this function determines:
* 1. Whether the thing can be draw directly to the stencil clip or
* needs to be drawn to the client portion of the stencil first.
* 2. How many passes are needed.
* 3. What those passes are.
* 4. The fill rule that should actually be used to render (will
* always be non-inverted).
*
* @param op the set op to combine this element with the
* existing clip
* @param stencilClipMask mask with just the stencil bit used for clipping
* enabled.
* @param invertedFill is this path inverted
* @param numPasses out: the number of passes needed to add the
* element to the clip.
* @param settings out: the stencil settings to use for each pass
*
* @return true if the clip element's geometry can be drawn directly to the
* stencil clip bit. Will only be true if canBeDirect is true.
* numPasses will be 1 if return value is true.
*/
static bool GetClipPasses(SkRegion::Op op,
bool canBeDirect,
unsigned int stencilClipMask,
bool invertedFill,
int* numPasses,
GrStencilSettings settings[kMaxStencilClipPasses]);
constexpr static bool IsDisabled(GrStencilOp frontPassOp, GrStencilOp backPassOp,
GrStencilOp frontFailOp, GrStencilOp backFailOp,
GrStencilFunc frontFunc, GrStencilFunc backFunc) {
return (((frontPassOp == kKeep_StencilOp && frontFailOp == kKeep_StencilOp)) &&
((backPassOp == kKeep_StencilOp && backFailOp == kKeep_StencilOp)) &&
frontFunc == kAlways_StencilFunc &&
backFunc == kAlways_StencilFunc);
}
constexpr static bool DoesWrite(GrStencilOp frontPassOp, GrStencilOp backPassOp,
GrStencilOp frontFailOp, GrStencilOp backFailOp,
GrStencilFunc frontFunc, GrStencilFunc backFunc,
uint16_t frontWriteMask, uint16_t backWriteMask) {
return (0 != (frontWriteMask | backWriteMask)) &&
// Can we write due to a front face passing the stencil test?
((frontFunc != kNever_StencilFunc && frontPassOp != kKeep_StencilOp) ||
// Can we write due to a back face passing the stencil test?
(backFunc != kNever_StencilFunc && backPassOp != kKeep_StencilOp) ||
// Can we write due to a front face failing the stencil test?
(frontFunc != kAlways_StencilFunc && frontFailOp != kKeep_StencilOp) ||
// Can we write due to a back face failing the stencil test?
(backFunc != kAlways_StencilFunc && backFailOp != kKeep_StencilOp));
}
constexpr static uint32_t ComputeFlags(GrStencilOp frontPassOp, GrStencilOp backPassOp,
GrStencilOp frontFailOp, GrStencilOp backFailOp,
GrStencilFunc frontFunc, GrStencilFunc backFunc,
uint16_t frontWriteMask, uint16_t backWriteMask) {
return (IsDisabled(frontPassOp, backPassOp, frontFailOp, backFailOp,
frontFunc, backFunc)
? kIsDisabled_StencilFlag
: kNotDisabled_StencilFlag) |
(DoesWrite(frontPassOp, backPassOp, frontFailOp, backFailOp,
frontFunc, backFunc, frontWriteMask, backWriteMask)
? kDoesWrite_StencilFlag
: kDoesNotWrite_StencilFlag);
}
bool computeIsDisabled() const {
return IsDisabled((GrStencilOp) fPassOps[kFront_Face], (GrStencilOp) fPassOps[kBack_Face],
(GrStencilOp) fFailOps[kFront_Face], (GrStencilOp) fFailOps[kBack_Face],
(GrStencilFunc) fFuncs[kFront_Face], (GrStencilFunc) fFuncs[kBack_Face]);
}
bool computeDoesWrite() const {
return DoesWrite((GrStencilOp)fPassOps[kFront_Face], (GrStencilOp)fPassOps[kBack_Face],
(GrStencilOp)fFailOps[kFront_Face], (GrStencilOp)fFailOps[kBack_Face],
(GrStencilFunc)fFuncs[kFront_Face], (GrStencilFunc)fFuncs[kBack_Face],
fWriteMasks[kFront_Face], fWriteMasks[kBack_Face]);
}
enum GrStencilFlags {
kIsDisabled_StencilFlag = 0x1,
kNotDisabled_StencilFlag = 0x2,
kDoesWrite_StencilFlag = 0x4,
kDoesNotWrite_StencilFlag = 0x8,
};
uint8_t fPassOps[2]; // op to perform when faces pass (GrStencilOp)
uint8_t fFailOps[2]; // op to perform when faces fail (GrStencilOp)
uint8_t fFuncs[2]; // test function for faces (GrStencilFunc)
uint8_t fPad0;
uint8_t fPad1;
uint16_t fFuncMasks[2]; // mask for face tests
uint16_t fFuncRefs[2]; // reference values for face tests
uint16_t fWriteMasks[2]; // stencil write masks
mutable uint32_t fFlags;
};
// We rely on this being packed and aligned (memcmp'ed and memcpy'ed)
GR_STATIC_ASSERT(sizeof(GrStencilSettings) % 4 == 0);
GR_STATIC_ASSERT(sizeof(GrStencilSettings) ==
4*sizeof(uint8_t) + // ops
2*sizeof(uint8_t) + // funcs
2*sizeof(uint8_t) + // pads
2*sizeof(uint16_t) + // func masks
2*sizeof(uint16_t) + // ref values
2*sizeof(uint16_t) + // write masks
sizeof(uint32_t)); // flags
#endif

View File

@ -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));
}

121
src/gpu/GrStencilSettings.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrStencilSettings_DEFINED
#define GrStencilSettings_DEFINED
#include "GrUserStencilSettings.h"
#include "SkRegion.h"
class GrProcessorKeyBuilder;
enum class GrStencilTest : uint16_t {
kAlways,
kNever,
kGreater,
kGEqual,
kLess,
kLEqual,
kEqual,
kNotEqual
};
static constexpr int kGrStencilTestCount = 1 + (int)GrStencilTest::kNotEqual;
enum class GrStencilOp : uint8_t {
kKeep,
kZero,
kReplace, // Replace stencil value with fRef (only the bits enabled in fWriteMask).
kInvert,
kIncWrap,
kDecWrap,
// NOTE: clamping occurs before the write mask. So if the MSB is zero and masked out, stencil
// values will still wrap when using clamping ops.
kIncClamp,
kDecClamp
};
static constexpr int kGrStencilOpCount = 1 + (int)GrStencilOp::kDecClamp;
/**
* This class defines concrete stencil settings that map directly to the underlying hardware. It
* is deduced from user stencil settings, stencil clip status, and the number of bits in the
* target stencil buffer.
*/
class GrStencilSettings {
public:
GrStencilSettings() { this->setDisabled(); }
GrStencilSettings(const GrUserStencilSettings& user, bool hasStencilClip, int numStencilBits) {
this->reset(user, hasStencilClip, numStencilBits);
}
GrStencilSettings(const GrStencilSettings& that) { this->reset(that); }
GrStencilSettings& operator=(const GrStencilSettings& that) { this->reset(that); return *this; }
void invalidate() { fFlags |= kInvalid_PrivateFlag; }
void setDisabled() { fFlags = kAll_StencilFlags; }
void reset(const GrUserStencilSettings&, bool hasStencilClip, int numStencilBits);
void reset(const GrStencilSettings&);
bool isValid() const { return !(fFlags & kInvalid_PrivateFlag); }
bool isDisabled() const { SkASSERT(this->isValid()); return fFlags & kDisabled_StencilFlag; }
bool doesWrite() const { SkASSERT(this->isValid());
return !(fFlags & kNoModifyStencil_StencilFlag); }
bool isTwoSided() const { SkASSERT(this->isValid());
return !(fFlags & kSingleSided_StencilFlag); }
bool usesWrapOp() const { SkASSERT(this->isValid());
return !(fFlags & kNoWrapOps_StencilFlag); }
void genKey(GrProcessorKeyBuilder* b) const;
bool operator!=(const GrStencilSettings& that) const { return !(*this == that); }
bool operator==(const GrStencilSettings&) const;
struct Face : public GrTStencilFaceSettings<GrStencilTest, GrStencilOp> {
void reset(const GrUserStencilSettings::Face&, bool useStencilClip, int numStencilBits);
void setDisabled();
};
const Face& front() const { SkASSERT(!this->isDisabled()); return fFront; }
const Face& back() const { SkASSERT(this->isTwoSided()); return fBack; }
/**
* Given a thing to draw into the stencil clip, a fill type, and a set op
* this function determines:
* 1. Whether the thing can be draw directly to the stencil clip or
* needs to be drawn to the client portion of the stencil first.
* 2. How many passes are needed.
* 3. What those passes are.
*
* @param op the set op to combine this element with the existing clip
* @param canBeDirect can the caller draw this element directly (without using stencil)?
* @param invertedFill is this path inverted
* @param drawDirectToClip out: true if caller should draw the element directly, false if it
* should draw it into the user stencil bits first.
*
* @return a null-terminated array of settings for stencil passes.
*
* If drawDirectToClip is false, the caller must first draw the element into the user
* stencil bits, and then cover the clip area with multiple passes using the returned
* stencil settings.
*
* If drawDirectToClip is true, the returned array will only have one pass and the
* caller should use those stencil settings while drawing the element directly.
*/
static GrUserStencilSettings const* const* GetClipPasses(SkRegion::Op op,
bool canBeDirect,
bool invertedFill,
bool* drawDirectToClip);
private:
// Internal flag for backends to optionally mark their tracked stencil state as invalid.
enum { kInvalid_PrivateFlag = (kLast_StencilFlag << 1) };
uint32_t fFlags;
Face fFront;
Face fBack;
};
#endif

View File

@ -0,0 +1,237 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrUserStencilSettings_DEFINED
#define GrUserStencilSettings_DEFINED
#include "GrTypes.h"
/**
* Gr uses the stencil buffer to implement complex clipping inside the
* GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer
* bits available for other uses by external code (user bits). Client code can
* modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits
* provided by clients that fall outside the user range.
*
* When code outside the GrDrawTarget class uses the stencil buffer the contract
* is as follows:
*
* > Normal stencil funcs allow the client to pass / fail regardless of the
* reserved clip bits.
* > Additional functions allow a test against the clip along with a limited
* set of tests against the user bits.
* > Client can assume all user bits are zero initially.
* > Client must ensure that after all its passes are finished it has only
* written to the color buffer in the region inside the clip. Furthermore, it
* must zero all user bits that were modifed (both inside and outside the
* clip).
*/
enum GrStencilFlags {
kDisabled_StencilFlag = 0x1,
kNoModifyStencil_StencilFlag = 0x2,
kNoWrapOps_StencilFlag = 0x4,
kSingleSided_StencilFlag = 0x8,
kLast_StencilFlag = kSingleSided_StencilFlag,
kAll_StencilFlags = kLast_StencilFlag | (kLast_StencilFlag - 1)
};
template<typename TTest, typename TOp> struct GrTStencilFaceSettings {
uint16_t fRef; // Reference value for stencil test and ops.
TTest fTest; // Stencil test function, where fRef is on the left side.
uint16_t fTestMask; // Bitwise "and" to perform on fRef and stencil values before testing.
// (e.g. (fRef & fTestMask) < (stencil & fTestMask))
TOp fPassOp; // Op to perform when the test passes.
TOp fFailOp; // Op to perform when the test fails.
uint16_t fWriteMask; // Indicates which bits in the stencil buffer should be updated.
// (e.g. stencil = (newValue & fWriteMask) | (stencil & ~fWriteMask))
};
enum class GrUserStencilTest : uint16_t {
// Tests that respect the clip bit. If a stencil clip is not in effect, the "IfInClip" is
// ignored and these only act on user bits.
kAlwaysIfInClip,
kEqualIfInClip,
kLessIfInClip,
kLEqualIfInClip,
// Tests that ignore the clip bit. The client is responsible to ensure no color write occurs
// outside the clip if it is in use.
kAlways,
kNever,
kGreater,
kGEqual,
kLess,
kLEqual,
kEqual,
kNotEqual
};
constexpr static GrUserStencilTest kLastClippedStencilTest = GrUserStencilTest::kLEqualIfInClip;
constexpr static int kGrUserStencilTestCount = 1 + (int)GrUserStencilTest::kNotEqual;
enum class GrUserStencilOp : uint8_t {
kKeep,
// Ops that only modify user bits. These must not be paired with ops that modify the clip bit.
kZero,
kReplace, // Replace stencil value with fRef (only the bits enabled in fWriteMask).
kInvert,
kIncWrap,
kDecWrap,
// These two should only be used if wrap ops are not supported, or if the math is guaranteed
// to not overflow. The user bits may or may not clamp, depending on the state of non-user bits.
kIncMaybeClamp,
kDecMaybeClamp,
// Ops that only modify the clip bit. These must not be paired with ops that modify user bits.
kZeroClipBit,
kSetClipBit,
kInvertClipBit,
// Ops that modify both clip and user bits. These can only be paired with kKeep or each other.
kSetClipAndReplaceUserBits,
kZeroClipAndUserBits
};
constexpr static GrUserStencilOp kLastUserOnlyStencilOp = GrUserStencilOp::kDecMaybeClamp;
constexpr static GrUserStencilOp kLastClipOnlyStencilOp = GrUserStencilOp::kInvertClipBit;
constexpr static int kGrUserStencilOpCount = 1 + (int)GrUserStencilOp::kZeroClipAndUserBits;
/**
* This struct is a compile-time constant representation of user stencil settings. It describes in
* abstract terms how a draw will use the stencil buffer. It gets ODR-used at runtime to define a
* draw's stencil settings, and is later translated into concrete settings when the pipeline is
* finalized.
*/
struct GrUserStencilSettings {
typedef GrTStencilFaceSettings<GrUserStencilTest, GrUserStencilOp> Face;
template<GrUserStencilTest, GrUserStencilOp PassOp, GrUserStencilOp FailOp> struct Attrs;
// Unfortunately, this is the only way to pass template arguments to a constructor.
template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask,
GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask> struct Init {};
template<uint16_t FtRef, uint16_t BkRef,
GrUserStencilTest FtTest, GrUserStencilTest BkTest,
uint16_t FtTestMask, uint16_t BkTestMask,
GrUserStencilOp FtPassOp, GrUserStencilOp BkPassOp,
GrUserStencilOp FtFailOp, GrUserStencilOp BkFailOp,
uint16_t FtWriteMask, uint16_t BkWriteMask> struct InitSeparate {};
template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask,
GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask>
constexpr static Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask> StaticInit() {
return Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask>();
}
template<uint16_t FtRef, uint16_t BkRef,
GrUserStencilTest FtTest, GrUserStencilTest BkTest,
uint16_t FtTestMask, uint16_t BkTestMask,
GrUserStencilOp FtPassOp, GrUserStencilOp BkPassOp,
GrUserStencilOp FtFailOp, GrUserStencilOp BkFailOp,
uint16_t FtWriteMask, uint16_t BkWriteMask>
constexpr static InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask,
FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask,
BkWriteMask> StaticInitSeparate() {
return InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask,
FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, BkWriteMask>();
}
// We construct with template arguments in order to enforce that the struct be compile-time
// constant and to make use of static asserts.
template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask,
GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask,
typename Attrs = Attrs<Test, PassOp, FailOp> >
constexpr explicit GrUserStencilSettings(
const Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask>&)
: fFrontFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag),
(uint16_t)(Attrs::Flags(true) | kSingleSided_StencilFlag)}
, fFront{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp,
Attrs::EffectiveWriteMask(WriteMask)}
, fBackFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag),
(uint16_t)(Attrs::Flags(true) | kSingleSided_StencilFlag)}
, fBack{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp,
Attrs::EffectiveWriteMask(WriteMask)} {
}
template<uint16_t FtRef, uint16_t BkRef,
GrUserStencilTest FtTest, GrUserStencilTest BkTest,
uint16_t FtTestMask, uint16_t BkTestMask,
GrUserStencilOp FtPassOp, GrUserStencilOp BkPassOp,
GrUserStencilOp FtFailOp, GrUserStencilOp BkFailOp,
uint16_t FtWriteMask, uint16_t BkWriteMask,
typename FtAttrs = Attrs<FtTest, FtPassOp, FtFailOp>,
typename BkAttrs = Attrs<BkTest, BkPassOp, BkFailOp> >
constexpr explicit GrUserStencilSettings(
const InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask,
FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, BkWriteMask>&)
: fFrontFlags{FtAttrs::Flags(false), FtAttrs::Flags(true)}
, fFront{FtRef, FtTest, FtAttrs::EffectiveTestMask(FtTestMask), FtPassOp, FtFailOp,
FtAttrs::EffectiveWriteMask(FtWriteMask)}
, fBackFlags{BkAttrs::Flags(false), BkAttrs::Flags(true)}
, fBack{BkRef, BkTest, BkAttrs::EffectiveTestMask(BkTestMask), BkPassOp, BkFailOp,
BkAttrs::EffectiveWriteMask(BkWriteMask)} {}
// This struct can only be constructed with static initializers.
GrUserStencilSettings() = delete;
GrUserStencilSettings(const GrUserStencilSettings&) = delete;
const uint16_t fFrontFlags[2]; // frontFlagsForDraw = fFrontFlags[hasStencilClip].
const Face fFront;
const uint16_t fBackFlags[2]; // backFlagsForDraw = fBackFlags[hasStencilClip].
const Face fBack;
static const GrUserStencilSettings& kUnused;
};
template<GrUserStencilTest Test, GrUserStencilOp PassOp, GrUserStencilOp FailOp>
struct GrUserStencilSettings::Attrs {
// Ensure an op that only modifies user bits isn't paired with one that modifies clip bits.
GR_STATIC_ASSERT(GrUserStencilOp::kKeep == PassOp || GrUserStencilOp::kKeep == FailOp ||
(PassOp <= kLastUserOnlyStencilOp) == (FailOp <= kLastUserOnlyStencilOp));
// Ensure an op that only modifies clip bits isn't paired with one that modifies clip and user.
GR_STATIC_ASSERT(GrUserStencilOp::kKeep == PassOp || GrUserStencilOp::kKeep == FailOp ||
(PassOp <= kLastClipOnlyStencilOp) == (FailOp <= kLastClipOnlyStencilOp));
constexpr static bool TestAlwaysPasses(bool hasStencilClip) {
return (!hasStencilClip && GrUserStencilTest::kAlwaysIfInClip == Test) ||
GrUserStencilTest::kAlways == Test;
}
constexpr static bool DoesNotModifyStencil(bool hasStencilClip) {
return (GrUserStencilTest::kNever == Test || GrUserStencilOp::kKeep == PassOp) &&
(TestAlwaysPasses(hasStencilClip) || GrUserStencilOp::kKeep == FailOp);
}
constexpr static bool IsDisabled(bool hasStencilClip) {
return TestAlwaysPasses(hasStencilClip) && DoesNotModifyStencil(hasStencilClip);
}
constexpr static bool UsesWrapOps() {
return GrUserStencilOp::kIncWrap == PassOp || GrUserStencilOp::kDecWrap == PassOp ||
GrUserStencilOp::kIncWrap == FailOp || GrUserStencilOp::kDecWrap == FailOp;
}
constexpr static bool TestIgnoresRef() {
return (GrUserStencilTest::kAlwaysIfInClip == Test || GrUserStencilTest::kAlways == Test ||
GrUserStencilTest::kNever == Test);
}
constexpr static uint16_t Flags(bool hasStencilClip) {
return (IsDisabled(hasStencilClip) ? kDisabled_StencilFlag : 0) |
(DoesNotModifyStencil(hasStencilClip) ? kNoModifyStencil_StencilFlag : 0) |
(UsesWrapOps() ? 0 : kNoWrapOps_StencilFlag);
}
constexpr static uint16_t EffectiveTestMask(uint16_t testMask) {
return TestIgnoresRef() ? 0 : testMask;
}
constexpr static uint16_t EffectiveWriteMask(uint16_t writeMask) {
// We don't modify the mask differently when hasStencilClip=false because either the entire
// face gets disabled in that case (e.g. Test=kAlwaysIfInClip, PassOp=kKeep), or else the
// effective mask stays the same either way.
return DoesNotModifyStencil(true) ? 0 : writeMask;
}
};
#endif

View File

@ -443,11 +443,11 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
// face culling doesn't make sense here
SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
int passCount = 0;
const GrStencilSettings* passes[3];
GrPipelineBuilder::DrawFace drawFace[3];
bool reverse = false;
bool lastPassIsBounds;
int passCount = 0;
const GrUserStencilSettings* passes[3];
GrPipelineBuilder::DrawFace drawFace[3];
bool reverse = false;
bool lastPassIsBounds;
if (isHairline) {
passCount = 1;
@ -544,7 +544,7 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target,
for (int p = 0; p < passCount; ++p) {
pipelineBuilder->setDrawFace(drawFace[p]);
if (passes[p]) {
*pipelineBuilder->stencil() = *passes[p];
pipelineBuilder->setUserStencil(passes[p]);
}
if (lastPassIsBounds && (p == passCount-1)) {

View File

@ -28,7 +28,10 @@ public:
GrPathRendering::FillType fillType() const { return fFillType; }
void setStencilSettings(const GrStencilSettings& stencil) { fStencilSettings = stencil; }
void setStencilSettings(const GrUserStencilSettings& stencil, bool hasStencilClip,
int numStencilBits) {
fStencilSettings.reset(stencil, hasStencilClip, numStencilBits);
}
protected:
GrDrawPathBatchBase(uint32_t classID, const SkMatrix& viewMatrix, GrColor initialColor,

View File

@ -578,11 +578,11 @@ bool GrMSAAPathRenderer::internalDrawPath(GrDrawTarget* target,
// face culling doesn't make sense here
SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace());
int passCount = 0;
const GrStencilSettings* passes[3];
GrPipelineBuilder::DrawFace drawFace[3];
bool reverse = false;
bool lastPassIsBounds;
int passCount = 0;
const GrUserStencilSettings* passes[3];
GrPipelineBuilder::DrawFace drawFace[3];
bool reverse = false;
bool lastPassIsBounds;
if (single_pass_path(path)) {
passCount = 1;
@ -647,7 +647,7 @@ bool GrMSAAPathRenderer::internalDrawPath(GrDrawTarget* target,
for (int p = 0; p < passCount; ++p) {
pipelineBuilder->setDrawFace(drawFace[p]);
if (passes[p]) {
*pipelineBuilder->stencil() = *passes[p];
pipelineBuilder->setUserStencil(passes[p]);
}
if (lastPassIsBounds && (p == passCount-1)) {

View File

@ -8,36 +8,44 @@
#ifndef GrPathStencilSettings_DEFINED
#define GrPathStencilSettings_DEFINED
#include "GrUserStencilSettings.h"
////////////////////////////////////////////////////////////////////////////////
// Stencil rules for paths
////// Even/Odd
static constexpr GrStencilSettings gEOStencilPass(
kInvert_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
static constexpr GrUserStencilSettings gEOStencilPass(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlwaysIfInClip,
0xffff,
GrUserStencilOp::kInvert,
GrUserStencilOp::kKeep,
0xffff>()
);
// ok not to check clip b/c stencil pass only wrote inside clip
static constexpr GrStencilSettings gEOColorPass(
kZero_StencilOp,
kZero_StencilOp,
kNotEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings gEOColorPass(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kNotEqual,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kZero,
0xffff>()
);
// have to check clip b/c outside clip will always be zero.
static constexpr GrStencilSettings gInvEOColorPass(
kZero_StencilOp,
kZero_StencilOp,
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings gInvEOColorPass(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kEqualIfInClip,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kZero,
0xffff>()
);
////// Winding
@ -45,90 +53,108 @@ static constexpr GrStencilSettings gInvEOColorPass(
// when we don't have wrap incr and decr we use the stencil test to simulate
// them.
static constexpr GrStencilSettings gWindStencilSeparateWithWrap(
kIncWrap_StencilOp, kDecWrap_StencilOp,
kKeep_StencilOp, kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
0xffff, 0xffff,
0xffff, 0xffff,
0xffff, 0xffff);
static constexpr GrUserStencilSettings gWindStencilSeparateWithWrap(
GrUserStencilSettings::StaticInitSeparate<
0xffff, 0xffff,
GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
0xffff, 0xffff,
GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
0xffff, 0xffff>()
);
// if inc'ing the max value, invert to make 0
// if dec'ing zero invert to make all ones.
// we can't avoid touching the stencil on both passing and
// failing, so we can't resctrict ourselves to the clip.
static constexpr GrStencilSettings gWindStencilSeparateNoWrap(
kInvert_StencilOp, kInvert_StencilOp,
kIncClamp_StencilOp, kDecClamp_StencilOp,
kEqual_StencilFunc, kEqual_StencilFunc,
0xffff, 0xffff,
0xffff, 0x0000,
0xffff, 0xffff);
static constexpr GrUserStencilSettings gWindStencilSeparateNoWrap(
GrUserStencilSettings::StaticInitSeparate<
0xffff, 0x0000,
GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
0xffff, 0xffff,
GrUserStencilOp::kInvert, GrUserStencilOp::kInvert,
GrUserStencilOp::kIncMaybeClamp, GrUserStencilOp::kDecMaybeClamp,
0xffff, 0xffff>()
);
// When there are no separate faces we do two passes to setup the winding rule
// stencil. First we draw the front faces and inc, then we draw the back faces
// and dec. These are same as the above two split into the incrementing and
// decrementing passes.
static constexpr GrStencilSettings gWindSingleStencilWithWrapInc(
kIncWrap_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
static constexpr GrUserStencilSettings gWindSingleStencilWithWrapInc(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlwaysIfInClip,
0xffff,
GrUserStencilOp::kIncWrap,
GrUserStencilOp::kKeep,
0xffff>()
);
static constexpr GrStencilSettings gWindSingleStencilWithWrapDec(
kDecWrap_StencilOp,
kKeep_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0xffff,
0xffff);
static constexpr GrUserStencilSettings gWindSingleStencilWithWrapDec(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlwaysIfInClip,
0xffff,
GrUserStencilOp::kDecWrap,
GrUserStencilOp::kKeep,
0xffff>()
);
static constexpr GrStencilSettings gWindSingleStencilNoWrapInc(
kInvert_StencilOp,
kIncClamp_StencilOp,
kEqual_StencilFunc,
0xffff,
0xffff,
0xffff);
static constexpr GrUserStencilSettings gWindSingleStencilNoWrapInc(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kEqual,
0xffff,
GrUserStencilOp::kInvert,
GrUserStencilOp::kIncMaybeClamp,
0xffff>()
);
static constexpr GrStencilSettings gWindSingleStencilNoWrapDec(
kInvert_StencilOp,
kDecClamp_StencilOp,
kEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings gWindSingleStencilNoWrapDec(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kEqual,
0xffff,
GrUserStencilOp::kInvert,
GrUserStencilOp::kDecMaybeClamp,
0xffff>()
);
// Color passes are the same whether we use the two-sided stencil or two passes
static constexpr GrStencilSettings gWindColorPass(
kZero_StencilOp,
kZero_StencilOp,
kNonZeroIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings gWindColorPass(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kLessIfInClip, // "0 < stencil" is equivalent to "0 != stencil".
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kZero,
0xffff>()
);
static constexpr GrStencilSettings gInvWindColorPass(
kZero_StencilOp,
kZero_StencilOp,
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings gInvWindColorPass(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kEqualIfInClip,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kZero,
0xffff>()
);
////// Normal render to stencil
// Sometimes the default path renderer can draw a path directly to the stencil
// buffer without having to first resolve the interior / exterior.
static constexpr GrStencilSettings gDirectToStencil(
kZero_StencilOp,
kIncClamp_StencilOp,
kAlwaysIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings gDirectToStencil(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kAlwaysIfInClip,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kIncMaybeClamp,
0xffff>()
);
#endif

View File

@ -36,7 +36,7 @@ bool GrStencilAndCoverPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
if (args.fStyle->hasNonDashPathEffect() || args.fStyle->strokeRec().isHairlineStyle()) {
return false;
}
if (!args.fIsStencilDisabled) {
if (args.fHasUserStencilSettings) {
return false;
}
if (args.fAntiAlias) {
@ -80,7 +80,7 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) {
GrPipelineBuilder* pipelineBuilder = args.fPipelineBuilder;
const SkMatrix& viewMatrix = *args.fViewMatrix;
SkASSERT(pipelineBuilder->getStencil().isDisabled());
SkASSERT(!pipelineBuilder->hasUserStencilSettings());
if (args.fAntiAlias) {
SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled());
@ -90,18 +90,21 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) {
SkAutoTUnref<GrPath> p(get_gr_path(fResourceProvider, path, *args.fStyle));
if (path.isInverseFillType()) {
static constexpr GrStencilSettings kInvertedStencilPass(
kKeep_StencilOp,
kZero_StencilOp,
// We know our rect will hit pixels outside the clip and the user bits will be 0
// outside the clip. So we can't just fill where the user bits are 0. We also need to
// check that the clip bit is set.
kEqualIfInClip_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings kInvertedCoverPass(
GrUserStencilSettings::StaticInit<
0x0000,
// We know our rect will hit pixels outside the clip and the user bits will be 0
// outside the clip. So we can't just fill where the user bits are 0. We also need
// to check that the clip bit is set.
GrUserStencilTest::kEqualIfInClip,
0xffff,
GrUserStencilOp::kKeep,
GrUserStencilOp::kZero,
0xffff>()
);
pipelineBuilder->setStencil(kInvertedStencilPass);
pipelineBuilder->setUserStencil(&kInvertedCoverPass);
// fake inverse with a stencil and cover
args.fTarget->stencilPath(*pipelineBuilder, viewMatrix, p, p->getFillType());
@ -133,20 +136,22 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) {
&invert));
args.fTarget->drawBatch(*pipelineBuilder, batch);
} else {
static constexpr GrStencilSettings kStencilPass(
kZero_StencilOp,
kKeep_StencilOp,
kNotEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings kCoverPass(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kNotEqual,
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kKeep,
0xffff>()
);
pipelineBuilder->setStencil(kStencilPass);
pipelineBuilder->setUserStencil(&kCoverPass);
SkAutoTUnref<GrDrawPathBatchBase> batch(
GrDrawPathBatch::Create(viewMatrix, args.fColor, p->getFillType(), p));
args.fTarget->drawPathBatch(*pipelineBuilder, batch);
}
pipelineBuilder->stencil()->setDisabled();
pipelineBuilder->disableUserStencil();
return true;
}

View File

@ -21,11 +21,14 @@ public:
static GrBatch* Create(const SkMatrix& viewMatrix,
bool useHWAA,
const GrStencilSettings& stencil,
const GrUserStencilSettings& userStencil,
bool hasStencilClip,
int numStencilBits,
const GrScissorState& scissor,
GrRenderTarget* renderTarget,
const GrPath* path) {
return new GrStencilPathBatch(viewMatrix, useHWAA, stencil, scissor, renderTarget, path);
return new GrStencilPathBatch(viewMatrix, useHWAA, userStencil, hasStencilClip,
numStencilBits, scissor, renderTarget, path);
}
const char* name() const override { return "StencilPath"; }
@ -42,14 +45,16 @@ public:
private:
GrStencilPathBatch(const SkMatrix& viewMatrix,
bool useHWAA,
const GrStencilSettings& stencil,
const GrUserStencilSettings& userStencil,
bool hasStencilClip,
int numStencilBits,
const GrScissorState& scissor,
GrRenderTarget* renderTarget,
const GrPath* path)
: INHERITED(ClassID())
, fViewMatrix(viewMatrix)
, fUseHWAA(useHWAA)
, fStencil(stencil)
, fStencil(userStencil, hasStencilClip, numStencilBits)
, fScissor(scissor)
, fRenderTarget(renderTarget)
, fPath(path) {

View File

@ -2874,40 +2874,38 @@ namespace {
GrGLenum gr_to_gl_stencil_op(GrStencilOp op) {
static const GrGLenum gTable[] = {
GR_GL_KEEP, // kKeep_StencilOp
GR_GL_REPLACE, // kReplace_StencilOp
GR_GL_INCR_WRAP, // kIncWrap_StencilOp
GR_GL_INCR, // kIncClamp_StencilOp
GR_GL_DECR_WRAP, // kDecWrap_StencilOp
GR_GL_DECR, // kDecClamp_StencilOp
GR_GL_ZERO, // kZero_StencilOp
GR_GL_INVERT, // kInvert_StencilOp
static const GrGLenum gTable[kGrStencilOpCount] = {
GR_GL_KEEP, // kKeep
GR_GL_ZERO, // kZero
GR_GL_REPLACE, // kReplace
GR_GL_INVERT, // kInvert
GR_GL_INCR_WRAP, // kIncWrap
GR_GL_DECR_WRAP, // kDecWrap
GR_GL_INCR, // kIncClamp
GR_GL_DECR, // kDecClamp
};
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gTable) == kStencilOpCnt);
GR_STATIC_ASSERT(0 == kKeep_StencilOp);
GR_STATIC_ASSERT(1 == kReplace_StencilOp);
GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
GR_STATIC_ASSERT(6 == kZero_StencilOp);
GR_STATIC_ASSERT(7 == kInvert_StencilOp);
SkASSERT((unsigned) op < kStencilOpCnt);
return gTable[op];
GR_STATIC_ASSERT(0 == (int)GrStencilOp::kKeep);
GR_STATIC_ASSERT(1 == (int)GrStencilOp::kZero);
GR_STATIC_ASSERT(2 == (int)GrStencilOp::kReplace);
GR_STATIC_ASSERT(3 == (int)GrStencilOp::kInvert);
GR_STATIC_ASSERT(4 == (int)GrStencilOp::kIncWrap);
GR_STATIC_ASSERT(5 == (int)GrStencilOp::kDecWrap);
GR_STATIC_ASSERT(6 == (int)GrStencilOp::kIncClamp);
GR_STATIC_ASSERT(7 == (int)GrStencilOp::kDecClamp);
SkASSERT(op < (GrStencilOp)kGrStencilOpCount);
return gTable[(int)op];
}
void set_gl_stencil(const GrGLInterface* gl,
const GrStencilSettings& settings,
GrGLenum glFace,
GrStencilSettings::Face grFace) {
GrGLenum glFunc = GrToGLStencilFunc(settings.func(grFace));
GrGLenum glFailOp = gr_to_gl_stencil_op(settings.failOp(grFace));
GrGLenum glPassOp = gr_to_gl_stencil_op(settings.passOp(grFace));
const GrStencilSettings::Face& face,
GrGLenum glFace) {
GrGLenum glFunc = GrToGLStencilFunc(face.fTest);
GrGLenum glFailOp = gr_to_gl_stencil_op(face.fFailOp);
GrGLenum glPassOp = gr_to_gl_stencil_op(face.fPassOp);
GrGLint ref = settings.funcRef(grFace);
GrGLint mask = settings.funcMask(grFace);
GrGLint writeMask = settings.writeMask(grFace);
GrGLint ref = face.fRef;
GrGLint mask = face.fTestMask;
GrGLint writeMask = face.fWriteMask;
if (GR_GL_FRONT_AND_BACK == glFace) {
// we call the combined func just in case separate stencil is not
@ -2937,20 +2935,18 @@ void GrGLGpu::flushStencil(const GrStencilSettings& stencilSettings) {
}
}
if (!stencilSettings.isDisabled()) {
if (this->caps()->twoSidedStencilSupport()) {
if (stencilSettings.isTwoSided()) {
SkASSERT(this->caps()->twoSidedStencilSupport());
set_gl_stencil(this->glInterface(),
stencilSettings,
GR_GL_FRONT,
GrStencilSettings::kFront_Face);
stencilSettings.front(),
GR_GL_FRONT);
set_gl_stencil(this->glInterface(),
stencilSettings,
GR_GL_BACK,
GrStencilSettings::kBack_Face);
stencilSettings.back(),
GR_GL_BACK);
} else {
set_gl_stencil(this->glInterface(),
stencilSettings,
GR_GL_FRONT_AND_BACK,
GrStencilSettings::kFront_Face);
stencilSettings.front(),
GR_GL_FRONT_AND_BACK);
}
}
fHWStencilSettings = stencilSettings;

View File

@ -70,9 +70,9 @@ static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) {
default:
SkFAIL("Unexpected path fill.");
/* fallthrough */;
case kIncClamp_StencilOp:
case GrStencilOp::kIncClamp:
return GR_GL_COUNT_UP;
case kInvert_StencilOp:
case GrStencilOp::kInvert:
return GR_GL_INVERT;
}
}
@ -133,9 +133,9 @@ void GrGLPathRendering::onStencilPath(const StencilPathArgs& args, const GrPath*
this->flushPathStencilSettings(*args.fStencil);
SkASSERT(!fHWPathStencilSettings.isTwoSided());
GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
GrGLenum fillMode =
gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.front().fPassOp);
GrGLint writeMask = fHWPathStencilSettings.front().fWriteMask;
if (glPath->shouldFill()) {
GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
@ -157,9 +157,9 @@ void GrGLPathRendering::onDrawPath(const GrPipeline& pipeline,
this->flushPathStencilSettings(stencil);
SkASSERT(!fHWPathStencilSettings.isTwoSided());
GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
GrGLint writeMask = fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
GrGLenum fillMode =
gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.front().fPassOp);
GrGLint writeMask = fHWPathStencilSettings.front().fWriteMask;
if (glPath->shouldStroke()) {
if (glPath->shouldFill()) {
@ -191,10 +191,8 @@ void GrGLPathRendering::onDrawPaths(const GrPipeline& pipeline,
const GrGLPathRange* glPathRange = static_cast<const GrGLPathRange*>(pathRange);
GrGLenum fillMode =
gr_stencil_op_to_gl_path_rendering_fill_mode(
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
GrGLint writeMask =
fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.front().fPassOp);
GrGLint writeMask = fHWPathStencilSettings.front().fWriteMask;
if (glPathRange->shouldStroke()) {
if (glPathRange->shouldFill()) {
@ -322,16 +320,15 @@ void GrGLPathRendering::flushPathStencilSettings(const GrStencilSettings& stenci
SkASSERT(stencilSettings.isValid());
// Just the func, ref, and mask is set here. The op and write mask are params to the call
// that draws the path to the SB (glStencilFillPath)
const GrStencilSettings::Face kFront_Face = GrStencilSettings::kFront_Face;
GrStencilFunc func = stencilSettings.func(kFront_Face);
uint16_t funcRef = stencilSettings.funcRef(kFront_Face);
uint16_t funcMask = stencilSettings.funcMask(kFront_Face);
uint16_t ref = stencilSettings.front().fRef;
GrStencilTest test = stencilSettings.front().fTest;
uint16_t testMask = stencilSettings.front().fTestMask;
if (!fHWPathStencilSettings.isValid() ||
func != fHWPathStencilSettings.func(kFront_Face) ||
funcRef != fHWPathStencilSettings.funcRef(kFront_Face) ||
funcMask != fHWPathStencilSettings.funcMask(kFront_Face)) {
GL_CALL(PathStencilFunc(GrToGLStencilFunc(func), funcRef, funcMask));
ref != fHWPathStencilSettings.front().fRef ||
test != fHWPathStencilSettings.front().fTest ||
testMask != fHWPathStencilSettings.front().fTestMask) {
GL_CALL(PathStencilFunc(GrToGLStencilFunc(test), ref, testMask));
}
fHWPathStencilSettings = stencilSettings;
}

View File

@ -10,12 +10,12 @@
#include "SkRefCnt.h"
#include "GrPathRendering.h"
#include "GrStencil.h"
#include "gl/GrGLTypes.h"
#include "glsl/GrGLSLUtil.h"
class GrGLNameAllocator;
class GrGLGpu;
class GrStencilSettings;
class GrStyle;
/**

View File

@ -334,27 +334,26 @@ GrGLRenderer GrGLGetRenderer(const GrGLInterface* gl) {
return GrGLGetRendererFromString((const char*) v);
}
GrGLenum GrToGLStencilFunc(GrStencilFunc basicFunc) {
static const GrGLenum gTable[] = {
GR_GL_ALWAYS, // kAlways_StencilFunc
GR_GL_NEVER, // kNever_StencilFunc
GR_GL_GREATER, // kGreater_StencilFunc
GR_GL_GEQUAL, // kGEqual_StencilFunc
GR_GL_LESS, // kLess_StencilFunc
GR_GL_LEQUAL, // kLEqual_StencilFunc,
GR_GL_EQUAL, // kEqual_StencilFunc,
GR_GL_NOTEQUAL, // kNotEqual_StencilFunc,
GrGLenum GrToGLStencilFunc(GrStencilTest test) {
static const GrGLenum gTable[kGrStencilTestCount] = {
GR_GL_ALWAYS, // kAlways
GR_GL_NEVER, // kNever
GR_GL_GREATER, // kGreater
GR_GL_GEQUAL, // kGEqual
GR_GL_LESS, // kLess
GR_GL_LEQUAL, // kLEqual
GR_GL_EQUAL, // kEqual
GR_GL_NOTEQUAL, // kNotEqual
};
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gTable) == kBasicStencilFuncCnt);
GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
GR_STATIC_ASSERT(1 == kNever_StencilFunc);
GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
GR_STATIC_ASSERT(4 == kLess_StencilFunc);
GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
SkASSERT((unsigned) basicFunc < kBasicStencilFuncCnt);
GR_STATIC_ASSERT(0 == (int)GrStencilTest::kAlways);
GR_STATIC_ASSERT(1 == (int)GrStencilTest::kNever);
GR_STATIC_ASSERT(2 == (int)GrStencilTest::kGreater);
GR_STATIC_ASSERT(3 == (int)GrStencilTest::kGEqual);
GR_STATIC_ASSERT(4 == (int)GrStencilTest::kLess);
GR_STATIC_ASSERT(5 == (int)GrStencilTest::kLEqual);
GR_STATIC_ASSERT(6 == (int)GrStencilTest::kEqual);
GR_STATIC_ASSERT(7 == (int)GrStencilTest::kNotEqual);
SkASSERT(test < (GrStencilTest)kGrStencilTestCount);
return gTable[basicFunc];
return gTable[(int)test];
}

View File

@ -10,7 +10,7 @@
#include "gl/GrGLInterface.h"
#include "GrGLDefines.h"
#include "GrStencil.h"
#include "GrStencilSettings.h"
class SkMatrix;
@ -205,7 +205,7 @@ void GrGLClearErr(const GrGLInterface* gl);
// call glGetError without doing a redundant error check or logging.
#define GR_GL_GET_ERROR(IFACE) (IFACE)->fFunctions.fGetError()
GrGLenum GrToGLStencilFunc(GrStencilFunc basicFunc);
GrGLenum GrToGLStencilFunc(GrStencilTest test);
#endif

View File

@ -610,15 +610,17 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
if (fInstanceData->count()) {
pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
static constexpr GrStencilSettings kStencilPass(
kZero_StencilOp,
kKeep_StencilOp,
kNotEqual_StencilFunc,
0xffff,
0x0000,
0xffff);
static constexpr GrUserStencilSettings kCoverPass(
GrUserStencilSettings::StaticInit<
0x0000,
GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
0xffff,
GrUserStencilOp::kZero,
GrUserStencilOp::kKeep,
0xffff>()
);
*pipelineBuilder->stencil() = kStencilPass;
pipelineBuilder->setUserStencil(&kCoverPass);
SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {

View File

@ -100,51 +100,51 @@ static void setup_input_assembly_state(GrPrimitiveType primitiveType,
VkStencilOp stencil_op_to_vk_stencil_op(GrStencilOp op) {
static const VkStencilOp gTable[] = {
VK_STENCIL_OP_KEEP, // kKeep_StencilOp
VK_STENCIL_OP_REPLACE, // kReplace_StencilOp
VK_STENCIL_OP_INCREMENT_AND_WRAP, // kIncWrap_StencilOp
VK_STENCIL_OP_INCREMENT_AND_CLAMP, // kIncClamp_StencilOp
VK_STENCIL_OP_DECREMENT_AND_WRAP, // kDecWrap_StencilOp
VK_STENCIL_OP_DECREMENT_AND_CLAMP, // kDecClamp_StencilOp
VK_STENCIL_OP_ZERO, // kZero_StencilOp
VK_STENCIL_OP_INVERT, // kInvert_StencilOp
VK_STENCIL_OP_KEEP, // kKeep
VK_STENCIL_OP_ZERO, // kZero
VK_STENCIL_OP_REPLACE, // kReplace
VK_STENCIL_OP_INVERT, // kInvert
VK_STENCIL_OP_INCREMENT_AND_WRAP, // kIncWrap
VK_STENCIL_OP_DECREMENT_AND_WRAP, // kDecWrap
VK_STENCIL_OP_INCREMENT_AND_CLAMP, // kIncClamp
VK_STENCIL_OP_DECREMENT_AND_CLAMP, // kDecClamp
};
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gTable) == kStencilOpCnt);
GR_STATIC_ASSERT(0 == kKeep_StencilOp);
GR_STATIC_ASSERT(1 == kReplace_StencilOp);
GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
GR_STATIC_ASSERT(6 == kZero_StencilOp);
GR_STATIC_ASSERT(7 == kInvert_StencilOp);
SkASSERT((unsigned)op < kStencilOpCnt);
return gTable[op];
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gTable) == kGrStencilOpCount);
GR_STATIC_ASSERT(0 == (int)GrStencilOp::kKeep);
GR_STATIC_ASSERT(1 == (int)GrStencilOp::kZero);
GR_STATIC_ASSERT(2 == (int)GrStencilOp::kReplace);
GR_STATIC_ASSERT(3 == (int)GrStencilOp::kInvert);
GR_STATIC_ASSERT(4 == (int)GrStencilOp::kIncWrap);
GR_STATIC_ASSERT(5 == (int)GrStencilOp::kDecWrap);
GR_STATIC_ASSERT(6 == (int)GrStencilOp::kIncClamp);
GR_STATIC_ASSERT(7 == (int)GrStencilOp::kDecClamp);
SkASSERT(op < (GrStencilOp)kGrStencilOpCount);
return gTable[(int)op];
}
VkCompareOp stencil_func_to_vk_compare_op(GrStencilFunc basicFunc) {
VkCompareOp stencil_func_to_vk_compare_op(GrStencilTest test) {
static const VkCompareOp gTable[] = {
VK_COMPARE_OP_ALWAYS, // kAlways_StencilFunc
VK_COMPARE_OP_NEVER, // kNever_StencilFunc
VK_COMPARE_OP_GREATER, // kGreater_StencilFunc
VK_COMPARE_OP_GREATER_OR_EQUAL, // kGEqual_StencilFunc
VK_COMPARE_OP_LESS, // kLess_StencilFunc
VK_COMPARE_OP_LESS_OR_EQUAL, // kLEqual_StencilFunc,
VK_COMPARE_OP_EQUAL, // kEqual_StencilFunc,
VK_COMPARE_OP_NOT_EQUAL, // kNotEqual_StencilFunc,
VK_COMPARE_OP_ALWAYS, // kAlways
VK_COMPARE_OP_NEVER, // kNever
VK_COMPARE_OP_GREATER, // kGreater
VK_COMPARE_OP_GREATER_OR_EQUAL, // kGEqual
VK_COMPARE_OP_LESS, // kLess
VK_COMPARE_OP_LESS_OR_EQUAL, // kLEqual
VK_COMPARE_OP_EQUAL, // kEqual
VK_COMPARE_OP_NOT_EQUAL, // kNotEqual
};
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gTable) == kBasicStencilFuncCnt);
GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
GR_STATIC_ASSERT(1 == kNever_StencilFunc);
GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
GR_STATIC_ASSERT(4 == kLess_StencilFunc);
GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
SkASSERT((unsigned)basicFunc < kBasicStencilFuncCnt);
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gTable) == kGrStencilTestCount);
GR_STATIC_ASSERT(0 == (int)GrStencilTest::kAlways);
GR_STATIC_ASSERT(1 == (int)GrStencilTest::kNever);
GR_STATIC_ASSERT(2 == (int)GrStencilTest::kGreater);
GR_STATIC_ASSERT(3 == (int)GrStencilTest::kGEqual);
GR_STATIC_ASSERT(4 == (int)GrStencilTest::kLess);
GR_STATIC_ASSERT(5 == (int)GrStencilTest::kLEqual);
GR_STATIC_ASSERT(6 == (int)GrStencilTest::kEqual);
GR_STATIC_ASSERT(7 == (int)GrStencilTest::kNotEqual);
SkASSERT(test < (GrStencilTest)kGrStencilTestCount);
return gTable[basicFunc];
return gTable[(int)test];
}
void setup_depth_stencil_state(const GrVkGpu* gpu,
@ -162,24 +162,28 @@ void setup_depth_stencil_state(const GrVkGpu* gpu,
stencilInfo->stencilTestEnable = !stencilSettings.isDisabled();
if (!stencilSettings.isDisabled()) {
// Set front face
GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
stencilInfo->front.failOp = stencil_op_to_vk_stencil_op(stencilSettings.failOp(face));
stencilInfo->front.passOp = stencil_op_to_vk_stencil_op(stencilSettings.passOp(face));
const GrStencilSettings::Face& front = stencilSettings.front();
stencilInfo->front.failOp = stencil_op_to_vk_stencil_op(front.fFailOp);
stencilInfo->front.passOp = stencil_op_to_vk_stencil_op(front.fPassOp);
stencilInfo->front.depthFailOp = stencilInfo->front.failOp;
stencilInfo->front.compareOp = stencil_func_to_vk_compare_op(stencilSettings.func(face));
stencilInfo->front.compareMask = stencilSettings.funcMask(face);
stencilInfo->front.writeMask = stencilSettings.writeMask(face);
stencilInfo->front.reference = stencilSettings.funcRef(face);
stencilInfo->front.compareOp = stencil_func_to_vk_compare_op(front.fTest);
stencilInfo->front.compareMask = front.fTestMask;
stencilInfo->front.writeMask = front.fWriteMask;
stencilInfo->front.reference = front.fRef;
// Set back face
face = GrStencilSettings::kBack_Face;
stencilInfo->back.failOp = stencil_op_to_vk_stencil_op(stencilSettings.failOp(face));
stencilInfo->back.passOp = stencil_op_to_vk_stencil_op(stencilSettings.passOp(face));
stencilInfo->back.depthFailOp = stencilInfo->front.failOp;
stencilInfo->back.compareOp = stencil_func_to_vk_compare_op(stencilSettings.func(face));
stencilInfo->back.compareMask = stencilSettings.funcMask(face);
stencilInfo->back.writeMask = stencilSettings.writeMask(face);
stencilInfo->back.reference = stencilSettings.funcRef(face);
if (!stencilSettings.isTwoSided()) {
stencilInfo->back = stencilInfo->front;
} else {
const GrStencilSettings::Face& back = stencilSettings.back();
stencilInfo->back.failOp = stencil_op_to_vk_stencil_op(back.fFailOp);
stencilInfo->back.passOp = stencil_op_to_vk_stencil_op(back.fPassOp);
stencilInfo->back.depthFailOp = stencilInfo->front.failOp;
stencilInfo->back.compareOp = stencil_func_to_vk_compare_op(back.fTest);
stencilInfo->back.compareMask = back.fTestMask;
stencilInfo->back.writeMask = back.fWriteMask;
stencilInfo->back.reference = back.fRef;
}
}
stencilInfo->minDepthBounds = 0.0f;
stencilInfo->maxDepthBounds = 1.0f;

View File

@ -9,6 +9,7 @@
#ifndef GrVkPipelineState_DEFINED
#define GrVkPipelineState_DEFINED
#include "GrStencilSettings.h"
#include "GrVkImage.h"
#include "GrVkProgramDesc.h"
#include "GrVkPipelineStateDataManager.h"

View File

@ -277,25 +277,29 @@ static void set_random_state(GrPipelineBuilder* pipelineBuilder, SkRandom* rando
// right now, the only thing we seem to care about in drawState's stencil is 'doesWrite()'
static void set_random_stencil(GrPipelineBuilder* pipelineBuilder, SkRandom* random) {
static constexpr GrStencilSettings kDoesWriteStencil(
kReplace_StencilOp,
kReplace_StencilOp,
kAlways_StencilFunc,
0xffff,
0xffff,
0xffff);
static constexpr GrStencilSettings kDoesNotWriteStencil(
kKeep_StencilOp,
kKeep_StencilOp,
kNever_StencilFunc,
0xffff,
0xffff,
0xffff);
static constexpr GrUserStencilSettings kDoesWriteStencil(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kAlways,
0xffff,
GrUserStencilOp::kReplace,
GrUserStencilOp::kReplace,
0xffff>()
);
static constexpr GrUserStencilSettings kDoesNotWriteStencil(
GrUserStencilSettings::StaticInit<
0xffff,
GrUserStencilTest::kNever,
0xffff,
GrUserStencilOp::kKeep,
GrUserStencilOp::kKeep,
0xffff>()
);
if (random->nextBool()) {
pipelineBuilder->setStencil(kDoesWriteStencil);
pipelineBuilder->setUserStencil(&kDoesWriteStencil);
} else {
pipelineBuilder->setStencil(kDoesNotWriteStencil);
pipelineBuilder->setUserStencil(&kDoesNotWriteStencil);
}
}