| /* | 
 |  * Copyright (c) 2022, Alliance for Open Media. All rights reserved | 
 |  * | 
 |  * This source code is subject to the terms of the BSD 3-Clause Clear License | 
 |  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear | 
 |  * License was not distributed with this source code in the LICENSE file, you | 
 |  * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/.  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 | 
 |  * aomedia.org/license/patent-license/. | 
 |  */ | 
 |  | 
 | #include "third_party/googletest/src/googletest/include/gtest/gtest.h" | 
 |  | 
 | #include "config/aom_config.h" | 
 | #include "config/aom_dsp_rtcd.h" | 
 | #include "config/av1_rtcd.h" | 
 |  | 
 | #include "aom_dsp/aom_dsp_common.h" | 
 |  | 
 | #include "av1/common/enums.h" | 
 | #include "av1/common/ccso.h" | 
 |  | 
 | #include "test/acm_random.h" | 
 | #include "test/function_equivalence_test.h" | 
 | #include "test/register_state_check.h" | 
 |  | 
 | using libaom_test::ACMRandom; | 
 | using libaom_test::FunctionEquivalenceTest; | 
 |  | 
 | namespace { | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | // ccso_filter_block_hbd_wo_buf | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | typedef void (*CCSO_WO_BUF)(const uint16_t *src_y, uint16_t *dst_yuv, | 
 |                             const int x, const int y, const int pic_width, | 
 |                             const int pic_height, int *src_cls, | 
 |                             const int8_t *offset_buf, const int src_y_stride, | 
 |                             const int dst_stride, const int y_uv_hscale, | 
 |                             const int y_uv_vscale, const int thr, | 
 |                             const int neg_thr, const int *src_loc, | 
 |                             const int max_val, const int blk_size, | 
 |                             const bool isSingleBand, const uint8_t shift_bits | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |                             , | 
 |                             const int edge_clf | 
 | #endif | 
 | #if CONFIG_CCSO_BO_ONLY_OPTION | 
 |                             , | 
 |                             const uint8_t ccso_bo_only | 
 | #endif | 
 | ); | 
 | typedef libaom_test::FuncParam<CCSO_WO_BUF> TestFuncsCCSO_WO_BUF; | 
 |  | 
 | template <typename F> | 
 | class CCSOFilterTest : public FunctionEquivalenceTest<F> { | 
 |  public: | 
 |   static const int kIterations = 10000; | 
 |   static const int kMaxWidth = | 
 |       (MAX_SB_SIZE << 1) * 5;  // * 5 to cover longer strides | 
 |   static const int kMaxHeight = (MAX_SB_SIZE << 1) * 3; | 
 |   static const int kBufSize = kMaxWidth * kMaxHeight; | 
 |   static const int kMaxMaskWidth = 2 * MAX_SB_SIZE; | 
 |   static const int kMaxMaskSize = kMaxMaskWidth; | 
 |  | 
 |   virtual ~CCSOFilterTest() {} | 
 |  | 
 |   virtual void Execute() = 0; | 
 |  | 
 |   void Common() { | 
 |     // we just test whether block level filter generate same results | 
 |     y_uv_hscale_ = this->rng_(2); | 
 |     y_uv_vscale_ = y_uv_hscale_; | 
 |     pic_width_ = (MAX_SB_SIZE * 2) >> y_uv_hscale_; | 
 |     pic_height_ = (MAX_SB_SIZE * 2) >> y_uv_vscale_; | 
 |     blk_size_ = pic_width_; | 
 |  | 
 |     // AVX2 fetch 16 elements, chroma 4:2:0 case 32 | 
 |     src_y_stride_ = | 
 |         this->rng_(kMaxWidth + 1 - 32) + 32 + (CCSO_PADDING_SIZE << 1); | 
 |     dst_stride_ = this->rng_(kMaxWidth + 1 - 32) + 32; | 
 |  | 
 |     filter_sup_ = this->rng_(6); | 
 |     derive_ccso_sample_pos(src_loc_, src_y_stride_, filter_sup_); | 
 |  | 
 |     const uint8_t quant_sz[4] = { 16, 8, 32, 64 }; | 
 |     thr_ = quant_sz[this->rng_(4)]; | 
 |     neg_thr_ = -1 * thr_; | 
 |  | 
 |     const uint8_t shift_bits_a[2] = { 8, 10 }; | 
 |     shift_bits_ = shift_bits_a[this->rng_(2)]; | 
 |     max_val_ = (1 << shift_bits_) - 1; | 
 |     isSingleBand_ = this->rng_(2); | 
 |  | 
 |     Execute(); | 
 |   } | 
 |  | 
 |   uint16_t dst_ref_[kBufSize]; | 
 |   uint16_t dst_tst_[kBufSize]; | 
 |   int dst_stride_; | 
 |  | 
 |   uint16_t src_y_[kBufSize]; | 
 |   int src_y_stride_; | 
 |  | 
 |   int8_t offset_buf_[CCSO_BAND_NUM * 16]; | 
 |   uint8_t mask_[kMaxMaskSize]; | 
 |  | 
 |   int src_loc_[2]; | 
 |  | 
 |   int pic_width_; | 
 |   int pic_height_; | 
 |   int blk_size_; | 
 |   uint8_t filter_sup_; | 
 |  | 
 |   int y_uv_vscale_; | 
 |   int y_uv_hscale_; | 
 |   int thr_; | 
 |   int neg_thr_; | 
 |   int max_val_; | 
 |   bool isSingleBand_; | 
 |   uint8_t shift_bits_; | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |   int edge_clf_; | 
 | #endif | 
 | }; | 
 |  | 
 | class CCSOWOBUFTest : public CCSOFilterTest<CCSO_WO_BUF> { | 
 |  protected: | 
 |   void Execute() { | 
 |     params_.ref_func(src_y_, dst_ref_, 0, 0, pic_width_, pic_height_, src_cls_, | 
 |                      offset_buf_, src_y_stride_, dst_stride_, y_uv_hscale_, | 
 |                      y_uv_vscale_, thr_, neg_thr_, src_loc_, max_val_, | 
 |                      blk_size_, isSingleBand_, shift_bits_ | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |                      , | 
 |                      edge_clf_ | 
 | #endif | 
 | #if CONFIG_CCSO_BO_ONLY_OPTION | 
 |                      , | 
 |                      0 | 
 | #endif | 
 |     ); | 
 |  | 
 |     ASM_REGISTER_STATE_CHECK(params_.tst_func( | 
 |         src_y_, dst_tst_, 0, 0, pic_width_, pic_height_, src_cls_, offset_buf_, | 
 |         src_y_stride_, dst_stride_, y_uv_hscale_, y_uv_vscale_, thr_, neg_thr_, | 
 |         src_loc_, max_val_, blk_size_, isSingleBand_, shift_bits_ | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |         , | 
 |         edge_clf_ | 
 | #endif | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |         , | 
 |         0 | 
 | #endif | 
 |         )); | 
 |  | 
 |     for (int r = 0; r < blk_size_; ++r) { | 
 |       for (int c = 0; c < blk_size_; ++c) { | 
 |         ASSERT_EQ(dst_ref_[r * dst_stride_ + c], dst_tst_[r * dst_stride_ + c]); | 
 |       } | 
 |     } | 
 |   } | 
 |   int src_cls_[2]; | 
 | }; | 
 |  | 
 | TEST_P(CCSOWOBUFTest, RandomValues) { | 
 |   for (int iter = 0; iter < kIterations && !HasFatalFailure(); ++iter) { | 
 |     const int hi = 1 << 10; | 
 |     for (int i = 0; i < kBufSize; ++i) { | 
 |       dst_ref_[i] = 0; | 
 |       dst_tst_[i] = 0; | 
 |       src_y_[i] = rng_(hi); | 
 |     } | 
 |     const int ccso_offset[8] = { -10, -7, -3, -1, 0, 1, 3, 7 }; | 
 |  | 
 |     for (int i = 0; i < CCSO_BAND_NUM * 16; i++) { | 
 |       offset_buf_[i] = ccso_offset[rng_(8)]; | 
 |     } | 
 |  | 
 |     Common(); | 
 |   } | 
 | } | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | // ccso_filter_block_hbd_with_buf | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | typedef void (*CCSO_With_BUF)(const uint16_t *src_y, uint16_t *dst_yuv, | 
 |                               const uint8_t *src_cls0, const uint8_t *src_cls1, | 
 |                               const int src_y_stride, const int dst_stride, | 
 |                               const int ccso_stride, const int x, const int y, | 
 |                               const int pic_width, const int pic_height, | 
 |                               const int8_t *offset_buf, const int blk_size, | 
 |                               const int y_uv_hscale, const int y_uv_vscale, | 
 |                               const int max_val, const uint8_t shift_bits | 
 | #if CONFIG_CCSO_BO_ONLY_OPTION | 
 |                               , | 
 |                               const uint8_t ccso_bo_only | 
 | #endif | 
 | ); | 
 | typedef libaom_test::FuncParam<CCSO_With_BUF> TestFuncsCCSO_With_BUF; | 
 |  | 
 | class CCSOWITHBUFTest : public CCSOFilterTest<CCSO_With_BUF> { | 
 |  protected: | 
 |   void Execute() { | 
 |     ccso_stride_ = src_y_stride_ - (CCSO_PADDING_SIZE << 1); | 
 |     params_.ref_func(src_y_, dst_ref_, src_cls0_, src_cls1_, src_y_stride_, | 
 |                      dst_stride_, ccso_stride_, 0, 0, pic_width_, pic_height_, | 
 |                      offset_buf_, blk_size_, y_uv_hscale_, y_uv_vscale_, | 
 |                      max_val_, shift_bits_ | 
 | #if CONFIG_CCSO_BO_ONLY_OPTION | 
 |                      , | 
 |                      0 | 
 | #endif | 
 |     ); | 
 |  | 
 |     ASM_REGISTER_STATE_CHECK(params_.tst_func( | 
 |         src_y_, dst_tst_, src_cls0_, src_cls1_, src_y_stride_, dst_stride_, | 
 |         ccso_stride_, 0, 0, pic_width_, pic_height_, offset_buf_, blk_size_, | 
 |         y_uv_hscale_, y_uv_vscale_, max_val_, shift_bits_ | 
 | #if CONFIG_CCSO_BO_ONLY_OPTION | 
 |         , | 
 |         0 | 
 | #endif | 
 |         )); | 
 |  | 
 |     for (int r = 0; r < blk_size_; ++r) { | 
 |       for (int c = 0; c < blk_size_; ++c) { | 
 |         ASSERT_EQ(dst_ref_[r * dst_stride_ + c], dst_tst_[r * dst_stride_ + c]); | 
 |       } | 
 |     } | 
 |   } | 
 |   uint8_t src_cls0_[kBufSize]; | 
 |   uint8_t src_cls1_[kBufSize]; | 
 |   int ccso_stride_; | 
 | }; | 
 |  | 
 | TEST_P(CCSOWITHBUFTest, RandomValues) { | 
 |   for (int iter = 0; iter < kIterations && !HasFatalFailure(); ++iter) { | 
 |     const int hi = 1 << 10; | 
 |     for (int i = 0; i < kBufSize; ++i) { | 
 |       dst_ref_[i] = 0; | 
 |       dst_tst_[i] = 0; | 
 |       src_cls0_[i] = rng_(3); | 
 |       src_cls1_[i] = rng_(3); | 
 |       src_y_[i] = rng_(hi); | 
 |     } | 
 |     const int ccso_offset[8] = { -10, -7, -3, -1, 0, 1, 3, 7 }; | 
 |  | 
 |     for (int i = 0; i < CCSO_BAND_NUM * 16; i++) { | 
 |       offset_buf_[i] = ccso_offset[rng_(8)]; | 
 |     } | 
 |  | 
 |     Common(); | 
 |   } | 
 | } | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | // ccso_derive_src_block_avx2 | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | typedef void (*CCSO_Derive_Src)(const uint16_t *src_y, uint8_t *const src_cls0, | 
 |                                 uint8_t *const src_cls1, const int src_y_stride, | 
 |                                 const int ccso_stride, const int x, const int y, | 
 |                                 const int pic_width, const int pic_height, | 
 |                                 const int y_uv_hscale, const int y_uv_vscale, | 
 |                                 const int thr, const int neg_thr, | 
 |                                 const int *src_loc, const int blk_size | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |                                 , | 
 |                                 const int edge_clf | 
 | #endif | 
 | ); | 
 | typedef libaom_test::FuncParam<CCSO_Derive_Src> TestFuncsCCSO_Derive_Src; | 
 |  | 
 | class CCSODeriveSrcTest : public CCSOFilterTest<CCSO_Derive_Src> { | 
 |  protected: | 
 |   void Execute() { | 
 |     ccso_stride_ = src_y_stride_ - (CCSO_PADDING_SIZE << 1); | 
 |     params_.ref_func(src_y_, src_cls0_ref, src_cls1_ref, src_y_stride_, | 
 |                      ccso_stride_, 0, 0, pic_width_, pic_height_, y_uv_hscale_, | 
 |                      y_uv_vscale_, thr_, neg_thr_, src_loc_, blk_size_ | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |                      , | 
 |                      edge_clf_ | 
 | #endif | 
 |     ); | 
 |  | 
 |     ASM_REGISTER_STATE_CHECK(params_.tst_func( | 
 |         src_y_, src_cls0_tst, src_cls1_tst, src_y_stride_, ccso_stride_, 0, 0, | 
 |         pic_width_, pic_height_, y_uv_hscale_, y_uv_vscale_, thr_, neg_thr_, | 
 |         src_loc_, blk_size_ | 
 | #if CONFIG_CCSO_EDGE_CLF | 
 |         , | 
 |         edge_clf_ | 
 | #endif | 
 |         )); | 
 |  | 
 |     for (int r = 0; r < blk_size_; ++r) { | 
 |       for (int c = 0; c < blk_size_; ++c) { | 
 |         ASSERT_EQ(src_cls0_ref[(r << y_uv_vscale_) * ccso_stride_ + | 
 |                                (c << y_uv_hscale_)], | 
 |                   src_cls0_tst[(r << y_uv_vscale_) * ccso_stride_ + | 
 |                                (c << y_uv_hscale_)]); | 
 |         ASSERT_EQ(src_cls1_ref[(r << y_uv_vscale_) * ccso_stride_ + | 
 |                                (c << y_uv_hscale_)], | 
 |                   src_cls1_tst[(r << y_uv_vscale_) * ccso_stride_ + | 
 |                                (c << y_uv_hscale_)]); | 
 |       } | 
 |     } | 
 |   } | 
 |   uint8_t src_cls0_ref[kBufSize]; | 
 |   uint8_t src_cls1_ref[kBufSize]; | 
 |   uint8_t src_cls0_tst[kBufSize]; | 
 |   uint8_t src_cls1_tst[kBufSize]; | 
 |   int ccso_stride_; | 
 | }; | 
 |  | 
 | TEST_P(CCSODeriveSrcTest, RandomValues) { | 
 |   for (int iter = 0; iter < kIterations && !HasFatalFailure(); ++iter) { | 
 |     const int hi = 1 << 10; | 
 |     for (int i = 0; i < kBufSize; ++i) { | 
 |       dst_ref_[i] = 0; | 
 |       dst_tst_[i] = 0; | 
 |       src_cls0_ref[i] = 0; | 
 |       src_cls1_ref[i] = 0; | 
 |       src_cls0_tst[i] = 0; | 
 |       src_cls1_tst[i] = 0; | 
 |       src_y_[i] = rng_(hi); | 
 |     } | 
 |  | 
 |     Common(); | 
 |   } | 
 | } | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | // compute_distortion_block_avx2 | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 | typedef uint64_t (*CCSO_Dist_Block)(const uint16_t *org, const int org_stride, | 
 |                                     const uint16_t *rec16, const int rec_stride, | 
 |                                     const int x, const int y, | 
 |                                     const int log2_filter_unit_size, | 
 |                                     const int height, const int width); | 
 |  | 
 | typedef libaom_test::FuncParam<CCSO_Dist_Block> TestFuncsCCSO_Dist_Block; | 
 |  | 
 | class CCSODistBlockTest : public CCSOFilterTest<CCSO_Dist_Block> { | 
 |  protected: | 
 |   void Execute() { | 
 |     org_ = src_y_; | 
 |     org_stride_ = src_y_stride_; | 
 |     rec16_ = dst_ref_; | 
 |     rec_stride_ = src_y_stride_; | 
 |     log2_filter_unit_size_ = 1 - y_uv_hscale_ + 7; | 
 |     height_ = pic_height_; | 
 |     width_ = pic_width_; | 
 |     uint64_t ref = params_.ref_func(org_, org_stride_, rec16_, rec_stride_, 0, | 
 |                                     0, log2_filter_unit_size_, height_, width_); | 
 |     uint64_t tst; | 
 |     ASM_REGISTER_STATE_CHECK( | 
 |         tst = params_.tst_func(org_, org_stride_, rec16_, rec_stride_, 0, 0, | 
 |                                log2_filter_unit_size_, height_, width_)); | 
 |     ASSERT_EQ(ref, tst); | 
 |   } | 
 |   uint16_t *org_; | 
 |   int org_stride_; | 
 |   uint16_t *rec16_; | 
 |   int rec_stride_; | 
 |   int log2_filter_unit_size_; | 
 |   int height_; | 
 |   int width_; | 
 | }; | 
 |  | 
 | TEST_P(CCSODistBlockTest, RandomValues) { | 
 |   for (int iter = 0; iter < kIterations && !HasFatalFailure(); ++iter) { | 
 |     const int hi = 1 << 10; | 
 |     for (int i = 0; i < kBufSize; ++i) { | 
 |       dst_ref_[i] = rng_(hi); | 
 |       src_y_[i] = rng_(hi); | 
 |     } | 
 |  | 
 |     Common(); | 
 |   } | 
 | } | 
 |  | 
 | #if HAVE_AVX2 | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AVX2, CCSODistBlockTest, | 
 |     ::testing::Values(TestFuncsCCSO_Dist_Block(compute_distortion_block_c, | 
 |                                                compute_distortion_block_avx2))); | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AVX2, CCSODeriveSrcTest, | 
 |     ::testing::Values(TestFuncsCCSO_Derive_Src(ccso_derive_src_block_c, | 
 |                                                ccso_derive_src_block_avx2))); | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P(AVX2, CCSOWITHBUFTest, | 
 |                          ::testing::Values(TestFuncsCCSO_With_BUF( | 
 |                              ccso_filter_block_hbd_with_buf_c, | 
 |                              ccso_filter_block_hbd_with_buf_avx2))); | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AVX2, CCSOWOBUFTest, | 
 |     ::testing::Values(TestFuncsCCSO_WO_BUF(ccso_filter_block_hbd_wo_buf_c, | 
 |                                            ccso_filter_block_hbd_wo_buf_avx2))); | 
 | #endif  // HAVE_AVX2 | 
 |  | 
 | }  // namespace |