GPU implementation of drawRegion()

Nexus 6P drawregion Bench Performance
Before 48.0ms
After  3.57ms

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

Review-Url: https://codereview.chromium.org/2267273006
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index eb99cfe..d1a6952 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -259,6 +259,8 @@
       '<(skia_src_path)/gpu/batches/GrPLSPathRenderer.h',
       '<(skia_src_path)/gpu/batches/GrRectBatchFactory.h',
       '<(skia_src_path)/gpu/batches/GrRectBatchFactory.cpp',
+      '<(skia_src_path)/gpu/batches/GrRegionBatch.cpp',
+      '<(skia_src_path)/gpu/batches/GrRegionBatch.h',
       '<(skia_src_path)/gpu/batches/GrStencilAndCoverPathRenderer.cpp',
       '<(skia_src_path)/gpu/batches/GrStencilAndCoverPathRenderer.h',
       '<(skia_src_path)/gpu/batches/GrStencilPathBatch.h',
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 780af10..d9b3282 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -708,6 +708,10 @@
         @param paint    The paint used to draw the region
     */
     void drawRegion(const SkRegion& region, const SkPaint& paint) {
+        if (region.isEmpty()) {
+            return;
+        }
+
         this->onDrawRegion(region, paint);
     }
 
diff --git a/include/gpu/GrDrawContext.h b/include/gpu/GrDrawContext.h
index 993bb56..29c7d1c 100644
--- a/include/gpu/GrDrawContext.h
+++ b/include/gpu/GrDrawContext.h
@@ -216,7 +216,21 @@
                    const SkRSXform xform[],
                    const SkRect texRect[],
                    const SkColor colors[]);
