| /* | 
 |  * Copyright (c) 2025, 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 <cstdlib> | 
 | #include <string> | 
 | #include <tuple> | 
 |  | 
 | #include "third_party/googletest/src/googletest/include/gtest/gtest.h" | 
 |  | 
 | #include "config/aom_config.h" | 
 | #include "config/av1_rtcd.h" | 
 |  | 
 | #include "av1/common/av1_common_int.h" | 
 | #include "aom_ports/aom_timer.h" | 
 |  | 
 | #include "av1/common/gdf_block.h" | 
 | #include "av1/common/gdf.h" | 
 | #include "test/acm_random.h" | 
 | #include "test/clear_system_state.h" | 
 | #include "test/util.h" | 
 |  | 
 | using libaom_test::ACMRandom; | 
 |  | 
 | namespace { | 
 | typedef void (*gdf_set_lap_and_cls_unit_func)( | 
 |     const int i_min, const int i_max, const int j_min, const int j_max, | 
 |     const int stripe_size, const uint16_t *rec_pnt, const int rec_stride, | 
 |     const int bit_depth, uint16_t *const *gdf_lap_y, const int gdf_lap_y_stride, | 
 |     uint32_t *gdf_cls_y, const int gdf_cls_y_stride); | 
 |  | 
 | typedef void (*gdf_inference_unit_func)( | 
 |     const int i_min, const int i_max, const int j_min, const int j_max, | 
 |     const int qp_idx, const uint16_t *rec_pnt, const int rec_stride, | 
 |     uint16_t *const *gdf_lap_pnt, const int gdf_lap_stride, | 
 |     const uint32_t *gdf_cls_pnt, const int gdf_cls_stride, int16_t *err_pnt, | 
 |     const int err_stride, const int pxl_shift, const int ref_dst_idx); | 
 |  | 
 | typedef void (*gdf_compensation_unit_func)( | 
 |     uint16_t *rec_pnt, const int rec_stride, int16_t *err_pnt, | 
 |     const int err_stride, const int err_shift, const int scale, | 
 |     const int pxl_max, const int blk_height, const int blk_width); | 
 |  | 
 | typedef std::tuple<gdf_set_lap_and_cls_unit_func, gdf_set_lap_and_cls_unit_func, | 
 |                    gdf_inference_unit_func, gdf_inference_unit_func, | 
 |                    gdf_compensation_unit_func, gdf_compensation_unit_func, int, | 
 |                    int, int, int, int, int> | 
 |     gdf_param_t; | 
 | constexpr int max_test_tile_size = 512; | 
 |  | 
 | class GDFTest : public ::testing::TestWithParam<gdf_param_t> { | 
 |  public: | 
 |   virtual ~GDFTest() {} | 
 |   virtual void SetUp() { | 
 |     lapgdf = GET_PARAM(0); | 
 |     ref_lapgdf = GET_PARAM(1); | 
 |     infgdf = GET_PARAM(2); | 
 |     ref_infgdf = GET_PARAM(3); | 
 |     compgdf = GET_PARAM(4); | 
 |     ref_compgdf = GET_PARAM(5); | 
 |     height = GET_PARAM(6); | 
 |     width = GET_PARAM(7); | 
 |     bd = GET_PARAM(8); | 
 |     qp_idx = GET_PARAM(9); | 
 |     ref_dst = GET_PARAM(10); | 
 |     mib_size = GET_PARAM(11); | 
 |   } | 
 |   virtual void TearDown() { libaom_test::ClearSystemState(); } | 
 |  | 
 |  protected: | 
 |   gdf_set_lap_and_cls_unit_func lapgdf; | 
 |   gdf_set_lap_and_cls_unit_func ref_lapgdf; | 
 |   gdf_inference_unit_func infgdf; | 
 |   gdf_inference_unit_func ref_infgdf; | 
 |   gdf_compensation_unit_func compgdf; | 
 |   gdf_compensation_unit_func ref_compgdf; | 
 |   int height; | 
 |   int width; | 
 |   int bd; | 
 |   int qp_idx; | 
 |   int ref_dst; | 
 |   int mib_size; | 
 | }; | 
 |  | 
 | GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GDFTest); | 
 |  | 
 | ACMRandom rnd(ACMRandom::DeterministicSeed()); | 
 | void test_gdf(int iterations, int height, int width, int depth, int qp_idx, | 
 |               int ref_dst, int mib_size, gdf_set_lap_and_cls_unit_func lapgdf, | 
 |               gdf_set_lap_and_cls_unit_func ref_lapgdf, | 
 |               gdf_inference_unit_func infgdf, | 
 |               gdf_inference_unit_func ref_infgdf, | 
 |               gdf_compensation_unit_func compgdf, | 
 |               gdf_compensation_unit_func ref_compgdf) { | 
 |   DECLARE_ALIGNED(16, static uint16_t, | 
 |                   rec[(max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |                        GDF_ERR_STRIDE_MARGIN)]); | 
 |   DECLARE_ALIGNED(16, static uint16_t, | 
 |                   out[(max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |                        GDF_ERR_STRIDE_MARGIN)]); | 
 |   DECLARE_ALIGNED( | 
 |       16, static uint16_t, | 
 |       ref_out[(max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2) * | 
 |               (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |                GDF_ERR_STRIDE_MARGIN)]); | 
 |  | 
 |   GdfInfo gi, ref_gi; | 
 |   memset(&gi, 0, sizeof(gi)); | 
 |   memset(&ref_gi, 0, sizeof(ref_gi)); | 
 |   init_gdf_test(&gi, mib_size, height, width); | 
 |   init_gdf_test(&ref_gi, mib_size, height, width); | 
 |  | 
 |   int err = 0, is_lap_err = 0, is_cls_err = 0; | 
 |   int is_inf_err = 0, is_comp_err = 0; | 
 |   const int rec_stride = | 
 |       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |        GDF_ERR_STRIDE_MARGIN); | 
 |   const int pxl_max = (1 << depth) - 1; | 
 |   const int pxl_shift = GDF_TEST_INP_PREC - AOMMIN(depth, GDF_TEST_INP_PREC); | 
 |   const int err_shift = GDF_RDO_SCALE_NUM_LOG2 + GDF_TEST_INP_PREC - depth; | 
 |   for (int iter = 0; iter < iterations && !err; iter++) { | 
 |     memset(rec, 0, sizeof(rec)); | 
 |     for (uint16_t &i : rec) { | 
 |       i = clamp(rnd.Rand16() & ((1 << depth) - 1), 0, (1 << depth) - 1); | 
 |     } | 
 |     alloc_gdf_buffers(&gi); | 
 |     alloc_gdf_buffers(&ref_gi); | 
 |     const int stripe_size = gi.gdf_stripe_size; | 
 |     int top_buf = GDF_TEST_EXTRA_VER_BORDER; | 
 |     int bot_buf = GDF_TEST_EXTRA_VER_BORDER; | 
 |     const int rec_height = height; | 
 |     const int rec_width = width; | 
 |  | 
 |     const int input_stride = (((rec_width + GDF_TEST_STRIPE_SIZE) >> 4) << 4) + | 
 |                              16;  // GDF_TEST_STRIPE_SIZE: max unit size | 
 |     // 16: AVX2 vector length | 
 |     gi.inp_stride = input_stride; | 
 |  | 
 |     gi.inp_pad_ptr = | 
 |         (uint16_t *)aom_memalign(32, (top_buf + rec_height + bot_buf + 4) * | 
 |                                          input_stride * sizeof(uint16_t)); | 
 |     for (int i = top_buf; i < top_buf + rec_height; i++) { | 
 |       memcpy(gi.inp_pad_ptr + i * input_stride + GDF_TEST_EXTRA_HOR_BORDER, | 
 |              rec + (i - top_buf) * rec_stride, sizeof(uint16_t) * rec_width); | 
 |       if (depth > GDF_TEST_INP_PREC) { | 
 |         const unsigned int diff_bit_depth = depth - GDF_TEST_INP_PREC; | 
 |         for (int j = 0; j < rec_width; j++) { | 
 |           uint16_t *cur_line = | 
 |               gi.inp_pad_ptr + i * input_stride + GDF_TEST_EXTRA_HOR_BORDER; | 
 |           cur_line[j] >>= diff_bit_depth; | 
 |         } | 
 |       } | 
 |     } | 
 |     gi.inp_ptr = | 
 |         gi.inp_pad_ptr + top_buf * input_stride + GDF_TEST_EXTRA_HOR_BORDER; | 
 |     gdf_extend_frame_highbd(gi.inp_ptr, rec_width, rec_height, input_stride, | 
 |                             GDF_TEST_EXTRA_HOR_BORDER, | 
 |                             GDF_TEST_EXTRA_VER_BORDER); | 
 |  | 
 |     for (int y_pos = -GDF_TEST_STRIPE_OFF; y_pos < height; | 
 |          y_pos += gi.gdf_block_size) { | 
 |       for (int x_pos = 0; x_pos < width; x_pos += gi.gdf_block_size) { | 
 |         for (int v_pos = y_pos; | 
 |              v_pos < y_pos + gi.gdf_block_size && v_pos < height; | 
 |              v_pos += gi.gdf_unit_size) { | 
 |           for (int u_pos = x_pos; | 
 |                u_pos < x_pos + gi.gdf_block_size && u_pos < width; | 
 |                u_pos += gi.gdf_unit_size) { | 
 |             int i_min = AOMMAX(v_pos, GDF_TEST_FRAME_BOUNDARY_SIZE); | 
 |             int i_max = AOMMIN(v_pos + gi.gdf_unit_size, | 
 |                                height - GDF_TEST_FRAME_BOUNDARY_SIZE); | 
 |             int j_min = AOMMAX(u_pos, GDF_TEST_FRAME_BOUNDARY_SIZE); | 
 |             int j_max = AOMMIN(u_pos + gi.gdf_unit_size, | 
 |                                width - GDF_TEST_FRAME_BOUNDARY_SIZE); | 
 |             const uint16_t *inp_ptr = | 
 |                 gi.inp_ptr + gi.inp_stride * i_min + j_min; | 
 |             lapgdf(i_min, i_max, j_min, j_max, stripe_size, inp_ptr, | 
 |                    gi.inp_stride, depth, gi.lap_ptr, gi.lap_stride, gi.cls_ptr, | 
 |                    gi.cls_stride); | 
 |             ref_lapgdf(i_min, i_max, j_min, j_max, stripe_size, inp_ptr, | 
 |                        gi.inp_stride, depth, ref_gi.lap_ptr, ref_gi.lap_stride, | 
 |                        ref_gi.cls_ptr, ref_gi.cls_stride); | 
 |  | 
 |             const int lap_height = (i_max - i_min) >> 1; | 
 |             const int lap_width = (j_max - j_min); | 
 |             const int cls_width = (j_max - j_min) >> 1; | 
 |             const int res_height = (i_max - i_min); | 
 |             const int res_width = (j_max - j_min); | 
 |             for (int i = 0; i < lap_height && !err; i++) { | 
 |               for (int grd_idx = 0; | 
 |                    grd_idx < GDF_NET_INP_GRD_NUM && !is_lap_err; grd_idx++) { | 
 |                 is_lap_err = | 
 |                     memcmp(gi.lap_ptr[grd_idx] + i * gi.lap_stride, | 
 |                            ref_gi.lap_ptr[grd_idx] + i * ref_gi.lap_stride, | 
 |                            sizeof(uint16_t) * lap_width); | 
 |               } | 
 |               is_cls_err = memcmp(gi.cls_ptr + i * gi.cls_stride, | 
 |                                   ref_gi.cls_ptr + i * ref_gi.cls_stride, | 
 |                                   sizeof(uint32_t) * cls_width); | 
 |               err = is_lap_err | is_cls_err; | 
 |             } | 
 |             infgdf(i_min, i_max, j_min, j_max, qp_idx, inp_ptr, gi.inp_stride, | 
 |                    gi.lap_ptr, gi.lap_stride, gi.cls_ptr, gi.cls_stride, | 
 |                    gi.err_ptr, gi.err_stride, pxl_shift, ref_dst); | 
 |             ref_infgdf(i_min, i_max, j_min, j_max, qp_idx, inp_ptr, | 
 |                        gi.inp_stride, ref_gi.lap_ptr, ref_gi.lap_stride, | 
 |                        ref_gi.cls_ptr, ref_gi.cls_stride, ref_gi.err_ptr, | 
 |                        ref_gi.err_stride, pxl_shift, ref_dst); | 
 |             for (int i = 0; i < res_height && !err; i++) { | 
 |               is_inf_err = memcmp(gi.err_ptr + i * gi.err_stride, | 
 |                                   ref_gi.err_ptr + i * ref_gi.err_stride, | 
 |                                   sizeof(int16_t) * res_width); | 
 |               err |= is_inf_err; | 
 |             } | 
 |             for (int s = 1; s <= GDF_RDO_SCALE_NUM && !err; s++) { | 
 |               memcpy( | 
 |                   out, rec, | 
 |                   sizeof(uint16_t) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |                        GDF_ERR_STRIDE_MARGIN)); | 
 |               compgdf(out, rec_stride, gi.err_ptr, gi.err_stride, err_shift, s, | 
 |                       pxl_max, i_max - i_min, j_max - j_min); | 
 |               memcpy( | 
 |                   ref_out, rec, | 
 |                   sizeof(uint16_t) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |                        GDF_ERR_STRIDE_MARGIN)); | 
 |               ref_compgdf(ref_out, rec_stride, ref_gi.err_ptr, | 
 |                           ref_gi.err_stride, err_shift, s, pxl_max, | 
 |                           i_max - i_min, j_max - j_min); | 
 |               is_comp_err = memcmp( | 
 |                   out, ref_out, | 
 |                   sizeof(uint16_t) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2) * | 
 |                       (max_test_tile_size + GDF_TEST_FRAME_BOUNDARY_SIZE * 2 + | 
 |                        GDF_ERR_STRIDE_MARGIN)); | 
 |               err |= is_comp_err; | 
 |             } | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |     aom_free(gi.inp_pad_ptr); | 
 |   } | 
 |   free_gdf_buffers(&gi); | 
 |   free_gdf_buffers(&ref_gi); | 
 |   EXPECT_EQ(err, 0) << "Error: GDFTest, SIMD and C mismatch." | 
 |                     << "is_lap_err : " << is_lap_err << std::endl | 
 |                     << "is_cls_err : " << is_cls_err << std::endl | 
 |                     << "is_inf_err : " << is_inf_err << std::endl | 
 |                     << "is_com_err : " << is_comp_err << std::endl | 
 |                     << std::endl; | 
 | } | 
 |  | 
 | TEST_P(GDFTest, TestSIMDNoMismatchGDF) { | 
 |   test_gdf(1, height, width, bd, qp_idx, ref_dst, mib_size, lapgdf, ref_lapgdf, | 
 |            infgdf, ref_infgdf, compgdf, ref_compgdf); | 
 | } | 
 |  | 
 | using std::make_tuple; | 
 |  | 
 | #if defined(_WIN64) || !defined(_MSC_VER) || defined(__clang__) | 
 |  | 
 | #if HAVE_AVX2 | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AVX2, GDFTest, | 
 |     ::testing::Combine(::testing::Values(&gdf_set_lap_and_cls_unit_c), | 
 |                        ::testing::Values(&gdf_set_lap_and_cls_unit_avx2), | 
 |                        ::testing::Values(&gdf_inference_unit_c), | 
 |                        ::testing::Values(&gdf_inference_unit_avx2), | 
 |                        ::testing::Values(&gdf_compensation_unit_c), | 
 |                        ::testing::Values(&gdf_compensation_unit_avx2), | 
 |                        ::testing::Values(128, max_test_tile_size), | 
 |                        ::testing::Values(128, max_test_tile_size), | 
 |                        ::testing::Values(8, 10, 12), | 
 |                        ::testing::Range(0, GDF_TRAIN_QP_NUM), | 
 |                        ::testing::Range(0, GDF_TRAIN_REFDST_NUM + 1), | 
 |                        ::testing::Values(32, 64))); | 
 | #endif | 
 |  | 
 | #endif | 
 |  | 
 | }  // namespace |