Implement difference clip rects with window rectangles
authorcsmartdalton <csmartdalton@google.com>
Wed, 17 Aug 2016 17:00:21 +0000 (10:00 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 17 Aug 2016 17:00:22 +0000 (10:00 -0700)
Plumbs the pipeline for window rectangles and uses them for a very
basic implementation of difference clip rects. This puts a common
Blink pattern on fast path, but we will still eventually need to make
more comprehensive use of window rectangles during clipping.

GTX 960 perf result:
                              gpu                     glinst4                 glinst16
desk_jsfiddlebigcar.skp       0.254 -> 0.177 [70%]    0.279 -> 0.197 [71%]    0.577 -> 0.196 [34%]
keymobi_sfgate_com_.skp       0.697 -> 0.513 [74%]    0.766 -> 0.451 [59%]    0.769 -> 0.597 [78%]
keymobi_blogger.skp           0.406 -> 0.314 [77%]    0.436 -> 0.292 [67%]    0.696 -> 0.319 [46%]
desk_pokemonwiki.skp          0.121 -> 0.098 [81%]     0.13 -> 0.105 [81%]    0.216 -> 0.097 [45%]
desk_wikipedia.skp            0.121 -> 0.098 [81%]     0.13 -> 0.104 [80%]    0.199 -> 0.104 [52%]
keymobi_androidpolice_co...   0.443 -> 0.382 [86%]    0.447 -> 0.398 [89%]    0.444 -> 0.396 [89%]
keymobi_booking_com_sear...   1 .15 ->  1.03 [90%]     1.17 ->  1.06 [91%]     1.17 ->  1.05 [90%]
keymobi_theverge_com.skp      0.417 -> 0.396 [95%]    0.426 -> 0.405 [95%]    0.429 ->   0.4 [93%]

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2251573002

Review-Url: https://codereview.chromium.org/2251573002

16 files changed:
gyp/gpu.gypi
include/gpu/GrClip.h
src/gpu/GrAppliedClip.h [new file with mode: 0644]
src/gpu/GrCaps.cpp
src/gpu/GrClip.cpp
src/gpu/GrClipStackClip.cpp
src/gpu/GrDrawTarget.cpp
src/gpu/GrNonAtomicRef.h
src/gpu/GrPipeline.cpp
src/gpu/GrPipeline.h
src/gpu/GrWindowRectangles.h [new file with mode: 0644]
src/gpu/gl/GrGLCaps.cpp
src/gpu/gl/GrGLGpu.cpp
src/gpu/gl/GrGLGpu.h
src/gpu/gl/GrGLIRect.h
tests/WindowRectanglesTest.cpp [new file with mode: 0644]

index a856cb4..e85b72e 100644 (file)
@@ -67,6 +67,7 @@
       '<(skia_include_path)/private/GrSurfaceProxy.h',
       '<(skia_include_path)/private/GrTextureProxy.h',
 
+      '<(skia_src_path)/gpu/GrAppliedClip.h',
       '<(skia_src_path)/gpu/GrAuditTrail.cpp',
       '<(skia_src_path)/gpu/GrAutoLocaleSetter.h',
       '<(skia_src_path)/gpu/GrAllocator.h',
       '<(skia_src_path)/gpu/GrTextureAccess.cpp',
       '<(skia_src_path)/gpu/GrTRecorder.h',
       '<(skia_src_path)/gpu/GrUserStencilSettings.h',
+      '<(skia_src_path)/gpu/GrWindowRectangles.h',
       '<(skia_src_path)/gpu/GrXferProcessor.cpp',
       '<(skia_src_path)/gpu/GrYUVProvider.cpp',
       '<(skia_src_path)/gpu/GrYUVProvider.h',
index a6a0a66..6457d03 100644 (file)
 #include "GrFragmentProcessor.h"
 #include "GrTypesPriv.h"
 
+class GrAppliedClip;
 class GrDrawContext;
 
 /**
- * Produced by GrClip. It provides a set of modifications to the drawing state that are used to
- * create the final GrPipeline for a GrBatch.
- */
-class GrAppliedClip : public SkNoncopyable {
-public:
-    GrAppliedClip(const SkRect& drawBounds)
-        : fHasStencilClip(false)
-        , fClippedDrawBounds(drawBounds) {
-    }
-
-    const GrScissorState& scissorState() const { return fScissorState; }
-    GrFragmentProcessor* clipCoverageFragmentProcessor() const { return fClipCoverageFP.get(); }
-    bool hasStencilClip() const { return fHasStencilClip; }
-
-    /**
-     * Intersects the applied clip with the provided rect. Returns false if the draw became empty.
-     */
-    bool addScissor(const SkIRect& irect) {
-        return fScissorState.intersect(irect) && fClippedDrawBounds.intersect(SkRect::Make(irect));
-    }
-
-    void addCoverageFP(sk_sp<GrFragmentProcessor> fp) {
-        SkASSERT(!fClipCoverageFP);
-        fClipCoverageFP = fp;
-    }
-
-    void addStencilClip() {
-        SkASSERT(!fHasStencilClip);
-        fHasStencilClip = true;
-    }
-
-    /**
-     * Returns the device bounds of the draw after clip has been applied. TODO: Ideally this would
-     * consider the combined effect of all clipping techniques in play (scissor, stencil, fp, etc.).
-     */
-    const SkRect& clippedDrawBounds() const { return fClippedDrawBounds; }
-
-private:
-    GrScissorState             fScissorState;
-    sk_sp<GrFragmentProcessor> fClipCoverageFP;
-    bool                       fHasStencilClip;
-    SkRect                     fClippedDrawBounds;
-    typedef SkNoncopyable INHERITED;
-};
-
-/**
  * GrClip is an abstract base class for applying a clip. It constructs a clip mask if necessary, and
  * fills out a GrAppliedClip instructing the caller on how to set up the draw state.
  */
diff --git a/src/gpu/GrAppliedClip.h b/src/gpu/GrAppliedClip.h
new file mode 100644 (file)
index 0000000..c02e9d0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 GrAppliedClip_DEFINED
+#define GrAppliedClip_DEFINED
+
+#include "GrTypesPriv.h"
+#include "GrWindowRectangles.h"
+
+class GrFragmentProcessor;
+
+/**
+ * Produced by GrClip. It provides a set of modifications to the drawing state that are used to
+ * create the final GrPipeline for a GrBatch.
+ */
+class GrAppliedClip : public SkNoncopyable {
+public:
+    GrAppliedClip(const SkRect& drawBounds)
+        : fHasStencilClip(false)
+        , fClippedDrawBounds(drawBounds) {
+    }
+
+    const GrScissorState& scissorState() const { return fScissorState; }
+    const GrWindowRectangles& windowRects() const { return fWindowRects; }
+    GrFragmentProcessor* clipCoverageFragmentProcessor() const { return fClipCoverageFP.get(); }
+    bool hasStencilClip() const { return fHasStencilClip; }
+
+    /**
+     * Intersects the applied clip with the provided rect. Returns false if the draw became empty.
+     */
+    bool addScissor(const SkIRect& irect) {
+        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 addCoverageFP(sk_sp<GrFragmentProcessor> fp) {
+        SkASSERT(!fClipCoverageFP);
+        fClipCoverageFP = fp;
+    }
+
+    void addStencilClip() {
+        SkASSERT(!fHasStencilClip);
+        fHasStencilClip = true;
+    }
+
+    /**
+     * Returns the device bounds of the draw after clip has been applied. TODO: Ideally this would
+     * consider the combined effect of all clipping techniques in play (scissor, stencil, fp, etc.).
+     */
+    const SkRect& clippedDrawBounds() const { return fClippedDrawBounds; }
+
+private:
+    GrScissorState             fScissorState;
+    GrWindowRectangles         fWindowRects;
+    sk_sp<GrFragmentProcessor> fClipCoverageFP;
+    bool                       fHasStencilClip;
+    SkRect                     fClippedDrawBounds;
+    typedef SkNoncopyable INHERITED;
+};
+
+#endif
index 19ef38c..622c685 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "GrCaps.h"
 #include "GrContextOptions.h"
+#include "GrWindowRectangles.h"
 
 GrShaderCaps::GrShaderCaps() {
     fShaderDerivativeSupport = false;
@@ -131,6 +132,7 @@ GrCaps::GrCaps(const GrContextOptions& options) {
 }
 
 void GrCaps::applyOptionsOverrides(const GrContextOptions& options) {
+    this->onApplyOptionsOverrides(options);
     fMaxTextureSize = SkTMin(fMaxTextureSize, options.fMaxTextureSizeOverride);
     // If the max tile override is zero, it means we should use the max texture size.
     if (!options.fMaxTileSizeOverride || options.fMaxTileSizeOverride > fMaxTextureSize) {
@@ -138,7 +140,9 @@ void GrCaps::applyOptionsOverrides(const GrContextOptions& options) {
     } else {
         fMaxTileSize = options.fMaxTileSizeOverride;
     }
-    this->onApplyOptionsOverrides(options);
+    if (fMaxWindowRectangles > GrWindowRectangles::kMaxWindows) {
+        fMaxWindowRectangles = GrWindowRectangles::kMaxWindows;
+    }
 }
 
 static SkString map_flags_to_string(uint32_t flags) {
index ef9c9cd..f89a366 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "GrClip.h"
 
+#include "GrAppliedClip.h"
 #include "GrDrawContext.h"
 
 void GrNoClip::getConservativeBounds(int width, int height, SkIRect* devResult,
index 0d6270f..975f56a 100644 (file)
@@ -7,9 +7,11 @@
 
 #include "GrClipStackClip.h"
 
+#include "GrAppliedClip.h"
 #include "GrDrawingManager.h"
 #include "GrDrawContextPriv.h"
 #include "GrGpuResourcePriv.h"
+#include "GrRenderTargetPriv.h"
 #include "GrStencilAttachment.h"
 #include "GrSWMaskHelper.h"
 #include "effects/GrConvexPolyEffect.h"
@@ -280,6 +282,23 @@ bool GrClipStackClip::apply(GrContext* context, GrDrawContext* drawContext, bool
 
     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
index b4142eb..1889b87 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "GrDrawTarget.h"
 
+#include "GrAppliedClip.h"
 #include "GrAuditTrail.h"
 #include "GrCaps.h"
 #include "GrDrawContext.h"
@@ -379,6 +380,7 @@ void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder,
         sk_sp_address_as_pointer_address(pipelineBuilder.fCoverageFragmentProcessors.begin()),
         pipelineBuilder.numCoverageFragmentProcessors());
     args.fScissor = &appliedClip.scissorState();
+    args.fWindowRects = &appliedClip.windowRects();
     args.fHasStencilClip = appliedClip.hasStencilClip();
     if (!this->setupDstReadIfNecessary(pipelineBuilder, drawContext->accessRenderTarget(),
                                        clip, args.fOpts,
index e1503bc..c23637f 100644 (file)
@@ -28,6 +28,8 @@ public:
     }
 #endif
 
+    bool unique() const { return 1 == fRefCnt; }
+
     void ref() const {
         // Once the ref cnt reaches zero it should never be ref'ed again.
         SkASSERT(fRefCnt > 0);
index c60a94b..8fe321d 100644 (file)
@@ -27,6 +27,7 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
     pipeline->fRenderTarget.reset(rt);
     SkASSERT(pipeline->fRenderTarget);
     pipeline->fScissorState = *args.fScissor;
+    pipeline->fWindowRects = *args.fWindowRects;
     if (builder.hasUserStencilSettings() || args.fHasStencilClip) {
         const GrRenderTargetPriv& rtPriv = rt->renderTargetPriv();
         pipeline->fStencilSettings.reset(*builder.getUserStencil(), args.fHasStencilClip,
@@ -229,6 +230,7 @@ bool GrPipeline::AreEqual(const GrPipeline& a, const GrPipeline& b,
         a.fFragmentProcessors.count() != b.fFragmentProcessors.count() ||
         a.fNumColorProcessors != b.fNumColorProcessors ||
         a.fScissorState != b.fScissorState ||
+        a.fWindowRects != b.fWindowRects ||
         a.fFlags != b.fFlags ||
         a.fStencilSettings != b.fStencilSettings ||
         a.fDrawFace != b.fDrawFace ||
index 1457dbc..18f1a2a 100644 (file)
@@ -18,6 +18,7 @@
 #include "GrProgramDesc.h"
 #include "GrStencilSettings.h"
 #include "GrTypesPriv.h"
+#include "GrWindowRectangles.h"
 #include "SkMatrix.h"
 #include "SkRefCnt.h"
 
@@ -59,6 +60,7 @@ public:
         const GrCaps*               fCaps;
         GrPipelineOptimizations     fOpts;
         const GrScissorState*       fScissor;
+        const GrWindowRectangles*   fWindowRects;
         bool                        fHasStencilClip;
         GrXferProcessor::DstTexture fDstTexture;
     };
@@ -152,6 +154,8 @@ public:
 
     const GrScissorState& getScissorState() const { return fScissorState; }
 
+    const GrWindowRectangles& getWindowRectangles() const { return fWindowRects; }
+
     bool isHWAntialiasState() const { return SkToBool(fFlags & kHWAA_Flag); }
     bool snapVerticesToPixelCenters() const { return SkToBool(fFlags & kSnapVertices_Flag); }
     bool getDisableOutputConversionToSRGB() const {
@@ -219,6 +223,7 @@ private:
     typedef GrPendingProgramElement<const GrXferProcessor> ProgramXferProcessor;
     RenderTarget                        fRenderTarget;
     GrScissorState                      fScissorState;
+    GrWindowRectangles                  fWindowRects;
     GrStencilSettings                   fStencilSettings;
     GrDrawFace                          fDrawFace;
     uint32_t                            fFlags;
diff --git a/src/gpu/GrWindowRectangles.h b/src/gpu/GrWindowRectangles.h
new file mode 100644 (file)
index 0000000..682af93
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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 GrWindowRectangles_DEFINED
+#define GrWindowRectangles_DEFINED
+
+#include "GrNonAtomicRef.h"
+#include "SkRect.h"
+
+class GrWindowRectangles {
+public:
+    constexpr static int kMaxWindows = 16;
+
+    enum class Mode : bool {
+        kExclusive,
+        kInclusive
+    };
+
+    GrWindowRectangles(Mode mode = Mode::kExclusive) : fMode(mode), fCount(0) {}
+    GrWindowRectangles(const GrWindowRectangles& that) : fCount(0) { *this = that; }
+    ~GrWindowRectangles() { SkSafeUnref(this->rec()); }
+
+    Mode mode() const { return fMode; }
+    uint16_t count() const { return fCount; }
+    bool disabled() const { return Mode::kExclusive == fMode && !fCount; }
+    const SkIRect* data() const;
+
+    void reset(Mode = Mode::kExclusive);
+    GrWindowRectangles& operator=(const GrWindowRectangles&);
+
+    SkIRect& addWindow(const SkIRect& window) { return this->addWindow() = window; }
+    SkIRect& addWindow();
+
+    bool operator!=(const GrWindowRectangles& that) const { return !(*this == that); }
+    bool operator==(const GrWindowRectangles&) const;
+
+private:
+    constexpr static int kNumLocalWindows = 1;
+    struct Rec;
+
+    const Rec* rec() const { return fCount <= kNumLocalWindows ? nullptr : fRec; }
+
+    Mode      fMode;
+    uint8_t   fCount;
+    union {
+        SkIRect   fLocalWindows[kNumLocalWindows]; // If fCount <= kNumLocalWindows.
+        Rec*      fRec;                            // If fCount > kNumLocalWindows.
+    };
+};
+
+struct GrWindowRectangles::Rec : public GrNonAtomicRef<Rec> {
+    Rec(const SkIRect* windows, int numWindows) {
+        SkASSERT(numWindows < kMaxWindows);
+        memcpy(fData, windows, sizeof(SkIRect) * numWindows);
+    }
+
+    SkIRect fData[kMaxWindows];
+};
+
+inline const SkIRect* GrWindowRectangles::data() const {
+    return fCount <= kNumLocalWindows ? fLocalWindows : fRec->fData;
+}
+
+inline void GrWindowRectangles::reset(Mode mode) {
+    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));
+    } else {
+        fRec = SkRef(that.fRec);
+    }
+    return *this;
+}
+
+inline SkIRect& GrWindowRectangles::addWindow() {
+    SkASSERT(fCount < kMaxWindows);
+    if (fCount < kNumLocalWindows) {
+        return fLocalWindows[fCount++];
+    }
+    if (fCount == kNumLocalWindows) {
+        fRec = new Rec(fLocalWindows, kNumLocalWindows);
+    } else if (!fRec->unique()) { // Simple copy-on-write.
+        fRec->unref();
+        fRec = new Rec(fRec->fData, fCount);
+    }
+    return fRec->fData[fCount++];
+}
+
+inline bool GrWindowRectangles::operator==(const GrWindowRectangles& that) const {
+    if (fMode != that.fMode || fCount != that.fCount) {
+        return false;
+    }
+    if (fCount > kNumLocalWindows && fRec == that.fRec) {
+        return true;
+    }
+    return !fCount || !memcmp(this->data(), that.data(), sizeof(SkIRect) * fCount);
+}
+
+#endif
index 4d856ac..75ae776 100644 (file)
@@ -463,8 +463,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
 
     if (ctxInfo.hasExtension("GL_EXT_window_rectangles")) {
         GR_GL_GetIntegerv(gli, GR_GL_MAX_WINDOW_RECTANGLES, &fMaxWindowRectangles);
-        // Protect ourselves against tracking huge amounts of window rectangle state.
-        fMaxWindowRectangles = SkTMin(31, fMaxWindowRectangles);
     }
 
     if (kPowerVR54x_GrGLRenderer == ctxInfo.renderer() ||
index 1300bb7..3e44138 100644 (file)
@@ -563,6 +563,7 @@ void GrGLGpu::onResetContext(uint32_t resetBits) {
 
     if (resetBits & kView_GrGLBackendState) {
         fHWScissorSettings.invalidate();
+        fHWWindowRects.invalidate();
         fHWViewport.invalidate();
     }
 
@@ -1995,6 +1996,37 @@ void GrGLGpu::flushScissor(const GrScissorState& scissorState,
     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.
+
+    if (!this->caps()->maxWindowRectangles() ||
+        fHWWindowRects.equal(rt->origin(), rt->getViewport(), windows)) {
+        return;
+
+    }
+
+    GrGLIRect glwindows[GrWindowRectangles::kMaxWindows];
+    const SkIRect* skwindows = windows.data();
+    for (int i = 0; i < windows.count(); ++i) {
+        glwindows[i].setRelativeTo(rt->getViewport(), skwindows[i], rt->origin());
+    }
+
+    GrGLenum glmode = (Mode::kExclusive == windows.mode()) ? GR_GL_EXCLUSIVE : GR_GL_INCLUSIVE;
+    GL_CALL(WindowRectangles(glmode, windows.count(), glwindows->asInts()));
+
+    fHWWindowRects.set(rt->origin(), rt->getViewport(), windows);
+}
+
+void GrGLGpu::disableWindowRectangles() {
+    if (!this->caps()->maxWindowRectangles() || fHWWindowRects.disabled()) {
+        return;
+    }
+    GL_CALL(WindowRectangles(GR_GL_EXCLUSIVE, 0, nullptr));
+    fHWWindowRects.setDisabled();
+}
+
 void GrGLGpu::flushMinSampleShading(float minSampleShading) {
     if (fHWMinSampleShading != minSampleShading) {
         if (minSampleShading > 0.0) {
@@ -2042,6 +2074,7 @@ bool GrGLGpu::flushGLState(const GrPipeline& pipeline, const GrPrimitiveProcesso
     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->flushHWAAState(glRT, pipeline.isHWAntialiasState(), !pipeline.getStencil().isDisabled());
 
     // This must come after textures are flushed because a texture may need
@@ -2171,6 +2204,7 @@ void GrGLGpu::clear(const SkIRect& rect, GrColor color, GrRenderTarget* target)
     GrScissorState scissorState;
     scissorState.set(rect);
     this->flushScissor(scissorState, glRT->getViewport(), glRT->origin());
+    this->disableWindowRectangles();
 
     GrGLfloat r, g, b, a;
     static const GrGLfloat scale255 = 1.f / 255.f;
@@ -2194,6 +2228,7 @@ void GrGLGpu::clearStencil(GrRenderTarget* target) {
     this->flushRenderTarget(glRT, &SkIRect::EmptyIRect());
 
     this->disableScissor();
+    this->disableWindowRectangles();
 
     GL_CALL(StencilMask(0xffffffff));
     GL_CALL(ClearStencil(0));
@@ -2233,6 +2268,7 @@ void GrGLGpu::clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTar
     GrScissorState scissorState;
     scissorState.set(rect);
     this->flushScissor(scissorState, glRT->getViewport(), glRT->origin());
+    this->disableWindowRectangles();
 
     GL_CALL(StencilMask((uint32_t) clipStencilMask));
     GL_CALL(ClearStencil(value));
@@ -2603,6 +2639,7 @@ void GrGLGpu::finishDrawTarget() {
          * it becomes safe to stop using this workaround once we start.
          */
         this->disableScissor();
+        this->disableWindowRectangles();
         // using PLS in the presence of MSAA results in GL_INVALID_OPERATION
         this->flushHWAAState(nullptr, false, false);
         SkASSERT(!fHWPLSEnabled);
@@ -2752,6 +2789,7 @@ void GrGLGpu::draw(const GrPipeline& pipeline,
         GL_CALL(Disable(GR_GL_SHADER_PIXEL_LOCAL_STORAGE));
         fHWPLSEnabled = false;
         this->disableScissor();
+        this->disableWindowRectangles();
     }
 
 #if SWAP_PER_DRAW
@@ -2848,6 +2886,7 @@ void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target) {
                 GrScissorState scissorState;
                 scissorState.set(dirtyRect);
                 this->flushScissor(scissorState, vp, rt->origin());
+                this->disableWindowRectangles();
                 GL_CALL(ResolveMultisampleFramebuffer());
             } else {
                 GrGLIRect r;
@@ -2859,6 +2898,7 @@ void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target) {
 
                 // BlitFrameBuffer respects the scissor, so disable it.
                 this->disableScissor();
+                this->disableWindowRectangles();
                 GL_CALL(BlitFramebuffer(r.fLeft, r.fBottom, right, top,
                                         r.fLeft, r.fBottom, right, top,
                                         GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
@@ -4091,6 +4131,7 @@ void GrGLGpu::drawDebugWireRect(GrRenderTarget* rt, const SkIRect& rect, GrColor
     this->flushDrawFace(GrDrawFace::kBoth);
     this->flushHWAAState(glRT, false, false);
     this->disableScissor();
+    this->disableWindowRectangles();
     GrStencilSettings stencil;
     stencil.setDisabled();
     this->flushStencil(stencil);
@@ -4179,6 +4220,7 @@ bool GrGLGpu::copySurfaceAsDraw(GrSurface* dst,
     this->flushDrawFace(GrDrawFace::kBoth);
     this->flushHWAAState(nullptr, false, false);
     this->disableScissor();
+    this->disableWindowRectangles();
     GrStencilSettings stencil;
     stencil.setDisabled();
     this->flushStencil(stencil);
@@ -4263,6 +4305,7 @@ bool GrGLGpu::copySurfaceAsBlitFramebuffer(GrSurface* dst,
 
     // BlitFrameBuffer respects the scissor, so disable it.
     this->disableScissor();
+    this->disableWindowRectangles();
 
     GrGLint srcY0;
     GrGLint srcY1;
@@ -4387,6 +4430,7 @@ bool GrGLGpu::generateMipmap(GrGLTexture* texture, bool gammaCorrect) {
     this->flushDrawFace(GrDrawFace::kBoth);
     this->flushHWAAState(nullptr, false, false);
     this->disableScissor();
+    this->disableWindowRectangles();
     GrStencilSettings stencil;
     stencil.setDisabled();
     this->flushStencil(stencil);
index 0356352..26c9588 100644 (file)
@@ -314,6 +314,9 @@ private:
     // disables the scissor
     void disableScissor();
 
+    void flushWindowRectangles(const GrWindowRectangles&, const GrGLRenderTarget*);
+    void disableWindowRectangles();
+
     void initFSAASupport();
 
     // determines valid stencil formats
@@ -419,6 +422,42 @@ private:
         }
     } fHWScissorSettings;
 
+    class {
+    public:
+        bool valid() const { return kInvalidOrigin != fOrigin; }
+        void invalidate() { fOrigin = kInvalidOrigin; }
+
+        bool disabled() const {
+            return this->valid() && Mode::kExclusive == fWindows.mode() && !fWindows.count();
+        }
+        void setDisabled() { fOrigin = kDefault_GrSurfaceOrigin, fWindows.reset(); }
+
+        bool equal(GrSurfaceOrigin org, const GrGLIRect& viewp,
+                   const GrWindowRectangles& windows) const {
+            if (!this->valid()) {
+                return false;
+            }
+            if (fWindows.count() && (fOrigin != org || fViewport != viewp)) {
+                return false;
+            }
+            return fWindows == windows;
+        }
+
+        void set(GrSurfaceOrigin org, const GrGLIRect& viewp, const GrWindowRectangles& windows) {
+            fOrigin = org;
+            fViewport = viewp;
+            fWindows = windows;
+        }
+
+    private:
+        typedef GrWindowRectangles::Mode Mode;
+        enum { kInvalidOrigin = -1 };
+
+        int                  fOrigin;
+        GrGLIRect            fViewport;
+        GrWindowRectangles   fWindows;
+    } fHWWindowRects;
+
     GrGLIRect                   fHWViewport;
 
     /**
index 41ac13b..a699ae3 100644 (file)
@@ -23,6 +23,20 @@ struct GrGLIRect {
     GrGLsizei fWidth;
     GrGLsizei fHeight;
 
+    /**
+     *  cast-safe way to treat the rect as an array of (4) ints.
+     */
+    const int* asInts() const {
+        return &fLeft;
+
+        GR_STATIC_ASSERT(0 == offsetof(GrGLIRect, fLeft));
+        GR_STATIC_ASSERT(4 == offsetof(GrGLIRect, fBottom));
+        GR_STATIC_ASSERT(8 == offsetof(GrGLIRect, fWidth));
+        GR_STATIC_ASSERT(12 == offsetof(GrGLIRect, fHeight));
+        GR_STATIC_ASSERT(16 == sizeof(GrGLIRect)); // For an array of GrGLIRect.
+    }
+    int* asInts() { return &fLeft; }
+
     void pushToGLViewport(const GrGLInterface* gl) const {
         GR_GL_CALL(gl, Viewport(fLeft, fBottom, fWidth, fHeight));
     }
@@ -39,6 +53,11 @@ struct GrGLIRect {
     // sometimes we have a SkIRect from the client that we
     // want to simultaneously make relative to GL's viewport
     // and (optionally) convert from top-down to bottom-up.
+    void setRelativeTo(const GrGLIRect& glViewport, const SkIRect& devRect, GrSurfaceOrigin org) {
+        this->setRelativeTo(glViewport, devRect.x(), devRect.y(), devRect.width(), devRect.height(),
+                            org);
+    }
+
     void setRelativeTo(const GrGLIRect& glRect,
                        int leftOffset,
                        int topOffset,
diff --git a/tests/WindowRectanglesTest.cpp b/tests/WindowRectanglesTest.cpp
new file mode 100644 (file)
index 0000000..d569f1d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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 "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrWindowRectangles.h"
+
+static SkIRect next_irect(SkRandom& r) {
+    return {r.nextS(), r.nextS(), r.nextS(), r.nextS()};
+}
+
+DEF_TEST(WindowRectangles, reporter) {
+    SkRandom r;
+
+    SkIRect windowData[GrWindowRectangles::kMaxWindows];
+    for (int i = 0; i < GrWindowRectangles::kMaxWindows; ++i) {
+        windowData[i] = next_irect(r);
+    }
+
+    GrWindowRectangles wr;
+    for (int i = 0; i < GrWindowRectangles::kMaxWindows - 1; ++i) {
+        REPORTER_ASSERT(reporter, wr.count() == i);
+        REPORTER_ASSERT(reporter, !memcmp(wr.data(), windowData, i * sizeof(SkIRect)));
+
+        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)));
+
+        wr.addWindow(windowData[i]);
+    }
+
+    SkASSERT(wr.count() == GrWindowRectangles::kMaxWindows - 1);
+    {
+        GrWindowRectangles A(wr), B(wr);
+        REPORTER_ASSERT(reporter, B == A);
+        REPORTER_ASSERT(reporter, B.data() == A.data()); // Should use copy-on-write.
+
+        A.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]);
+        REPORTER_ASSERT(reporter, B.data() != A.data());
+        REPORTER_ASSERT(reporter, B != A);
+
+        B.addWindow(SkIRect::MakeLargest());
+        REPORTER_ASSERT(reporter, B != A);
+
+        REPORTER_ASSERT(reporter, !memcmp(A.data(), windowData,
+                                          GrWindowRectangles::kMaxWindows * sizeof(SkIRect)));
+        REPORTER_ASSERT(reporter, !memcmp(B.data(), windowData,
+                                          (GrWindowRectangles::kMaxWindows - 1) * sizeof(SkIRect)));
+        REPORTER_ASSERT(reporter,
+                        B.data()[GrWindowRectangles::kMaxWindows - 1] == SkIRect::MakeLargest());
+    }
+    {
+        GrWindowRectangles A(wr), B(wr);
+        REPORTER_ASSERT(reporter, B == A);
+        REPORTER_ASSERT(reporter, B.data() == A.data()); // Should use copy-on-write.
+
+        A.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]);
+        B.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]);
+        REPORTER_ASSERT(reporter, B == A);
+        REPORTER_ASSERT(reporter, B.data() != A.data());
+        REPORTER_ASSERT(reporter, !memcmp(B.data(), A.data(),
+                                          GrWindowRectangles::kMaxWindows * sizeof(SkIRect)));
+        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