| OLD | NEW |
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2010 Google Inc. | 3 * Copyright 2010 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 // #define TEST_UNCACHED 1 | 58 // #define TEST_UNCACHED 1 |
| 59 // #define TEST_RSTSC 1 | 59 // #define TEST_RSTSC 1 |
| 60 #endif | 60 #endif |
| 61 | 61 |
| 62 #if defined(TEST_UNCACHED) || defined(TEST_RSTSC) | 62 #if defined(TEST_UNCACHED) || defined(TEST_RSTSC) |
| 63 #ifdef _MSC_VER | 63 #ifdef _MSC_VER |
| 64 #include <emmintrin.h> // NOLINT | 64 #include <emmintrin.h> // NOLINT |
| 65 #endif | 65 #endif |
| 66 | 66 |
| 67 #if defined(__GNUC__) && defined(__i386__) | 67 #if defined(__GNUC__) && defined(__i386__) |
| 68 static inline uint64 __rdtsc(void) { | 68 static inline uint64_t __rdtsc(void) { |
| 69 uint32_t a, d; | 69 uint32_t a, d; |
| 70 __asm__ volatile("rdtsc" : "=a" (a), "=d" (d)); | 70 __asm__ volatile("rdtsc" : "=a" (a), "=d" (d)); |
| 71 return (reinterpret_cast<uint64>(d) << 32) + a; | 71 return (reinterpret_cast<uint64_t>(d) << 32) + a; |
| 72 } | 72 } |
| 73 | 73 |
| 74 static inline void _mm_clflush(volatile void *__p) { | 74 static inline void _mm_clflush(volatile void *__p) { |
| 75 asm volatile("clflush %0" : "+m" (*(volatile char *)__p)); | 75 asm volatile("clflush %0" : "+m" (*(volatile char *)__p)); |
| 76 } | 76 } |
| 77 #endif | 77 #endif |
| 78 | 78 |
| 79 static void FlushCache(uint8* dst, int count) { | 79 static void FlushCache(uint8_t* dst, int count) { |
| 80 while (count >= 32) { | 80 while (count >= 32) { |
| 81 _mm_clflush(dst); | 81 _mm_clflush(dst); |
| 82 dst += 32; | 82 dst += 32; |
| 83 count -= 32; | 83 count -= 32; |
| 84 } | 84 } |
| 85 } | 85 } |
| 86 #endif | 86 #endif |
| 87 | 87 |
| 88 class YuvScalerTest : public testing::Test { | 88 class YuvScalerTest : public testing::Test { |
| 89 protected: | 89 protected: |
| 90 virtual void SetUp() { | 90 virtual void SetUp() { |
| 91 dump_ = *rtc::FlagList::Lookup("yuvscaler_dump")->bool_variable(); | 91 dump_ = *rtc::FlagList::Lookup("yuvscaler_dump")->bool_variable(); |
| 92 repeat_ = *rtc::FlagList::Lookup("yuvscaler_repeat")->int_variable(); | 92 repeat_ = *rtc::FlagList::Lookup("yuvscaler_repeat")->int_variable(); |
| 93 } | 93 } |
| 94 | 94 |
| 95 // Scale an image and compare against a Lanczos-filtered test image. | 95 // Scale an image and compare against a Lanczos-filtered test image. |
| 96 // Lanczos is considered to be the "ideal" image resampling method, so we try | 96 // Lanczos is considered to be the "ideal" image resampling method, so we try |
| 97 // to get as close to that as possible, while being as fast as possible. | 97 // to get as close to that as possible, while being as fast as possible. |
| 98 bool TestScale(int iw, int ih, int ow, int oh, int offset, bool usefile, | 98 bool TestScale(int iw, int ih, int ow, int oh, int offset, bool usefile, |
| 99 bool optimize, int cpuflags, bool interpolate, | 99 bool optimize, int cpuflags, bool interpolate, |
| 100 int memoffset, double* error) { | 100 int memoffset, double* error) { |
| 101 *error = 0.; | 101 *error = 0.; |
| 102 size_t isize = I420_SIZE(iw, ih); | 102 size_t isize = I420_SIZE(iw, ih); |
| 103 size_t osize = I420_SIZE(ow, oh); | 103 size_t osize = I420_SIZE(ow, oh); |
| 104 scoped_ptr<uint8[]> ibuffer(new uint8[isize + kAlignment + memoffset]()); | 104 scoped_ptr<uint8_t[]> ibuffer( |
| 105 scoped_ptr<uint8[]> obuffer(new uint8[osize + kAlignment + memoffset]()); | 105 new uint8_t[isize + kAlignment + memoffset]()); |
| 106 scoped_ptr<uint8[]> xbuffer(new uint8[osize + kAlignment + memoffset]()); | 106 scoped_ptr<uint8_t[]> obuffer( |
| 107 new uint8_t[osize + kAlignment + memoffset]()); |
| 108 scoped_ptr<uint8_t[]> xbuffer( |
| 109 new uint8_t[osize + kAlignment + memoffset]()); |
| 107 | 110 |
| 108 uint8 *ibuf = ALIGNP(ibuffer.get(), kAlignment) + memoffset; | 111 uint8_t* ibuf = ALIGNP(ibuffer.get(), kAlignment) + memoffset; |
| 109 uint8 *obuf = ALIGNP(obuffer.get(), kAlignment) + memoffset; | 112 uint8_t* obuf = ALIGNP(obuffer.get(), kAlignment) + memoffset; |
| 110 uint8 *xbuf = ALIGNP(xbuffer.get(), kAlignment) + memoffset; | 113 uint8_t* xbuf = ALIGNP(xbuffer.get(), kAlignment) + memoffset; |
| 111 | 114 |
| 112 if (usefile) { | 115 if (usefile) { |
| 113 if (!LoadPlanarYuvTestImage("faces", iw, ih, ibuf) || | 116 if (!LoadPlanarYuvTestImage("faces", iw, ih, ibuf) || |
| 114 !LoadPlanarYuvTestImage("faces", ow, oh, xbuf)) { | 117 !LoadPlanarYuvTestImage("faces", ow, oh, xbuf)) { |
| 115 LOG(LS_ERROR) << "Failed to load image"; | 118 LOG(LS_ERROR) << "Failed to load image"; |
| 116 return false; | 119 return false; |
| 117 } | 120 } |
| 118 } else { | 121 } else { |
| 119 // These are used to test huge images. | 122 // These are used to test huge images. |
| 120 memset(ibuf, 213, isize); // Input is constant color. | 123 memset(ibuf, 213, isize); // Input is constant color. |
| 121 memset(obuf, 100, osize); // Output set to something wrong for now. | 124 memset(obuf, 100, osize); // Output set to something wrong for now. |
| 122 memset(xbuf, 213, osize); // Expected result. | 125 memset(xbuf, 213, osize); // Expected result. |
| 123 } | 126 } |
| 124 | 127 |
| 125 #ifdef TEST_UNCACHED | 128 #ifdef TEST_UNCACHED |
| 126 FlushCache(ibuf, isize); | 129 FlushCache(ibuf, isize); |
| 127 FlushCache(obuf, osize); | 130 FlushCache(obuf, osize); |
| 128 FlushCache(xbuf, osize); | 131 FlushCache(xbuf, osize); |
| 129 #endif | 132 #endif |
| 130 | 133 |
| 131 // Scale down. | 134 // Scale down. |
| 132 // If cpu true, disable cpu optimizations. Else allow auto detect | 135 // If cpu true, disable cpu optimizations. Else allow auto detect |
| 133 // TODO(fbarchard): set flags for libyuv | 136 // TODO(fbarchard): set flags for libyuv |
| 134 libyuv::MaskCpuFlags(cpuflags); | 137 libyuv::MaskCpuFlags(cpuflags); |
| 135 #ifdef TEST_RSTSC | 138 #ifdef TEST_RSTSC |
| 136 uint64 t = 0; | 139 uint64_t t = 0; |
| 137 #endif | 140 #endif |
| 138 for (int i = 0; i < repeat_; ++i) { | 141 for (int i = 0; i < repeat_; ++i) { |
| 139 #ifdef TEST_UNCACHED | 142 #ifdef TEST_UNCACHED |
| 140 FlushCache(ibuf, isize); | 143 FlushCache(ibuf, isize); |
| 141 FlushCache(obuf, osize); | 144 FlushCache(obuf, osize); |
| 142 #endif | 145 #endif |
| 143 #ifdef TEST_RSTSC | 146 #ifdef TEST_RSTSC |
| 144 uint64 t1 = __rdtsc(); | 147 uint64_t t1 = __rdtsc(); |
| 145 #endif | 148 #endif |
| 146 EXPECT_EQ(0, libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, | 149 EXPECT_EQ(0, libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, |
| 147 offset, interpolate)); | 150 offset, interpolate)); |
| 148 #ifdef TEST_RSTSC | 151 #ifdef TEST_RSTSC |
| 149 uint64 t2 = __rdtsc(); | 152 uint64_t t2 = __rdtsc(); |
| 150 t += t2 - t1; | 153 t += t2 - t1; |
| 151 #endif | 154 #endif |
| 152 } | 155 } |
| 153 | 156 |
| 154 #ifdef TEST_RSTSC | 157 #ifdef TEST_RSTSC |
| 155 LOG(LS_INFO) << "Time: " << std::setw(9) << t; | 158 LOG(LS_INFO) << "Time: " << std::setw(9) << t; |
| 156 #endif | 159 #endif |
| 157 | 160 |
| 158 if (dump_) { | 161 if (dump_) { |
| 159 const testing::TestInfo* const test_info = | 162 const testing::TestInfo* const test_info = |
| 160 testing::UnitTest::GetInstance()->current_test_info(); | 163 testing::UnitTest::GetInstance()->current_test_info(); |
| 161 std::string test_name(test_info->name()); | 164 std::string test_name(test_info->name()); |
| 162 DumpPlanarYuvTestImage(test_name, obuf, ow, oh); | 165 DumpPlanarYuvTestImage(test_name, obuf, ow, oh); |
| 163 } | 166 } |
| 164 | 167 |
| 165 double sse = cricket::ComputeSumSquareError(obuf, xbuf, osize); | 168 double sse = cricket::ComputeSumSquareError(obuf, xbuf, osize); |
| 166 *error = sse / osize; // Mean Squared Error. | 169 *error = sse / osize; // Mean Squared Error. |
| 167 double PSNR = cricket::ComputePSNR(sse, osize); | 170 double PSNR = cricket::ComputePSNR(sse, osize); |
| 168 LOG(LS_INFO) << "Image MSE: " << | 171 LOG(LS_INFO) << "Image MSE: " << |
| 169 std::setw(6) << std::setprecision(4) << *error << | 172 std::setw(6) << std::setprecision(4) << *error << |
| 170 " Image PSNR: " << PSNR; | 173 " Image PSNR: " << PSNR; |
| 171 return true; | 174 return true; |
| 172 } | 175 } |
| 173 | 176 |
| 174 // Returns the index of the first differing byte. Easier to debug than memcmp. | 177 // Returns the index of the first differing byte. Easier to debug than memcmp. |
| 175 static int FindDiff(const uint8* buf1, const uint8* buf2, int len) { | 178 static int FindDiff(const uint8_t* buf1, const uint8_t* buf2, int len) { |
| 176 int i = 0; | 179 int i = 0; |
| 177 while (i < len && buf1[i] == buf2[i]) { | 180 while (i < len && buf1[i] == buf2[i]) { |
| 178 i++; | 181 i++; |
| 179 } | 182 } |
| 180 return (i < len) ? i : -1; | 183 return (i < len) ? i : -1; |
| 181 } | 184 } |
| 182 | 185 |
| 183 protected: | 186 protected: |
| 184 bool dump_; | 187 bool dump_; |
| 185 int repeat_; | 188 int repeat_; |
| 186 }; | 189 }; |
| 187 | 190 |
| 188 // Tests straight copy of data. | 191 // Tests straight copy of data. |
| 189 TEST_F(YuvScalerTest, TestCopy) { | 192 TEST_F(YuvScalerTest, TestCopy) { |
| 190 const int iw = 640, ih = 360; | 193 const int iw = 640, ih = 360; |
| 191 const int ow = 640, oh = 360; | 194 const int ow = 640, oh = 360; |
| 192 ALIGN16(uint8 ibuf[I420_SIZE(iw, ih)]); | 195 ALIGN16(uint8_t ibuf[I420_SIZE(iw, ih)]); |
| 193 ALIGN16(uint8 obuf[I420_SIZE(ow, oh)]); | 196 ALIGN16(uint8_t obuf[I420_SIZE(ow, oh)]); |
| 194 | 197 |
| 195 // Load the frame, scale it, check it. | 198 // Load the frame, scale it, check it. |
| 196 ASSERT_TRUE(LoadPlanarYuvTestImage("faces", iw, ih, ibuf)); | 199 ASSERT_TRUE(LoadPlanarYuvTestImage("faces", iw, ih, ibuf)); |
| 197 for (int i = 0; i < repeat_; ++i) { | 200 for (int i = 0; i < repeat_; ++i) { |
| 198 libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, 0, false); | 201 libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, 0, false); |
| 199 } | 202 } |
| 200 if (dump_) DumpPlanarYuvTestImage("TestCopy", obuf, ow, oh); | 203 if (dump_) DumpPlanarYuvTestImage("TestCopy", obuf, ow, oh); |
| 201 EXPECT_EQ(-1, FindDiff(obuf, ibuf, sizeof(ibuf))); | 204 EXPECT_EQ(-1, FindDiff(obuf, ibuf, sizeof(ibuf))); |
| 202 } | 205 } |
| 203 | 206 |
| 204 // Tests copy from 4:3 to 16:9. | 207 // Tests copy from 4:3 to 16:9. |
| 205 TEST_F(YuvScalerTest, TestOffset16_10Copy) { | 208 TEST_F(YuvScalerTest, TestOffset16_10Copy) { |
| 206 const int iw = 640, ih = 360; | 209 const int iw = 640, ih = 360; |
| 207 const int ow = 640, oh = 480; | 210 const int ow = 640, oh = 480; |
| 208 const int offset = (480 - 360) / 2; | 211 const int offset = (480 - 360) / 2; |
| 209 scoped_ptr<uint8[]> ibuffer(new uint8[I420_SIZE(iw, ih) + kAlignment]); | 212 scoped_ptr<uint8_t[]> ibuffer(new uint8_t[I420_SIZE(iw, ih) + kAlignment]); |
| 210 scoped_ptr<uint8[]> obuffer(new uint8[I420_SIZE(ow, oh) + kAlignment]); | 213 scoped_ptr<uint8_t[]> obuffer(new uint8_t[I420_SIZE(ow, oh) + kAlignment]); |
| 211 | 214 |
| 212 uint8 *ibuf = ALIGNP(ibuffer.get(), kAlignment); | 215 uint8_t* ibuf = ALIGNP(ibuffer.get(), kAlignment); |
| 213 uint8 *obuf = ALIGNP(obuffer.get(), kAlignment); | 216 uint8_t* obuf = ALIGNP(obuffer.get(), kAlignment); |
| 214 | 217 |
| 215 // Load the frame, scale it, check it. | 218 // Load the frame, scale it, check it. |
| 216 ASSERT_TRUE(LoadPlanarYuvTestImage("faces", iw, ih, ibuf)); | 219 ASSERT_TRUE(LoadPlanarYuvTestImage("faces", iw, ih, ibuf)); |
| 217 | 220 |
| 218 // Clear to black, which is Y = 0 and U and V = 128 | 221 // Clear to black, which is Y = 0 and U and V = 128 |
| 219 memset(obuf, 0, ow * oh); | 222 memset(obuf, 0, ow * oh); |
| 220 memset(obuf + ow * oh, 128, ow * oh / 2); | 223 memset(obuf + ow * oh, 128, ow * oh / 2); |
| 221 for (int i = 0; i < repeat_; ++i) { | 224 for (int i = 0; i < repeat_; ++i) { |
| 222 libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, offset, false); | 225 libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, offset, false); |
| 223 } | 226 } |
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 606 TEST_H(TestScaleDown8xHDOptInt, 1280, 720, 1280 / 8, 720 / 8, true, ALLFLAGS, | 609 TEST_H(TestScaleDown8xHDOptInt, 1280, 720, 1280 / 8, 720 / 8, true, ALLFLAGS, |
| 607 true, 1) | 610 true, 1) |
| 608 | 611 |
| 609 // Tests interpolated 1/8x scale down, using optimized algorithm. | 612 // Tests interpolated 1/8x scale down, using optimized algorithm. |
| 610 TEST_H(TestScaleDown9xHDOptInt, 1280, 720, 1280 / 9, 720 / 9, true, ALLFLAGS, | 613 TEST_H(TestScaleDown9xHDOptInt, 1280, 720, 1280 / 9, 720 / 9, true, ALLFLAGS, |
| 611 true, 1) | 614 true, 1) |
| 612 | 615 |
| 613 // Tests interpolated 1/8x scale down, using optimized algorithm. | 616 // Tests interpolated 1/8x scale down, using optimized algorithm. |
| 614 TEST_H(TestScaleDown10xHDOptInt, 1280, 720, 1280 / 10, 720 / 10, true, ALLFLAGS, | 617 TEST_H(TestScaleDown10xHDOptInt, 1280, 720, 1280 / 10, 720 / 10, true, ALLFLAGS, |
| 615 true, 1) | 618 true, 1) |
| OLD | NEW |