speed up maprect for scale+trans case
authorreed <reed@google.com>
Thu, 30 Jun 2016 13:38:54 +0000 (06:38 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 30 Jun 2016 13:38:54 +0000 (06:38 -0700)
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2111703002

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

bench/MatrixBench.cpp
include/core/SkMatrix.h
src/core/SkMatrix.cpp
tests/MatrixTest.cpp

index a185fec..53e7296 100644 (file)
@@ -297,3 +297,37 @@ DEF_BENCH( return new MapPointsMatrixBench("mappoints_identity", SkMatrix::I());
 DEF_BENCH( return new MapPointsMatrixBench("mappoints_trans", make_trans()); )
 DEF_BENCH( return new MapPointsMatrixBench("mappoints_scale", make_scale()); )
 DEF_BENCH( return new MapPointsMatrixBench("mappoints_affine", make_afine()); )
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MapRectMatrixBench : public MatrixBench {
+    SkMatrix fM;
+    SkRect   fR;
+    bool     fScaleTrans;
+
+    enum { MEGA_LOOP = 1000 * 1000 };
+public:
+    MapRectMatrixBench(const char name[], bool scale_trans)
+        : MatrixBench(name), fScaleTrans(scale_trans)
+    {
+        fM.setScale(2, 3);
+        fM.postTranslate(1, 2);
+
+        fR.set(10, 10, 100, 200);
+    }
+
+    void performTest() override {
+        SkRect dst;
+        if (fScaleTrans) {
+            for (int i = 0; i < MEGA_LOOP; ++i) {
+                fM.mapRectScaleTranslate(&dst, fR);
+            }
+        } else {
+            for (int i = 0; i < MEGA_LOOP; ++i) {
+                fM.mapRect(&dst, fR);
+            }
+        }
+    }
+};
+DEF_BENCH( return new MapRectMatrixBench("maprect", false); )
+DEF_BENCH( return new MapRectMatrixBench("maprectscaletrans", true); )
index 6738a0e..d6d0295 100644 (file)
@@ -561,6 +561,12 @@ public:
         this->mapPoints(dst, 4);
     }
 
+    /**
+     *  Maps a rect to another rect, asserting (in debug mode) that the matrix only contains
+     *  scale and translate elements. If it contains other elements, the results are undefined.
+     */
+    void mapRectScaleTranslate(SkRect* dst, const SkRect& src) const;
+    
     /** Return the mean radius of a circle after it has been mapped by
         this matrix. NOTE: in perspective this value assumes the circle
         has its center at the origin.
index 5711bd9..492f3f6 100644 (file)
@@ -1097,12 +1097,32 @@ void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
     }
 }
 
+void SkMatrix::mapRectScaleTranslate(SkRect* dst, const SkRect& src) const {
+    SkASSERT(dst);
+    SkASSERT(this->isScaleTranslate());
+    
+    SkScalar sx = fMat[kMScaleX];
+    SkScalar sy = fMat[kMScaleY];
+    SkScalar tx = fMat[kMTransX];
+    SkScalar ty = fMat[kMTransY];
+    Sk4f scale(sx, sy, sx, sy);
+    Sk4f trans(tx, ty, tx, ty);
+
+    Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
+    // need to sort so we're not inverted
+    Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
+    Sk4f min = Sk4f::Min(ltrb, rblt);
+    Sk4f max = Sk4f::Max(ltrb, rblt);
+    // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
+    // ARM this sequence generates the fastest (a single instruction).
+    Sk4f(min[2], min[3], max[0], max[1]).store(&dst->fLeft);
+}
+
 bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
     SkASSERT(dst);
 
-    if (this->rectStaysRect()) {
-        this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
-        dst->sort();
+    if (this->isScaleTranslate()) {
+        this->mapRectScaleTranslate(dst, src);
         return true;
     } else {
         SkPoint quad[4];
index 2f91cca..01b2ba4 100644 (file)
@@ -953,3 +953,29 @@ DEF_TEST(Matrix_Concat, r) {
 
     REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));
 }
+
+// Test that all variants of maprect are correct.
+DEF_TEST(Matrix_maprects, r) {
+    const SkScalar scale = 1000;
+    
+    SkMatrix mat;
+    mat.setScale(2, 3);
+    mat.postTranslate(1, 4);
+
+    SkRandom rand;
+    for (int i = 0; i < 10000; ++i) {
+        SkRect src = SkRect::MakeLTRB(rand.nextSScalar1() * scale,
+                                      rand.nextSScalar1() * scale,
+                                      rand.nextSScalar1() * scale,
+                                      rand.nextSScalar1() * scale);
+        SkRect dst[3];
+        
+        mat.mapPoints((SkPoint*)&dst[0].fLeft, (SkPoint*)&src.fLeft, 2);
+        dst[0].sort();
+        mat.mapRect(&dst[1], src);
+        mat.mapRectScaleTranslate(&dst[2], src);
+
+        REPORTER_ASSERT(r, dst[0] == dst[1]);
+        REPORTER_ASSERT(r, dst[0] == dst[2]);
+    }
+}