xref: /aosp_15_r20/external/libaom/test/av1_k_means_test.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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