'<(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',
#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.
*/
--- /dev/null
+/*
+ * 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
#include "GrCaps.h"
#include "GrContextOptions.h"
+#include "GrWindowRectangles.h"
GrShaderCaps::GrShaderCaps() {
fShaderDerivativeSupport = false;
}
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) {
} else {
fMaxTileSize = options.fMaxTileSizeOverride;
}
- this->onApplyOptionsOverrides(options);
+ if (fMaxWindowRectangles > GrWindowRectangles::kMaxWindows) {
+ fMaxWindowRectangles = GrWindowRectangles::kMaxWindows;
+ }
}
static SkString map_flags_to_string(uint32_t flags) {
#include "GrClip.h"
+#include "GrAppliedClip.h"
#include "GrDrawContext.h"
void GrNoClip::getConservativeBounds(int width, int height, SkIRect* devResult,
#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"
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
#include "GrDrawTarget.h"
+#include "GrAppliedClip.h"
#include "GrAuditTrail.h"
#include "GrCaps.h"
#include "GrDrawContext.h"
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,
}
#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);
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,
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 ||
#include "GrProgramDesc.h"
#include "GrStencilSettings.h"
#include "GrTypesPriv.h"
+#include "GrWindowRectangles.h"
#include "SkMatrix.h"
#include "SkRefCnt.h"
const GrCaps* fCaps;
GrPipelineOptimizations fOpts;
const GrScissorState* fScissor;
+ const GrWindowRectangles* fWindowRects;
bool fHasStencilClip;
GrXferProcessor::DstTexture fDstTexture;
};
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 {
typedef GrPendingProgramElement<const GrXferProcessor> ProgramXferProcessor;
RenderTarget fRenderTarget;
GrScissorState fScissorState;
+ GrWindowRectangles fWindowRects;
GrStencilSettings fStencilSettings;
GrDrawFace fDrawFace;
uint32_t fFlags;
--- /dev/null
+/*
+ * 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
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() ||
if (resetBits & kView_GrGLBackendState) {
fHWScissorSettings.invalidate();
+ fHWWindowRects.invalidate();
fHWViewport.invalidate();
}
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) {
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
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;
this->flushRenderTarget(glRT, &SkIRect::EmptyIRect());
this->disableScissor();
+ this->disableWindowRectangles();
GL_CALL(StencilMask(0xffffffff));
GL_CALL(ClearStencil(0));
GrScissorState scissorState;
scissorState.set(rect);
this->flushScissor(scissorState, glRT->getViewport(), glRT->origin());
+ this->disableWindowRectangles();
GL_CALL(StencilMask((uint32_t) clipStencilMask));
GL_CALL(ClearStencil(value));
* 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);
GL_CALL(Disable(GR_GL_SHADER_PIXEL_LOCAL_STORAGE));
fHWPLSEnabled = false;
this->disableScissor();
+ this->disableWindowRectangles();
}
#if SWAP_PER_DRAW
GrScissorState scissorState;
scissorState.set(dirtyRect);
this->flushScissor(scissorState, vp, rt->origin());
+ this->disableWindowRectangles();
GL_CALL(ResolveMultisampleFramebuffer());
} else {
GrGLIRect r;
// 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));
this->flushDrawFace(GrDrawFace::kBoth);
this->flushHWAAState(glRT, false, false);
this->disableScissor();
+ this->disableWindowRectangles();
GrStencilSettings stencil;
stencil.setDisabled();
this->flushStencil(stencil);
this->flushDrawFace(GrDrawFace::kBoth);
this->flushHWAAState(nullptr, false, false);
this->disableScissor();
+ this->disableWindowRectangles();
GrStencilSettings stencil;
stencil.setDisabled();
this->flushStencil(stencil);
// BlitFrameBuffer respects the scissor, so disable it.
this->disableScissor();
+ this->disableWindowRectangles();
GrGLint srcY0;
GrGLint srcY1;
this->flushDrawFace(GrDrawFace::kBoth);
this->flushHWAAState(nullptr, false, false);
this->disableScissor();
+ this->disableWindowRectangles();
GrStencilSettings stencil;
stencil.setDisabled();
this->flushStencil(stencil);
// disables the scissor
void disableScissor();
+ void flushWindowRectangles(const GrWindowRectangles&, const GrGLRenderTarget*);
+ void disableWindowRectangles();
+
void initFSAASupport();
// determines valid stencil formats
}
} 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;
/**
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));
}
// 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,
--- /dev/null
+/*
+ * 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