Only use scissor state for native clears

This is the first of many CLs that progressively refine the clipping,
scissoring, and clearing APIs.

The series of changes focus on simplifying the clear APIs, consolidating
clip intersection logic, and moving towards a more explicitly sized
render target context (where confusion between approx-fit and exact-fit
is handled externally, although I don't take it that far).

Next step will be to propagate the simpler calls up to GrRTC.

Bug:skia:10205
Change-Id: Idb0c58a63b7a3950a92604dd4c03536d668be7c4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290123
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp
index 66e346b..07b4eee 100644
--- a/src/gpu/GrOpsRenderPass.cpp
+++ b/src/gpu/GrOpsRenderPass.cpp
@@ -11,12 +11,13 @@
 #include "include/gpu/GrContext.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrContextPriv.h"
-#include "src/gpu/GrFixedClip.h"
+#include "src/gpu/GrCpuBuffer.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrPrimitiveProcessor.h"
 #include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTarget.h"
 #include "src/gpu/GrRenderTargetPriv.h"
+#include "src/gpu/GrScissorState.h"
 #include "src/gpu/GrSimpleMesh.h"
 #include "src/gpu/GrTexturePriv.h"
 
@@ -37,21 +38,22 @@
     this->resetActiveBuffers();
 }
 
-void GrOpsRenderPass::clear(const GrFixedClip& clip, const SkPMColor4f& color) {
+void GrOpsRenderPass::clear(const GrScissorState& scissor, const SkPMColor4f& color) {
     SkASSERT(fRenderTarget);
     // A clear at this level will always be a true clear, so make sure clears were not supposed to
     // be redirected to draws instead
     SkASSERT(!this->gpu()->caps()->performColorClearsAsDraws());
-    SkASSERT(!clip.scissorEnabled() || !this->gpu()->caps()->performPartialClearsAsDraws());
+    SkASSERT(!scissor.enabled() || !this->gpu()->caps()->performPartialClearsAsDraws());
     fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
-    this->onClear(clip, color);
+    this->onClear(scissor, color);
 }
 
-void GrOpsRenderPass::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
+void GrOpsRenderPass::clearStencilClip(const GrScissorState& scissor, bool insideStencilMask) {
     // As above, make sure the stencil clear wasn't supposed to be a draw rect with stencil settings
     SkASSERT(!this->gpu()->caps()->performStencilClearsAsDraws());
+    SkASSERT(!scissor.enabled() || !this->gpu()->caps()->performPartialClearsAsDraws());
     fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
-    this->onClearStencilClip(clip, insideStencilMask);
+    this->onClearStencilClip(scissor, insideStencilMask);
 }
 
 void GrOpsRenderPass::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
diff --git a/src/gpu/GrOpsRenderPass.h b/src/gpu/GrOpsRenderPass.h
index 959d1a2..ec7960f 100644
--- a/src/gpu/GrOpsRenderPass.h
+++ b/src/gpu/GrOpsRenderPass.h
@@ -13,12 +13,12 @@
 #include "src/gpu/ops/GrDrawOp.h"
 
 class GrOpFlushState;
-class GrFixedClip;
 class GrGpu;
 class GrPipeline;
 class GrPrimitiveProcessor;
 class GrProgramInfo;
 class GrRenderTarget;
+class GrScissorState;
 class GrSemaphore;
 struct SkIRect;
 struct SkRect;
@@ -120,11 +120,17 @@
     virtual void inlineUpload(GrOpFlushState*, GrDeferredTextureUploadFn&) = 0;
 
     /**
-     * Clear the owned render target. Ignores the draw state and clip.
+     * Clear the owned render target. Clears the full target if 'scissor' is disabled, otherwise it
+     * is restricted to 'scissor'. Must check caps.performPartialClearsAsDraws() before using an
+     * enabled scissor test; must check caps.performColorClearsAsDraws() before using this at all.
      */
-    void clear(const GrFixedClip&, const SkPMColor4f&);
+    void clear(const GrScissorState& scissor, const SkPMColor4f&);
 
-    void clearStencilClip(const GrFixedClip&, bool insideStencilMask);
+    /**
+     * Same as clear() but modifies the stencil; check caps.performStencilClearsAsDraws() and
+     * caps.performPartialClearsAsDraws().
+     */
+    void clearStencilClip(const GrScissorState& scissor, bool insideStencilMask);
 
     /**
      * Executes the SkDrawable object for the underlying backend.
@@ -189,8 +195,8 @@
     virtual void onDrawIndexedIndirect(const GrBuffer*, size_t offset, int drawCount) {
         SK_ABORT("Not implemented.");  // Only called if caps.nativeDrawIndirectSupport().
     }
-    virtual void onClear(const GrFixedClip&, const SkPMColor4f&) = 0;
-    virtual void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) = 0;
+    virtual void onClear(const GrScissorState&, const SkPMColor4f&) = 0;
+    virtual void onClearStencilClip(const GrScissorState&, bool insideStencilMask) = 0;
     virtual void onExecuteDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) {}
 
     enum class DrawPipelineStatus {
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 0990e76..be418f6 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -558,7 +558,7 @@
                     fContext, SkIRect::MakeEmpty(), color, /* fullscreen */ true));
         }
     } else {
-        if (this->caps()->performPartialClearsAsDraws()) {
+        if (this->caps()->performPartialClearsAsDraws() || clip.hasWindowRectangles()) {
             // performPartialClearsAsDraws() also returns true if any clear has to be a draw.
             GrPaint paint;
             clear_to_grpaint(color, &paint);
@@ -567,7 +567,7 @@
                             GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
                                                         SkRect::Make(clip.scissorRect())));
         } else {
-            std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip, color,
+            std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip.scissorState(), color,
                                                      this->asSurfaceProxy()));
             // This version of the clear op factory can return null if the clip doesn't intersect
             // with the surface proxy's boundary
