Optimize software radial gradients (remove branches from the inner loop where
we can, because an entire row is conservatively either outside or inside the
gradient.) Change the gradient benchmark to capture both cases, and add new
gm to detect errors in these optimized paths.



git-svn-id: http://skia.googlecode.com/svn/trunk@2327 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp
index 707cfc5..04923c9 100644
--- a/bench/GradientBench.cpp
+++ b/bench/GradientBench.cpp
@@ -38,23 +38,29 @@
     { 5, gColors, gPos2 }
 };
 
+/// Ignores scale
 static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+                            SkShader::TileMode tm, SkUnitMapper* mapper,
+                            float scale) {
     return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
                                           data.fCount, tm, mapper);
 }
 
 static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+                            SkShader::TileMode tm, SkUnitMapper* mapper,
+                            float scale) {
     SkPoint center;
     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
                SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+    return SkGradientShader::CreateRadial(center, center.fX * scale,
+                                          data.fColors,
                                           data.fPos, data.fCount, tm, mapper);
 }
 
+/// Ignores scale
 static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
-                           SkShader::TileMode tm, SkUnitMapper* mapper) {
+                           SkShader::TileMode tm, SkUnitMapper* mapper,
+                           float scale) {
     SkPoint center;
     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
                SkScalarAve(pts[0].fY, pts[1].fY));
@@ -62,21 +68,24 @@
                                          data.fPos, data.fCount, mapper);
 }
 
+/// Ignores scale
 static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
-                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+                             SkShader::TileMode tm, SkUnitMapper* mapper,
+                             float scale) {
     SkPoint center0, center1;
     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
     return SkGradientShader::CreateTwoPointRadial(
-                                                  center1, (pts[1].fX - pts[0].fX) / 7,
-                                                  center0, (pts[1].fX - pts[0].fX) / 2,
-                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+            center1, (pts[1].fX - pts[0].fX) / 7,
+            center0, (pts[1].fX - pts[0].fX) / 2,
+            data.fColors, data.fPos, data.fCount, tm, mapper);
 }
 
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
-                               SkShader::TileMode tm, SkUnitMapper* mapper);
+                               SkShader::TileMode tm, SkUnitMapper* mapper,
+                               float scale);
 
 static const struct {
     GradMaker   fMaker;
@@ -96,6 +105,11 @@
     kRadial2_GradType
 };
 
+enum GeomType {
+    kRect_GeomType,
+    kOval_GeomType
+};
+
 static const char* tilemodename(SkShader::TileMode tm) {
     switch (tm) {
         case SkShader::kClamp_TileMode:
@@ -110,6 +124,18 @@
     }
 }
 
+static const char* geomtypename(GeomType gt) {
+    switch (gt) {
+        case kRect_GeomType:
+            return "rectangle";
+        case kOval_GeomType:
+            return "oval";
+        default:
+            SkASSERT(!"unknown geometry type");
+            return "error";
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class GradientBench : public SkBenchmark {
@@ -122,17 +148,26 @@
         N   = 1
     };
 public:
-    GradientBench(void* param, GradType gt,
-                  SkShader::TileMode tm = SkShader::kClamp_TileMode) : INHERITED(param) {
-        fName.printf("gradient_%s_%s", gGrads[gt].fName, tilemodename(tm));
+    GradientBench(void* param, GradType gradType,
+                  SkShader::TileMode tm = SkShader::kClamp_TileMode,
+                  GeomType geomType = kRect_GeomType,
+                  float scale = 1.0f)
+        : INHERITED(param) {
+        fName.printf("gradient_%s_%s", gGrads[gradType].fName,
+                     tilemodename(tm));
+        if (geomType != kRect_GeomType) {
+            fName.append("_");
+            fName.append(geomtypename(geomType));
+        }
 
         const SkPoint pts[2] = {
             { 0, 0 },
             { SkIntToScalar(W), SkIntToScalar(H) }
         };
         
-        fCount = N * gGrads[gt].fRepeat;
-        fShader = gGrads[gt].fMaker(pts, gGradData[0], tm, NULL);
+        fCount = N * gGrads[gradType].fRepeat;
+        fShader = gGrads[gradType].fMaker(pts, gGradData[0], tm, NULL, scale);
+        fGeomType = geomType;
     }
 
     virtual ~GradientBench() {
@@ -152,12 +187,21 @@
 
         SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
         for (int i = 0; i < fCount; i++) {
-            canvas->drawRect(r, paint);
+            switch (fGeomType) {
+               case kRect_GeomType:
+                   canvas->drawRect(r, paint);
+                   break;
+               case kOval_GeomType:
+                   canvas->drawOval(r, paint);
+                   break;
+            }
         }
     }
 
 private:
     typedef SkBenchmark INHERITED;
+
+    GeomType fGeomType;
 };
 
 class Gradient2Bench : public SkBenchmark {
@@ -181,7 +225,10 @@
 
         for (int i = 0; i < 1000; i++) {
             const int a = i % 256;
-            SkColor colors[] = { SK_ColorBLACK, SkColorSetARGB(a, a, a, a), SK_ColorWHITE };
+            SkColor colors[] = {
+                SK_ColorBLACK,
+                SkColorSetARGB(a, a, a, a),
+                SK_ColorWHITE };
             SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL,
                                                          SK_ARRAY_COUNT(colors),
                                                          SkShader::kClamp_TileMode);
@@ -196,7 +243,15 @@
 
 static SkBenchmark* Fact0(void* p) { return new GradientBench(p, kLinear_GradType); }
 static SkBenchmark* Fact01(void* p) { return new GradientBench(p, kLinear_GradType, SkShader::kMirror_TileMode); }
-static SkBenchmark* Fact1(void* p) { return new GradientBench(p, kRadial_GradType); }
+
+// Draw a radial gradient of radius 1/2 on a rectangle; half the lines should
+// be completely pinned, the other half should pe partially pinned
+static SkBenchmark* Fact1(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kClamp_TileMode, kRect_GeomType, 0.5f); }
+
+// Draw a radial gradient on a circle of equal size; all the lines should
+// hit the unpinned fast path (so long as GradientBench.W == H)
+static SkBenchmark* Fact1o(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kClamp_TileMode, kOval_GeomType); }
+
 static SkBenchmark* Fact11(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kMirror_TileMode); }
 static SkBenchmark* Fact2(void* p) { return new GradientBench(p, kSweep_GradType); }
 static SkBenchmark* Fact3(void* p) { return new GradientBench(p, kRadial2_GradType); }
@@ -207,6 +262,7 @@
 static BenchRegistry gReg0(Fact0);
 static BenchRegistry gReg01(Fact01);
 static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg1o(Fact1o);
 static BenchRegistry gReg11(Fact11);
 static BenchRegistry gReg2(Fact2);
 static BenchRegistry gReg3(Fact3);