diff options
-rw-r--r-- | bench/MathBench.cpp | 36 | ||||
-rw-r--r-- | include/core/SkMath.h | 22 | ||||
-rw-r--r-- | src/core/SkBitmap.cpp | 3 | ||||
-rw-r--r-- | src/core/SkScaledImageCache.cpp | 7 | ||||
-rw-r--r-- | tests/MathTest.cpp | 71 |
5 files changed, 133 insertions, 6 deletions
diff --git a/bench/MathBench.cpp b/bench/MathBench.cpp index c34be44303..260159f3f0 100644 --- a/bench/MathBench.cpp +++ b/bench/MathBench.cpp @@ -512,6 +512,42 @@ private: /////////////////////////////////////////////////////////////////////////////// +template <typename T> +class DivModBench : public SkBenchmark { + const char* fName; +public: + explicit DivModBench(const char* name) : fName(name) { + fIsRendering = false; + } + +protected: + virtual const char* onGetName() { + return SkStringPrintf("divmod_%s", fName).c_str(); + } + + virtual void onDraw(SkCanvas*) { + volatile T a = 0, b = 0; + T div = 0, mod = 0; + for (int i = 0; i < this->getLoops(); i++) { + if ((T)i == 0) continue; // Small T will wrap around. + SkTDivMod((T)(i+1), (T)i, &div, &mod); + a ^= div; + b ^= mod; + } + } +}; +DEF_BENCH(return new DivModBench<uint8_t>("uint8_t")) +DEF_BENCH(return new DivModBench<uint16_t>("uint16_t")) +DEF_BENCH(return new DivModBench<uint32_t>("uint32_t")) +DEF_BENCH(return new DivModBench<uint64_t>("uint64_t")) + +DEF_BENCH(return new DivModBench<int8_t>("int8_t")) +DEF_BENCH(return new DivModBench<int16_t>("int16_t")) +DEF_BENCH(return new DivModBench<int32_t>("int32_t")) +DEF_BENCH(return new DivModBench<int64_t>("int64_t")) + +/////////////////////////////////////////////////////////////////////////////// + DEF_BENCH( return new NoOpMathBench(); ) DEF_BENCH( return new SlowISqrtMathBench(); ) DEF_BENCH( return new FastISqrtMathBench(); ) diff --git a/include/core/SkMath.h b/include/core/SkMath.h index 078c8fccdc..affcadaf2c 100644 --- a/include/core/SkMath.h +++ b/include/core/SkMath.h @@ -173,4 +173,26 @@ static inline U8CPU SkMulDiv255Round(U16CPU a, U16CPU b) { return (prod + (prod >> 8)) >> 8; } +/** + * Stores numer/denom and numer%denom into div and mod respectively. + */ +template <typename In, typename Out> +inline void SkTDivMod(In numer, In denom, Out* div, Out* mod) { +#ifdef SK_CPU_ARM + // If we wrote this as in the else branch, GCC won't fuse the two into one + // divmod call, but rather a div call followed by a divmod. Silly! This + // version is just as fast as calling __aeabi_[u]idivmod manually, but with + // prettier code. + // + // This benches as around 2x faster than the code in the else branch. + const In d = numer/denom; + *div = static_cast<Out>(d); + *mod = static_cast<Out>(numer-d*denom); +#else + // On x86 this will just be a single idiv. + *div = static_cast<Out>(numer/denom); + *mod = static_cast<Out>(numer%denom); +#endif // SK_CPU_ARM +} + #endif diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index d3bbecd706..128726cb63 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -911,9 +911,8 @@ bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t r return true; } // Use integer division to find the correct y position. - *y = SkToS32(offset / rowBytes); // The remainder will be the x position, after we reverse get_sub_offset. - *x = SkToS32(offset % rowBytes); + SkTDivMod(offset, rowBytes, y, x); switch (config) { case SkBitmap::kA8_Config: // Fall through. diff --git a/src/core/SkScaledImageCache.cpp b/src/core/SkScaledImageCache.cpp index 75dac78598..11a0ee448f 100644 --- a/src/core/SkScaledImageCache.cpp +++ b/src/core/SkScaledImageCache.cpp @@ -48,10 +48,9 @@ struct Key { return false; } - size_t offset = bm.pixelRefOffset(); - size_t rowBytes = bm.rowBytes(); - int x = (offset % rowBytes) >> 2; - int y = offset / rowBytes; + size_t x, y; + SkTDivMod(bm.pixelRefOffset(), bm.rowBytes(), &y, &x); + x >>= 2; fGenID = pr->getGenerationID(); fBounds.set(x, y, x + bm.width(), y + bm.height()); diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp index cb4d0b8bd2..bc8e6a32de 100644 --- a/tests/MathTest.cpp +++ b/tests/MathTest.cpp @@ -691,3 +691,74 @@ static void TestEndian(skiatest::Reporter* reporter) { } DEFINE_TESTCLASS("Endian", EndianTestClass, TestEndian) + +template <typename T> +static void test_divmod(skiatest::Reporter* r) { + const struct { + T numer; + T denom; + } kEdgeCases[] = { + {(T)17, (T)17}, + {(T)17, (T)4}, + {(T)0, (T)17}, + // For unsigned T these negatives are just some large numbers. Doesn't hurt to test them. + {(T)-17, (T)-17}, + {(T)-17, (T)4}, + {(T)17, (T)-4}, + {(T)-17, (T)-4}, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(kEdgeCases); i++) { + const T numer = kEdgeCases[i].numer; + const T denom = kEdgeCases[i].denom; + T div, mod; + SkTDivMod(numer, denom, &div, &mod); + REPORTER_ASSERT(r, numer/denom == div); + REPORTER_ASSERT(r, numer%denom == mod); + } + + SkRandom rand; + for (size_t i = 0; i < 10000; i++) { + const T numer = (T)rand.nextS(); + T denom = 0; + while (0 == denom) { + denom = (T)rand.nextS(); + } + T div, mod; + SkTDivMod(numer, denom, &div, &mod); + REPORTER_ASSERT(r, numer/denom == div); + REPORTER_ASSERT(r, numer%denom == mod); + } +} + +DEF_TEST(divmod_u8, r) { + test_divmod<uint8_t>(r); +} + +DEF_TEST(divmod_u16, r) { + test_divmod<uint16_t>(r); +} + +DEF_TEST(divmod_u32, r) { + test_divmod<uint32_t>(r); +} + +DEF_TEST(divmod_u64, r) { + test_divmod<uint64_t>(r); +} + +DEF_TEST(divmod_s8, r) { + test_divmod<int8_t>(r); +} + +DEF_TEST(divmod_s16, r) { + test_divmod<int16_t>(r); +} + +DEF_TEST(divmod_s32, r) { + test_divmod<int32_t>(r); +} + +DEF_TEST(divmod_s64, r) { + test_divmod<int64_t>(r); +} |