From 47df89ebfd548de2ccd91387e1d7eee418b65f96 Mon Sep 17 00:00:00 2001 From: reed Date: Thu, 30 Jun 2016 06:38:54 -0700 Subject: [PATCH] speed up maprect for scale+trans case BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2111703002 Review-Url: https://codereview.chromium.org/2111703002 --- bench/MatrixBench.cpp | 34 ++++++++++++++++++++++++++++++++++ include/core/SkMatrix.h | 6 ++++++ src/core/SkMatrix.cpp | 26 +++++++++++++++++++++++--- tests/MatrixTest.cpp | 26 ++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp index a185fec..53e7296 100644 --- a/bench/MatrixBench.cpp +++ b/bench/MatrixBench.cpp @@ -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); ) diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h index 6738a0e..d6d0295 100644 --- a/include/core/SkMatrix.h +++ b/include/core/SkMatrix.h @@ -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. diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp index 5711bd9..492f3f6 100644 --- a/src/core/SkMatrix.cpp +++ b/src/core/SkMatrix.cpp @@ -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]; diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp index 2f91cca..01b2ba4 100644 --- a/tests/MatrixTest.cpp +++ b/tests/MatrixTest.cpp @@ -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]); + } +} -- 2.7.4