Handle stroked single line special case in Ganesh
authorrobertphillips <robertphillips@google.com>
Thu, 2 Jun 2016 12:21:34 +0000 (05:21 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 2 Jun 2016 12:21:34 +0000 (05:21 -0700)
This CL roughly halves the time spent on the captured stroked lines skp.
On my Linux desktop it boosts the external benchmark from 2618 to 5007.

This is a companion to: https://codereview.chromium.org/2019193002/ (Add new GM to exercise stroked line special case)

The idea is to land the GM first so any regressions are visible.

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

Review-Url: https://codereview.chromium.org/2023693002

src/core/SkPoint.cpp
src/gpu/SkGpuDevice.cpp
src/gpu/SkGpuDevice.h
src/gpu/batches/GrAAFillRectBatch.cpp

index 490e4a2..162c62a 100644 (file)
@@ -88,8 +88,8 @@ static inline float getLengthSquared(float dx, float dy) {
 // This logic is encapsulated in a helper method to make it explicit that we
 // always perform this check in the same manner, to avoid inconsistencies
 // (see http://code.google.com/p/skia/issues/detail?id=560 ).
-static inline bool isLengthNearlyZero(float dx, float dy,
-                                      float *lengthSquared) {
+static inline bool is_length_nearly_zero(float dx, float dy,
+                                         float *lengthSquared) {
     *lengthSquared = getLengthSquared(dx, dy);
     return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
 }
@@ -98,7 +98,7 @@ SkScalar SkPoint::Normalize(SkPoint* pt) {
     float x = pt->fX;
     float y = pt->fY;
     float mag2;
-    if (isLengthNearlyZero(x, y, &mag2)) {
+    if (is_length_nearly_zero(x, y, &mag2)) {
         pt->set(0, 0);
         return 0;
     }
@@ -146,7 +146,7 @@ SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
  */
 bool SkPoint::setLength(float x, float y, float length) {
     float mag2;
-    if (isLengthNearlyZero(x, y, &mag2)) {
+    if (is_length_nearly_zero(x, y, &mag2)) {
         this->set(0, 0);
         return false;
     }
@@ -183,7 +183,7 @@ bool SkPoint::setLengthFast(float length) {
 
 bool SkPoint::setLengthFast(float x, float y, float length) {
     float mag2;
-    if (isLengthNearlyZero(x, y, &mag2)) {
+    if (is_length_nearly_zero(x, y, &mag2)) {
         this->set(0, 0);
         return false;
     }
index 10bcecf..20afada 100644 (file)
@@ -692,12 +692,78 @@ void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint
 #include "SkMaskFilter.h"
 
 ///////////////////////////////////////////////////////////////////////////////
+void SkGpuDevice::drawStrokedLine(const SkPoint points[2],
+                                  const SkDraw& draw,
+                                  const SkPaint& origPaint) {
+    ASSERT_SINGLE_OWNER
+    GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawStrokedLine", fContext);
+    CHECK_SHOULD_DRAW(draw);
+
+    // Adding support for round capping would require a GrDrawContext::fillRRectWithLocalMatrix
+    // entry point
+    SkASSERT(SkPaint::kRound_Cap != origPaint.getStrokeCap());
+    SkASSERT(SkPaint::kStroke_Style == origPaint.getStyle());
+    SkASSERT(!origPaint.getPathEffect());
+    SkASSERT(!origPaint.getMaskFilter());
+
+    const SkScalar halfWidth = 0.5f * origPaint.getStrokeWidth();
+    SkASSERT(halfWidth > 0);
+
+    SkVector v = points[1] - points[0];
+
+    SkScalar length = SkPoint::Normalize(&v);
+    if (!length) {
+        v.fX = 1.0f;
+        v.fY = 0.0f;
+    }
+
+    SkPaint newPaint(origPaint);
+    newPaint.setStyle(SkPaint::kFill_Style);
+
+    SkScalar xtraLength = 0.0f;
+    if (SkPaint::kButt_Cap != origPaint.getStrokeCap()) {
+        xtraLength = halfWidth;
+    }
+
+    SkPoint mid = points[0] + points[1];
+    mid.scale(0.5f);
+
+    SkRect rect = SkRect::MakeLTRB(mid.fX-halfWidth, mid.fY - 0.5f*length - xtraLength,
+                                   mid.fX+halfWidth, mid.fY + 0.5f*length + xtraLength);
+    SkMatrix m;
+    m.setSinCos(v.fX, -v.fY, mid.fX, mid.fY);
+
+    SkMatrix local = m;
+
+    m.postConcat(*draw.fMatrix);
+
+    GrPaint grPaint;
+    if (!SkPaintToGrPaint(this->context(), newPaint, m,
+                          this->surfaceProps().isGammaCorrect(), &grPaint)) {
+        return;
+    }
+
+    fDrawContext->fillRectWithLocalMatrix(fClip, grPaint, m, rect, local);
+}
 
 void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
                            const SkPaint& paint, const SkMatrix* prePathMatrix,
                            bool pathIsMutable) {
     ASSERT_SINGLE_OWNER
     if (!origSrcPath.isInverseFillType() && !paint.getPathEffect() && !prePathMatrix) {
+        SkPoint points[2];
+        if (SkPaint::kStroke_Style == paint.getStyle() && paint.getStrokeWidth() > 0 &&
+            !paint.getMaskFilter() && SkPaint::kRound_Cap != paint.getStrokeCap() &&
+            draw.fMatrix->preservesRightAngles() && origSrcPath.isLine(points)) {
+            // Path-based stroking looks better for thin rects
+            SkScalar strokeWidth = draw.fMatrix->getMaxScale() * paint.getStrokeWidth();
+            if (strokeWidth > 0.9f) {
+                // Round capping support is currently disabled b.c. it would require
+                // a RRect batch that takes a localMatrix.
+                this->drawStrokedLine(points, draw, paint);
+                return;
+            }
+        }
         bool isClosed;
         SkRect rect;
         if (origSrcPath.isRect(&rect, &isClosed) && isClosed) {
index 474a030..4c6a0f3 100644 (file)
@@ -252,6 +252,7 @@ private:
                           const SkRect& dst, const SkPaint&);
 
     bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
+    void drawStrokedLine(const SkPoint pts[2], const SkDraw&, const SkPaint&);
 
     static sk_sp<GrDrawContext> CreateDrawContext(GrContext*,
                                                   SkBudgeted,
index 859328a..2c90b8b 100644 (file)
@@ -84,10 +84,12 @@ static void generate_aa_fill_rect_geometry(intptr_t verts,
     SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
     SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
 
-    SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
-    inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
+    SkScalar inset;
 
     if (viewMatrix.rectStaysRect()) {
+        inset = SkMinScalar(devRect.width(), SK_Scalar1);
+        inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
+
         set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
         set_inset_fan(fan1Pos, vertexStride, devRect, inset,  inset);
     } else {
@@ -97,11 +99,14 @@ static void generate_aa_fill_rect_geometry(intptr_t verts,
           { viewMatrix[SkMatrix::kMSkewX],  viewMatrix[SkMatrix::kMScaleY] }
         };
 
-        vec[0].normalize();
+        SkScalar len1 = SkPoint::Normalize(&vec[0]);
         vec[0].scale(SK_ScalarHalf);
-        vec[1].normalize();
+        SkScalar len2 = SkPoint::Normalize(&vec[1]);
         vec[1].scale(SK_ScalarHalf);
 
+        inset = SkMinScalar(len1 * rect.width(), SK_Scalar1);
+        inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height());
+
         // create the rotated rect
         fan0Pos->setRectFan(rect.fLeft, rect.fTop,
                             rect.fRight, rect.fBottom, vertexStride);