Add a new non-AA rect op that does not inherit from GrLegacyMeshDrawOp.

This uses a new helper class, GrSimpleMeshDrawOpHelper, which it uses to fullfill the GrMeshDrawOp contract and to construct its GrPipline when flushed. The helper is intended to be used such that the op only stores a GrProcessorSet if it is constructed with a "nontrivial" GrPaint. "Trivial" currently means no fragment processors and src-over blending. The helper allows the op subclass to specify whether it supports stenciling via a template parameter. The helper class is initially intended to be used for ops that don't have per-vertex colors and construct a single GrPipeline at flush time, though perhaps this can be relaxed in future changes.

On the microbenchmark "rotated_rects_bw_same_transparent_srcover" this produces a 18-20% reduction in time on my Z840 running Linux and 33% on my 2010 MacPro.

Bug: skia:
Change-Id: I9f655827a70bee585b0b0e1255371ffd995a0b80
Reviewed-on: https://skia-review.googlesource.com/14604
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index b2f8555..e66ab77 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -261,6 +261,8 @@
   "$_src/gpu/ops/GrMSAAPathRenderer.h",
   "$_src/gpu/ops/GrNonAAFillRectOp.h",
   "$_src/gpu/ops/GrNonAAFillRectOp.cpp",
+  "$_src/gpu/ops/GrNewNonAAFillRectOp.h",
+  "$_src/gpu/ops/GrNewNonAAFillRectOp.cpp",
   "$_src/gpu/ops/GrNonAAFillRectPerspectiveOp.cpp",
   "$_src/gpu/ops/GrNonAAStrokeRectOp.cpp",
   "$_src/gpu/ops/GrNonAAStrokeRectOp.h",
@@ -278,6 +280,7 @@
   "$_src/gpu/ops/GrSemaphoreOp.h",
   "$_src/gpu/ops/GrShadowRRectOp.cpp",
   "$_src/gpu/ops/GrShadowRRectOp.h",
+  "$_src/gpu/ops/GrSimpleMeshDrawOpHelper.h",
   "$_src/gpu/ops/GrSmallPathRenderer.cpp",
   "$_src/gpu/ops/GrSmallPathRenderer.h",
   "$_src/gpu/ops/GrStencilAndCoverPathRenderer.cpp",
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 4ba87fe..0fc7b7f 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -10,6 +10,7 @@
 
 #include "GrBufferAllocPool.h"
 #include "GrGpu.h"
+#include "SkArenaAlloc.h"
 #include "ops/GrMeshDrawOp.h"
 
 class GrGpuCommandBuffer;
@@ -94,6 +95,7 @@
     void reset() {
         fVertexPool.reset();
         fIndexPool.reset();
+        fPipelines.reset();
     }
 
     /** Additional data required on a per-op basis when executing GrDrawOps. */
@@ -110,16 +112,22 @@
         return *fOpArgs;
     }
 
+    template <typename... Args>
+    GrPipeline* allocPipeline(Args... args) {
+        return fPipelines.make<GrPipeline>(std::forward<Args>(args)...);
+    }
+
 private:
