1 /*
2 * Copyright (c) 2024, Alliance for Open Media. All rights reserved.
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 #include <memory>
13 #include <new>
14
15 #include "config/av1_rtcd.h"
16 #include "aom_ports/aom_timer.h"
17 #include "aom_ports/bitops.h"
18 #include "gtest/gtest.h"
19 #include "test/acm_random.h"
20 #include "test/util.h"
21
22 namespace {
23
24 using ::testing::Combine;
25 using ::testing::Values;
26 using ::testing::ValuesIn;
27
28 using std::make_tuple;
29 using std::tuple;
30
31 const int kIters = 1000;
32
33 typedef tuple<int, int> FrameDimension;
34
35 // Check that two 8-bit output buffers are identical.
AssertOutputBufferEq(const uint8_t * p1,const uint8_t * p2,int width,int height)36 void AssertOutputBufferEq(const uint8_t *p1, const uint8_t *p2, int width,
37 int height) {
38 ASSERT_TRUE(p1 != p2) << "Buffers must be at different memory locations";
39 for (int j = 0; j < height; ++j) {
40 if (memcmp(p1, p2, sizeof(*p1) * width) == 0) {
41 p1 += width;
42 p2 += width;
43 continue;
44 }
45 for (int i = 0; i < width; ++i) {
46 ASSERT_EQ(p1[i], p2[i])
47 << width << "x" << height << " Pixel mismatch at (" << i << ", " << j
48 << ")";
49 }
50 }
51 }
52
53 typedef bool (*LowBDResizeFunc)(uint8_t *intbuf, uint8_t *output,
54 int out_stride, int height, int height2,
55 int stride, int start_wd);
56 // Test parameter list:
57 // <tst_fun, dims>
58 typedef tuple<LowBDResizeFunc, FrameDimension> ResizeTestParams;
59
60 class AV1ResizeYTest : public ::testing::TestWithParam<ResizeTestParams> {
61 public:
SetUp()62 void SetUp() {
63 test_fun_ = GET_PARAM(0);
64 frame_dim_ = GET_PARAM(1);
65 width_ = std::get<0>(frame_dim_);
66 height_ = std::get<1>(frame_dim_);
67 const int msb = get_msb(AOMMIN(width_, height_));
68 n_levels_ = AOMMAX(msb - MIN_PYRAMID_SIZE_LOG2, 1);
69 const int src_buf_size = (width_ / 2) * height_;
70 const int dest_buf_size = (width_ * height_) / 4;
71 src_ = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[src_buf_size]);
72 ASSERT_NE(src_, nullptr);
73
74 ref_dest_ =
75 std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]);
76 ASSERT_NE(ref_dest_, nullptr);
77
78 test_dest_ =
79 std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]);
80 ASSERT_NE(test_dest_, nullptr);
81 }
82
RunTest()83 void RunTest() {
84 for (int i = 0; i < (width_ / 2) * height_; i++) src_[i] = rng_.Rand8();
85 for (int level = 1; level < n_levels_; level++) {
86 const int width2 = (width_ >> level);
87 const int height2 = (height_ >> level);
88 av1_resize_vert_dir_c(src_.get(), ref_dest_.get(), width2, height2 << 1,
89 height2, width2, 0);
90 test_fun_(src_.get(), test_dest_.get(), width2, height2 << 1, height2,
91 width2, 0);
92
93 AssertOutputBufferEq(ref_dest_.get(), test_dest_.get(), width2, height2);
94 }
95 }
96
SpeedTest()97 void SpeedTest() {
98 for (int i = 0; i < (width_ / 2) * height_; i++) src_[i] = rng_.Rand8();
99 for (int level = 1; level < n_levels_; level++) {
100 const int width2 = (width_ >> level);
101 const int height2 = (height_ >> level);
102 aom_usec_timer ref_timer;
103 aom_usec_timer_start(&ref_timer);
104 for (int j = 0; j < kIters; j++) {
105 av1_resize_vert_dir_c(src_.get(), ref_dest_.get(), width2, height2 << 1,
106 height2, width2, 0);
107 }
108 aom_usec_timer_mark(&ref_timer);
109 const int64_t ref_time = aom_usec_timer_elapsed(&ref_timer);
110
111 aom_usec_timer tst_timer;
112 aom_usec_timer_start(&tst_timer);
113 for (int j = 0; j < kIters; j++) {
114 test_fun_(src_.get(), test_dest_.get(), width2, height2 << 1, height2,
115 width2, 0);
116 }
117 aom_usec_timer_mark(&tst_timer);
118 const int64_t tst_time = aom_usec_timer_elapsed(&tst_timer);
119
120 std::cout << "level: " << level << " [" << width2 << " x " << height2
121 << "] C time = " << ref_time << " , SIMD time = " << tst_time
122 << " scaling=" << float(1.00) * ref_time / tst_time << "x \n";
123 }
124 }
125
126 private:
127 LowBDResizeFunc test_fun_;
128 FrameDimension frame_dim_;
129 int width_;
130 int height_;
131 int n_levels_;
132 std::unique_ptr<uint8_t[]> src_;
133 std::unique_ptr<uint8_t[]> ref_dest_;
134 std::unique_ptr<uint8_t[]> test_dest_;
135 libaom_test::ACMRandom rng_;
136 };
137
138 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1ResizeYTest);
139
TEST_P(AV1ResizeYTest,RunTest)140 TEST_P(AV1ResizeYTest, RunTest) { RunTest(); }
141
TEST_P(AV1ResizeYTest,DISABLED_SpeedTest)142 TEST_P(AV1ResizeYTest, DISABLED_SpeedTest) { SpeedTest(); }
143
144 #if HAVE_AVX2 || HAVE_SSE2
145 // Resolutions (width x height) to be tested for resizing.
146 const FrameDimension kFrameDim[] = {
147 make_tuple(3840, 2160), make_tuple(2560, 1440), make_tuple(1920, 1080),
148 make_tuple(1280, 720), make_tuple(640, 480), make_tuple(640, 360),
149 make_tuple(286, 286), make_tuple(284, 284), make_tuple(282, 282),
150 make_tuple(280, 280), make_tuple(262, 262), make_tuple(258, 258),
151 make_tuple(256, 256), make_tuple(34, 34),
152 };
153 #endif
154
155 #if HAVE_AVX2
156 INSTANTIATE_TEST_SUITE_P(
157 AVX2, AV1ResizeYTest,
158 ::testing::Combine(::testing::Values(av1_resize_vert_dir_avx2),
159 ::testing::ValuesIn(kFrameDim)));
160 #endif
161
162 #if HAVE_SSE2
163 INSTANTIATE_TEST_SUITE_P(
164 SSE2, AV1ResizeYTest,
165 ::testing::Combine(::testing::Values(av1_resize_vert_dir_sse2),
166 ::testing::ValuesIn(kFrameDim)));
167 #endif
168
169 typedef void (*LowBDResize_x_Func)(const uint8_t *const input, int in_stride,
170 uint8_t *intbuf, int height,
171 int filtered_length, int width2);
172
173 typedef tuple<LowBDResize_x_Func, FrameDimension> Resize_x_TestParams;
174
175 class AV1ResizeXTest : public ::testing::TestWithParam<Resize_x_TestParams> {
176 public:
SetUp()177 void SetUp() {
178 test_fun_ = GET_PARAM(0);
179 frame_dim_ = GET_PARAM(1);
180 width_ = std::get<0>(frame_dim_);
181 height_ = std::get<1>(frame_dim_);
182 const int msb = get_msb(AOMMIN(width_, height_));
183 n_levels_ = AOMMAX(msb - MIN_PYRAMID_SIZE_LOG2, 1);
184 const int src_buf_size = width_ * height_;
185 const int dest_buf_size = (width_ * height_) / 2;
186 src_ = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[src_buf_size]);
187 ASSERT_NE(src_, nullptr);
188
189 ref_dest_ =
190 std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]);
191 ASSERT_NE(ref_dest_, nullptr);
192
193 test_dest_ =
194 std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]);
195 ASSERT_NE(test_dest_, nullptr);
196 }
197
RunTest()198 void RunTest() {
199 for (int i = 0; i < width_ * height_; ++i) src_[i] = rng_.Rand8();
200
201 for (int level = 1; level < n_levels_; ++level) {
202 const int width2 = (width_ >> level);
203 av1_resize_horz_dir_c(src_.get(), width_, ref_dest_.get(), height_,
204 width2 << 1, width2);
205 test_fun_(src_.get(), width_, test_dest_.get(), height_, width2 << 1,
206 width2);
207 AssertOutputBufferEq(ref_dest_.get(), test_dest_.get(), width2, height_);
208 }
209 }
210
SpeedTest()211 void SpeedTest() {
212 for (int i = 0; i < width_ * height_; ++i) src_[i] = rng_.Rand8();
213
214 for (int level = 1; level < n_levels_; ++level) {
215 const int width2 = (width_ >> level);
216 aom_usec_timer ref_timer;
217 aom_usec_timer_start(&ref_timer);
218 for (int j = 0; j < kIters; ++j) {
219 av1_resize_horz_dir_c(src_.get(), width_, ref_dest_.get(), height_,
220 width2 << 1, width2);
221 }
222 aom_usec_timer_mark(&ref_timer);
223 const int64_t ref_time = aom_usec_timer_elapsed(&ref_timer);
224
225 aom_usec_timer tst_timer;
226 aom_usec_timer_start(&tst_timer);
227 for (int j = 0; j < kIters; ++j) {
228 test_fun_(src_.get(), width_, test_dest_.get(), height_, width2 << 1,
229 width2);
230 }
231 aom_usec_timer_mark(&tst_timer);
232 const int64_t tst_time = aom_usec_timer_elapsed(&tst_timer);
233
234 std::cout << "level: " << level << " [" << width2 << " x " << height_
235 << "] C time = " << ref_time << " , SIMD time = " << tst_time
236 << " scaling=" << float(1.00) * ref_time / tst_time << "x \n";
237 }
238 }
239
240 private:
241 LowBDResize_x_Func test_fun_;
242 FrameDimension frame_dim_;
243 int width_;
244 int height_;
245 int n_levels_;
246 std::unique_ptr<uint8_t[]> src_;
247 std::unique_ptr<uint8_t[]> ref_dest_;
248 std::unique_ptr<uint8_t[]> test_dest_;
249 libaom_test::ACMRandom rng_;
250 };
251
252 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1ResizeXTest);
253
TEST_P(AV1ResizeXTest,RunTest)254 TEST_P(AV1ResizeXTest, RunTest) { RunTest(); }
255
TEST_P(AV1ResizeXTest,DISABLED_SpeedTest)256 TEST_P(AV1ResizeXTest, DISABLED_SpeedTest) { SpeedTest(); }
257
258 #if HAVE_SSE2
259 INSTANTIATE_TEST_SUITE_P(
260 SSE2, AV1ResizeXTest,
261 ::testing::Combine(::testing::Values(av1_resize_horz_dir_sse2),
262 ::testing::ValuesIn(kFrameDim)));
263 #endif
264
265 #if HAVE_AVX2
266 INSTANTIATE_TEST_SUITE_P(
267 AVX2, AV1ResizeXTest,
268 ::testing::Combine(::testing::Values(av1_resize_horz_dir_avx2),
269 ::testing::ValuesIn(kFrameDim)));
270 #endif
271
272 } // namespace
273