-    
+
+    /**
+     * Draws a region.
+     *
+     * @param paint         describes how to color pixels
+     * @param viewMatrix    transformation matrix
+     * @param region        the region to be drawn
+     * @param style         style to apply to the region
+     */
+    void drawRegion(const GrClip&,
+                    const GrPaint& paint,
+                    const SkMatrix& viewMatrix,
+                    const SkRegion& region,
+                    const GrStyle& style);
+
     /**
      * Draws an oval.
      *
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 5bda884..5afb2ea 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -26,6 +26,7 @@
 #include "batches/GrDrawVerticesBatch.h"
 #include "batches/GrRectBatchFactory.h"
 #include "batches/GrNinePatch.h" // TODO Factory
+#include "batches/GrRegionBatch.h"
 
 #include "effects/GrRRectEffect.h"
 
@@ -944,6 +945,35 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static inline bool is_int(float x) {
+    return x == (float) sk_float_round2int(x);
+}
+
+void GrDrawContext::drawRegion(const GrClip& clip,
+                               const GrPaint& paint,
+                               const SkMatrix& viewMatrix,
+                               const SkRegion& region,
+                               const GrStyle& style) {
+    ASSERT_SINGLE_OWNER
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+    GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawRegion");
+
+    bool isNonTranslate = SkToBool(viewMatrix.getType() & ~(SkMatrix::kTranslate_Mask));
+    bool complexStyle = !style.isSimpleFill();
+    bool antiAlias = paint.isAntiAlias() && (!is_int(viewMatrix.getTranslateX()) ||
+                                             !is_int(viewMatrix.getTranslateY()));
+    if (isNonTranslate || complexStyle || antiAlias) {
+        SkPath path;
+        region.getBoundaryPath(&path);
+        return this->drawPath(clip, paint, viewMatrix, path, style);
+    }
+
+    SkAutoTUnref<GrDrawBatch> batch(GrRegionBatch::Create(paint.getColor(), viewMatrix, region));
+    GrPipelineBuilder pipelineBuilder(paint, false);
+    this->getDrawTarget()->drawBatch(pipelineBuilder, this, clip, batch);
+}
+
 void GrDrawContext::drawOval(const GrClip& clip,
                              const GrPaint& paint,
                              const SkMatrix& viewMatrix,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index c91331f..2d9b3ef 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -523,6 +523,21 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
+void SkGpuDevice::drawRegion(const SkDraw& draw, const SkRegion& region, const SkPaint& paint) {
+    if (paint.getMaskFilter()) {
+        SkPath path;
+        region.getBoundaryPath(&path);
+        return this->drawPath(draw, path, paint, nullptr, false);
+    }
+
+    GrPaint grPaint;
+    if (!SkPaintToGrPaint(this->context(), fDrawContext.get(), paint, *draw.fMatrix, &grPaint)) {
+        return;
+    }
+
+    fDrawContext->drawRegion(fClip, grPaint, *draw.fMatrix, region, GrStyle(paint));
+}
+
 void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawOval", fContext);
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index cbe86a4..a49d160 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -73,6 +73,7 @@
     void drawRRect(const SkDraw&, const SkRRect& r, const SkPaint& paint) override;
     void drawDRRect(const SkDraw& draw, const SkRRect& outer, const SkRRect& inner,
                     const SkPaint& paint) override;
+    void drawRegion(const SkDraw&, const SkRegion& r, const SkPaint& paint) override;
     void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override;
     void drawArc(const SkDraw&, const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
                  bool useCenter, const SkPaint& paint) override;
diff --git a/src/gpu/batches/GrRegionBatch.cpp b/src/gpu/batches/GrRegionBatch.cpp
new file mode 100644
index 0000000..271f6c5
--- /dev/null
+++ b/src/gpu/batches/GrRegionBatch.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "GrRegionBatch.h"
+
+#include "GrDefaultGeoProcFactory.h"
+#include "GrBatchFlushState.h"
+#include "GrResourceProvider.h"
+#include "GrVertexBatch.h"
+#include "SkMatrixPriv.h"
+#include "SkRegion.h"
+
+static const int kVertsPerInstance = 4;
+static const int kIndicesPerInstance = 6;
+
+static sk_sp<GrGeometryProcessor> make_gp(bool readsCoverage) {
+    using namespace GrDefaultGeoProcFactory;
+    Color color(Color::kAttribute_Type);
+    Coverage coverage(readsCoverage ? Coverage::kSolid_Type : Coverage::kNone_Type);
+
+    LocalCoords localCoords(LocalCoords::kHasExplicit_Type);
+    return GrDefaultGeoProcFactory::Make(color, coverage, localCoords, SkMatrix::I());
+}
+
+static int tesselate_region(intptr_t vertices,
+                            size_t vertexStride,
+                            GrColor color,
+                            const SkMatrix& viewMatrix,
+                            const SkRegion& region) {
+    SkRegion::Iterator iter(region);
+
+    intptr_t verts = vertices;
+    while (!iter.done()) {
+        SkIRect rect = iter.rect();
+        SkPoint* position = (SkPoint*) verts;
+        position->setIRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
+
+        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+        SkPoint* localPosition = (SkPoint*) (verts + kLocalOffset);
+        localPosition->setIRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
+
+        static const int kColorOffset = sizeof(SkPoint);
+        GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
+        for (int i = 0; i < kVertsPerInstance; i++) {
+            *vertColor = color;
+            vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
+        }
+
+        verts += vertexStride * kVertsPerInstance;
+        iter.next();
+    }
+
+    SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
+    int numRects = region.computeRegionComplexity();
+    SkMatrixPriv::MapPointsWithStride(viewMatrix, positions, vertexStride,
+                                      numRects * kVertsPerInstance);
+
+    return numRects;
+}
+
+class RegionBatch : public GrVertexBatch {
+public:
+    DEFINE_BATCH_CLASS_ID
+
+    RegionBatch(GrColor color, const SkMatrix& viewMatrix, const SkRegion& region)
+            : INHERITED(ClassID()) {
+
+        RegionInfo& info = fRegions.push_back();
+        info.fColor = color;
+        info.fViewMatrix = viewMatrix;
+        info.fRegion = region;
+
+        SkRect bounds = SkRect::Make(region.getBounds());
+        this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+    const char* name() const override { return "GrRegionBatch"; }
+
+    SkString dumpInfo() const override {
+        SkString str;
+        str.appendf("# batched: %d\n", fRegions.count());
+        for (int i = 0; i < fRegions.count(); ++i) {
+            const RegionInfo& info = fRegions[i];
+            str.appendf("%d: Color: 0x%08x, Region with %d rects\n",
+                        i, info.fColor, info.fRegion.computeRegionComplexity());
+        }
+        str.append(INHERITED::dumpInfo());
+        return str;
+    }
+
+    void computePipelineOptimizations(GrInitInvariantOutput* color,
+                                      GrInitInvariantOutput* coverage,
+                                      GrBatchToXPOverrides* overrides) const override {
+        // When this is called on a batch, there is only one region.
+        color->setKnownFourComponents(fRegions[0].fColor);
+        coverage->setKnownSingleComponent(0xff);
+    }
+
+    void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
+        overrides.getOverrideColorIfSet(&fRegions[0].fColor);
+        fOverrides = overrides;
+    }
+
+private:
+
+    void onPrepareDraws(Target* target) const override {
+        sk_sp<GrGeometryProcessor> gp = make_gp(fOverrides.readsCoverage());
+        if (!gp) {
+            SkDebugf("Couldn't create GrGeometryProcessor\n");
+            return;
+        }
+        SkASSERT(gp->getVertexStride() ==
+                sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
+
+        int numRegions = fRegions.count();
+        int numRects = 0;
+        for (int i = 0; i < numRegions; i++) {
+            numRects += fRegions[i].fRegion.computeRegionComplexity();
+        }
+
+        size_t vertexStride = gp->getVertexStride();
+        SkAutoTUnref<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
+        InstancedHelper helper;
+        void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
+                                     indexBuffer, kVertsPerInstance, kIndicesPerInstance, numRects);
+        if (!vertices || !indexBuffer) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        intptr_t verts = reinterpret_cast<intptr_t>(vertices);
+        for (int i = 0; i < numRegions; i++) {
+            int numRectsInRegion = tesselate_region(verts, vertexStride, fRegions[i].fColor,
+                                                    fRegions[i].fViewMatrix, fRegions[i].fRegion);
+            verts += numRectsInRegion * kVertsPerInstance * vertexStride;
+        }
+        helper.recordDraw(target, gp.get());
+    }
+
+    bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
+        RegionBatch* that = t->cast<RegionBatch>();
+        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
+                                    that->bounds(), caps)) {
+            return false;
+        }
+
+        fRegions.push_back_n(that->fRegions.count(), that->fRegions.begin());
+        this->joinBounds(*that);
+        return true;
+    }
+
+    struct RegionInfo {
+        GrColor fColor;
+        SkMatrix fViewMatrix;
+        SkRegion fRegion;
+    };
+
+    GrXPOverridesForBatch fOverrides;
+    SkSTArray<1, RegionInfo, true> fRegions;
+
+    typedef GrVertexBatch INHERITED;
+};
+
+namespace GrRegionBatch {
+
+GrDrawBatch* Create(GrColor color,
+                    const SkMatrix& viewMatrix,
+                    const SkRegion& region) {
+    return new RegionBatch(color, viewMatrix, region);
+}
+
+};
diff --git a/src/gpu/batches/GrRegionBatch.h b/src/gpu/batches/GrRegionBatch.h
new file mode 100644
index 0000000..d928d0a
--- /dev/null
+++ b/src/gpu/batches/GrRegionBatch.h
@@ -0,0 +1,25 @@
+/*
+ * 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 GrRegionBatch_DEFINED
+#define GrRegionBatch_DEFINED
+
+#include "GrColor.h"
+
+class GrDrawBatch;
+class SkMatrix;
+class SkRegion;
+
+namespace GrRegionBatch {
+
+GrDrawBatch* Create(GrColor color,
+                    const SkMatrix& viewMatrix,
+                    const SkRegion& region);
+
+};
+
+#endif