aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Chris Dalton <csmartdalton@google.com>2018-03-30 15:59:38 +0000
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-03-30 17:03:16 +0000
commitc534808ba21e61507fe3ebf9f23d230a71e5ada7 (patch)
tree3ff1d39feb8fe4c464950178c3bc1ecfaf403b11
parent7a002c36ce3faed3b3201453770b8f18eecc8f2f (diff)
Revert "Delete GPU alpha clip masks"
This reverts commit a466228a616b1b02ede2d4389fefcfc839a54a25. Reason for revert: Turned up a vulkan driver bug Original change's description: > Delete GPU alpha clip masks > > The cost of switching render targets on each draw to make a custom > clip is enormous. There are virtually no circumstances where this will > outperform our cached, multi-threaded software mask generator. The > tried-and-true approach to clipping on-GPU is with analytic FPs. And > now that we support CCPR clip FPs, there ulitmately should be very few > clip stacks that even require a mask as long as they don't use > deprecated SkClipOps. > > Bug: skia: > Change-Id: I79c5558c93c1b99179f1e933d029f69b14ad1ce3 > Reviewed-on: https://skia-review.googlesource.com/116724 > Reviewed-by: Brian Osman <brianosman@google.com> > Commit-Queue: Chris Dalton <csmartdalton@google.com> TBR=robertphillips@google.com,brianosman@google.com,csmartdalton@google.com # Not skipping CQ checks because original CL landed > 1 day ago. Bug: skia: Change-Id: Iba289e00ba2eca7084dc8517491cfb5f6ab6266f Reviewed-on: https://skia-review.googlesource.com/117420 Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
-rw-r--r--gm/windowrectangles.cpp45
-rw-r--r--src/gpu/GrClipStackClip.cpp168
-rw-r--r--src/gpu/GrClipStackClip.h21
-rw-r--r--src/gpu/GrReducedClip.cpp128
-rw-r--r--src/gpu/GrReducedClip.h1
5 files changed, 347 insertions, 16 deletions
diff --git a/gm/windowrectangles.cpp b/gm/windowrectangles.cpp
index 8b85554af8..931a154df4 100644
--- a/gm/windowrectangles.cpp
+++ b/gm/windowrectangles.cpp
@@ -107,8 +107,8 @@ DEF_GM( return new WindowRectanglesGM(); )
constexpr static int kNumWindows = 8;
/**
- * Visualizes the stencil mask for a clip with several window rectangles. The purpose of this test
- * is to verify that window rectangles are being used during clip mask generation, and to
+ * Visualizes the mask (alpha or stencil) for a clip with several window rectangles. The purpose of
+ * this test is to verify that window rectangles are being used during clip mask generation, and to
* visualize where the window rectangles are placed.
*
* We use window rectangles when generating the clip mask because there is no need to invest time
@@ -123,6 +123,7 @@ private:
constexpr static int kMaskCheckerSize = 5;
SkString onShortName() final { return SkString("windowrectangles_mask"); }
void onCoverClipStack(const SkClipStack&, SkCanvas*) final;
+ void visualizeAlphaMask(GrContext*, GrRenderTargetContext*, const GrReducedClip&, GrPaint&&);
void visualizeStencilMask(GrContext*, GrRenderTargetContext*, const GrReducedClip&, GrPaint&&);
void stencilCheckerboard(GrRenderTargetContext*, bool flip);
void fail(SkCanvas*);
@@ -183,14 +184,48 @@ void WindowRectanglesMaskGM::onCoverClipStack(const SkClipStack& stack, SkCanvas
const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), rtc->caps()->shaderCaps(),
kNumWindows);
- GrPaint paint;
+ GrPaint paint;
if (GrFSAAType::kNone == rtc->fsaaType()) {
- // Use different colors so we don't confuse masks that don't have AA with ones that should.
paint.setColor4f(GrColor4f(0, 0.25f, 1, 1));
+ this->visualizeAlphaMask(ctx, rtc, reducedClip, std::move(paint));
} else {
paint.setColor4f(GrColor4f(1, 0.25f, 0.25f, 1));
+ this->visualizeStencilMask(ctx, rtc, reducedClip, std::move(paint));
}
- this->visualizeStencilMask(ctx, rtc, reducedClip, std::move(paint));
+}
+
+void WindowRectanglesMaskGM::visualizeAlphaMask(GrContext* ctx, GrRenderTargetContext* rtc,
+ const GrReducedClip& reducedClip, GrPaint&& paint) {
+ const int padRight = (kDeviceRect.right() - kCoverRect.right()) / 2;
+ const int padBottom = (kDeviceRect.bottom() - kCoverRect.bottom()) / 2;
+ sk_sp<GrRenderTargetContext> maskRTC(
+ ctx->contextPriv().makeDeferredRenderTargetContextWithFallback(
+ SkBackingFit::kExact,
+ kCoverRect.width() + padRight,
+ kCoverRect.height() + padBottom,
+ kAlpha_8_GrPixelConfig, nullptr));
+ if (!maskRTC) {
+ return;
+ }
+
+ // Draw a checker pattern into the alpha mask so we can visualize the regions left untouched by
+ // the clip mask generation.
+ this->stencilCheckerboard(maskRTC.get(), true);
+ maskRTC->clear(nullptr, GrColorPackA4(0xff), GrRenderTargetContext::CanClearFullscreen::kYes);
+ maskRTC->priv().drawAndStencilRect(make_stencil_only_clip(), &GrUserStencilSettings::kUnused,
+ SkRegion::kDifference_Op, false, GrAA::kNo, SkMatrix::I(),
+ SkRect::MakeIWH(maskRTC->width(), maskRTC->height()));
+ reducedClip.drawAlphaClipMask(maskRTC.get());
+
+ int x = kCoverRect.x() - kDeviceRect.x(),
+ y = kCoverRect.y() - kDeviceRect.y();
+
+ // Now visualize the alpha mask by drawing a rect over the area where it is defined. The regions
+ // inside window rectangles or outside the scissor should still have the initial checkerboard
+ // intact. (This verifies we didn't spend any time modifying those pixels in the mask.)
+ AlphaOnlyClip clip(maskRTC->asTextureProxyRef(), x, y);
+ rtc->drawRect(clip, std::move(paint), GrAA::kYes, SkMatrix::I(),
+ SkRect::Make(SkIRect::MakeXYWH(x, y, maskRTC->width(), maskRTC->height())));
}
void WindowRectanglesMaskGM::visualizeStencilMask(GrContext* ctx, GrRenderTargetContext* rtc,
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index ed4a098c8d..fe8d10095c 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -73,6 +73,111 @@ void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devR
devBounds.roundOut(devResult);
}
+////////////////////////////////////////////////////////////////////////////////
+// set up the draw state to enable the aa clipping mask.
+static std::unique_ptr<GrFragmentProcessor> create_fp_for_mask(sk_sp<GrTextureProxy> mask,
+ const SkIRect& devBound) {
+ SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
+ return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(mask), domainTexels,
+ {devBound.fLeft, devBound.fTop});
+}
+
+// Does the path in 'element' require SW rendering? If so, return true (and,
+// 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 GrClipStackClip::PathNeedsSWRenderer(GrContext* context,
+ const SkIRect& scissorRect,
+ bool hasUserStencilSettings,
+ const GrRenderTargetContext* renderTargetContext,
+ const SkMatrix& viewMatrix,
+ const Element* element,
+ GrPathRenderer** prOut,
+ bool needsStencil) {
+ if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
+ // rects can always be drawn directly w/o using the software path
+ // TODO: skip rrects once we're drawing them directly.
+ if (prOut) {
+ *prOut = nullptr;
+ }
+ return false;
+ } else {
+ // We shouldn't get here with an empty clip element.
+ SkASSERT(Element::DeviceSpaceType::kEmpty != element->getDeviceSpaceType());
+
+ // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
+ SkPath path;
+ element->asDeviceSpacePath(&path);
+ if (path.isInverseFillType()) {
+ path.toggleInverseFillType();
+ }
+
+ GrPathRendererChain::DrawType type =
+ needsStencil ? GrPathRendererChain::DrawType::kStencilAndColor
+ : GrPathRendererChain::DrawType::kColor;
+
+ GrShape shape(path, GrStyle::SimpleFill());
+ GrPathRenderer::CanDrawPathArgs canDrawArgs;
+ canDrawArgs.fCaps = context->caps();
+ canDrawArgs.fClipConservativeBounds = &scissorRect;
+ canDrawArgs.fViewMatrix = &viewMatrix;
+ canDrawArgs.fShape = &shape;
+ canDrawArgs.fAAType = GrChooseAAType(GrAA(element->isAA()),
+ renderTargetContext->fsaaType(),
+ GrAllowMixedSamples::kYes,
+ *context->caps());
+ canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
+
+ // the 'false' parameter disallows use of the SW path renderer
+ GrPathRenderer* pr =
+ context->contextPriv().drawingManager()->getPathRenderer(canDrawArgs, false, type);
+ if (prOut) {
+ *prOut = pr;
+ }
+ return SkToBool(!pr);
+ }
+}
+
+/*
+ * This method traverses the clip stack to see if the GrSoftwarePathRenderer
+ * will be used on any element. If so, it returns true to indicate that the
+ * entire clip should be rendered in SW and then uploaded en masse to the gpu.
+ */
+bool GrClipStackClip::UseSWOnlyPath(GrContext* context,
+ bool hasUserStencilSettings,
+ const GrRenderTargetContext* renderTargetContext,
+ const GrReducedClip& reducedClip) {
+ // TODO: generalize this function so that when
+ // a clip gets complex enough it can just be done in SW regardless
+ // of whether it would invoke the GrSoftwarePathRenderer.
+
+ // If we're avoiding stencils, always use SW:
+ if (context->caps()->avoidStencilBuffers())
+ return true;
+
+ // Set the matrix so that rendered clip elements are transformed to mask space from clip
+ // space.
+ SkMatrix translate;
+ translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top()));
+
+ for (ElementList::Iter iter(reducedClip.maskElements()); iter.get(); iter.next()) {
+ const Element* element = iter.get();
+
+ SkClipOp op = element->getOp();
+ bool invert = element->isInverseFilled();
+ bool needsStencil = invert ||
+ kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op;
+
+ if (PathNeedsSWRenderer(context, reducedClip.scissor(), hasUserStencilSettings,
+ renderTargetContext, translate, element, nullptr, needsStencil)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// sort out what kind of clip mask needs to be created: alpha, stencil,
+// scissor, or entirely software
bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTargetContext,
bool useHWAA, bool hasUserStencilSettings, GrAppliedClip* out,
SkRect* bounds) const {
@@ -148,18 +253,24 @@ bool GrClipStackClip::applyClipMask(GrContext* context, GrRenderTargetContext* r
// If the stencil buffer is multisampled we can use it to do everything.
if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.maskRequiresAA()) ||
context->caps()->avoidStencilBuffers()) {
- if (auto mask = this->createSoftwareClipMask(context, reducedClip, renderTargetContext)) {
- // The mask should fill the clip's scissor.
- SkIRect domainTexels = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
- SkIPoint maskOffset = SkIPoint::Make(reducedClip.left(), reducedClip.top());
- out->addCoverageFP(GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(mask),
- domainTexels,
- maskOffset));
+ sk_sp<GrTextureProxy> result;
+ if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) {
+ // The clip geometry is complex enough that it will be more efficient to create it
+ // entirely in software
+ result = this->createSoftwareClipMask(context, reducedClip, renderTargetContext);
+ } else {
+ result = this->createAlphaClipMask(context, reducedClip);
+ }
+
+ if (result) {
+ // The mask's top left coord should be pinned to the rounded-out top left corner of
+ // the clip's device space bounds.
+ out->addCoverageFP(create_fp_for_mask(std::move(result), reducedClip.scissor()));
return true;
}
- // If software clip mask creation fails, fall through to the stencil code paths, unless
- // stencils are disallowed.
+ // If alpha or software clip mask creation fails, fall through to the stencil code paths,
+ // unless stencils are disallowed.
if (context->caps()->avoidStencilBuffers()) {
SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. "
"Clip will be ignored.\n");
@@ -213,6 +324,45 @@ static void add_invalidate_on_pop_message(const SkClipStack& stack, uint32_t cli
SkDEBUGFAIL("Gen ID was not found in stack.");
}
+sk_sp<GrTextureProxy> GrClipStackClip::createAlphaClipMask(GrContext* context,
+ const GrReducedClip& reducedClip) const {
+ GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
+ GrUniqueKey key;
+ create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(),
+ reducedClip.numAnalyticFPs(), &key);
+
+ sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey(
+ key, kBottomLeft_GrSurfaceOrigin));
+ if (proxy) {
+ return proxy;
+ }
+
+ sk_sp<GrRenderTargetContext> rtc(
+ context->contextPriv().makeDeferredRenderTargetContextWithFallback(SkBackingFit::kApprox,
+ reducedClip.width(),
+ reducedClip.height(),
+ kAlpha_8_GrPixelConfig,
+ nullptr));
+ if (!rtc) {
+ return nullptr;
+ }
+
+ if (!reducedClip.drawAlphaClipMask(rtc.get())) {
+ return nullptr;
+ }
+
+ sk_sp<GrTextureProxy> result(rtc->asTextureProxyRef());
+ if (!result) {
+ return nullptr;
+ }
+
+ SkASSERT(result->origin() == kBottomLeft_GrSurfaceOrigin);
+ proxyProvider->assignUniqueKeyToProxy(key, result.get());
+ add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
+
+ return result;
+}
+
namespace {
/**
diff --git a/src/gpu/GrClipStackClip.h b/src/gpu/GrClipStackClip.h
index 05d9f18c88..aeb983468c 100644
--- a/src/gpu/GrClipStackClip.h
+++ b/src/gpu/GrClipStackClip.h
@@ -37,14 +37,31 @@ public:
static const char kMaskTestTag[];
private:
+ static bool PathNeedsSWRenderer(GrContext* context,
+ const SkIRect& scissorRect,
+ bool hasUserStencilSettings,
+ const GrRenderTargetContext*,
+ const SkMatrix& viewMatrix,
+ const SkClipStack::Element* element,
+ GrPathRenderer** prOut,
+ bool needsStencil);
+
bool applyClipMask(GrContext*, GrRenderTargetContext*, const GrReducedClip&,
bool hasUserStencilSettings, GrAppliedClip*) const;
- // Creates an alpha mask of the remaining reduced clip elements that could not be handled
- // analytically on the GPU. The mask fills the reduced clip's scissor rect.
+ // Creates an alpha mask of the clip. The mask is a rasterization of elements through the
+ // rect specified by clipSpaceIBounds.
+ sk_sp<GrTextureProxy> createAlphaClipMask(GrContext*, const GrReducedClip&) const;
+
+ // Similar to createAlphaClipMask but it rasterizes in SW and uploads to the result texture.
sk_sp<GrTextureProxy> createSoftwareClipMask(GrContext*, const GrReducedClip&,
GrRenderTargetContext*) const;
+ static bool UseSWOnlyPath(GrContext*,
+ bool hasUserStencilSettings,
+ const GrRenderTargetContext*,
+ const GrReducedClip&);
+
const SkClipStack* fStack;
};
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 2f60dad28f..9d27f7696e 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -673,6 +673,134 @@ void GrReducedClip::makeEmpty() {
}
////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+
+static bool stencil_element(GrRenderTargetContext* rtc,
+ const GrFixedClip& clip,
+ const GrUserStencilSettings* ss,
+ const SkMatrix& viewMatrix,
+ const SkClipStack::Element* element) {
+ GrAA aa = GrAA(element->isAA());
+ switch (element->getDeviceSpaceType()) {
+ case SkClipStack::Element::DeviceSpaceType::kEmpty:
+ SkDEBUGFAIL("Should never get here with an empty element.");
+ break;
+ case SkClipStack::Element::DeviceSpaceType::kRect:
+ return rtc->priv().drawAndStencilRect(clip, ss, (SkRegion::Op)element->getOp(),
+ element->isInverseFilled(), aa, viewMatrix,
+ element->getDeviceSpaceRect());
+ break;
+ default: {
+ SkPath path;
+ element->asDeviceSpacePath(&path);
+ if (path.isInverseFillType()) {
+ path.toggleInverseFillType();
+ }
+
+ return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
+ element->isInverseFilled(), aa, viewMatrix, path);
+ break;
+ }
+ }
+
+ return false;
+}
+
+static void draw_element(GrRenderTargetContext* rtc,
+ const GrClip& clip, // TODO: can this just always be WideOpen?
+ GrPaint&& paint,
+ GrAA aa,
+ const SkMatrix& viewMatrix,
+ const SkClipStack::Element* element) {
+ // TODO: Draw rrects directly here.
+ switch (element->getDeviceSpaceType()) {
+ case SkClipStack::Element::DeviceSpaceType::kEmpty:
+ SkDEBUGFAIL("Should never get here with an empty element.");
+ break;
+ case SkClipStack::Element::DeviceSpaceType::kRect:
+ rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
+ break;
+ default: {
+ SkPath path;
+ element->asDeviceSpacePath(&path);
+ if (path.isInverseFillType()) {
+ path.toggleInverseFillType();
+ }
+
+ rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
+ break;
+ }
+ }
+}
+
+bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
+ // The texture may be larger than necessary, this rect represents the part of the texture
+ // we populate with a rasterization of the clip.
+ GrFixedClip clip(SkIRect::MakeWH(fScissor.width(), fScissor.height()));
+
+ if (!fWindowRects.empty()) {
+ clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
+ GrWindowRectsState::Mode::kExclusive);
+ }
+
+ // The scratch texture that we are drawing into can be substantially larger than the mask. Only
+ // clear the part that we care about.
+ GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0;
+ rtc->priv().clear(clip, initialCoverage, GrRenderTargetContext::CanClearFullscreen::kYes);
+
+ // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
+ SkMatrix translate;
+ translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
+
+ // walk through each clip element and perform its set op
+ for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
+ const Element* element = iter.get();
+ SkRegion::Op op = (SkRegion::Op)element->getOp();
+ GrAA aa = GrAA(element->isAA());
+ bool invert = element->isInverseFilled();
+ if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
+ // draw directly into the result with the stencil set to make the pixels affected
+ // by the clip shape be non-zero.
+ static constexpr GrUserStencilSettings kStencilInElement(
+ GrUserStencilSettings::StaticInit<
+ 0xffff,
+ GrUserStencilTest::kAlways,
+ 0xffff,
+ GrUserStencilOp::kReplace,
+ GrUserStencilOp::kReplace,
+ 0xffff>()
+ );
+ if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
+ return false;
+ }
+
+ // Draw to the exterior pixels (those with a zero stencil value).
+ static constexpr GrUserStencilSettings kDrawOutsideElement(
+ GrUserStencilSettings::StaticInit<
+ 0x0000,
+ GrUserStencilTest::kEqual,
+ 0xffff,
+ GrUserStencilOp::kZero,
+ GrUserStencilOp::kZero,
+ 0xffff>()
+ );
+ if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo,
+ translate, SkRect::Make(fScissor))) {
+ return false;
+ }
+ } else {
+ // all the remaining ops can just be directly draw into the accumulation buffer
+ GrPaint paint;
+ paint.setCoverageSetOpXPFactory(op, false);
+
+ draw_element(rtc, clip, std::move(paint), aa, translate, element);
+ }
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
// Create a 1-bit clip mask in the stencil buffer.
bool GrReducedClip::drawStencilClipMask(GrContext* context,
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
index 5447652137..c3838d8802 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -83,6 +83,7 @@ public:
*/
bool maskRequiresAA() const { SkASSERT(!fMaskElements.isEmpty()); return fMaskRequiresAA; }
+ bool drawAlphaClipMask(GrRenderTargetContext*) const;
bool drawStencilClipMask(GrContext*, GrRenderTargetContext*) const;
int numAnalyticFPs() const { return fAnalyticFPs.count() + fCCPRClipPaths.count(); }