| /* | |
| * Copyright (c) 2024, Alliance for Open Media. All rights reserved. | |
| * | |
| * This source code is subject to the terms of the BSD 2 Clause License and | |
| * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |
| * was not distributed with this source code in the LICENSE file, you can | |
| * obtain it at www.aomedia.org/license/software. If the Alliance for Open | |
| * Media Patent License 1.0 was not distributed with this source code in the | |
| * PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |
| */ | |
| #include <memory> | |
| #include <new> | |
| #include "config/av1_rtcd.h" | |
| #include "aom_ports/aom_timer.h" | |
| #include "aom_ports/bitops.h" | |
| #include "gtest/gtest.h" | |
| #include "test/acm_random.h" | |
| #include "test/util.h" | |
| namespace { | |
| using ::testing::Combine; | |
| using ::testing::Values; | |
| using ::testing::ValuesIn; | |
| using std::make_tuple; | |
| using std::tuple; | |
| const int kIters = 1000; | |
| using FrameDimension = tuple<int, int>; | |
| // Check that two 8-bit output buffers are identical. | |
| void AssertOutputBufferEq(const uint8_t *p1, const uint8_t *p2, int width, | |
| int height) { | |
| ASSERT_TRUE(p1 != p2) << "Buffers must be at different memory locations"; | |
| for (int j = 0; j < height; ++j) { | |
| if (memcmp(p1, p2, sizeof(*p1) * width) == 0) { | |
| p1 += width; | |
| p2 += width; | |
| continue; | |
| } | |
| for (int i = 0; i < width; ++i) { | |
| ASSERT_EQ(p1[i], p2[i]) | |
| << width << "x" << height << " Pixel mismatch at (" << i << ", " << j | |
| << ")"; | |
| } | |
| } | |
| } | |
| using LowBDResizeFunc = bool (*)(uint8_t *intbuf, uint8_t *output, | |
| int out_stride, int height, int height2, | |
| int stride, int start_wd); | |
| // Test parameter list: | |
| // <tst_fun, dims> | |
| using ResizeTestParams = tuple<LowBDResizeFunc, FrameDimension>; | |
| class AV1ResizeYTest : public ::testing::TestWithParam<ResizeTestParams> { | |
| public: | |
| void SetUp() { | |
| test_fun_ = GET_PARAM(0); | |
| frame_dim_ = GET_PARAM(1); | |
| width_ = std::get<0>(frame_dim_); | |
| height_ = std::get<1>(frame_dim_); | |
| const int msb = get_msb(AOMMIN(width_, height_)); | |
| n_levels_ = AOMMAX(msb - MIN_PYRAMID_SIZE_LOG2, 1); | |
| const int src_buf_size = (width_ / 2) * height_; | |
| const int dest_buf_size = (width_ * height_) / 4; | |
| src_ = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[src_buf_size]); | |
| ASSERT_NE(src_, nullptr); | |
| ref_dest_ = | |
| std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]); | |
| ASSERT_NE(ref_dest_, nullptr); | |
| test_dest_ = | |
| std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]); | |
| ASSERT_NE(test_dest_, nullptr); | |
| } | |
| void RunTest() { | |
| for (int i = 0; i < (width_ / 2) * height_; i++) src_[i] = rng_.Rand8(); | |
| for (int level = 1; level < n_levels_; level++) { | |
| const int width2 = (width_ >> level); | |
| const int height2 = (height_ >> level); | |
| av1_resize_vert_dir_c(src_.get(), ref_dest_.get(), width2, height2 << 1, | |
| height2, width2, 0); | |
| test_fun_(src_.get(), test_dest_.get(), width2, height2 << 1, height2, | |
| width2, 0); | |
| AssertOutputBufferEq(ref_dest_.get(), test_dest_.get(), width2, height2); | |
| } | |
| } | |
| void SpeedTest() { | |
| for (int i = 0; i < (width_ / 2) * height_; i++) src_[i] = rng_.Rand8(); | |
| for (int level = 1; level < n_levels_; level++) { | |
| const int width2 = (width_ >> level); | |
| const int height2 = (height_ >> level); | |
| aom_usec_timer ref_timer; | |
| aom_usec_timer_start(&ref_timer); | |
| for (int j = 0; j < kIters; j++) { | |
| av1_resize_vert_dir_c(src_.get(), ref_dest_.get(), width2, height2 << 1, | |
| height2, width2, 0); | |
| } | |
| aom_usec_timer_mark(&ref_timer); | |
| const int64_t ref_time = aom_usec_timer_elapsed(&ref_timer); | |
| aom_usec_timer tst_timer; | |
| aom_usec_timer_start(&tst_timer); | |
| for (int j = 0; j < kIters; j++) { | |
| test_fun_(src_.get(), test_dest_.get(), width2, height2 << 1, height2, | |
| width2, 0); | |
| } | |
| aom_usec_timer_mark(&tst_timer); | |
| const int64_t tst_time = aom_usec_timer_elapsed(&tst_timer); | |
| std::cout << "level: " << level << " [" << width2 << " x " << height2 | |
| << "] C time = " << ref_time << " , SIMD time = " << tst_time | |
| << " scaling=" << float(1.00) * ref_time / tst_time << "x \n"; | |
| } | |
| } | |
| private: | |
| LowBDResizeFunc test_fun_; | |
| FrameDimension frame_dim_; | |
| int width_; | |
| int height_; | |
| int n_levels_; | |
| std::unique_ptr<uint8_t[]> src_; | |
| std::unique_ptr<uint8_t[]> ref_dest_; | |
| std::unique_ptr<uint8_t[]> test_dest_; | |
| libaom_test::ACMRandom rng_; | |
| }; | |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1ResizeYTest); | |
| TEST_P(AV1ResizeYTest, RunTest) { RunTest(); } | |
| TEST_P(AV1ResizeYTest, DISABLED_SpeedTest) { SpeedTest(); } | |
| #if HAVE_AVX2 || HAVE_SSE2 | |
| // Resolutions (width x height) to be tested for resizing. | |
| const FrameDimension kFrameDim[] = { | |
| make_tuple(3840, 2160), make_tuple(2560, 1440), make_tuple(1920, 1080), | |
| make_tuple(1280, 720), make_tuple(640, 480), make_tuple(640, 360), | |
| make_tuple(286, 286), make_tuple(284, 284), make_tuple(282, 282), | |
| make_tuple(280, 280), make_tuple(262, 262), make_tuple(258, 258), | |
| make_tuple(256, 256), make_tuple(34, 34), | |
| }; | |
| #endif | |
| #if HAVE_AVX2 | |
| INSTANTIATE_TEST_SUITE_P( | |
| AVX2, AV1ResizeYTest, | |
| ::testing::Combine(::testing::Values(av1_resize_vert_dir_avx2), | |
| ::testing::ValuesIn(kFrameDim))); | |
| #endif | |
| #if HAVE_SSE2 | |
| INSTANTIATE_TEST_SUITE_P( | |
| SSE2, AV1ResizeYTest, | |
| ::testing::Combine(::testing::Values(av1_resize_vert_dir_sse2), | |
| ::testing::ValuesIn(kFrameDim))); | |
| #endif | |
| using LowBDResize_x_Func = void (*)(const uint8_t *const input, int in_stride, | |
| uint8_t *intbuf, int height, | |
| int filtered_length, int width2); | |
| using Resize_x_TestParams = tuple<LowBDResize_x_Func, FrameDimension>; | |
| class AV1ResizeXTest : public ::testing::TestWithParam<Resize_x_TestParams> { | |
| public: | |
| void SetUp() { | |
| test_fun_ = GET_PARAM(0); | |
| frame_dim_ = GET_PARAM(1); | |
| width_ = std::get<0>(frame_dim_); | |
| height_ = std::get<1>(frame_dim_); | |
| const int msb = get_msb(AOMMIN(width_, height_)); | |
| n_levels_ = AOMMAX(msb - MIN_PYRAMID_SIZE_LOG2, 1); | |
| const int src_buf_size = width_ * height_; | |
| const int dest_buf_size = (width_ * height_) / 2; | |
| src_ = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[src_buf_size]); | |
| ASSERT_NE(src_, nullptr); | |
| ref_dest_ = | |
| std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]); | |
| ASSERT_NE(ref_dest_, nullptr); | |
| test_dest_ = | |
| std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[dest_buf_size]); | |
| ASSERT_NE(test_dest_, nullptr); | |
| } | |
| void RunTest() { | |
| for (int i = 0; i < width_ * height_; ++i) src_[i] = rng_.Rand8(); | |
| for (int level = 1; level < n_levels_; ++level) { | |
| const int width2 = (width_ >> level); | |
| av1_resize_horz_dir_c(src_.get(), width_, ref_dest_.get(), height_, | |
| width2 << 1, width2); | |
| test_fun_(src_.get(), width_, test_dest_.get(), height_, width2 << 1, | |
| width2); | |
| AssertOutputBufferEq(ref_dest_.get(), test_dest_.get(), width2, height_); | |
| } | |
| } | |
| void SpeedTest() { | |
| for (int i = 0; i < width_ * height_; ++i) src_[i] = rng_.Rand8(); | |
| for (int level = 1; level < n_levels_; ++level) { | |
| const int width2 = (width_ >> level); | |
| aom_usec_timer ref_timer; | |
| aom_usec_timer_start(&ref_timer); | |
| for (int j = 0; j < kIters; ++j) { | |
| av1_resize_horz_dir_c(src_.get(), width_, ref_dest_.get(), height_, | |
| width2 << 1, width2); | |
| } | |
| aom_usec_timer_mark(&ref_timer); | |
| const int64_t ref_time = aom_usec_timer_elapsed(&ref_timer); | |
| aom_usec_timer tst_timer; | |
| aom_usec_timer_start(&tst_timer); | |
| for (int j = 0; j < kIters; ++j) { | |
| test_fun_(src_.get(), width_, test_dest_.get(), height_, width2 << 1, | |
| width2); | |
| } | |
| aom_usec_timer_mark(&tst_timer); | |
| const int64_t tst_time = aom_usec_timer_elapsed(&tst_timer); | |
| std::cout << "level: " << level << " [" << width2 << " x " << height_ | |
| << "] C time = " << ref_time << " , SIMD time = " << tst_time | |
| << " scaling=" << float(1.00) * ref_time / tst_time << "x \n"; | |
| } | |
| } | |
| private: | |
| LowBDResize_x_Func test_fun_; | |
| FrameDimension frame_dim_; | |
| int width_; | |
| int height_; | |
| int n_levels_; | |
| std::unique_ptr<uint8_t[]> src_; | |
| std::unique_ptr<uint8_t[]> ref_dest_; | |
| std::unique_ptr<uint8_t[]> test_dest_; | |
| libaom_test::ACMRandom rng_; | |
| }; | |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1ResizeXTest); | |
| TEST_P(AV1ResizeXTest, RunTest) { RunTest(); } | |
| TEST_P(AV1ResizeXTest, DISABLED_SpeedTest) { SpeedTest(); } | |
| #if HAVE_SSE2 | |
| INSTANTIATE_TEST_SUITE_P( | |
| SSE2, AV1ResizeXTest, | |
| ::testing::Combine(::testing::Values(av1_resize_horz_dir_sse2), | |
| ::testing::ValuesIn(kFrameDim))); | |
| #endif | |
| #if HAVE_AVX2 | |
| INSTANTIATE_TEST_SUITE_P( | |
| AVX2, AV1ResizeXTest, | |
| ::testing::Combine(::testing::Values(av1_resize_horz_dir_avx2), | |
| ::testing::ValuesIn(kFrameDim))); | |
| #endif | |
| } // namespace |