@@ -979,18 +979,23 @@
 void GrRenderTargetContext::internalStencilClear(const GrFixedClip& clip, bool insideStencilMask) {
     this->setNeedsStencil(/* useMixedSamplesIfNotMSAA = */ false);
 
-    if (this->caps()->performStencilClearsAsDraws()) {
+    bool clearWithDraw = this->caps()->performStencilClearsAsDraws() ||
+                         (clip.scissorEnabled() && this->caps()->performPartialClearsAsDraws());
+    // TODO(michaelludwig): internalStencilClear will eventually just take a GrScissorState so
+    // we won't need to check window rectangles here.
+    if (clearWithDraw || clip.hasWindowRectangles()) {
         const GrUserStencilSettings* ss = GrStencilSettings::SetClipBitSettings(insideStencilMask);
-        SkRect rtRect = SkRect::MakeWH(this->width(), this->height());
+        SkRect rect = clip.scissorEnabled() ? SkRect::Make(clip.scissorRect())
+                                            : SkRect::MakeWH(this->width(), this->height());
 
         // Configure the paint to have no impact on the color buffer
         GrPaint paint;
         paint.setXPFactory(GrDisableColorXPFactory::Get());
         this->addDrawOp(clip, GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
-                                                          rtRect, ss));
+                                                          rect, ss));
     } else {
-        std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(fContext, clip, insideStencilMask,
-                                                            this->asRenderTargetProxy()));
+        std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(
+                fContext, clip.scissorState(), insideStencilMask, this->asRenderTargetProxy()));
         if (!op) {
             return;
         }
diff --git a/src/gpu/d3d/GrD3DCommandList.cpp b/src/gpu/d3d/GrD3DCommandList.cpp
index aa9825c..fd06a98 100644
--- a/src/gpu/d3d/GrD3DCommandList.cpp
+++ b/src/gpu/d3d/GrD3DCommandList.cpp
@@ -7,6 +7,7 @@
 
 #include "src/gpu/d3d/GrD3DCommandList.h"
 
+#include "src/gpu/GrScissorState.h"
 #include "src/gpu/d3d/GrD3DBuffer.h"
 #include "src/gpu/d3d/GrD3DGpu.h"
 #include "src/gpu/d3d/GrD3DPipelineState.h"
@@ -296,12 +297,13 @@
 
 void GrD3DDirectCommandList::clearRenderTargetView(GrD3DRenderTarget* renderTarget,
                                                    const SkPMColor4f& color,
-                                                   const GrFixedClip& clip) {
+                                                   const GrScissorState& scissor) {
+    SkASSERT(!scissor.enabled()); // no cliprects for now
     this->addingWork();
     this->addResource(renderTarget->resource());
     fCommandList->ClearRenderTargetView(renderTarget->colorRenderTargetView(),
                                         color.vec(),
-                                        0, NULL); // no cliprects for now
+                                        0, NULL);
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/d3d/GrD3DCommandList.h b/src/gpu/d3d/GrD3DCommandList.h
index ee06b49..6a00ded 100644
--- a/src/gpu/d3d/GrD3DCommandList.h
+++ b/src/gpu/d3d/GrD3DCommandList.h
@@ -22,7 +22,7 @@
 class GrD3DRootSignature;
 class GrD3DTextureResource;
 
-class GrFixedClip;
+class GrScissorState;
 
 class GrD3DCommandList {
 public:
@@ -134,7 +134,7 @@
                               unsigned int startInstance);
 
     void clearRenderTargetView(GrD3DRenderTarget* renderTarget, const SkPMColor4f& color,
-                               const GrFixedClip& clip);
+                               const GrScissorState& scissor);
 private:
     GrD3DDirectCommandList(gr_cp<ID3D12CommandAllocator> allocator,
                            gr_cp<ID3D12GraphicsCommandList> commandList);
diff --git a/src/gpu/d3d/GrD3DGpu.cpp b/src/gpu/d3d/GrD3DGpu.cpp
index e722b37..c41ae08 100644
--- a/src/gpu/d3d/GrD3DGpu.cpp
+++ b/src/gpu/d3d/GrD3DGpu.cpp
@@ -563,12 +563,12 @@
     return true;
 }
 
