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
Review-Url: https://codereview.chromium.org/1962243002
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 48f1c85..dc16c2c 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -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',
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 56c0ee6..c7b88ca 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -62,7 +62,7 @@
// 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 @@
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 @@
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 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 @@
}
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 @@
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 @@
// 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 @@
const GrClip& clip = doDevBoundsClip ? devBoundsClip : pipelineBuilder.clip();
if (clip.isWideOpen(clipSpaceRTIBounds)) {
- this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
@@ -396,7 +390,7 @@
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 @@
if (elements.isEmpty()) {
if (GrReducedClip::kAllIn_InitialState == initialState) {
if (clipSpaceIBounds == clipSpaceRTIBounds) {
- this->setPipelineBuilderStencil(pipelineBuilder, ars);
return true;
}
} else {
@@ -434,6 +427,8 @@
} 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 @@
// 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 @@
!SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) {
out->fScissorState.set(scissorSpaceIBounds);
}
- this->setPipelineBuilderStencil(pipelineBuilder, ars);
out->fClipCoverageFP.reset(clipFP);
return true;
}
@@ -508,7 +502,6 @@
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 @@
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 @@
// 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 @@
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 @@
clipPath.toggleInverseFillType();
}
- SkASSERT(pipelineBuilder.getStencil().isDisabled());
+ SkASSERT(!pipelineBuilder.hasUserStencilSettings());
GrPathRenderer::CanDrawPathArgs canDrawArgs;
canDrawArgs.fShaderCaps = this->getContext()->caps()->shaderCaps();
@@ -806,7 +800,7 @@
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 @@
}
}
- 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 @@
// 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 @@
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 @@
return result;
}
-
-////////////////////////////////////////////////////////////////////////////////
-
-void GrClipMaskManager::adjustPathStencilParams(const GrStencilAttachment* stencilAttachment,
- GrStencilSettings* settings) {
- if (stencilAttachment) {
- int stencilBits = stencilAttachment->bits();
- this->adjustStencilParams(settings, fClipMode, stencilBits);
- }
-}
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index ef0c2be..096dafd 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -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 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 @@
/**
* 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 @@
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;
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index a7c7f6a..7f5cdc7 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -375,7 +375,7 @@
}
bool GrDrawContextPriv::drawAndStencilRect(const SkIRect* scissorRect,
- const GrStencilSettings& ss,
+ const GrUserStencilSettings* ss,
SkRegion::Op op,
bool invert,
bool doAA,
@@ -397,7 +397,7 @@
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 @@
}
bool GrDrawContextPriv::drawAndStencilPath(const SkIRect* scissorRect,
- const GrStencilSettings& ss,
+ const GrUserStencilSettings* ss,
SkRegion::Op op,
bool invert,
bool doAA,
@@ -880,7 +880,7 @@
// 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 @@
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 @@
}
GrPipelineBuilder pipelineBuilder(paint, fDrawContext->accessRenderTarget(), clip);
- pipelineBuilder.setStencil(ss);
+ pipelineBuilder.setUserStencil(ss);
GrPathRenderer::DrawPathArgs args;
args.fTarget = fDrawContext->getDrawTarget();
@@ -939,7 +939,7 @@
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 @@
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)
diff --git a/src/gpu/GrDrawContextPriv.h b/src/gpu/GrDrawContextPriv.h
index 4482e35..973315b 100644
--- a/src/gpu/GrDrawContextPriv.h
+++ b/src/gpu/GrDrawContextPriv.h
@@ -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 GrDrawContextPriv {
public:
bool drawAndStencilRect(const SkIRect* scissorRect,
- const GrStencilSettings&,
+ const GrUserStencilSettings*,
SkRegion::Op op,
bool invert,
bool doAA,
@@ -26,7 +26,7 @@
const SkRect&);
bool drawAndStencilPath(const SkIRect* scissorRect,
- const GrStencilSettings&,
+ const GrUserStencilSettings*,
SkRegion::Op op,
bool invert,
bool doAA,
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 0f180dc..f824c81 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -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 @@
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 @@
}
GrPipeline::CreateArgs args;
- if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(), batch)) {
+ if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(),
+ clip.hasStencilClip(), batch)) {
return;
}
@@ -269,34 +270,36 @@
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 @@
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,16 @@
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 = rt->renderTargetPriv().getStencilAttachment();
+ SkASSERT(stencilAttachment)
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 +346,8 @@
// 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 +358,16 @@
}
// 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 +551,20 @@
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) {
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index c863b45..907fc1f 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -223,6 +223,7 @@
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 @@
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.
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index e38b913..63a56dd 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -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 GrPrimitiveProcessor;
class GrRenderTarget;
class GrStencilAttachment;
+class GrStencilSettings;
class GrSurface;
class GrTexture;
@@ -485,17 +485,6 @@
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);
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 3bc0230..a7609db 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -9,7 +9,6 @@
#define GrPathRenderer_DEFINED
#include "GrDrawTarget.h"
-#include "GrStencil.h"
#include "GrStyle.h"
#include "SkDrawProcs.h"
@@ -82,7 +81,7 @@
bool fAntiAlias;
// These next two are only used by GrStencilAndCoverPathRenderer
- bool fIsStencilDisabled;
+ bool fHasUserStencilSettings;
bool fIsStencilBufferMSAA;
void validate() const {
@@ -155,11 +154,11 @@
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 @@
* 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;
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index 8d384ef..761a876 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -20,9 +20,35 @@
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 @@
&args.fDstTexture,
*args.fCaps));
if (!xferProcessor) {
+ pipeline->~GrPipeline();
return nullptr;
}
} else {
@@ -51,7 +78,7 @@
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 @@
// 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 @@
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
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index f64b875..ff1905b 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -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 @@
const GrCaps* fCaps;
GrPipelineOptimizations fOpts;
const GrScissorState* fScissor;
+ int fNumStencilBits;
+ bool fHasStencilClip;
GrXferProcessor::DstTexture fDstTexture;
};
diff --git a/src/gpu/GrPipelineBuilder.cpp b/src/gpu/GrPipelineBuilder.cpp
index 1fd568e..4731cbb 100644
--- a/src/gpu/GrPipelineBuilder.cpp
+++ b/src/gpu/GrPipelineBuilder.cpp
@@ -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 @@
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,
diff --git a/src/gpu/GrPipelineBuilder.h b/src/gpu/GrPipelineBuilder.h
index 2f78fc5..18f817b 100644
--- a/src/gpu/GrPipelineBuilder.h
+++ b/src/gpu/GrPipelineBuilder.h
@@ -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 @@
/// @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 @@
SkAutoTUnref<GrRenderTarget> fRenderTarget;
uint32_t fFlags;
- GrStencilSettings fStencilSettings;
+ const GrUserStencilSettings* fUserStencilSettings;
DrawFace fDrawFace;
mutable SkAutoTUnref<const GrXPFactory> fXPFactory;
FragmentProcessorArray fColorFragmentProcessors;
diff --git a/src/gpu/GrStencil.cpp b/src/gpu/GrStencil.cpp
deleted file mode 100644
index 94559ab..0000000
--- a/src/gpu/GrStencil.cpp
+++ /dev/null
@@ -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));
-}
diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h
deleted file mode 100644
index 075eb1e..0000000
--- a/src/gpu/GrStencil.h
+++ /dev/null
@@ -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
diff --git a/src/gpu/GrStencilSettings.cpp b/src/gpu/GrStencilSettings.cpp
new file mode 100644
index 0000000..ae8b03b
--- /dev/null
+++ b/src/gpu/GrStencilSettings.cpp
@@ -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));
+}
diff --git a/src/gpu/GrStencilSettings.h b/src/gpu/GrStencilSettings.h
new file mode 100644
index 0000000..15a8cac
--- /dev/null
+++ b/src/gpu/GrStencilSettings.h
@@ -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
diff --git a/src/gpu/GrUserStencilSettings.h b/src/gpu/GrUserStencilSettings.h
new file mode 100644
index 0000000..a07a083
--- /dev/null
+++ b/src/gpu/GrUserStencilSettings.h
@@ -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
diff --git a/src/gpu/batches/GrDefaultPathRenderer.cpp b/src/gpu/batches/GrDefaultPathRenderer.cpp
index 9994b26..747be2d 100644
--- a/src/gpu/batches/GrDefaultPathRenderer.cpp
+++ b/src/gpu/batches/GrDefaultPathRenderer.cpp
@@ -443,11 +443,11 @@
// 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 @@
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)) {
diff --git a/src/gpu/batches/GrDrawPathBatch.h b/src/gpu/batches/GrDrawPathBatch.h
index d29d046..55fe31a 100644
--- a/src/gpu/batches/GrDrawPathBatch.h
+++ b/src/gpu/batches/GrDrawPathBatch.h
@@ -28,7 +28,10 @@
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,
diff --git a/src/gpu/batches/GrMSAAPathRenderer.cpp b/src/gpu/batches/GrMSAAPathRenderer.cpp
index 6e0076c..8b1319b 100644
--- a/src/gpu/batches/GrMSAAPathRenderer.cpp
+++ b/src/gpu/batches/GrMSAAPathRenderer.cpp
@@ -578,11 +578,11 @@
// 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 @@
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)) {
diff --git a/src/gpu/batches/GrPathStencilSettings.h b/src/gpu/batches/GrPathStencilSettings.h
index dd930a0d..f37d1b2 100644
--- a/src/gpu/batches/GrPathStencilSettings.h
+++ b/src/gpu/batches/GrPathStencilSettings.h
@@ -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 @@
// 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
diff --git a/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp b/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp
index ddba1c8..1309ded 100644
--- a/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp
@@ -36,7 +36,7 @@
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 @@
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 @@
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 @@
&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;
}
diff --git a/src/gpu/batches/GrStencilPathBatch.h b/src/gpu/batches/GrStencilPathBatch.h
index 33189c2..8477822 100644
--- a/src/gpu/batches/GrStencilPathBatch.h
+++ b/src/gpu/batches/GrStencilPathBatch.h
@@ -21,11 +21,14 @@
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 @@
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) {
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 40d0e48..8c5951d 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2869,40 +2869,38 @@
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
@@ -2932,20 +2930,18 @@
}
}
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;
diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp
index 5616f9d..66648d8 100644
--- a/src/gpu/gl/GrGLPathRendering.cpp
+++ b/src/gpu/gl/GrGLPathRendering.cpp
@@ -70,9 +70,9 @@
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 @@
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 @@
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 @@
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 @@
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;
}
diff --git a/src/gpu/gl/GrGLPathRendering.h b/src/gpu/gl/GrGLPathRendering.h
index 8fb699d..40f72cc 100644
--- a/src/gpu/gl/GrGLPathRendering.h
+++ b/src/gpu/gl/GrGLPathRendering.h
@@ -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;
/**
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
index 46b58f1..ecc587b 100644
--- a/src/gpu/gl/GrGLUtil.cpp
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -334,27 +334,26 @@
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];
}
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
index c30ee93..98f738b 100644
--- a/src/gpu/gl/GrGLUtil.h
+++ b/src/gpu/gl/GrGLUtil.h
@@ -10,7 +10,7 @@
#include "gl/GrGLInterface.h"
#include "GrGLDefines.h"
-#include "GrStencil.h"
+#include "GrStencilSettings.h"
class SkMatrix;
@@ -205,7 +205,7 @@
// 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
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.cpp b/src/gpu/text/GrStencilAndCoverTextContext.cpp
index cc01884..bce0716 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/text/GrStencilAndCoverTextContext.cpp
@@ -610,15 +610,17 @@
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()) {
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 2a9c6e2..8227e12 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -100,51 +100,51 @@
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 @@
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;
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index b7f954c..d0b0f56 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -9,6 +9,7 @@
#ifndef GrVkPipelineState_DEFINED
#define GrVkPipelineState_DEFINED
+#include "GrStencilSettings.h"
#include "GrVkImage.h"
#include "GrVkProgramDesc.h"
#include "GrVkPipelineStateDataManager.h"
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index c5d61d8..3cbc76c 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -277,25 +277,29 @@
// 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);
}
}