1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/base_paths.h"
6#include "base/cpu.h"
7#include "base/files/file_util.h"
8#include "base/logging.h"
9#include "base/path_service.h"
10#include "base/time/time.h"
11#include "media/base/simd/convert_yuv_to_rgb.h"
12#include "media/base/yuv_convert.h"
13#include "testing/gtest/include/gtest/gtest.h"
14#include "testing/perf/perf_test.h"
15
16namespace media {
17#if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
18// Size of raw image.
19static const int kSourceWidth = 640;
20static const int kSourceHeight = 360;
21static const int kSourceYSize = kSourceWidth * kSourceHeight;
22static const int kSourceUOffset = kSourceYSize;
23static const int kSourceVOffset = kSourceYSize * 5 / 4;
24static const int kBpp = 4;
25
26// Width of the row to convert. Odd so that we exercise the ending
27// one-pixel-leftover case.
28static const int kWidth = 639;
29
30// Surface sizes for various test files.
31static const int kYUV12Size = kSourceYSize * 12 / 8;
32static const int kRGBSize = kSourceYSize * kBpp;
33
34static const int kPerfTestIterations = 2000;
35
36class YUVConvertPerfTest : public testing::Test {
37 public:
38  YUVConvertPerfTest()
39      : yuv_bytes_(new uint8[kYUV12Size]),
40        rgb_bytes_converted_(new uint8[kRGBSize]) {
41    base::FilePath path;
42    CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path));
43    path = path.Append(FILE_PATH_LITERAL("media"))
44               .Append(FILE_PATH_LITERAL("test"))
45               .Append(FILE_PATH_LITERAL("data"))
46               .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv"));
47
48    // Verify file size is correct.
49    int64 actual_size = 0;
50    base::GetFileSize(path, &actual_size);
51    CHECK_EQ(actual_size, kYUV12Size);
52
53    // Verify bytes read are correct.
54    int bytes_read = base::ReadFile(
55        path, reinterpret_cast<char*>(yuv_bytes_.get()), kYUV12Size);
56
57    CHECK_EQ(bytes_read, kYUV12Size);
58  }
59
60  scoped_ptr<uint8[]> yuv_bytes_;
61  scoped_ptr<uint8[]> rgb_bytes_converted_;
62
63 private:
64  DISALLOW_COPY_AND_ASSIGN(YUVConvertPerfTest);
65};
66
67TEST_F(YUVConvertPerfTest, ConvertYUVToRGB32Row_SSE) {
68  ASSERT_TRUE(base::CPU().has_sse());
69
70  base::TimeTicks start = base::TimeTicks::HighResNow();
71  for (int i = 0; i < kPerfTestIterations; ++i) {
72    for (int row = 0; row < kSourceHeight; ++row) {
73      int chroma_row = row / 2;
74      ConvertYUVToRGB32Row_SSE(
75          yuv_bytes_.get() + row * kSourceWidth,
76          yuv_bytes_.get() + kSourceUOffset + (chroma_row * kSourceWidth / 2),
77          yuv_bytes_.get() + kSourceVOffset + (chroma_row * kSourceWidth / 2),
78          rgb_bytes_converted_.get(),
79          kWidth,
80          GetLookupTable(YV12));
81    }
82  }
83  double total_time_seconds =
84      (base::TimeTicks::HighResNow() - start).InSecondsF();
85  perf_test::PrintResult(
86      "yuv_convert_perftest", "", "ConvertYUVToRGB32Row_SSE",
87      kPerfTestIterations / total_time_seconds, "runs/s", true);
88  media::EmptyRegisterState();
89}
90
91// 64-bit release + component builds on Windows are too smart and optimizes
92// away the function being tested.
93#if defined(OS_WIN) && (defined(ARCH_CPU_X86) || !defined(COMPONENT_BUILD))
94TEST_F(YUVConvertPerfTest, ScaleYUVToRGB32Row_SSE) {
95  ASSERT_TRUE(base::CPU().has_sse());
96
97  const int kSourceDx = 80000;  // This value means a scale down.
98
99  base::TimeTicks start = base::TimeTicks::HighResNow();
100  for (int i = 0; i < kPerfTestIterations; ++i) {
101    for (int row = 0; row < kSourceHeight; ++row) {
102      int chroma_row = row / 2;
103      ScaleYUVToRGB32Row_SSE(
104          yuv_bytes_.get() + row * kSourceWidth,
105          yuv_bytes_.get() + kSourceUOffset + (chroma_row * kSourceWidth / 2),
106          yuv_bytes_.get() + kSourceVOffset + (chroma_row * kSourceWidth / 2),
107          rgb_bytes_converted_.get(),
108          kWidth,
109          kSourceDx,
110          GetLookupTable(YV12));
111    }
112  }
113  double total_time_seconds =
114      (base::TimeTicks::HighResNow() - start).InSecondsF();
115  perf_test::PrintResult(
116      "yuv_convert_perftest", "", "ScaleYUVToRGB32Row_SSE",
117      kPerfTestIterations / total_time_seconds, "runs/s", true);
118  media::EmptyRegisterState();
119}
120
121TEST_F(YUVConvertPerfTest, LinearScaleYUVToRGB32Row_SSE) {
122  ASSERT_TRUE(base::CPU().has_sse());
123
124  const int kSourceDx = 80000;  // This value means a scale down.
125
126  base::TimeTicks start = base::TimeTicks::HighResNow();
127  for (int i = 0; i < kPerfTestIterations; ++i) {
128    for (int row = 0; row < kSourceHeight; ++row) {
129      int chroma_row = row / 2;
130      LinearScaleYUVToRGB32Row_SSE(
131          yuv_bytes_.get() + row * kSourceWidth,
132          yuv_bytes_.get() + kSourceUOffset + (chroma_row * kSourceWidth / 2),
133          yuv_bytes_.get() + kSourceVOffset + (chroma_row * kSourceWidth / 2),
134          rgb_bytes_converted_.get(),
135          kWidth,
136          kSourceDx,
137          GetLookupTable(YV12));
138    }
139  }
140  double total_time_seconds =
141      (base::TimeTicks::HighResNow() - start).InSecondsF();
142  perf_test::PrintResult(
143      "yuv_convert_perftest", "", "LinearScaleYUVToRGB32Row_SSE",
144      kPerfTestIterations / total_time_seconds, "runs/s", true);
145  media::EmptyRegisterState();
146}
147#endif  // defined(OS_WIN) && (ARCH_CPU_X86 || COMPONENT_BUILD)
148
149#endif  // !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
150
151}  // namespace media
152