-    GrGpu*                                      fGpu;
-    GrResourceProvider*                         fResourceProvider;
-    GrGpuCommandBuffer*                         fCommandBuffer;
-    GrVertexBufferAllocPool                     fVertexPool;
-    GrIndexBufferAllocPool                      fIndexPool;
-    SkSTArray<4, GrDrawOp::DeferredUploadFn>    fAsapUploads;
-    GrDrawOpUploadToken                         fLastIssuedToken;
-    GrDrawOpUploadToken                         fLastFlushedToken;
-    DrawOpArgs*                                 fOpArgs;
+    GrGpu* fGpu;
+    GrResourceProvider* fResourceProvider;
+    GrGpuCommandBuffer* fCommandBuffer;
+    GrVertexBufferAllocPool fVertexPool;
+    GrIndexBufferAllocPool fIndexPool;
+    SkSTArray<4, GrDrawOp::DeferredUploadFn> fAsapUploads;
+    GrDrawOpUploadToken fLastIssuedToken;
+    GrDrawOpUploadToken fLastFlushedToken;
+    DrawOpArgs* fOpArgs;
+    SkArenaAlloc fPipelines{sizeof(GrPipeline) * 100};
 };
 
 /**
@@ -182,6 +190,7 @@
 protected:
     GrDrawOp* op() { return fOp; }
     GrOpFlushState* state() { return fState; }
+    const GrOpFlushState* state() const { return fState; }
 
 private:
     GrOpFlushState* fState;
@@ -211,6 +220,19 @@
         this->state()->putBackVertexSpace(vertices * vertexStride);
     }
 
+    GrRenderTarget* renderTarget() const { return this->state()->drawOpArgs().fRenderTarget; }
+
+    const GrAppliedClip* clip() const { return this->state()->drawOpArgs().fAppliedClip; }
+
+    const GrXferProcessor::DstTexture& dstTexture() const {
+        return this->state()->drawOpArgs().fDstTexture;
+    }
+
+    template <typename... Args>
+    GrPipeline* allocPipeline(Args... args) {
+        return this->state()->allocPipeline(std::forward<Args>(args)...);
+    }
+
 private:
     GrMeshDrawOp* meshDrawOp() { return static_cast<GrMeshDrawOp*>(this->op()); }
     typedef GrDrawOp::Target INHERITED;
diff --git a/src/gpu/GrPaint.cpp b/src/gpu/GrPaint.cpp
index 6c5a041..9bbeec5 100644
--- a/src/gpu/GrPaint.cpp
+++ b/src/gpu/GrPaint.cpp
@@ -12,11 +12,11 @@
 #include "effects/GrSimpleTextureEffect.h"
 
 void GrPaint::setPorterDuffXPFactory(SkBlendMode mode) {
-    fXPFactory = GrPorterDuffXPFactory::Get(mode);
+    this->setXPFactory(GrPorterDuffXPFactory::Get(mode));
 }
 
 void GrPaint::setCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage) {
-    fXPFactory = GrCoverageSetOpXPFactory::Get(regionOp, invertCoverage);
+    this->setXPFactory(GrCoverageSetOpXPFactory::Get(regionOp, invertCoverage));
 }
 
 void GrPaint::addColorTextureProcessor(GrResourceProvider* resourceProvider,
diff --git a/src/gpu/GrPaint.h b/src/gpu/GrPaint.h
index bcf6858..6fe561a 100644
--- a/src/gpu/GrPaint.h
+++ b/src/gpu/GrPaint.h
@@ -79,11 +79,14 @@
      * as such (with linear blending), and sRGB inputs to be filtered and decoded correctly.
      */
     void setGammaCorrect(bool gammaCorrect) {
-        setDisableOutputConversionToSRGB(!gammaCorrect);
-        setAllowSRGBInputs(gammaCorrect);
+        this->setDisableOutputConversionToSRGB(!gammaCorrect);
+        this->setAllowSRGBInputs(gammaCorrect);
     }
 
-    void setXPFactory(const GrXPFactory* xpFactory) { fXPFactory = xpFactory; }
+    void setXPFactory(const GrXPFactory* xpFactory) {
+        fXPFactory = xpFactory;
+        fTrivial &= !SkToBool(xpFactory);
+    }
 
     void setPorterDuffXPFactory(SkBlendMode mode);
 
@@ -96,6 +99,7 @@
         SkASSERT(fp);
         fUsesDistanceVectorField |= fp->usesDistanceVectorField();
         fColorFragmentProcessors.push_back(std::move(fp));
+        fTrivial = false;
     }
 
     /**
@@ -105,6 +109,7 @@
         SkASSERT(fp);
         fUsesDistanceVectorField |= fp->usesDistanceVectorField();
         fCoverageFragmentProcessors.push_back(std::move(fp));
+        fTrivial = false;
     }
 
     /**
@@ -143,6 +148,12 @@
      */
     bool isConstantBlendedColor(GrColor* constantColor) const;
 
+    /**
+     * A trivial paint is one that uses src-over and has no fragment processors.
+     * It may have variable sRGB settings.
+     **/
+    bool isTrivial() const { return fTrivial; }
+
 private:
     template <bool> class MoveOrImpl;
 
@@ -172,6 +183,7 @@
     bool fDisableOutputConversionToSRGB = false;
     bool fAllowSRGBInputs = false;
     bool fUsesDistanceVectorField = false;
+    bool fTrivial = true;
     GrColor4f fColor = GrColor4f::OpaqueWhite();
 };
 
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index 3bac5f7..7fa42d0 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -90,6 +90,8 @@
      **/
     GrPipeline(GrRenderTarget*, SkBlendMode);
 
