Reland "Refactor stencil clip mask generation into helper"

This reverts commit de228e53fe.

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 commit 8b3a8a5238.
>
> 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:
Michael Ludwig 2020-05-12 13:15:35 -04:00 committed by Skia Commit-Bot
parent f497c36625
commit 828d341199
9 changed files with 575 additions and 419 deletions

View File

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

View File

@ -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",

View File

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

View File

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

View File

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

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

View 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

View File

@ -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 {

View File

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