Improve usage of window rectangles
* Skips non-AA diff rect elements and replaces them with window
rectangles.
* Places window rectangles in the interiors of antialiased diff rects.
* Arranges two overlapping window rectangles in a plus shape inside of
diff rounded rects.
* Enables window rectangles when clearing and generating clip masks.
GTX 960 perf result (with vs. without window rectangles):
glinst4 msaa16 gpu
keymobi_pinterest.skp 0.48 -> 0.17 [ 35%] 2.77 -> 1.49 [ 54%] 0.22 -> 0.16 [ 70%]
keymobi_digg_com.skp 0.42 -> 0.23 [ 55%] 2.34 -> 1.08 [ 46%] 0.25 -> 0.21 [ 83%]
desk_jsfiddlebigcar.skp 0.28 -> 0.16 [ 59%] 1.70 -> 0.96 [ 57%] 0.19 -> 0.14 [ 70%]
top25desk_wordpress.skp 0.45 -> 0.18 [ 40%] 2.78 -> 1.53 [ 55%] 0.21 -> 0.19 [ 94%]
top25desk_weather_com.skp 2.01 -> 1.93 [ 96%] 23.5 -> 2.54 [ 11%] 1.90 -> 1.68 [ 88%]
keymobi_blogger.skp 0.57 -> 0.37 [ 65%] 2.87 -> 1.54 [ 54%] 0.43 -> 0.33 [ 77%]
keymobi_linkedin.skp 0.32 -> 0.17 [ 51%] 1.93 -> 1.04 [ 54%] 0.17 -> 0.15 [ 91%]
keymobi_bing_com_search_... 0.29 -> 0.25 [ 83%] 1.85 -> 1.23 [ 66%] 0.50 -> 0.24 [ 48%]
keymobi_theverge_com_201... 1.00 -> 0.67 [ 68%] 9.46 -> 3.84 [ 41%] 0.72 -> 0.65 [ 90%]
keymobi_sfgate_com_.skp 1.56 -> 1.13 [ 72%] 4.49 -> 2.86 [ 64%] 1.54 -> 1.11 [ 72%]
...
GEOMEAN (All 79 blink skps) 1.04 -> 0.90 [ 86%] 4.22 -> 2.81 [ 67%] 0.95 -> 0.89 [ 94%]
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2289363005
Committed: https://skia.googlesource.com/skia/+/db42be9a326c747ff92ed1da8c3536c5b3e8e22b
Review-Url: https://codereview.chromium.org/2289363005
diff --git a/gm/windowrectangles.cpp b/gm/windowrectangles.cpp
new file mode 100644
index 0000000..b4ce7c2
--- /dev/null
+++ b/gm/windowrectangles.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkClipStack.h"
+#include "SkRRect.h"
+
+#if SK_SUPPORT_GPU
+# include "GrAppliedClip.h"
+# include "GrDrawContext.h"
+# include "GrDrawContextPriv.h"
+# include "GrFixedClip.h"
+# include "GrReducedClip.h"
+# include "GrRenderTargetPriv.h"
+# include "GrResourceProvider.h"
+# include "effects/GrTextureDomain.h"
+#endif
+
+constexpr static SkIRect kDeviceRect = {0, 0, 600, 600};
+constexpr static SkIRect kLayerRect = {25, 25, 575, 575};
+constexpr static SkIRect kCoverRect = {50, 50, 550, 550};
+constexpr static int kNumWindows = 8;
+
+namespace skiagm {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class WindowRectanglesBaseGM : public GM {
+protected:
+ virtual void onCoverClipStack(const SkClipStack&, SkCanvas*) = 0;
+
+private:
+ SkISize onISize() override { return SkISize::Make(kDeviceRect.width(), kDeviceRect.height()); }
+ void onDraw(SkCanvas*) final;
+};
+
+void WindowRectanglesBaseGM::onDraw(SkCanvas* canvas) {
+ sk_tool_utils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6, 25);
+ canvas->saveLayer(SkRect::Make(kLayerRect), nullptr);
+
+ SkClipStack stack;
+ stack.clipDevRect(SkRect::MakeXYWH(370.75, 80.25, 149, 100), SkRegion::kDifference_Op, false);
+ stack.clipDevRect(SkRect::MakeXYWH(80.25, 420.75, 150, 100), SkRegion::kDifference_Op, true);
+ stack.clipDevRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(200, 200, 200, 200), 60, 45),
+ SkRegion::kDifference_Op, true);
+
+ SkRRect nine;
+ nine.setNinePatch(SkRect::MakeXYWH(550 - 30.25 - 100, 370.75, 100, 150), 12, 35, 23, 20);
+ stack.clipDevRRect(nine, SkRegion::kDifference_Op, true);
+
+ SkRRect complx;
+ SkVector complxRadii[4] = {{6, 4}, {8, 12}, {16, 24}, {48, 32}};
+ complx.setRectRadii(SkRect::MakeXYWH(80.25, 80.75, 100, 149), complxRadii);
+ stack.clipDevRRect(complx, SkRegion::kDifference_Op, false);
+
+ this->onCoverClipStack(stack, canvas);
+
+ canvas->restore();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Draws a clip that will exercise window rectangles if they are supported.
+ */
+class WindowRectanglesGM : public WindowRectanglesBaseGM {
+private:
+ SkString onShortName() final { return SkString("windowrectangles"); }
+ void onCoverClipStack(const SkClipStack&, SkCanvas*) final;
+};
+
+/**
+ * This is a simple helper class for resetting a canvas's clip to our test’s SkClipStack.
+ */
+class ReplayClipStackVisitor final : public SkCanvasClipVisitor {
+public:
+ typedef SkRegion::Op Op;
+ ReplayClipStackVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
+ void clipRect(const SkRect& r, Op op, bool aa) override { fCanvas->clipRect(r, op, aa); }
+ void clipRRect(const SkRRect& rr, Op op, bool aa) override { fCanvas->clipRRect(rr, op, aa); }
+ void clipPath(const SkPath&, Op, bool) override { SkFAIL("Not implemented"); }
+private:
+ SkCanvas* const fCanvas;
+};
+
+void WindowRectanglesGM::onCoverClipStack(const SkClipStack& stack, SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setColor(0xff00aa80);
+
+ // Set up the canvas's clip to match our SkClipStack.
+ ReplayClipStackVisitor visitor(canvas);
+ SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+ for (const SkClipStack::Element* element = iter.next(); element; element = iter.next()) {
+ element->replay(&visitor);
+ }
+
+ canvas->drawRect(SkRect::Make(kCoverRect), paint);
+}
+
+DEF_GM( return new WindowRectanglesGM(); )
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+/**
+ * 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
+ * defining those regions where window rectangles will be in effect during the actual draw anyway.
+ *
+ * This test works by filling the entire clip mask with a small checkerboard pattern before drawing
+ * it, and then covering the mask with a solid color once it has been generated. The regions inside
+ * window rectangles or outside the scissor should still have the initial checkerboard intact.
+ */
+class WindowRectanglesMaskGM : public WindowRectanglesBaseGM {
+private:
+ constexpr static int kMaskCheckerSize = 5;
+ SkString onShortName() final { return SkString("windowrectangles_mask"); }
+ void onCoverClipStack(const SkClipStack&, SkCanvas*) final;
+ void visualizeAlphaMask(GrContext*, GrDrawContext*, const GrReducedClip&, const GrPaint&);
+ void visualizeStencilMask(GrContext*, GrDrawContext*, const GrReducedClip&, const GrPaint&);
+ void stencilCheckerboard(GrDrawContext*, bool flip);
+ void fail(SkCanvas*);
+};
+
+/**
+ * Base class for GrClips that visualize a clip mask.
+ */
+class MaskOnlyClipBase : public GrClip {
+private:
+ bool quickContains(const SkRect&) const final { return false; }
+ bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final { return false; }
+ void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final {
+ rect->set(0, 0, width, height);
+ if (iior) {
+ *iior = false;
+ }
+ }
+};
+
+/**
+ * This class clips a cover by an alpha mask. We use it to visualize the alpha clip mask.
+ */
+class AlphaOnlyClip final : public MaskOnlyClipBase {
+public:
+ AlphaOnlyClip(GrTexture* mask, int x, int y) {
+ int w = mask->width(), h = mask->height();
+ SkMatrix mat = SkMatrix::MakeScale(1.f / SkIntToScalar(w), 1.f / SkIntToScalar(h));
+ mat.preTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+ fFP = GrTextureDomainEffect::Make(
+ mask, nullptr, mat,
+ GrTextureDomain::MakeTexelDomain(mask, SkIRect::MakeWH(w, h)),
+ GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode,
+ kDevice_GrCoordSet);
+
+ }
+private:
+ bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const override {
+ out->addCoverageFP(fFP);
+ return true;
+ }
+ sk_sp<GrFragmentProcessor> fFP;
+};
+
+/**
+ * This class clips a cover by the stencil clip bit. We use it to visualize the stencil mask.
+ */
+class StencilOnlyClip final : public MaskOnlyClipBase {
+private:
+ bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const override {
+ out->addStencilClip();
+ return true;
+ }
+};
+
+void WindowRectanglesMaskGM::onCoverClipStack(const SkClipStack& stack, SkCanvas* canvas) {
+ GrContext* ctx = canvas->getGrContext();
+ GrDrawContext* dc = canvas->internal_private_accessTopLayerDrawContext();
+
+ if (!ctx || !dc ||
+ dc->accessRenderTarget()->renderTargetPriv().maxWindowRectangles() < kNumWindows) {
+ this->fail(canvas);
+ return;
+ }
+
+ const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), kNumWindows);
+
+ GrPaint paint;
+ paint.setAntiAlias(true);
+ if (!dc->isStencilBufferMultisampled()) {
+ paint.setColor4f(GrColor4f(0, 0.25f, 1, 1));
+ this->visualizeAlphaMask(ctx, dc, reducedClip, paint);
+ } else {
+ paint.setColor4f(GrColor4f(1, 0.25f, 0.25f, 1));
+ this->visualizeStencilMask(ctx, dc, reducedClip, paint);
+ }
+}
+
+void WindowRectanglesMaskGM::visualizeAlphaMask(GrContext* ctx, GrDrawContext* dc,
+ const GrReducedClip& reducedClip,
+ const GrPaint& paint) {
+ sk_sp<GrDrawContext> maskDC(
+ ctx->makeDrawContextWithFallback(SkBackingFit::kExact, kLayerRect.width(),
+ kLayerRect.height(), kAlpha_8_GrPixelConfig, nullptr));
+ if (!maskDC ||
+ !ctx->resourceProvider()->attachStencilAttachment(maskDC->accessRenderTarget())) {
+ return;
+ }
+
+ // Draw a checker pattern into the alpha mask so we can visualize the regions left untouched by
+ // the clip mask generation.
+ this->stencilCheckerboard(maskDC.get(), true);
+ maskDC->clear(nullptr, GrColorPackA4(0xff), true);
+ maskDC->drawContextPriv().drawAndStencilRect(StencilOnlyClip(), &GrUserStencilSettings::kUnused,
+ SkRegion::kDifference_Op, false, false,
+ SkMatrix::I(),
+ SkRect::MakeIWH(maskDC->width(), maskDC->height()));
+ reducedClip.drawAlphaClipMask(maskDC.get());
+ sk_sp<GrTexture> mask(maskDC->asTexture());
+
+ int x = kCoverRect.x() - kLayerRect.x(),
+ y = kCoverRect.y() - kLayerRect.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(mask.get(), x, y);
+ dc->drawRect(clip, paint, SkMatrix::I(),
+ SkRect::Make(SkIRect::MakeXYWH(x, y, mask->width(), mask->height())));
+}
+
+void WindowRectanglesMaskGM::visualizeStencilMask(GrContext* ctx, GrDrawContext* dc,
+ const GrReducedClip& reducedClip,
+ const GrPaint& paint) {
+ if (!ctx->resourceProvider()->attachStencilAttachment(dc->accessRenderTarget())) {
+ return;
+ }
+
+ // Draw a checker pattern into the stencil buffer so we can visualize the regions left untouched
+ // by the clip mask generation.
+ this->stencilCheckerboard(dc, false);
+ reducedClip.drawStencilClipMask(ctx, dc, {kLayerRect.x(), kLayerRect.y()});
+
+ // Now visualize the stencil mask by covering the entire render target. The regions inside
+ // window rectangless 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.)
+ dc->drawPaint(StencilOnlyClip(), paint, SkMatrix::I());
+}
+
+void WindowRectanglesMaskGM::stencilCheckerboard(GrDrawContext* dc, bool flip) {
+ constexpr static GrUserStencilSettings kSetClip(
+ GrUserStencilSettings::StaticInit<
+ 0,
+ GrUserStencilTest::kAlways,
+ 0,
+ GrUserStencilOp::kSetClipBit,
+ GrUserStencilOp::kKeep,
+ 0>()
+ );
+
+ dc->drawContextPriv().clearStencilClip(GrFixedClip::Disabled(), false);
+
+ for (int y = 0; y < kLayerRect.height(); y += kMaskCheckerSize) {
+ for (int x = (y & 1) == flip ? 0 : kMaskCheckerSize;
+ x < kLayerRect.width(); x += 2 * kMaskCheckerSize) {
+ SkIRect checker = SkIRect::MakeXYWH(x, y, kMaskCheckerSize, kMaskCheckerSize);
+ dc->drawContextPriv().stencilRect(GrNoClip(), &kSetClip, false, SkMatrix::I(),
+ SkRect::Make(checker));
+ }
+ }
+}
+
+void WindowRectanglesMaskGM::fail(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextAlign(SkPaint::kCenter_Align);
+ paint.setTextSize(20);
+ sk_tool_utils::set_portable_typeface(&paint);
+
+ SkString errorMsg;
+ errorMsg.printf("Requires GPU with %i window rectangles", kNumWindows);
+
+ canvas->clipRect(SkRect::Make(kCoverRect));
+ canvas->clear(SK_ColorWHITE);
+ canvas->drawText(errorMsg.c_str(), errorMsg.size(), SkIntToScalar(kCoverRect.centerX()),
+ SkIntToScalar(kCoverRect.centerY() - 10), paint);
+}
+
+DEF_GM( return new WindowRectanglesMaskGM(); )
+
+#endif
+
+}
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index d1a6952..33142f2 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -170,6 +170,7 @@
'<(skia_src_path)/gpu/GrResourceHandle.h',
'<(skia_src_path)/gpu/GrResourceProvider.cpp',
'<(skia_src_path)/gpu/GrResourceProvider.h',
+ '<(skia_src_path)/gpu/GrScissorState.h',
'<(skia_src_path)/gpu/GrShape.cpp',
'<(skia_src_path)/gpu/GrShape.h',
'<(skia_src_path)/gpu/GrStencilAttachment.cpp',
@@ -204,6 +205,7 @@
'<(skia_src_path)/gpu/GrTRecorder.h',
'<(skia_src_path)/gpu/GrUserStencilSettings.h',
'<(skia_src_path)/gpu/GrWindowRectangles.h',
+ '<(skia_src_path)/gpu/GrWindowRectsState.h',
'<(skia_src_path)/gpu/GrXferProcessor.cpp',
'<(skia_src_path)/gpu/GrYUVProvider.cpp',
'<(skia_src_path)/gpu/GrYUVProvider.h',
diff --git a/include/gpu/GrTypesPriv.h b/include/gpu/GrTypesPriv.h
index 8c42d64..d60fab1 100644
--- a/include/gpu/GrTypesPriv.h
+++ b/include/gpu/GrTypesPriv.h
@@ -9,7 +9,6 @@
#define GrTypesPriv_DEFINED
#include "GrTypes.h"
-#include "SkRect.h"
#include "SkRefCnt.h"
/**
@@ -420,32 +419,6 @@
kRW_GrIOType
};
-struct GrScissorState {
- GrScissorState() : fEnabled(false) {}
- GrScissorState(const SkIRect& rect) : fEnabled(true), fRect(rect) {}
- void setDisabled() { fEnabled = false; }
- void set(const SkIRect& rect) { fRect = rect; fEnabled = true; }
- bool SK_WARN_UNUSED_RESULT intersect(const SkIRect& rect) {
- if (!fEnabled) {
- this->set(rect);
- return true;
- }
- return fRect.intersect(rect);
- }
- bool operator==(const GrScissorState& other) const {
- return fEnabled == other.fEnabled &&
- (false == fEnabled || fRect == other.fRect);
- }
- bool operator!=(const GrScissorState& other) const { return !(*this == other); }
-
- bool enabled() const { return fEnabled; }
- const SkIRect& rect() const { return fRect; }
-
-private:
- bool fEnabled;
- SkIRect fRect;
-};
-
/**
* Indicates the type of data that a GPU buffer will be used for.
*/
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 98979ac..69656fe 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -9,6 +9,7 @@
#define GrSurfaceProxy_DEFINED
#include "GrGpuResource.h"
+#include "SkRect.h"
class GrTextureProxy;
class GrRenderTargetProxy;
diff --git a/src/gpu/GrAppliedClip.h b/src/gpu/GrAppliedClip.h
index c02e9d0..3e98c6c 100644
--- a/src/gpu/GrAppliedClip.h
+++ b/src/gpu/GrAppliedClip.h
@@ -8,8 +8,8 @@
#ifndef GrAppliedClip_DEFINED
#define GrAppliedClip_DEFINED
-#include "GrTypesPriv.h"
-#include "GrWindowRectangles.h"
+#include "GrScissorState.h"
+#include "GrWindowRectsState.h"
class GrFragmentProcessor;
@@ -25,7 +25,7 @@
}
const GrScissorState& scissorState() const { return fScissorState; }
- const GrWindowRectangles& windowRects() const { return fWindowRects; }
+ const GrWindowRectsState& windowRectsState() const { return fWindowRectsState; }
GrFragmentProcessor* clipCoverageFragmentProcessor() const { return fClipCoverageFP.get(); }
bool hasStencilClip() const { return fHasStencilClip; }
@@ -36,12 +36,15 @@
return fScissorState.intersect(irect) && fClippedDrawBounds.intersect(SkRect::Make(irect));
}
- /**
- * Adds an exclusive window rectangle to the clip. It is not currently supported to switch the
- * windows to inclusive mode.
- */
- void addWindowRectangle(const SkIRect& window) {
- fWindowRects.addWindow(window);
+ void addWindowRectangles(const GrWindowRectsState& windowState) {
+ SkASSERT(!fWindowRectsState.enabled());
+ fWindowRectsState = windowState;
+ }
+
+ void addWindowRectangles(const GrWindowRectangles& windows, const SkIPoint& origin,
+ GrWindowRectsState::Mode mode) {
+ SkASSERT(!fWindowRectsState.enabled());
+ fWindowRectsState.set(windows, origin, mode);
}
void addCoverageFP(sk_sp<GrFragmentProcessor> fp) {
@@ -62,7 +65,7 @@
private:
GrScissorState fScissorState;
- GrWindowRectangles fWindowRects;
+ GrWindowRectsState fWindowRectsState;
sk_sp<GrFragmentProcessor> fClipCoverageFP;
bool fHasStencilClip;
SkRect fClippedDrawBounds;
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 4572db5..d6ad1d9 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -287,11 +287,14 @@
return false;
}
+ GrRenderTarget* rt = drawContext->accessRenderTarget();
+
const SkScalar clipX = SkIntToScalar(fOrigin.x()),
clipY = SkIntToScalar(fOrigin.y());
SkRect clipSpaceDevBounds = devBounds.makeOffset(clipX, clipY);
- const GrReducedClip reducedClip(*fStack, clipSpaceDevBounds);
+ const GrReducedClip reducedClip(*fStack, clipSpaceDevBounds,
+ rt->renderTargetPriv().maxWindowRectangles());
if (reducedClip.hasIBounds() &&
!GrClip::IsInsideClip(reducedClip.ibounds(), clipSpaceDevBounds)) {
@@ -300,29 +303,17 @@
out->addScissor(scissorSpaceIBounds);
}
+ if (!reducedClip.windowRectangles().empty()) {
+ out->addWindowRectangles(reducedClip.windowRectangles(), fOrigin,
+ GrWindowRectsState::Mode::kExclusive);
+ }
+
if (reducedClip.elements().isEmpty()) {
return InitialState::kAllIn == reducedClip.initialState();
}
SkASSERT(reducedClip.hasIBounds());
- // Attempt to implement difference clip rects with window rectangles. This will eventually
- // become more comprehensive.
- if (drawContext->accessRenderTarget()->renderTargetPriv().supportsWindowRectangles() &&
- 1 == reducedClip.elements().count() && !reducedClip.requiresAA() &&
- InitialState::kAllIn == reducedClip.initialState()) {
- const Element* element = reducedClip.elements().head();
- SkRegion::Op op = element->getOp();
- if (Element::kRect_Type == element->getType() &&
- (SkRegion::kDifference_Op == op || SkRegion::kXOR_Op == op)) {
- SkIRect window;
- element->getRect().round(&window);
- window.offset(-fOrigin);
- out->addWindowRectangle(window);
- return true;
- }
- }
-
// An element count of 4 was chosen because of the common pattern in Blink of:
// isect RR
// diff RR
@@ -377,12 +368,15 @@
// use the stencil clip if we can't represent the clip as a rectangle.
// TODO: these need to be swapped over to using a StencilAttachmentProxy
GrStencilAttachment* stencilAttachment =
- context->resourceProvider()->attachStencilAttachment(drawContext->accessRenderTarget());
+ context->resourceProvider()->attachStencilAttachment(rt);
if (nullptr == stencilAttachment) {
SkDebugf("WARNING: failed to attach stencil buffer for clip mask. Clip will be ignored.\n");
return true;
}
+ // 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 (stencilAttachment->mustRenderClip(reducedClip.elementsGenID(), reducedClip.ibounds(),
fOrigin)) {
reducedClip.drawStencilClipMask(context, drawContext, fOrigin);
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 1fd113d..87252f3 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -215,9 +215,12 @@
void GrDrawContext::internalClear(const GrFixedClip& clip,
const GrColor color,
bool canIgnoreClip) {
- bool isFull = !clip.scissorEnabled() ||
- (canIgnoreClip && fContext->caps()->fullClearIsFree()) ||
- clip.scissorRect().contains(SkIRect::MakeWH(this->width(), this->height()));
+ bool isFull = false;
+ if (!clip.hasWindowRectangles()) {
+ isFull = !clip.scissorEnabled() ||
+ (canIgnoreClip && fContext->caps()->fullClearIsFree()) ||
+ clip.scissorRect().contains(SkIRect::MakeWH(this->width(), this->height()));
+ }
if (fContext->caps()->useDrawInsteadOfClear()) {
// This works around a driver bug with clear by drawing a rect instead.
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 3b9f1ec..0117d0e 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -381,7 +381,7 @@
sk_sp_address_as_pointer_address(pipelineBuilder.fCoverageFragmentProcessors.begin()),
pipelineBuilder.numCoverageFragmentProcessors());
args.fScissor = &appliedClip.scissorState();
- args.fWindowRects = &appliedClip.windowRects();
+ args.fWindowRectsState = &appliedClip.windowRectsState();
args.fHasStencilClip = appliedClip.hasStencilClip();
if (!this->setupDstReadIfNecessary(pipelineBuilder, drawContext->accessRenderTarget(),
clip, args.fOpts,
diff --git a/src/gpu/GrFixedClip.cpp b/src/gpu/GrFixedClip.cpp
index ebdd49b..7385028 100644
--- a/src/gpu/GrFixedClip.cpp
+++ b/src/gpu/GrFixedClip.cpp
@@ -10,25 +10,45 @@
#include "GrAppliedClip.h"
#include "GrDrawContext.h"
-void GrFixedClip::getConservativeBounds(int width, int height, SkIRect* devResult,
- bool* isIntersectionOfRects) const {
- devResult->setXYWH(0, 0, width, height);
+bool GrFixedClip::quickContains(const SkRect& rect) const {
+ if (fWindowRectsState.enabled()) {
+ return false;
+ }
+ return !fScissorState.enabled() || GrClip::IsInsideClip(fScissorState.rect(), rect);
+}
+
+void GrFixedClip::getConservativeBounds(int w, int h, SkIRect* devResult, bool* iior) const {
+ devResult->setXYWH(0, 0, w, h);
if (fScissorState.enabled()) {
if (!devResult->intersect(fScissorState.rect())) {
devResult->setEmpty();
}
}
- if (isIntersectionOfRects) {
- *isIntersectionOfRects = true;
+ if (iior) {
+ *iior = true;
}
}
-bool GrFixedClip::apply(GrContext*, GrDrawContext* drawContext, bool isHWAntiAlias,
- bool hasUserStencilSettings, GrAppliedClip* out) const {
+bool GrFixedClip::isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const {
+ if (fWindowRectsState.enabled()) {
+ return false;
+ }
if (fScissorState.enabled()) {
- SkIRect tightScissor;
- if (!tightScissor.intersect(fScissorState.rect(),
- SkIRect::MakeWH(drawContext->width(), drawContext->height()))) {
+ SkRect rect = SkRect::Make(fScissorState.rect());
+ if (!rect.intersects(rtBounds)) {
+ return false;
+ }
+ rr->setRect(rect);
+ *aa = false;
+ return true;
+ }
+ return false;
+};
+
+bool GrFixedClip::apply(GrContext*, GrDrawContext* dc, bool, bool, GrAppliedClip* out) const {
+ if (fScissorState.enabled()) {
+ SkIRect tightScissor = SkIRect::MakeWH(dc->width(), dc->height());
+ if (!tightScissor.intersect(fScissorState.rect())) {
return false;
}
if (IsOutsideClip(tightScissor, out->clippedDrawBounds())) {
@@ -39,6 +59,10 @@
}
}
+ if (fWindowRectsState.enabled()) {
+ out->addWindowRectangles(fWindowRectsState);
+ }
+
return true;
}
diff --git a/src/gpu/GrFixedClip.h b/src/gpu/GrFixedClip.h
index 8b3a9c1..6fb7d23 100644
--- a/src/gpu/GrFixedClip.h
+++ b/src/gpu/GrFixedClip.h
@@ -9,7 +9,8 @@
#define GrFixedClip_DEFINED
#include "GrClip.h"
-#include "GrTypesPriv.h"
+#include "GrScissorState.h"
+#include "GrWindowRectsState.h"
/**
* GrFixedClip is a clip that gets implemented by fixed-function hardware.
@@ -29,32 +30,26 @@
return fScissorState.intersect(irect);
}
- bool quickContains(const SkRect& rect) const final {
- return !fScissorState.enabled() || GrClip::IsInsideClip(fScissorState.rect(), rect);
+ const GrWindowRectsState& windowRectsState() const { return fWindowRectsState; }
+ bool hasWindowRectangles() const { return fWindowRectsState.enabled(); }
+
+ void disableWindowRectangles() { fWindowRectsState.setDisabled(); }
+
+ void setWindowRectangles(const GrWindowRectangles& windows, const SkIPoint& origin,
+ GrWindowRectsState::Mode mode) {
+ fWindowRectsState.set(windows, origin, mode);
}
- void getConservativeBounds(int width, int height, SkIRect* devResult,
- bool* isIntersectionOfRects) const final;
- bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const override {
- if (fScissorState.enabled()) {
- SkRect rect = SkRect::Make(fScissorState.rect());
- if (!rect.intersects(rtBounds)) {
- return false;
- }
- rr->setRect(rect);
- *aa = false;
- return true;
- }
- return false;
- };
-
- bool apply(GrContext*, GrDrawContext*, bool useHWAA, bool hasUserStencilSettings,
- GrAppliedClip* out) const final;
+ bool quickContains(const SkRect&) const override;
+ void getConservativeBounds(int w, int h, SkIRect* devResult, bool* iior) const override;
+ bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const override;
+ bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const override;
static const GrFixedClip& Disabled();
private:
- GrScissorState fScissorState;
+ GrScissorState fScissorState;
+ GrWindowRectsState fWindowRectsState;
};
#endif
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index 8fe321d..892bccd 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -27,7 +27,7 @@
pipeline->fRenderTarget.reset(rt);
SkASSERT(pipeline->fRenderTarget);
pipeline->fScissorState = *args.fScissor;
- pipeline->fWindowRects = *args.fWindowRects;
+ pipeline->fWindowRectsState = *args.fWindowRectsState;
if (builder.hasUserStencilSettings() || args.fHasStencilClip) {
const GrRenderTargetPriv& rtPriv = rt->renderTargetPriv();
pipeline->fStencilSettings.reset(*builder.getUserStencil(), args.fHasStencilClip,
@@ -230,7 +230,7 @@
a.fFragmentProcessors.count() != b.fFragmentProcessors.count() ||
a.fNumColorProcessors != b.fNumColorProcessors ||
a.fScissorState != b.fScissorState ||
- a.fWindowRects != b.fWindowRects ||
+ !a.fWindowRectsState.cheapEqualTo(b.fWindowRectsState) ||
a.fFlags != b.fFlags ||
a.fStencilSettings != b.fStencilSettings ||
a.fDrawFace != b.fDrawFace ||
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index 18f1a2a..8c2f538 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -16,9 +16,9 @@
#include "GrPrimitiveProcessor.h"
#include "GrProcOptInfo.h"
#include "GrProgramDesc.h"
+#include "GrScissorState.h"
#include "GrStencilSettings.h"
-#include "GrTypesPriv.h"
-#include "GrWindowRectangles.h"
+#include "GrWindowRectsState.h"
#include "SkMatrix.h"
#include "SkRefCnt.h"
@@ -60,7 +60,7 @@
const GrCaps* fCaps;
GrPipelineOptimizations fOpts;
const GrScissorState* fScissor;
- const GrWindowRectangles* fWindowRects;
+ const GrWindowRectsState* fWindowRectsState;
bool fHasStencilClip;
GrXferProcessor::DstTexture fDstTexture;
};
@@ -154,7 +154,7 @@
const GrScissorState& getScissorState() const { return fScissorState; }
- const GrWindowRectangles& getWindowRectangles() const { return fWindowRects; }
+ const GrWindowRectsState& getWindowRectsState() const { return fWindowRectsState; }
bool isHWAntialiasState() const { return SkToBool(fFlags & kHWAA_Flag); }
bool snapVerticesToPixelCenters() const { return SkToBool(fFlags & kSnapVertices_Flag); }
@@ -223,7 +223,7 @@
typedef GrPendingProgramElement<const GrXferProcessor> ProgramXferProcessor;
RenderTarget fRenderTarget;
GrScissorState fScissorState;
- GrWindowRectangles fWindowRects;
+ GrWindowRectsState fWindowRectsState;
GrStencilSettings fStencilSettings;
GrDrawFace fDrawFace;
uint32_t fFlags;
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index dfc0a26..89690d3 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -28,7 +28,8 @@
* based on later intersect operations, and perhaps remove intersect-rects. We could optionally
* take a rect in case the caller knows a bound on what is to be drawn through this clip.
*/
-GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds) {
+GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
+ int maxWindowRectangles) {
SkASSERT(!queryBounds.isEmpty());
fHasIBounds = false;
@@ -94,10 +95,15 @@
// Now that we have determined the bounds to use and filtered out the trivial cases, call the
// helper that actually walks the stack.
- this->walkStack(stack, tighterQuery);
+ this->walkStack(stack, tighterQuery, maxWindowRectangles);
+
+ if (fWindowRects.count() < maxWindowRectangles) {
+ this->addInteriorWindowRectangles(maxWindowRectangles);
+ }
}
-void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
+void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds,
+ int maxWindowRectangles) {
// walk backwards until we get to:
// a) the beginning
// b) an operation that is known to make the bounds all inside/outside
@@ -156,6 +162,10 @@
skippable = true;
} else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
skippable = true;
+ } else if (fWindowRects.count() < maxWindowRectangles && !embiggens &&
+ !element->isAA() && Element::kRect_Type == element->getType()) {
+ this->addWindowRectangle(element->getRect(), false);
+ skippable = true;
}
}
if (!skippable) {
@@ -420,10 +430,88 @@
fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
}
+static bool element_is_pure_subtract(SkRegion::Op op) {
+ SkASSERT(op >= 0);
+ return op <= SkRegion::kIntersect_Op;
+
+ GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op);
+ GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op);
+}
+
+void GrReducedClip::addInteriorWindowRectangles(int maxWindowRectangles) {
+ SkASSERT(fWindowRects.count() < maxWindowRectangles);
+ // Walk backwards through the element list and add window rectangles to the interiors of
+ // "difference" elements. Quit if we encounter an element that may grow the clip.
+ ElementList::Iter iter(fElements, ElementList::Iter::kTail_IterStart);
+ for (; iter.get() && element_is_pure_subtract(iter.get()->getOp()); iter.prev()) {
+ const Element* element = iter.get();
+ if (SkRegion::kDifference_Op != element->getOp()) {
+ continue;
+ }
+
+ if (Element::kRect_Type == element->getType()) {
+ SkASSERT(element->isAA());
+ this->addWindowRectangle(element->getRect(), true);
+ if (fWindowRects.count() >= maxWindowRectangles) {
+ return;
+ }
+ continue;
+ }
+
+ if (Element::kRRect_Type == element->getType()) {
+ // For round rects we add two overlapping windows in the shape of a plus.
+ const SkRRect& clipRRect = element->getRRect();
+ SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
+ SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
+ if (SkRRect::kComplex_Type == clipRRect.getType()) {
+ const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
+ const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
+ insetTL.fX = SkTMax(insetTL.x(), insetBL.x());
+ insetTL.fY = SkTMax(insetTL.y(), insetTR.y());
+ insetBR.fX = SkTMax(insetBR.x(), insetTR.x());
+ insetBR.fY = SkTMax(insetBR.y(), insetBL.y());
+ }
+ const SkRect& bounds = clipRRect.getBounds();
+ if (insetTL.x() + insetBR.x() >= bounds.width() ||
+ insetTL.y() + insetBR.y() >= bounds.height()) {
+ continue; // The interior "plus" is empty.
+ }
+
+ SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
+ bounds.right(), bounds.bottom() - insetBR.y());
+ this->addWindowRectangle(horzRect, element->isAA());
+ if (fWindowRects.count() >= maxWindowRectangles) {
+ return;
+ }
+
+ SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
+ bounds.right() - insetBR.x(), bounds.bottom());
+ this->addWindowRectangle(vertRect, element->isAA());
+ if (fWindowRects.count() >= maxWindowRectangles) {
+ return;
+ }
+ continue;
+ }
+ }
+}
+
+inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
+ SkIRect window;
+ if (!elementIsAA) {
+ elementInteriorRect.round(&window);
+ } else {
+ elementInteriorRect.roundIn(&window);
+ }
+ if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
+ fWindowRects.addWindow(window);
+ }
+}
+
inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) {
SkASSERT(fHasIBounds);
if (!fIBounds.intersect(irect)) {
fHasIBounds = false;
+ fWindowRects.reset();
fElements.reset();
fRequiresAA = false;
fInitialState = InitialState::kAllOut;
@@ -503,6 +591,11 @@
// we populate with a rasterization of the clip.
GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height()));
+ if (!fWindowRects.empty()) {
+ clip.setWindowRectangles(fWindowRects, {fIBounds.left(), fIBounds.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;
@@ -570,18 +663,23 @@
StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {}
const GrFixedClip& fixedClip() const { return fFixedClip; }
+ void setWindowRectangles(const GrWindowRectangles& windows, const SkIPoint& origin,
+ GrWindowRectsState::Mode mode) {
+ fFixedClip.setWindowRectangles(windows, origin, mode);
+ }
+
private:
- bool quickContains(const SkRect&) const final {
+ bool quickContains(const SkRect&) const override {
return false;
}
- void getConservativeBounds(int width, int height, SkIRect* devResult, bool* iior) const final {
- fFixedClip.getConservativeBounds(width, height, devResult, iior);
+ void getConservativeBounds(int width, int height, SkIRect* bounds, bool* iior) const override {
+ fFixedClip.getConservativeBounds(width, height, bounds, iior);
}
- bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final {
+ bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const override {
return false;
}
bool apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA,
- bool hasUserStencilSettings, GrAppliedClip* out) const final {
+ bool hasUserStencilSettings, GrAppliedClip* out) const override {
if (!fFixedClip.apply(context, drawContext, useHWAA, hasUserStencilSettings, out)) {
return false;
}
@@ -600,6 +698,11 @@
// We set the current clip to the bounds so that our recursive draws are scissored to them.
StencilClip stencilClip(fIBounds.makeOffset(-clipOrigin.x(), -clipOrigin.y()));
+ if (!fWindowRects.empty()) {
+ stencilClip.setWindowRectangles(fWindowRects, clipOrigin,
+ GrWindowRectsState::Mode::kExclusive);
+ }
+
bool initialState = InitialState::kAllIn == this->initialState();
drawContext->drawContextPriv().clearStencilClip(stencilClip.fixedClip(), initialState);
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
index 731d58f..b8413e6 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -8,6 +8,7 @@
#ifndef GrReducedClip_DEFINED
#define GrReducedClip_DEFINED
+#include "GrWindowRectangles.h"
#include "SkClipStack.h"
#include "SkTLList.h"
@@ -20,11 +21,11 @@
*/
class SK_API GrReducedClip {
public:
- GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds);
+ GrReducedClip(const SkClipStack&, const SkRect& queryBounds, int maxWindowRectangles = 0);
/**
- * If hasIBounds() is true, this is the bounding box within which the reduced clip is valid, and
- * the caller must not modify any pixels outside this box. Undefined if hasIBounds() is false.
+ * If hasIBounds() is true, this is the bounding box within which the clip elements are valid.
+ * The caller must not modify any pixels outside this box. Undefined if hasIBounds() is false.
*/
const SkIRect& ibounds() const { SkASSERT(fHasIBounds); return fIBounds; }
int left() const { return this->ibounds().left(); }
@@ -38,10 +39,16 @@
*/
bool hasIBounds() const { return fHasIBounds; }
+ /**
+ * If nonempty, this is a set of "exclusive" windows within which the clip elements are NOT
+ * valid. The caller must not modify any pixels inside these windows.
+ */
+ const GrWindowRectangles& windowRectangles() const { return fWindowRects; }
+
typedef SkTLList<SkClipStack::Element, 16> ElementList;
/**
- * Populated with a minimal list of elements that implement the clip.
+ * Populated with a minimal list of elements required to fully implement the clip.
*/
const ElementList& elements() const { return fElements; }
@@ -67,15 +74,18 @@
bool drawStencilClipMask(GrContext*, GrDrawContext*, const SkIPoint& clipOrigin) const;
private:
- void walkStack(const SkClipStack&, const SkRect& queryBounds);
+ void walkStack(const SkClipStack&, const SkRect& queryBounds, int maxWindowRectangles);
+ void addInteriorWindowRectangles(int maxWindowRectangles);
+ void addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA);
bool intersectIBounds(const SkIRect&);
- SkIRect fIBounds;
- bool fHasIBounds;
- ElementList fElements;
- int32_t fElementsGenID;
- bool fRequiresAA;
- InitialState fInitialState;
+ SkIRect fIBounds;
+ bool fHasIBounds;
+ GrWindowRectangles fWindowRects;
+ ElementList fElements;
+ int32_t fElementsGenID;
+ bool fRequiresAA;
+ InitialState fInitialState;
};
#endif
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index 90f94b3..2053a16 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -127,3 +127,8 @@
GrRenderTargetPriv::getMultisampleSpecs(const GrStencilSettings& stencil) const {
return fRenderTarget->getGpu()->getMultisampleSpecs(fRenderTarget, stencil);
}
+
+int GrRenderTargetPriv::maxWindowRectangles() const {
+ return (this->flags() & Flags::kWindowRectsSupport) ?
+ fRenderTarget->getGpu()->caps()->maxWindowRectangles() : 0;
+}
diff --git a/src/gpu/GrRenderTargetPriv.h b/src/gpu/GrRenderTargetPriv.h
index 2c828ec..698288e 100644
--- a/src/gpu/GrRenderTargetPriv.h
+++ b/src/gpu/GrRenderTargetPriv.h
@@ -38,7 +38,7 @@
typedef GrRenderTarget::Flags Flags;
Flags flags() const { return fRenderTarget->fFlags; }
- bool supportsWindowRectangles() const { return this->flags() & Flags::kWindowRectsSupport; }
+ int maxWindowRectangles() const;
private:
explicit GrRenderTargetPriv(GrRenderTarget* renderTarget) : fRenderTarget(renderTarget) {}
diff --git a/src/gpu/GrScissorState.h b/src/gpu/GrScissorState.h
new file mode 100644
index 0000000..59ea088
--- /dev/null
+++ b/src/gpu/GrScissorState.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrScissorState_DEFINED
+#define GrScissorState_DEFINED
+
+#include "SkRect.h"
+
+class GrScissorState {
+public:
+ GrScissorState() : fEnabled(false) {}
+ GrScissorState(const SkIRect& rect) : fEnabled(true), fRect(rect) {}
+ void setDisabled() { fEnabled = false; }
+ void set(const SkIRect& rect) { fRect = rect; fEnabled = true; }
+ bool SK_WARN_UNUSED_RESULT intersect(const SkIRect& rect) {
+ if (!fEnabled) {
+ this->set(rect);
+ return true;
+ }
+ return fRect.intersect(rect);
+ }
+ bool operator==(const GrScissorState& other) const {
+ return fEnabled == other.fEnabled &&
+ (false == fEnabled || fRect == other.fRect);
+ }
+ bool operator!=(const GrScissorState& other) const { return !(*this == other); }
+
+ bool enabled() const { return fEnabled; }
+ const SkIRect& rect() const { return fRect; }
+
+private:
+ bool fEnabled;
+ SkIRect fRect;
+};
+
+#endif
diff --git a/src/gpu/GrWindowRectangles.h b/src/gpu/GrWindowRectangles.h
index b2f6e1a..076c40d 100644
--- a/src/gpu/GrWindowRectangles.h
+++ b/src/gpu/GrWindowRectangles.h
@@ -15,21 +15,15 @@
public:
constexpr static int kMaxWindows = 8;
- enum class Mode : bool {
- kExclusive,
- kInclusive
- };
-
- GrWindowRectangles(Mode mode = Mode::kExclusive) : fMode(mode), fCount(0) {}
+ GrWindowRectangles() : fCount(0) {}
GrWindowRectangles(const GrWindowRectangles& that) : fCount(0) { *this = that; }
~GrWindowRectangles() { SkSafeUnref(this->rec()); }
- Mode mode() const { return fMode; }
+ bool empty() const { return !fCount; }
int count() const { return fCount; }
- bool disabled() const { return Mode::kExclusive == fMode && !fCount; }
const SkIRect* data() const;
- void reset(Mode = Mode::kExclusive);
+ void reset();
GrWindowRectangles& operator=(const GrWindowRectangles&);
SkIRect& addWindow(const SkIRect& window) { return this->addWindow() = window; }
@@ -44,8 +38,7 @@
const Rec* rec() const { return fCount <= kNumLocalWindows ? nullptr : fRec; }
- Mode fMode;
- uint8_t fCount;
+ int fCount;
union {
SkIRect fLocalWindows[kNumLocalWindows]; // If fCount <= kNumLocalWindows.
Rec* fRec; // If fCount > kNumLocalWindows.
@@ -65,15 +58,13 @@
return fCount <= kNumLocalWindows ? fLocalWindows : fRec->fData;
}
-inline void GrWindowRectangles::reset(Mode mode) {
+inline void GrWindowRectangles::reset() {
SkSafeUnref(this->rec());
- fMode = mode;
fCount = 0;
}
inline GrWindowRectangles& GrWindowRectangles::operator=(const GrWindowRectangles& that) {
SkSafeUnref(this->rec());
- fMode = that.fMode;
fCount = that.fCount;
if (fCount <= kNumLocalWindows) {
memcpy(fLocalWindows, that.fLocalWindows, fCount * sizeof(SkIRect));
@@ -98,7 +89,7 @@
}
inline bool GrWindowRectangles::operator==(const GrWindowRectangles& that) const {
- if (fMode != that.fMode || fCount != that.fCount) {
+ if (fCount != that.fCount) {
return false;
}
if (fCount > kNumLocalWindows && fRec == that.fRec) {
diff --git a/src/gpu/GrWindowRectsState.h b/src/gpu/GrWindowRectsState.h
new file mode 100644
index 0000000..9d3b61b
--- /dev/null
+++ b/src/gpu/GrWindowRectsState.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrWindowRectsState_DEFINED
+#define GrWindowRectsState_DEFINED
+
+#include "GrWindowRectangles.h"
+
+class GrWindowRectsState {
+public:
+ enum class Mode : bool {
+ kExclusive,
+ kInclusive
+ };
+
+ GrWindowRectsState() : fMode(Mode::kExclusive) {}
+ GrWindowRectsState(const GrWindowRectangles& windows, const SkIPoint& origin, Mode mode)
+ : fMode(mode)
+ , fOrigin(origin)
+ , fWindows(windows) {
+ }
+
+ bool enabled() const { return Mode::kInclusive == fMode || !fWindows.empty(); }
+ Mode mode() const { return fMode; }
+ const SkIPoint& origin() const { return fOrigin; }
+ const GrWindowRectangles& windows() const { return fWindows; }
+ int numWindows() const { return fWindows.count(); }
+
+ void setDisabled() {
+ fMode = Mode::kExclusive;
+ fWindows.reset();
+ }
+
+ void set(const GrWindowRectangles& windows, const SkIPoint& origin, Mode mode) {
+ fMode = mode;
+ fOrigin = origin;
+ fWindows = windows;
+ }
+
+ bool cheapEqualTo(const GrWindowRectsState& that) const {
+ if (fMode != that.fMode) {
+ return false;
+ }
+ if (!fWindows.empty() && fOrigin != that.fOrigin) {
+ return false;
+ }
+ return fWindows == that.fWindows;
+ }
+
+private:
+ Mode fMode;
+ SkIPoint fOrigin;
+ GrWindowRectangles fWindows;
+};
+
+#endif
diff --git a/src/gpu/batches/GrClearBatch.h b/src/gpu/batches/GrClearBatch.h
index 27e19fa..16f1ddd 100644
--- a/src/gpu/batches/GrClearBatch.h
+++ b/src/gpu/batches/GrClearBatch.h
@@ -71,6 +71,9 @@
// same color.
GrClearBatch* cb = t->cast<GrClearBatch>();
SkASSERT(cb->fRenderTarget == fRenderTarget);
+ if (!fClip.windowRectsState().cheapEqualTo(cb->fClip.windowRectsState())) {
+ return false;
+ }
if (cb->contains(this)) {
fClip = cb->fClip;
this->replaceBounds(*t);
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 466af6c..e002d51 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -564,7 +564,7 @@
if (resetBits & kView_GrGLBackendState) {
fHWScissorSettings.invalidate();
- fHWWindowRects.invalidate();
+ fHWWindowRectsState.invalidate();
fHWViewport.invalidate();
}
@@ -1997,39 +1997,42 @@
this->disableScissor();
}
-void GrGLGpu::flushWindowRectangles(const GrWindowRectangles& windows, const GrGLRenderTarget* rt) {
- typedef GrWindowRectangles::Mode Mode;
- SkASSERT(windows.count() <= this->caps()->maxWindowRectangles());
- SkASSERT(windows.disabled() || rt->renderFBOID()); // Window rectangles can't be used on-screen.
+void GrGLGpu::flushWindowRectangles(const GrWindowRectsState& windowState,
+ const GrGLRenderTarget* rt) {
+ typedef GrWindowRectsState::Mode Mode;
+ SkASSERT(!windowState.enabled() || rt->renderFBOID()); // Window rects can't be used on-screen.
+ SkASSERT(windowState.numWindows() <= this->caps()->maxWindowRectangles());
if (!this->caps()->maxWindowRectangles() ||
- fHWWindowRects.equal(rt->origin(), rt->getViewport(), windows)) {
+ fHWWindowRectsState.knownEqualTo(rt->origin(), rt->getViewport(), windowState)) {
return;
}
// This is purely a workaround for a spurious warning generated by gcc. Otherwise the above
// assert would be sufficient. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=5912
- int numWindows = SkTMin(windows.count(), int(GrWindowRectangles::kMaxWindows));
- SkASSERT(windows.count() == numWindows);
+ int numWindows = SkTMin(windowState.numWindows(), int(GrWindowRectangles::kMaxWindows));
+ SkASSERT(windowState.numWindows() == numWindows);
GrGLIRect glwindows[GrWindowRectangles::kMaxWindows];
- const SkIRect* skwindows = windows.data();
+ const SkIRect* skwindows = windowState.windows().data();
+ int dx = -windowState.origin().x(), dy = -windowState.origin().y();
for (int i = 0; i < numWindows; ++i) {
- glwindows[i].setRelativeTo(rt->getViewport(), skwindows[i], rt->origin());
+ const SkIRect& skwindow = skwindows[i].makeOffset(dx, dy);
+ glwindows[i].setRelativeTo(rt->getViewport(), skwindow, rt->origin());
}
- GrGLenum glmode = (Mode::kExclusive == windows.mode()) ? GR_GL_EXCLUSIVE : GR_GL_INCLUSIVE;
+ GrGLenum glmode = (Mode::kExclusive == windowState.mode()) ? GR_GL_EXCLUSIVE : GR_GL_INCLUSIVE;
GL_CALL(WindowRectangles(glmode, numWindows, glwindows->asInts()));
- fHWWindowRects.set(rt->origin(), rt->getViewport(), windows);
+ fHWWindowRectsState.set(rt->origin(), rt->getViewport(), windowState);
}
void GrGLGpu::disableWindowRectangles() {
- if (!this->caps()->maxWindowRectangles() || fHWWindowRects.disabled()) {
+ if (!this->caps()->maxWindowRectangles() || fHWWindowRectsState.knownDisabled()) {
return;
}
GL_CALL(WindowRectangles(GR_GL_EXCLUSIVE, 0, nullptr));
- fHWWindowRects.setDisabled();
+ fHWWindowRectsState.setDisabled();
}
void GrGLGpu::flushMinSampleShading(float minSampleShading) {
@@ -2079,7 +2082,7 @@
GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.getRenderTarget());
this->flushStencil(pipeline.getStencil());
this->flushScissor(pipeline.getScissorState(), glRT->getViewport(), glRT->origin());
- this->flushWindowRectangles(pipeline.getWindowRectangles(), glRT);
+ this->flushWindowRectangles(pipeline.getWindowRectsState(), glRT);
this->flushHWAAState(glRT, pipeline.isHWAntialiasState(), !pipeline.getStencil().isDisabled());
// This must come after textures are flushed because a texture may need
@@ -2207,7 +2210,7 @@
this->flushRenderTarget(glRT, clip.scissorEnabled() ? &clip.scissorRect() : nullptr);
this->flushScissor(clip.scissorState(), glRT->getViewport(), glRT->origin());
- this->disableWindowRectangles();
+ this->flushWindowRectangles(clip.windowRectsState(), glRT);
GrGLfloat r, g, b, a;
static const GrGLfloat scale255 = 1.f / 255.f;
@@ -2271,7 +2274,7 @@
this->flushRenderTarget(glRT, &SkIRect::EmptyIRect());
this->flushScissor(clip.scissorState(), glRT->getViewport(), glRT->origin());
- this->disableWindowRectangles();
+ this->flushWindowRectangles(clip.windowRectsState(), glRT);
GL_CALL(StencilMask((uint32_t) clipStencilMask));
GL_CALL(ClearStencil(value));
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 3fe4204..62803ee 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -18,6 +18,7 @@
#include "GrGLVertexArray.h"
#include "GrGpu.h"
#include "GrTypes.h"
+#include "GrWindowRectsState.h"
#include "GrXferProcessor.h"
#include "SkTArray.h"
#include "SkTypes.h"
@@ -314,7 +315,7 @@
// disables the scissor
void disableScissor();
- void flushWindowRectangles(const GrWindowRectangles&, const GrGLRenderTarget*);
+ void flushWindowRectangles(const GrWindowRectsState&, const GrGLRenderTarget*);
void disableWindowRectangles();
void initFSAASupport();
@@ -424,39 +425,36 @@
class {
public:
- bool valid() const { return kInvalidOrigin != fOrigin; }
- void invalidate() { fOrigin = kInvalidOrigin; }
+ bool valid() const { return kInvalidSurfaceOrigin != fRTOrigin; }
+ void invalidate() { fRTOrigin = kInvalidSurfaceOrigin; }
+ bool knownDisabled() const { return this->valid() && !fWindowState.enabled(); }
+ void setDisabled() { fRTOrigin = kDefault_GrSurfaceOrigin, fWindowState.setDisabled(); }
- bool disabled() const {
- return this->valid() && Mode::kExclusive == fWindows.mode() && !fWindows.count();
+ void set(GrSurfaceOrigin rtOrigin, const GrGLIRect& viewport,
+ const GrWindowRectsState& windowState) {
+ fRTOrigin = rtOrigin;
+ fViewport = viewport;
+ fWindowState = windowState;
}
- void setDisabled() { fOrigin = kDefault_GrSurfaceOrigin, fWindows.reset(); }
- bool equal(GrSurfaceOrigin org, const GrGLIRect& viewp,
- const GrWindowRectangles& windows) const {
+ bool knownEqualTo(GrSurfaceOrigin rtOrigin, const GrGLIRect& viewport,
+ const GrWindowRectsState& windowState) const {
if (!this->valid()) {
return false;
}
- if (fWindows.count() && (fOrigin != org || fViewport != viewp)) {
+ if (fWindowState.numWindows() && (fRTOrigin != rtOrigin || fViewport != viewport)) {
return false;
}
- return fWindows == windows;
- }
-
- void set(GrSurfaceOrigin org, const GrGLIRect& viewp, const GrWindowRectangles& windows) {
- fOrigin = org;
- fViewport = viewp;
- fWindows = windows;
+ return fWindowState.cheapEqualTo(windowState);
}
private:
- typedef GrWindowRectangles::Mode Mode;
- enum { kInvalidOrigin = -1 };
+ enum { kInvalidSurfaceOrigin = -1 };
- int fOrigin;
+ int fRTOrigin;
GrGLIRect fViewport;
- GrWindowRectangles fWindows;
- } fHWWindowRects;
+ GrWindowRectsState fWindowState;
+ } fHWWindowRectsState;
GrGLIRect fHWViewport;
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 6144cf9..e3c7cb7 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -162,6 +162,7 @@
const GrFixedClip& clip,
bool insideStencilMask) {
SkASSERT(target);
+ SkASSERT(!clip.hasWindowRectangles());
GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(target);
GrStencilAttachment* sb = target->renderTargetPriv().getStencilAttachment();
@@ -215,6 +216,7 @@
void GrVkGpuCommandBuffer::onClear(GrRenderTarget* target, const GrFixedClip& clip, GrColor color) {
// parent class should never let us get here with no RT
SkASSERT(target);
+ SkASSERT(!clip.hasWindowRectangles());
VkClearColorValue vkColor;
GrColorToRGBAFloat(color, vkColor.float32);
diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp
index 016ce5e..367dee4 100644
--- a/tests/ProxyTest.cpp
+++ b/tests/ProxyTest.cpp
@@ -162,7 +162,8 @@
GrGpu* gpu = ctxInfo.grContext()->getGpu();
sk_sp<GrRenderTarget> defaultFBO(
gpu->wrapBackendRenderTarget(backendDesc, kBorrow_GrWrapOwnership));
- SkASSERT(!defaultFBO->renderTargetPriv().supportsWindowRectangles());
+ REPORTER_ASSERT(reporter,
+ !defaultFBO->renderTargetPriv().maxWindowRectangles());
sk_sp<GrRenderTargetProxy> rtProxy(
GrRenderTargetProxy::Make(caps, defaultFBO));
@@ -178,8 +179,9 @@
desc.fFlags = kRenderTarget_GrSurfaceFlag;
tex.reset(provider->createTexture(desc, budgeted));
sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget()));
- SkASSERT(caps.maxWindowRectangles() <= 0 ||
- rt->renderTargetPriv().supportsWindowRectangles());
+ REPORTER_ASSERT(reporter,
+ caps.maxWindowRectangles() ==
+ rt->renderTargetPriv().maxWindowRectangles());
sk_sp<GrRenderTargetProxy> rtProxy(GrRenderTargetProxy::Make(caps, rt));
check_surface(reporter, rtProxy.get(), origin,
diff --git a/tests/WindowRectanglesTest.cpp b/tests/WindowRectanglesTest.cpp
index d569f1d..e954565 100644
--- a/tests/WindowRectanglesTest.cpp
+++ b/tests/WindowRectanglesTest.cpp
@@ -31,7 +31,6 @@
GrWindowRectangles wr2(wr);
REPORTER_ASSERT(reporter, wr2 == wr);
- REPORTER_ASSERT(reporter, wr2.mode() == wr.mode());
REPORTER_ASSERT(reporter, wr2.count() == wr.count());
REPORTER_ASSERT(reporter, !memcmp(wr2.data(), wr.data(), i * sizeof(SkIRect)));
@@ -72,21 +71,6 @@
REPORTER_ASSERT(reporter, !memcmp(A.data(), windowData,
GrWindowRectangles::kMaxWindows * sizeof(SkIRect)));
}
-
- GrWindowRectangles wrI(GrWindowRectangles::Mode::kInclusive);
- for (int i = 0; i < wr.count(); ++i) {
- wrI.addWindow(windowData[i]);
- }
- REPORTER_ASSERT(reporter, wrI != wr);
- REPORTER_ASSERT(reporter, wrI.mode() != wr.mode());
- REPORTER_ASSERT(reporter, wrI.count() == wr.count());
- REPORTER_ASSERT(reporter, !memcmp(wrI.data(), wr.data(), wr.count() * sizeof(SkIRect)));
-
- wr.reset(GrWindowRectangles::Mode::kInclusive);
- for (int i = 0; i < wrI.count(); ++i) {
- wr.addWindow(windowData[i]);
- }
- REPORTER_ASSERT(reporter, wrI == wr);
}
#endif