+    GrPipeline(const InitArgs& args) { this->init(args); }
+
     /** (Re)initializes a pipeline. After initialization the pipeline can be used. */
     void init(const InitArgs&);
 
diff --git a/src/gpu/GrProcessorSet.cpp b/src/gpu/GrProcessorSet.cpp
index 69fa69a..db73266 100644
--- a/src/gpu/GrProcessorSet.cpp
+++ b/src/gpu/GrProcessorSet.cpp
@@ -11,6 +11,8 @@
 #include "GrXferProcessor.h"
 #include "effects/GrPorterDuffXferProcessor.h"
 
+const GrProcessorSet GrProcessorSet::gEmpty{GrProcessorSet::Empty::kEmpty};
+
 GrProcessorSet::GrProcessorSet(GrPaint&& paint) : fXP(paint.getXPFactory()) {
     fFlags = 0;
     if (paint.numColorFragmentProcessors() <= kMaxColorProcessors) {
diff --git a/src/gpu/GrProcessorSet.h b/src/gpu/GrProcessorSet.h
index 6ebb00b..56cbee1 100644
--- a/src/gpu/GrProcessorSet.h
+++ b/src/gpu/GrProcessorSet.h
@@ -18,6 +18,10 @@
 class GrXPFactory;
 
 class GrProcessorSet : private SkNoncopyable {
+private:
+    // Arbitrary constructor arg for empty set and analysis
+    enum class Empty { kEmpty };
+
 public:
     GrProcessorSet(GrPaint&& paint);
 
@@ -80,6 +84,14 @@
         }
 
     private:
+        constexpr Analysis(Empty)
+                : fUsesLocalCoords(false)
+                , fCompatibleWithCoverageAsAlpha(true)
+                , fRequiresDstTexture(false)
+                , fCanCombineOverlappedStencilAndCover(true)
+                , fRequiresBarrierBetweenOverlappingDraws(false)
+                , fIsInitialized(true)
+                , fInputColorType(kOriginal_InputColorType) {}
         enum InputColorType : uint32_t {
             kOriginal_InputColorType,
             kOverridden_InputColorType,
@@ -122,7 +134,13 @@
 
     bool isFinalized() const { return SkToBool(kFinalized_Flag & fFlags); }
 
+    static const GrProcessorSet& EmptySet() { return gEmpty; }
+    static constexpr const Analysis EmptySetAnalysis() { return Analysis(Empty::kEmpty); }
+
 private:
+    GrProcessorSet(Empty) : fXP((const GrXferProcessor*)nullptr), fFlags(kFinalized_Flag) {}
+    static const GrProcessorSet gEmpty;
+
     // This absurdly large limit allows Analysis and this to pack fields together.
     static constexpr int kMaxColorProcessors = UINT8_MAX;
 
@@ -130,6 +148,7 @@
 
     union XP {
         XP(const GrXPFactory* factory) : fFactory(factory) {}
+        XP(const GrXferProcessor* processor) : fProcessor(processor) {}
         const GrXPFactory* fFactory;
         const GrXferProcessor* fProcessor;
     };
@@ -141,7 +160,7 @@
 
     SkAutoSTArray<4, const GrFragmentProcessor*> fFragmentProcessors;
     XP fXP;
-    uint8_t fColorFragmentProcessorCnt;
+    uint8_t fColorFragmentProcessorCnt = 0;
     uint8_t fFragmentProcessorOffset = 0;
     uint8_t fFlags;
 };
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 6e72441..e21522f 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrRenderTargetContext.h"
+#include "../private/GrAuditTrail.h"
 #include "GrAppliedClip.h"
 #include "GrColor.h"
 #include "GrContextPriv.h"
@@ -27,10 +28,11 @@
 #include "ops/GrClearOp.h"
 #include "ops/GrClearStencilClipOp.h"
 #include "ops/GrDiscardOp.h"
-#include "ops/GrDrawOp.h"
 #include "ops/GrDrawAtlasOp.h"
+#include "ops/GrDrawOp.h"
 #include "ops/GrDrawVerticesOp.h"
 #include "ops/GrLatticeOp.h"
+#include "ops/GrNewNonAAFillRectOp.h"
 #include "ops/GrOp.h"
 #include "ops/GrOvalOpFactory.h"
 #include "ops/GrRectOpFactory.h"
@@ -39,7 +41,6 @@
 #include "ops/GrStencilPathOp.h"
 #include "text/GrAtlasTextContext.h"
 #include "text/GrStencilAndCoverTextContext.h"
-#include "../private/GrAuditTrail.h"
 
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this->drawingManager()->getContext())
 #define ASSERT_SINGLE_OWNER \
@@ -1275,7 +1276,13 @@
                                                 const GrUserStencilSettings* ss,
                                                 GrAAType hwOrNoneAAType) {
     SkASSERT(GrAAType::kCoverage != hwOrNoneAAType);
-    SkASSERT(hwOrNoneAAType == GrAAType::kNone || this->isStencilBufferMultisampled());
+    SkASSERT(GrAAType::kNone == hwOrNoneAAType || this->isStencilBufferMultisampled());
+    if (!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective())) {
+        std::unique_ptr<GrDrawOp> op = GrNewNonAAFillRectOp::Make(
+                std::move(paint), viewMatrix, rect, localRect, localMatrix, hwOrNoneAAType, ss);
+        this->addDrawOp(clip, std::move(op));
+        return;
+    }
     std::unique_ptr<GrLegacyMeshDrawOp> op = GrRectOpFactory::MakeNonAAFill(
             paint.getColor(), viewMatrix, rect, localRect, localMatrix);
     GrPipelineBuilder pipelineBuilder(std::move(paint), hwOrNoneAAType);
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index 92571d6..ee7c0a5 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -67,8 +67,6 @@
 }
 
 void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