-void GrD3DGpu::clear(const GrFixedClip& clip, const SkPMColor4f& color, GrRenderTarget* rt) {
+void GrD3DGpu::clear(const GrScissorState& scissor, const SkPMColor4f& color, GrRenderTarget* rt) {
     GrD3DRenderTarget* d3dRT = static_cast<GrD3DRenderTarget*>(rt);
 
     d3dRT->setResourceState(this, D3D12_RESOURCE_STATE_RENDER_TARGET);
 
-    fCurrentDirectCommandList->clearRenderTargetView(d3dRT, color, clip);
+    fCurrentDirectCommandList->clearRenderTargetView(d3dRT, color, scissor);
 }
 
 static bool check_resource_info(const GrD3DTextureResourceInfo& info) {
diff --git a/src/gpu/d3d/GrD3DGpu.h b/src/gpu/d3d/GrD3DGpu.h
index be8fbc5..4100893 100644
--- a/src/gpu/d3d/GrD3DGpu.h
+++ b/src/gpu/d3d/GrD3DGpu.h
@@ -99,7 +99,7 @@
         return nullptr;
     }
 
-    void clear(const GrFixedClip& clip, const SkPMColor4f& color, GrRenderTarget*);
+    void clear(const GrScissorState& scissor, const SkPMColor4f& color, GrRenderTarget*);
 
     void submit(GrOpsRenderPass* renderPass) override;
 
diff --git a/src/gpu/d3d/GrD3DOpsRenderPass.cpp b/src/gpu/d3d/GrD3DOpsRenderPass.cpp
index e6a8344..18309da 100644
--- a/src/gpu/d3d/GrD3DOpsRenderPass.cpp
+++ b/src/gpu/d3d/GrD3DOpsRenderPass.cpp
@@ -46,8 +46,7 @@
 
 void GrD3DOpsRenderPass::onBegin() {
     if (GrLoadOp::kClear == fColorLoadOp) {
-        GrFixedClip clip;
-        fGpu->clear(clip, fClearColor, fRenderTarget);
+        fGpu->clear(GrScissorState(), fClearColor, fRenderTarget);
     }
 }
 
@@ -234,6 +233,6 @@
     fGpu->stats()->incNumDraws();
 }
 
-void GrD3DOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
-    fGpu->clear(clip, color, fRenderTarget);
+void GrD3DOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
+    fGpu->clear(scissor, color, fRenderTarget);
 }
diff --git a/src/gpu/d3d/GrD3DOpsRenderPass.h b/src/gpu/d3d/GrD3DOpsRenderPass.h
index 7ec4181..51a385e 100644
--- a/src/gpu/d3d/GrD3DOpsRenderPass.h
+++ b/src/gpu/d3d/GrD3DOpsRenderPass.h
@@ -56,9 +56,9 @@
     void onDrawIndirect(const GrBuffer*, size_t offset, int drawCount) override {}
     void onDrawIndexedIndirect(const GrBuffer*, size_t offset, int drawCount) override {}
 
-    void onClear(const GrFixedClip&, const SkPMColor4f& color) override;
+    void onClear(const GrScissorState& scissor, const SkPMColor4f& color) override;
 
-    void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) override {}
+    void onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) override {}
 
     GrD3DGpu* fGpu;
 
diff --git a/src/gpu/dawn/GrDawnOpsRenderPass.cpp b/src/gpu/dawn/GrDawnOpsRenderPass.cpp
index 5857ac6..1b0db7a 100644
--- a/src/gpu/dawn/GrDawnOpsRenderPass.cpp
+++ b/src/gpu/dawn/GrDawnOpsRenderPass.cpp
@@ -94,12 +94,15 @@
     fGpu->appendCommandBuffer(fEncoder.Finish());
 }
 
-void GrDawnOpsRenderPass::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
+void GrDawnOpsRenderPass::onClearStencilClip(const GrScissorState& scissor,
+                                             bool insideStencilMask) {
+    SkASSERT(!scissor.enabled());
     fPassEncoder.EndPass();
     fPassEncoder = beginRenderPass(wgpu::LoadOp::Load, wgpu::LoadOp::Clear);
 }
 
-void GrDawnOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
+void GrDawnOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
+    SkASSERT(!scissor.enabled());
     fPassEncoder.EndPass();
     fPassEncoder = beginRenderPass(wgpu::LoadOp::Clear, wgpu::LoadOp::Load);
 }
diff --git a/src/gpu/dawn/GrDawnOpsRenderPass.h b/src/gpu/dawn/GrDawnOpsRenderPass.h
index 8ab0c77..1f7b70f 100644
--- a/src/gpu/dawn/GrDawnOpsRenderPass.h
+++ b/src/gpu/dawn/GrDawnOpsRenderPass.h
@@ -51,9 +51,9 @@
     void onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance,
                                 int baseVertex) override;
 
-    void onClear(const GrFixedClip&, const SkPMColor4f& color) override;
+    void onClear(const GrScissorState& scissor, const SkPMColor4f& color) override;
 
-    void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) override;
+    void onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) override;
 
     struct InlineUploadInfo {
         InlineUploadInfo(GrOpFlushState* state, const GrDeferredTextureUploadFn& upload)
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index ef000ad..7813e40 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1869,51 +1869,29 @@
     return bufferState->fGLTarget;
 }
 
