1 /*
2 * Copyright (c) 2020, 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 <cstdlib>
13 #include <new>
14 #include <tuple>
15
16 #include "config/aom_config.h"
17 #include "config/av1_rtcd.h"
18
19 #include "aom/aom_codec.h"
20 #include "aom/aom_integer.h"
21 #include "aom_mem/aom_mem.h"
22 #include "aom_ports/aom_timer.h"
23 #include "aom_ports/mem.h"
24 #include "av1/encoder/palette.h"
25 #include "gtest/gtest.h"
26 #include "test/acm_random.h"
27 #include "test/register_state_check.h"
28 #include "test/util.h"
29
30 namespace AV1Kmeans {
31 typedef void (*av1_calc_indices_dim1_func)(const int16_t *data,
32 const int16_t *centroids,
33 uint8_t *indices,
34 int64_t *total_dist, int n, int k);
35 typedef void (*av1_calc_indices_dim2_func)(const int16_t *data,
36 const int16_t *centroids,
37 uint8_t *indices,
38 int64_t *total_dist, int n, int k);
39
40 typedef std::tuple<av1_calc_indices_dim1_func, BLOCK_SIZE>
41 av1_calc_indices_dim1Param;
42
43 typedef std::tuple<av1_calc_indices_dim2_func, BLOCK_SIZE>
44 av1_calc_indices_dim2Param;
45
46 class AV1KmeansTest1
47 : public ::testing::TestWithParam<av1_calc_indices_dim1Param> {
48 public:
49 ~AV1KmeansTest1() override;
50 void SetUp() override;
51
52 protected:
53 void RunCheckOutput(av1_calc_indices_dim1_func test_impl, BLOCK_SIZE bsize,
54 int centroids);
55 void RunSpeedTest(av1_calc_indices_dim1_func test_impl, BLOCK_SIZE bsize,
56 int centroids);
CheckResult(int n)57 bool CheckResult(int n) {
58 for (int idx = 0; idx < n; ++idx) {
59 if (indices1_[idx] != indices2_[idx]) {
60 printf("%d ", idx);
61 printf("%d != %d ", indices1_[idx], indices2_[idx]);
62 return false;
63 }
64 }
65 return true;
66 }
67
68 libaom_test::ACMRandom rnd_;
69 int16_t data_[4096];
70 int16_t centroids_[8];
71 uint8_t indices1_[4096];
72 uint8_t indices2_[4096];
73 };
74 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1KmeansTest1);
75
76 AV1KmeansTest1::~AV1KmeansTest1() = default;
77
SetUp()78 void AV1KmeansTest1::SetUp() {
79 rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed());
80 for (int i = 0; i < 4096; ++i) {
81 data_[i] = (int)rnd_.Rand8() << 4;
82 }
83 for (int i = 0; i < 8; i++) {
84 centroids_[i] = (int)rnd_.Rand8() << 4;
85 }
86 }
87
RunCheckOutput(av1_calc_indices_dim1_func test_impl,BLOCK_SIZE bsize,int k)88 void AV1KmeansTest1::RunCheckOutput(av1_calc_indices_dim1_func test_impl,
89 BLOCK_SIZE bsize, int k) {
90 const int w = block_size_wide[bsize];
91 const int h = block_size_high[bsize];
92 const int n = w * h;
93 int64_t total_dist_dim1, total_dist_impl;
94 av1_calc_indices_dim1_c(data_, centroids_, indices1_, &total_dist_dim1, n, k);
95 test_impl(data_, centroids_, indices2_, &total_dist_impl, n, k);
96
97 ASSERT_EQ(total_dist_dim1, total_dist_impl);
98 ASSERT_EQ(CheckResult(n), true)
99 << " block " << bsize << " index " << n << " Centroids " << k;
100 }
101
RunSpeedTest(av1_calc_indices_dim1_func test_impl,BLOCK_SIZE bsize,int k)102 void AV1KmeansTest1::RunSpeedTest(av1_calc_indices_dim1_func test_impl,
103 BLOCK_SIZE bsize, int k) {
104 const int w = block_size_wide[bsize];
105 const int h = block_size_high[bsize];
106 const int n = w * h;
107 const int num_loops = 1000000000 / n;
108
109 av1_calc_indices_dim1_func funcs[2] = { av1_calc_indices_dim1_c, test_impl };
110 double elapsed_time[2] = { 0 };
111 for (int i = 0; i < 2; ++i) {
112 aom_usec_timer timer;
113 aom_usec_timer_start(&timer);
114 av1_calc_indices_dim1_func func = funcs[i];
115 for (int j = 0; j < num_loops; ++j) {
116 func(data_, centroids_, indices1_, /*total_dist=*/nullptr, n, k);
117 }
118 aom_usec_timer_mark(&timer);
119 double time = static_cast<double>(aom_usec_timer_elapsed(&timer));
120 elapsed_time[i] = 1000.0 * time / num_loops;
121 }
122 printf("av1_calc_indices_dim1 indices= %d centroids=%d: %7.2f/%7.2fns", n, k,
123 elapsed_time[0], elapsed_time[1]);
124 printf("(%3.2f)\n", elapsed_time[0] / elapsed_time[1]);
125 }
126
TEST_P(AV1KmeansTest1,CheckOutput)127 TEST_P(AV1KmeansTest1, CheckOutput) {
128 // centroids = 2..8
129 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 2);
130 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 3);
131 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 4);
132 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 5);
133 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 6);
134 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 7);
135 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 8);
136 }
137
TEST_P(AV1KmeansTest1,DISABLED_Speed)138 TEST_P(AV1KmeansTest1, DISABLED_Speed) {
139 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 2);
140 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 3);
141 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 4);
142 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 5);
143 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 6);
144 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 7);
145 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 8);
146 }
147
148 class AV1KmeansTest2
149 : public ::testing::TestWithParam<av1_calc_indices_dim2Param> {
150 public:
151 ~AV1KmeansTest2() override;
152 void SetUp() override;
153
154 protected:
155 void RunCheckOutput(av1_calc_indices_dim2_func test_impl, BLOCK_SIZE bsize,
156 int centroids);
157 void RunSpeedTest(av1_calc_indices_dim2_func test_impl, BLOCK_SIZE bsize,
158 int centroids);
CheckResult(int n)159 bool CheckResult(int n) {
160 bool flag = true;
161 for (int idx = 0; idx < n; ++idx) {
162 if (indices1_[idx] != indices2_[idx]) {
163 printf("%d ", idx);
164 printf("%d != %d ", indices1_[idx], indices2_[idx]);
165 flag = false;
166 }
167 }
168 if (flag == false) {
169 return false;
170 }
171 return true;
172 }
173
174 libaom_test::ACMRandom rnd_;
175 int16_t data_[4096 * 2];
176 int16_t centroids_[8 * 2];
177 uint8_t indices1_[4096];
178 uint8_t indices2_[4096];
179 };
180 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1KmeansTest2);
181
182 AV1KmeansTest2::~AV1KmeansTest2() = default;
183
SetUp()184 void AV1KmeansTest2::SetUp() {
185 rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed());
186 for (int i = 0; i < 4096 * 2; ++i) {
187 data_[i] = (int)rnd_.Rand8();
188 }
189 for (int i = 0; i < 8 * 2; i++) {
190 centroids_[i] = (int)rnd_.Rand8();
191 }
192 }
193
RunCheckOutput(av1_calc_indices_dim2_func test_impl,BLOCK_SIZE bsize,int k)194 void AV1KmeansTest2::RunCheckOutput(av1_calc_indices_dim2_func test_impl,
195 BLOCK_SIZE bsize, int k) {
196 const int w = block_size_wide[bsize];
197 const int h = block_size_high[bsize];
198 const int n = w * h;
199 int64_t total_dist_dim2, total_dist_impl;
200 av1_calc_indices_dim2_c(data_, centroids_, indices1_, &total_dist_dim2, n, k);
201 test_impl(data_, centroids_, indices2_, &total_dist_impl, n, k);
202
203 ASSERT_EQ(total_dist_dim2, total_dist_impl);
204 ASSERT_EQ(CheckResult(n), true)
205 << " block " << bsize << " index " << n << " Centroids " << k;
206 }
207
RunSpeedTest(av1_calc_indices_dim2_func test_impl,BLOCK_SIZE bsize,int k)208 void AV1KmeansTest2::RunSpeedTest(av1_calc_indices_dim2_func test_impl,
209 BLOCK_SIZE bsize, int k) {
210 const int w = block_size_wide[bsize];
211 const int h = block_size_high[bsize];
212 const int n = w * h;
213 const int num_loops = 1000000000 / n;
214
215 av1_calc_indices_dim2_func funcs[2] = { av1_calc_indices_dim2_c, test_impl };
216 double elapsed_time[2] = { 0 };
217 for (int i = 0; i < 2; ++i) {
218 aom_usec_timer timer;
219 aom_usec_timer_start(&timer);
220 av1_calc_indices_dim2_func func = funcs[i];
221 for (int j = 0; j < num_loops; ++j) {
222 func(data_, centroids_, indices1_, /*total_dist=*/nullptr, n, k);
223 }
224 aom_usec_timer_mark(&timer);
225 double time = static_cast<double>(aom_usec_timer_elapsed(&timer));
226 elapsed_time[i] = 1000.0 * time / num_loops;
227 }
228 printf("av1_calc_indices_dim2 indices= %d centroids=%d: %7.2f/%7.2fns", n, k,
229 elapsed_time[0], elapsed_time[1]);
230 printf("(%3.2f)\n", elapsed_time[0] / elapsed_time[1]);
231 }
232
TEST_P(AV1KmeansTest2,CheckOutput)233 TEST_P(AV1KmeansTest2, CheckOutput) {
234 // centroids = 2..8
235 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 2);
236 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 3);
237 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 4);
238 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 5);
239 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 6);
240 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 7);
241 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 8);
242 }
243
TEST_P(AV1KmeansTest2,DISABLED_Speed)244 TEST_P(AV1KmeansTest2, DISABLED_Speed) {
245 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 2);
246 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 3);
247 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 4);
248 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 5);
249 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 6);
250 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 7);
251 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 8);
252 }
253
254 #if HAVE_SSE2 || HAVE_AVX2 || HAVE_NEON
255 const BLOCK_SIZE kValidBlockSize[] = { BLOCK_8X8, BLOCK_8X16, BLOCK_8X32,
256 BLOCK_16X8, BLOCK_16X16, BLOCK_16X32,
257 BLOCK_32X8, BLOCK_32X16, BLOCK_32X32,
258 BLOCK_32X64, BLOCK_64X32, BLOCK_64X64,
259 BLOCK_16X64, BLOCK_64X16 };
260 #endif
261
262 #if HAVE_SSE2
263 INSTANTIATE_TEST_SUITE_P(
264 SSE2, AV1KmeansTest1,
265 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_sse2),
266 ::testing::ValuesIn(kValidBlockSize)));
267 INSTANTIATE_TEST_SUITE_P(
268 SSE2, AV1KmeansTest2,
269 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_sse2),
270 ::testing::ValuesIn(kValidBlockSize)));
271 #endif
272
273 #if HAVE_AVX2
274 INSTANTIATE_TEST_SUITE_P(
275 AVX2, AV1KmeansTest1,
276 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_avx2),
277 ::testing::ValuesIn(kValidBlockSize)));
278 INSTANTIATE_TEST_SUITE_P(
279 AVX2, AV1KmeansTest2,
280 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_avx2),
281 ::testing::ValuesIn(kValidBlockSize)));
282 #endif
283
284 #if HAVE_NEON
285 INSTANTIATE_TEST_SUITE_P(
286 NEON, AV1KmeansTest1,
287 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_neon),
288 ::testing::ValuesIn(kValidBlockSize)));
289 INSTANTIATE_TEST_SUITE_P(
290 NEON, AV1KmeansTest2,
291 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_neon),
292 ::testing::ValuesIn(kValidBlockSize)));
293 #endif
294
295 } // namespace AV1Kmeans
296