-    SkASSERT(!state->drawOpArgs().fAppliedClip);
-    SkASSERT(!state->drawOpArgs().fDstTexture.texture());
     int currUploadIdx = 0;
     int currMeshIdx = 0;
 
diff --git a/src/gpu/ops/GrNewNonAAFillRectOp.cpp b/src/gpu/ops/GrNewNonAAFillRectOp.cpp
new file mode 100644
index 0000000..5eac33d
--- /dev/null
+++ b/src/gpu/ops/GrNewNonAAFillRectOp.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrNewNonAAFillRectOp.h"
+#include "GrAppliedClip.h"
+#include "GrColor.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrMeshDrawOp.h"
+#include "GrOpFlushState.h"
+#include "GrPrimitiveProcessor.h"
+#include "GrQuad.h"
+#include "GrResourceProvider.h"
+#include "GrSimpleMeshDrawOpHelper.h"
+#include "SkMatrixPriv.h"
+
+static const int kVertsPerInstance = 4;
+static const int kIndicesPerInstance = 6;
+
+/** We always use per-vertex colors so that rects can be combined across color changes. Sometimes
+    we  have explicit local coords and sometimes not. We *could* always provide explicit local
+    coords and just duplicate the positions when the caller hasn't provided a local coord rect,
+    but we haven't seen a use case which frequently switches between local rect and no local
+    rect draws.
+
+    The vertex attrib order is always pos, color, [local coords].
+ */
+static sk_sp<GrGeometryProcessor> make_gp() {
+    using namespace GrDefaultGeoProcFactory;
+    return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type,
+                                         LocalCoords::kHasExplicit_Type, SkMatrix::I());
+}
+
+static void tesselate(intptr_t vertices,
+                      size_t vertexStride,
+                      GrColor color,
+                      const SkMatrix* viewMatrix,
+                      const SkRect& rect,
+                      const GrQuad* localQuad) {
+    SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
+
+    positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
+
+    if (viewMatrix) {
+        SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerInstance);
+    }
+
+    // Setup local coords
+    // TODO we should only do this if local coords are being read
+    if (localQuad) {
+        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+        for (int i = 0; i < kVertsPerInstance; i++) {
+            SkPoint* coords =
+                    reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
+            *coords = localQuad->point(i);
+        }
+    }
+
+    static const int kColorOffset = sizeof(SkPoint);
+    GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
+    for (int j = 0; j < 4; ++j) {
+        *vertColor = color;
+        vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
+    }
+}
+
+class NewNonAAFillRectOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
+
+public:
+    DEFINE_OP_CLASS_ID
+    NewNonAAFillRectOp() = delete;
+
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& rect, const SkRect* localRect,
+                                          const SkMatrix* localMatrix, GrAAType aaType,
+                                          const GrUserStencilSettings* stencilSettings) {
+        SkASSERT(GrAAType::kCoverage != aaType);
+        return Helper::FactoryHelper<NewNonAAFillRectOp>(std::move(paint), viewMatrix, rect,
+                                                         localRect, localMatrix, aaType,
+                                                         stencilSettings);
+    }
+
+    const char* name() const override { return "NonAAFillRectOp"; }
+
+    SkString dumpInfo() const override {
+        SkString str;
+        str.append(GrMeshDrawOp::dumpInfo());
+        str.appendf("# combined: %d\n", fRects.count());
+        for (int i = 0; i < fRects.count(); ++i) {
+            const RectInfo& info = fRects[i];
+            str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
+                        info.fColor, info.fRect.fLeft, info.fRect.fTop, info.fRect.fRight,
+                        info.fRect.fBottom);
+        }
+        return str;
+    }
+
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor* color = &fRects.front().fColor;
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
+    NewNonAAFillRectOp(const Helper::MakeArgs& args, GrColor color, const SkMatrix& viewMatrix,
+                       const SkRect& rect, const SkRect* localRect, const SkMatrix* localMatrix,
+                       GrAAType aaType, const GrUserStencilSettings* stencilSettings)
+            : INHERITED(ClassID()), fHelper(args, aaType, stencilSettings) {
+        SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective()));
+        RectInfo& info = fRects.push_back();
+        info.fColor = color;
+        info.fViewMatrix = viewMatrix;
+        info.fRect = rect;
+        if (localRect && localMatrix) {
+            info.fLocalQuad.setFromMappedRect(*localRect, *localMatrix);
+        } else if (localRect) {
+            info.fLocalQuad.set(*localRect);
+        } else if (localMatrix) {
+            info.fLocalQuad.setFromMappedRect(rect, *localMatrix);
+        } else {
+            info.fLocalQuad.set(rect);
+        }
+        this->setTransformedBounds(fRects[0].fRect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+private:
+    void onPrepareDraws(Target* target) const override {
+        sk_sp<GrGeometryProcessor> gp = make_gp();
+        if (!gp) {
+            SkDebugf("Couldn't create GrGeometryProcessor\n");
+            return;
+        }
+        SkASSERT(gp->getVertexStride() ==
+                 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
+
+        size_t vertexStride = gp->getVertexStride();
+        int instanceCount = fRects.count();
+
+        sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
+        InstancedHelper helper;
+        void* vertices =
+                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
+                            kVertsPerInstance, kIndicesPerInstance, instanceCount);
+        if (!vertices || !indexBuffer) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        for (int i = 0; i < instanceCount; i++) {
+            intptr_t verts =
+                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride;
+            tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
+                      fRects[i].fRect, &fRects[i].fLocalQuad);
+        }
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
+    }
+
+    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
+        NewNonAAFillRectOp* that = t->cast<NewNonAAFillRectOp>();
+        if (!fHelper.isCompatible(that->fHelper)) {
+            return false;
+        }
+        fRects.push_back_n(that->fRects.count(), that->fRects.begin());
+        this->joinBounds(*that);
+        return true;
+    }
+
+    struct RectInfo {
+        GrColor fColor;
+        SkMatrix fViewMatrix;
+        SkRect fRect;
+        GrQuad fLocalQuad;
+    };
+
+    Helper fHelper;
+    SkSTArray<1, RectInfo, true> fRects;
+    typedef GrMeshDrawOp INHERITED;
+};
+
+namespace GrNewNonAAFillRectOp {
+
+std::unique_ptr<GrDrawOp> Make(GrPaint&& paint,
+                               const SkMatrix& viewMatrix,
+                               const SkRect& rect,
+                               const SkRect* localRect,
+                               const SkMatrix* localMatrix,
+                               GrAAType aaType,
+                               const GrUserStencilSettings* stencilSettings) {
+    return NewNonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, localRect, localMatrix,
+                                    aaType, stencilSettings);
+}
+};  // namespace GrNewNonAAFillRectOp
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/ops/GrNewNonAAFillRectOp.h b/src/gpu/ops/GrNewNonAAFillRectOp.h
new file mode 100644
index 0000000..8f78707
--- /dev/null
+++ b/src/gpu/ops/GrNewNonAAFillRectOp.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrNewNonAAFillRectOp_DEFINED
+#define GrNewNonAAFillRectOp_DEFINED
+
+#include <memory>
+#include "GrColor.h"
+
+class GrDrawOp;
+class GrPaint;
+class SkMatrix;
+struct SkRect;
+struct GrUserStencilSettings;
+enum class GrAAType : unsigned;
+
+namespace GrNewNonAAFillRectOp {
+std::unique_ptr<GrDrawOp> Make(GrPaint&&,
+                               const SkMatrix& viewMatrix,
+                               const SkRect& rect,
+                               const SkRect* localRect,
+                               const SkMatrix* localMatrix,
+                               GrAAType,
+                               const GrUserStencilSettings* = nullptr);
+};
+
+#endif
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
new file mode 100644
index 0000000..f64f9ec
--- /dev/null
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrSimpleMeshDrawOpHelper_DEFINED
+#define GrSimpleMeshDrawOpHelper_DEFINED
+
+#include "GrAppliedClip.h"
+#include "GrOpFlushState.h"
+#include "GrPipeline.h"
+#include "GrProcessorSet.h"
+#include "GrUserStencilSettings.h"
+
+/**
+ * This class can be used to help implement simple mesh draw ops. It reduces the amount of
+ * boilerplate code to type and also provides a mechanism for optionally allocating space for a
+ * GrProcessorSet based on a GrPaint. It is intended to be used by ops that construct a single
+ * GrPipeline for a uniform primitive color and a GrPaint.
+ */
+class GrSimpleMeshDrawOpHelper {
+public:
+    struct MakeArgs;
+
+    /**
+     * This can be used by a Op class to perform allocation and initialization such that a
+     * GrProcessorSet (if required) is allocated at the same time as the Op instance. It requires
+     * that Op implements a constructor of the form:
+     *      Op(MakeArgs, GrColor, OpArgs...)
+     * which is public or made accessible via 'friend'.
+     */
+    template <typename Op, typename... OpArgs>
+    static std::unique_ptr<GrDrawOp> FactoryHelper(GrPaint&& paint, OpArgs... opArgs);
+
+    GrSimpleMeshDrawOpHelper(const MakeArgs& args, GrAAType aaType,
+                             GrUserStencilSettings* stencilSettings = nullptr)
+            : fProcessors(args.fProcessorSet)
+            , fPipelineFlags(args.fSRGBFlags)
+            , fAAType((int)aaType) {
+        SkASSERT(!stencilSettings);
+        if (GrAATypeIsHW(aaType)) {
+            fPipelineFlags |= GrPipeline::kHWAntialias_Flag;
+        }
+    }
+
+    ~GrSimpleMeshDrawOpHelper() {
+        if (fProcessors) {
+            fProcessors->~GrProcessorSet();
+        }
+    }
+
+    GrSimpleMeshDrawOpHelper() = delete;
+    GrSimpleMeshDrawOpHelper(const GrSimpleMeshDrawOpHelper&) = delete;
+    GrSimpleMeshDrawOpHelper& operator=(const GrSimpleMeshDrawOpHelper&) = delete;
+
+    GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const {
+        return GrAATypeIsHW((this->aaType())) ? GrDrawOp::FixedFunctionFlags::kUsesHWAA
+                                              : GrDrawOp::FixedFunctionFlags::kNone;
+    }
+
+    bool isCompatible(const GrSimpleMeshDrawOpHelper& that) const {
+        if (SkToBool(fProcessors) != SkToBool(that.fProcessors)) {
+            return false;
+        }
+        if (SkToBool(fProcessors) && *fProcessors != *that.fProcessors) {
+            return false;
+        }
+        return fPipelineFlags == that.fPipelineFlags && fAAType == that.fAAType;
+    }
+
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip,
+                              GrProcessorAnalysisCoverage geometryCoverage, GrColor* color) {
+        if (fProcessors) {
+            GrProcessorAnalysisCoverage coverage = geometryCoverage;
+            if (GrProcessorAnalysisCoverage::kNone == coverage) {
+                coverage = clip->clipCoverageFragmentProcessor()
+                                   ? GrProcessorAnalysisCoverage::kSingleChannel
+                                   : GrProcessorAnalysisCoverage::kNone;
+            }
+            bool isMixedSamples = this->aaType() == GrAAType::kMixedSamples;
+            GrProcessorSet::Analysis analysis =
+                    fProcessors->finalize(*color, coverage, clip, isMixedSamples, caps, color);
+            return analysis.requiresDstTexture();
+        } else {
+            return GrProcessorSet::EmptySetAnalysis().requiresDstTexture();
+        }
+    }
+
+    GrPipeline* makePipeline(GrMeshDrawOp::Target* target) const {
+        return target->allocPipeline(this->pipelineInitArgs(target));
+    }
+
+    struct MakeArgs {
+    private:
+        MakeArgs() = default;
+
+        GrProcessorSet* fProcessorSet;
+        uint32_t fSRGBFlags;
+
+        friend class GrSimpleMeshDrawOpHelper;
+    };
+
+protected:
+    GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
+    uint32_t pipelineFlags() const { return fPipelineFlags; }
+    const GrProcessorSet& processors() const {
+        return fProcessors ? *fProcessors : GrProcessorSet::EmptySet();
+    }
+
+    GrPipeline::InitArgs pipelineInitArgs(GrMeshDrawOp::Target* target) const {
+        GrPipeline::InitArgs args;
+        args.fFlags = this->pipelineFlags();
+        args.fProcessors = &this->processors();
+        args.fRenderTarget = target->renderTarget();
+        args.fAppliedClip = target->clip();
+        args.fDstTexture = target->dstTexture();
+        args.fCaps = &target->caps();
+        return args;
+    }
+
+private:
+    GrProcessorSet* fProcessors;
+    unsigned fPipelineFlags : 8;
+    unsigned fAAType : 2;
+};
+
+/**
+ * This class extends GrSimpleMeshDrawOpHelper to support an optional GrUserStencilSettings. This
+ * uses private inheritance because it non-virtually overrides methods in the base class and should
+ * never be used with a GrSimpleMeshDrawOpHelper pointer or reference.
+ */
+class GrSimpleMeshDrawOpHelperWithStencil : private GrSimpleMeshDrawOpHelper {
+public:
+    using MakeArgs = GrSimpleMeshDrawOpHelper::MakeArgs;
+
+    // using declarations can't be templated, so this is a pass through function instead.
+    template <typename Op, typename... OpArgs>
+    static std::unique_ptr<GrDrawOp> FactoryHelper(GrPaint&& paint, OpArgs... opArgs) {
+        return GrSimpleMeshDrawOpHelper::FactoryHelper<Op, OpArgs...>(
+                std::move(paint), std::forward<OpArgs>(opArgs)...);
+    }
+
+    GrSimpleMeshDrawOpHelperWithStencil(const MakeArgs& args, GrAAType aaType,
+                                         const GrUserStencilSettings* stencilSettings)
+            : INHERITED(args, aaType)
+            , fStencilSettings(stencilSettings ? stencilSettings
+                                               : &GrUserStencilSettings::kUnused) {}
+
+    GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const {
+        GrDrawOp::FixedFunctionFlags flags = INHERITED::fixedFunctionFlags();
+        if (fStencilSettings != &GrUserStencilSettings::kUnused) {
+            flags |= GrDrawOp::FixedFunctionFlags::kUsesStencil;
+        }
+        return flags;
+    }
+
+    using GrSimpleMeshDrawOpHelper::xpRequiresDstTexture;
+
+    bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil& that) const {
+        return INHERITED::isCompatible(that) && fStencilSettings == that.fStencilSettings;
+    }
+
+    GrPipeline* makePipeline(GrMeshDrawOp::Target* target) const {
+        auto args = INHERITED::pipelineInitArgs(target);
+        args.fUserStencil = fStencilSettings;
+        return target->allocPipeline(args);
+    }
+
+private:
+    const GrUserStencilSettings* fStencilSettings;
+    typedef GrSimpleMeshDrawOpHelper INHERITED;
+};
+
+template <typename Op, typename... OpArgs>
+std::unique_ptr<GrDrawOp> GrSimpleMeshDrawOpHelper::FactoryHelper(GrPaint&& paint,
+                                                                  OpArgs... opArgs) {
+    MakeArgs makeArgs;
+    makeArgs.fSRGBFlags = GrPipeline::SRGBFlagsFromPaint(paint);
+    GrColor color = paint.getColor();
+    if (paint.isTrivial()) {
+        makeArgs.fProcessorSet = nullptr;
+        return std::unique_ptr<GrDrawOp>(new Op(makeArgs, color, std::forward<OpArgs>(opArgs)...));
+    } else {
+        char* mem = (char*)GrOp::operator new(sizeof(Op) + sizeof(GrProcessorSet));
+        char* setMem = mem + sizeof(Op);
+        makeArgs.fProcessorSet = new (setMem) GrProcessorSet(std::move(paint));
+        return std::unique_ptr<GrDrawOp>(
+                new (mem) Op(makeArgs, color, std::forward<OpArgs>(opArgs)...));
+    }
+}
+
+#endif