aboutsummaryrefslogtreecommitdiff
path: root/src/reconstruction_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/reconstruction_test.cc')
-rw-r--r--src/reconstruction_test.cc294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/reconstruction_test.cc b/src/reconstruction_test.cc
new file mode 100644
index 0000000..fd780b3
--- /dev/null
+++ b/src/reconstruction_test.cc
@@ -0,0 +1,294 @@
+// Copyright 2021 The libgav1 Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/reconstruction.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+#include "absl/strings/match.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "src/dsp/constants.h"
+#include "src/dsp/dsp.h"
+#include "src/dsp/inverse_transform.h"
+#include "src/utils/array_2d.h"
+#include "src/utils/common.h"
+#include "src/utils/constants.h"
+#include "src/utils/cpu.h"
+#include "src/utils/memory.h"
+#include "tests/block_utils.h"
+#include "tests/utils.h"
+
+namespace libgav1 {
+namespace {
+
+// Import the scan tables in the anonymous namespace.
+#include "src/scan_tables.inc"
+
+constexpr int kTestTransformSize = 4;
+constexpr int8_t kTestBitdepth = 8;
+
+using testing::ElementsAreArray;
+
+// The 'int' parameter is unused but required to allow for instantiations of C,
+// NEON, etc.
+class ReconstructionTest : public testing::TestWithParam<int> {
+ public:
+ ReconstructionTest() = default;
+ ReconstructionTest(const ReconstructionTest&) = delete;
+ ReconstructionTest& operator=(const ReconstructionTest&) = delete;
+ ~ReconstructionTest() override = default;
+
+ protected:
+ void SetUp() override {
+ test_utils::ResetDspTable(kTestBitdepth);
+ dsp::InverseTransformInit_C();
+ dsp_ = dsp::GetDspTable(kTestBitdepth);
+ ASSERT_NE(dsp_, nullptr);
+ const testing::TestInfo* const test_info =
+ testing::UnitTest::GetInstance()->current_test_info();
+ if (test_info->value_param() != nullptr) {
+ const char* const test_case = test_info->test_suite_name();
+ if (absl::StartsWith(test_case, "C/")) {
+ } else if (absl::StartsWith(test_case, "SSE41/")) {
+ if ((GetCpuInfo() & kSSE4_1) != 0) {
+ dsp::InverseTransformInit_SSE4_1();
+ }
+ } else if (absl::StartsWith(test_case, "NEON/")) {
+ dsp::InverseTransformInit_NEON();
+ } else {
+ FAIL() << "Unrecognized architecture prefix in test case name: "
+ << test_case;
+ }
+ }
+ InitBuffers();
+ }
+
+ void InitBuffers(int width = kTestTransformSize,
+ int height = kTestTransformSize) {
+ const int size = width * height;
+ buffer_.clear();
+ buffer_.resize(size);
+ residual_buffer_.clear();
+ residual_buffer_.resize(size);
+ for (int i = 0; i < size; ++i) {
+ buffer_[i] = residual_buffer_[i] = i % 256;
+ }
+ frame_buffer_.Reset(height, width, buffer_.data());
+ }
+
+ template <int bitdepth>
+ void TestWht();
+
+ std::vector<uint8_t> buffer_;
+ std::vector<int16_t> residual_buffer_;
+ // |frame_buffer_| is just a 2D array view into the |buffer_|.
+ Array2DView<uint8_t> frame_buffer_;
+ const dsp::Dsp* dsp_;
+};
+
+template <int bitdepth>
+void ReconstructionTest::TestWht() {
+ static_assert(bitdepth == kBitdepth8 || bitdepth == kBitdepth10, "");
+ for (const auto transform :
+ dsp_->inverse_transforms[dsp::kTransform1dWht][dsp::kTransform1dSize4]) {
+ if (transform == nullptr) {
+ GTEST_SKIP() << "No function available for dsp::kTransform1dWht";
+ }
+ }
+ constexpr int max = 16 << bitdepth;
+ constexpr int min = -max;
+ static constexpr int16_t residual_inputs[][16]{
+ {64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, max - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, min - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ // Note these are unrealistic inputs, but serve to test each position in
+ // the array and match extremes in some commercial test vectors.
+ {max, max, max, max, max, max, max, max, max, max, max, max, max, max,
+ max, max},
+ {min, min, min, min, min, min, min, min, min, min, min, min, min, min,
+ min, min}};
+ // Before the Reconstruct() call, the frame buffer is filled with all 127.
+ // After the Reconstruct() call, the frame buffer is expected to have the
+ // following values.
+ static constexpr uint8_t frame_outputs[][16]{
+ {131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131},
+ {132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131},
+ {255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255},
+ {0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0},
+ {255, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127},
+ {0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127},
+ };
+
+ const TransformSize tx_size = kTransformSize4x4;
+ const TransformType tx_type = kTransformTypeDctDct;
+ const int tx_width = kTransformWidth[tx_size];
+ const int tx_height = kTransformHeight[tx_size];
+ const uint16_t* const scan = kScan[GetTransformClass(tx_type)][tx_size];
+
+ InitBuffers(tx_width, tx_height);
+
+ const int num_tests = sizeof(residual_inputs) / sizeof(residual_inputs[0]);
+ for (int i = 0; i < num_tests; ++i) {
+ int16_t eob; // Also known as non_zero_coeff_count.
+ for (eob = 15; eob >= 0; --eob) {
+ if (residual_inputs[i][scan[eob]] != 0) break;
+ }
+ ++eob;
+ memcpy(residual_buffer_.data(), residual_inputs[i],
+ sizeof(residual_inputs[i]));
+ memset(buffer_.data(), 127, sizeof(frame_outputs[i]));
+ Reconstruct(*dsp_, tx_type, tx_size, /*lossless=*/true,
+ residual_buffer_.data(), 0, 0, &frame_buffer_, eob);
+
+ EXPECT_TRUE(test_utils::CompareBlocks(buffer_.data(), frame_outputs[i],
+ tx_width, tx_height, tx_width,
+ tx_width, false, true))
+ << "Mismatch WHT test case " << i;
+ }
+}
+
+TEST_P(ReconstructionTest, ReconstructionSimple) {
+ for (const auto transform :
+ dsp_->inverse_transforms[dsp::kTransform1dIdentity]
+ [dsp::kTransform1dSize4]) {
+ if (transform == nullptr) GTEST_SKIP();
+ }
+ Reconstruct(*dsp_, kTransformTypeIdentityIdentity, kTransformSize4x4, false,
+ residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
+ // clang-format off
+ static constexpr uint8_t expected_output_buffer[] = {
+ 0, 1, 2, 3,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12,
+ 14, 15, 16, 17
+ };
+ // clang-format on
+ EXPECT_THAT(buffer_, ElementsAreArray(expected_output_buffer));
+}
+
+TEST_P(ReconstructionTest, ReconstructionFlipY) {
+ for (const auto transform :
+ dsp_->inverse_transforms[dsp::kTransform1dIdentity]
+ [dsp::kTransform1dSize4]) {
+ if (transform == nullptr) GTEST_SKIP();
+ }
+ Reconstruct(*dsp_, kTransformTypeIdentityFlipadst, kTransformSize4x4, false,
+ residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
+ // clang-format off
+ static constexpr uint8_t expected_buffer[] = {
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 7, 8, 9, 10,
+ 14, 15, 16, 17
+ };
+ // clang-format on
+ EXPECT_THAT(buffer_, ElementsAreArray(expected_buffer));
+}
+
+TEST_P(ReconstructionTest, ReconstructionFlipX) {
+ for (const auto transform :
+ dsp_->inverse_transforms[dsp::kTransform1dIdentity]
+ [dsp::kTransform1dSize4]) {
+ if (transform == nullptr) GTEST_SKIP();
+ }
+ Reconstruct(*dsp_, kTransformTypeFlipadstIdentity, kTransformSize4x4, false,
+ residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
+ // clang-format off
+ static constexpr uint8_t expected_buffer[] = {
+ 0, 1, 2, 3,
+ 4, 5, 6, 8,
+ 8, 10, 10, 13,
+ 12, 14, 14, 18
+ };
+ // clang-format on
+ EXPECT_THAT(buffer_, ElementsAreArray(expected_buffer));
+}
+
+TEST_P(ReconstructionTest, ReconstructionFlipXAndFlipY) {
+ for (const auto transform :
+ dsp_->inverse_transforms[dsp::kTransform1dIdentity]
+ [dsp::kTransform1dSize4]) {
+ if (transform == nullptr) GTEST_SKIP();
+ }
+ Reconstruct(*dsp_, kTransformTypeFlipadstFlipadst, kTransformSize4x4, false,
+ residual_buffer_.data(), 0, 0, &frame_buffer_, 16);
+ // clang-format off
+ static constexpr uint8_t expected_buffer[] = {
+ 0, 1, 2, 3,
+ 4, 5, 6, 8,
+ 8, 8, 10, 9,
+ 12, 14, 14, 19
+ };
+ // clang-format on
+ EXPECT_THAT(buffer_, ElementsAreArray(expected_buffer));
+}
+
+TEST_P(ReconstructionTest, ReconstructionNonZeroStart) {
+ uint8_t buffer[64] = {};
+ Array2DView<uint8_t> frame_buffer(8, 8, buffer);
+ int k = 0;
+ for (int i = 0; i < kTestTransformSize; ++i) {
+ for (int j = 0; j < kTestTransformSize; ++j) {
+ frame_buffer[i + 4][j + 4] = k++;
+ }
+ }
+ for (const auto transform :
+ dsp_->inverse_transforms[dsp::kTransform1dIdentity]
+ [dsp::kTransform1dSize4]) {
+ if (transform == nullptr) GTEST_SKIP();
+ }
+ Reconstruct(*dsp_, kTransformTypeIdentityIdentity, kTransformSize4x4, false,
+ residual_buffer_.data(), 4, 4, &frame_buffer, 64);
+ // clang-format off
+ static constexpr uint8_t expected_buffer[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3,
+ 0, 0, 0, 0, 5, 6, 7, 8,
+ 0, 0, 0, 0, 9, 10, 11, 12,
+ 0, 0, 0, 0, 14, 15, 16, 17
+ };
+ // clang-format on
+ EXPECT_THAT(buffer, ElementsAreArray(expected_buffer));
+}
+
+TEST_P(ReconstructionTest, Wht8bit) { TestWht<kBitdepth8>(); }
+
+#if LIBGAV1_MAX_BITDEPTH >= 10
+TEST_P(ReconstructionTest, Wht10bit) { TestWht<kBitdepth10>(); }
+#endif
+
+INSTANTIATE_TEST_SUITE_P(C, ReconstructionTest, testing::Values(0));
+
+#if LIBGAV1_ENABLE_SSE4_1
+INSTANTIATE_TEST_SUITE_P(SSE41, ReconstructionTest, testing::Values(0));
+#endif
+
+#if LIBGAV1_ENABLE_NEON
+INSTANTIATE_TEST_SUITE_P(NEON, ReconstructionTest, testing::Values(0));
+#endif
+
+} // namespace
+} // namespace libgav1