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 "test/acm_random.h"
25 #include "av1/encoder/palette.h"
26 #include "test/register_state_check.h"
27 #include "test/util.h"
28 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
29
30 namespace AV1Kmeans {
31 typedef void (*av1_calc_indices_dim1_func)(const int *data,
32 const int *centroids,
33 uint8_t *indices, int n, int k);
34 typedef void (*av1_calc_indices_dim2_func)(const int *data,
35 const int *centroids,
36 uint8_t *indices, int n, int k);
37
38 typedef std::tuple<av1_calc_indices_dim1_func, BLOCK_SIZE>
39 av1_calc_indices_dim1Param;
40
41 typedef std::tuple<av1_calc_indices_dim2_func, BLOCK_SIZE>
42 av1_calc_indices_dim2Param;
43
44 class AV1KmeansTest1
45 : public ::testing::TestWithParam<av1_calc_indices_dim1Param> {
46 public:
47 ~AV1KmeansTest1();
48 void SetUp();
49
50 void TearDown();
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 int data_[4096];
70 int centroids_[8];
71 uint8_t indices1_[4096];
72 uint8_t indices2_[4096];
73 };
74 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1KmeansTest1);
75
~AV1KmeansTest1()76 AV1KmeansTest1::~AV1KmeansTest1() { ; }
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
TearDown()88 void AV1KmeansTest1::TearDown() {}
89
RunCheckOutput(av1_calc_indices_dim1_func test_impl,BLOCK_SIZE bsize,int k)90 void AV1KmeansTest1::RunCheckOutput(av1_calc_indices_dim1_func test_impl,
91 BLOCK_SIZE bsize, int k) {
92 const int w = block_size_wide[bsize];
93 const int h = block_size_high[bsize];
94 const int n = w * h;
95 av1_calc_indices_dim1_c(data_, centroids_, indices1_, n, k);
96 test_impl(data_, centroids_, indices2_, n, k);
97
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_, 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();
152 void SetUp();
153
154 void TearDown();
155
156 protected:
157 void RunCheckOutput(av1_calc_indices_dim2_func test_impl, BLOCK_SIZE bsize,
158 int centroids);
159 void RunSpeedTest(av1_calc_indices_dim2_func test_impl, BLOCK_SIZE bsize,
160 int centroids);
CheckResult(int n)161 bool CheckResult(int n) {
162 bool flag = true;
163 for (int idx = 0; idx < n; ++idx) {
164 if (indices1_[idx] != indices2_[idx]) {
165 printf("%d ", idx);
166 printf("%d != %d ", indices1_[idx], indices2_[idx]);
167 flag = false;
168 }
169 }
170 if (flag == false) {
171 return false;
172 }
173 return true;
174 }
175
176 libaom_test::ACMRandom rnd_;
177 int data_[4096 * 2];
178 int centroids_[8 * 2];
179 uint8_t indices1_[4096];
180 uint8_t indices2_[4096];
181 };
182 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1KmeansTest2);
183
~AV1KmeansTest2()184 AV1KmeansTest2::~AV1KmeansTest2() { ; }
185
SetUp()186 void AV1KmeansTest2::SetUp() {
187 rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed());
188 for (int i = 0; i < 4096 * 2; ++i) {
189 data_[i] = (int)rnd_.Rand8();
190 }
191 for (int i = 0; i < 8 * 2; i++) {
192 centroids_[i] = (int)rnd_.Rand8();
193 }
194 }
195
TearDown()196 void AV1KmeansTest2::TearDown() {}
197
RunCheckOutput(av1_calc_indices_dim2_func test_impl,BLOCK_SIZE bsize,int k)198 void AV1KmeansTest2::RunCheckOutput(av1_calc_indices_dim2_func test_impl,
199 BLOCK_SIZE bsize, int k) {
200 const int w = block_size_wide[bsize];
201 const int h = block_size_high[bsize];
202 const int n = w * h;
203 av1_calc_indices_dim2_c(data_, centroids_, indices1_, n, k);
204 test_impl(data_, centroids_, indices2_, n, k);
205
206 ASSERT_EQ(CheckResult(n), true)
207 << " block " << bsize << " index " << n << " Centroids " << k;
208 }
209
RunSpeedTest(av1_calc_indices_dim2_func test_impl,BLOCK_SIZE bsize,int k)210 void AV1KmeansTest2::RunSpeedTest(av1_calc_indices_dim2_func test_impl,
211 BLOCK_SIZE bsize, int k) {
212 const int w = block_size_wide[bsize];
213 const int h = block_size_high[bsize];
214 const int n = w * h;
215 const int num_loops = 1000000000 / n;
216
217 av1_calc_indices_dim2_func funcs[2] = { av1_calc_indices_dim2_c, test_impl };
218 double elapsed_time[2] = { 0 };
219 for (int i = 0; i < 2; ++i) {
220 aom_usec_timer timer;
221 aom_usec_timer_start(&timer);
222 av1_calc_indices_dim2_func func = funcs[i];
223 for (int j = 0; j < num_loops; ++j) {
224 func(data_, centroids_, indices1_, n, k);
225 }
226 aom_usec_timer_mark(&timer);
227 double time = static_cast<double>(aom_usec_timer_elapsed(&timer));
228 elapsed_time[i] = 1000.0 * time / num_loops;
229 }
230 printf("av1_calc_indices_dim2 indices= %d centroids=%d: %7.2f/%7.2fns", n, k,
231 elapsed_time[0], elapsed_time[1]);
232 printf("(%3.2f)\n", elapsed_time[0] / elapsed_time[1]);
233 }
234
TEST_P(AV1KmeansTest2,CheckOutput)235 TEST_P(AV1KmeansTest2, CheckOutput) {
236 // centroids = 2..8
237 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 2);
238 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 3);
239 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 4);
240 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 5);
241 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 6);
242 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 7);
243 RunCheckOutput(GET_PARAM(0), GET_PARAM(1), 8);
244 }
245
TEST_P(AV1KmeansTest2,DISABLED_Speed)246 TEST_P(AV1KmeansTest2, DISABLED_Speed) {
247 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 2);
248 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 3);
249 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 4);
250 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 5);
251 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 6);
252 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 7);
253 RunSpeedTest(GET_PARAM(0), GET_PARAM(1), 8);
254 }
255
256 #if HAVE_AVX2 || HAVE_SSE2
257 const BLOCK_SIZE kValidBlockSize[] = { BLOCK_8X8, BLOCK_8X16, BLOCK_8X32,
258 BLOCK_16X8, BLOCK_16X16, BLOCK_16X32,
259 BLOCK_32X8, BLOCK_32X16, BLOCK_32X32,
260 BLOCK_32X64, BLOCK_64X32, BLOCK_64X64,
261 BLOCK_16X64, BLOCK_64X16 };
262 #endif
263
264 #if HAVE_AVX2
265 INSTANTIATE_TEST_SUITE_P(
266 AVX2, AV1KmeansTest1,
267 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_avx2),
268 ::testing::ValuesIn(kValidBlockSize)));
269 INSTANTIATE_TEST_SUITE_P(
270 AVX2, AV1KmeansTest2,
271 ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_avx2),
272 ::testing::ValuesIn(kValidBlockSize)));
273 #endif
274
275 #if HAVE_SSE2
276
277 INSTANTIATE_TEST_SUITE_P(
278 SSE2, AV1KmeansTest1,
279 ::testing::Combine(::testing::Values(&av1_calc_indices_dim1_sse2),
280 ::testing::ValuesIn(kValidBlockSize)));
281 // TODO(any): Disable av1_calc_indices_dim2 sse2 SIMD and its unit test due to
282 // c/SIMD mismatch. Re-enable it after mismatch is fixed.
283 // INSTANTIATE_TEST_SUITE_P(
284 // SSE2, AV1KmeansTest2,
285 // ::testing::Combine(::testing::Values(&av1_calc_indices_dim2_sse2),
286 // ::testing::ValuesIn(kValidBlockSize)));
287 #endif
288
289 } // namespace AV1Kmeans
290