xref: /aosp_15_r20/external/libgav1/src/reconstruction_test.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1 // Copyright 2021 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/reconstruction.h"
16 
17 #include <cstddef>
18 #include <cstdint>
19 #include <cstring>
20 #include <vector>
21 
22 #include "absl/strings/match.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "src/dsp/constants.h"
26 #include "src/dsp/dsp.h"
27 #include "src/dsp/inverse_transform.h"
28 #include "src/utils/array_2d.h"
29 #include "src/utils/common.h"
30 #include "src/utils/constants.h"
31 #include "src/utils/cpu.h"
32 #include "src/utils/memory.h"
33 #include "tests/block_utils.h"
34 #include "tests/utils.h"
35 
36 namespace libgav1 {
37 namespace {
38 
39 // Import the scan tables in the anonymous namespace.
40 #include "src/scan_tables.inc"
41 
42 constexpr int kTestTransformSize = 4;
43 constexpr int8_t kTestBitdepth = 8;
44 
45 using testing::ElementsAreArray;
46 
47 // The 'int' parameter is unused but required to allow for instantiations of C,
48 // NEON, etc.
49 class ReconstructionTest : public testing::TestWithParam<int> {
50  public:
51   ReconstructionTest() = default;
52   ReconstructionTest(const ReconstructionTest&) = delete;
53   ReconstructionTest& operator=(const ReconstructionTest&) = delete;
54   ~ReconstructionTest() override = default;
55 
56  protected:
SetUp()57   void SetUp() override {
58     test_utils::ResetDspTable(kTestBitdepth);
59     dsp::InverseTransformInit_C();
60     dsp_ = dsp::GetDspTable(kTestBitdepth);
61     ASSERT_NE(dsp_, nullptr);
62     const testing::TestInfo* const test_info =
63         testing::UnitTest::GetInstance()->current_test_info();
64     if (test_info->value_param() != nullptr) {
65       const char* const test_case = test_info->test_suite_name();
66       if (absl::StartsWith(test_case, "C/")) {
67       } else if (absl::StartsWith(test_case, "SSE41/")) {
68         if ((GetCpuInfo() & kSSE4_1) == 0) GTEST_SKIP() << "No SSE4.1 support!";
69         dsp::InverseTransformInit_SSE4_1();
70       } else if (absl::StartsWith(test_case, "NEON/")) {
71         dsp::InverseTransformInit_NEON();
72       } else {
73         FAIL() << "Unrecognized architecture prefix in test case name: "
74                << test_case;
75       }
76     }
77     InitBuffers();
78   }
79 
InitBuffers(int width=kTestTransformSize,int height=kTestTransformSize)80   void InitBuffers(int width = kTestTransformSize,
81                    int height = kTestTransformSize) {
82     const int size = width * height;
83     buffer_.clear();
84     buffer_.resize(size);
85     residual_buffer_.clear();
86     residual_buffer_.resize(size);
87     for (int i = 0; i < size; ++i) {
88       buffer_[i] = residual_buffer_[i] = i % 256;
89     }
90     frame_buffer_.Reset(height, width, buffer_.data());
91   }
92 
93   template <int bitdepth>
94   void TestWht();
95 
96   std::vector<uint8_t> buffer_;
97   std::vector<int16_t> residual_buffer_;
98   // |frame_buffer_| is just a 2D array view into the |buffer_|.
99   Array2DView<uint8_t> frame_buffer_;
100   const dsp::Dsp* dsp_;
101 };
102 
103 template <int bitdepth>
TestWht()104 void ReconstructionTest::TestWht() {
105   static_assert(bitdepth == kBitdepth8 || bitdepth == kBitdepth10, "");
106   for (const auto transform :
107        dsp_->inverse_transforms[dsp::kTransform1dWht][dsp::kTransform1dSize4]) {
108     if (transform == nullptr) {
109       GTEST_SKIP() << "No function available for dsp::kTransform1dWht";
110     }
111   }
112   constexpr int max = 16 << bitdepth;
113   constexpr int min = -max;
114   static constexpr int16_t residual_inputs[][16]{
115       {64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
116       {69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
117       {0, 0, 0, 0, 0, max - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
118       {0, 0, 0, 0, 0, min - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
119       // Note these are unrealistic inputs, but serve to test each position in
120       // the array and match extremes in some commercial test vectors.
121       {max, max, max, max, max, max, max, max, max, max, max, max, max, max,
122        max, max},
123       {min, min, min, min, min, min, min, min, min, min, min, min, min, min,
124        min, min}};
125   // Before the Reconstruct() call, the frame buffer is filled with all 127.
126   // After the Reconstruct() call, the frame buffer is expected to have the
127   // following values.
128   static constexpr uint8_t frame_outputs[][16]{
129       {131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
130        131, 131},
131       {132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
132        131, 131},
133       {255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255},
134       {0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0},
135       {255, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
136        127, 127},
137       {0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
138        127},
139   };
140 
141   const TransformSize tx_size = kTransformSize4x4;
142   const TransformType tx_type = kTransformTypeDctDct;
143   const int tx_width = kTransformWidth[tx_size];
144   const int tx_height = kTransformHeight[tx_size];
145   const uint16_t* const scan = kScan[GetTransformClass(tx_type)][tx_size];
146 
147   InitBuffers(tx_width, tx_height);
148 
149   const int num_tests = sizeof(residual_inputs) / sizeof(residual_inputs[0]);
150   for (int i = 0; i < num_tests; ++i) {
151     int16_t eob;  // Also known as non_zero_coeff_count.
152     for (eob = 15; eob >= 0; --eob) {
153       if (residual_inputs[i][scan[eob]] != 0) break;
154     }
155     ++eob;
156     memcpy(residual_buffer_.data(), residual_inputs[i],
157            sizeof(residual_inputs[i]));
158     memset(buffer_.data(), 127, sizeof(frame_outputs[i]));
159     Reconstruct(*dsp_, tx_type, tx_size, /*lossless=*/true,
160                 residual_buffer_.data(), 0, 0, &frame_buffer_, eob);
161 
162     EXPECT_TRUE(test_utils::CompareBlocks(buffer_.data(), frame_outputs[i],
163                                           tx_width, tx_height, tx_width,
164                                           tx_width, false, true))
165         << "Mismatch WHT test case " << i;
166   }
167 }
168 
TEST_P(ReconstructionTest,ReconstructionSimple)169 TEST_P(ReconstructionTest, ReconstructionSimple) {
170   for (const auto transform :
171        dsp_->inverse_transforms[dsp::kTransform1dIdentity]
172                                [dsp::kTransform1dSize4]) {
173     if (transform == nullptr) GTEST_SKIP();
174   }
175   Reconstruct(*dsp_, kTransformTypeIdentityIdentity, kTransformSize4x4, false,
176               residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
177   // clang-format off
178   static constexpr uint8_t expected_output_buffer[] = {
179       0, 1, 2, 3,
180       5, 6, 7, 8,
181       9, 10, 11, 12,
182       14, 15, 16, 17
183   };
184   // clang-format on
185   EXPECT_THAT(buffer_, ElementsAreArray(expected_output_buffer));
186 }
187 
TEST_P(ReconstructionTest,ReconstructionFlipY)188 TEST_P(ReconstructionTest, ReconstructionFlipY) {
189   for (const auto transform :
190        dsp_->inverse_transforms[dsp::kTransform1dIdentity]
191                                [dsp::kTransform1dSize4]) {
192     if (transform == nullptr) GTEST_SKIP();
193   }
194   Reconstruct(*dsp_, kTransformTypeIdentityFlipadst, kTransformSize4x4, false,
195               residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
196   // clang-format off
197   static constexpr uint8_t expected_buffer[] = {
198       0, 1, 2, 3,
199       4, 5, 6, 7,
200       7, 8, 9, 10,
201       14, 15, 16, 17
202   };
203   // clang-format on
204   EXPECT_THAT(buffer_, ElementsAreArray(expected_buffer));
205 }
206 
TEST_P(ReconstructionTest,ReconstructionFlipX)207 TEST_P(ReconstructionTest, ReconstructionFlipX) {
208   for (const auto transform :
209        dsp_->inverse_transforms[dsp::kTransform1dIdentity]
210                                [dsp::kTransform1dSize4]) {
211     if (transform == nullptr) GTEST_SKIP();
212   }
213   Reconstruct(*dsp_, kTransformTypeFlipadstIdentity, kTransformSize4x4, false,
214               residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
215   // clang-format off
216   static constexpr uint8_t expected_buffer[] = {
217       0, 1, 2, 3,
218       4, 5, 6, 8,
219       8, 10, 10, 13,
220       12, 14, 14, 18
221   };
222   // clang-format on
223   EXPECT_THAT(buffer_, ElementsAreArray(expected_buffer));
224 }
225 
TEST_P(ReconstructionTest,ReconstructionFlipXAndFlipY)226 TEST_P(ReconstructionTest, ReconstructionFlipXAndFlipY) {
227   for (const auto transform :
228        dsp_->inverse_transforms[dsp::kTransform1dIdentity]
229                                [dsp::kTransform1dSize4]) {
230     if (transform == nullptr) GTEST_SKIP();
231   }
232   Reconstruct(*dsp_, kTransformTypeFlipadstFlipadst, kTransformSize4x4, false,
233               residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
234   // clang-format off
235   static constexpr uint8_t expected_buffer[] = {
236       0, 1, 2, 3,
237       4, 5, 6, 8,
238       8, 8, 10, 9,
239       12, 14, 14, 19
240   };
241   // clang-format on
242   EXPECT_THAT(buffer_, ElementsAreArray(expected_buffer));
243 }
244 
TEST_P(ReconstructionTest,ReconstructionNonZeroStart)245 TEST_P(ReconstructionTest, ReconstructionNonZeroStart) {
246   uint8_t buffer[64] = {};
247   Array2DView<uint8_t> frame_buffer(8, 8, buffer);
248   int k = 0;
249   for (int i = 0; i < kTestTransformSize; ++i) {
250     for (int j = 0; j < kTestTransformSize; ++j) {
251       frame_buffer[i + 4][j + 4] = k++;
252     }
253   }
254   for (const auto transform :
255        dsp_->inverse_transforms[dsp::kTransform1dIdentity]
256                                [dsp::kTransform1dSize4]) {
257     if (transform == nullptr) GTEST_SKIP();
258   }
259   Reconstruct(*dsp_, kTransformTypeIdentityIdentity, kTransformSize4x4, false,
260               residual_buffer_.data(), 4, 4, &frame_buffer, 64);
261   // clang-format off
262   static constexpr uint8_t expected_buffer[] = {
263       0, 0, 0, 0, 0, 0, 0, 0,
264       0, 0, 0, 0, 0, 0, 0, 0,
265       0, 0, 0, 0, 0, 0, 0, 0,
266       0, 0, 0, 0, 0, 0, 0, 0,
267       0, 0, 0, 0, 0, 1, 2, 3,
268       0, 0, 0, 0, 5, 6, 7, 8,
269       0, 0, 0, 0, 9, 10, 11, 12,
270       0, 0, 0, 0, 14, 15, 16, 17
271   };
272   // clang-format on
273   EXPECT_THAT(buffer, ElementsAreArray(expected_buffer));
274 }
275 
TEST_P(ReconstructionTest,Wht8bit)276 TEST_P(ReconstructionTest, Wht8bit) { TestWht<kBitdepth8>(); }
277 
278 #if LIBGAV1_MAX_BITDEPTH >= 10
TEST_P(ReconstructionTest,Wht10bit)279 TEST_P(ReconstructionTest, Wht10bit) { TestWht<kBitdepth10>(); }
280 #endif
281 
282 INSTANTIATE_TEST_SUITE_P(C, ReconstructionTest, testing::Values(0));
283 
284 #if LIBGAV1_ENABLE_SSE4_1
285 INSTANTIATE_TEST_SUITE_P(SSE41, ReconstructionTest, testing::Values(0));
286 #endif
287 
288 #if LIBGAV1_ENABLE_NEON
289 INSTANTIATE_TEST_SUITE_P(NEON, ReconstructionTest, testing::Values(0));
290 #endif
291 
292 }  // namespace
293 }  // namespace libgav1
294