-void GrGLGpu::clear(const GrFixedClip& clip, const SkPMColor4f& color,
+void GrGLGpu::clear(const GrScissorState& scissor, const SkPMColor4f& color,
                     GrRenderTarget* target, GrSurfaceOrigin origin) {
     // parent class should never let us get here with no RT
     SkASSERT(target);
     SkASSERT(!this->caps()->performColorClearsAsDraws());
-    SkASSERT(!clip.scissorEnabled() || !this->caps()->performPartialClearsAsDraws());
+    SkASSERT(!scissor.enabled() || !this->caps()->performPartialClearsAsDraws());
 
     this->handleDirtyContext();
 
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
 
-    if (clip.scissorEnabled()) {
-        this->flushRenderTarget(glRT, origin, clip.scissorRect());
+    if (scissor.enabled()) {
+        this->flushRenderTarget(glRT, origin, scissor.rect());
     } else {
         this->flushRenderTarget(glRT);
     }
-    this->flushScissor(clip.scissorState(), glRT->width(), glRT->height(), origin);
-    this->flushWindowRectangles(clip.windowRectsState(), glRT, origin);
+    this->flushScissor(scissor, glRT->width(), glRT->height(), origin);
+    this->disableWindowRectangles();
     this->flushColorWrite(true);
     this->flushClearColor(color);
     GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
 }
 
-void GrGLGpu::clearStencil(GrRenderTarget* target, int clearValue) {
-    SkASSERT(!this->caps()->performStencilClearsAsDraws());
-
-    if (!target) {
-        return;
-    }
-
-    // This should only be called internally when we know we have a stencil buffer.
-    SkASSERT(target->renderTargetPriv().getStencilAttachment());
-
-    GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
-    this->flushRenderTargetNoColorWrites(glRT);
-
-    this->flushScissorTest(GrScissorTest::kDisabled);
-    this->disableWindowRectangles();
-
-    GL_CALL(StencilMask(0xffffffff));
-    GL_CALL(ClearStencil(clearValue));
-    GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
-    fHWStencilSettings.invalidate();
-}
-
 static bool use_tiled_rendering(const GrGLCaps& glCaps,
                                 const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilLoadStore) {
     // Only use the tiled rendering extension if we can explicitly clear and discard the stencil.
@@ -2011,11 +1989,11 @@
     SkDEBUGCODE(fIsExecutingCommandBuffer_DebugOnly = false);
 }
 
-void GrGLGpu::clearStencilClip(const GrFixedClip& clip,
-                               bool insideStencilMask,
+void GrGLGpu::clearStencilClip(const GrScissorState& scissor, bool insideStencilMask,
                                GrRenderTarget* target, GrSurfaceOrigin origin) {
     SkASSERT(target);
     SkASSERT(!this->caps()->performStencilClearsAsDraws());
+    SkASSERT(!scissor.enabled() || !this->caps()->performPartialClearsAsDraws());
     this->handleDirtyContext();
 
     GrStencilAttachment* sb = target->renderTargetPriv().getStencilAttachment();
@@ -2046,8 +2024,8 @@
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
     this->flushRenderTargetNoColorWrites(glRT);
 
-    this->flushScissor(clip.scissorState(), glRT->width(), glRT->height(), origin);
-    this->flushWindowRectangles(clip.windowRectsState(), glRT, origin);
+    this->flushScissor(scissor, glRT->width(), glRT->height(), origin);
+    this->disableWindowRectangles();
 
     GL_CALL(StencilMask((uint32_t) clipStencilMask));
     GL_CALL(ClearStencil(value));
@@ -2281,11 +2259,10 @@
     fHWBoundRenderTargetUniqueID.makeInvalid();
     if (GrGLCaps::kES_Apple_MSFBOType == this->glCaps().msFBOType()) {
         // Apple's extension uses the scissor as the blit bounds.
-        GrScissorState scissorState;
-        scissorState.set(resolveRect);
         // Passing in kTopLeft_GrSurfaceOrigin will make sure no transformation of the rect
         // happens inside flushScissor since resolveRect is already in native device coordinates.
-        this->flushScissor(scissorState, rt->width(), rt->height(), kTopLeft_GrSurfaceOrigin);
+        this->flushScissor(GrScissorState(resolveRect), rt->width(), rt->height(),
+                           kTopLeft_GrSurfaceOrigin);
         this->disableWindowRectangles();
         GL_CALL(ResolveMultisampleFramebuffer());
     } else {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 3f22309..ba0cadf 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -104,18 +104,14 @@
     // The GrGLOpsRenderPass does not buffer up draws before submitting them to the gpu.
     // Thus this is the implementation of the clear call for the corresponding passthrough function
     // on GrGLOpsRenderPass.
-    void clear(const GrFixedClip&, const SkPMColor4f&, GrRenderTarget*, GrSurfaceOrigin);
+    void clear(const GrScissorState&, const SkPMColor4f&, GrRenderTarget*, GrSurfaceOrigin);
 
     // The GrGLOpsRenderPass does not buffer up draws before submitting them to the gpu.
     // Thus this is the implementation of the clearStencil call for the corresponding passthrough
     // function on GrGLOpsrenderPass.
-    void clearStencilClip(const GrFixedClip&, bool insideStencilMask,
+    void clearStencilClip(const GrScissorState&, bool insideStencilMask,
                           GrRenderTarget*, GrSurfaceOrigin);
 
-    // FIXME (michaelludwig): Can this go away and just use clearStencilClip() + marking the
-    // stencil buffer as not dirty?
-    void clearStencil(GrRenderTarget*, int clearValue);
-
     void beginCommandBuffer(GrRenderTarget*, const SkIRect& bounds, GrSurfaceOrigin,
                             const GrOpsRenderPass::LoadAndStoreInfo& colorLoadStore,
                             const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilLoadStore);
diff --git a/src/gpu/gl/GrGLOpsRenderPass.cpp b/src/gpu/gl/GrGLOpsRenderPass.cpp
index 865d807..737d69b 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.cpp
+++ b/src/gpu/gl/GrGLOpsRenderPass.cpp
@@ -259,12 +259,10 @@
     }
 }
 
-void GrGLOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
-    fGpu->clear(clip, color, fRenderTarget, fOrigin);
+void GrGLOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
+    fGpu->clear(scissor, color, fRenderTarget, fOrigin);
 }
 
-void GrGLOpsRenderPass::onClearStencilClip(const GrFixedClip& clip,
-                                           bool insideStencilMask) {
-    fGpu->clearStencilClip(clip, insideStencilMask, fRenderTarget, fOrigin);
+void GrGLOpsRenderPass::onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) {
+    fGpu->clearStencilClip(scissor, insideStencilMask, fRenderTarget, fOrigin);
 }
-
diff --git a/src/gpu/gl/GrGLOpsRenderPass.h b/src/gpu/gl/GrGLOpsRenderPass.h
index b16dda1..038e558 100644
--- a/src/gpu/gl/GrGLOpsRenderPass.h
+++ b/src/gpu/gl/GrGLOpsRenderPass.h
@@ -69,8 +69,8 @@
     void onDrawIndirect(const GrBuffer* drawIndirectBuffer, size_t offset, int drawCount) override;
     void onDrawIndexedIndirect(const GrBuffer* drawIndirectBuffer, size_t offset,
                                int drawCount) override;
-    void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
-    void onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) override;
+    void onClear(const GrScissorState& scissor, const SkPMColor4f& color) override;
+    void onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) override;
 
     GrGLGpu* fGpu;
     SkIRect fContentBounds;
@@ -89,4 +89,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/mock/GrMockOpsRenderPass.h b/src/gpu/mock/GrMockOpsRenderPass.h
index 5a45a3e..703eac2 100644
--- a/src/gpu/mock/GrMockOpsRenderPass.h
+++ b/src/gpu/mock/GrMockOpsRenderPass.h
@@ -45,8 +45,10 @@
     void onDrawIndexedInstanced(int, int, int, int, int) override { this->dummyDraw(); }
     void onDrawIndirect(const GrBuffer*, size_t, int) override { this->dummyDraw(); }
     void onDrawIndexedIndirect(const GrBuffer*, size_t, int) override { this->dummyDraw(); }
-    void onClear(const GrFixedClip&, const SkPMColor4f&) override { this->markRenderTargetDirty(); }
-    void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) override {}
+    void onClear(const GrScissorState& scissor, const SkPMColor4f&) override {
+        this->markRenderTargetDirty();
+    }
+    void onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) override {}
     void dummyDraw() {
         this->markRenderTargetDirty();
         ++fNumDraws;
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.h b/src/gpu/mtl/GrMtlOpsRenderPass.h
index 77229ce..c4cf385 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.h
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.h
@@ -49,9 +49,9 @@
     void onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance,
                                 int baseVertex) override;
 
