Make it possible to draw multiple paths at once to a draw target

Add interface to draw multiple paths in a single "command" to a draw
target. Implement this interface in GrGpuGL with NVPR "instanced"
calls.

The instanced calls accept list of paths and list of transformations as
their parameters. The transformations are at this moment expected to be
2d affine transformations, as the functions are called only for text
rendering.

This will be used when drawing fonts. Later it can be maybe be used in
GrInOrderDrawBuffer to aggregate many draw calls into one instanced draw
call, similar to drawing rects.

R=jvanverth@google.com, bsalomon@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/209413006

git-svn-id: http://skia.googlecode.com/svn/trunk@13930 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 5fb2688..0aa072c 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -1695,6 +1695,99 @@
     }
 }
 
+void GrGpuGL::onGpuDrawPaths(size_t pathCount, const GrPath** paths,
+                             const SkMatrix* transforms,
+                             SkPath::FillType fill,
+                             SkStrokeRec::Style stroke) {
+    SkASSERT(this->caps()->pathRenderingSupport());
+    SkASSERT(NULL != this->drawState()->getRenderTarget());
+    SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer());
+    SkASSERT(!fCurrentProgram->hasVertexShader());
+    SkASSERT(stroke != SkStrokeRec::kHairline_Style);
+
+    SkAutoMalloc pathData(pathCount * sizeof(GrGLuint));
+    SkAutoMalloc transformData(pathCount * sizeof(GrGLfloat) * 6);
+    GrGLfloat* transformValues =
+        reinterpret_cast<GrGLfloat*>(transformData.get());
+    GrGLuint* pathIDs = reinterpret_cast<GrGLuint*>(pathData.get());
+
+    for (size_t i = 0; i < pathCount; ++i) {
+        SkASSERT(transforms[i].asAffine(NULL));
+        const SkMatrix& m = transforms[i];
+        transformValues[i * 6] = m.getScaleX();
+        transformValues[i * 6 + 1] = m.getSkewY();
+        transformValues[i * 6 + 2] = m.getSkewX();
+        transformValues[i * 6 + 3] = m.getScaleY();
+        transformValues[i * 6 + 4] = m.getTranslateX();
+        transformValues[i * 6 + 5] = m.getTranslateY();
+        pathIDs[i] = static_cast<const GrGLPath*>(paths[i])->pathID();
+    }
+
+    flushPathStencilSettings(fill);
+
+    SkPath::FillType nonInvertedFill =
+        SkPath::ConvertToNonInverseFillType(fill);
+
+    SkASSERT(!fHWPathStencilSettings.isTwoSided());
+    GrGLenum fillMode =
+        gr_stencil_op_to_gl_path_rendering_fill_mode(
+            fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
+    GrGLint writeMask =
+        fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
+
+    bool doFill = stroke == SkStrokeRec::kFill_Style
+        || stroke == SkStrokeRec::kStrokeAndFill_Style;
+    bool doStroke = stroke == SkStrokeRec::kStroke_Style
+        || stroke == SkStrokeRec::kStrokeAndFill_Style;
+
+    if (doFill) {
+        GL_CALL(StencilFillPathInstanced(pathCount, GR_GL_UNSIGNED_INT,
+                                         pathIDs, 0,
+                                         fillMode, writeMask,
+                                         GR_GL_AFFINE_2D, transformValues));
+    }
+    if (doStroke) {
+        GL_CALL(StencilStrokePathInstanced(pathCount, GR_GL_UNSIGNED_INT,
+                                           pathIDs, 0,
+                                           0xffff, writeMask,
+                                           GR_GL_AFFINE_2D, transformValues));
+    }
+
+    if (nonInvertedFill == fill) {
+        if (doStroke) {
+            GL_CALL(CoverStrokePathInstanced(
+                        pathCount, GR_GL_UNSIGNED_INT, pathIDs, 0,
+                        GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
+                        GR_GL_AFFINE_2D, transformValues));
+        } else {
+            GL_CALL(CoverFillPathInstanced(
+                        pathCount, GR_GL_UNSIGNED_INT, pathIDs, 0,
+                        GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
+                        GR_GL_AFFINE_2D, transformValues));
+
+        }
+    } else {
+        GrDrawState* drawState = this->drawState();
+        GrDrawState::AutoViewMatrixRestore avmr;
+        SkRect bounds = SkRect::MakeLTRB(0, 0,
+                                         SkIntToScalar(drawState->getRenderTarget()->width()),
+                                         SkIntToScalar(drawState->getRenderTarget()->height()));
+        SkMatrix vmi;
+        // mapRect through persp matrix may not be correct
+        if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) {
+            vmi.mapRect(&bounds);
+            // theoretically could set bloat = 0, instead leave it because of matrix inversion
+            // precision.
+            SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf;
+            bounds.outset(bloat, bloat);
+        } else {
+            avmr.setIdentity(drawState);
+        }
+
+        this->drawSimpleRect(bounds, NULL);
+    }
+}
+
 void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
     GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
     if (rt->needsResolve()) {