Reland "Refactor stencil clip mask generation into helper"
This reverts commitde228e53fe
. Reason for revert: GrReducedClip now assumes context isn't abandoned, windowrectangles GM abuses GrReducedClip and has to be abandon-aware. Original change's description: > Revert "Refactor stencil clip mask generation into helper" > > This reverts commit8b3a8a5238
. > > Reason for revert: GM assert failure > > Original change's description: > > Refactor stencil clip mask generation into helper > > > > Change-Id: If3dc80efde3b44e87ba8e7af3a258896ec5e78e6 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/288977 > > Commit-Queue: Michael Ludwig <michaelludwig@google.com> > > Reviewed-by: Chris Dalton <csmartdalton@google.com> > > Reviewed-by: Brian Salomon <bsalomon@google.com> > > TBR=bsalomon@google.com,csmartdalton@google.com,michaelludwig@google.com > > Change-Id: I16559f791601145f57d147cdae345c200af313f1 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/289237 > Reviewed-by: Michael Ludwig <michaelludwig@google.com> > Commit-Queue: Michael Ludwig <michaelludwig@google.com> # Not skipping CQ checks because this is a reland. Change-Id: I6a9372edecd0bdc1a38464ab85f7b7f3ca85e5ed Reviewed-on: https://skia-review.googlesource.com/c/skia/+/289239 Reviewed-by: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
f497c36625
commit
828d341199
@ -266,6 +266,12 @@ void WindowRectanglesMaskGM::visualizeAlphaMask(GrContext* ctx, GrRenderTargetCo
|
||||
void WindowRectanglesMaskGM::visualizeStencilMask(GrContext* ctx, GrRenderTargetContext* rtc,
|
||||
const GrReducedClip& reducedClip,
|
||||
GrPaint&& paint) {
|
||||
if (ctx->abandoned()) {
|
||||
// GrReducedClip assumes the context hasn't been abandoned, which is reasonable since it is
|
||||
// only ever used if a draw op is made. Since this GM calls it directly, it has to be taken
|
||||
// into account.
|
||||
return;
|
||||
}
|
||||
// Draw a checker pattern into the stencil buffer so we can visualize the regions left untouched
|
||||
// by the clip mask generation.
|
||||
this->stencilCheckerboard(rtc, false);
|
||||
|
@ -199,6 +199,8 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/GrStencilAttachment.cpp",
|
||||
"$_src/gpu/GrStencilAttachment.h",
|
||||
"$_src/gpu/GrStencilClip.h",
|
||||
"$_src/gpu/GrStencilMaskHelper.cpp",
|
||||
"$_src/gpu/GrStencilMaskHelper.h",
|
||||
"$_src/gpu/GrStencilSettings.cpp",
|
||||
"$_src/gpu/GrStencilSettings.h",
|
||||
"$_src/gpu/GrStyle.cpp",
|
||||
|
@ -307,17 +307,7 @@ bool GrClipStackClip::applyClipMask(GrRecordingContext* context,
|
||||
}
|
||||
}
|
||||
|
||||
// This relies on the property that a reduced sub-rect of the last clip will contain all the
|
||||
// relevant window rectangles that were in the last clip. This subtle requirement will go away
|
||||
// after clipping is overhauled.
|
||||
if (renderTargetContext->priv().mustRenderClip(reducedClip.maskGenID(), reducedClip.scissor(),
|
||||
reducedClip.numAnalyticFPs())) {
|
||||
reducedClip.drawStencilClipMask(context, renderTargetContext);
|
||||
renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor(),
|
||||
reducedClip.numAnalyticFPs());
|
||||
}
|
||||
// GrAppliedClip doesn't need to figure numAnalyticFPs into its key (used by operator==) because
|
||||
// it verifies the FPs are also equal.
|
||||
reducedClip.drawStencilClipMask(context, renderTargetContext);
|
||||
out->hardClip().addStencilClip(reducedClip.maskGenID());
|
||||
return true;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "src/gpu/GrRenderTargetContext.h"
|
||||
#include "src/gpu/GrRenderTargetContextPriv.h"
|
||||
#include "src/gpu/GrStencilClip.h"
|
||||
#include "src/gpu/GrStencilMaskHelper.h"
|
||||
#include "src/gpu/GrStencilSettings.h"
|
||||
#include "src/gpu/GrStyle.h"
|
||||
#include "src/gpu/GrUserStencilSettings.h"
|
||||
@ -711,16 +712,6 @@ static bool stencil_element(GrRenderTargetContext* rtc,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void stencil_device_rect(GrRenderTargetContext* rtc,
|
||||
const GrHardClip& clip,
|
||||
const GrUserStencilSettings* ss,
|
||||
GrAA aa,
|
||||
const SkRect& rect) {
|
||||
GrPaint paint;
|
||||
paint.setXPFactory(GrDisableColorXPFactory::Get());
|
||||
rtc->priv().stencilRect(clip, ss, std::move(paint), aa, SkMatrix::I(), rect);
|
||||
}
|
||||
|
||||
static void draw_element(GrRenderTargetContext* rtc,
|
||||
const GrClip& clip, // TODO: can this just always be WideOpen?
|
||||
GrPaint&& paint,
|
||||
@ -822,153 +813,32 @@ bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
|
||||
|
||||
bool GrReducedClip::drawStencilClipMask(GrRecordingContext* context,
|
||||
GrRenderTargetContext* renderTargetContext) const {
|
||||
// We set the current clip to the bounds so that our recursive draws are scissored to them.
|
||||
GrStencilClip stencilClip(fScissor, this->maskGenID());
|
||||
|
||||
if (!fWindowRects.empty()) {
|
||||
stencilClip.fixedClip().setWindowRectangles(fWindowRects,
|
||||
GrWindowRectsState::Mode::kExclusive);
|
||||
GrStencilMaskHelper helper(context, renderTargetContext);
|
||||
if (!helper.init(fScissor, this->maskGenID(), fWindowRects, this->numAnalyticFPs())) {
|
||||
// The stencil mask doesn't need updating
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initialState = InitialState::kAllIn == this->initialState();
|
||||
renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState);
|
||||
helper.clear(InitialState::kAllIn == this->initialState());
|
||||
|
||||
// walk through each clip element and perform its set op with the existing clip.
|
||||
for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
|
||||
const Element* element = iter.get();
|
||||
// MIXED SAMPLES TODO: We can use stencil with mixed samples as well.
|
||||
bool doStencilMSAA = element->isAA() && renderTargetContext->numSamples() > 1;
|
||||
// Since we are only drawing to the stencil buffer, we can use kMSAA even if the render
|
||||
// target is mixed sampled.
|
||||
auto pathAAType = (doStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
|
||||
bool fillInverted = false;
|
||||
|
||||
// This will be used to determine whether the clip shape can be rendered into the
|
||||
// stencil with arbitrary stencil settings.
|
||||
GrPathRenderer::StencilSupport stencilSupport;
|
||||
|
||||
SkRegion::Op op = (SkRegion::Op)element->getOp();
|
||||
GrAA aa = element->isAA() ? GrAA::kYes : GrAA::kNo;
|
||||
|
||||
GrPathRenderer* pr = nullptr;
|
||||
SkPath clipPath;
|
||||
if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
|
||||
stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
|
||||
fillInverted = false;
|
||||
helper.drawRect(element->getDeviceSpaceRect(), SkMatrix::I(), op, aa);
|
||||
} else {
|
||||
element->asDeviceSpacePath(&clipPath);
|
||||
fillInverted = clipPath.isInverseFillType();
|
||||
if (fillInverted) {
|
||||
clipPath.toggleInverseFillType();
|
||||
}
|
||||
|
||||
GrStyledShape shape(clipPath, GrStyle::SimpleFill());
|
||||
GrPathRenderer::CanDrawPathArgs canDrawArgs;
|
||||
canDrawArgs.fCaps = context->priv().caps();
|
||||
canDrawArgs.fProxy = renderTargetContext->asRenderTargetProxy();
|
||||
canDrawArgs.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
|
||||
canDrawArgs.fViewMatrix = &SkMatrix::I();
|
||||
canDrawArgs.fShape = &shape;
|
||||
canDrawArgs.fPaint = nullptr;
|
||||
canDrawArgs.fAAType = pathAAType;
|
||||
canDrawArgs.fHasUserStencilSettings = false;
|
||||
canDrawArgs.fTargetIsWrappedVkSecondaryCB = renderTargetContext->wrapsVkSecondaryCB();
|
||||
|
||||
GrDrawingManager* dm = context->priv().drawingManager();
|
||||
pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil,
|
||||
&stencilSupport);
|
||||
if (!pr) {
|
||||
SkPath path;
|
||||
element->asDeviceSpacePath(&path);
|
||||
if (!helper.drawPath(path, SkMatrix::I(), op, aa)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool canRenderDirectToStencil =
|
||||
GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
|
||||
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 (!drawDirectToClip) {
|
||||
static constexpr GrUserStencilSettings kDrawToStencil(
|
||||
GrUserStencilSettings::StaticInit<
|
||||
0x0000,
|
||||
GrUserStencilTest::kAlways,
|
||||
0xffff,
|
||||
GrUserStencilOp::kIncMaybeClamp,
|
||||
GrUserStencilOp::kIncMaybeClamp,
|
||||
0xffff>()
|
||||
);
|
||||
if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
|
||||
stencil_device_rect(renderTargetContext, stencilClip.fixedClip(), &kDrawToStencil,
|
||||
GrAA(doStencilMSAA), element->getDeviceSpaceRect());
|
||||
} else {
|
||||
if (!clipPath.isEmpty()) {
|
||||
GrStyledShape shape(clipPath, GrStyle::SimpleFill());
|
||||
if (canRenderDirectToStencil) {
|
||||
GrPaint paint;
|
||||
paint.setXPFactory(GrDisableColorXPFactory::Get());
|
||||
|
||||
GrPathRenderer::DrawPathArgs args{context,
|
||||
std::move(paint),
|
||||
&kDrawToStencil,
|
||||
renderTargetContext,
|
||||
&stencilClip.fixedClip(),
|
||||
&stencilClip.fixedClip().scissorRect(),
|
||||
&SkMatrix::I(),
|
||||
&shape,
|
||||
pathAAType,
|
||||
false};
|
||||
pr->drawPath(args);
|
||||
} else {
|
||||
GrPathRenderer::StencilPathArgs args;
|
||||
args.fContext = context;
|
||||
args.fRenderTargetContext = renderTargetContext;
|
||||
args.fClip = &stencilClip.fixedClip();
|
||||
args.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
|
||||
args.fViewMatrix = &SkMatrix::I();
|
||||
args.fDoStencilMSAA = GrAA(doStencilMSAA);
|
||||
args.fShape = &shape;
|
||||
pr->stencilPath(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now we modify the clip bit by rendering either the clip
|
||||
// element directly or a bounding rect of the entire clip.
|
||||
for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
|
||||
if (drawDirectToClip) {
|
||||
if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
|
||||
stencil_device_rect(renderTargetContext, stencilClip, *pass,
|
||||
GrAA(doStencilMSAA), element->getDeviceSpaceRect());
|
||||
} else {
|
||||
GrStyledShape shape(clipPath, GrStyle::SimpleFill());
|
||||
GrPaint paint;
|
||||
paint.setXPFactory(GrDisableColorXPFactory::Get());
|
||||
GrPathRenderer::DrawPathArgs args{context,
|
||||
std::move(paint),
|
||||
*pass,
|
||||
renderTargetContext,
|
||||
&stencilClip,
|
||||
&stencilClip.fixedClip().scissorRect(),
|
||||
&SkMatrix::I(),
|
||||
&shape,
|
||||
pathAAType,
|
||||
false};
|
||||
pr->drawPath(args);
|
||||
}
|
||||
} else {
|
||||
// The view matrix is setup to do clip space -> stencil space translation, so
|
||||
// draw rect in clip space.
|
||||
stencil_device_rect(renderTargetContext, stencilClip, *pass, GrAA(doStencilMSAA),
|
||||
SkRect::Make(fScissor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
helper.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
const GrFixedClip& fixedClip() const { return fFixedClip; }
|
||||
GrFixedClip& fixedClip() { return fFixedClip; }
|
||||
|
||||
bool stencilStackID() const { return fStencilStackID; }
|
||||
uint32_t stencilStackID() const { return fStencilStackID; }
|
||||
bool hasStencilClip() const { return SK_InvalidGenID != fStencilStackID; }
|
||||
void setStencilClip(uint32_t stencilStackID) { fStencilStackID = stencilStackID; }
|
||||
|
||||
|
470
src/gpu/GrStencilMaskHelper.cpp
Normal file
470
src/gpu/GrStencilMaskHelper.cpp
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/gpu/GrStencilMaskHelper.h"
|
||||
|
||||
#include "include/core/SkMatrix.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "src/gpu/GrRecordingContextPriv.h"
|
||||
#include "src/gpu/GrRenderTargetContextPriv.h"
|
||||
#include "src/gpu/GrStencilSettings.h"
|
||||
#include "src/gpu/geometry/GrShape.h"
|
||||
#include "src/gpu/geometry/GrStyledShape.h"
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
}
|
||||
};
|
||||
|
||||
///////
|
||||
// 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.
|
||||
};
|
||||
|
||||
static_assert(0 == SkRegion::kDifference_Op);
|
||||
static_assert(1 == SkRegion::kIntersect_Op);
|
||||
static_assert(2 == SkRegion::kUnion_Op);
|
||||
static_assert(3 == SkRegion::kXOR_Op);
|
||||
static_assert(4 == SkRegion::kReverseDifference_Op);
|
||||
static_assert(5 == SkRegion::kReplace_Op);
|
||||
|
||||
// Settings used to when not allowed to draw directly to the clip to fill the user stencil bits
|
||||
// before applying the covering clip stencil passes.
|
||||
static constexpr GrUserStencilSettings gDrawToStencil(
|
||||
GrUserStencilSettings::StaticInit<
|
||||
0x0000,
|
||||
GrUserStencilTest::kAlways,
|
||||
0xffff,
|
||||
GrUserStencilOp::kIncMaybeClamp,
|
||||
GrUserStencilOp::kIncMaybeClamp,
|
||||
0xffff>()
|
||||
);
|
||||
|
||||
// Get the stencil settings per-pass to achieve the given fill+region op effect on the
|
||||
// stencil buffer.
|
||||
//
|
||||
// If drawDirectToClip comes back 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.
|
||||
//
|
||||
// This returns a null-terminated list of const GrUserStencilSettings*
|
||||
static GrUserStencilSettings const* const* get_stencil_passes(
|
||||
SkRegion::Op op, GrPathRenderer::StencilSupport stencilSupport, bool fillInverted,
|
||||
bool* drawDirectToClip) {
|
||||
bool canRenderDirectToStencil =
|
||||
GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
|
||||
|
||||
// TODO: inverse fill + intersect op can be direct.
|
||||
// TODO: this can be greatly simplified when we only need intersect and difference ops and
|
||||
// none of the paths will be inverse-filled (just toggle the op instead).
|
||||
SkASSERT((unsigned)op <= SkRegion::kLastOp);
|
||||
if (canRenderDirectToStencil && !fillInverted) {
|
||||
GrUserStencilSettings const* const* directPass = gDirectDrawTable[op];
|
||||
if (directPass[0]) {
|
||||
*drawDirectToClip = true;
|
||||
return directPass;
|
||||
}
|
||||
}
|
||||
*drawDirectToClip = false;
|
||||
return gUserToClipTable[fillInverted][op];
|
||||
}
|
||||
|
||||
static void draw_stencil_rect(GrRenderTargetContext* rtc, const GrHardClip& clip,
|
||||
const GrUserStencilSettings* ss, const SkMatrix& matrix,
|
||||
const SkRect& rect, GrAA aa) {
|
||||
GrPaint paint;
|
||||
paint.setXPFactory(GrDisableColorXPFactory::Get());
|
||||
rtc->priv().stencilRect(clip, ss, std::move(paint), aa, matrix, rect);
|
||||
}
|
||||
|
||||
static void draw_path(GrRecordingContext* context, GrRenderTargetContext* rtc,
|
||||
GrPathRenderer* pr, const GrHardClip& clip, const SkIRect& bounds,
|
||||
const GrUserStencilSettings* ss, const SkMatrix& matrix,
|
||||
const GrStyledShape& shape, GrAA aa) {
|
||||
GrPaint paint;
|
||||
paint.setXPFactory(GrDisableColorXPFactory::Get());
|
||||
|
||||
// Since we are only drawing to the stencil buffer, we can use kMSAA even if the render
|
||||
// target is mixed sampled.
|
||||
GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
|
||||
|
||||
GrPathRenderer::DrawPathArgs args{context,
|
||||
std::move(paint),
|
||||
ss,
|
||||
rtc,
|
||||
&clip,
|
||||
&bounds,
|
||||
&matrix,
|
||||
&shape,
|
||||
pathAAType,
|
||||
false};
|
||||
pr->drawPath(args);
|
||||
}
|
||||
|
||||
static void stencil_path(GrRecordingContext* context, GrRenderTargetContext* rtc,
|
||||
GrPathRenderer* pr, const GrFixedClip& clip, const SkMatrix& matrix,
|
||||
const GrStyledShape& shape, GrAA aa) {
|
||||
GrPathRenderer::StencilPathArgs args;
|
||||
args.fContext = context;
|
||||
args.fRenderTargetContext = rtc;
|
||||
args.fClip = &clip;
|
||||
args.fClipConservativeBounds = &clip.scissorRect();
|
||||
args.fViewMatrix = &matrix;
|
||||
args.fShape = &shape;
|
||||
args.fDoStencilMSAA = aa;
|
||||
|
||||
pr->stencilPath(args);
|
||||
}
|
||||
|
||||
static GrAA supported_aa(GrRenderTargetContext* rtc, GrAA aa) {
|
||||
// MIXED SAMPLES TODO: We can use stencil with mixed samples as well.
|
||||
return rtc->numSamples() > 1 ? aa : GrAA::kNo;
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
bool GrStencilMaskHelper::init(const SkIRect& bounds, uint32_t genID,
|
||||
const GrWindowRectangles& windowRects, int numFPs) {
|
||||
if (!fRTC->priv().mustRenderClip(genID, bounds, numFPs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fClip.setStencilClip(genID);
|
||||
fClip.fixedClip().setScissor(bounds);
|
||||
if (!windowRects.empty()) {
|
||||
fClip.fixedClip().setWindowRectangles(
|
||||
windowRects, GrWindowRectsState::Mode::kExclusive);
|
||||
}
|
||||
fNumFPs = numFPs;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrStencilMaskHelper::drawRect(const SkRect& rect,
|
||||
const SkMatrix& matrix,
|
||||
SkRegion::Op op,
|
||||
GrAA aa) {
|
||||
if (rect.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool drawDirectToClip;
|
||||
auto passes = get_stencil_passes(op, GrPathRenderer::kNoRestriction_StencilSupport, false,
|
||||
&drawDirectToClip);
|
||||
aa = supported_aa(fRTC, aa);
|
||||
|
||||
if (!drawDirectToClip) {
|
||||
// Draw to client stencil bits first
|
||||
draw_stencil_rect(fRTC, fClip.fixedClip(), &gDrawToStencil, matrix, rect, aa);
|
||||
}
|
||||
|
||||
// Now modify the clip bit (either by rendering directly), or by covering the bounding box
|
||||
// of the clip
|
||||
for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
|
||||
if (drawDirectToClip) {
|
||||
draw_stencil_rect(fRTC, fClip, *pass, matrix, rect, aa);
|
||||
} else {
|
||||
draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
|
||||
SkRect::Make(fClip.fixedClip().scissorRect()), aa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GrStencilMaskHelper::drawPath(const SkPath& path,
|
||||
const SkMatrix& matrix,
|
||||
SkRegion::Op op,
|
||||
GrAA aa) {
|
||||
if (path.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// drawPath follows a similar approach to drawRect(), where we either draw directly to the clip
|
||||
// bit or first draw to client bits and then apply a cover pass. The complicating factor is that
|
||||
// we rely on path rendering and how the chosen path renderer uses the stencil buffer.
|
||||
aa = supported_aa(fRTC, aa);
|
||||
|
||||
GrAAType pathAAType = aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone;
|
||||
|
||||
// This will be used to determine whether the clip shape can be rendered into the
|
||||
// stencil with arbitrary stencil settings.
|
||||
GrPathRenderer::StencilSupport stencilSupport;
|
||||
|
||||
// Make path canonical with regards to fill type (inverse handled by stencil settings).
|
||||
bool fillInverted = path.isInverseFillType();
|
||||
SkTCopyOnFirstWrite<SkPath> clipPath(path);
|
||||
if (fillInverted) {
|
||||
clipPath.writable()->toggleInverseFillType();
|
||||
}
|
||||
|
||||
GrStyledShape shape(*clipPath, GrStyle::SimpleFill());
|
||||
SkASSERT(!shape.inverseFilled());
|
||||
|
||||
GrPathRenderer::CanDrawPathArgs canDrawArgs;
|
||||
canDrawArgs.fCaps = fContext->priv().caps();
|
||||
canDrawArgs.fProxy = fRTC->asRenderTargetProxy();
|
||||
canDrawArgs.fClipConservativeBounds = &fClip.fixedClip().scissorRect();
|
||||
canDrawArgs.fViewMatrix = &matrix;
|
||||
canDrawArgs.fShape = &shape;
|
||||
canDrawArgs.fPaint = nullptr;
|
||||
canDrawArgs.fAAType = pathAAType;
|
||||
canDrawArgs.fHasUserStencilSettings = false;
|
||||
canDrawArgs.fTargetIsWrappedVkSecondaryCB = fRTC->wrapsVkSecondaryCB();
|
||||
|
||||
GrPathRenderer* pr = fContext->priv().drawingManager()->getPathRenderer(
|
||||
canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, &stencilSupport);
|
||||
if (!pr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool drawDirectToClip;
|
||||
auto passes = get_stencil_passes(op, stencilSupport, fillInverted, &drawDirectToClip);
|
||||
|
||||
// Write to client bits if necessary
|
||||
if (!drawDirectToClip) {
|
||||
if (stencilSupport == GrPathRenderer::kNoRestriction_StencilSupport) {
|
||||
draw_path(fContext, fRTC, pr, fClip.fixedClip(), fClip.fixedClip().scissorRect(),
|
||||
&gDrawToStencil, matrix, shape, aa);
|
||||
} else {
|
||||
stencil_path(fContext, fRTC, pr, fClip.fixedClip(), matrix, shape, aa);
|
||||
}
|
||||
}
|
||||
|
||||
// Now modify the clip bit (either by rendering directly), or by covering the bounding box
|
||||
// of the clip
|
||||
for (GrUserStencilSettings const* const* pass = passes; *pass; ++pass) {
|
||||
if (drawDirectToClip) {
|
||||
draw_path(fContext, fRTC, pr, fClip, fClip.fixedClip().scissorRect(),
|
||||
*pass, matrix, shape, aa);
|
||||
} else {
|
||||
draw_stencil_rect(fRTC, fClip, *pass, SkMatrix::I(),
|
||||
SkRect::Make(fClip.fixedClip().scissorRect()), aa);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrStencilMaskHelper::drawShape(const GrShape& shape,
|
||||
const SkMatrix& matrix,
|
||||
SkRegion::Op op,
|
||||
GrAA aa) {
|
||||
if (shape.isRect() && !shape.inverted()) {
|
||||
this->drawRect(shape.rect(), matrix, op, aa);
|
||||
return true;
|
||||
} else {
|
||||
SkPath p;
|
||||
shape.asPath(&p);
|
||||
return this->drawPath(p, matrix, op, aa);
|
||||
}
|
||||
}
|
||||
|
||||
void GrStencilMaskHelper::clear(bool insideStencil) {
|
||||
fRTC->priv().clearStencilClip(fClip.fixedClip(), insideStencil);
|
||||
}
|
||||
|
||||
void GrStencilMaskHelper::finish() {
|
||||
fRTC->priv().setLastClip(fClip.stencilStackID(), fClip.fixedClip().scissorRect(), fNumFPs);
|
||||
}
|
72
src/gpu/GrStencilMaskHelper.h
Normal file
72
src/gpu/GrStencilMaskHelper.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrStencilMaskHelper_DEFINED
|
||||
#define GrStencilMaskHelper_DEFINED
|
||||
|
||||
#include "include/core/SkRect.h"
|
||||
#include "include/private/GrTypesPriv.h"
|
||||
#include "src/gpu/GrStencilClip.h"
|
||||
#include "src/gpu/GrWindowRectsState.h"
|
||||
|
||||
class GrShape;
|
||||
class GrRecordingContext;
|
||||
class GrRenderTargetContext;
|
||||
class SkMatrix;
|
||||
class SkRRect;
|
||||
|
||||
/**
|
||||
* The GrStencilMaskHelper helps generate clip masks using the stencil buffer.
|
||||
* It is intended to be used as:
|
||||
*
|
||||
* GrStencilMaskHelper helper;
|
||||
* helper.init(...);
|
||||
*
|
||||
* draw one or more paths/rects specifying the required boolean ops
|
||||
*
|
||||
* helper.finish();
|
||||
*
|
||||
* The result of this process will be the mask stored in the clip bits of the stencil buffer.
|
||||
*/
|
||||
class GrStencilMaskHelper : SkNoncopyable {
|
||||
public:
|
||||
// Configure the helper to update the stencil mask within the given rectangle, respecting the
|
||||
// set window rectangles. It will use the provided context and render target to draw into, both
|
||||
// of which must outlive the helper.
|
||||
GrStencilMaskHelper(GrRecordingContext* context, GrRenderTargetContext* rtc)
|
||||
: fContext(context)
|
||||
, fRTC(rtc) {}
|
||||
|
||||
// Returns true if the stencil mask must be redrawn
|
||||
bool init(const SkIRect& maskBounds, uint32_t genID,
|
||||
const GrWindowRectangles& windowRects, int numFPs);
|
||||
|
||||
// Draw a single rect into the stencil clip using the specified op
|
||||
void drawRect(const SkRect& rect, const SkMatrix& matrix, SkRegion::Op, GrAA);
|
||||
|
||||
// Draw a single filled path into the stencil clip with the specified op
|
||||
bool drawPath(const SkPath& path, const SkMatrix& matrix, SkRegion::Op, GrAA);
|
||||
|
||||
// Draw a single shape into the stencil clip assuming a simple fill style, with the specified op
|
||||
bool drawShape(const GrShape& shape, const SkMatrix& matrix, SkRegion::Op, GrAA);
|
||||
|
||||
// Reset the stencil buffer's clip bit to in or out.
|
||||
void clear(bool insideStencil);
|
||||
|
||||
// Marks the last rendered stencil mask on the render target context
|
||||
void finish();
|
||||
|
||||
private:
|
||||
GrRecordingContext* fContext;
|
||||
GrRenderTargetContext* fRTC;
|
||||
GrStencilClip fClip;
|
||||
int fNumFPs;
|
||||
|
||||
typedef SkNoncopyable INHERITED;
|
||||
};
|
||||
|
||||
#endif // GrStencilMaskHelper_DEFINED
|
@ -220,241 +220,6 @@ void GrStencilSettings::Face::setDisabled() {
|
||||
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.
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(0 == SkRegion::kDifference_Op);
|
||||
static_assert(1 == SkRegion::kIntersect_Op);
|
||||
static_assert(2 == SkRegion::kUnion_Op);
|
||||
static_assert(3 == SkRegion::kXOR_Op);
|
||||
static_assert(4 == SkRegion::kReverseDifference_Op);
|
||||
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.
|
||||
};
|
||||
|
||||
static_assert(0 == SkRegion::kDifference_Op);
|
||||
static_assert(1 == SkRegion::kIntersect_Op);
|
||||
static_assert(2 == SkRegion::kUnion_Op);
|
||||
static_assert(3 == SkRegion::kXOR_Op);
|
||||
static_assert(4 == SkRegion::kReverseDifference_Op);
|
||||
static_assert(5 == SkRegion::kReplace_Op);
|
||||
|
||||
GrUserStencilSettings const* const* GrStencilSettings::GetClipPasses(SkRegion::Op op,
|
||||
bool canBeDirect,
|
||||
bool invertedFill,
|
||||
bool* drawDirectToClip) {
|
||||
SkASSERT((unsigned)op <= SkRegion::kLastOp);
|
||||
if (canBeDirect && !invertedFill) { // TODO: inverse fill + intersect op can be direct.
|
||||
GrUserStencilSettings const* const* directPass = gDirectDrawTable[op];
|
||||
if (directPass[0]) {
|
||||
*drawDirectToClip = true;
|
||||
return directPass;
|
||||
}
|
||||
}
|
||||
*drawDirectToClip = false;
|
||||
return gUserToClipTable[invertedFill][op];
|
||||
}
|
||||
|
||||
static constexpr GrUserStencilSettings gZeroStencilClipBit(
|
||||
GrUserStencilSettings::StaticInit<
|
||||
0x0000,
|
||||
@ -464,9 +229,18 @@ static constexpr GrUserStencilSettings gZeroStencilClipBit(
|
||||
GrUserStencilOp::kZeroClipBit,
|
||||
0x0000>()
|
||||
);
|
||||
static constexpr GrUserStencilSettings gSetStencilClipBit(
|
||||
GrUserStencilSettings::StaticInit<
|
||||
0x0000,
|
||||
GrUserStencilTest::kAlways,
|
||||
0xffff,
|
||||
GrUserStencilOp::kSetClipBit,
|
||||
GrUserStencilOp::kSetClipBit,
|
||||
0x0000>()
|
||||
);
|
||||
|
||||
const GrUserStencilSettings* GrStencilSettings::SetClipBitSettings(bool setToInside) {
|
||||
return setToInside ? &gReplaceClip : &gZeroStencilClipBit;
|
||||
return setToInside ? &gSetStencilClipBit : &gZeroStencilClipBit;
|
||||
}
|
||||
|
||||
void GrStencilSettings::genKey(GrProcessorKeyBuilder* b, bool includeRefs) const {
|
||||
|
@ -98,34 +98,6 @@ public:
|
||||
return (kTopLeft_GrSurfaceOrigin == origin) ? fCCWFace : fCWFace;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/** Gets the user stencil settings to directly set the clip bit. */
|
||||
static const GrUserStencilSettings* SetClipBitSettings(bool setToInside);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user