-    void onClear(const GrFixedClip& clip, const SkPMColor4f& color) override;
+    void onClear(const GrScissorState& scissor, const SkPMColor4f& color) override;
 
-    void onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) override;
+    void onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) override;
 
     void setupRenderPass(const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
                          const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo);
@@ -80,4 +80,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm
index 576a134..aaa394f 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.mm
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm
@@ -123,7 +123,10 @@
     return true;
 }
 
-void GrMtlOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
+void GrMtlOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
+    // Partial clears are not supported
+    SkASSERT(!scissor.enabled());
+
     // Ideally we should never end up here since all clears should either be done as draws or
     // load ops in metal. However, if a client inserts a wait op we need to handle it.
     fRenderPassDesc.colorAttachments[0].clearColor =
@@ -135,8 +138,9 @@
             fGpu->commandBuffer()->getRenderCommandEncoder(fRenderPassDesc, nullptr, this);
 }
 
-void GrMtlOpsRenderPass::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
-    SkASSERT(!clip.hasWindowRectangles());
+void GrMtlOpsRenderPass::onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) {
+    // Partial clears are not supported
+    SkASSERT(!scissor.enabled());
 
     GrStencilAttachment* sb = fRenderTarget->renderTargetPriv().getStencilAttachment();
     // this should only be called internally when we know we have a
diff --git a/src/gpu/ops/GrClearOp.cpp b/src/gpu/ops/GrClearOp.cpp
index 28529f4..dc1b939 100644
--- a/src/gpu/ops/GrClearOp.cpp
+++ b/src/gpu/ops/GrClearOp.cpp
@@ -15,17 +15,17 @@
 #include "src/gpu/GrRecordingContextPriv.h"
 
 std::unique_ptr<GrClearOp> GrClearOp::Make(GrRecordingContext* context,
-                                           const GrFixedClip& clip,
+                                           const GrScissorState& scissor,
                                            const SkPMColor4f& color,
                                            GrSurfaceProxy* dstProxy) {
     const SkIRect rect = SkIRect::MakeSize(dstProxy->dimensions());
-    if (clip.scissorEnabled() && !SkIRect::Intersects(clip.scissorRect(), rect)) {
+    if (scissor.enabled() && !SkIRect::Intersects(scissor.rect(), rect)) {
         return nullptr;
     }
 
     GrOpMemoryPool* pool = context->priv().opMemoryPool();
 
-    return pool->allocate<GrClearOp>(clip, color, dstProxy);
+    return pool->allocate<GrClearOp>(scissor, color, dstProxy);
 }
 
 std::unique_ptr<GrClearOp> GrClearOp::Make(GrRecordingContext* context,
@@ -39,27 +39,27 @@
     return pool->allocate<GrClearOp>(rect, color, fullScreen);
 }
 
-GrClearOp::GrClearOp(const GrFixedClip& clip, const SkPMColor4f& color, GrSurfaceProxy* proxy)
+GrClearOp::GrClearOp(const GrScissorState& scissor, const SkPMColor4f& color, GrSurfaceProxy* proxy)
         : INHERITED(ClassID())
-        , fClip(clip)
+        , fScissor(scissor)
         , fColor(color) {
     const SkIRect rtRect = SkIRect::MakeSize(proxy->dimensions());
-    if (fClip.scissorEnabled()) {
+    if (fScissor.enabled()) {
         // Don't let scissors extend outside the RT. This may improve op combining.
-        if (!fClip.intersect(rtRect)) {
+        if (!fScissor.intersect(rtRect)) {
             SkASSERT(0);  // should be caught upstream
-            fClip = GrFixedClip(SkIRect::MakeEmpty());
+            fScissor.set(SkIRect::MakeEmpty());
         }
 
-        if (proxy->isFunctionallyExact() && fClip.scissorRect() == rtRect) {
-            fClip.disableScissor();
+        if (proxy->isFunctionallyExact() && fScissor.rect() == rtRect) {
+            fScissor.setDisabled();
         }
     }
-    this->setBounds(SkRect::Make(fClip.scissorEnabled() ? fClip.scissorRect() : rtRect),
+    this->setBounds(SkRect::Make(fScissor.enabled() ? fScissor.rect() : rtRect),
                     HasAABloat::kNo, IsHairline::kNo);
 }
 
 void GrClearOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
     SkASSERT(state->opsRenderPass());
-    state->opsRenderPass()->clear(fClip, fColor);
+    state->opsRenderPass()->clear(fScissor, fColor);
 }
diff --git a/src/gpu/ops/GrClearOp.h b/src/gpu/ops/GrClearOp.h
index fbac02f..0857064 100644
--- a/src/gpu/ops/GrClearOp.h
+++ b/src/gpu/ops/GrClearOp.h
@@ -8,7 +8,7 @@
 #ifndef GrClearOp_DEFINED
 #define GrClearOp_DEFINED
 
-#include "src/gpu/GrFixedClip.h"
+#include "src/gpu/GrScissorState.h"
 #include "src/gpu/ops/GrOp.h"
 
 class GrOpFlushState;
@@ -19,7 +19,7 @@
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrClearOp> Make(GrRecordingContext* context,
-                                           const GrFixedClip& clip,
+                                           const GrScissorState& scissor,
                                            const SkPMColor4f& color,
                                            GrSurfaceProxy* dstProxy);
 
@@ -35,8 +35,8 @@
         SkString string;
         string.append(INHERITED::dumpInfo());
         string.appendf("Scissor [ ");
-        if (fClip.scissorEnabled()) {
-            const SkIRect& r = fClip.scissorRect();
+        if (fScissor.enabled()) {
+            const SkIRect& r = fScissor.rect();
             string.appendf("L: %d, T: %d, R: %d, B: %d", r.fLeft, r.fTop, r.fRight, r.fBottom);
         } else {
             string.append("disabled");
@@ -52,15 +52,15 @@
 private:
     friend class GrOpMemoryPool; // for ctors
 
-    GrClearOp(const GrFixedClip& clip, const SkPMColor4f& color, GrSurfaceProxy* proxy);
+    GrClearOp(const GrScissorState& scissor, const SkPMColor4f& color, GrSurfaceProxy* proxy);
 
     GrClearOp(const SkIRect& rect, const SkPMColor4f& color, bool fullScreen)
         : INHERITED(ClassID())
-        , fClip(GrFixedClip(rect))
+        , fScissor(rect)
         , fColor(color) {
 
         if (fullScreen) {
-            fClip.disableScissor();
+            fScissor.setDisabled();
         }
         this->setBounds(SkRect::Make(rect), HasAABloat::kNo, IsHairline::kNo);
     }
@@ -71,11 +71,8 @@
         // contains the old clear, or when the new clear is a subset of the old clear and is the
         // same color.
         GrClearOp* cb = t->cast<GrClearOp>();
-        if (fClip.windowRectsState() != cb->fClip.windowRectsState()) {
-            return CombineResult::kCannotCombine;
-        }
         if (cb->contains(this)) {
-            fClip = cb->fClip;
+            fScissor = cb->fScissor;
             fColor = cb->fColor;
             return CombineResult::kMerged;
         } else if (cb->fColor == fColor && this->contains(cb)) {
@@ -86,9 +83,8 @@
 
     bool contains(const GrClearOp* that) const {
         // The constructor ensures that scissor gets disabled on any clip that fills the entire RT.
-        return !fClip.scissorEnabled() ||
-               (that->fClip.scissorEnabled() &&
-                fClip.scissorRect().contains(that->fClip.scissorRect()));
+        return !fScissor.enabled() ||
+               (that->fScissor.enabled() && fScissor.rect().contains(that->fScissor.rect()));
     }
 
     void onPrePrepare(GrRecordingContext*,
@@ -100,8 +96,8 @@
 
     void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override;
 
-    GrFixedClip fClip;
-    SkPMColor4f fColor;
+    GrScissorState fScissor;
+    SkPMColor4f    fColor;
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/ops/GrClearStencilClipOp.cpp b/src/gpu/ops/GrClearStencilClipOp.cpp
index 7ca4063..dc26471 100644
--- a/src/gpu/ops/GrClearStencilClipOp.cpp
+++ b/src/gpu/ops/GrClearStencilClipOp.cpp
@@ -14,15 +14,15 @@
 #include "src/gpu/GrRecordingContextPriv.h"
 
 std::unique_ptr<GrOp> GrClearStencilClipOp::Make(GrRecordingContext* context,
-                                                 const GrFixedClip& clip,
+                                                 const GrScissorState& scissor,
                                                  bool insideStencilMask,
                                                  GrRenderTargetProxy* proxy) {
     GrOpMemoryPool* pool = context->priv().opMemoryPool();
 
-    return pool->allocate<GrClearStencilClipOp>(clip, insideStencilMask, proxy);
+    return pool->allocate<GrClearStencilClipOp>(scissor, insideStencilMask, proxy);
 }
 
 void GrClearStencilClipOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
     SkASSERT(state->opsRenderPass());
-    state->opsRenderPass()->clearStencilClip(fClip, fInsideStencilMask);
+    state->opsRenderPass()->clearStencilClip(fScissor, fInsideStencilMask);
 }
diff --git a/src/gpu/ops/GrClearStencilClipOp.h b/src/gpu/ops/GrClearStencilClipOp.h
index 68f825a..f185cde 100644
--- a/src/gpu/ops/GrClearStencilClipOp.h
+++ b/src/gpu/ops/GrClearStencilClipOp.h
@@ -8,8 +8,8 @@
 #ifndef GrClearStencilClipOp_DEFINED
 #define GrClearStencilClipOp_DEFINED
 
-#include "src/gpu/GrFixedClip.h"
 #include "src/gpu/GrRenderTargetProxy.h"
+#include "src/gpu/GrScissorState.h"
 #include "src/gpu/ops/GrOp.h"
 
 class GrOpFlushState;
@@ -20,7 +20,7 @@
     DEFINE_OP_CLASS_ID
 
     static std::unique_ptr<GrOp> Make(GrRecordingContext* context,
-                                      const GrFixedClip& clip,
+                                      const GrScissorState& scissor,
                                       bool insideStencilMask,
                                       GrRenderTargetProxy* proxy);
 
@@ -29,8 +29,8 @@
 #ifdef SK_DEBUG
     SkString dumpInfo() const override {
         SkString string("Scissor [");
-        if (fClip.scissorEnabled()) {
-            const SkIRect& r = fClip.scissorRect();
+        if (fScissor.enabled()) {
+            const SkIRect& r = fScissor.rect();
             string.appendf("L: %d, T: %d, R: %d, B: %d", r.fLeft, r.fTop, r.fRight, r.fBottom);
         } else {
             string.append("disabled");
@@ -44,13 +44,13 @@
 private:
     friend class GrOpMemoryPool; // for ctor
 
-    GrClearStencilClipOp(const GrFixedClip& clip, bool insideStencilMask,
+    GrClearStencilClipOp(const GrScissorState& scissor, bool insideStencilMask,
                          GrRenderTargetProxy* proxy)
             : INHERITED(ClassID())
-            , fClip(clip)
+            , fScissor(scissor)
             , fInsideStencilMask(insideStencilMask) {
         const SkRect& bounds =
-                fClip.scissorEnabled() ? SkRect::Make(fClip.scissorRect()) : proxy->getBoundsRect();
+                fScissor.enabled() ? SkRect::Make(fScissor.rect()) : proxy->getBoundsRect();
         this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
     }
 
@@ -63,8 +63,8 @@
 
     void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
 
-    const GrFixedClip fClip;
-    const bool        fInsideStencilMask;
+    const GrScissorState fScissor;
+    const bool           fInsideStencilMask;
 
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/vk/GrVkOpsRenderPass.cpp b/src/gpu/vk/GrVkOpsRenderPass.cpp
index 5eef9cc..5828af8 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.cpp
+++ b/src/gpu/vk/GrVkOpsRenderPass.cpp
@@ -265,14 +265,12 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrVkOpsRenderPass::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
+void GrVkOpsRenderPass::onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) {
     if (!fCurrentRenderPass) {
         SkASSERT(fGpu->isDeviceLost());
         return;
     }
 
-    SkASSERT(!clip.hasWindowRectangles());
-
     GrStencilAttachment* sb = fRenderTarget->renderTargetPriv().getStencilAttachment();
     // this should only be called internally when we know we have a
     // stencil buffer.
@@ -293,14 +291,13 @@
     VkClearRect clearRect;
     // Flip rect if necessary
     SkIRect vkRect;
-    if (!clip.scissorEnabled()) {
+    if (!scissor.enabled()) {
         vkRect.setXYWH(0, 0, fRenderTarget->width(), fRenderTarget->height());
     } else if (kBottomLeft_GrSurfaceOrigin != fOrigin) {
-        vkRect = clip.scissorRect();
+        vkRect = scissor.rect();
     } else {
-        const SkIRect& scissor = clip.scissorRect();
-        vkRect.setLTRB(scissor.fLeft, fRenderTarget->height() - scissor.fBottom,
-                       scissor.fRight, fRenderTarget->height() - scissor.fTop);
+        vkRect.setLTRB(scissor.rect().fLeft, fRenderTarget->height() - scissor.rect().fBottom,
+                       scissor.rect().fRight, fRenderTarget->height() - scissor.rect().fTop);
     }
 
     clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop };
@@ -321,15 +318,12 @@
     fCurrentCBIsEmpty = false;
 }
 
-void GrVkOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
+void GrVkOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
     if (!fCurrentRenderPass) {
         SkASSERT(fGpu->isDeviceLost());
         return;
     }
 
-    // parent class should never let us get here with no RT
-    SkASSERT(!clip.hasWindowRectangles());
-
     VkClearColorValue vkColor = {{color.fR, color.fG, color.fB, color.fA}};
 
     // If we end up in a situation where we are calling clear without a scissior then in general it
@@ -338,20 +332,19 @@
     // load op (e.g. if we needed to execute a wait op). Thus we also have the empty check here.
     // TODO: Make the waitOp a RenderTask instead so we can clear out the GrOpsTask for a clear. We
     // can then reenable this assert assuming we can't get messed up by a waitOp.
-    //SkASSERT(!fCurrentCBIsEmpty || clip.scissorEnabled());
+    //SkASSERT(!fCurrentCBIsEmpty || scissor);
 
     // We always do a sub rect clear with clearAttachments since we are inside a render pass
     VkClearRect clearRect;
     // Flip rect if necessary
     SkIRect vkRect;
-    if (!clip.scissorEnabled()) {
+    if (!scissor.enabled()) {
         vkRect.setXYWH(0, 0, fRenderTarget->width(), fRenderTarget->height());
     } else if (kBottomLeft_GrSurfaceOrigin != fOrigin) {
-        vkRect = clip.scissorRect();
+        vkRect = scissor.rect();
     } else {
-        const SkIRect& scissor = clip.scissorRect();
-        vkRect.setLTRB(scissor.fLeft, fRenderTarget->height() - scissor.fBottom,
-                       scissor.fRight, fRenderTarget->height() - scissor.fTop);
+        vkRect.setLTRB(scissor.rect().fLeft, fRenderTarget->height() - scissor.rect().fBottom,
+                       scissor.rect().fRight, fRenderTarget->height() - scissor.rect().fTop);
     }
     clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop };
     clearRect.rect.extent = { (uint32_t)vkRect.width(), (uint32_t)vkRect.height() };
@@ -682,4 +675,3 @@
     drawable->draw(info);
     fGpu->addDrawable(std::move(drawable));
 }
-
diff --git a/src/gpu/vk/GrVkOpsRenderPass.h b/src/gpu/vk/GrVkOpsRenderPass.h
index 53bb07e..ef666ec 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.h
+++ b/src/gpu/vk/GrVkOpsRenderPass.h
@@ -84,9 +84,9 @@
     void onDrawIndexedIndirect(const GrBuffer* drawIndirectBuffer, size_t offset,
                                int drawCount) override;
 
-    void onClear(const GrFixedClip&, const SkPMColor4f& color) override;
+    void onClear(const GrScissorState& scissor, const SkPMColor4f& color) override;
 
-    void onClearStencilClip(const GrFixedClip&, bool insideStencilMask) override;
+    void onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) override;
 
     void addAdditionalRenderPass(bool mustUseSecondaryCommandBuffer);