Merge tag 'v3.13.3' into main Release v3.13.3 2026-04-02 v3.13.3 This release includes several bug fixes. This release is ABI compatible with the last release. See https://aomedia.googlesource.com/aom/+log/v3.13.2..v3.13.3 for all the commits in this release. - Bug Fixes * Commit 4cc0867f: fix multithreading crashes when loop restoration filtering is used by the encoder. * Commit 74aaa1ef: fix AOM_SCALING_MODE crashes when compound mask prediction modes are used by the encoder. Bug: 498612990 Change-Id: I60c9a3cef2e0e4e5b7113da1f5052a51fa9ef28b
diff --git a/.gitattributes b/.gitattributes index ffc6912..991963a 100644 --- a/.gitattributes +++ b/.gitattributes
@@ -16,3 +16,4 @@ *.pjt -crlf *.vcp -crlf *.inf -crlf +*.patch -whitespace
diff --git a/CHANGELOG b/CHANGELOG index 354123b..3e1f97c 100644 --- a/CHANGELOG +++ b/CHANGELOG
@@ -1,3 +1,46 @@ +yyyy-mm-dd next version + This release is ABI compatible with the last release. + + This release includes significant compression efficiency and perceptual + quality improvements for layered image encoding, in addition to bug + fixes. + + - New Features + * Tuning modes AOM_TUNE_IQ and AOM_TUNE_SSIMULACRA2 now work with + inter-frame encoding modes (good-quality and realtime), enabling + efficient layered image encoding. + * Variance Boost (deltaq-mode 6) support has been extended to + include good-quality and realtime modes. + * New value 2 for use_fixed_qp_offsets: disallow the application of + per-frame QP offsets, enabling full control of each frame's QP, by + adjusting rc_cfg.cq_level between each encoded frame. + + - Compression Efficiency Improvements + * When encoding 2-layered images with AOM_TUNE_IQ vs. AOM_TUNE_SSIM: + - Good-quality mode: up to 15% gains at similar SSIMULACRA 2 scores. + - Realtime mode: up to 30% gains at similar SSIMULACRA 2 scores. + * Enable screen detection mode 2 (anti-aliased text and graphics aware) in + all-intra mode. + * Some minor quantization matrix formula and delta chroma q tweaks for the + AOM_TUNE_SSIMULACRA2 tuning mode, with up to 0.2% SSIMULACRA 2 score + efficiency gains. + + - Perceptual Quality Improvements + * AOM_TUNE_IQ improves the visual quality of layered image encoding + compared to AOM_TUNE_SSIM. + * Adaptive sharpness: tweak loop filter sharpness threshold to improve + visual quality at QPs 29 and 30. + + - Speedups + * Make Adaptive CDEF more decoder friendly by limiting CDEF application to + only regions where high filter strengths are needed. + + - Bug Fixes + * Commit 4cc0867f: fix multithreading crashes when loop restoration + filtering is used by the encoder. + * Commit 74aaa1ef: fix AOM_SCALING_MODE crashes when compound mask + prediction modes are used by the encoder. + 2026-04-02 v3.13.3 This release includes several bug fixes. This release is ABI compatible with the last release. See
diff --git a/CMakeLists.txt b/CMakeLists.txt index 089149e..580a693 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -151,10 +151,12 @@ "${AOM_ROOT}/aom/aom_codec.h" "${AOM_ROOT}/aom/aom_decoder.h" "${AOM_ROOT}/aom/aom_encoder.h" + "${AOM_ROOT}/aom/aom_ext_ratectrl.h" "${AOM_ROOT}/aom/aom_external_partition.h" "${AOM_ROOT}/aom/aom_frame_buffer.h" "${AOM_ROOT}/aom/aom_image.h" "${AOM_ROOT}/aom/aom_integer.h" + "${AOM_ROOT}/aom/aom_tpl.h" "${AOM_ROOT}/aom/aomcx.h" "${AOM_ROOT}/aom/aomdx.h" "${AOM_ROOT}/aom/internal/aom_codec_internal.h" @@ -799,6 +801,15 @@ list(APPEND AOM_APP_TARGETS lightfield_bitstream_parsing) endif() +if(ENABLE_EXAMPLES AND CONFIG_AV1_ENCODER) + add_executable(low_complexity_mode_encoder + "${AOM_ROOT}/examples/low_complexity_mode_encoder.c" + $<TARGET_OBJECTS:aom_common_app_util> + $<TARGET_OBJECTS:aom_encoder_app_util>) + list(APPEND AOM_EXAMPLE_TARGETS low_complexity_mode_encoder) + list(APPEND AOM_APP_TARGETS low_complexity_mode_encoder) +endif() + foreach(aom_app ${AOM_APP_TARGETS}) target_link_libraries(${aom_app} ${AOM_LIB_LINK_TYPE} aom) endforeach()
diff --git a/aom/aom_codec.h b/aom/aom_codec.h index 84454a2..1ecf5f8 100644 --- a/aom/aom_codec.h +++ b/aom/aom_codec.h
@@ -52,7 +52,7 @@ // configuration is changed based on the parameter. Note that a aom_codec_err_t // is returned, which indicates if the operation was successful or not. // -// Note that for the encoder, the aom_codec_alg_priv_t points to the +// Note that for the encoder, the aom_codec_alg_priv_t points to // the aom_codec_alg_priv structure in av1/av1_cx_iface.c, and for the decoder, // the struct in av1/av1_dx_iface.c. Variables such as AV1_COMP cpi are stored // here and also used in the core algorithm.
diff --git a/aom/aom_decoder.h b/aom/aom_decoder.h index ceef0a6..c202fe2 100644 --- a/aom/aom_decoder.h +++ b/aom/aom_decoder.h
@@ -106,6 +106,10 @@ * call is not thread safe and should be guarded with a lock if being used * in a multithreaded context. * + * On success, aom_codec_destroy() must be used to free resources allocated for + * the decoder context. If aom_codec_dec_init_ver() fails, it is not necessary + * to call aom_codec_destroy() on the decoder context. + * * \param[in] ctx Pointer to this instance's context. * \param[in] iface Pointer to the algorithm interface to use. * \param[in] cfg Configuration to use, if known. May be NULL.
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h index fa5f643..9d412af 100644 --- a/aom/aom_encoder.h +++ b/aom/aom_encoder.h
@@ -379,6 +379,9 @@ #define AOM_EFLAG_FORCE_KF (1 << 0) /*!\brief Calculate PSNR for this frame, requires g_lag_in_frames to be 0 */ #define AOM_EFLAG_CALCULATE_PSNR (1 << 1) +/*!\brief Freeze internal state, do not update reference buffers, entropy + * tables, rate control state, etc. */ +#define AOM_EFLAG_FREEZE_INTERNAL_STATE (1 << 2) /*!\brief Encoder configuration structure * @@ -887,11 +890,14 @@ */ int tile_heights[MAX_TILE_HEIGHTS]; - /*!\brief Whether encoder should use fixed QP offsets. + /*!\brief Controls how the encoder applies fixed QP offsets * + * If a value of 0 is provided, encoder will adaptively choose QP offsets for + * frames at different levels of the pyramid. * If a value of 1 is provided, encoder will use fixed QP offsets for frames * at different levels of the pyramid. - * If a value of 0 is provided, encoder will NOT use fixed QP offsets. + * If a value of 2 is provided, encoder will use the same QP for all frames + * at different levels of the pyramid (i.e. no QP offsets are applied). * Note: This option is only relevant for --end-usage=q. */ unsigned int use_fixed_qp_offsets; @@ -919,8 +925,9 @@ * is not thread safe and should be guarded with a lock if being used * in a multithreaded context. * - * If aom_codec_enc_init_ver() fails, it is not necessary to call - * aom_codec_destroy() on the encoder context. + * On success, aom_codec_destroy() must be used to free resources allocated for + * the encoder context. If aom_codec_enc_init_ver() fails, it is not necessary + * to call aom_codec_destroy() on the encoder context. * * \param[in] ctx Pointer to this instance's context. * \param[in] iface Pointer to the algorithm interface to use.
diff --git a/aom/aom_ext_ratectrl.h b/aom/aom_ext_ratectrl.h new file mode 100644 index 0000000..88598a3 --- /dev/null +++ b/aom/aom_ext_ratectrl.h
@@ -0,0 +1,663 @@ +/* + * Copyright (c) 2025, 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. + */ + +/*!\file + * \brief Defines structs and callbacks needed for external rate control. + * + */ +#ifndef AOM_AOM_AOM_EXT_RATECTRL_H_ +#define AOM_AOM_AOM_EXT_RATECTRL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "./aom_integer.h" +#include "./aom_tpl.h" + +/*!\brief Current ABI version number + * + * \internal + * If this file is altered in any way that changes the ABI, this value + * must be bumped. Examples include, but are not limited to, changing + * types, removing or reassigning enums, adding/removing/rearranging + * fields to structures. + */ +#define AOM_EXT_RATECTRL_ABI_VERSION (2 + AOM_TPL_ABI_VERSION) + +/*!\brief Corresponds to MAX_STATIC_GF_GROUP_LENGTH defined in ratectrl.h + */ +#define AOM_RC_MAX_STATIC_GF_GROUP_LENGTH 250 + +/*!\brief Max number of ref frames returned by the external RC. + * + * Corresponds to REF_FRAMES defined in enums.h. + */ +#define AOM_RC_MAX_REF_FRAMES 8 + +/*!\brief Max layer depth. + * + * Corresponds to MAX_ARF_LAYERS defined in ratectrl.h. + */ +#define AOM_RC_MAX_ARF_LAYERS 6 + +/*!\brief The type of the external rate control. + * + * This controls what encoder parameters are determined by the external rate + * control. + */ +typedef enum aom_rc_type { + /*! + * The external rate control doesn't determine anything. + * This mode is used as baseline. + */ + AOM_RC_NONE = 0, + /*! + * The external rate control model determines the quantization parameter (QP) + * for each frame. + */ + AOM_RC_QP = 1 << 0, + /*! + * The external rate control model determines the group of picture (GOP) of + * the video sequence. + */ + AOM_RC_GOP = 1 << 1, + /*! + * The external rate control model determines the rate-distortion multiplier + * (rdmult) for the current frame. + */ + AOM_RC_RDMULT = 1 << 2, + /*! + * The external rate control model determines both QP and GOP. + */ + AOM_RC_GOP_QP = AOM_RC_QP | AOM_RC_GOP, + /*! + * The external rate control model determines the QP, GOP and the rdmult. + */ + AOM_RC_GOP_QP_RDMULT = AOM_RC_QP | AOM_RC_GOP | AOM_RC_RDMULT +} aom_rc_type_t; + +/*!\brief The rate control mode for the external rate control model. + */ +typedef enum aom_ext_rc_mode { + AOM_RC_QMODE = 0, + AOM_RC_VBR = 1, + AOM_RC_CQ = 2, +} aom_ext_rc_mode_t; + +/*!\brief Corresponds to FRAME_UPDATE_TYPE defined in ratectrl.h. + */ +typedef enum aom_rc_frame_update_type { + AOM_RC_INVALID_UPDATE_TYPE = -1, + AOM_RC_KF_UPDATE = 0, + AOM_RC_LF_UPDATE = 1, + AOM_RC_GF_UPDATE = 2, + AOM_RC_ARF_UPDATE = 3, + AOM_RC_OVERLAY_UPDATE = 4, + AOM_RC_INTNL_OVERLAY_UPDATE = 5, // Internal Overlay Frame + AOM_RC_INTNL_ARF_UPDATE = 6, // Internal Altref Frame +} aom_rc_frame_update_type_t; + +/*!\brief Name for the ref frames returned by the external RC. + * + * Corresponds to the ref frames defined in enums.h. + */ +typedef enum aom_rc_ref_name { + AOM_RC_INVALID_REF_FRAME = -1, + AOM_RC_INTRA_FRAME, + AOM_RC_LAST_FRAME, + AOM_RC_LAST2_FRAME, + AOM_RC_LAST3_FRAME, + AOM_RC_GOLDEN_FRAME, + AOM_RC_BWDREF_FRAME, + AOM_RC_ALTREF2_FRAME, + AOM_RC_ALTREF_FRAME, +} aom_rc_ref_name_t; + +/*!\brief Abstract rate control model handler + * + * The encoder will receive the model handler from + * aom_rc_funcs_t::create_model(). + */ +typedef void *aom_rc_model_t; + +/*!\brief A reserved value for the q index. + * If the external rate control model returns this value, + * the encoder will use the default q selected by libaom's rate control + * system. + */ +#define AOM_DEFAULT_Q -1 + +/*!\brief A reserved value for the rdmult. + * If the external rate control model returns this value, + * the encoder will use the default rdmult selected by libaom's rate control + * system. + */ +#define AOM_DEFAULT_RDMULT -1 + +/*!\brief Superblock quantization parameters + * Store the superblock quantiztaion parameters + */ +typedef struct aom_sb_parameters { + int q_index; /**< Quantizer step index [0..255]*/ + int rdmult; /**< Superblock level Lagrangian multiplier*/ +} aom_sb_params; + +/*!\brief Encode frame decision made by the external rate control model + * + * The encoder will receive the decision from the external rate control model + * through aom_rc_funcs_t::get_encodeframe_decision(). + */ +typedef struct aom_rc_encodeframe_decision { + int q_index; /**< Required: Quantizer step index [0..255]*/ + int rdmult; /**< Required: Frame level Lagrangian multiplier*/ + // Whether per-superblock delta Q should be used. + // The rate control model should set the value pointed to by this member + // to 1 if per-superblock delta-Q is used for this frame, or 0 otherwise. + // This is a pointer to the flag in cpi->ext_ratectrl. + int *use_delta_q; + /*! + * Optional: Superblock quantization parameters + * It is zero initialized by default. It will be set for key and ARF frames + * but not leaf frames. + */ + aom_sb_params *sb_params_list; +} aom_rc_encodeframe_decision_t; + +/*!\brief Information for the frame to be encoded. + * + * The encoder will send the information to external rate control model through + * aom_rc_funcs_t::get_encodeframe_decision(). + * + */ +typedef struct aom_rc_encodeframe_info { + /*! + * 0: Key frame + * 1: Inter frame + * 2: Alternate reference frame + * 3: Overlay frame + * 4: Golden frame + */ + int frame_type; + int show_index; /**< display index, starts from zero*/ + int coding_index; /**< coding index, starts from zero*/ + /*! + * index of the current frame in this group of picture, starts from zero. + */ + int gop_index; + int ref_frame_coding_indexes[AOM_RC_MAX_REF_FRAMES]; /**< reference frames' + coding indices*/ + /*! + * The validity of the three reference frames. + * 0: Invalid + * 1: Valid + */ + int ref_frame_valid_list[AOM_RC_MAX_REF_FRAMES]; + /*! + * The length of the current GOP. + */ + int gop_size; + /*! + * Whether the current GOP uses an alt ref. + */ + int use_alt_ref; +} aom_rc_encodeframe_info_t; + +/*!\brief Frame coding result + * + * The encoder will send the result to the external rate control model through + * aom_rc_funcs_t::update_encodeframe_result(). + */ +typedef struct aom_rc_encodeframe_result { + int64_t bit_count; /**< number of bits spent on coding the frame*/ + int actual_encoding_qindex; /**< the actual qindex used to encode the frame*/ +} aom_rc_encodeframe_result_t; + +/*!\brief Status returned by rate control callback functions. + */ +typedef enum aom_rc_status { + AOM_RC_OK = 0, + AOM_RC_ERROR = 1, +} aom_rc_status_t; + +/*!\brief First pass frame stats + * This is a mirror of FIRSTPASS_STATS in firstpass.h + */ +typedef struct aom_rc_frame_stats { + /*! + * Frame number in display order, if stats are for a single frame. + * No real meaning for a collection of frames. + */ + double frame; + /*! + * Weight assigned to this frame (or total weight for the collection of + * frames) currently based on intra factor and brightness factor. This is used + * to distribute bits betweeen easier and harder frames. + */ + double weight; + /*! + * Intra prediction error. + */ + double intra_error; + /*! + * Average wavelet energy computed using Discrete Wavelet Transform (DWT). + */ + double frame_avg_wavelet_energy; + /*! + * Best of intra pred error and inter pred error using last frame as ref. + */ + double coded_error; + /*! + * Best of intra pred error and inter pred error using golden frame as ref. + */ + double sr_coded_error; + /*! + * Percentage of blocks with inter pred error < intra pred error. + */ + double pcnt_inter; + /*! + * Percentage of blocks using (inter prediction and) non-zero motion vectors. + */ + double pcnt_motion; + /*! + * Percentage of blocks where golden frame was better than last or intra: + * inter pred error using golden frame < inter pred error using last frame and + * inter pred error using golden frame < intra pred error + */ + double pcnt_second_ref; + /*! + * Percentage of blocks where intra and inter prediction errors were very + * close. Note that this is a 'weighted count', that is, the so blocks may be + * weighted by how close the two errors were. + */ + double pcnt_neutral; + /*! + * Percentage of blocks that have almost no intra error residual + * (i.e. are in effect completely flat and untextured in the intra + * domain). In natural videos this is uncommon, but it is much more + * common in animations, graphics and screen content, so may be used + * as a signal to detect these types of content. + */ + double intra_skip_pct; + /*! + * Image mask rows top and bottom. + */ + double inactive_zone_rows; + /*! + * Image mask columns at left and right edges. + */ + double inactive_zone_cols; + /*! + * Average of row motion vectors. + */ + double MVr; + /*! + * Mean of absolute value of row motion vectors. + */ + double mvr_abs; + /*! + * Mean of column motion vectors. + */ + double MVc; + /*! + * Mean of absolute value of column motion vectors. + */ + double mvc_abs; + /*! + * Variance of row motion vectors. + */ + double MVrv; + /*! + * Variance of column motion vectors. + */ + double MVcv; + /*! + * Value in range [-1,1] indicating fraction of row and column motion vectors + * that point inwards (negative MV value) or outwards (positive MV value). + * For example, value of 1 indicates, all row/column MVs are inwards. + */ + double mv_in_out_count; + /*! + * Count of unique non-zero motion vectors. + */ + double new_mv_count; + /*! + * Duration of the frame / collection of frames. + */ + double duration; + /*! + * 1.0 if stats are for a single frame, OR + * Number of frames in this collection for which the stats are accumulated. + */ + double count; + /*! + * standard deviation for (0, 0) motion prediction error + */ + double raw_error_stdev; + /*! + * Whether the frame contains a flash + */ + int64_t is_flash; + /*! + * Estimated noise variance + */ + double noise_var; + /*! + * Correlation coefficient with the previous frame + */ + double cor_coeff; + /*! + * log of intra_error + */ + double log_intra_error; + /*! + * log of coded_error + */ + double log_coded_error; +} aom_rc_frame_stats_t; + +/*!\brief Collection of first pass frame stats + */ +typedef struct aom_rc_firstpass_stats { + /*! + * Pointer to first pass frame stats. + * The pointed array of aom_rc_frame_stats_t should have length equal to + * number of show frames in the video. + */ + aom_rc_frame_stats_t *frame_stats; + /*! + * Number of show frames in the video. + */ + int num_frames; +} aom_rc_firstpass_stats_t; + +/*!\brief Encode config sent to external rate control model + */ +typedef struct aom_rc_config { + int frame_width; /**< frame width */ + int frame_height; /**< frame height */ + int show_frame_count; /**< number of visible frames in the video */ + int max_gf_interval; /**< max GOP size in number of show frames */ + int min_gf_interval; /**< min GOP size in number of show frames */ + /*! + * Target bitrate in kilobytes per second + */ + int target_bitrate_kbps; + int frame_rate_num; /**< numerator of frame rate */ + int frame_rate_den; /**< denominator of frame rate */ + /*! + * The following fields are only for external rate control models that support + * different rate control modes. + */ + aom_ext_rc_mode_t rc_mode; /**< Q mode or VBR mode */ + int overshoot_percent; /**< for VBR mode only */ + int undershoot_percent; /**< for VBR mode only */ + int min_base_q_index; /**< for VBR mode only */ + int max_base_q_index; /**< for VBR mode only */ + int base_qp; /**< base QP for leaf frames, 0-255 */ + int superblock_size; /**< 64 or 128 */ +} aom_rc_config_t; + +/*!\brief Control what ref frame to use and its index. + */ +typedef struct aom_rc_ref_frame { + /*! + * Ref frame index. + */ + int index[AOM_RC_MAX_REF_FRAMES]; + /*! + * Ref frame name. + */ + aom_rc_ref_name_t name[AOM_RC_MAX_REF_FRAMES]; +} aom_rc_ref_frame_t; + +/*!\brief A single reference frame. + */ +typedef struct aom_rc_single_ref_frame { + int index; /**< Ref frame index. */ + aom_rc_ref_name_t name; /**< Ref frame name. */ +} aom_rc_single_ref_frame_t; + +/*!\brief A single frame in the GOP. + */ +typedef struct aom_rc_gop_frame { + // Basic info + int order_idx; /**< Index in display order within a GOP, starting at 0. */ + int coding_idx; /**< Index in coding order within a GOP, starting at 0. */ + int display_idx; /**< The number of displayed frames preceding this frame in a + GOP. */ + int order_hint; /**< Index in display order since a key frame. */ + int global_order_idx; /**< Index in display order in the whole video chunk. */ + int global_coding_idx; /**< Index in coding order in the whole video chunk. */ + int is_key_frame; /**< Whether this frame is a key frame. */ + aom_rc_frame_update_type_t update_type; /**< The type of frame. */ + + // Reference frame info + /** + * For an overlay or show_existing frame: index of primary reference frame; + * otherwise -1. + */ + int colocated_ref_idx; + /** + * The reference index that this frame should be updated to. -1 when this + * frame will not serve as a reference frame. + */ + int update_ref_idx; + /** + * Additional bits to set in refresh_frame_flags, to allow extra frames to be + * evicted from the DPB. -1 for not set. + */ + int extra_refresh_frame_flags; + /** + * Candidate reference frames which may be used for coding the current frame. + */ + aom_rc_ref_frame_t ref_frame_list; + int layer_depth; /**< Layer depth in the GOP structure. */ + /** + * Primary reference frame (used to update current frame's initial probability + * model). + */ + aom_rc_single_ref_frame_t primary_ref_frame; +} aom_rc_gop_frame_t; + +/*!\brief The decision made by the external rate control model to set the + * group of picture. This struct represents a list of GOPs. + */ +typedef struct aom_rc_gop_decision { + int show_frame_count; /**< Number of visible frames in this GOP. */ + int order_hint_offset; /**< Order hint offset for this GOP. */ + int global_order_idx_offset; /**< Global order index offset for this GOP. */ + int global_coding_idx_offset; /**< Global coding index offset for this GOP. */ + int is_scene_cut; /**< Whether this GOP starts with a scene cut. */ + /** + * The adjustment ratio, based on which the base QP index of this GOP will be + * adjusted from RateControlParam::base_q_index. + */ + double base_q_ratio; + aom_rc_gop_frame_t *gop_frame_list; /**< List of frames in this GOP. */ + int gop_frame_count; +} aom_rc_gop_decision_t; + +/*!\brief The decision made by the external rate control model to set the + * key frame location and the show frame count in the key frame group + */ +typedef struct aom_rc_key_frame_decision { + int key_frame_show_index; /**< This key frame's show index in the video */ + int key_frame_group_size; /**< Show frame count of this key frame group */ +} aom_rc_key_frame_decision_t; + +/*!\brief Create an external rate control model callback prototype + * + * This callback is invoked by the encoder to create an external rate control + * model. + * + * \param[in] priv Callback's private data + * \param[in] ratectrl_config Pointer to aom_rc_config_t + * \param[out] rate_ctrl_model_ptr Pointer to aom_rc_model_t + */ +typedef aom_rc_status_t (*aom_rc_create_model_cb_fn_t)( + void *priv, const aom_rc_config_t *ratectrl_config, + aom_rc_model_t *rate_ctrl_model_ptr); + +/*!\brief Send first pass stats to the external rate control model callback + * prototype + * + * This callback is invoked by the encoder to send first pass stats to the + * external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[in] first_pass_stats first pass stats + */ +typedef aom_rc_status_t (*aom_rc_send_firstpass_stats_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, + const aom_rc_firstpass_stats_t *first_pass_stats); + +/*!\brief Send TPL stats for the current GOP to the external rate control model + * callback prototype + * + * This callback is invoked by the encoder to send TPL stats for the GOP to the + * external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[in] tpl_gop_stats TPL stats for current GOP + */ +typedef aom_rc_status_t (*aom_rc_send_tpl_gop_stats_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, const AomTplGopStats *extrc_tpl_gop_stats); + +/*!\brief Receive encode frame decision callback prototype + * + * This callback is invoked by the encoder to receive encode frame decision from + * the external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[in] frame_gop_index index of the frame in current gop + * \param[out] frame_decision encode decision of the coding frame + */ +typedef aom_rc_status_t (*aom_rc_get_encodeframe_decision_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, const int frame_gop_index, + aom_rc_encodeframe_decision_t *frame_decision); + +/*!\brief Update encode frame result callback prototype + * + * This callback is invoked by the encoder to update encode frame result to the + * external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[out] encode_frame_result encode result of the coding frame + */ +typedef aom_rc_status_t (*aom_rc_update_encodeframe_result_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, + const aom_rc_encodeframe_result_t *encode_frame_result); + +/*!\brief Get the key frame decision from the external rate control model. + * + * This callback is invoked by the encoder to get key frame decision from + * the external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[out] key_frame_decision key frame decision from the model + */ +typedef aom_rc_status_t (*aom_rc_get_key_frame_decision_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, + aom_rc_key_frame_decision_t *key_frame_decision); + +/*!\brief Get the GOP structure from the external rate control model. + * + * This callback is invoked by the encoder to get GOP decisions from + * the external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[out] gop_decision GOP decision from the model + */ +typedef aom_rc_status_t (*aom_rc_get_gop_decision_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, aom_rc_gop_decision_t *gop_decision); + +/*!\brief Get the frame rdmult from the external rate control model. + * + * This callback is invoked by the encoder to get rdmult from + * the external rate control model. + * + * \param[in] rate_ctrl_model rate control model + * \param[in] frame_info information collected from the encoder + * \param[out] rdmult frame rate-distortion multiplier from the model + */ +typedef aom_rc_status_t (*aom_rc_get_frame_rdmult_cb_fn_t)( + aom_rc_model_t rate_ctrl_model, const aom_rc_encodeframe_info_t *frame_info, + int *rdmult); + +/*!\brief Delete the external rate control model callback prototype + * + * This callback is invoked by the encoder to delete the external rate control + * model. + * + * \param[in] rate_ctrl_model rate control model + */ +typedef aom_rc_status_t (*aom_rc_delete_model_cb_fn_t)( + aom_rc_model_t rate_ctrl_model); + +/*!\brief Callback function set for external rate control. + * + * The user can enable external rate control by registering + * a set of callback functions with the codec control flag + * #AOME_SET_EXTERNAL_RATE_CONTROL. + */ +typedef struct aom_rc_funcs { + /*! + * The rate control type of this API. + */ + aom_rc_type_t rc_type; + /*! + * Create an external rate control model. + */ + aom_rc_create_model_cb_fn_t create_model; + /*! + * Send first pass stats to the external rate control model. + */ + aom_rc_send_firstpass_stats_cb_fn_t send_firstpass_stats; + /*! + * Send TPL stats for current GOP to the external rate control model. + */ + aom_rc_send_tpl_gop_stats_cb_fn_t send_tpl_gop_stats; + /*! + * Get encodeframe decision from the external rate control model. + */ + aom_rc_get_encodeframe_decision_cb_fn_t get_encodeframe_decision; + /*! + * Update encodeframe result to the external rate control model. + */ + aom_rc_update_encodeframe_result_cb_fn_t update_encodeframe_result; + /*! + * Get key frame decision from the external rate control model. + */ + aom_rc_get_key_frame_decision_cb_fn_t get_key_frame_decision; + /*! + * Get GOP decisions from the external rate control model. + */ + aom_rc_get_gop_decision_cb_fn_t get_gop_decision; + /*! + * Get rdmult for the frame from the external rate control model. + */ + aom_rc_get_frame_rdmult_cb_fn_t get_frame_rdmult; + /*! + * Delete the external rate control model. + */ + aom_rc_delete_model_cb_fn_t delete_model; + /*! + * Private data for the external rate control model. + */ + void *priv; +} aom_rc_funcs_t; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // AOM_AOM_AOM_EXT_RATECTRL_H_
diff --git a/aom/aom_image.h b/aom/aom_image.h index 248b5b6..67af54a 100644 --- a/aom/aom_image.h +++ b/aom/aom_image.h
@@ -43,8 +43,17 @@ AOM_IMG_FMT_YV12 = AOM_IMG_FMT_PLANAR | AOM_IMG_FMT_UV_FLIP | 1, /**< planar YVU */ AOM_IMG_FMT_I420 = AOM_IMG_FMT_PLANAR | 2, - AOM_IMG_FMT_AOMYV12 = AOM_IMG_FMT_PLANAR | AOM_IMG_FMT_UV_FLIP | - 3, /** < planar 4:2:0 format with aom color space */ + /** Planar 4:2:0 format with aom color space + * + * \deprecated This value is unsupported and will be removed in a future + * release. + */ + AOM_IMG_FMT_AOMYV12 = AOM_IMG_FMT_PLANAR | AOM_IMG_FMT_UV_FLIP | 3, + /** Planar 4:2:0 format with aom color space + * + * \deprecated This value is unsupported and will be removed in a future + * release. + */ AOM_IMG_FMT_AOMI420 = AOM_IMG_FMT_PLANAR | 4, AOM_IMG_FMT_I422 = AOM_IMG_FMT_PLANAR | 5, AOM_IMG_FMT_I444 = AOM_IMG_FMT_PLANAR | 6, @@ -123,10 +132,14 @@ AOM_CICP_MC_SMPTE_2085 = 11, /**< SMPTE ST 2085 YDzDx */ AOM_CICP_MC_CHROMAT_NCL = 12, /**< Chromaticity-derived non-constant luminance */ - AOM_CICP_MC_CHROMAT_CL = 13, /**< Chromaticity-derived constant luminance */ - AOM_CICP_MC_ICTCP = 14, /**< BT.2100 ICtCp */ - AOM_CICP_MC_RESERVED_15 = 15 /**< For future use (values 15-255) */ -} aom_matrix_coefficients_t; /**< alias for enum aom_matrix_coefficients */ + AOM_CICP_MC_CHROMAT_CL = 13, /**< Chromaticity-derived constant luminance */ + AOM_CICP_MC_ICTCP = 14, /**< BT.2100 ICtCp */ + AOM_CICP_MC_RESERVED_15 = 15, /**< For backward compatibility */ + AOM_CICP_MC_IPT_C2 = 15, /**< IPT-C2 */ + AOM_CICP_MC_YCGCO_RE = 16, /**< YCgCo-Re */ + AOM_CICP_MC_YCGCO_RO = 17, /**< YCgCo-Ro */ + /**< For future use (values 18-255) */ +} aom_matrix_coefficients_t; /**< alias for enum aom_matrix_coefficients */ /*!\brief List of supported color range */ typedef enum aom_color_range { @@ -154,17 +167,33 @@ * * While encoding, when metadata is added to an aom_image via * aom_img_add_metadata(), the flag passed along with the metadata will - * determine where the metadata OBU will be placed in the encoded OBU stream. - * Metadata will be emitted into the output stream within the next temporal unit - * if it satisfies the specified insertion flag. + * determine where the metadata OBU will be placed in the encoded OBU stream, + * and whether it's layer-specific. Metadata will be emitted into the output + * stream within the next temporal unit if it satisfies the specified insertion + * flag. * - * During decoding, when the library encounters a metadata OBU, it is always - * flagged as AOM_MIF_ANY_FRAME and emitted with the next output aom_image. + * If the video contains multiple spatial and/or temporal layers, + * a layer-specific metadata OBU only applies to the current frame's layer, as + * determined by the frame's temporal_id and spatial_id. Some metadata types + * cannot be layer-specific, as listed in Section 6.7.1 of the draft AV1 + * specification as of 2025-03-06. + * + * During decoding, when the library encounters a metadata OBU, it is emitted + * with the next output aom_image. Its insert_flag is set to either + * AOM_MIF_ANY_FRAME, or AOM_MIF_ANY_FRAME_LAYER_SPECIFIC if the OBU contains an + * OBU header extension (i.e. the video contains multiple layers AND the + * metadata was added using *_LAYER_SPECIFIC insert flag if using libaom). */ typedef enum aom_metadata_insert_flags { - AOM_MIF_NON_KEY_FRAME = 0, /**< Adds metadata if it's not keyframe */ + AOM_MIF_NON_KEY_FRAME = 0, /**< Adds metadata if it's not a keyframe */ AOM_MIF_KEY_FRAME = 1, /**< Adds metadata only if it's a keyframe */ - AOM_MIF_ANY_FRAME = 2 /**< Adds metadata to any type of frame */ + AOM_MIF_ANY_FRAME = 2, /**< Adds metadata to any type of frame */ + /** Adds layer-specific metadata if it's not a keyframe */ + AOM_MIF_NON_KEY_FRAME_LAYER_SPECIFIC = 16, + /** Adds layer-specific metadata only if it's a keyframe */ + AOM_MIF_KEY_FRAME_LAYER_SPECIFIC = 17, + /** Adds layer-specific metadata to any type of frame */ + AOM_MIF_ANY_FRAME_LAYER_SPECIFIC = 18, } aom_metadata_insert_flags_t; /*!\brief Array of aom_metadata structs for an image. */ @@ -213,8 +242,13 @@ /* planes[AOM_PLANE_V] = NULL and stride[AOM_PLANE_V] = 0 when fmt == * AOM_IMG_FMT_NV12 */ unsigned char *planes[3]; /**< pointer to the top left pixel for each plane */ - int stride[3]; /**< stride between rows for each plane */ - size_t sz; /**< data size */ + /*!Stride between rows for each plane + * + * \note With planar formats, \c stride[AOM_PLANE_U] must be the same as \c + * stride[AOM_PLANE_V]. + */ + int stride[3]; + size_t sz; /**< data size */ int bps; /**< bits per sample (for packed formats) */ @@ -268,26 +302,55 @@ * storage for the image has been allocated elsewhere, and a descriptor is * desired to "wrap" that storage. * - * \param[in] img Pointer to storage for descriptor. If this parameter - * is NULL, the storage for the descriptor will be - * allocated on the heap. - * \param[in] fmt Format for the image - * \param[in] d_w Width of the image. Must not exceed 0x08000000 - * (2^27). - * \param[in] d_h Height of the image. Must not exceed 0x08000000 - * (2^27). - * \param[in] align Alignment, in bytes, of each row in the image - * (stride). Must not exceed 65536. - * \param[in] img_data Storage to use for the image. The storage must - * outlive the returned image descriptor; it can be - * disposed of after calling aom_img_free(). + * \param[in] img Pointer to storage for descriptor. If this + * parameter is NULL, the storage for the descriptor + * will be allocated on the heap. + * \param[in] fmt Format for the image + * \param[in] d_w Width of the image. Must not exceed 0x08000000 + * (2^27). + * \param[in] d_h Height of the image. Must not exceed 0x08000000 + * (2^27). + * \param[in] stride_align Alignment, in bytes, of each row in the image + * (stride). Must not exceed 65536. + * \param[in] img_data Storage to use for the image. The storage must + * outlive the returned image descriptor; it can be + * disposed of after calling aom_img_free(). * * \return Returns a pointer to the initialized image descriptor. If the img * parameter is non-null, the value of the img parameter will be * returned. + * + * \note \a img_data is required to have a minimum allocation size that + * satisfies the requirements of the \a fmt, \a d_w, \a d_h and \a + * stride_align parameters. This size can be calculated as follows (see + * \c img_alloc_helper in the aom_image.c file in the libaom source tree + * for more detail): + * \code + * align = (1 << x_chroma_shift) - 1; + * w = (d_w + align) & ~align; + * align = (1 << y_chroma_shift) - 1; + * h = (d_h + align) & ~align; + * + * s = (fmt & AOM_IMG_FMT_PLANAR) ? w : (uint64_t)bps * w / 8; + * s = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s; + * s = (s + stride_align - 1) & ~((uint64_t)stride_align - 1); + * s = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? s / 2 : s; + * alloc_size = (fmt & AOM_IMG_FMT_PLANAR) ? (uint64_t)h * s * bps / 8 + * : (uint64_t)h * s; + * \endcode + * \a x_chroma_shift, \a y_chroma_shift and \a bps can be obtained by calling + * \ref aom_img_wrap with a non-\c NULL \a img_data parameter. The \c + * aom_image_t pointer should \em not be used in other API calls until \em after + * a successful call to \ref aom_img_wrap with a valid image buffer. For + * example: + * \code + * aom_img_wrap(img, fmt, d_w, d_h, stride_align, (unsigned char *)1); + * ... calculate buffer size and allocate buffer as described earlier + * aom_img_wrap(img, fmt, d_w, d_h, stride_align, img_data); + * \endcode */ aom_image_t *aom_img_wrap(aom_image_t *img, aom_img_fmt_t fmt, unsigned int d_w, - unsigned int d_h, unsigned int align, + unsigned int d_h, unsigned int stride_align, unsigned char *img_data); /*!\brief Open a descriptor, allocating storage for the underlying image with a
diff --git a/aom/aom_tpl.h b/aom/aom_tpl.h new file mode 100644 index 0000000..60d879e --- /dev/null +++ b/aom/aom_tpl.h
@@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025, 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. + */ + +#ifndef AOM_AOM_AOM_TPL_H_ +#define AOM_AOM_AOM_TPL_H_ + +#include "./aom_integer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*!\brief Current ABI version number + * + * \internal + * If this file is altered in any way that changes the ABI, this value + * must be bumped. Examples include, but are not limited to, changing + * types, removing or reassigning enums, adding/removing/rearranging + * fields to structures + */ +#define AOM_TPL_ABI_VERSION 1 /**<\hideinitializer*/ + +#define AOM_RC_INTER_REFS_PER_FRAME 7 + +// The motion vector in units of full pixel +typedef struct aom_fullpel_mv { + int16_t row; + int16_t col; +} AOM_FULLPEL_MV; + +// The motion vector in units of 1/8-pel +typedef struct aom_mv { + int16_t row; + int16_t col; +} AOM_MV; + +typedef union aom_int_mv { + uint32_t as_int; + AOM_MV as_mv; + AOM_FULLPEL_MV as_fullmv; +} aom_int_mv; /* facilitates faster equality tests and copies */ + +/*!\brief Temporal dependency model stats for each block before propagation */ +typedef struct AomTplBlockStats { + int16_t row; /**< Pixel row of the top left corner */ + int16_t col; /**< Pixel col of the top left corner */ + int64_t srcrf_sse; + int64_t srcrf_dist; + int64_t recrf_sse; + int64_t recrf_dist; + int64_t intra_sse; + int64_t intra_dist; + int64_t cmp_recrf_dist[2]; + int64_t mc_dep_rate; + int64_t mc_dep_dist; + int64_t pred_error[AOM_RC_INTER_REFS_PER_FRAME]; + int32_t intra_cost; + int32_t inter_cost; + int32_t srcrf_rate; + int32_t recrf_rate; + int32_t intra_rate; + int32_t cmp_recrf_rate[2]; + aom_int_mv mv[AOM_RC_INTER_REFS_PER_FRAME]; + int8_t ref_frame_index[2]; +} AomTplBlockStats; + +/*!\brief Temporal dependency model stats for each frame before propagation */ +typedef struct AomTplFrameStats { + int frame_width; /**< Frame width */ + int frame_height; /**< Frame height */ + int num_blocks; /**< Number of blocks. Size of block_stats_list */ + AomTplBlockStats *block_stats_list; /**< List of tpl stats for each block */ +} AomTplFrameStats; + +/*!\brief Temporal dependency model stats for each GOP before propagation */ +typedef struct AomTplGopStats { + int size; /**< GOP size, also the size of frame_stats_list. */ + AomTplFrameStats *frame_stats_list; /**< List of tpl stats for each frame */ +} AomTplGopStats; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // AOM_AOM_AOM_TPL_H_
diff --git a/aom/aomcx.h b/aom/aomcx.h index 3dfaa64..70c2dad 100644 --- a/aom/aomcx.h +++ b/aom/aomcx.h
@@ -18,6 +18,7 @@ */ #include "aom/aom.h" #include "aom/aom_encoder.h" +#include "aom/aom_ext_ratectrl.h" #include "aom/aom_external_partition.h" /*!\file @@ -219,7 +220,7 @@ */ AOME_SET_CPUUSED = 13, - /*!\brief Codec control function to enable automatic set and use alf frames, + /*!\brief Codec control function to enable automatic set and use arf frames, * unsigned int parameter * * - 0 = disable @@ -1595,9 +1596,10 @@ /*!\brief Codec control to set the screen content detection mode, * aom_screen_detection_mode parameter. * - * - 1: AOM_SCREEN_DETECTION_STANDARD = standard (default) + * - 1: AOM_SCREEN_DETECTION_STANDARD = standard (default in good quality and + realtime modes) * - 2: AOM_SCREEN_DETECTION_ANTIALIASING_AWARE = anti-aliased text and - * graphics aware + * graphics aware (default in all intra mode) */ AV1E_SET_SCREEN_CONTENT_DETECTION_MODE = 171, @@ -1617,6 +1619,18 @@ */ AV1E_SET_ENABLE_ADAPTIVE_SHARPNESS = 172, + /*!\brief Codec control function to enable external rate control library. + * + * args: a pointer to aom_rc_funcs_t that contains implementation of callbacks + */ + AV1E_SET_EXTERNAL_RATE_CONTROL = 173, + + /*!\brief Codec control function to get GOP structure from the encoder. + * + * args: a pointer to aom_gop_info_t + */ + AV1E_GET_GOP_INFO, + // Any new encoder control IDs should be added above. // Maximum allowed encoder control ID is 229. // No encoder control ID should be added below. @@ -1718,10 +1732,10 @@ * Changes the encoder to tune for certain types of input material. * * \note - * AOM_TUNE_IQ and AOM_TUNE_SSIMULACRA2 are restricted to all intra mode - * (AOM_USAGE_ALL_INTRA). Setting the tuning option to either AOM_TUNE_IQ or - * AOM_TUNE_SSIMULACRA2 causes the following options to be set (expressed as - * command-line options): + * AOM_TUNE_IQ and AOM_TUNE_SSIMULACRA2 are meant for image encoding. Using + * these tuning modes for videos isn't recommended. + * Setting the tuning option to either AOM_TUNE_IQ or AOM_TUNE_SSIMULACRA2 + * causes the following options to be set (expressed as command-line options): * * --enable-qm=1 * * --qm-min=2 * * --qm-max=10 @@ -1746,16 +1760,30 @@ AOM_TUNE_VMAF_SALIENCY_MAP = 9, /*!\brief Allows detection of the presence of AOM_TUNE_IQ at compile time. */ #define AOM_HAVE_TUNE_IQ 1 - /* Image quality (or intra quality). Increases image quality and consistency, + /* "Image Quality" tuning mode. Increases image quality and consistency, * guided by the SSIMULACRA 2 metric and subjective quality checks. Shares * the rdmult code with AOM_TUNE_SSIM. + * Note: AOM_TUNE_IQ is only meant to be used to encode a still image or a + * layered AVIF image. */ AOM_TUNE_IQ = 10, /*!\brief Allows detection of the presence of AOM_TUNE_SSIMULACRA2 at compile * time. */ #define AOM_HAVE_TUNE_SSIMULACRA2 1 - /* Tune that optimizes for maximum SSIMULACRA 2 scores. Shares the rdmult code - with AOM_TUNE_SSIM. */ + /* A tuning mode that optimizes for maximum SSIMULACRA 2 scores. Shares the + * rdmult code with AOM_TUNE_SSIM. + * Unlike metrics like AOM_TUNE_VMAF_* or AOM_TUNE_BUTTERAUGLI, + * AOM_TUNE_SSIMULACRA2 doesn't use the SSIMULACRA 2 metric for + * rate-distortion optimization decisions. Instead, the tuning mode relies + * purely on hand-crafted heuristics. This means no additional external + * dependencies are required. + * AOM_TUNE_SSIMULACRA2 shares most of the tweaks and optimizations with + * AOM_TUNE_IQ. However, AOM_TUNE_SSIMULACRA2 fine-tunes the encoder in ways + * that have been shown to not come with a corresponding positive impact on + * subjective quality in human evaluations. + * Note: AOM_TUNE_SSIMULACRA2 is only meant to be used to encode a still + * image or a layered AVIF image. + */ AOM_TUNE_SSIMULACRA2 = 11, } aom_tune_metric; @@ -1853,6 +1881,20 @@ AOM_FULL_SUPERFRAME_DROP, /**< Only full superframe can drop. */ } AOM_SVC_FRAME_DROP_MODE; +/*!\brief The GOP structure information determined by the encoder. + * 250 is MAX_STATIC_GF_GROUP_LENGTH defined in av1/firstpass.h. + * This is a subset of GF_GROUP. More fields can be added if needed. + */ +typedef struct aom_gop_info { + int gop_size; /**< The number of frames of this GOP */ + /*! The coding index for each entry in the gop */ + int coding_index[250]; + /*! The display index for each entry in the gop */ + int display_index[250]; + /*! The layer depth for each entry in the gop */ + int layer_depth[250]; +} aom_gop_info_t; + /*!\cond */ /*!\brief Encoder control function parameter type * @@ -2360,6 +2402,12 @@ AOM_CTRL_USE_TYPE(AV1E_SET_ENABLE_ADAPTIVE_SHARPNESS, unsigned int) #define AOM_CTRL_AV1E_SET_ENABLE_ADAPTIVE_SHARPNESS +AOM_CTRL_USE_TYPE(AV1E_SET_EXTERNAL_RATE_CONTROL, aom_rc_funcs_t *) +#define AOM_CTRL_AV1E_SET_EXTERNAL_RATE_CONTROL + +AOM_CTRL_USE_TYPE(AV1E_GET_GOP_INFO, aom_gop_info_t *) +#define AOM_CTRL_AV1E_GET_GOP_INFO + /*!\endcond */ /*! @} - end defgroup aom_encoder */ #ifdef __cplusplus
diff --git a/aom/aomdx.h b/aom/aomdx.h index f18cbbf..4c32beb 100644 --- a/aom/aomdx.h +++ b/aom/aomdx.h
@@ -466,6 +466,15 @@ * be used. */ AV1D_GET_MI_INFO, + + /*!\brief Codec control function to set the maximum frame size, unsigned int + * parameter + * + * - 0 = unlimited (default) + * - nonzero = frame size (width * height) must be less than or equal to this + * maximum + */ + AOMD_SET_FRAME_SIZE_LIMIT, }; /*!\cond */ @@ -598,6 +607,9 @@ // The AOM_CTRL_USE_TYPE macro can't be used with AV1D_GET_MI_INFO because // AV1D_GET_MI_INFO takes more than one parameter. #define AOM_CTRL_AV1D_GET_MI_INFO + +AOM_CTRL_USE_TYPE(AOMD_SET_FRAME_SIZE_LIMIT, unsigned int) +#define AOM_CTRL_AOMD_SET_FRAME_SIZE_LIMIT /*!\endcond */ /*! @} - end defgroup aom_decoder */ #ifdef __cplusplus
diff --git a/aom/internal/aom_image_internal.h b/aom/internal/aom_image_internal.h index ef0f166..e11223a 100644 --- a/aom/internal/aom_image_internal.h +++ b/aom/internal/aom_image_internal.h
@@ -29,6 +29,14 @@ aom_metadata_t **metadata_array; /* Array of metadata structs */ }; +/*! \brief Bit in aom_metadata_insert_flags marking metadata as layer-specific. + */ +#define AOM_MIF_LAYER_SPECIFIC 0x10 +/*! \brief Bits in aom_metadata_insert_flags used to signal which frames to + * add the metadata to (keyframes, non keyframes...). + */ +#define AOM_MIF_INSERT_LOCATION_MASK 0x0f + /*!\brief Alloc memory for aom_metadata_array struct. * * Allocate memory for aom_metadata_array struct.
diff --git a/aom/src/aom_image.c b/aom/src/aom_image.c index 497dff3..c25143b 100644 --- a/aom/src/aom_image.c +++ b/aom/src/aom_image.c
@@ -11,6 +11,7 @@ #include <assert.h> #include <limits.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> @@ -18,6 +19,7 @@ #include "aom/aom_integer.h" #include "aom/internal/aom_image_internal.h" #include "aom_mem/aom_mem.h" +#include "aom/aom_codec.h" static inline unsigned int align_image_dimension(unsigned int d, unsigned int subsampling, @@ -29,6 +31,21 @@ return ((d + align) & ~align); } +static bool is_valid_img_fmt(aom_img_fmt_t fmt) { + switch (fmt) { + case AOM_IMG_FMT_YV12: + case AOM_IMG_FMT_I420: + case AOM_IMG_FMT_I422: + case AOM_IMG_FMT_I444: + case AOM_IMG_FMT_NV12: + case AOM_IMG_FMT_I42016: + case AOM_IMG_FMT_YV1216: + case AOM_IMG_FMT_I42216: + case AOM_IMG_FMT_I44416: return true; + default: return false; + } +} + static aom_image_t *img_alloc_helper( aom_image_t *img, aom_img_fmt_t fmt, unsigned int d_w, unsigned int d_h, unsigned int buf_align, unsigned int stride_align, unsigned int size_align, @@ -41,7 +58,7 @@ if (img != NULL) memset(img, 0, sizeof(aom_image_t)); - if (fmt == AOM_IMG_FMT_NONE) goto fail; + if (!is_valid_img_fmt(fmt)) goto fail; /* Impose maximum values on input parameters so that this function can * perform arithmetic operations without worrying about overflows. @@ -73,9 +90,7 @@ switch (fmt) { case AOM_IMG_FMT_I420: case AOM_IMG_FMT_YV12: - case AOM_IMG_FMT_NV12: - case AOM_IMG_FMT_AOMI420: - case AOM_IMG_FMT_AOMYV12: bps = 12; break; + case AOM_IMG_FMT_NV12: bps = 12; break; case AOM_IMG_FMT_I422: bps = 16; break; case AOM_IMG_FMT_I444: bps = 24; break; case AOM_IMG_FMT_YV1216: @@ -92,8 +107,6 @@ case AOM_IMG_FMT_I420: case AOM_IMG_FMT_YV12: case AOM_IMG_FMT_NV12: - case AOM_IMG_FMT_AOMI420: - case AOM_IMG_FMT_AOMYV12: case AOM_IMG_FMT_I422: case AOM_IMG_FMT_I42016: case AOM_IMG_FMT_YV1216: @@ -105,8 +118,6 @@ case AOM_IMG_FMT_I420: case AOM_IMG_FMT_YV12: case AOM_IMG_FMT_NV12: - case AOM_IMG_FMT_AOMI420: - case AOM_IMG_FMT_AOMYV12: case AOM_IMG_FMT_YV1216: case AOM_IMG_FMT_I42016: ycs = 1; break; default: ycs = 0; break; @@ -383,6 +394,15 @@ img->metadata = aom_img_metadata_array_alloc(0); if (!img->metadata) return -1; } + // Some metadata types are not allowed to be layer specific, according to + // the Table in Section 6.7.1 of the AV1 specifiction. + // Do not check for OBU_METADATA_TYPE_HDR_CLL or OBU_METADATA_TYPE_HDR_MDCV + // because there are plans to alow them to be layer specific. + if ((insert_flag & AOM_MIF_LAYER_SPECIFIC) && + (type == OBU_METADATA_TYPE_SCALABILITY || + type == OBU_METADATA_TYPE_TIMECODE)) { + return -1; + } aom_metadata_t *metadata = aom_img_metadata_alloc(type, data, sz, insert_flag); if (!metadata) return -1;
diff --git a/aom_dsp/aom_dsp.cmake b/aom_dsp/aom_dsp.cmake index 3047bc7..0738274 100644 --- a/aom_dsp/aom_dsp.cmake +++ b/aom_dsp/aom_dsp.cmake
@@ -97,15 +97,6 @@ "${AOM_ROOT}/aom_dsp/x86/blend_a64_mask_avx2.c" "${AOM_ROOT}/aom_dsp/x86/bitdepth_conversion_avx2.h") -if(CONFIG_SVT_AV1) - list(APPEND AOM_DSP_COMMON_INTRIN_AVX2 - "${AOM_ROOT}/third_party/SVT-AV1/convolve_2d_avx2.h" - "${AOM_ROOT}/third_party/SVT-AV1/convolve_avx2.h" - "${AOM_ROOT}/third_party/SVT-AV1/EbMemory_AVX2.h" - "${AOM_ROOT}/third_party/SVT-AV1/EbMemory_SSE4_1.h" - "${AOM_ROOT}/third_party/SVT-AV1/synonyms.h") -endif() - list(APPEND AOM_DSP_COMMON_INTRIN_NEON "${AOM_ROOT}/aom_dsp/arm/aom_convolve_copy_neon.c" "${AOM_ROOT}/aom_dsp/arm/aom_convolve8_neon.c" @@ -312,6 +303,7 @@ "${AOM_ROOT}/aom_dsp/arm/sad_neon_dotprod.c" "${AOM_ROOT}/aom_dsp/arm/sadxd_neon_dotprod.c" "${AOM_ROOT}/aom_dsp/arm/sse_neon_dotprod.c" + "${AOM_ROOT}/aom_dsp/arm/subpel_variance_neon_dotprod.c" "${AOM_ROOT}/aom_dsp/arm/sum_squares_neon_dotprod.c" "${AOM_ROOT}/aom_dsp/arm/variance_neon_dotprod.c")
diff --git a/aom_dsp/aom_dsp_rtcd_defs.pl b/aom_dsp/aom_dsp_rtcd_defs.pl index 7a5a615..a3b3564 100755 --- a/aom_dsp/aom_dsp_rtcd_defs.pl +++ b/aom_dsp/aom_dsp_rtcd_defs.pl
@@ -1404,23 +1404,23 @@ specialize qw/aom_variance8x8 sse2 neon neon_dotprod/; specialize qw/aom_variance8x4 sse2 neon neon_dotprod/; specialize qw/aom_variance4x8 sse2 neon neon_dotprod/; - specialize qw/aom_variance4x4 sse2 neon neon_dotprod/; + specialize qw/aom_variance4x4 sse2 neon/; - specialize qw/aom_sub_pixel_variance128x128 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance128x64 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance64x128 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance64x64 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance64x32 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance32x64 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance32x32 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance32x16 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance16x32 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance16x16 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance16x8 avx2 neon ssse3/; - specialize qw/aom_sub_pixel_variance8x16 neon ssse3/; - specialize qw/aom_sub_pixel_variance8x8 neon ssse3/; - specialize qw/aom_sub_pixel_variance8x4 neon ssse3/; - specialize qw/aom_sub_pixel_variance4x8 neon ssse3/; + specialize qw/aom_sub_pixel_variance128x128 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance128x64 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance64x128 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance64x64 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance64x32 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance32x64 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance32x32 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance32x16 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance16x32 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance16x16 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance16x8 avx2 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance8x16 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance8x8 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance8x4 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance4x8 neon neon_dotprod ssse3/; specialize qw/aom_sub_pixel_variance4x4 neon ssse3/; specialize qw/aom_sub_pixel_avg_variance128x128 avx2 neon ssse3/; @@ -1448,12 +1448,12 @@ specialize qw/aom_variance16x64 neon neon_dotprod sse2 avx2/; specialize qw/aom_variance64x16 neon neon_dotprod sse2 avx2/; - specialize qw/aom_sub_pixel_variance4x16 neon ssse3/; - specialize qw/aom_sub_pixel_variance16x4 neon avx2 ssse3/; - specialize qw/aom_sub_pixel_variance8x32 neon ssse3/; - specialize qw/aom_sub_pixel_variance32x8 neon ssse3/; - specialize qw/aom_sub_pixel_variance16x64 neon avx2 ssse3/; - specialize qw/aom_sub_pixel_variance64x16 neon ssse3/; + specialize qw/aom_sub_pixel_variance4x16 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance16x4 neon neon_dotprod avx2 ssse3/; + specialize qw/aom_sub_pixel_variance8x32 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance32x8 neon neon_dotprod ssse3/; + specialize qw/aom_sub_pixel_variance16x64 neon neon_dotprod avx2 ssse3/; + specialize qw/aom_sub_pixel_variance64x16 neon neon_dotprod ssse3/; specialize qw/aom_sub_pixel_avg_variance4x16 neon ssse3/; specialize qw/aom_sub_pixel_avg_variance16x4 neon ssse3/; specialize qw/aom_sub_pixel_avg_variance8x32 neon ssse3/;
diff --git a/aom_dsp/arm/aom_convolve8_neon_dotprod.c b/aom_dsp/arm/aom_convolve8_neon_dotprod.c index 7fc9cb1..fd86a39 100644 --- a/aom_dsp/arm/aom_convolve8_neon_dotprod.c +++ b/aom_dsp/arm/aom_convolve8_neon_dotprod.c
@@ -25,25 +25,11 @@ #include "aom_dsp/arm/mem_neon.h" #include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" +#include "av1/common/arm/convolve_neon_dotprod.h" // Filter values always sum to 128. #define FILTER_WEIGHT 128 -DECLARE_ALIGNED(16, static const uint8_t, kDotProdPermuteTbl[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 -}; - -DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { - // Shift left and insert new last column in transposed 4x4 block. - 1, 2, 3, 16, 5, 6, 7, 20, 9, 10, 11, 24, 13, 14, 15, 28, - // Shift left and insert two new columns in transposed 4x4 block. - 2, 3, 16, 17, 6, 7, 20, 21, 10, 11, 24, 25, 14, 15, 28, 29, - // Shift left and insert three new columns in transposed 4x4 block. - 3, 16, 17, 18, 7, 20, 21, 22, 11, 24, 25, 26, 15, 28, 29, 30 -}; - static inline int16x4_t convolve8_4_h(const uint8x16_t samples, const int8x8_t filters, const uint8x16x2_t permute_tbl) { @@ -58,12 +44,13 @@ vqtbl1q_s8(samples_128, permute_tbl.val[1]) }; // Accumulate into 128 * FILTER_WEIGHT to account for range transform. - int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT); + // (Divide by 2 since we halved the filter values.) + int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT / 2); int32x4_t sum = vdotq_lane_s32(acc, perm_samples[0], filters, 0); sum = vdotq_lane_s32(sum, perm_samples[1], filters, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vmovn_s32(sum); } static inline uint8x8_t convolve8_8_h(const uint8x16_t samples, @@ -82,7 +69,8 @@ vqtbl1q_s8(samples_128, permute_tbl.val[2]) }; // Accumulate into 128 * FILTER_WEIGHT to account for range transform. - int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT); + // (Divide by 2 since we halved the filter values.) + int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT / 2); // First 4 output values. int32x4_t sum0 = vdotq_lane_s32(acc, perm_samples[0], filters, 0); sum0 = vdotq_lane_s32(sum0, perm_samples[1], filters, 1); @@ -91,14 +79,16 @@ sum1 = vdotq_lane_s32(sum1, perm_samples[2], filters, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0), vqmovn_s32(sum1)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0), vmovn_s32(sum1)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve8_horiz_8tap_neon_dotprod( const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const int16_t *filter_x, int w, int h) { - const int8x8_t filter = vmovn_s16(vld1q_s16(filter_x)); + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(filter_x), 1); if (w == 4) { const uint8x16x2_t perm_tbl = vld1q_u8_x2(kDotProdPermuteTbl); @@ -110,8 +100,9 @@ int16x4_t d1 = convolve8_4_h(s1, filter, perm_tbl); int16x4_t d2 = convolve8_4_h(s2, filter, perm_tbl); int16x4_t d3 = convolve8_4_h(s3, filter, perm_tbl); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); @@ -284,69 +275,19 @@ } } -static inline void transpose_concat_4x4(int8x8_t a0, int8x8_t a1, int8x8_t a2, - int8x8_t a3, int8x16_t *b) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, XX, XX, XX, XX - // a1: 10, 11, 12, 13, XX, XX, XX, XX - // a2: 20, 21, 22, 23, XX, XX, XX, XX - // a3: 30, 31, 32, 33, XX, XX, XX, XX - // - // b: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - - int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); - int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); - int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); - int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); - - int8x16_t a01 = vzipq_s8(a0q, a1q).val[0]; - int8x16_t a23 = vzipq_s8(a2q, a3q).val[0]; - - int16x8_t a0123 = - vzipq_s16(vreinterpretq_s16_s8(a01), vreinterpretq_s16_s8(a23)).val[0]; - - *b = vreinterpretq_s8_s16(a0123); -} - -static inline void transpose_concat_8x4(int8x8_t a0, int8x8_t a1, int8x8_t a2, - int8x8_t a3, int8x16_t *b0, - int8x16_t *b1) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, 04, 05, 06, 07 - // a1: 10, 11, 12, 13, 14, 15, 16, 17 - // a2: 20, 21, 22, 23, 24, 25, 26, 27 - // a3: 30, 31, 32, 33, 34, 35, 36, 37 - // - // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 - - int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); - int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); - int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); - int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); - - int8x16_t a01 = vzipq_s8(a0q, a1q).val[0]; - int8x16_t a23 = vzipq_s8(a2q, a3q).val[0]; - - int16x8x2_t a0123 = - vzipq_s16(vreinterpretq_s16_s8(a01), vreinterpretq_s16_s8(a23)); - - *b0 = vreinterpretq_s8_s16(a0123.val[0]); - *b1 = vreinterpretq_s8_s16(a0123.val[1]); -} - static inline int16x4_t convolve8_4_v(const int8x16_t samples_lo, const int8x16_t samples_hi, const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 * FILTER_WEIGHT to account for range transform. - int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT); + // (Divide by 2 since we halved the filter values.) + int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT / 2); int32x4_t sum = vdotq_lane_s32(acc, samples_lo, filters, 0); sum = vdotq_lane_s32(sum, samples_hi, filters, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vmovn_s32(sum); } static inline uint8x8_t convolve8_8_v(const int8x16_t samples0_lo, @@ -357,7 +298,8 @@ // The sample range transform and permutation are performed by the caller. // Accumulate into 128 * FILTER_WEIGHT to account for range transform. - int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT); + // (Divide by 2 since we halved the filter values.) + int32x4_t acc = vdupq_n_s32(128 * FILTER_WEIGHT / 2); // First 4 output values. int32x4_t sum0 = vdotq_lane_s32(acc, samples0_lo, filters, 0); sum0 = vdotq_lane_s32(sum0, samples0_hi, filters, 1); @@ -366,14 +308,16 @@ sum1 = vdotq_lane_s32(sum1, samples1_hi, filters, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0), vqmovn_s32(sum1)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0), vmovn_s32(sum1)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve8_vert_8tap_neon_dotprod( const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const int16_t *filter_y, int w, int h) { - const int8x8_t filter = vmovn_s16(vld1q_s16(filter_y)); + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(filter_y), 1); const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); int8x16x2_t samples_LUT; @@ -394,10 +338,10 @@ // This operation combines a conventional transpose and the sample permute // (see horizontal case) required before computing the dot product. int8x16_t s0123, s1234, s2345, s3456; - transpose_concat_4x4(s0, s1, s2, s3, &s0123); - transpose_concat_4x4(s1, s2, s3, s4, &s1234); - transpose_concat_4x4(s2, s3, s4, s5, &s2345); - transpose_concat_4x4(s3, s4, s5, s6, &s3456); + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_s8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_s8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_s8_4x4(s3, s4, s5, s6, &s3456); do { uint8x8_t t7, t8, t9, t10; @@ -409,7 +353,7 @@ int8x8_t s10 = vreinterpret_s8_u8(vsub_u8(t10, vdup_n_u8(128))); int8x16_t s4567, s5678, s6789, s78910; - transpose_concat_4x4(s7, s8, s9, s10, &s78910); + transpose_concat_elems_s8_4x4(s7, s8, s9, s10, &s78910); // Merge new data into block from previous iteration. samples_LUT.val[0] = s3456; @@ -422,8 +366,9 @@ int16x4_t d1 = convolve8_4_v(s1234, s5678, filter); int16x4_t d2 = convolve8_4_v(s2345, s6789, filter); int16x4_t d3 = convolve8_4_v(s3456, s78910, filter); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); @@ -462,10 +407,10 @@ // (see horizontal case) required before computing the dot product. int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, s3456_lo, s3456_hi; - transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); - transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); - transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); - transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_s8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_s8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_s8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); do { uint8x8_t t7, t8, t9, t10; @@ -478,7 +423,7 @@ int8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, s78910_lo, s78910_hi; - transpose_concat_8x4(s7, s8, s9, s10, &s78910_lo, &s78910_hi); + transpose_concat_elems_s8_8x4(s7, s8, s9, s10, &s78910_lo, &s78910_hi); // Merge new data into block from previous iteration. samples_LUT.val[0] = s3456_lo;
diff --git a/aom_dsp/arm/aom_convolve8_neon_i8mm.c b/aom_dsp/arm/aom_convolve8_neon_i8mm.c index 121e892..33694f8 100644 --- a/aom_dsp/arm/aom_convolve8_neon_i8mm.c +++ b/aom_dsp/arm/aom_convolve8_neon_i8mm.c
@@ -24,88 +24,87 @@ #include "aom_dsp/arm/mem_neon.h" #include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" - -DECLARE_ALIGNED(16, static const uint8_t, kMatMulPermuteTbl[32]) = { - // clang-format off - 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9, - 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 - // clang-format on -}; - -DECLARE_ALIGNED(16, static const uint8_t, kDotProdPermuteTbl[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 -}; - -DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { - // Shift left and insert new last column in transposed 4x4 block. - 1, 2, 3, 16, 5, 6, 7, 20, 9, 10, 11, 24, 13, 14, 15, 28, - // Shift left and insert two new columns in transposed 4x4 block. - 2, 3, 16, 17, 6, 7, 20, 21, 10, 11, 24, 25, 14, 15, 28, 29, - // Shift left and insert three new columns in transposed 4x4 block. - 3, 16, 17, 18, 7, 20, 21, 22, 11, 24, 25, 26, 15, 28, 29, 30 -}; +#include "av1/common/arm/convolve_neon_dotprod.h" +#include "av1/common/arm/convolve_neon_i8mm.h" static inline int16x4_t convolve8_4_h(const uint8x16_t samples, - const int8x8_t filters, - const uint8x16x2_t permute_tbl) { - // Permute samples ready for dot product. - // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } - // { 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 } - uint8x16_t permuted_samples[2] = { vqtbl1q_u8(samples, permute_tbl.val[0]), - vqtbl1q_u8(samples, permute_tbl.val[1]) }; + const int8x16_t filters, + const uint8x16_t permute_tbl) { + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + uint8x16_t perm_samples = vqtbl1q_u8(samples, permute_tbl); - int32x4_t sum = - vusdotq_lane_s32(vdupq_n_s32(0), permuted_samples[0], filters, 0); - sum = vusdotq_lane_s32(sum, permuted_samples[1], filters, 1); + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum = vusmmlaq_s32(vdupq_n_s32(0), perm_samples, filters); - // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + // Tap 0, as well as further narrowing and packing, is applied by the caller. + return vmovn_s32(sum); } static inline uint8x8_t convolve8_8_h(const uint8x16_t samples, - const int8x8_t filters, - const uint8x16x3_t permute_tbl) { - // Permute samples ready for dot product. - // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } - // { 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 } - // { 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 } - uint8x16_t permuted_samples[3] = { vqtbl1q_u8(samples, permute_tbl.val[0]), - vqtbl1q_u8(samples, permute_tbl.val[1]), - vqtbl1q_u8(samples, permute_tbl.val[2]) }; + const int8x16_t filters, + const uint8x8_t f0, + const uint8x16x2_t permute_tbl) { + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(samples, permute_tbl.val[0]), + vqtbl1q_u8(samples, permute_tbl.val[1]) }; - // First 4 output values. - int32x4_t sum0 = - vusdotq_lane_s32(vdupq_n_s32(0), permuted_samples[0], filters, 0); - sum0 = vusdotq_lane_s32(sum0, permuted_samples[1], filters, 1); - // Second 4 output values. - int32x4_t sum1 = - vusdotq_lane_s32(vdupq_n_s32(0), permuted_samples[1], filters, 0); - sum1 = vusdotq_lane_s32(sum1, permuted_samples[2], filters, 1); + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum0123 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[0], filters); + int32x4_t sum4567 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[1], filters); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0), vqmovn_s32(sum1)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + // Apply tap 0 and accumulate. + sum = vreinterpretq_s16_u16( + vmlsl_u8(vreinterpretq_u16_s16(sum), vget_low_u8(samples), f0)); + + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve8_horiz_8tap_neon_i8mm( const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const int16_t *filter_x, int w, int h) { - const int8x8_t filter = vmovn_s16(vld1q_s16(filter_x)); + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter_s8 = vshrn_n_s16(vld1q_s16(filter_x), 1); + // Stagger the filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); + const int8x16_t filter = + vqtbl1q_s8(vcombine_s8(filter_s8, vdup_n_s8(0)), filter_idx); + + // Since f0 is always negative and samples are unsigned, subtract (unsigned) + // s0 * -f0 to avoid signed overflow. + const uint8x8_t f0 = vdup_n_u8(-filter_x[0] >> 1); if (w == 4) { - const uint8x16x2_t perm_tbl = vld1q_u8_x2(kDotProdPermuteTbl); + const uint8x16_t perm_tbl = vld1q_u8(kMatMul8PermuteTbl); + do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src, src_stride, &s0, &s1, &s2, &s3); + uint8x8_t s01 = load_u8_4x2(src + 0 * src_stride, src_stride); + uint8x8_t s23 = load_u8_4x2(src + 2 * src_stride, src_stride); - int16x4_t d0 = convolve8_4_h(s0, filter, perm_tbl); - int16x4_t d1 = convolve8_4_h(s1, filter, perm_tbl); - int16x4_t d2 = convolve8_4_h(s2, filter, perm_tbl); - int16x4_t d3 = convolve8_4_h(s3, filter, perm_tbl); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + int16x4_t t0 = convolve8_4_h(s0, filter, perm_tbl); + int16x4_t t1 = convolve8_4_h(s1, filter, perm_tbl); + int16x4_t t2 = convolve8_4_h(s2, filter, perm_tbl); + int16x4_t t3 = convolve8_4_h(s3, filter, perm_tbl); + // Apply tap 0 and accumulate. + int16x8_t t01 = vcombine_s16(t0, t1); + int16x8_t t23 = vcombine_s16(t2, t3); + t01 = + vreinterpretq_s16_u16(vmlsl_u8(vreinterpretq_u16_s16(t01), s01, f0)); + t23 = + vreinterpretq_s16_u16(vmlsl_u8(vreinterpretq_u16_s16(t23), s23, f0)); + // We halved the filter values to -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(t01, FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(t23, FILTER_BITS - 1); store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); @@ -115,7 +114,7 @@ h -= 4; } while (h > 0); } else { - const uint8x16x3_t perm_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + const uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); do { int width = w; @@ -125,10 +124,10 @@ uint8x16_t s0, s1, s2, s3; load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); - uint8x8_t d0 = convolve8_8_h(s0, filter, perm_tbl); - uint8x8_t d1 = convolve8_8_h(s1, filter, perm_tbl); - uint8x8_t d2 = convolve8_8_h(s2, filter, perm_tbl); - uint8x8_t d3 = convolve8_8_h(s3, filter, perm_tbl); + uint8x8_t d0 = convolve8_8_h(s0, filter, f0, perm_tbl); + uint8x8_t d1 = convolve8_8_h(s1, filter, f0, perm_tbl); + uint8x8_t d2 = convolve8_8_h(s2, filter, f0, perm_tbl); + uint8x8_t d3 = convolve8_8_h(s3, filter, f0, perm_tbl); store_u8_8x4(d, dst_stride, d0, d1, d2, d3); @@ -189,7 +188,7 @@ vcombine_s8(vext_s8(x_filter, x_filter, 1), x_filter); if (width == 4) { - const uint8x16_t perm_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src, src_stride, &s0, &s1, &s2, &s3); @@ -210,7 +209,7 @@ height -= 4; } while (height > 0); } else { - const uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { int w = width; @@ -266,58 +265,6 @@ } } -static inline void transpose_concat_4x4(uint8x8_t a0, uint8x8_t a1, - uint8x8_t a2, uint8x8_t a3, - uint8x16_t *b) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, XX, XX, XX, XX - // a1: 10, 11, 12, 13, XX, XX, XX, XX - // a2: 20, 21, 22, 23, XX, XX, XX, XX - // a3: 30, 31, 32, 33, XX, XX, XX, XX - // - // b: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - - uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); - uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); - uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); - uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); - - uint8x16_t a01 = vzipq_u8(a0q, a1q).val[0]; - uint8x16_t a23 = vzipq_u8(a2q, a3q).val[0]; - - uint16x8_t a0123 = - vzipq_u16(vreinterpretq_u16_u8(a01), vreinterpretq_u16_u8(a23)).val[0]; - - *b = vreinterpretq_u8_u16(a0123); -} - -static inline void transpose_concat_8x4(uint8x8_t a0, uint8x8_t a1, - uint8x8_t a2, uint8x8_t a3, - uint8x16_t *b0, uint8x16_t *b1) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, 04, 05, 06, 07 - // a1: 10, 11, 12, 13, 14, 15, 16, 17 - // a2: 20, 21, 22, 23, 24, 25, 26, 27 - // a3: 30, 31, 32, 33, 34, 35, 36, 37 - // - // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 - - uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); - uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); - uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); - uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); - - uint8x16_t a01 = vzipq_u8(a0q, a1q).val[0]; - uint8x16_t a23 = vzipq_u8(a2q, a3q).val[0]; - - uint16x8x2_t a0123 = - vzipq_u16(vreinterpretq_u16_u8(a01), vreinterpretq_u16_u8(a23)); - - *b0 = vreinterpretq_u8_u16(a0123.val[0]); - *b1 = vreinterpretq_u8_u16(a0123.val[1]); -} - static inline int16x4_t convolve8_4_v(const uint8x16_t samples_lo, const uint8x16_t samples_hi, const int8x8_t filters) { @@ -326,7 +273,7 @@ sum = vusdotq_lane_s32(sum, samples_hi, filters, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vmovn_s32(sum); } static inline uint8x8_t convolve8_8_v(const uint8x16_t samples0_lo, @@ -344,14 +291,16 @@ sum1 = vusdotq_lane_s32(sum1, samples1_hi, filters, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0), vqmovn_s32(sum1)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0), vmovn_s32(sum1)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve8_vert_8tap_neon_i8mm( const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const int16_t *filter_y, int w, int h) { - const int8x8_t filter = vmovn_s16(vld1q_s16(filter_y)); + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(filter_y), 1); const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); uint8x16x2_t samples_LUT; @@ -361,19 +310,19 @@ src += 7 * src_stride; // This operation combines a conventional transpose and the sample permute - // (see horizontal case) required before computing the dot product. + // required before computing the dot product. uint8x16_t s0123, s1234, s2345, s3456; - transpose_concat_4x4(s0, s1, s2, s3, &s0123); - transpose_concat_4x4(s1, s2, s3, s4, &s1234); - transpose_concat_4x4(s2, s3, s4, s5, &s2345); - transpose_concat_4x4(s3, s4, s5, s6, &s3456); + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_u8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_u8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_u8_4x4(s3, s4, s5, s6, &s3456); do { uint8x8_t s7, s8, s9, s10; load_u8_8x4(src, src_stride, &s7, &s8, &s9, &s10); uint8x16_t s4567, s5678, s6789, s78910; - transpose_concat_4x4(s7, s8, s9, s10, &s78910); + transpose_concat_elems_u8_4x4(s7, s8, s9, s10, &s78910); // Merge new data into block from previous iteration. samples_LUT.val[0] = s3456; @@ -386,8 +335,9 @@ int16x4_t d1 = convolve8_4_v(s1234, s5678, filter); int16x4_t d2 = convolve8_4_v(s2345, s6789, filter); int16x4_t d3 = convolve8_4_v(s3456, s78910, filter); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); @@ -414,13 +364,13 @@ s += 7 * src_stride; // This operation combines a conventional transpose and the sample permute - // (see horizontal case) required before computing the dot product. + // required before computing the dot product. uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, s3456_lo, s3456_hi; - transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); - transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); - transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); - transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_u8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_u8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_u8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); do { uint8x8_t s7, s8, s9, s10; @@ -428,7 +378,7 @@ uint8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, s78910_lo, s78910_hi; - transpose_concat_8x4(s7, s8, s9, s10, &s78910_lo, &s78910_hi); + transpose_concat_elems_u8_8x4(s7, s8, s9, s10, &s78910_lo, &s78910_hi); // Merge new data into block from previous iteration. samples_LUT.val[0] = s3456_lo; @@ -476,6 +426,142 @@ } } +static inline int16x4_t convolve4_4_v(const uint8x16_t samples, + const int8x8_t filters) { + // Sample permutation is performed by the caller. + int32x4_t sum = vusdotq_lane_s32(vdupq_n_s32(0), samples, filters, 0); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint8x8_t convolve4_8_v(const uint8x16_t samples0, + const uint8x16_t samples1, + const int8x8_t filters) { + // Sample permutation is performed by the caller. + + // First 4 output values. + int32x4_t sum0 = vusdotq_lane_s32(vdupq_n_s32(0), samples0, filters, 0); + // Second 4 output values. + int32x4_t sum1 = vusdotq_lane_s32(vdupq_n_s32(0), samples1, filters, 0); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0), vmovn_s32(sum1)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); +} + +static inline void convolve8_vert_4tap_neon_i8mm( + const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const int16_t *filter_y, int w, int h) { + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(filter_y + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + uint8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(src, src_stride, &s0, &s1, &s2, &s3); + src += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(src, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567; + transpose_concat_elems_u8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + uint8x16_t s1234 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_v(s0123, filter); + int16x4_t d1 = convolve4_4_v(s1234, filter); + int16x4_t d2 = convolve4_4_v(s2345, filter); + int16x4_t d3 = convolve4_4_v(s3456, filter); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); + + store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); + store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src += 4 * src_stride; + dst += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src; + uint8_t *d = dst; + + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3); + s += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(s, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_u8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + uint8x16_t s1234_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + uint8x16_t s1234_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + uint8x8_t d0 = convolve4_8_v(s0123_lo, s0123_hi, filter); + uint8x8_t d1 = convolve4_8_v(s1234_lo, s1234_hi, filter); + uint8x8_t d2 = convolve4_8_v(s2345_lo, s2345_hi, filter); + uint8x8_t d3 = convolve4_8_v(s3456_lo, s3456_hi, filter); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src += 8; + dst += 8; + w -= 8; + } while (w != 0); + } +} + void aom_convolve8_vert_neon_i8mm(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const int16_t *filter_x, int x_step_q4, @@ -496,8 +582,8 @@ convolve8_vert_2tap_neon(src + 3 * src_stride, src_stride, dst, dst_stride, filter_y, w, h); } else if (filter_taps == 4) { - convolve8_vert_4tap_neon(src + 2 * src_stride, src_stride, dst, dst_stride, - filter_y, w, h); + convolve8_vert_4tap_neon_i8mm(src + 2 * src_stride, src_stride, dst, + dst_stride, filter_y, w, h); } else { convolve8_vert_8tap_neon_i8mm(src, src_stride, dst, dst_stride, filter_y, w, h);
diff --git a/aom_dsp/arm/highbd_convolve8_sve.c b/aom_dsp/arm/highbd_convolve8_sve.c index b5db14b..8af5c5e 100644 --- a/aom_dsp/arm/highbd_convolve8_sve.c +++ b/aom_dsp/arm/highbd_convolve8_sve.c
@@ -20,6 +20,18 @@ #include "aom_dsp/arm/aom_filter.h" #include "aom_dsp/arm/highbd_convolve8_neon.h" #include "aom_dsp/arm/mem_neon.h" +#include "aom_dsp/arm/transpose_neon.h" + +DECLARE_ALIGNED(16, const uint16_t, kHbdDotProdTbl[32]) = { + 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, + 4, 5, 6, 7, 5, 6, 7, 0, 6, 7, 0, 1, 7, 0, 1, 2, +}; + +// clang-format off +DECLARE_ALIGNED(16, const uint16_t, kDeinterleaveTbl[8]) = { + 0, 2, 4, 6, 1, 3, 5, 7, +}; +// clang-format on static inline uint16x4_t highbd_convolve8_4_h(int16x8_t s[4], int16x8_t filter, uint16x4_t max) { @@ -130,16 +142,6 @@ } } -// clang-format off -DECLARE_ALIGNED(16, static const uint16_t, kDotProdTbl[16]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, -}; - -DECLARE_ALIGNED(16, static const uint16_t, kDeinterleaveTbl[8]) = { - 0, 2, 4, 6, 1, 3, 5, 7, -}; -// clang-format on - static inline uint16x4_t highbd_convolve4_4_h(int16x8_t s, int16x8_t filter, uint16x8x2_t permute_tbl, uint16x4_t max) { @@ -183,7 +185,7 @@ if (width == 4) { const uint16x4_t max = vdup_n_u16((1 << bd) - 1); - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); const int16_t *s = (const int16_t *)src; uint16_t *d = dst; @@ -267,97 +269,6 @@ } } -DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { - // Shift left and insert new last column in transposed 4x4 block. - 2, 3, 4, 5, 6, 7, 16, 17, 10, 11, 12, 13, 14, 15, 24, 25, - // Shift left and insert two new columns in transposed 4x4 block. - 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15, 24, 25, 26, 27, - // Shift left and insert three new columns in transposed 4x4 block. - 6, 7, 16, 17, 18, 19, 20, 21, 14, 15, 24, 25, 26, 27, 28, 29 -}; - -static inline void transpose_concat_4x4(int16x4_t s0, int16x4_t s1, - int16x4_t s2, int16x4_t s3, - int16x8_t res[2]) { - // Transpose 16-bit elements and concatenate result rows as follows: - // s0: 00, 01, 02, 03 - // s1: 10, 11, 12, 13 - // s2: 20, 21, 22, 23 - // s3: 30, 31, 32, 33 - // - // res[0]: 00 10 20 30 01 11 21 31 - // res[1]: 02 12 22 32 03 13 23 33 - - int16x8_t s0q = vcombine_s16(s0, vdup_n_s16(0)); - int16x8_t s1q = vcombine_s16(s1, vdup_n_s16(0)); - int16x8_t s2q = vcombine_s16(s2, vdup_n_s16(0)); - int16x8_t s3q = vcombine_s16(s3, vdup_n_s16(0)); - - int32x4_t s01 = vreinterpretq_s32_s16(vzip1q_s16(s0q, s1q)); - int32x4_t s23 = vreinterpretq_s32_s16(vzip1q_s16(s2q, s3q)); - - int32x4x2_t s0123 = vzipq_s32(s01, s23); - - res[0] = vreinterpretq_s16_s32(s0123.val[0]); - res[1] = vreinterpretq_s16_s32(s0123.val[1]); -} - -static inline void transpose_concat_8x4(int16x8_t s0, int16x8_t s1, - int16x8_t s2, int16x8_t s3, - int16x8_t res[4]) { - // Transpose 16-bit elements and concatenate result rows as follows: - // s0: 00, 01, 02, 03, 04, 05, 06, 07 - // s1: 10, 11, 12, 13, 14, 15, 16, 17 - // s2: 20, 21, 22, 23, 24, 25, 26, 27 - // s3: 30, 31, 32, 33, 34, 35, 36, 37 - // - // res_lo[0]: 00 10 20 30 01 11 21 31 - // res_lo[1]: 02 12 22 32 03 13 23 33 - // res_hi[0]: 04 14 24 34 05 15 25 35 - // res_hi[1]: 06 16 26 36 07 17 27 37 - - int16x8x2_t tr01_16 = vzipq_s16(s0, s1); - int16x8x2_t tr23_16 = vzipq_s16(s2, s3); - - int32x4x2_t tr01_32 = vzipq_s32(vreinterpretq_s32_s16(tr01_16.val[0]), - vreinterpretq_s32_s16(tr23_16.val[0])); - int32x4x2_t tr23_32 = vzipq_s32(vreinterpretq_s32_s16(tr01_16.val[1]), - vreinterpretq_s32_s16(tr23_16.val[1])); - - res[0] = vreinterpretq_s16_s32(tr01_32.val[0]); - res[1] = vreinterpretq_s16_s32(tr01_32.val[1]); - res[2] = vreinterpretq_s16_s32(tr23_32.val[0]); - res[3] = vreinterpretq_s16_s32(tr23_32.val[1]); -} - -static inline void aom_tbl2x4_s16(int16x8_t t0[4], int16x8_t t1[4], - uint8x16_t tbl, int16x8_t res[4]) { - int8x16x2_t samples0 = { vreinterpretq_s8_s16(t0[0]), - vreinterpretq_s8_s16(t1[0]) }; - int8x16x2_t samples1 = { vreinterpretq_s8_s16(t0[1]), - vreinterpretq_s8_s16(t1[1]) }; - int8x16x2_t samples2 = { vreinterpretq_s8_s16(t0[2]), - vreinterpretq_s8_s16(t1[2]) }; - int8x16x2_t samples3 = { vreinterpretq_s8_s16(t0[3]), - vreinterpretq_s8_s16(t1[3]) }; - - res[0] = vreinterpretq_s16_s8(vqtbl2q_s8(samples0, tbl)); - res[1] = vreinterpretq_s16_s8(vqtbl2q_s8(samples1, tbl)); - res[2] = vreinterpretq_s16_s8(vqtbl2q_s8(samples2, tbl)); - res[3] = vreinterpretq_s16_s8(vqtbl2q_s8(samples3, tbl)); -} - -static inline void aom_tbl2x2_s16(int16x8_t t0[2], int16x8_t t1[2], - uint8x16_t tbl, int16x8_t res[2]) { - int8x16x2_t samples0 = { vreinterpretq_s8_s16(t0[0]), - vreinterpretq_s8_s16(t1[0]) }; - int8x16x2_t samples1 = { vreinterpretq_s8_s16(t0[1]), - vreinterpretq_s8_s16(t1[1]) }; - - res[0] = vreinterpretq_s16_s8(vqtbl2q_s8(samples0, tbl)); - res[1] = vreinterpretq_s16_s8(vqtbl2q_s8(samples1, tbl)); -} - static inline uint16x4_t highbd_convolve8_4_v(int16x8_t samples_lo[2], int16x8_t samples_hi[2], int16x8_t filter, @@ -377,47 +288,17 @@ return vmin_u16(res, max); } -static inline uint16x8_t highbd_convolve8_8_v(int16x8_t samples_lo[4], - int16x8_t samples_hi[4], - int16x8_t filter, - uint16x8_t max) { - int64x2_t sum[4]; - - sum[0] = aom_svdot_lane_s16(vdupq_n_s64(0), samples_lo[0], filter, 0); - sum[0] = aom_svdot_lane_s16(sum[0], samples_hi[0], filter, 1); - - sum[1] = aom_svdot_lane_s16(vdupq_n_s64(0), samples_lo[1], filter, 0); - sum[1] = aom_svdot_lane_s16(sum[1], samples_hi[1], filter, 1); - - sum[2] = aom_svdot_lane_s16(vdupq_n_s64(0), samples_lo[2], filter, 0); - sum[2] = aom_svdot_lane_s16(sum[2], samples_hi[2], filter, 1); - - sum[3] = aom_svdot_lane_s16(vdupq_n_s64(0), samples_lo[3], filter, 0); - sum[3] = aom_svdot_lane_s16(sum[3], samples_hi[3], filter, 1); - - int32x4_t res0 = vcombine_s32(vmovn_s64(sum[0]), vmovn_s64(sum[1])); - int32x4_t res1 = vcombine_s32(vmovn_s64(sum[2]), vmovn_s64(sum[3])); - - uint16x8_t res = vcombine_u16(vqrshrun_n_s32(res0, FILTER_BITS), - vqrshrun_n_s32(res1, FILTER_BITS)); - - return vminq_u16(res, max); -} - static inline void highbd_convolve8_vert_8tap_sve( const uint16_t *src, ptrdiff_t src_stride, uint16_t *dst, ptrdiff_t dst_stride, const int16_t *filter_y, int width, int height, int bd) { const int16x8_t y_filter = vld1q_s16(filter_y); - uint8x16_t merge_block_tbl[3]; - merge_block_tbl[0] = vld1q_u8(kDotProdMergeBlockTbl); - merge_block_tbl[1] = vld1q_u8(kDotProdMergeBlockTbl + 16); - merge_block_tbl[2] = vld1q_u8(kDotProdMergeBlockTbl + 32); - - if (width == 4) { + do { const uint16x4_t max = vdup_n_u16((1 << bd) - 1); int16_t *s = (int16_t *)src; + uint16_t *d = dst; + int h = height; int16x4_t s0, s1, s2, s3, s4, s5, s6; load_s16_4x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); @@ -426,10 +307,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); do { int16x4_t s7, s8, s9, s10; @@ -438,19 +319,17 @@ int16x8_t s4567[2], s5678[2], s6789[2], s78910[2]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_4x4(s7, s8, s9, s10, s78910); - - // Merge new data into block from previous iteration. - aom_tbl2x2_s16(s3456, s78910, merge_block_tbl[0], s4567); - aom_tbl2x2_s16(s3456, s78910, merge_block_tbl[1], s5678); - aom_tbl2x2_s16(s3456, s78910, merge_block_tbl[2], s6789); + transpose_concat_elems_s16_4x4(s4, s5, s6, s7, s4567); + transpose_concat_elems_s16_4x4(s5, s6, s7, s8, s5678); + transpose_concat_elems_s16_4x4(s6, s7, s8, s9, s6789); + transpose_concat_elems_s16_4x4(s7, s8, s9, s10, s78910); uint16x4_t d0 = highbd_convolve8_4_v(s0123, s4567, y_filter, max); uint16x4_t d1 = highbd_convolve8_4_v(s1234, s5678, y_filter, max); uint16x4_t d2 = highbd_convolve8_4_v(s2345, s6789, y_filter, max); uint16x4_t d3 = highbd_convolve8_4_v(s3456, s78910, y_filter, max); - store_u16_4x4(dst, dst_stride, d0, d1, d2, d3); + store_u16_4x4(d, dst_stride, d0, d1, d2, d3); // Prepare block for next iteration - re-using as much as possible. // Shuffle everything up four rows. @@ -463,81 +342,18 @@ s3456[0] = s78910[0]; s3456[1] = s78910[1]; + s4 = s8; + s5 = s9; + s6 = s10; + s += 4 * src_stride; - dst += 4 * dst_stride; - height -= 4; - } while (height != 0); - } else { - const uint16x8_t max = vdupq_n_u16((1 << bd) - 1); - do { - int h = height; - int16_t *s = (int16_t *)src; - uint16_t *d = dst; - - int16x8_t s0, s1, s2, s3, s4, s5, s6; - load_s16_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); - s += 7 * src_stride; - - // This operation combines a conventional transpose and the sample permute - // required before computing the dot product. - int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); - - do { - int16x8_t s7, s8, s9, s10; - load_s16_8x4(s, src_stride, &s7, &s8, &s9, &s10); - - int16x8_t s4567[4], s5678[4], s6789[4], s78910[4]; - - // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_8x4(s7, s8, s9, s10, s78910); - - // Merge new data into block from previous iteration. - aom_tbl2x4_s16(s3456, s78910, merge_block_tbl[0], s4567); - aom_tbl2x4_s16(s3456, s78910, merge_block_tbl[1], s5678); - aom_tbl2x4_s16(s3456, s78910, merge_block_tbl[2], s6789); - - uint16x8_t d0 = highbd_convolve8_8_v(s0123, s4567, y_filter, max); - uint16x8_t d1 = highbd_convolve8_8_v(s1234, s5678, y_filter, max); - uint16x8_t d2 = highbd_convolve8_8_v(s2345, s6789, y_filter, max); - uint16x8_t d3 = highbd_convolve8_8_v(s3456, s78910, y_filter, max); - - store_u16_8x4(d, dst_stride, d0, d1, d2, d3); - - // Prepare block for next iteration - re-using as much as possible. - // Shuffle everything up four rows. - s0123[0] = s4567[0]; - s0123[1] = s4567[1]; - s0123[2] = s4567[2]; - s0123[3] = s4567[3]; - - s1234[0] = s5678[0]; - s1234[1] = s5678[1]; - s1234[2] = s5678[2]; - s1234[3] = s5678[3]; - - s2345[0] = s6789[0]; - s2345[1] = s6789[1]; - s2345[2] = s6789[2]; - s2345[3] = s6789[3]; - - s3456[0] = s78910[0]; - s3456[1] = s78910[1]; - s3456[2] = s78910[2]; - s3456[3] = s78910[3]; - - s += 4 * src_stride; - d += 4 * dst_stride; - h -= 4; - } while (h != 0); - src += 8; - dst += 8; - width -= 8; - } while (width != 0); - } + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + src += 4; + dst += 4; + width -= 4; + } while (width != 0); } void aom_highbd_convolve8_vert_sve(const uint8_t *src8, ptrdiff_t src_stride,
diff --git a/aom_dsp/arm/highbd_convolve8_sve.h b/aom_dsp/arm/highbd_convolve8_sve.h new file mode 100644 index 0000000..1bbdc28 --- /dev/null +++ b/aom_dsp/arm/highbd_convolve8_sve.h
@@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025, 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. + */ + +#ifndef AOM_AOM_DSP_ARM_HIGHBD_CONVOLVE8_SVE_H_ +#define AOM_AOM_DSP_ARM_HIGHBD_CONVOLVE8_SVE_H_ + +#include "aom_ports/mem.h" + +DECLARE_ALIGNED(16, extern const uint16_t, kHbdDotProdTbl[32]); +DECLARE_ALIGNED(16, extern const uint16_t, kDeinterleaveTbl[8]); + +#endif // AOM_AOM_DSP_ARM_HIGHBD_CONVOLVE8_SVE_H_
diff --git a/aom_dsp/arm/highbd_sad_neon.c b/aom_dsp/arm/highbd_sad_neon.c index 115aae8..b8dde45 100644 --- a/aom_dsp/arm/highbd_sad_neon.c +++ b/aom_dsp/arm/highbd_sad_neon.c
@@ -19,15 +19,14 @@ #include "aom_dsp/arm/mem_neon.h" #include "aom_dsp/arm/sum_neon.h" -static inline uint32_t highbd_sad4xh_small_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { +static inline uint32_t highbd_sad4xh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); uint32x4_t sum = vdupq_n_u32(0); - int i = h; do { uint16x4_t s = vld1_u16(src16_ptr); uint16x4_t r = vld1_u16(ref16_ptr); @@ -35,248 +34,259 @@ src16_ptr += src_stride; ref16_ptr += ref_stride; - } while (--i != 0); + } while (--h != 0); return horizontal_add_u32x4(sum); } -static inline uint32_t highbd_sad8xh_small_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { +static inline uint32_t highbd_sad8xh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); - uint16x8_t sum = vdupq_n_u16(0); - int i = h; - do { - uint16x8_t s = vld1q_u16(src16_ptr); - uint16x8_t r = vld1q_u16(ref16_ptr); - sum = vabaq_u16(sum, s, r); + // 'h_overflow' is the number of 8-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 8-wide rows. + const int h_overflow = 16; + // If block height 'h' is smaller than this limit, use 'h' instead. + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); - src16_ptr += src_stride; - ref16_ptr += ref_stride; - } while (--i != 0); - - return horizontal_add_u16x8(sum); -} - -#if !CONFIG_REALTIME_ONLY -static inline uint32_t highbd_sad8xh_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); uint32x4_t sum_u32 = vdupq_n_u32(0); - int i = h; do { - uint16x8_t s = vld1q_u16(src16_ptr); - uint16x8_t r = vld1q_u16(ref16_ptr); - uint16x8_t sum_u16 = vabdq_u16(s, r); + uint16x8_t sum_u16 = vdupq_n_u16(0); + + int i = h_limit; + do { + uint16x8_t s0 = vld1q_u16(src16_ptr); + uint16x8_t r0 = vld1q_u16(ref16_ptr); + sum_u16 = vabaq_u16(sum_u16, s0, r0); + + src16_ptr += src_stride; + ref16_ptr += ref_stride; + } while (--i != 0); + sum_u32 = vpadalq_u16(sum_u32, sum_u16); - src16_ptr += src_stride; - ref16_ptr += ref_stride; - } while (--i != 0); + h -= h_limit; + } while (h != 0); return horizontal_add_u32x4(sum_u32); } -#endif // !CONFIG_REALTIME_ONLY -static inline uint32_t highbd_sad16xh_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { +static inline uint32_t highbd_sad16xh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); - uint32x4_t sum[2] = { vdupq_n_u32(0), vdupq_n_u32(0) }; - int i = h; + // 'h_overflow' is the number of 16-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 16-wide rows using two accumulators. + const int h_overflow = 16; + // If block height 'h' is smaller than this limit, use 'h' instead. + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + + uint32x4_t sum_u32 = vdupq_n_u32(0); + do { - uint16x8_t s0 = vld1q_u16(src16_ptr); - uint16x8_t r0 = vld1q_u16(ref16_ptr); - uint16x8_t diff0 = vabdq_u16(s0, r0); - sum[0] = vpadalq_u16(sum[0], diff0); - - uint16x8_t s1 = vld1q_u16(src16_ptr + 8); - uint16x8_t r1 = vld1q_u16(ref16_ptr + 8); - uint16x8_t diff1 = vabdq_u16(s1, r1); - sum[1] = vpadalq_u16(sum[1], diff1); - - src16_ptr += src_stride; - ref16_ptr += ref_stride; - } while (--i != 0); - - sum[0] = vaddq_u32(sum[0], sum[1]); - return horizontal_add_u32x4(sum[0]); -} - -static inline uint32_t highbd_sadwxh_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int w, int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); - uint32x4_t sum[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; - - int i = h; - do { - int j = 0; + uint16x8_t sum_u16[2] = { vdupq_n_u16(0), vdupq_n_u16(0) }; + int i = h_limit; do { - uint16x8_t s0 = vld1q_u16(src16_ptr + j); - uint16x8_t r0 = vld1q_u16(ref16_ptr + j); - uint16x8_t diff0 = vabdq_u16(s0, r0); - sum[0] = vpadalq_u16(sum[0], diff0); + uint16x8_t s0 = vld1q_u16(src16_ptr); + uint16x8_t r0 = vld1q_u16(ref16_ptr); + sum_u16[0] = vabaq_u16(sum_u16[0], s0, r0); - uint16x8_t s1 = vld1q_u16(src16_ptr + j + 8); - uint16x8_t r1 = vld1q_u16(ref16_ptr + j + 8); - uint16x8_t diff1 = vabdq_u16(s1, r1); - sum[1] = vpadalq_u16(sum[1], diff1); + uint16x8_t s1 = vld1q_u16(src16_ptr + 8); + uint16x8_t r1 = vld1q_u16(ref16_ptr + 8); + sum_u16[1] = vabaq_u16(sum_u16[1], s1, r1); - uint16x8_t s2 = vld1q_u16(src16_ptr + j + 16); - uint16x8_t r2 = vld1q_u16(ref16_ptr + j + 16); - uint16x8_t diff2 = vabdq_u16(s2, r2); - sum[2] = vpadalq_u16(sum[2], diff2); + src16_ptr += src_stride; + ref16_ptr += ref_stride; + } while (--i != 0); - uint16x8_t s3 = vld1q_u16(src16_ptr + j + 24); - uint16x8_t r3 = vld1q_u16(ref16_ptr + j + 24); - uint16x8_t diff3 = vabdq_u16(s3, r3); - sum[3] = vpadalq_u16(sum[3], diff3); - - j += 32; - } while (j < w); - - src16_ptr += src_stride; - ref16_ptr += ref_stride; - } while (--i != 0); - - sum[0] = vaddq_u32(sum[0], sum[1]); - sum[2] = vaddq_u32(sum[2], sum[3]); - sum[0] = vaddq_u32(sum[0], sum[2]); - - return horizontal_add_u32x4(sum[0]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[0]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[1]); + h -= h_limit; + } while (h != 0); + return horizontal_add_u32x4(sum_u32); } -static inline unsigned int highbd_sad128xh_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { - return highbd_sadwxh_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, 128, - h); +static inline uint32_t highbd_sadwxh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int w, int h, + const int h_overflow) { + const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); + const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); + + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + + uint32x4_t sum_u32 = vdupq_n_u32(0); + + do { + uint16x8_t sum_u16[4] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0), + vdupq_n_u16(0) }; + + int i = h_limit; + do { + int j = 0; + do { + uint16x8_t s0 = vld1q_u16(src16_ptr + j); + uint16x8_t r0 = vld1q_u16(ref16_ptr + j); + sum_u16[0] = vabaq_u16(sum_u16[0], s0, r0); + + uint16x8_t s1 = vld1q_u16(src16_ptr + j + 8); + uint16x8_t r1 = vld1q_u16(ref16_ptr + j + 8); + sum_u16[1] = vabaq_u16(sum_u16[1], s1, r1); + + uint16x8_t s2 = vld1q_u16(src16_ptr + j + 16); + uint16x8_t r2 = vld1q_u16(ref16_ptr + j + 16); + sum_u16[2] = vabaq_u16(sum_u16[2], s2, r2); + + uint16x8_t s3 = vld1q_u16(src16_ptr + j + 24); + uint16x8_t r3 = vld1q_u16(ref16_ptr + j + 24); + sum_u16[3] = vabaq_u16(sum_u16[3], s3, r3); + + j += 32; + } while (j < w); + + src16_ptr += src_stride; + ref16_ptr += ref_stride; + } while (--i != 0); + + sum_u32 = vpadalq_u16(sum_u32, sum_u16[0]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[1]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[2]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[3]); + + h -= h_limit; + } while (h != 0); + return horizontal_add_u32x4(sum_u32); } -static inline unsigned int highbd_sad64xh_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { - return highbd_sadwxh_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, 64, - h); +static inline uint32_t highbd_sad32xh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { + // 'h_overflow' is the number of 32-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 32-wide rows using four accumulators. + const int h_overflow = 16; + return highbd_sadwxh_neon(src_ptr, src_stride, ref_ptr, ref_stride, 32, h, + h_overflow); } -static inline unsigned int highbd_sad32xh_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h) { - return highbd_sadwxh_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, 32, - h); +static inline uint32_t highbd_sad64xh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { + // 'h_overflow' is the number of 64-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 8 64-wide rows using four accumulators. + const int h_overflow = 8; + return highbd_sadwxh_neon(src_ptr, src_stride, ref_ptr, ref_stride, 64, h, + h_overflow); } -#define HBD_SAD_WXH_SMALL_NEON(w, h) \ +static inline uint32_t highbd_sad128xh_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { + // 'h_overflow' is the number of 128-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 4 128-wide rows using four accumulators. + const int h_overflow = 4; + return highbd_sadwxh_neon(src_ptr, src_stride, ref_ptr, ref_stride, 128, h, + h_overflow); +} + +#define HBD_SAD_WXH_NEON(w, h) \ unsigned int aom_highbd_sad##w##x##h##_neon( \ const uint8_t *src, int src_stride, const uint8_t *ref, \ int ref_stride) { \ - return highbd_sad##w##xh_small_neon(src, src_stride, ref, ref_stride, \ - (h)); \ + return highbd_sad##w##xh_neon(src, src_stride, ref, ref_stride, (h)); \ } -#define HBD_SAD_WXH_LARGE_NEON(w, h) \ - unsigned int aom_highbd_sad##w##x##h##_neon( \ - const uint8_t *src, int src_stride, const uint8_t *ref, \ - int ref_stride) { \ - return highbd_sad##w##xh_large_neon(src, src_stride, ref, ref_stride, \ - (h)); \ - } +HBD_SAD_WXH_NEON(4, 4) +HBD_SAD_WXH_NEON(4, 8) -HBD_SAD_WXH_SMALL_NEON(4, 4) -HBD_SAD_WXH_SMALL_NEON(4, 8) +HBD_SAD_WXH_NEON(8, 4) +HBD_SAD_WXH_NEON(8, 8) +HBD_SAD_WXH_NEON(8, 16) -HBD_SAD_WXH_SMALL_NEON(8, 4) -HBD_SAD_WXH_SMALL_NEON(8, 8) -HBD_SAD_WXH_SMALL_NEON(8, 16) +HBD_SAD_WXH_NEON(16, 8) +HBD_SAD_WXH_NEON(16, 16) +HBD_SAD_WXH_NEON(16, 32) -HBD_SAD_WXH_LARGE_NEON(16, 8) -HBD_SAD_WXH_LARGE_NEON(16, 16) -HBD_SAD_WXH_LARGE_NEON(16, 32) +HBD_SAD_WXH_NEON(32, 16) +HBD_SAD_WXH_NEON(32, 32) +HBD_SAD_WXH_NEON(32, 64) -HBD_SAD_WXH_LARGE_NEON(32, 16) -HBD_SAD_WXH_LARGE_NEON(32, 32) -HBD_SAD_WXH_LARGE_NEON(32, 64) +HBD_SAD_WXH_NEON(64, 32) +HBD_SAD_WXH_NEON(64, 64) +HBD_SAD_WXH_NEON(64, 128) -HBD_SAD_WXH_LARGE_NEON(64, 32) -HBD_SAD_WXH_LARGE_NEON(64, 64) -HBD_SAD_WXH_LARGE_NEON(64, 128) - -HBD_SAD_WXH_LARGE_NEON(128, 64) -HBD_SAD_WXH_LARGE_NEON(128, 128) +HBD_SAD_WXH_NEON(128, 64) +HBD_SAD_WXH_NEON(128, 128) #if !CONFIG_REALTIME_ONLY -HBD_SAD_WXH_SMALL_NEON(4, 16) +HBD_SAD_WXH_NEON(4, 16) -HBD_SAD_WXH_LARGE_NEON(8, 32) +HBD_SAD_WXH_NEON(8, 32) -HBD_SAD_WXH_LARGE_NEON(16, 4) -HBD_SAD_WXH_LARGE_NEON(16, 64) +HBD_SAD_WXH_NEON(16, 4) +HBD_SAD_WXH_NEON(16, 64) -HBD_SAD_WXH_LARGE_NEON(32, 8) +HBD_SAD_WXH_NEON(32, 8) -HBD_SAD_WXH_LARGE_NEON(64, 16) +HBD_SAD_WXH_NEON(64, 16) #endif // !CONFIG_REALTIME_ONLY -#define HBD_SAD_SKIP_WXH_SMALL_NEON(w, h) \ - unsigned int aom_highbd_sad_skip_##w##x##h##_neon( \ - const uint8_t *src, int src_stride, const uint8_t *ref, \ - int ref_stride) { \ - return 2 * highbd_sad##w##xh_small_neon(src, 2 * src_stride, ref, \ - 2 * ref_stride, (h) / 2); \ +#undef HBD_SAD_WXH_NEON + +#define HBD_SAD_SKIP_WXH_NEON(w, h) \ + unsigned int aom_highbd_sad_skip_##w##x##h##_neon( \ + const uint8_t *src, int src_stride, const uint8_t *ref, \ + int ref_stride) { \ + return 2 * highbd_sad##w##xh_neon(src, 2 * src_stride, ref, \ + 2 * ref_stride, (h) / 2); \ } -#define HBD_SAD_SKIP_WXH_LARGE_NEON(w, h) \ - unsigned int aom_highbd_sad_skip_##w##x##h##_neon( \ - const uint8_t *src, int src_stride, const uint8_t *ref, \ - int ref_stride) { \ - return 2 * highbd_sad##w##xh_large_neon(src, 2 * src_stride, ref, \ - 2 * ref_stride, (h) / 2); \ - } +HBD_SAD_SKIP_WXH_NEON(8, 16) -HBD_SAD_SKIP_WXH_SMALL_NEON(8, 16) +HBD_SAD_SKIP_WXH_NEON(16, 16) +HBD_SAD_SKIP_WXH_NEON(16, 32) -HBD_SAD_SKIP_WXH_LARGE_NEON(16, 16) -HBD_SAD_SKIP_WXH_LARGE_NEON(16, 32) +HBD_SAD_SKIP_WXH_NEON(32, 16) +HBD_SAD_SKIP_WXH_NEON(32, 32) +HBD_SAD_SKIP_WXH_NEON(32, 64) -HBD_SAD_SKIP_WXH_LARGE_NEON(32, 16) -HBD_SAD_SKIP_WXH_LARGE_NEON(32, 32) -HBD_SAD_SKIP_WXH_LARGE_NEON(32, 64) +HBD_SAD_SKIP_WXH_NEON(64, 32) +HBD_SAD_SKIP_WXH_NEON(64, 64) +HBD_SAD_SKIP_WXH_NEON(64, 128) -HBD_SAD_SKIP_WXH_LARGE_NEON(64, 32) -HBD_SAD_SKIP_WXH_LARGE_NEON(64, 64) -HBD_SAD_SKIP_WXH_LARGE_NEON(64, 128) - -HBD_SAD_SKIP_WXH_LARGE_NEON(128, 64) -HBD_SAD_SKIP_WXH_LARGE_NEON(128, 128) +HBD_SAD_SKIP_WXH_NEON(128, 64) +HBD_SAD_SKIP_WXH_NEON(128, 128) #if !CONFIG_REALTIME_ONLY -HBD_SAD_SKIP_WXH_SMALL_NEON(4, 16) +HBD_SAD_SKIP_WXH_NEON(4, 16) -HBD_SAD_SKIP_WXH_SMALL_NEON(8, 32) +HBD_SAD_SKIP_WXH_NEON(8, 32) -HBD_SAD_SKIP_WXH_LARGE_NEON(16, 64) +HBD_SAD_SKIP_WXH_NEON(16, 64) -HBD_SAD_SKIP_WXH_LARGE_NEON(64, 16) +HBD_SAD_SKIP_WXH_NEON(64, 16) #endif // !CONFIG_REALTIME_ONLY +#undef HBD_SAD_SKIP_WXH_NEON + static inline uint32_t highbd_sad8xh_avg_neon(const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, @@ -285,24 +295,40 @@ const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); const uint16_t *pred16_ptr = CONVERT_TO_SHORTPTR(second_pred); - uint32x4_t sum = vdupq_n_u32(0); - int i = h; + // 'h_overflow' is the number of 8-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 8-wide rows. + const int h_overflow = 16; + // If block height 'h' is smaller than this limit, use 'h' instead. + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + + uint32x4_t sum_u32 = vdupq_n_u32(0); + do { - uint16x8_t s = vld1q_u16(src16_ptr); - uint16x8_t r = vld1q_u16(ref16_ptr); - uint16x8_t p = vld1q_u16(pred16_ptr); + uint16x8_t sum_u16 = vdupq_n_u16(0); - uint16x8_t avg = vrhaddq_u16(r, p); - uint16x8_t diff = vabdq_u16(s, avg); - sum = vpadalq_u16(sum, diff); + int i = h_limit; + do { + uint16x8_t s = vld1q_u16(src16_ptr); + uint16x8_t r = vld1q_u16(ref16_ptr); + uint16x8_t p = vld1q_u16(pred16_ptr); - src16_ptr += src_stride; - ref16_ptr += ref_stride; - pred16_ptr += 8; - } while (--i != 0); + uint16x8_t avg = vrhaddq_u16(r, p); + sum_u16 = vabaq_u16(sum_u16, s, avg); - return horizontal_add_u32x4(sum); + src16_ptr += src_stride; + ref16_ptr += ref_stride; + pred16_ptr += 8; + } while (--i != 0); + + sum_u32 = vpadalq_u16(sum_u32, sum_u16); + + h -= h_limit; + } while (h != 0); + + return horizontal_add_u32x4(sum_u32); } static inline uint32_t highbd_sad16xh_avg_neon(const uint8_t *src_ptr, @@ -313,120 +339,157 @@ const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); const uint16_t *pred16_ptr = CONVERT_TO_SHORTPTR(second_pred); - uint32x4_t sum[2] = { vdupq_n_u32(0), vdupq_n_u32(0) }; - int i = h; + // 'h_overflow' is the number of 16-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 16-wide rows using two accumulators. + const int h_overflow = 16; + // If block height 'h' is smaller than this limit, use 'h' instead. + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + + uint32x4_t sum_u32 = vdupq_n_u32(0); + do { - uint16x8_t s0, s1, r0, r1, p0, p1; - uint16x8_t avg0, avg1, diff0, diff1; + uint16x8_t sum_u16[2] = { vdupq_n_u16(0), vdupq_n_u16(0) }; - s0 = vld1q_u16(src16_ptr); - r0 = vld1q_u16(ref16_ptr); - p0 = vld1q_u16(pred16_ptr); - avg0 = vrhaddq_u16(r0, p0); - diff0 = vabdq_u16(s0, avg0); - sum[0] = vpadalq_u16(sum[0], diff0); + int i = h_limit; + do { + uint16x8_t s0 = vld1q_u16(src16_ptr); + uint16x8_t r0 = vld1q_u16(ref16_ptr); + uint16x8_t p0 = vld1q_u16(pred16_ptr); - s1 = vld1q_u16(src16_ptr + 8); - r1 = vld1q_u16(ref16_ptr + 8); - p1 = vld1q_u16(pred16_ptr + 8); - avg1 = vrhaddq_u16(r1, p1); - diff1 = vabdq_u16(s1, avg1); - sum[1] = vpadalq_u16(sum[1], diff1); + uint16x8_t avg0 = vrhaddq_u16(r0, p0); + sum_u16[0] = vabaq_u16(sum_u16[0], s0, avg0); - src16_ptr += src_stride; - ref16_ptr += ref_stride; - pred16_ptr += 16; - } while (--i != 0); + uint16x8_t s1 = vld1q_u16(src16_ptr + 8); + uint16x8_t r1 = vld1q_u16(ref16_ptr + 8); + uint16x8_t p1 = vld1q_u16(pred16_ptr + 8); - sum[0] = vaddq_u32(sum[0], sum[1]); - return horizontal_add_u32x4(sum[0]); + uint16x8_t avg1 = vrhaddq_u16(r1, p1); + sum_u16[1] = vabaq_u16(sum_u16[1], s1, avg1); + + src16_ptr += src_stride; + ref16_ptr += ref_stride; + pred16_ptr += 16; + } while (--i != 0); + + sum_u32 = vpadalq_u16(sum_u32, sum_u16[0]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[1]); + + h -= h_limit; + } while (h != 0); + + return horizontal_add_u32x4(sum_u32); } static inline uint32_t highbd_sadwxh_avg_neon(const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, - int ref_stride, int w, int h, - const uint8_t *second_pred) { + int ref_stride, + const uint8_t *second_pred, int w, + int h, const int h_overflow) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr = CONVERT_TO_SHORTPTR(ref_ptr); const uint16_t *pred16_ptr = CONVERT_TO_SHORTPTR(second_pred); - uint32x4_t sum[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; - int i = h; + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + + uint32x4_t sum_u32 = vdupq_n_u32(0); + do { - int j = 0; + uint16x8_t sum_u16[4] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0), + vdupq_n_u16(0) }; + + int i = h_limit; do { - uint16x8_t s0, s1, s2, s3, r0, r1, r2, r3, p0, p1, p2, p3; - uint16x8_t avg0, avg1, avg2, avg3, diff0, diff1, diff2, diff3; + int j = 0; + do { + uint16x8_t s0 = vld1q_u16(src16_ptr + j); + uint16x8_t r0 = vld1q_u16(ref16_ptr + j); + uint16x8_t p0 = vld1q_u16(pred16_ptr + j); - s0 = vld1q_u16(src16_ptr + j); - r0 = vld1q_u16(ref16_ptr + j); - p0 = vld1q_u16(pred16_ptr + j); - avg0 = vrhaddq_u16(r0, p0); - diff0 = vabdq_u16(s0, avg0); - sum[0] = vpadalq_u16(sum[0], diff0); + uint16x8_t avg0 = vrhaddq_u16(r0, p0); + sum_u16[0] = vabaq_u16(sum_u16[0], s0, avg0); - s1 = vld1q_u16(src16_ptr + j + 8); - r1 = vld1q_u16(ref16_ptr + j + 8); - p1 = vld1q_u16(pred16_ptr + j + 8); - avg1 = vrhaddq_u16(r1, p1); - diff1 = vabdq_u16(s1, avg1); - sum[1] = vpadalq_u16(sum[1], diff1); + uint16x8_t s1 = vld1q_u16(src16_ptr + j + 8); + uint16x8_t r1 = vld1q_u16(ref16_ptr + j + 8); + uint16x8_t p1 = vld1q_u16(pred16_ptr + j + 8); - s2 = vld1q_u16(src16_ptr + j + 16); - r2 = vld1q_u16(ref16_ptr + j + 16); - p2 = vld1q_u16(pred16_ptr + j + 16); - avg2 = vrhaddq_u16(r2, p2); - diff2 = vabdq_u16(s2, avg2); - sum[2] = vpadalq_u16(sum[2], diff2); + uint16x8_t avg1 = vrhaddq_u16(r1, p1); + sum_u16[1] = vabaq_u16(sum_u16[1], s1, avg1); - s3 = vld1q_u16(src16_ptr + j + 24); - r3 = vld1q_u16(ref16_ptr + j + 24); - p3 = vld1q_u16(pred16_ptr + j + 24); - avg3 = vrhaddq_u16(r3, p3); - diff3 = vabdq_u16(s3, avg3); - sum[3] = vpadalq_u16(sum[3], diff3); + uint16x8_t s2 = vld1q_u16(src16_ptr + j + 16); + uint16x8_t r2 = vld1q_u16(ref16_ptr + j + 16); + uint16x8_t p2 = vld1q_u16(pred16_ptr + j + 16); - j += 32; - } while (j < w); + uint16x8_t avg2 = vrhaddq_u16(r2, p2); + sum_u16[2] = vabaq_u16(sum_u16[2], s2, avg2); - src16_ptr += src_stride; - ref16_ptr += ref_stride; - pred16_ptr += w; - } while (--i != 0); + uint16x8_t s3 = vld1q_u16(src16_ptr + j + 24); + uint16x8_t r3 = vld1q_u16(ref16_ptr + j + 24); + uint16x8_t p3 = vld1q_u16(pred16_ptr + j + 24); - sum[0] = vaddq_u32(sum[0], sum[1]); - sum[2] = vaddq_u32(sum[2], sum[3]); - sum[0] = vaddq_u32(sum[0], sum[2]); + uint16x8_t avg3 = vrhaddq_u16(r3, p3); + sum_u16[3] = vabaq_u16(sum_u16[3], s3, avg3); - return horizontal_add_u32x4(sum[0]); + j += 32; + } while (j < w); + + src16_ptr += src_stride; + ref16_ptr += ref_stride; + pred16_ptr += w; + } while (--i != 0); + + sum_u32 = vpadalq_u16(sum_u32, sum_u16[0]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[1]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[2]); + sum_u32 = vpadalq_u16(sum_u32, sum_u16[3]); + + h -= h_limit; + } while (h != 0); + + return horizontal_add_u32x4(sum_u32); } -static inline unsigned int highbd_sad128xh_avg_neon( - const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, - int ref_stride, int h, const uint8_t *second_pred) { - return highbd_sadwxh_avg_neon(src_ptr, src_stride, ref_ptr, ref_stride, 128, - h, second_pred); +static inline uint32_t highbd_sad32xh_avg_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h, + const uint8_t *second_pred) { + // 'h_overflow' is the number of 32-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 32-wide rows using four accumulators. + const int h_overflow = 16; + return highbd_sadwxh_avg_neon(src_ptr, src_stride, ref_ptr, ref_stride, + second_pred, 32, h, h_overflow); } -static inline unsigned int highbd_sad64xh_avg_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h, - const uint8_t *second_pred) { - return highbd_sadwxh_avg_neon(src_ptr, src_stride, ref_ptr, ref_stride, 64, h, - second_pred); +static inline uint32_t highbd_sad64xh_avg_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h, + const uint8_t *second_pred) { + // 'h_overflow' is the number of 64-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 8 64-wide rows using four accumulators. + const int h_overflow = 8; + return highbd_sadwxh_avg_neon(src_ptr, src_stride, ref_ptr, ref_stride, + second_pred, 64, h, h_overflow); } -static inline unsigned int highbd_sad32xh_avg_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int h, - const uint8_t *second_pred) { - return highbd_sadwxh_avg_neon(src_ptr, src_stride, ref_ptr, ref_stride, 32, h, - second_pred); +static inline uint32_t highbd_sad128xh_avg_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h, + const uint8_t *second_pred) { + // 'h_overflow' is the number of 128-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 4 128-wide rows using four accumulators. + const int h_overflow = 4; + return highbd_sadwxh_avg_neon(src_ptr, src_stride, ref_ptr, ref_stride, + second_pred, 128, h, h_overflow); } #define HBD_SAD_WXH_AVG_NEON(w, h) \ @@ -464,3 +527,5 @@ HBD_SAD_WXH_AVG_NEON(64, 16) #endif // !CONFIG_REALTIME_ONLY + +#undef HBD_SAD_WXH_AVG_NEON
diff --git a/aom_dsp/arm/highbd_sadxd_neon.c b/aom_dsp/arm/highbd_sadxd_neon.c index 0e43f6d..0b45278 100644 --- a/aom_dsp/arm/highbd_sadxd_neon.c +++ b/aom_dsp/arm/highbd_sadxd_neon.c
@@ -19,11 +19,10 @@ #include "aom_dsp/arm/mem_neon.h" #include "aom_dsp/arm/sum_neon.h" -static inline void highbd_sad4xhx4d_small_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { +static inline void highbd_sad4xhx4d_neon(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); @@ -51,317 +50,261 @@ vst1q_u32(res, horizontal_add_4d_u32x4(sum)); } -static inline void highbd_sad8xhx4d_small_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { +static inline void highbd_sad8xhx4d_neon(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); const uint16_t *ref16_ptr3 = CONVERT_TO_SHORTPTR(ref_ptr[3]); - uint16x8_t sum[4] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0), - vdupq_n_u16(0) }; - uint32x4_t sum_u32[4]; + // 'h_overflow' is the number of 8-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 8-wide rows. + const int h_overflow = 16; + // If block height 'h' is smaller than this limit, use 'h' instead. + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + uint32x4_t sum_u32[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), + vdupq_n_u32(0) }; + + int h_tmp = h_limit; int i = 0; do { - uint16x8_t s = vld1q_u16(src16_ptr + i * src_stride); + uint16x8_t sum_u16[4] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0), + vdupq_n_u16(0) }; - sum[0] = vabaq_u16(sum[0], s, vld1q_u16(ref16_ptr0 + i * ref_stride)); - sum[1] = vabaq_u16(sum[1], s, vld1q_u16(ref16_ptr1 + i * ref_stride)); - sum[2] = vabaq_u16(sum[2], s, vld1q_u16(ref16_ptr2 + i * ref_stride)); - sum[3] = vabaq_u16(sum[3], s, vld1q_u16(ref16_ptr3 + i * ref_stride)); + do { + uint16x8_t s0 = vld1q_u16(src16_ptr + i * src_stride); - } while (++i < h); + sum_u16[0] = + vabaq_u16(sum_u16[0], s0, vld1q_u16(ref16_ptr0 + i * ref_stride)); + sum_u16[1] = + vabaq_u16(sum_u16[1], s0, vld1q_u16(ref16_ptr1 + i * ref_stride)); + sum_u16[2] = + vabaq_u16(sum_u16[2], s0, vld1q_u16(ref16_ptr2 + i * ref_stride)); + sum_u16[3] = + vabaq_u16(sum_u16[3], s0, vld1q_u16(ref16_ptr3 + i * ref_stride)); + } while (++i < h_tmp); - sum_u32[0] = vpaddlq_u16(sum[0]); - sum_u32[1] = vpaddlq_u16(sum[1]); - sum_u32[2] = vpaddlq_u16(sum[2]); - sum_u32[3] = vpaddlq_u16(sum[3]); + sum_u32[0] = vpadalq_u16(sum_u32[0], sum_u16[0]); + sum_u32[1] = vpadalq_u16(sum_u32[1], sum_u16[1]); + sum_u32[2] = vpadalq_u16(sum_u32[2], sum_u16[2]); + sum_u32[3] = vpadalq_u16(sum_u32[3], sum_u16[3]); + + h_tmp += h_limit; + h -= h_limit; + } while (h != 0); + vst1q_u32(res, horizontal_add_4d_u32x4(sum_u32)); } -static inline void sad8_neon(uint16x8_t src, uint16x8_t ref, - uint32x4_t *const sad_sum) { - uint16x8_t abs_diff = vabdq_u16(src, ref); - *sad_sum = vpadalq_u16(*sad_sum, abs_diff); -} - -#if !CONFIG_REALTIME_ONLY -static inline void highbd_sad8xhx4d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { +static inline void highbd_sadwxhx4d_neon(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], int w, + int h, const int h_overflow) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); const uint16_t *ref16_ptr3 = CONVERT_TO_SHORTPTR(ref_ptr[3]); - uint32x4_t sum[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); - int i = 0; + uint32x4_t sum_u32[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), + vdupq_n_u32(0) }; + do { - uint16x8_t s = vld1q_u16(src16_ptr + i * src_stride); - sad8_neon(s, vld1q_u16(ref16_ptr0 + i * ref_stride), &sum[0]); - sad8_neon(s, vld1q_u16(ref16_ptr1 + i * ref_stride), &sum[1]); - sad8_neon(s, vld1q_u16(ref16_ptr2 + i * ref_stride), &sum[2]); - sad8_neon(s, vld1q_u16(ref16_ptr3 + i * ref_stride), &sum[3]); + uint16x8_t sum_u16[4] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0), + vdupq_n_u16(0) }; - } while (++i < h); - - vst1q_u32(res, horizontal_add_4d_u32x4(sum)); -} -#endif // !CONFIG_REALTIME_ONLY - -static inline void highbd_sad16xhx4d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); - const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); - const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - const uint16_t *ref16_ptr3 = CONVERT_TO_SHORTPTR(ref_ptr[3]); - - uint32x4_t sum_lo[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; - uint32x4_t sum_hi[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; - uint32x4_t sum[4]; - - int i = 0; - do { - uint16x8_t s0 = vld1q_u16(src16_ptr + i * src_stride); - sad8_neon(s0, vld1q_u16(ref16_ptr0 + i * ref_stride), &sum_lo[0]); - sad8_neon(s0, vld1q_u16(ref16_ptr1 + i * ref_stride), &sum_lo[1]); - sad8_neon(s0, vld1q_u16(ref16_ptr2 + i * ref_stride), &sum_lo[2]); - sad8_neon(s0, vld1q_u16(ref16_ptr3 + i * ref_stride), &sum_lo[3]); - - uint16x8_t s1 = vld1q_u16(src16_ptr + i * src_stride + 8); - sad8_neon(s1, vld1q_u16(ref16_ptr0 + i * ref_stride + 8), &sum_hi[0]); - sad8_neon(s1, vld1q_u16(ref16_ptr1 + i * ref_stride + 8), &sum_hi[1]); - sad8_neon(s1, vld1q_u16(ref16_ptr2 + i * ref_stride + 8), &sum_hi[2]); - sad8_neon(s1, vld1q_u16(ref16_ptr3 + i * ref_stride + 8), &sum_hi[3]); - - } while (++i < h); - - sum[0] = vaddq_u32(sum_lo[0], sum_hi[0]); - sum[1] = vaddq_u32(sum_lo[1], sum_hi[1]); - sum[2] = vaddq_u32(sum_lo[2], sum_hi[2]); - sum[3] = vaddq_u32(sum_lo[3], sum_hi[3]); - - vst1q_u32(res, horizontal_add_4d_u32x4(sum)); -} - -static inline void highbd_sadwxhx4d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int w, int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); - const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); - const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - const uint16_t *ref16_ptr3 = CONVERT_TO_SHORTPTR(ref_ptr[3]); - - uint32x4_t sum_lo[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; - uint32x4_t sum_hi[4] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0), - vdupq_n_u32(0) }; - uint32x4_t sum[4]; - - int i = 0; - do { - int j = 0; + int i = h_limit; do { - uint16x8_t s0 = vld1q_u16(src16_ptr + i * src_stride + j); - sad8_neon(s0, vld1q_u16(ref16_ptr0 + i * ref_stride + j), &sum_lo[0]); - sad8_neon(s0, vld1q_u16(ref16_ptr1 + i * ref_stride + j), &sum_lo[1]); - sad8_neon(s0, vld1q_u16(ref16_ptr2 + i * ref_stride + j), &sum_lo[2]); - sad8_neon(s0, vld1q_u16(ref16_ptr3 + i * ref_stride + j), &sum_lo[3]); + int j = 0; + do { + uint16x8_t s0 = vld1q_u16(src16_ptr + j); - uint16x8_t s1 = vld1q_u16(src16_ptr + i * src_stride + j + 8); - sad8_neon(s1, vld1q_u16(ref16_ptr0 + i * ref_stride + j + 8), &sum_hi[0]); - sad8_neon(s1, vld1q_u16(ref16_ptr1 + i * ref_stride + j + 8), &sum_hi[1]); - sad8_neon(s1, vld1q_u16(ref16_ptr2 + i * ref_stride + j + 8), &sum_hi[2]); - sad8_neon(s1, vld1q_u16(ref16_ptr3 + i * ref_stride + j + 8), &sum_hi[3]); + sum_u16[0] = vabaq_u16(sum_u16[0], s0, vld1q_u16(ref16_ptr0 + j)); + sum_u16[1] = vabaq_u16(sum_u16[1], s0, vld1q_u16(ref16_ptr1 + j)); + sum_u16[2] = vabaq_u16(sum_u16[2], s0, vld1q_u16(ref16_ptr2 + j)); + sum_u16[3] = vabaq_u16(sum_u16[3], s0, vld1q_u16(ref16_ptr3 + j)); - uint16x8_t s2 = vld1q_u16(src16_ptr + i * src_stride + j + 16); - sad8_neon(s2, vld1q_u16(ref16_ptr0 + i * ref_stride + j + 16), - &sum_lo[0]); - sad8_neon(s2, vld1q_u16(ref16_ptr1 + i * ref_stride + j + 16), - &sum_lo[1]); - sad8_neon(s2, vld1q_u16(ref16_ptr2 + i * ref_stride + j + 16), - &sum_lo[2]); - sad8_neon(s2, vld1q_u16(ref16_ptr3 + i * ref_stride + j + 16), - &sum_lo[3]); + uint16x8_t s1 = vld1q_u16(src16_ptr + j + 8); + sum_u16[0] = vabaq_u16(sum_u16[0], s1, vld1q_u16(ref16_ptr0 + j + 8)); + sum_u16[1] = vabaq_u16(sum_u16[1], s1, vld1q_u16(ref16_ptr1 + j + 8)); + sum_u16[2] = vabaq_u16(sum_u16[2], s1, vld1q_u16(ref16_ptr2 + j + 8)); + sum_u16[3] = vabaq_u16(sum_u16[3], s1, vld1q_u16(ref16_ptr3 + j + 8)); - uint16x8_t s3 = vld1q_u16(src16_ptr + i * src_stride + j + 24); - sad8_neon(s3, vld1q_u16(ref16_ptr0 + i * ref_stride + j + 24), - &sum_hi[0]); - sad8_neon(s3, vld1q_u16(ref16_ptr1 + i * ref_stride + j + 24), - &sum_hi[1]); - sad8_neon(s3, vld1q_u16(ref16_ptr2 + i * ref_stride + j + 24), - &sum_hi[2]); - sad8_neon(s3, vld1q_u16(ref16_ptr3 + i * ref_stride + j + 24), - &sum_hi[3]); + j += 16; + } while (j < w); - j += 32; - } while (j < w); + src16_ptr += src_stride; + ref16_ptr0 += ref_stride; + ref16_ptr1 += ref_stride; + ref16_ptr2 += ref_stride; + ref16_ptr3 += ref_stride; + } while (--i != 0); - } while (++i < h); + sum_u32[0] = vpadalq_u16(sum_u32[0], sum_u16[0]); + sum_u32[1] = vpadalq_u16(sum_u32[1], sum_u16[1]); + sum_u32[2] = vpadalq_u16(sum_u32[2], sum_u16[2]); + sum_u32[3] = vpadalq_u16(sum_u32[3], sum_u16[3]); - sum[0] = vaddq_u32(sum_lo[0], sum_hi[0]); - sum[1] = vaddq_u32(sum_lo[1], sum_hi[1]); - sum[2] = vaddq_u32(sum_lo[2], sum_hi[2]); - sum[3] = vaddq_u32(sum_lo[3], sum_hi[3]); + h -= h_limit; + } while (h != 0); - vst1q_u32(res, horizontal_add_4d_u32x4(sum)); + vst1q_u32(res, horizontal_add_4d_u32x4(sum_u32)); } -static inline void highbd_sad128xhx4d_large_neon( - const uint8_t *src_ptr, int src_stride, const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], int h) { - highbd_sadwxhx4d_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, - 128, h); +static inline void highbd_sad16xhx4d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 16-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 8 16-wide rows. + const int h_overflow = 8; + highbd_sadwxhx4d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 16, h, + h_overflow); } -static inline void highbd_sad64xhx4d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - highbd_sadwxhx4d_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 64, - h); +static inline void highbd_sad32xhx4d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 32-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 4 32-wide rows. + const int h_overflow = 4; + highbd_sadwxhx4d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 32, h, + h_overflow); } -static inline void highbd_sad32xhx4d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - highbd_sadwxhx4d_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 32, - h); +static inline void highbd_sad64xhx4d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 64-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 2 64-wide rows. + const int h_overflow = 2; + highbd_sadwxhx4d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 64, h, + h_overflow); } -#define HBD_SAD_WXH_4D_SMALL_NEON(w, h) \ +static inline void highbd_sad128xhx4d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 128-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 1 128-wide rows. + const int h_overflow = 1; + highbd_sadwxhx4d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 128, h, + h_overflow); +} + +#define HBD_SAD_WXH_4D_NEON(w, h) \ void aom_highbd_sad##w##x##h##x4d_neon( \ const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ int ref_stride, uint32_t sad_array[4]) { \ - highbd_sad##w##xhx4d_small_neon(src, src_stride, ref_array, ref_stride, \ - sad_array, (h)); \ + highbd_sad##w##xhx4d_neon(src, src_stride, ref_array, ref_stride, \ + sad_array, (h)); \ } -#define HBD_SAD_WXH_4D_LARGE_NEON(w, h) \ - void aom_highbd_sad##w##x##h##x4d_neon( \ - const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ - int ref_stride, uint32_t sad_array[4]) { \ - highbd_sad##w##xhx4d_large_neon(src, src_stride, ref_array, ref_stride, \ - sad_array, (h)); \ - } +HBD_SAD_WXH_4D_NEON(4, 4) +HBD_SAD_WXH_4D_NEON(4, 8) -HBD_SAD_WXH_4D_SMALL_NEON(4, 4) -HBD_SAD_WXH_4D_SMALL_NEON(4, 8) +HBD_SAD_WXH_4D_NEON(8, 4) +HBD_SAD_WXH_4D_NEON(8, 8) +HBD_SAD_WXH_4D_NEON(8, 16) -HBD_SAD_WXH_4D_SMALL_NEON(8, 4) -HBD_SAD_WXH_4D_SMALL_NEON(8, 8) -HBD_SAD_WXH_4D_SMALL_NEON(8, 16) +HBD_SAD_WXH_4D_NEON(16, 8) +HBD_SAD_WXH_4D_NEON(16, 16) +HBD_SAD_WXH_4D_NEON(16, 32) -HBD_SAD_WXH_4D_LARGE_NEON(16, 8) -HBD_SAD_WXH_4D_LARGE_NEON(16, 16) -HBD_SAD_WXH_4D_LARGE_NEON(16, 32) +HBD_SAD_WXH_4D_NEON(32, 16) +HBD_SAD_WXH_4D_NEON(32, 32) +HBD_SAD_WXH_4D_NEON(32, 64) -HBD_SAD_WXH_4D_LARGE_NEON(32, 16) -HBD_SAD_WXH_4D_LARGE_NEON(32, 32) -HBD_SAD_WXH_4D_LARGE_NEON(32, 64) +HBD_SAD_WXH_4D_NEON(64, 32) +HBD_SAD_WXH_4D_NEON(64, 64) +HBD_SAD_WXH_4D_NEON(64, 128) -HBD_SAD_WXH_4D_LARGE_NEON(64, 32) -HBD_SAD_WXH_4D_LARGE_NEON(64, 64) -HBD_SAD_WXH_4D_LARGE_NEON(64, 128) - -HBD_SAD_WXH_4D_LARGE_NEON(128, 64) -HBD_SAD_WXH_4D_LARGE_NEON(128, 128) +HBD_SAD_WXH_4D_NEON(128, 64) +HBD_SAD_WXH_4D_NEON(128, 128) #if !CONFIG_REALTIME_ONLY -HBD_SAD_WXH_4D_SMALL_NEON(4, 16) +HBD_SAD_WXH_4D_NEON(4, 16) -HBD_SAD_WXH_4D_LARGE_NEON(8, 32) +HBD_SAD_WXH_4D_NEON(8, 32) -HBD_SAD_WXH_4D_LARGE_NEON(16, 4) -HBD_SAD_WXH_4D_LARGE_NEON(16, 64) +HBD_SAD_WXH_4D_NEON(16, 4) +HBD_SAD_WXH_4D_NEON(16, 64) -HBD_SAD_WXH_4D_LARGE_NEON(32, 8) +HBD_SAD_WXH_4D_NEON(32, 8) -HBD_SAD_WXH_4D_LARGE_NEON(64, 16) +HBD_SAD_WXH_4D_NEON(64, 16) #endif // !CONFIG_REALTIME_ONLY -#define HBD_SAD_SKIP_WXH_4D_SMALL_NEON(w, h) \ - void aom_highbd_sad_skip_##w##x##h##x4d_neon( \ - const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ - int ref_stride, uint32_t sad_array[4]) { \ - highbd_sad##w##xhx4d_small_neon(src, 2 * src_stride, ref_array, \ - 2 * ref_stride, sad_array, ((h) >> 1)); \ - sad_array[0] <<= 1; \ - sad_array[1] <<= 1; \ - sad_array[2] <<= 1; \ - sad_array[3] <<= 1; \ +#undef HBD_SAD_WXH_4D_NEON + +#define HBD_SAD_SKIP_WXH_4D_NEON(w, h) \ + void aom_highbd_sad_skip_##w##x##h##x4d_neon( \ + const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ + int ref_stride, uint32_t sad_array[4]) { \ + highbd_sad##w##xhx4d_neon(src, 2 * src_stride, ref_array, 2 * ref_stride, \ + sad_array, ((h) >> 1)); \ + sad_array[0] <<= 1; \ + sad_array[1] <<= 1; \ + sad_array[2] <<= 1; \ + sad_array[3] <<= 1; \ } -#define HBD_SAD_SKIP_WXH_4D_LARGE_NEON(w, h) \ - void aom_highbd_sad_skip_##w##x##h##x4d_neon( \ - const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ - int ref_stride, uint32_t sad_array[4]) { \ - highbd_sad##w##xhx4d_large_neon(src, 2 * src_stride, ref_array, \ - 2 * ref_stride, sad_array, ((h) >> 1)); \ - sad_array[0] <<= 1; \ - sad_array[1] <<= 1; \ - sad_array[2] <<= 1; \ - sad_array[3] <<= 1; \ - } +HBD_SAD_SKIP_WXH_4D_NEON(8, 16) -HBD_SAD_SKIP_WXH_4D_SMALL_NEON(8, 16) +HBD_SAD_SKIP_WXH_4D_NEON(16, 16) +HBD_SAD_SKIP_WXH_4D_NEON(16, 32) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(16, 16) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(16, 32) +HBD_SAD_SKIP_WXH_4D_NEON(32, 16) +HBD_SAD_SKIP_WXH_4D_NEON(32, 32) +HBD_SAD_SKIP_WXH_4D_NEON(32, 64) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(32, 16) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(32, 32) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(32, 64) +HBD_SAD_SKIP_WXH_4D_NEON(64, 32) +HBD_SAD_SKIP_WXH_4D_NEON(64, 64) +HBD_SAD_SKIP_WXH_4D_NEON(64, 128) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(64, 32) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(64, 64) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(64, 128) - -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(128, 64) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(128, 128) +HBD_SAD_SKIP_WXH_4D_NEON(128, 64) +HBD_SAD_SKIP_WXH_4D_NEON(128, 128) #if !CONFIG_REALTIME_ONLY -HBD_SAD_SKIP_WXH_4D_SMALL_NEON(4, 16) +HBD_SAD_SKIP_WXH_4D_NEON(4, 16) -HBD_SAD_SKIP_WXH_4D_SMALL_NEON(8, 32) +HBD_SAD_SKIP_WXH_4D_NEON(8, 32) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(16, 64) +HBD_SAD_SKIP_WXH_4D_NEON(16, 64) -HBD_SAD_SKIP_WXH_4D_LARGE_NEON(64, 16) +HBD_SAD_SKIP_WXH_4D_NEON(64, 16) #endif // !CONFIG_REALTIME_ONLY -static inline void highbd_sad4xhx3d_small_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { +#undef HBD_SAD_SKIP_WXH_4D_NEON + +static inline void highbd_sad4xhx3d_neon(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - uint32x4_t sum[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; int i = 0; @@ -382,227 +325,198 @@ res[2] = horizontal_add_u32x4(sum[2]); } -static inline void highbd_sad8xhx3d_small_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { +static inline void highbd_sad8xhx3d_neon(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - uint16x8_t sum[3] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0) }; + // 'h_overflow' is the number of 8-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 16 8-wide rows. + const int h_overflow = 16; + // If block height 'h' is smaller than this limit, use 'h' instead. + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + uint32x4_t sum_u32[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; + + int h_tmp = h_limit; int i = 0; do { - uint16x8_t s = vld1q_u16(src16_ptr + i * src_stride); - - sum[0] = vabaq_u16(sum[0], s, vld1q_u16(ref16_ptr0 + i * ref_stride)); - sum[1] = vabaq_u16(sum[1], s, vld1q_u16(ref16_ptr1 + i * ref_stride)); - sum[2] = vabaq_u16(sum[2], s, vld1q_u16(ref16_ptr2 + i * ref_stride)); - - } while (++i < h); - - res[0] = horizontal_add_u32x4(vpaddlq_u16(sum[0])); - res[1] = horizontal_add_u32x4(vpaddlq_u16(sum[1])); - res[2] = horizontal_add_u32x4(vpaddlq_u16(sum[2])); -} - -#if !CONFIG_REALTIME_ONLY -static inline void highbd_sad8xhx3d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); - const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); - const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - - uint32x4_t sum[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; - - int i = 0; - do { - uint16x8_t s = vld1q_u16(src16_ptr + i * src_stride); - uint16x8_t r0 = vld1q_u16(ref16_ptr0 + i * ref_stride); - uint16x8_t r1 = vld1q_u16(ref16_ptr1 + i * ref_stride); - uint16x8_t r2 = vld1q_u16(ref16_ptr2 + i * ref_stride); - - sad8_neon(s, r0, &sum[0]); - sad8_neon(s, r1, &sum[1]); - sad8_neon(s, r2, &sum[2]); - - } while (++i < h); - - res[0] = horizontal_add_u32x4(sum[0]); - res[1] = horizontal_add_u32x4(sum[1]); - res[2] = horizontal_add_u32x4(sum[2]); -} -#endif // !CONFIG_REALTIME_ONLY - -static inline void highbd_sad16xhx3d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); - const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); - const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - - uint32x4_t sum_lo[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; - uint32x4_t sum_hi[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; - - int i = 0; - do { - uint16x8_t s0 = vld1q_u16(src16_ptr + i * src_stride); - sad8_neon(s0, vld1q_u16(ref16_ptr0 + i * ref_stride), &sum_lo[0]); - sad8_neon(s0, vld1q_u16(ref16_ptr1 + i * ref_stride), &sum_lo[1]); - sad8_neon(s0, vld1q_u16(ref16_ptr2 + i * ref_stride), &sum_lo[2]); - - uint16x8_t s1 = vld1q_u16(src16_ptr + i * src_stride + 8); - sad8_neon(s1, vld1q_u16(ref16_ptr0 + i * ref_stride + 8), &sum_hi[0]); - sad8_neon(s1, vld1q_u16(ref16_ptr1 + i * ref_stride + 8), &sum_hi[1]); - sad8_neon(s1, vld1q_u16(ref16_ptr2 + i * ref_stride + 8), &sum_hi[2]); - - } while (++i < h); - - res[0] = horizontal_add_u32x4(vaddq_u32(sum_lo[0], sum_hi[0])); - res[1] = horizontal_add_u32x4(vaddq_u32(sum_lo[1], sum_hi[1])); - res[2] = horizontal_add_u32x4(vaddq_u32(sum_lo[2], sum_hi[2])); -} - -static inline void highbd_sadwxhx3d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int w, int h) { - const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); - const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); - const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); - const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); - - uint32x4_t sum_lo[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; - uint32x4_t sum_hi[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; - uint32x4_t sum[3]; - - int i = 0; - do { - int j = 0; + uint16x8_t sum_u16[3] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0) }; do { - uint16x8_t s0 = vld1q_u16(src16_ptr + i * src_stride + j); - sad8_neon(s0, vld1q_u16(ref16_ptr0 + i * ref_stride + j), &sum_lo[0]); - sad8_neon(s0, vld1q_u16(ref16_ptr1 + i * ref_stride + j), &sum_lo[1]); - sad8_neon(s0, vld1q_u16(ref16_ptr2 + i * ref_stride + j), &sum_lo[2]); + uint16x8_t s0 = vld1q_u16(src16_ptr + i * src_stride); - uint16x8_t s1 = vld1q_u16(src16_ptr + i * src_stride + j + 8); - sad8_neon(s1, vld1q_u16(ref16_ptr0 + i * ref_stride + j + 8), &sum_hi[0]); - sad8_neon(s1, vld1q_u16(ref16_ptr1 + i * ref_stride + j + 8), &sum_hi[1]); - sad8_neon(s1, vld1q_u16(ref16_ptr2 + i * ref_stride + j + 8), &sum_hi[2]); + sum_u16[0] = + vabaq_u16(sum_u16[0], s0, vld1q_u16(ref16_ptr0 + i * ref_stride)); + sum_u16[1] = + vabaq_u16(sum_u16[1], s0, vld1q_u16(ref16_ptr1 + i * ref_stride)); + sum_u16[2] = + vabaq_u16(sum_u16[2], s0, vld1q_u16(ref16_ptr2 + i * ref_stride)); + } while (++i < h_tmp); - uint16x8_t s2 = vld1q_u16(src16_ptr + i * src_stride + j + 16); - sad8_neon(s2, vld1q_u16(ref16_ptr0 + i * ref_stride + j + 16), - &sum_lo[0]); - sad8_neon(s2, vld1q_u16(ref16_ptr1 + i * ref_stride + j + 16), - &sum_lo[1]); - sad8_neon(s2, vld1q_u16(ref16_ptr2 + i * ref_stride + j + 16), - &sum_lo[2]); + sum_u32[0] = vpadalq_u16(sum_u32[0], sum_u16[0]); + sum_u32[1] = vpadalq_u16(sum_u32[1], sum_u16[1]); + sum_u32[2] = vpadalq_u16(sum_u32[2], sum_u16[2]); - uint16x8_t s3 = vld1q_u16(src16_ptr + i * src_stride + j + 24); - sad8_neon(s3, vld1q_u16(ref16_ptr0 + i * ref_stride + j + 24), - &sum_hi[0]); - sad8_neon(s3, vld1q_u16(ref16_ptr1 + i * ref_stride + j + 24), - &sum_hi[1]); - sad8_neon(s3, vld1q_u16(ref16_ptr2 + i * ref_stride + j + 24), - &sum_hi[2]); + h_tmp += h_limit; + h -= h_limit; + } while (h != 0); - j += 32; - } while (j < w); - - } while (++i < h); - - sum[0] = vaddq_u32(sum_lo[0], sum_hi[0]); - sum[1] = vaddq_u32(sum_lo[1], sum_hi[1]); - sum[2] = vaddq_u32(sum_lo[2], sum_hi[2]); - - res[0] = horizontal_add_u32x4(sum[0]); - res[1] = horizontal_add_u32x4(sum[1]); - res[2] = horizontal_add_u32x4(sum[2]); + res[0] = horizontal_add_u32x4(sum_u32[0]); + res[1] = horizontal_add_u32x4(sum_u32[1]); + res[2] = horizontal_add_u32x4(sum_u32[2]); } -static inline void highbd_sad128xhx3d_large_neon( - const uint8_t *src_ptr, int src_stride, const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], int h) { - highbd_sadwxhx3d_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, - 128, h); +static inline void highbd_sadwxhx3d_neon(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], int w, + int h, const int h_overflow) { + const uint16_t *src16_ptr = CONVERT_TO_SHORTPTR(src_ptr); + const uint16_t *ref16_ptr0 = CONVERT_TO_SHORTPTR(ref_ptr[0]); + const uint16_t *ref16_ptr1 = CONVERT_TO_SHORTPTR(ref_ptr[1]); + const uint16_t *ref16_ptr2 = CONVERT_TO_SHORTPTR(ref_ptr[2]); + uint32x4_t sum_u32[3] = { vdupq_n_u32(0), vdupq_n_u32(0), vdupq_n_u32(0) }; + + const int h_limit = h < h_overflow ? h : h_overflow; + assert(h % h_limit == 0); + + do { + uint16x8_t sum_u16[3] = { vdupq_n_u16(0), vdupq_n_u16(0), vdupq_n_u16(0) }; + + int i = h_limit; + do { + int j = 0; + do { + uint16x8_t s0 = vld1q_u16(src16_ptr + j); + + sum_u16[0] = vabaq_u16(sum_u16[0], s0, vld1q_u16(ref16_ptr0 + j)); + sum_u16[1] = vabaq_u16(sum_u16[1], s0, vld1q_u16(ref16_ptr1 + j)); + sum_u16[2] = vabaq_u16(sum_u16[2], s0, vld1q_u16(ref16_ptr2 + j)); + + uint16x8_t s1 = vld1q_u16(src16_ptr + j + 8); + sum_u16[0] = vabaq_u16(sum_u16[0], s1, vld1q_u16(ref16_ptr0 + j + 8)); + sum_u16[1] = vabaq_u16(sum_u16[1], s1, vld1q_u16(ref16_ptr1 + j + 8)); + sum_u16[2] = vabaq_u16(sum_u16[2], s1, vld1q_u16(ref16_ptr2 + j + 8)); + + j += 16; + } while (j < w); + + src16_ptr += src_stride; + ref16_ptr0 += ref_stride; + ref16_ptr1 += ref_stride; + ref16_ptr2 += ref_stride; + } while (--i != 0); + + sum_u32[0] = vpadalq_u16(sum_u32[0], sum_u16[0]); + sum_u32[1] = vpadalq_u16(sum_u32[1], sum_u16[1]); + sum_u32[2] = vpadalq_u16(sum_u32[2], sum_u16[2]); + + h -= h_limit; + } while (h != 0); + + res[0] = horizontal_add_u32x4(sum_u32[0]); + res[1] = horizontal_add_u32x4(sum_u32[1]); + res[2] = horizontal_add_u32x4(sum_u32[2]); } -static inline void highbd_sad64xhx3d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - highbd_sadwxhx3d_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 64, - h); +static inline void highbd_sad16xhx3d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 16-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 8 16-wide rows. + const int h_overflow = 8; + highbd_sadwxhx3d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 16, h, + h_overflow); } -static inline void highbd_sad32xhx3d_large_neon(const uint8_t *src_ptr, - int src_stride, - const uint8_t *const ref_ptr[4], - int ref_stride, uint32_t res[4], - int h) { - highbd_sadwxhx3d_large_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 32, - h); +static inline void highbd_sad32xhx3d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 32-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 4 32-wide rows. + const int h_overflow = 4; + highbd_sadwxhx3d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 32, h, + h_overflow); } -#define HBD_SAD_WXH_3D_SMALL_NEON(w, h) \ +static inline void highbd_sad64xhx3d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 64-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 2 64-wide rows. + const int h_overflow = 2; + highbd_sadwxhx3d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 64, h, + h_overflow); +} + +static inline void highbd_sad128xhx3d_neon(const uint8_t *src_ptr, + int src_stride, + const uint8_t *const ref_ptr[4], + int ref_stride, uint32_t res[4], + int h) { + // 'h_overflow' is the number of 128-wide rows we can process before 16-bit + // accumulators overflow. After hitting this limit accumulate into 32-bit + // elements. 65535 / 4095 ~= 16, so 1 128-wide rows. + const int h_overflow = 1; + highbd_sadwxhx3d_neon(src_ptr, src_stride, ref_ptr, ref_stride, res, 128, h, + h_overflow); +} + +#define HBD_SAD_WXH_3D_NEON(w, h) \ void aom_highbd_sad##w##x##h##x3d_neon( \ const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ int ref_stride, uint32_t sad_array[4]) { \ - highbd_sad##w##xhx3d_small_neon(src, src_stride, ref_array, ref_stride, \ - sad_array, (h)); \ + highbd_sad##w##xhx3d_neon(src, src_stride, ref_array, ref_stride, \ + sad_array, (h)); \ } -#define HBD_SAD_WXH_3D_LARGE_NEON(w, h) \ - void aom_highbd_sad##w##x##h##x3d_neon( \ - const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ - int ref_stride, uint32_t sad_array[4]) { \ - highbd_sad##w##xhx3d_large_neon(src, src_stride, ref_array, ref_stride, \ - sad_array, (h)); \ - } +HBD_SAD_WXH_3D_NEON(4, 4) +HBD_SAD_WXH_3D_NEON(4, 8) -HBD_SAD_WXH_3D_SMALL_NEON(4, 4) -HBD_SAD_WXH_3D_SMALL_NEON(4, 8) +HBD_SAD_WXH_3D_NEON(8, 4) +HBD_SAD_WXH_3D_NEON(8, 8) +HBD_SAD_WXH_3D_NEON(8, 16) -HBD_SAD_WXH_3D_SMALL_NEON(8, 4) -HBD_SAD_WXH_3D_SMALL_NEON(8, 8) -HBD_SAD_WXH_3D_SMALL_NEON(8, 16) +HBD_SAD_WXH_3D_NEON(16, 8) +HBD_SAD_WXH_3D_NEON(16, 16) +HBD_SAD_WXH_3D_NEON(16, 32) -HBD_SAD_WXH_3D_LARGE_NEON(16, 8) -HBD_SAD_WXH_3D_LARGE_NEON(16, 16) -HBD_SAD_WXH_3D_LARGE_NEON(16, 32) +HBD_SAD_WXH_3D_NEON(32, 16) +HBD_SAD_WXH_3D_NEON(32, 32) +HBD_SAD_WXH_3D_NEON(32, 64) -HBD_SAD_WXH_3D_LARGE_NEON(32, 16) -HBD_SAD_WXH_3D_LARGE_NEON(32, 32) -HBD_SAD_WXH_3D_LARGE_NEON(32, 64) +HBD_SAD_WXH_3D_NEON(64, 32) +HBD_SAD_WXH_3D_NEON(64, 64) +HBD_SAD_WXH_3D_NEON(64, 128) -HBD_SAD_WXH_3D_LARGE_NEON(64, 32) -HBD_SAD_WXH_3D_LARGE_NEON(64, 64) -HBD_SAD_WXH_3D_LARGE_NEON(64, 128) - -HBD_SAD_WXH_3D_LARGE_NEON(128, 64) -HBD_SAD_WXH_3D_LARGE_NEON(128, 128) +HBD_SAD_WXH_3D_NEON(128, 64) +HBD_SAD_WXH_3D_NEON(128, 128) #if !CONFIG_REALTIME_ONLY -HBD_SAD_WXH_3D_SMALL_NEON(4, 16) +HBD_SAD_WXH_3D_NEON(4, 16) -HBD_SAD_WXH_3D_LARGE_NEON(8, 32) +HBD_SAD_WXH_3D_NEON(8, 32) -HBD_SAD_WXH_3D_LARGE_NEON(16, 4) -HBD_SAD_WXH_3D_LARGE_NEON(16, 64) +HBD_SAD_WXH_3D_NEON(16, 4) +HBD_SAD_WXH_3D_NEON(16, 64) -HBD_SAD_WXH_3D_LARGE_NEON(32, 8) +HBD_SAD_WXH_3D_NEON(32, 8) -HBD_SAD_WXH_3D_LARGE_NEON(64, 16) +HBD_SAD_WXH_3D_NEON(64, 16) #endif // !CONFIG_REALTIME_ONLY + +#undef HBD_SAD_WXH_3D_NEON
diff --git a/aom_dsp/arm/intrapred_neon.c b/aom_dsp/arm/intrapred_neon.c index f4f7591..be63638 100644 --- a/aom_dsp/arm/intrapred_neon.c +++ b/aom_dsp/arm/intrapred_neon.c
@@ -1170,132 +1170,115 @@ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, }; -static AOM_FORCE_INLINE void dr_prediction_z1_HxW_internal_neon_64( - int H, int W, uint8x8_t *dst, const uint8_t *above, int upsample_above, +static AOM_FORCE_INLINE void dr_prediction_z1_WxH_internal_neon_small( + int W, int H, uint8x8_t *dst, const uint8_t *above, int upsample_above, int dx) { const int frac_bits = 6 - upsample_above; const int max_base_x = ((W + H) - 1) << upsample_above; assert(dx > 0); - // pre-filter above pixels - // store in temp buffers: - // above[x] * 32 + 16 - // above[x+1] - above[x] - // final pixels will be calculated as: - // (above[x] * 32 + 16 + (above[x+1] - above[x]) * shift) >> 5 + // above0 * (32 - shift) + above1 * shift + // = (above1 - above0) * shift + above0 * 32 const uint8x8_t a_mbase_x = vdup_n_u8(above[max_base_x]); int x = dx; - for (int r = 0; r < W; r++) { + for (int r = 0; r < H; r++) { int base = x >> frac_bits; int base_max_diff = (max_base_x - base) >> upsample_above; if (base_max_diff <= 0) { - for (int i = r; i < W; ++i) { + for (int i = r; i < H; ++i) { dst[i] = a_mbase_x; // save 4 values } return; } - if (base_max_diff > H) base_max_diff = H; + if (base_max_diff > W) base_max_diff = W; uint8x8x2_t a01_128; uint16x8_t shift; if (upsample_above) { a01_128 = vld2_u8(above + base); - shift = vdupq_n_u16(((x << upsample_above) & 0x3f) >> 1); + shift = vdupq_n_u16(x & 0x1f); // ((x << upsample_above) & 0x3f) >> 1 } else { a01_128.val[0] = vld1_u8(above + base); a01_128.val[1] = vld1_u8(above + base + 1); shift = vdupq_n_u16((x & 0x3f) >> 1); } uint16x8_t diff = vsubl_u8(a01_128.val[1], a01_128.val[0]); - uint16x8_t a32 = vmlal_u8(vdupq_n_u16(16), a01_128.val[0], vdup_n_u8(32)); + uint16x8_t a32 = vshll_n_u8(a01_128.val[0], 5); uint16x8_t res = vmlaq_u16(a32, diff, shift); uint8x8_t mask = vld1_u8(BaseMask[base_max_diff]); - dst[r] = vbsl_u8(mask, vshrn_n_u16(res, 5), a_mbase_x); + dst[r] = vbsl_u8(mask, vrshrn_n_u16(res, 5), a_mbase_x); x += dx; } } -static void dr_prediction_z1_4xN_neon(int N, uint8_t *dst, ptrdiff_t stride, +static void dr_prediction_z1_4xH_neon(int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, int upsample_above, int dx) { uint8x8_t dstvec[16]; - dr_prediction_z1_HxW_internal_neon_64(4, N, dstvec, above, upsample_above, - dx); - for (int i = 0; i < N; i++) { + dr_prediction_z1_WxH_internal_neon_small(4, H, dstvec, above, upsample_above, + dx); + for (int i = 0; i < H; i++) { vst1_lane_u32((uint32_t *)(dst + stride * i), vreinterpret_u32_u8(dstvec[i]), 0); } } -static void dr_prediction_z1_8xN_neon(int N, uint8_t *dst, ptrdiff_t stride, +static void dr_prediction_z1_8xH_neon(int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, int upsample_above, int dx) { uint8x8_t dstvec[32]; - dr_prediction_z1_HxW_internal_neon_64(8, N, dstvec, above, upsample_above, - dx); - for (int i = 0; i < N; i++) { + dr_prediction_z1_WxH_internal_neon_small(8, H, dstvec, above, upsample_above, + dx); + for (int i = 0; i < H; i++) { vst1_u8(dst + stride * i, dstvec[i]); } } -static AOM_FORCE_INLINE void dr_prediction_z1_HxW_internal_neon( - int H, int W, uint8x16_t *dst, const uint8_t *above, int upsample_above, - int dx) { - const int frac_bits = 6 - upsample_above; - const int max_base_x = ((W + H) - 1) << upsample_above; +static AOM_FORCE_INLINE void dr_prediction_z1_WxH_internal_neon_large( + int W, int H, uint8x16_t *dst, const uint8_t *above, int dx) { + // Here upsample_above is 0 by design of av1_use_intra_edge_upsample defined + // in av1/common/reconintra.h. + const int frac_bits = 6; + const int max_base_x = (W + H) - 1; assert(dx > 0); - // pre-filter above pixels - // store in temp buffers: - // above[x] * 32 + 16 - // above[x+1] - above[x] - // final pixels will be calculated as: - // (above[x] * 32 + 16 + (above[x+1] - above[x]) * shift) >> 5 + // above0 * (32 - shift) + above1 * shift + // = (above1 - above0) * shift + above0 * 32 const uint8x16_t a_mbase_x = vdupq_n_u8(above[max_base_x]); int x = dx; - for (int r = 0; r < W; r++) { + for (int r = 0; r < H; r++) { int base = x >> frac_bits; - int base_max_diff = (max_base_x - base) >> upsample_above; + int base_max_diff = max_base_x - base; if (base_max_diff <= 0) { - for (int i = r; i < W; ++i) { + for (int i = r; i < H; ++i) { dst[i] = a_mbase_x; // save 4 values } return; } - if (base_max_diff > H) base_max_diff = H; + if (base_max_diff > W) base_max_diff = W; - uint16x8_t shift; - uint8x16_t a0_128, a1_128; - if (upsample_above) { - uint8x8x2_t v_tmp_a0_128 = vld2_u8(above + base); - a0_128 = vcombine_u8(v_tmp_a0_128.val[0], v_tmp_a0_128.val[1]); - a1_128 = vextq_u8(a0_128, vdupq_n_u8(0), 8); - shift = vdupq_n_u16(x & 0x1f); - } else { - a0_128 = vld1q_u8(above + base); - a1_128 = vld1q_u8(above + base + 1); - shift = vdupq_n_u16((x & 0x3f) >> 1); - } + uint8x16_t a0_128 = vld1q_u8(above + base); + uint8x16_t a1_128 = vld1q_u8(above + base + 1); + uint16x8_t shift = vdupq_n_u16((x & 0x3f) >> 1); + uint16x8_t diff_lo = vsubl_u8(vget_low_u8(a1_128), vget_low_u8(a0_128)); uint16x8_t diff_hi = vsubl_u8(vget_high_u8(a1_128), vget_high_u8(a0_128)); - uint16x8_t a32_lo = - vmlal_u8(vdupq_n_u16(16), vget_low_u8(a0_128), vdup_n_u8(32)); - uint16x8_t a32_hi = - vmlal_u8(vdupq_n_u16(16), vget_high_u8(a0_128), vdup_n_u8(32)); + uint16x8_t a32_lo = vshll_n_u8(vget_low_u8(a0_128), 5); + uint16x8_t a32_hi = vshll_n_u8(vget_high_u8(a0_128), 5); uint16x8_t res_lo = vmlaq_u16(a32_lo, diff_lo, shift); uint16x8_t res_hi = vmlaq_u16(a32_hi, diff_hi, shift); uint8x16_t v_temp = - vcombine_u8(vshrn_n_u16(res_lo, 5), vshrn_n_u16(res_hi, 5)); + vcombine_u8(vrshrn_n_u16(res_lo, 5), vrshrn_n_u16(res_hi, 5)); uint8x16_t mask = vld1q_u8(BaseMask[base_max_diff]); dst[r] = vbslq_u8(mask, v_temp, a_mbase_x); @@ -1304,37 +1287,34 @@ } } -static void dr_prediction_z1_16xN_neon(int N, uint8_t *dst, ptrdiff_t stride, - const uint8_t *above, int upsample_above, - int dx) { +static void dr_prediction_z1_16xH_neon(int H, uint8_t *dst, ptrdiff_t stride, + const uint8_t *above, int dx) { uint8x16_t dstvec[64]; - dr_prediction_z1_HxW_internal_neon(16, N, dstvec, above, upsample_above, dx); - for (int i = 0; i < N; i++) { + dr_prediction_z1_WxH_internal_neon_large(16, H, dstvec, above, dx); + for (int i = 0; i < H; i++) { vst1q_u8(dst + stride * i, dstvec[i]); } } -static AOM_FORCE_INLINE void dr_prediction_z1_32xN_internal_neon( - int N, uint8x16x2_t *dstvec, const uint8_t *above, int dx) { +static AOM_FORCE_INLINE void dr_prediction_z1_32xH_internal_neon( + int H, uint8x16x2_t *dstvec, const uint8_t *above, int dx) { + // Here upsample_above is 0 by design of av1_use_intra_edge_upsample defined + // in av1/common/reconintra.h. const int frac_bits = 6; - const int max_base_x = ((32 + N) - 1); + const int max_base_x = (32 + H) - 1; - // pre-filter above pixels - // store in temp buffers: - // above[x] * 32 + 16 - // above[x+1] - above[x] - // final pixels will be calculated as: - // (above[x] * 32 + 16 + (above[x+1] - above[x]) * shift) >> 5 + // above0 * (32 - shift) + above1 * shift + // = (above1 - above0) * shift + above0 * 32 const uint8x16_t a_mbase_x = vdupq_n_u8(above[max_base_x]); int x = dx; - for (int r = 0; r < N; r++) { + for (int r = 0; r < H; r++) { int base = x >> frac_bits; int base_max_diff = (max_base_x - base); if (base_max_diff <= 0) { - for (int i = r; i < N; ++i) { + for (int i = r; i < H; ++i) { dstvec[i].val[0] = a_mbase_x; // save 32 values dstvec[i].val[1] = a_mbase_x; } @@ -1352,17 +1332,17 @@ } else { uint8x16_t a0_128 = vld1q_u8(above + base + j); uint8x16_t a1_128 = vld1q_u8(above + base + j + 1); + uint16x8_t diff_lo = vsubl_u8(vget_low_u8(a1_128), vget_low_u8(a0_128)); uint16x8_t diff_hi = vsubl_u8(vget_high_u8(a1_128), vget_high_u8(a0_128)); - uint16x8_t a32_lo = - vmlal_u8(vdupq_n_u16(16), vget_low_u8(a0_128), vdup_n_u8(32)); - uint16x8_t a32_hi = - vmlal_u8(vdupq_n_u16(16), vget_high_u8(a0_128), vdup_n_u8(32)); + uint16x8_t a32_lo = vshll_n_u8(vget_low_u8(a0_128), 5); + uint16x8_t a32_hi = vshll_n_u8(vget_high_u8(a0_128), 5); uint16x8_t res_lo = vmlaq_u16(a32_lo, diff_lo, shift); uint16x8_t res_hi = vmlaq_u16(a32_hi, diff_hi, shift); - res16[jj] = vcombine_u8(vshrn_n_u16(res_lo, 5), vshrn_n_u16(res_hi, 5)); + res16[jj] = + vcombine_u8(vrshrn_n_u16(res_lo, 5), vrshrn_n_u16(res_hi, 5)); } } @@ -1374,12 +1354,12 @@ } } -static void dr_prediction_z1_32xN_neon(int N, uint8_t *dst, ptrdiff_t stride, +static void dr_prediction_z1_32xH_neon(int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, int dx) { uint8x16x2_t dstvec[64]; - dr_prediction_z1_32xN_internal_neon(N, dstvec, above, dx); - for (int i = 0; i < N; i++) { + dr_prediction_z1_32xH_internal_neon(H, dstvec, above, dx); + for (int i = 0; i < H; i++) { vst1q_u8(dst + stride * i, dstvec[i].val[0]); vst1q_u8(dst + stride * i + 16, dstvec[i].val[1]); } @@ -1420,25 +1400,21 @@ #endif } -static void dr_prediction_z1_64xN_neon(int N, uint8_t *dst, ptrdiff_t stride, +static void dr_prediction_z1_64xH_neon(int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, int dx) { const int frac_bits = 6; - const int max_base_x = ((64 + N) - 1); + const int max_base_x = (64 + H) - 1; - // pre-filter above pixels - // store in temp buffers: - // above[x] * 32 + 16 - // above[x+1] - above[x] - // final pixels will be calculated as: - // (above[x] * 32 + 16 + (above[x+1] - above[x]) * shift) >> 5 + // above0 * (32 - shift) + above1 * shift + // = (above1 - above0) * shift + above0 * 32 const uint8x16_t a_mbase_x = vdupq_n_u8(above[max_base_x]); int x = dx; - for (int r = 0; r < N; r++, dst += stride) { + for (int r = 0; r < H; r++, dst += stride) { int base = x >> frac_bits; if (base >= max_base_x) { - for (int i = r; i < N; ++i) { + for (int i = r; i < H; ++i) { vst1q_u8(dst, a_mbase_x); vst1q_u8(dst + 16, a_mbase_x); vst1q_u8(dst + 32, a_mbase_x); @@ -1449,9 +1425,6 @@ } uint16x8_t shift = vdupq_n_u16((x & 0x3f) >> 1); - uint8x16_t base_inc128 = - vaddq_u8(vdupq_n_u8(base), vcombine_u8(vcreate_u8(0x0706050403020100), - vcreate_u8(0x0F0E0D0C0B0A0908))); for (int j = 0; j < 64; j += 16) { if (base + j >= max_base_x) { @@ -1475,16 +1448,12 @@ uint16x8_t diff_lo = vsubl_u8(vget_low_u8(a1_128), vget_low_u8(a0_128)); uint16x8_t diff_hi = vsubl_u8(vget_high_u8(a1_128), vget_high_u8(a0_128)); - uint16x8_t a32_lo = - vmlal_u8(vdupq_n_u16(16), vget_low_u8(a0_128), vdup_n_u8(32)); - uint16x8_t a32_hi = - vmlal_u8(vdupq_n_u16(16), vget_high_u8(a0_128), vdup_n_u8(32)); + uint16x8_t a32_lo = vshll_n_u8(vget_low_u8(a0_128), 5); + uint16x8_t a32_hi = vshll_n_u8(vget_high_u8(a0_128), 5); uint16x8_t res_lo = vmlaq_u16(a32_lo, diff_lo, shift); uint16x8_t res_hi = vmlaq_u16(a32_hi, diff_hi, shift); vst1q_u8(dst + j, - vcombine_u8(vshrn_n_u16(res_lo, 5), vshrn_n_u16(res_hi, 5))); - - base_inc128 = vaddq_u8(base_inc128, vdupq_n_u8(16)); + vcombine_u8(vrshrn_n_u16(res_lo, 5), vrshrn_n_u16(res_hi, 5))); } } x += dx; @@ -1500,16 +1469,14 @@ switch (bw) { case 4: - dr_prediction_z1_4xN_neon(bh, dst, stride, above, upsample_above, dx); + dr_prediction_z1_4xH_neon(bh, dst, stride, above, upsample_above, dx); break; case 8: - dr_prediction_z1_8xN_neon(bh, dst, stride, above, upsample_above, dx); + dr_prediction_z1_8xH_neon(bh, dst, stride, above, upsample_above, dx); break; - case 16: - dr_prediction_z1_16xN_neon(bh, dst, stride, above, upsample_above, dx); - break; - case 32: dr_prediction_z1_32xN_neon(bh, dst, stride, above, dx); break; - case 64: dr_prediction_z1_64xN_neon(bh, dst, stride, above, dx); break; + case 16: dr_prediction_z1_16xH_neon(bh, dst, stride, above, dx); break; + case 32: dr_prediction_z1_32xH_neon(bh, dst, stride, above, dx); break; + case 64: dr_prediction_z1_64xH_neon(bh, dst, stride, above, dx); break; default: break; } } @@ -1527,44 +1494,40 @@ }; #endif // !AOM_ARCH_AARCH64 -static AOM_FORCE_INLINE void dr_prediction_z2_Nx4_above_neon( +static AOM_FORCE_INLINE void dr_prediction_z2_4xH_above_neon( const uint8_t *above, int upsample_above, int dx, int base_x, int y, uint8x8_t *a0_x, uint8x8_t *a1_x, uint16x4_t *shift0) { - uint16x4_t r6 = vcreate_u16(0x00C0008000400000); - uint16x4_t ydx = vdup_n_u16(y * dx); + const int x = -y * dx; + if (upsample_above) { // Cannot use LD2 here since we only want to load eight bytes, but LD2 can // only load either 16 or 32. uint8x8_t v_tmp = vld1_u8(above + base_x); *a0_x = vuzp_u8(v_tmp, vdup_n_u8(0)).val[0]; *a1_x = vuzp_u8(v_tmp, vdup_n_u8(0)).val[1]; - *shift0 = vand_u16(vsub_u16(r6, ydx), vdup_n_u16(0x1f)); + *shift0 = vdup_n_u16(x & 0x1f); // ((x << upsample_above) & 0x3f) >> 1 } else { *a0_x = load_unaligned_u8_4x1(above + base_x); *a1_x = load_unaligned_u8_4x1(above + base_x + 1); - *shift0 = vand_u16(vhsub_u16(r6, ydx), vdup_n_u16(0x1f)); + *shift0 = vdup_n_u16((x & 0x3f) >> 1); } } -static AOM_FORCE_INLINE void dr_prediction_z2_Nx4_left_neon( +static AOM_FORCE_INLINE void dr_prediction_z2_4xH_left_neon( #if AOM_ARCH_AARCH64 uint8x16x2_t left_vals, #else const uint8_t *left, #endif - int upsample_left, int dy, int r, int min_base_y, int frac_bits_y, - uint16x4_t *a0_y, uint16x4_t *a1_y, uint16x4_t *shift1) { + int upsample_left, int dy, int r, int frac_bits_y, uint16x4_t *a0_y, + uint16x4_t *a1_y, uint16x4_t *shift1) { int16x4_t dy64 = vdup_n_s16(dy); int16x4_t v_1234 = vcreate_s16(0x0004000300020001); int16x4_t v_frac_bits_y = vdup_n_s16(-frac_bits_y); - int16x4_t min_base_y64 = vdup_n_s16(min_base_y); int16x4_t v_r6 = vdup_n_s16(r << 6); int16x4_t y_c64 = vmls_s16(v_r6, v_1234, dy64); int16x4_t base_y_c64 = vshl_s16(y_c64, v_frac_bits_y); - // Values in base_y_c64 range from -2 through 14 inclusive. - base_y_c64 = vmax_s16(base_y_c64, min_base_y64); - #if AOM_ARCH_AARCH64 uint8x8_t left_idx0 = vreinterpret_u8_s16(vadd_s16(base_y_c64, vdup_n_s16(2))); // [0, 16] @@ -1596,19 +1559,17 @@ #endif // AOM_ARCH_AARCH64 if (upsample_left) { - *shift1 = vand_u16(vreinterpret_u16_s16(y_c64), vdup_n_u16(0x1f)); + *shift1 = vand_u16(vreinterpret_u16_s16(y_c64), + vdup_n_u16(0x1f)); // ((y << upsample_left) & 0x3f) >> 1 } else { *shift1 = vand_u16(vshr_n_u16(vreinterpret_u16_s16(y_c64), 1), vdup_n_u16(0x1f)); } } -static AOM_FORCE_INLINE uint8x8_t dr_prediction_z2_Nx8_above_neon( +static AOM_FORCE_INLINE uint8x8_t dr_prediction_z2_8xH_above_neon( const uint8_t *above, int upsample_above, int dx, int base_x, int y) { - uint16x8_t c1234 = vcombine_u16(vcreate_u16(0x0004000300020001), - vcreate_u16(0x0008000700060005)); - uint16x8_t ydx = vdupq_n_u16(y * dx); - uint16x8_t r6 = vshlq_n_u16(vextq_u16(c1234, vdupq_n_u16(0), 2), 6); + const int x = -y * dx; uint16x8_t shift0; uint8x8_t a0_x0; @@ -1617,40 +1578,36 @@ uint8x8x2_t v_tmp = vld2_u8(above + base_x); a0_x0 = v_tmp.val[0]; a1_x0 = v_tmp.val[1]; - shift0 = vandq_u16(vsubq_u16(r6, ydx), vdupq_n_u16(0x1f)); + shift0 = vdupq_n_u16(x & 0x1f); // ((x << upsample_above) & 0x3f) >> 1 } else { a0_x0 = vld1_u8(above + base_x); a1_x0 = vld1_u8(above + base_x + 1); - shift0 = vandq_u16(vhsubq_u16(r6, ydx), vdupq_n_u16(0x1f)); + shift0 = vdupq_n_u16((x & 0x3f) >> 1); } uint16x8_t diff0 = vsubl_u8(a1_x0, a0_x0); // a[x+1] - a[x] - uint16x8_t a32 = - vmlal_u8(vdupq_n_u16(16), a0_x0, vdup_n_u8(32)); // a[x] * 32 + 16 + uint16x8_t a32 = vshll_n_u8(a0_x0, 5); // a[x] * 32 uint16x8_t res = vmlaq_u16(a32, diff0, shift0); - return vshrn_n_u16(res, 5); + + return vrshrn_n_u16(res, 5); } -static AOM_FORCE_INLINE uint8x8_t dr_prediction_z2_Nx8_left_neon( +static AOM_FORCE_INLINE uint8x8_t dr_prediction_z2_8xH_left_neon( #if AOM_ARCH_AARCH64 uint8x16x3_t left_vals, #else const uint8_t *left, #endif - int upsample_left, int dy, int r, int min_base_y, int frac_bits_y) { + int upsample_left, int dy, int r, int frac_bits_y) { int16x8_t v_r6 = vdupq_n_s16(r << 6); int16x8_t dy128 = vdupq_n_s16(dy); int16x8_t v_frac_bits_y = vdupq_n_s16(-frac_bits_y); - int16x8_t min_base_y128 = vdupq_n_s16(min_base_y); uint16x8_t c1234 = vcombine_u16(vcreate_u16(0x0004000300020001), vcreate_u16(0x0008000700060005)); int16x8_t y_c128 = vmlsq_s16(v_r6, vreinterpretq_s16_u16(c1234), dy128); int16x8_t base_y_c128 = vshlq_s16(y_c128, v_frac_bits_y); - // Values in base_y_c128 range from -2 through 31 inclusive. - base_y_c128 = vmaxq_s16(base_y_c128, min_base_y128); - #if AOM_ARCH_AARCH64 uint8x16_t left_idx0 = vreinterpretq_u8_s16(vaddq_s16(base_y_c128, vdupq_n_s16(2))); // [0, 33] @@ -1668,49 +1625,42 @@ uint16x8_t shift1; if (upsample_left) { - shift1 = vandq_u16(vreinterpretq_u16_s16(y_c128), vdupq_n_u16(0x1f)); + shift1 = + vandq_u16(vreinterpretq_u16_s16(y_c128), + vdupq_n_u16(0x1f)); // ((y << upsample_left) & 0x3f) >> 1 } else { shift1 = vshrq_n_u16( vandq_u16(vreinterpretq_u16_s16(y_c128), vdupq_n_u16(0x3f)), 1); } uint16x8_t diff1 = vsubl_u8(a1_x1, a0_x1); - uint16x8_t a32 = vmlal_u8(vdupq_n_u16(16), a0_x1, vdup_n_u8(32)); + uint16x8_t a32 = vshll_n_u8(a0_x1, 5); uint16x8_t res = vmlaq_u16(a32, diff1, shift1); - return vshrn_n_u16(res, 5); + + return vrshrn_n_u16(res, 5); } -static AOM_FORCE_INLINE uint8x16_t dr_prediction_z2_NxW_above_neon( +static AOM_FORCE_INLINE uint8x16_t dr_prediction_z2_WxH_above_neon( const uint8_t *above, int dx, int base_x, int y, int j) { - uint16x8x2_t c0123 = { { vcombine_u16(vcreate_u16(0x0003000200010000), - vcreate_u16(0x0007000600050004)), - vcombine_u16(vcreate_u16(0x000B000A00090008), - vcreate_u16(0x000F000E000D000C)) } }; - uint16x8_t j256 = vdupq_n_u16(j); - uint16x8_t ydx = vdupq_n_u16((uint16_t)(y * dx)); - + const int x = -y * dx; const uint8x16_t a0_x128 = vld1q_u8(above + base_x + j); const uint8x16_t a1_x128 = vld1q_u8(above + base_x + j + 1); - uint16x8_t res6_0 = vshlq_n_u16(vaddq_u16(c0123.val[0], j256), 6); - uint16x8_t res6_1 = vshlq_n_u16(vaddq_u16(c0123.val[1], j256), 6); - uint16x8_t shift0 = - vshrq_n_u16(vandq_u16(vsubq_u16(res6_0, ydx), vdupq_n_u16(0x3f)), 1); - uint16x8_t shift1 = - vshrq_n_u16(vandq_u16(vsubq_u16(res6_1, ydx), vdupq_n_u16(0x3f)), 1); + const uint16x8_t shift = vdupq_n_u16((x & 0x3f) >> 1); + // a[x+1] - a[x] uint16x8_t diff0 = vsubl_u8(vget_low_u8(a1_x128), vget_low_u8(a0_x128)); uint16x8_t diff1 = vsubl_u8(vget_high_u8(a1_x128), vget_high_u8(a0_x128)); - // a[x] * 32 + 16 - uint16x8_t a32_0 = - vmlal_u8(vdupq_n_u16(16), vget_low_u8(a0_x128), vdup_n_u8(32)); - uint16x8_t a32_1 = - vmlal_u8(vdupq_n_u16(16), vget_high_u8(a0_x128), vdup_n_u8(32)); - uint16x8_t res0 = vmlaq_u16(a32_0, diff0, shift0); - uint16x8_t res1 = vmlaq_u16(a32_1, diff1, shift1); - return vcombine_u8(vshrn_n_u16(res0, 5), vshrn_n_u16(res1, 5)); + + // a[x] * 32 + uint16x8_t a32_0 = vshll_n_u8(vget_low_u8(a0_x128), 5); + uint16x8_t a32_1 = vshll_n_u8(vget_high_u8(a0_x128), 5); + uint16x8_t res0 = vmlaq_u16(a32_0, diff0, shift); + uint16x8_t res1 = vmlaq_u16(a32_1, diff1, shift); + + return vcombine_u8(vrshrn_n_u16(res0, 5), vrshrn_n_u16(res1, 5)); } -static AOM_FORCE_INLINE uint8x16_t dr_prediction_z2_NxW_left_neon( +static AOM_FORCE_INLINE uint8x16_t dr_prediction_z2_WxH_left_neon( #if AOM_ARCH_AARCH64 uint8x16x4_t left_vals0, uint8x16x4_t left_vals1, #else @@ -1719,39 +1669,25 @@ int dy, int r, int j) { // here upsample_above and upsample_left are 0 by design of // av1_use_intra_edge_upsample - const int min_base_y = -1; - - int16x8_t min_base_y256 = vdupq_n_s16(min_base_y); - int16x8_t half_min_base_y256 = vdupq_n_s16(min_base_y >> 1); int16x8_t dy256 = vdupq_n_s16(dy); uint16x8_t j256 = vdupq_n_u16(j); - uint16x8x2_t c0123 = { { vcombine_u16(vcreate_u16(0x0003000200010000), - vcreate_u16(0x0007000600050004)), - vcombine_u16(vcreate_u16(0x000B000A00090008), - vcreate_u16(0x000F000E000D000C)) } }; - uint16x8x2_t c1234 = { { vaddq_u16(c0123.val[0], vdupq_n_u16(1)), - vaddq_u16(c0123.val[1], vdupq_n_u16(1)) } }; + uint16x8x2_t c1234 = { { vcombine_u16(vcreate_u16(0x0004000300020001), + vcreate_u16(0x0008000700060005)), + vcombine_u16(vcreate_u16(0x000C000B000A0009), + vcreate_u16(0x0010000F000E000D)) } }; int16x8_t v_r6 = vdupq_n_s16(r << 6); int16x8_t c256_0 = vreinterpretq_s16_u16(vaddq_u16(j256, c1234.val[0])); int16x8_t c256_1 = vreinterpretq_s16_u16(vaddq_u16(j256, c1234.val[1])); - int16x8_t mul16_lo = vreinterpretq_s16_u16( - vminq_u16(vreinterpretq_u16_s16(vmulq_s16(c256_0, dy256)), - vreinterpretq_u16_s16(half_min_base_y256))); - int16x8_t mul16_hi = vreinterpretq_s16_u16( - vminq_u16(vreinterpretq_u16_s16(vmulq_s16(c256_1, dy256)), - vreinterpretq_u16_s16(half_min_base_y256))); - int16x8_t y_c256_lo = vsubq_s16(v_r6, mul16_lo); - int16x8_t y_c256_hi = vsubq_s16(v_r6, mul16_hi); + + int16x8_t y_c256_lo = vmlsq_s16(v_r6, c256_0, dy256); + int16x8_t y_c256_hi = vmlsq_s16(v_r6, c256_1, dy256); int16x8_t base_y_c256_lo = vshrq_n_s16(y_c256_lo, 6); int16x8_t base_y_c256_hi = vshrq_n_s16(y_c256_hi, 6); - base_y_c256_lo = vmaxq_s16(min_base_y256, base_y_c256_lo); - base_y_c256_hi = vmaxq_s16(min_base_y256, base_y_c256_hi); - #if !AOM_ARCH_AARCH64 int16_t min_y = vgetq_lane_s16(base_y_c256_hi, 7); int16_t max_y = vgetq_lane_s16(base_y_c256_lo, 0); @@ -1832,31 +1768,26 @@ uint16x8_t diff_lo = vsubl_u8(a1_y0, a0_y0); uint16x8_t diff_hi = vsubl_u8(a1_y1, a0_y1); // a[x] * 32 + 16 - uint16x8_t a32_lo = vmlal_u8(vdupq_n_u16(16), a0_y0, vdup_n_u8(32)); - uint16x8_t a32_hi = vmlal_u8(vdupq_n_u16(16), a0_y1, vdup_n_u8(32)); + uint16x8_t a32_lo = vshll_n_u8(a0_y0, 5); + uint16x8_t a32_hi = vshll_n_u8(a0_y1, 5); uint16x8_t res0 = vmlaq_u16(a32_lo, diff_lo, shifty_lo); uint16x8_t res1 = vmlaq_u16(a32_hi, diff_hi, shifty_hi); - return vcombine_u8(vshrn_n_u16(res0, 5), vshrn_n_u16(res1, 5)); + return vcombine_u8(vrshrn_n_u16(res0, 5), vrshrn_n_u16(res1, 5)); } -static void dr_prediction_z2_Nx4_neon(int N, uint8_t *dst, ptrdiff_t stride, +static void dr_prediction_z2_4xH_neon(int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left, int upsample_above, int upsample_left, int dx, int dy) { const int min_base_x = -(1 << upsample_above); - const int min_base_y = -(1 << upsample_left); const int frac_bits_x = 6 - upsample_above; const int frac_bits_y = 6 - upsample_left; assert(dx > 0); - // pre-filter above pixels - // store in temp buffers: - // above[x] * 32 + 16 - // above[x+1] - above[x] - // final pixels will be calculated as: - // (above[x] * 32 + 16 + (above[x+1] - above[x]) * shift) >> 5 + // above0 * (32 - shift) + above1 * shift + // = (above1 - above0) * shift + above0 * 32 #if AOM_ARCH_AARCH64 // Use ext rather than loading left + 14 directly to avoid over-read. @@ -1869,7 +1800,7 @@ #define LEFT left #endif // AOM_ARCH_AARCH64 - for (int r = 0; r < N; r++) { + for (int r = 0; r < H; r++) { int y = r + 1; int base_x = (-y * dx) >> frac_bits_x; const int base_min_diff = @@ -1879,22 +1810,21 @@ if (base_min_diff <= 0) { uint8x8_t a0_x_u8, a1_x_u8; uint16x4_t shift0; - dr_prediction_z2_Nx4_above_neon(above, upsample_above, dx, base_x, y, + dr_prediction_z2_4xH_above_neon(above, upsample_above, dx, base_x, y, &a0_x_u8, &a1_x_u8, &shift0); uint8x8_t a0_x = a0_x_u8; uint8x8_t a1_x = a1_x_u8; uint16x8_t diff = vsubl_u8(a1_x, a0_x); // a[x+1] - a[x] - uint16x8_t a32 = - vmlal_u8(vdupq_n_u16(16), a0_x, vdup_n_u8(32)); // a[x] * 32 + 16 + uint16x8_t a32 = vshll_n_u8(a0_x, 5); uint16x8_t res = vmlaq_u16(a32, diff, vcombine_u16(shift0, vdup_n_u16(0))); - uint8x8_t resx = vshrn_n_u16(res, 5); + uint8x8_t resx = vrshrn_n_u16(res, 5); vst1_lane_u32((uint32_t *)dst, vreinterpret_u32_u8(resx), 0); } else if (base_min_diff < 4) { uint8x8_t a0_x_u8, a1_x_u8; uint16x4_t shift0; - dr_prediction_z2_Nx4_above_neon(above, upsample_above, dx, base_x, y, + dr_prediction_z2_4xH_above_neon(above, upsample_above, dx, base_x, y, &a0_x_u8, &a1_x_u8, &shift0); uint16x8_t a0_x = vmovl_u8(a0_x_u8); uint16x8_t a1_x = vmovl_u8(a1_x_u8); @@ -1902,17 +1832,16 @@ uint16x4_t a0_y; uint16x4_t a1_y; uint16x4_t shift1; - dr_prediction_z2_Nx4_left_neon(LEFT, upsample_left, dy, r, min_base_y, - frac_bits_y, &a0_y, &a1_y, &shift1); + dr_prediction_z2_4xH_left_neon(LEFT, upsample_left, dy, r, frac_bits_y, + &a0_y, &a1_y, &shift1); a0_x = vcombine_u16(vget_low_u16(a0_x), a0_y); a1_x = vcombine_u16(vget_low_u16(a1_x), a1_y); uint16x8_t shift = vcombine_u16(shift0, shift1); uint16x8_t diff = vsubq_u16(a1_x, a0_x); // a[x+1] - a[x] - uint16x8_t a32 = - vmlaq_n_u16(vdupq_n_u16(16), a0_x, 32); // a[x] * 32 + 16 + uint16x8_t a32 = vshlq_n_u16(a0_x, 5); uint16x8_t res = vmlaq_u16(a32, diff, shift); - uint8x8_t resx = vshrn_n_u16(res, 5); + uint8x8_t resx = vrshrn_n_u16(res, 5); uint8x8_t resy = vext_u8(resx, vdup_n_u8(0), 4); uint8x8_t mask = vld1_u8(BaseMask[base_min_diff]); @@ -1921,12 +1850,12 @@ } else { uint16x4_t a0_y, a1_y; uint16x4_t shift1; - dr_prediction_z2_Nx4_left_neon(LEFT, upsample_left, dy, r, min_base_y, - frac_bits_y, &a0_y, &a1_y, &shift1); - uint16x4_t diff = vsub_u16(a1_y, a0_y); // a[x+1] - a[x] - uint16x4_t a32 = vmla_n_u16(vdup_n_u16(16), a0_y, 32); // a[x] * 32 + 16 + dr_prediction_z2_4xH_left_neon(LEFT, upsample_left, dy, r, frac_bits_y, + &a0_y, &a1_y, &shift1); + uint16x4_t diff = vsub_u16(a1_y, a0_y); // a[x+1] - a[x] + uint16x4_t a32 = vshl_n_u16(a0_y, 5); uint16x4_t res = vmla_u16(a32, diff, shift1); - uint8x8_t resy = vshrn_n_u16(vcombine_u16(res, vdup_n_u16(0)), 5); + uint8x8_t resy = vrshrn_n_u16(vcombine_u16(res, vdup_n_u16(0)), 5); vst1_lane_u32((uint32_t *)dst, vreinterpret_u32_u8(resy), 0); } @@ -1936,21 +1865,16 @@ #undef LEFT } -static void dr_prediction_z2_Nx8_neon(int N, uint8_t *dst, ptrdiff_t stride, +static void dr_prediction_z2_8xH_neon(int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left, int upsample_above, int upsample_left, int dx, int dy) { const int min_base_x = -(1 << upsample_above); - const int min_base_y = -(1 << upsample_left); const int frac_bits_x = 6 - upsample_above; const int frac_bits_y = 6 - upsample_left; - // pre-filter above pixels - // store in temp buffers: - // above[x] * 32 + 16 - // above[x+1] - above[x] - // final pixels will be calculated as: - // (above[x] * 32 + 16 + (above[x+1] - above[x]) * shift) >> 5 + // above0 * (32 - shift) + above1 * shift + // = (above1 - above0) * shift + above0 * 32 #if AOM_ARCH_AARCH64 // Use ext rather than loading left + 30 directly to avoid over-read. @@ -1965,7 +1889,7 @@ #define LEFT left #endif // AOM_ARCH_AARCH64 - for (int r = 0; r < N; r++) { + for (int r = 0; r < H; r++) { int y = r + 1; int base_x = (-y * dx) >> frac_bits_x; int base_min_diff = @@ -1973,19 +1897,19 @@ if (base_min_diff <= 0) { uint8x8_t resx = - dr_prediction_z2_Nx8_above_neon(above, upsample_above, dx, base_x, y); + dr_prediction_z2_8xH_above_neon(above, upsample_above, dx, base_x, y); vst1_u8(dst, resx); } else if (base_min_diff < 8) { uint8x8_t resx = - dr_prediction_z2_Nx8_above_neon(above, upsample_above, dx, base_x, y); - uint8x8_t resy = dr_prediction_z2_Nx8_left_neon( - LEFT, upsample_left, dy, r, min_base_y, frac_bits_y); + dr_prediction_z2_8xH_above_neon(above, upsample_above, dx, base_x, y); + uint8x8_t resy = dr_prediction_z2_8xH_left_neon(LEFT, upsample_left, dy, + r, frac_bits_y); uint8x8_t mask = vld1_u8(BaseMask[base_min_diff]); uint8x8_t resxy = vbsl_u8(mask, resy, resx); vst1_u8(dst, resxy); } else { - uint8x8_t resy = dr_prediction_z2_Nx8_left_neon( - LEFT, upsample_left, dy, r, min_base_y, frac_bits_y); + uint8x8_t resy = dr_prediction_z2_8xH_left_neon(LEFT, upsample_left, dy, + r, frac_bits_y); vst1_u8(dst, resy); } @@ -1994,7 +1918,7 @@ #undef LEFT } -static void dr_prediction_z2_HxW_neon(int H, int W, uint8_t *dst, +static void dr_prediction_z2_WxH_neon(int W, int H, uint8_t *dst, ptrdiff_t stride, const uint8_t *above, const uint8_t *left, int dx, int dy) { // here upsample_above and upsample_left are 0 by design of @@ -2025,17 +1949,17 @@ if (base_min_diff <= 0) { uint8x16_t resx = - dr_prediction_z2_NxW_above_neon(above, dx, base_x, y, j); + dr_prediction_z2_WxH_above_neon(above, dx, base_x, y, j); vst1q_u8(dst + j, resx); } else if (base_min_diff < 16) { uint8x16_t resx = - dr_prediction_z2_NxW_above_neon(above, dx, base_x, y, j); - uint8x16_t resy = dr_prediction_z2_NxW_left_neon(LEFT, dy, r, j); + dr_prediction_z2_WxH_above_neon(above, dx, base_x, y, j); + uint8x16_t resy = dr_prediction_z2_WxH_left_neon(LEFT, dy, r, j); uint8x16_t mask = vld1q_u8(BaseMask[base_min_diff]); uint8x16_t resxy = vbslq_u8(mask, resy, resx); vst1q_u8(dst + j, resxy); } else { - uint8x16_t resy = dr_prediction_z2_NxW_left_neon(LEFT, dy, r, j); + uint8x16_t resy = dr_prediction_z2_WxH_left_neon(LEFT, dy, r, j); vst1q_u8(dst + j, resy); } } // for j @@ -2054,15 +1978,15 @@ switch (bw) { case 4: - dr_prediction_z2_Nx4_neon(bh, dst, stride, above, left, upsample_above, + dr_prediction_z2_4xH_neon(bh, dst, stride, above, left, upsample_above, upsample_left, dx, dy); break; case 8: - dr_prediction_z2_Nx8_neon(bh, dst, stride, above, left, upsample_above, + dr_prediction_z2_8xH_neon(bh, dst, stride, above, left, upsample_above, upsample_left, dx, dy); break; default: - dr_prediction_z2_HxW_neon(bh, bw, dst, stride, above, left, dx, dy); + dr_prediction_z2_WxH_neon(bw, bh, dst, stride, above, left, dx, dy); break; } } @@ -2134,7 +2058,8 @@ uint8x8_t dstvec[4]; uint8x8x2_t dest; - dr_prediction_z1_HxW_internal_neon_64(4, 4, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(4, 4, dstvec, left, upsample_left, + dy); z3_transpose_arrays_u8_4x4(dstvec, &dest); store_u8x4_strided_x2(dst + stride * 0, stride, dest.val[0]); store_u8x4_strided_x2(dst + stride * 2, stride, dest.val[1]); @@ -2146,7 +2071,8 @@ uint8x8_t dstvec[8]; uint8x8_t d[8]; - dr_prediction_z1_HxW_internal_neon_64(8, 8, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(8, 8, dstvec, left, upsample_left, + dy); transpose_arrays_u8_8x8(dstvec, d); store_u8_8x8(dst, stride, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]); } @@ -2157,7 +2083,8 @@ uint8x8_t dstvec[4]; uint8x8x2_t d[2]; - dr_prediction_z1_HxW_internal_neon_64(8, 4, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(8, 4, dstvec, left, upsample_left, + dy); z3_transpose_arrays_u8_8x4(dstvec, d); store_u8x4_strided_x2(dst + stride * 0, stride, d[0].val[0]); store_u8x4_strided_x2(dst + stride * 2, stride, d[0].val[1]); @@ -2171,7 +2098,8 @@ uint8x8_t dstvec[8]; uint8x8_t d[8]; - dr_prediction_z1_HxW_internal_neon_64(4, 8, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(4, 8, dstvec, left, upsample_left, + dy); transpose_arrays_u8_8x8(dstvec, d); store_u8_8x4(dst, stride, d[0], d[1], d[2], d[3]); } @@ -2179,10 +2107,12 @@ static void dr_prediction_z3_8x16_neon(uint8_t *dst, ptrdiff_t stride, const uint8_t *left, int upsample_left, int dy) { + (void)upsample_left; + uint8x16_t dstvec[8]; uint8x8_t d[16]; - dr_prediction_z1_HxW_internal_neon(16, 8, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_large(16, 8, dstvec, left, dy); transpose_arrays_u8_16x8(dstvec, d); for (int i = 0; i < 16; i++) { vst1_u8(dst + i * stride, d[i]); @@ -2195,7 +2125,8 @@ uint8x8_t dstvec[16]; uint8x16_t d[8]; - dr_prediction_z1_HxW_internal_neon_64(8, 16, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(8, 16, dstvec, left, upsample_left, + dy); transpose_arrays_u8_8x16(dstvec, d); for (int i = 0; i < 8; i++) { vst1q_u8(dst + i * stride, d[i]); @@ -2206,10 +2137,12 @@ static void dr_prediction_z3_4x16_neon(uint8_t *dst, ptrdiff_t stride, const uint8_t *left, int upsample_left, int dy) { + (void)upsample_left; + uint8x16_t dstvec[4]; uint8x16x2_t d[2]; - dr_prediction_z1_HxW_internal_neon(16, 4, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_large(16, 4, dstvec, left, dy); z3_transpose_arrays_u8_16x4(dstvec, d); store_u8x4_strided_x4(dst + stride * 0, stride, d[0].val[0]); store_u8x4_strided_x4(dst + stride * 4, stride, d[0].val[1]); @@ -2223,7 +2156,8 @@ uint8x8_t dstvec[16]; uint8x16_t d[8]; - dr_prediction_z1_HxW_internal_neon_64(4, 16, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(4, 16, dstvec, left, upsample_left, + dy); transpose_arrays_u8_8x16(dstvec, d); for (int i = 0; i < 4; i++) { vst1q_u8(dst + i * stride, d[i]); @@ -2238,7 +2172,7 @@ uint8x16_t d[32]; uint8x16_t v_zero = vdupq_n_u8(0); - dr_prediction_z1_32xN_internal_neon(8, dstvec, left, dy); + dr_prediction_z1_32xH_internal_neon(8, dstvec, left, dy); for (int i = 8; i < 16; i++) { dstvec[i].val[0] = v_zero; dstvec[i].val[1] = v_zero; @@ -2255,7 +2189,8 @@ uint8x8_t dstvec[32]; uint8x16_t d[16]; - dr_prediction_z1_HxW_internal_neon_64(8, 32, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_small(8, 32, dstvec, left, upsample_left, + dy); transpose_arrays_u8_8x16(dstvec, d); transpose_arrays_u8_8x16(dstvec + 16, d + 8); for (int i = 0; i < 8; i++) { @@ -2268,10 +2203,12 @@ static void dr_prediction_z3_16x16_neon(uint8_t *dst, ptrdiff_t stride, const uint8_t *left, int upsample_left, int dy) { + (void)upsample_left; + uint8x16_t dstvec[16]; uint8x16_t d[16]; - dr_prediction_z1_HxW_internal_neon(16, 16, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_large(16, 16, dstvec, left, dy); transpose_arrays_u8_16x16(dstvec, d); for (int i = 0; i < 16; i++) { vst1q_u8(dst + i * stride, d[i]); @@ -2285,7 +2222,7 @@ uint8x16x2_t dstvec[32]; uint8x16_t d[64]; - dr_prediction_z1_32xN_internal_neon(32, dstvec, left, dy); + dr_prediction_z1_32xH_internal_neon(32, dstvec, left, dy); transpose_arrays_u8_32x16(dstvec, d); transpose_arrays_u8_32x16(dstvec + 16, d + 32); for (int i = 0; i < 32; i++) { @@ -2300,7 +2237,7 @@ (void)upsample_left; DECLARE_ALIGNED(16, uint8_t, dstT[64 * 64]); - dr_prediction_z1_64xN_neon(64, dstT, 64, left, dy); + dr_prediction_z1_64xH_neon(64, dstT, 64, left, dy); z3_transpose_arrays_u8_16nx16n(dstT, 64, dst, stride, 64, 64); } @@ -2311,7 +2248,7 @@ uint8x16x2_t dstvec[16]; uint8x16_t d[32]; - dr_prediction_z1_32xN_internal_neon(16, dstvec, left, dy); + dr_prediction_z1_32xH_internal_neon(16, dstvec, left, dy); transpose_arrays_u8_32x16(dstvec, d); for (int i = 0; i < 16; i++) { vst1q_u8(dst + 2 * i * stride, d[2 * i + 0]); @@ -2322,9 +2259,11 @@ static void dr_prediction_z3_32x16_neon(uint8_t *dst, ptrdiff_t stride, const uint8_t *left, int upsample_left, int dy) { + (void)upsample_left; + uint8x16_t dstvec[32]; - dr_prediction_z1_HxW_internal_neon(16, 32, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_large(16, 32, dstvec, left, dy); for (int i = 0; i < 32; i += 16) { uint8x16_t d[16]; transpose_arrays_u8_16x16(dstvec + i, d); @@ -2340,7 +2279,7 @@ (void)upsample_left; uint8_t dstT[64 * 32]; - dr_prediction_z1_64xN_neon(32, dstT, 64, left, dy); + dr_prediction_z1_64xH_neon(32, dstT, 64, left, dy); z3_transpose_arrays_u8_16nx16n(dstT, 64, dst, stride, 32, 64); } @@ -2350,7 +2289,7 @@ (void)upsample_left; uint8_t dstT[32 * 64]; - dr_prediction_z1_32xN_neon(64, dstT, 32, left, dy); + dr_prediction_z1_32xH_neon(64, dstT, 32, left, dy); z3_transpose_arrays_u8_16nx16n(dstT, 32, dst, stride, 64, 32); } @@ -2361,16 +2300,18 @@ (void)upsample_left; uint8_t dstT[64 * 16]; - dr_prediction_z1_64xN_neon(16, dstT, 64, left, dy); + dr_prediction_z1_64xH_neon(16, dstT, 64, left, dy); z3_transpose_arrays_u8_16nx16n(dstT, 64, dst, stride, 16, 64); } static void dr_prediction_z3_64x16_neon(uint8_t *dst, ptrdiff_t stride, const uint8_t *left, int upsample_left, int dy) { + (void)upsample_left; + uint8x16_t dstvec[64]; - dr_prediction_z1_HxW_internal_neon(16, 64, dstvec, left, upsample_left, dy); + dr_prediction_z1_WxH_internal_neon_large(16, 64, dstvec, left, dy); for (int i = 0; i < 64; i += 16) { uint8x16_t d[16]; transpose_arrays_u8_16x16(dstvec + i, d);
diff --git a/aom_dsp/arm/mem_neon.h b/aom_dsp/arm/mem_neon.h index ad761de..6590a7f 100644 --- a/aom_dsp/arm/mem_neon.h +++ b/aom_dsp/arm/mem_neon.h
@@ -176,7 +176,7 @@ return ret; } -static inline uint8x8_t load_u8_4x2(const uint8_t *p, int stride) { +static inline uint8x8_t load_u8_4x2(const uint8_t *p, ptrdiff_t stride) { uint8x8_t ret = vdup_n_u8(0); ret = vreinterpret_u8_u32( vld1_lane_u32((const uint32_t *)p, vreinterpret_u32_u8(ret), 0)); @@ -186,7 +186,7 @@ return ret; } -static inline uint16x4_t load_u16_2x2(const uint16_t *p, int stride) { +static inline uint16x4_t load_u16_2x2(const uint16_t *p, ptrdiff_t stride) { uint16x4_t ret = vdup_n_u16(0); ret = vreinterpret_u16_u32( vld1_lane_u32((const uint32_t *)p, vreinterpret_u32_u16(ret), 0)); @@ -1194,7 +1194,8 @@ #endif // Load 2 sets of 4 bytes when alignment is not guaranteed. -static inline uint8x8_t load_unaligned_u8(const uint8_t *buf, int stride) { +static inline uint8x8_t load_unaligned_u8(const uint8_t *buf, + ptrdiff_t stride) { uint32_t a; memcpy(&a, buf, 4); buf += stride; @@ -1205,7 +1206,8 @@ } // Load 4 sets of 4 bytes when alignment is not guaranteed. -static inline uint8x16_t load_unaligned_u8q(const uint8_t *buf, int stride) { +static inline uint8x16_t load_unaligned_u8q(const uint8_t *buf, + ptrdiff_t stride) { uint32_t a; uint32x4_t a_u32; if (stride == 4) return vld1q_u8(buf); @@ -1223,7 +1225,8 @@ return vreinterpretq_u8_u32(a_u32); } -static inline uint8x8_t load_unaligned_u8_2x2(const uint8_t *buf, int stride) { +static inline uint8x8_t load_unaligned_u8_2x2(const uint8_t *buf, + ptrdiff_t stride) { uint16_t a; uint16x4_t a_u16; @@ -1263,7 +1266,8 @@ return vreinterpret_u8_u16(a_u32); } -static inline uint8x8_t load_unaligned_u8_4x2(const uint8_t *buf, int stride) { +static inline uint8x8_t load_unaligned_u8_4x2(const uint8_t *buf, + ptrdiff_t stride) { uint32_t a; uint32x2_t a_u32; @@ -1275,14 +1279,14 @@ return vreinterpret_u8_u32(a_u32); } -static inline void load_unaligned_u8_4x4(const uint8_t *buf, int stride, +static inline void load_unaligned_u8_4x4(const uint8_t *buf, ptrdiff_t stride, uint8x8_t *tu0, uint8x8_t *tu1) { *tu0 = load_unaligned_u8_4x2(buf, stride); buf += 2 * stride; *tu1 = load_unaligned_u8_4x2(buf, stride); } -static inline void load_unaligned_u8_3x8(const uint8_t *buf, int stride, +static inline void load_unaligned_u8_3x8(const uint8_t *buf, ptrdiff_t stride, uint8x8_t *tu0, uint8x8_t *tu1, uint8x8_t *tu2) { load_unaligned_u8_4x4(buf, stride, tu0, tu1); @@ -1290,7 +1294,7 @@ *tu2 = load_unaligned_u8_4x2(buf, stride); } -static inline void load_unaligned_u8_4x8(const uint8_t *buf, int stride, +static inline void load_unaligned_u8_4x8(const uint8_t *buf, ptrdiff_t stride, uint8x8_t *tu0, uint8x8_t *tu1, uint8x8_t *tu2, uint8x8_t *tu3) { load_unaligned_u8_4x4(buf, stride, tu0, tu1); @@ -1397,7 +1401,7 @@ } static inline uint16x4_t load_unaligned_u16_2x2(const uint16_t *buf, - int stride) { + ptrdiff_t stride) { uint32_t a; uint32x2_t a_u32; @@ -1418,7 +1422,7 @@ } static inline uint16x8_t load_unaligned_u16_4x2(const uint16_t *buf, - uint32_t stride) { + ptrdiff_t stride) { uint64_t a; uint64x2_t a_u64; @@ -1433,7 +1437,7 @@ } static inline int16x8_t load_unaligned_s16_4x2(const int16_t *buf, - uint32_t stride) { + ptrdiff_t stride) { int64_t a; int64x2_t a_s64; memcpy(&a, buf, 8); @@ -1446,14 +1450,14 @@ return vreinterpretq_s16_s64(a_s64); } -static inline void load_unaligned_u16_4x4(const uint16_t *buf, uint32_t stride, +static inline void load_unaligned_u16_4x4(const uint16_t *buf, ptrdiff_t stride, uint16x8_t *tu0, uint16x8_t *tu1) { *tu0 = load_unaligned_u16_4x2(buf, stride); buf += 2 * stride; *tu1 = load_unaligned_u16_4x2(buf, stride); } -static inline void load_s32_4x4(int32_t *s, int32_t p, int32x4_t *s1, +static inline void load_s32_4x4(int32_t *s, ptrdiff_t p, int32x4_t *s1, int32x4_t *s2, int32x4_t *s3, int32x4_t *s4) { *s1 = vld1q_s32(s); s += p; @@ -1464,7 +1468,7 @@ *s4 = vld1q_s32(s); } -static inline void store_s32_4x4(int32_t *s, int32_t p, int32x4_t s1, +static inline void store_s32_4x4(int32_t *s, ptrdiff_t p, int32x4_t s1, int32x4_t s2, int32x4_t s3, int32x4_t s4) { vst1q_s32(s, s1); s += p; @@ -1475,7 +1479,7 @@ vst1q_s32(s, s4); } -static inline void load_u32_4x4(uint32_t *s, int32_t p, uint32x4_t *s1, +static inline void load_u32_4x4(uint32_t *s, ptrdiff_t p, uint32x4_t *s1, uint32x4_t *s2, uint32x4_t *s3, uint32x4_t *s4) { *s1 = vld1q_u32(s); @@ -1487,7 +1491,7 @@ *s4 = vld1q_u32(s); } -static inline void store_u32_4x4(uint32_t *s, int32_t p, uint32x4_t s1, +static inline void store_u32_4x4(uint32_t *s, ptrdiff_t p, uint32x4_t s1, uint32x4_t s2, uint32x4_t s3, uint32x4_t s4) { vst1q_u32(s, s1); s += p; @@ -1578,14 +1582,14 @@ } // Store two blocks of 16-bits from a single vector. -static inline void store_u8x2_strided_x2(uint8_t *dst, uint32_t dst_stride, +static inline void store_u8x2_strided_x2(uint8_t *dst, ptrdiff_t dst_stride, uint8x8_t src) { store_u8_2x1_lane(dst, src, 0); dst += dst_stride; store_u8_2x1_lane(dst, src, 1); } -static inline void store_u8x2_strided_x4(uint8_t *dst, uint32_t dst_stride, +static inline void store_u8x2_strided_x4(uint8_t *dst, ptrdiff_t dst_stride, uint8x8_t src) { store_u8_2x1_lane(dst, src, 0); dst += dst_stride; @@ -1622,7 +1626,7 @@ } // Store two blocks of 32-bits from a single vector. -static inline void store_u16x2_strided_x2(uint16_t *dst, uint32_t dst_stride, +static inline void store_u16x2_strided_x2(uint16_t *dst, ptrdiff_t dst_stride, uint16x4_t src) { store_u16_2x1_lane(dst, src, 0); dst += dst_stride; @@ -1630,7 +1634,7 @@ } // Store two blocks of 64-bits from a single vector. -static inline void store_u16x4_strided_x2(uint16_t *dst, uint32_t dst_stride, +static inline void store_u16x4_strided_x2(uint16_t *dst, ptrdiff_t dst_stride, uint16x8_t src) { store_u16_4x1_lane(dst, src, 0); dst += dst_stride; @@ -1638,7 +1642,7 @@ } // Store two blocks of 64-bits from a single vector. -static inline void store_s16x4_strided_x2(int16_t *dst, int32_t dst_stride, +static inline void store_s16x4_strided_x2(int16_t *dst, ptrdiff_t dst_stride, int16x8_t src) { store_s16_4x1_lane(dst, src, 0); dst += dst_stride;
diff --git a/aom_dsp/arm/subpel_variance_neon.c b/aom_dsp/arm/subpel_variance_neon.c index 489c8d0..4e74737 100644 --- a/aom_dsp/arm/subpel_variance_neon.c +++ b/aom_dsp/arm/subpel_variance_neon.c
@@ -19,128 +19,7 @@ #include "aom_dsp/variance.h" #include "aom_dsp/arm/mem_neon.h" - -static void var_filter_block2d_bil_w4(const uint8_t *src_ptr, uint8_t *dst_ptr, - int src_stride, int pixel_step, - int dst_height, int filter_offset) { - const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); - const uint8x8_t f1 = vdup_n_u8(filter_offset); - - int i = dst_height; - do { - uint8x8_t s0 = load_unaligned_u8(src_ptr, src_stride); - uint8x8_t s1 = load_unaligned_u8(src_ptr + pixel_step, src_stride); - uint16x8_t blend = vmull_u8(s0, f0); - blend = vmlal_u8(blend, s1, f1); - uint8x8_t blend_u8 = vrshrn_n_u16(blend, 3); - vst1_u8(dst_ptr, blend_u8); - - src_ptr += 2 * src_stride; - dst_ptr += 2 * 4; - i -= 2; - } while (i != 0); -} - -static void var_filter_block2d_bil_w8(const uint8_t *src_ptr, uint8_t *dst_ptr, - int src_stride, int pixel_step, - int dst_height, int filter_offset) { - const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); - const uint8x8_t f1 = vdup_n_u8(filter_offset); - - int i = dst_height; - do { - uint8x8_t s0 = vld1_u8(src_ptr); - uint8x8_t s1 = vld1_u8(src_ptr + pixel_step); - uint16x8_t blend = vmull_u8(s0, f0); - blend = vmlal_u8(blend, s1, f1); - uint8x8_t blend_u8 = vrshrn_n_u16(blend, 3); - vst1_u8(dst_ptr, blend_u8); - - src_ptr += src_stride; - dst_ptr += 8; - } while (--i != 0); -} - -static void var_filter_block2d_bil_large(const uint8_t *src_ptr, - uint8_t *dst_ptr, int src_stride, - int pixel_step, int dst_width, - int dst_height, int filter_offset) { - const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); - const uint8x8_t f1 = vdup_n_u8(filter_offset); - - int i = dst_height; - do { - int j = 0; - do { - uint8x16_t s0 = vld1q_u8(src_ptr + j); - uint8x16_t s1 = vld1q_u8(src_ptr + j + pixel_step); - uint16x8_t blend_l = vmull_u8(vget_low_u8(s0), f0); - blend_l = vmlal_u8(blend_l, vget_low_u8(s1), f1); - uint16x8_t blend_h = vmull_u8(vget_high_u8(s0), f0); - blend_h = vmlal_u8(blend_h, vget_high_u8(s1), f1); - uint8x16_t blend_u8 = - vcombine_u8(vrshrn_n_u16(blend_l, 3), vrshrn_n_u16(blend_h, 3)); - vst1q_u8(dst_ptr + j, blend_u8); - - j += 16; - } while (j < dst_width); - - src_ptr += src_stride; - dst_ptr += dst_width; - } while (--i != 0); -} - -static void var_filter_block2d_bil_w16(const uint8_t *src_ptr, uint8_t *dst_ptr, - int src_stride, int pixel_step, - int dst_height, int filter_offset) { - var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 16, - dst_height, filter_offset); -} - -static void var_filter_block2d_bil_w32(const uint8_t *src_ptr, uint8_t *dst_ptr, - int src_stride, int pixel_step, - int dst_height, int filter_offset) { - var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 32, - dst_height, filter_offset); -} - -static void var_filter_block2d_bil_w64(const uint8_t *src_ptr, uint8_t *dst_ptr, - int src_stride, int pixel_step, - int dst_height, int filter_offset) { - var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 64, - dst_height, filter_offset); -} - -static void var_filter_block2d_bil_w128(const uint8_t *src_ptr, - uint8_t *dst_ptr, int src_stride, - int pixel_step, int dst_height, - int filter_offset) { - var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 128, - dst_height, filter_offset); -} - -static void var_filter_block2d_avg(const uint8_t *src_ptr, uint8_t *dst_ptr, - int src_stride, int pixel_step, - int dst_width, int dst_height) { - // We only specialise on the filter values for large block sizes (>= 16x16.) - assert(dst_width >= 16 && dst_width % 16 == 0); - - int i = dst_height; - do { - int j = 0; - do { - uint8x16_t s0 = vld1q_u8(src_ptr + j); - uint8x16_t s1 = vld1q_u8(src_ptr + j + pixel_step); - uint8x16_t avg = vrhaddq_u8(s0, s1); - vst1q_u8(dst_ptr + j, avg); - - j += 16; - } while (j < dst_width); - - src_ptr += src_stride; - dst_ptr += dst_width; - } while (--i != 0); -} +#include "aom_dsp/arm/subpel_variance_neon.h" #define SUBPEL_VARIANCE_WXH_NEON(w, h, padding) \ unsigned int aom_sub_pixel_variance##w##x##h##_neon( \ @@ -151,61 +30,62 @@ var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, (h + padding), \ xoffset); \ var_filter_block2d_bil_w##w(tmp0, tmp1, w, w, h, yoffset); \ - return aom_variance##w##x##h(tmp1, w, ref, ref_stride, sse); \ + return aom_variance##w##x##h##_neon(tmp1, w, ref, ref_stride, sse); \ } -#define SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON(w, h, padding) \ - unsigned int aom_sub_pixel_variance##w##x##h##_neon( \ - const uint8_t *src, int src_stride, int xoffset, int yoffset, \ - const uint8_t *ref, int ref_stride, unsigned int *sse) { \ - if (xoffset == 0) { \ - if (yoffset == 0) { \ - return aom_variance##w##x##h(src, src_stride, ref, ref_stride, sse); \ - } else if (yoffset == 4) { \ - uint8_t tmp[w * h]; \ - var_filter_block2d_avg(src, tmp, src_stride, src_stride, w, h); \ - return aom_variance##w##x##h(tmp, w, ref, ref_stride, sse); \ - } else { \ - uint8_t tmp[w * h]; \ - var_filter_block2d_bil_w##w(src, tmp, src_stride, src_stride, h, \ - yoffset); \ - return aom_variance##w##x##h(tmp, w, ref, ref_stride, sse); \ - } \ - } else if (xoffset == 4) { \ - uint8_t tmp0[w * (h + padding)]; \ - if (yoffset == 0) { \ - var_filter_block2d_avg(src, tmp0, src_stride, 1, w, h); \ - return aom_variance##w##x##h(tmp0, w, ref, ref_stride, sse); \ - } else if (yoffset == 4) { \ - uint8_t tmp1[w * (h + padding)]; \ - var_filter_block2d_avg(src, tmp0, src_stride, 1, w, (h + padding)); \ - var_filter_block2d_avg(tmp0, tmp1, w, w, w, h); \ - return aom_variance##w##x##h(tmp1, w, ref, ref_stride, sse); \ - } else { \ - uint8_t tmp1[w * (h + padding)]; \ - var_filter_block2d_avg(src, tmp0, src_stride, 1, w, (h + padding)); \ - var_filter_block2d_bil_w##w(tmp0, tmp1, w, w, h, yoffset); \ - return aom_variance##w##x##h(tmp1, w, ref, ref_stride, sse); \ - } \ - } else { \ - uint8_t tmp0[w * (h + padding)]; \ - if (yoffset == 0) { \ - var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, h, xoffset); \ - return aom_variance##w##x##h(tmp0, w, ref, ref_stride, sse); \ - } else if (yoffset == 4) { \ - uint8_t tmp1[w * h]; \ - var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, (h + padding), \ - xoffset); \ - var_filter_block2d_avg(tmp0, tmp1, w, w, w, h); \ - return aom_variance##w##x##h(tmp1, w, ref, ref_stride, sse); \ - } else { \ - uint8_t tmp1[w * h]; \ - var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, (h + padding), \ - xoffset); \ - var_filter_block2d_bil_w##w(tmp0, tmp1, w, w, h, yoffset); \ - return aom_variance##w##x##h(tmp1, w, ref, ref_stride, sse); \ - } \ - } \ +#define SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON(w, h, padding) \ + unsigned int aom_sub_pixel_variance##w##x##h##_neon( \ + const uint8_t *src, int src_stride, int xoffset, int yoffset, \ + const uint8_t *ref, int ref_stride, unsigned int *sse) { \ + if (xoffset == 0) { \ + if (yoffset == 0) { \ + return aom_variance##w##x##h##_neon(src, src_stride, ref, ref_stride, \ + sse); \ + } else if (yoffset == 4) { \ + uint8_t tmp[w * h]; \ + var_filter_block2d_avg(src, tmp, src_stride, src_stride, w, h); \ + return aom_variance##w##x##h##_neon(tmp, w, ref, ref_stride, sse); \ + } else { \ + uint8_t tmp[w * h]; \ + var_filter_block2d_bil_w##w(src, tmp, src_stride, src_stride, h, \ + yoffset); \ + return aom_variance##w##x##h##_neon(tmp, w, ref, ref_stride, sse); \ + } \ + } else if (xoffset == 4) { \ + uint8_t tmp0[w * (h + padding)]; \ + if (yoffset == 0) { \ + var_filter_block2d_avg(src, tmp0, src_stride, 1, w, h); \ + return aom_variance##w##x##h##_neon(tmp0, w, ref, ref_stride, sse); \ + } else if (yoffset == 4) { \ + uint8_t tmp1[w * (h + padding)]; \ + var_filter_block2d_avg(src, tmp0, src_stride, 1, w, (h + padding)); \ + var_filter_block2d_avg(tmp0, tmp1, w, w, w, h); \ + return aom_variance##w##x##h##_neon(tmp1, w, ref, ref_stride, sse); \ + } else { \ + uint8_t tmp1[w * (h + padding)]; \ + var_filter_block2d_avg(src, tmp0, src_stride, 1, w, (h + padding)); \ + var_filter_block2d_bil_w##w(tmp0, tmp1, w, w, h, yoffset); \ + return aom_variance##w##x##h##_neon(tmp1, w, ref, ref_stride, sse); \ + } \ + } else { \ + uint8_t tmp0[w * (h + padding)]; \ + if (yoffset == 0) { \ + var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, h, xoffset); \ + return aom_variance##w##x##h##_neon(tmp0, w, ref, ref_stride, sse); \ + } else if (yoffset == 4) { \ + uint8_t tmp1[w * h]; \ + var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, (h + padding), \ + xoffset); \ + var_filter_block2d_avg(tmp0, tmp1, w, w, w, h); \ + return aom_variance##w##x##h##_neon(tmp1, w, ref, ref_stride, sse); \ + } else { \ + uint8_t tmp1[w * h]; \ + var_filter_block2d_bil_w##w(src, tmp0, src_stride, 1, (h + padding), \ + xoffset); \ + var_filter_block2d_bil_w##w(tmp0, tmp1, w, w, h, yoffset); \ + return aom_variance##w##x##h##_neon(tmp1, w, ref, ref_stride, sse); \ + } \ + } \ } SUBPEL_VARIANCE_WXH_NEON(4, 4, 2)
diff --git a/aom_dsp/arm/subpel_variance_neon.h b/aom_dsp/arm/subpel_variance_neon.h new file mode 100644 index 0000000..18baba6 --- /dev/null +++ b/aom_dsp/arm/subpel_variance_neon.h
@@ -0,0 +1,148 @@ +/* + * Copyright (c) 2026, 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. + */ + +#ifndef AOM_AOM_DSP_ARM_SUBPEL_VARIANCE_NEON_H_ +#define AOM_AOM_DSP_ARM_SUBPEL_VARIANCE_NEON_H_ + +#include <arm_neon.h> +#include "aom_dsp/arm/mem_neon.h" +#include "config/aom_dsp_rtcd.h" + +static inline void var_filter_block2d_bil_w4(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_height, + int filter_offset) { + const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); + const uint8x8_t f1 = vdup_n_u8(filter_offset); + + int i = dst_height; + do { + uint8x8_t s0 = load_unaligned_u8(src_ptr, src_stride); + uint8x8_t s1 = load_unaligned_u8(src_ptr + pixel_step, src_stride); + uint16x8_t blend = vmull_u8(s0, f0); + blend = vmlal_u8(blend, s1, f1); + uint8x8_t blend_u8 = vrshrn_n_u16(blend, 3); + vst1_u8(dst_ptr, blend_u8); + + src_ptr += 2 * src_stride; + dst_ptr += 2 * 4; + i -= 2; + } while (i != 0); +} + +static inline void var_filter_block2d_bil_w8(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_height, + int filter_offset) { + const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); + const uint8x8_t f1 = vdup_n_u8(filter_offset); + + int i = dst_height; + do { + uint8x8_t s0 = vld1_u8(src_ptr); + uint8x8_t s1 = vld1_u8(src_ptr + pixel_step); + uint16x8_t blend = vmull_u8(s0, f0); + blend = vmlal_u8(blend, s1, f1); + uint8x8_t blend_u8 = vrshrn_n_u16(blend, 3); + vst1_u8(dst_ptr, blend_u8); + + src_ptr += src_stride; + dst_ptr += 8; + } while (--i != 0); +} + +static inline void var_filter_block2d_bil_large(const uint8_t *src_ptr, + uint8_t *dst_ptr, + int src_stride, int pixel_step, + int dst_width, int dst_height, + int filter_offset) { + const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); + const uint8x8_t f1 = vdup_n_u8(filter_offset); + + int i = dst_height; + do { + int j = 0; + do { + uint8x16_t s0 = vld1q_u8(src_ptr + j); + uint8x16_t s1 = vld1q_u8(src_ptr + j + pixel_step); + uint16x8_t blend_l = vmull_u8(vget_low_u8(s0), f0); + blend_l = vmlal_u8(blend_l, vget_low_u8(s1), f1); + uint16x8_t blend_h = vmull_u8(vget_high_u8(s0), f0); + blend_h = vmlal_u8(blend_h, vget_high_u8(s1), f1); + uint8x16_t blend_u8 = + vcombine_u8(vrshrn_n_u16(blend_l, 3), vrshrn_n_u16(blend_h, 3)); + vst1q_u8(dst_ptr + j, blend_u8); + + j += 16; + } while (j < dst_width); + + src_ptr += src_stride; + dst_ptr += dst_width; + } while (--i != 0); +} + +static inline void var_filter_block2d_bil_w16(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_height, + int filter_offset) { + var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 16, + dst_height, filter_offset); +} + +static inline void var_filter_block2d_bil_w32(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_height, + int filter_offset) { + var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 32, + dst_height, filter_offset); +} + +static inline void var_filter_block2d_bil_w64(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_height, + int filter_offset) { + var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 64, + dst_height, filter_offset); +} + +static inline void var_filter_block2d_bil_w128(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_height, + int filter_offset) { + var_filter_block2d_bil_large(src_ptr, dst_ptr, src_stride, pixel_step, 128, + dst_height, filter_offset); +} + +static inline void var_filter_block2d_avg(const uint8_t *src_ptr, + uint8_t *dst_ptr, int src_stride, + int pixel_step, int dst_width, + int dst_height) { + // We only specialise on the filter values for large block sizes (>= 16x16.) + assert(dst_width >= 16 && dst_width % 16 == 0); + + int i = dst_height; + do { + int j = 0; + do { + uint8x16_t s0 = vld1q_u8(src_ptr + j); + uint8x16_t s1 = vld1q_u8(src_ptr + j + pixel_step); + uint8x16_t avg = vrhaddq_u8(s0, s1); + vst1q_u8(dst_ptr + j, avg); + + j += 16; + } while (j < dst_width); + + src_ptr += src_stride; + dst_ptr += dst_width; + } while (--i != 0); +} + +#endif // AOM_AOM_DSP_ARM_SUBPEL_VARIANCE_NEON_H_
diff --git a/aom_dsp/arm/subpel_variance_neon_dotprod.c b/aom_dsp/arm/subpel_variance_neon_dotprod.c new file mode 100644 index 0000000..6cb12d8 --- /dev/null +++ b/aom_dsp/arm/subpel_variance_neon_dotprod.c
@@ -0,0 +1,245 @@ +/* + * Copyright (c) 2026, 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 <arm_neon.h> + +#include "aom_dsp/arm/subpel_variance_neon.h" + +static inline void bil_variance_8xh_neon_dotprod( + const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, + int pixel_step, int h, uint32_t *sse, int *sum, int filter_offset) { + const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); + const uint8x8_t f1 = vdup_n_u8(filter_offset); + + uint32x4_t src_sum = vdupq_n_u32(0); + uint32x4_t ref_sum = vdupq_n_u32(0); + uint32x4_t sse_u32 = vdupq_n_u32(0); + + do { + uint8x8_t s0_lo = vld1_u8(src); + uint8x8_t s1_lo = vld1_u8(src + pixel_step); + uint16x8_t blend_l = vmull_u8(s0_lo, f0); + blend_l = vmlal_u8(blend_l, s1_lo, f1); + uint8x8_t s0_hi = vld1_u8(src + src_stride); + uint8x8_t s1_hi = vld1_u8(src + src_stride + pixel_step); + uint16x8_t blend_h = vmull_u8(s0_hi, f0); + blend_h = vmlal_u8(blend_h, s1_hi, f1); + uint8x16_t s = + vcombine_u8(vrshrn_n_u16(blend_l, 3), vrshrn_n_u16(blend_h, 3)); + uint8x16_t r = load_u8_8x2(ref, ref_stride); + + src_sum = vdotq_u32(src_sum, s, vdupq_n_u8(1)); + ref_sum = vdotq_u32(ref_sum, r, vdupq_n_u8(1)); + + uint8x16_t abs_diff = vabdq_u8(s, r); + sse_u32 = vdotq_u32(sse_u32, abs_diff, abs_diff); + + src += 2 * src_stride; + ref += 2 * ref_stride; + h -= 2; + } while (h != 0); + + int32x4_t sum_diff = + vsubq_s32(vreinterpretq_s32_u32(src_sum), vreinterpretq_s32_u32(ref_sum)); + *sum = vaddvq_s32(sum_diff); + *sse = vaddvq_u32(sse_u32); +} + +static inline void bil_variance_neon_dotprod(const uint8_t *src, int src_stride, + const uint8_t *ref, int ref_stride, + int pixel_step, int w, int h, + uint32_t *sse, int *sum, + int filter_offset) { + assert(w != 4); + + if (w == 8) { + bil_variance_8xh_neon_dotprod(src, src_stride, ref, ref_stride, pixel_step, + h, sse, sum, filter_offset); + return; + } + + const uint8x8_t f0 = vdup_n_u8(8 - filter_offset); + const uint8x8_t f1 = vdup_n_u8(filter_offset); + + uint32x4_t src_sum = vdupq_n_u32(0); + uint32x4_t ref_sum = vdupq_n_u32(0); + uint32x4_t sse_u32 = vdupq_n_u32(0); + + do { + int i = 0; + do { + uint8x16_t s0 = vld1q_u8(src + i); + uint8x16_t s1 = vld1q_u8(src + i + pixel_step); + uint16x8_t blend_l = vmull_u8(vget_low_u8(s0), f0); + blend_l = vmlal_u8(blend_l, vget_low_u8(s1), f1); + uint16x8_t blend_h = vmull_u8(vget_high_u8(s0), f0); + blend_h = vmlal_u8(blend_h, vget_high_u8(s1), f1); + uint8x16_t s = + vcombine_u8(vrshrn_n_u16(blend_l, 3), vrshrn_n_u16(blend_h, 3)); + uint8x16_t r = vld1q_u8(ref + i); + + src_sum = vdotq_u32(src_sum, s, vdupq_n_u8(1)); + ref_sum = vdotq_u32(ref_sum, r, vdupq_n_u8(1)); + + uint8x16_t abs_diff = vabdq_u8(s, r); + sse_u32 = vdotq_u32(sse_u32, abs_diff, abs_diff); + + i += 16; + } while (i < w); + + src += src_stride; + ref += ref_stride; + } while (--h != 0); + + int32x4_t sum_diff = + vsubq_s32(vreinterpretq_s32_u32(src_sum), vreinterpretq_s32_u32(ref_sum)); + *sum = vaddvq_s32(sum_diff); + *sse = vaddvq_u32(sse_u32); +} + +static inline void avg_variance_neon_dotprod(const uint8_t *src, int src_stride, + const uint8_t *ref, int ref_stride, + int pixel_step, int w, int h, + uint32_t *sse, int *sum) { + assert(w >= 16 && w % 16 == 0); + uint32x4_t src_sum = vdupq_n_u32(0); + uint32x4_t ref_sum = vdupq_n_u32(0); + uint32x4_t sse_u32 = vdupq_n_u32(0); + + do { + int i = 0; + do { + uint8x16_t s0 = vld1q_u8(src + i); + uint8x16_t s1 = vld1q_u8(src + i + pixel_step); + uint8x16_t s = vrhaddq_u8(s0, s1); + uint8x16_t r = vld1q_u8(ref + i); + + src_sum = vdotq_u32(src_sum, s, vdupq_n_u8(1)); + ref_sum = vdotq_u32(ref_sum, r, vdupq_n_u8(1)); + + uint8x16_t abs_diff = vabdq_u8(s, r); + sse_u32 = vdotq_u32(sse_u32, abs_diff, abs_diff); + + i += 16; + } while (i < w); + + src += src_stride; + ref += ref_stride; + } while (--h != 0); + + int32x4_t sum_diff = + vsubq_s32(vreinterpretq_s32_u32(src_sum), vreinterpretq_s32_u32(ref_sum)); + *sum = vaddvq_s32(sum_diff); + *sse = vaddvq_u32(sse_u32); +} + +#define SUBPEL_VARIANCE_4XH_NEON_DOTPROD(h, padding) \ + unsigned int aom_sub_pixel_variance4x##h##_neon_dotprod( \ + const uint8_t *src, int src_stride, int xoffset, int yoffset, \ + const uint8_t *ref, int ref_stride, uint32_t *sse) { \ + uint8_t tmp0[4 * (h + padding)]; \ + uint8_t tmp1[4 * h]; \ + var_filter_block2d_bil_w4(src, tmp0, src_stride, 1, (h + padding), \ + xoffset); \ + var_filter_block2d_bil_w4(tmp0, tmp1, 4, 4, h, yoffset); \ + return aom_variance4x##h##_neon_dotprod(tmp1, 4, ref, ref_stride, sse); \ + } + +#define SUBPEL_VARIANCE_WXH_NEON_DOTPROD(w, h, shift, padding) \ + unsigned int aom_sub_pixel_variance##w##x##h##_neon_dotprod( \ + const uint8_t *src, int src_stride, int xoffset, int yoffset, \ + const uint8_t *ref, int ref_stride, unsigned int *sse) { \ + uint8_t tmp[w * (h + padding)]; \ + int sum; \ + var_filter_block2d_bil_w##w(src, tmp, src_stride, 1, h + padding, \ + xoffset); \ + bil_variance_neon_dotprod(tmp, w, ref, ref_stride, w, w, h, sse, &sum, \ + yoffset); \ + return *sse - (uint32_t)(((int64_t)sum * sum) >> shift); \ + } + +#define SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(w, h, shift, padding) \ + unsigned int aom_sub_pixel_variance##w##x##h##_neon_dotprod( \ + const uint8_t *src, int src_stride, int xoffset, int yoffset, \ + const uint8_t *ref, int ref_stride, unsigned int *sse) { \ + int sum; \ + if (xoffset == 0) { \ + if (yoffset == 0) { \ + return aom_variance##w##x##h##_neon_dotprod(src, src_stride, ref, \ + ref_stride, sse); \ + } else if (yoffset == 4) { \ + avg_variance_neon_dotprod(src, src_stride, ref, ref_stride, \ + src_stride, w, h, sse, &sum); \ + } else { \ + bil_variance_neon_dotprod(src, src_stride, ref, ref_stride, \ + src_stride, w, h, sse, &sum, yoffset); \ + } \ + } else if (xoffset == 4) { \ + uint8_t tmp[w * (h + padding)]; \ + if (yoffset == 0) { \ + avg_variance_neon_dotprod(src, src_stride, ref, ref_stride, 1, w, h, \ + sse, &sum); \ + } else if (yoffset == 4) { \ + var_filter_block2d_avg(src, tmp, src_stride, 1, w, h + padding); \ + avg_variance_neon_dotprod(tmp, w, ref, ref_stride, w, w, h, sse, \ + &sum); \ + } else { \ + var_filter_block2d_avg(src, tmp, src_stride, 1, w, h + padding); \ + bil_variance_neon_dotprod(tmp, w, ref, ref_stride, w, w, h, sse, &sum, \ + yoffset); \ + } \ + } else { \ + uint8_t tmp[w * (h + padding)]; \ + if (yoffset == 0) { \ + bil_variance_neon_dotprod(src, src_stride, ref, ref_stride, 1, w, h, \ + sse, &sum, xoffset); \ + } else if (yoffset == 4) { \ + var_filter_block2d_bil_w##w(src, tmp, src_stride, 1, h + padding, \ + xoffset); \ + avg_variance_neon_dotprod(tmp, w, ref, ref_stride, w, w, h, sse, \ + &sum); \ + } else { \ + var_filter_block2d_bil_w##w(src, tmp, src_stride, 1, h + padding, \ + xoffset); \ + bil_variance_neon_dotprod(tmp, w, ref, ref_stride, w, w, h, sse, &sum, \ + yoffset); \ + } \ + } \ + return *sse - (uint32_t)(((int64_t)sum * sum) >> shift); \ + } + +SUBPEL_VARIANCE_4XH_NEON_DOTPROD(8, 2) +SUBPEL_VARIANCE_WXH_NEON_DOTPROD(8, 4, 5, 1) +SUBPEL_VARIANCE_WXH_NEON_DOTPROD(8, 8, 6, 1) +SUBPEL_VARIANCE_WXH_NEON_DOTPROD(8, 16, 7, 1) +SUBPEL_VARIANCE_WXH_NEON_DOTPROD(16, 8, 7, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(16, 16, 8, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(16, 32, 9, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(32, 16, 9, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(32, 32, 10, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(32, 64, 11, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(64, 32, 11, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(64, 64, 12, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(64, 128, 13, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(128, 64, 13, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(128, 128, 14, 1) + +#if !CONFIG_REALTIME_ONLY +SUBPEL_VARIANCE_4XH_NEON_DOTPROD(16, 2) +SUBPEL_VARIANCE_WXH_NEON_DOTPROD(8, 32, 8, 1) +SUBPEL_VARIANCE_WXH_NEON_DOTPROD(16, 4, 6, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(16, 64, 10, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(32, 8, 8, 1) +SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD(64, 16, 10, 1) +#endif // !CONFIG_REALTIME_ONLY + +#undef SUBPEL_VARIANCE_WXH_NEON_DOTPROD +#undef SPECIALIZED_SUBPEL_VARIANCE_WXH_NEON_DOTPROD
diff --git a/aom_dsp/arm/transpose_neon.h b/aom_dsp/arm/transpose_neon.h index 88df0d6..2a548bb 100644 --- a/aom_dsp/arm/transpose_neon.h +++ b/aom_dsp/arm/transpose_neon.h
@@ -17,6 +17,155 @@ #include "aom_dsp/aom_dsp_common.h" // For AOM_FORCE_INLINE. #include "config/aom_config.h" +static inline void transpose_concat_elems_u8_4x4(uint8x8_t a0, uint8x8_t a1, + uint8x8_t a2, uint8x8_t a3, + uint8x16_t *b) { + // Transpose 8-bit elements and concatenate result rows as follows: + // a0: 00, 01, 02, 03, XX, XX, XX, XX + // a1: 10, 11, 12, 13, XX, XX, XX, XX + // a2: 20, 21, 22, 23, XX, XX, XX, XX + // a3: 30, 31, 32, 33, XX, XX, XX, XX + // + // b: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 + + uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); + uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); + uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); + uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); + + uint8x16_t a02 = vzipq_u8(a0q, a2q).val[0]; + uint8x16_t a13 = vzipq_u8(a1q, a3q).val[0]; + + *b = vzipq_u8(a02, a13).val[0]; +} + +static inline void transpose_concat_elems_u8_8x4(uint8x8_t a0, uint8x8_t a1, + uint8x8_t a2, uint8x8_t a3, + uint8x16_t *b0, + uint8x16_t *b1) { + // Transpose 8-bit elements and concatenate result rows as follows: + // a0: 00, 01, 02, 03, 04, 05, 06, 07 + // a1: 10, 11, 12, 13, 14, 15, 16, 17 + // a2: 20, 21, 22, 23, 24, 25, 26, 27 + // a3: 30, 31, 32, 33, 34, 35, 36, 37 + // + // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 + // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 + + uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); + uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); + uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); + uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); + + uint8x16_t a02 = vzipq_u8(a0q, a2q).val[0]; + uint8x16_t a13 = vzipq_u8(a1q, a3q).val[0]; + + uint8x16x2_t a0123 = vzipq_u8(a02, a13); + + *b0 = a0123.val[0]; + *b1 = a0123.val[1]; +} + +static inline void transpose_concat_elems_s8_4x4(int8x8_t a0, int8x8_t a1, + int8x8_t a2, int8x8_t a3, + int8x16_t *b) { + // Transpose 8-bit elements and concatenate result rows as follows: + // a0: 00, 01, 02, 03, XX, XX, XX, XX + // a1: 10, 11, 12, 13, XX, XX, XX, XX + // a2: 20, 21, 22, 23, XX, XX, XX, XX + // a3: 30, 31, 32, 33, XX, XX, XX, XX + // + // b: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 + + int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); + int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); + int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); + int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); + + int8x16_t a02 = vzipq_s8(a0q, a2q).val[0]; + int8x16_t a13 = vzipq_s8(a1q, a3q).val[0]; + + *b = vzipq_s8(a02, a13).val[0]; +} + +static inline void transpose_concat_elems_s8_8x4(int8x8_t a0, int8x8_t a1, + int8x8_t a2, int8x8_t a3, + int8x16_t *b0, int8x16_t *b1) { + // Transpose 8-bit elements and concatenate result rows as follows: + // a0: 00, 01, 02, 03, 04, 05, 06, 07 + // a1: 10, 11, 12, 13, 14, 15, 16, 17 + // a2: 20, 21, 22, 23, 24, 25, 26, 27 + // a3: 30, 31, 32, 33, 34, 35, 36, 37 + // + // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 + // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 + + int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); + int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); + int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); + int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); + + int8x16_t a02 = vzipq_s8(a0q, a2q).val[0]; + int8x16_t a13 = vzipq_s8(a1q, a3q).val[0]; + + int8x16x2_t a0123 = vzipq_s8(a02, a13); + + *b0 = a0123.val[0]; + *b1 = a0123.val[1]; +} + +static inline void transpose_concat_elems_s16_4x4(int16x4_t s0, int16x4_t s1, + int16x4_t s2, int16x4_t s3, + int16x8_t res[2]) { + // Transpose 16-bit elements and concatenate result rows as follows: + // s0: 00, 01, 02, 03 + // s1: 10, 11, 12, 13 + // s2: 20, 21, 22, 23 + // s3: 30, 31, 32, 33 + // + // res[0]: 00 10 20 30 01 11 21 31 + // res[1]: 02 12 22 32 03 13 23 33 + + int16x8_t s0q = vcombine_s16(s0, vdup_n_s16(0)); + int16x8_t s1q = vcombine_s16(s1, vdup_n_s16(0)); + int16x8_t s2q = vcombine_s16(s2, vdup_n_s16(0)); + int16x8_t s3q = vcombine_s16(s3, vdup_n_s16(0)); + + int16x8_t s02 = vzipq_s16(s0q, s2q).val[0]; + int16x8_t s13 = vzipq_s16(s1q, s3q).val[0]; + + int16x8x2_t s0123 = vzipq_s16(s02, s13); + + res[0] = s0123.val[0]; + res[1] = s0123.val[1]; +} + +static inline void transpose_concat_elems_s16_8x4(int16x8_t s0, int16x8_t s1, + int16x8_t s2, int16x8_t s3, + int16x8_t res[4]) { + // Transpose 16-bit elements and concatenate result rows as follows: + // s0: 00, 01, 02, 03, 04, 05, 06, 07 + // s1: 10, 11, 12, 13, 14, 15, 16, 17 + // s2: 20, 21, 22, 23, 24, 25, 26, 27 + // s3: 30, 31, 32, 33, 34, 35, 36, 37 + // + // res[0]: 00 10 20 30 01 11 21 31 + // res[1]: 02 12 22 32 03 13 23 33 + // res[2]: 04 14 24 34 05 15 25 35 + // res[3]: 06 16 26 36 07 17 27 37 + + int16x8x2_t s02 = vzipq_s16(s0, s2); + int16x8x2_t s13 = vzipq_s16(s1, s3); + + int16x8x2_t s0123_lo = vzipq_s16(s02.val[0], s13.val[0]); + int16x8x2_t s0123_hi = vzipq_s16(s02.val[1], s13.val[1]); + + res[0] = s0123_lo.val[0]; + res[1] = s0123_lo.val[1]; + res[2] = s0123_hi.val[0]; + res[3] = s0123_hi.val[1]; +} + static inline void transpose_elems_u8_8x8( uint8x8_t a0, uint8x8_t a1, uint8x8_t a2, uint8x8_t a3, uint8x8_t a4, uint8x8_t a5, uint8x8_t a6, uint8x8_t a7, uint8x8_t *o0, uint8x8_t *o1,
diff --git a/aom_dsp/arm/variance_neon_dotprod.c b/aom_dsp/arm/variance_neon_dotprod.c index dd03443..3a3965e 100644 --- a/aom_dsp/arm/variance_neon_dotprod.c +++ b/aom_dsp/arm/variance_neon_dotprod.c
@@ -178,7 +178,7 @@ return *sse - (uint32_t)(((int64_t)sum * sum) >> shift); \ } -VARIANCE_WXH_NEON_DOTPROD(4, 4, 4) +// The Armv8.0 Neon implementation is faster than Neon DotProd for 4x4. VARIANCE_WXH_NEON_DOTPROD(4, 8, 5) VARIANCE_WXH_NEON_DOTPROD(8, 4, 5)
diff --git a/aom_dsp/avg.c b/aom_dsp/avg.c index 5cba8a8..6cfbfdd 100644 --- a/aom_dsp/avg.c +++ b/aom_dsp/avg.c
@@ -119,18 +119,20 @@ int16_t buffer2[16]; int16_t *tmp_buf = &buffer[0]; for (idx = 0; idx < 4; ++idx) { - hadamard_col4(src_diff, src_stride, tmp_buf); // src_diff: 9 bit - // dynamic range [-255, 255] + // src_diff: 9 bit (8b), 13 bit (HBD) + // dynamic range [-255, 255] (8b), [-4095, 4095] (HBD) + hadamard_col4(src_diff, src_stride, tmp_buf); tmp_buf += 4; ++src_diff; } tmp_buf = &buffer[0]; for (idx = 0; idx < 4; ++idx) { - hadamard_col4(tmp_buf, 4, buffer2 + 4 * idx); // tmp_buf: 12 bit - // dynamic range [-2040, 2040] - // buffer2: 15 bit - // dynamic range [-16320, 16320] + // tmp_buf: 10 bit (8b), 14 bit (HBD) + // dynamic range [-510, 510] (8b), [-8190, 8190] (HBD) + // buffer2: 11 bit (8b), 15 bit (HBD) + // dynamic range [-1020, 1020] (8b), [-16380, 16380] (HBD) + hadamard_col4(tmp_buf, 4, buffer2 + 4 * idx); ++tmp_buf; }
diff --git a/aom_dsp/grain_table.c b/aom_dsp/grain_table.c index 6c525d9..38ca1e4 100644 --- a/aom_dsp/grain_table.c +++ b/aom_dsp/grain_table.c
@@ -30,6 +30,7 @@ * cCr <ar_coeff_cr_0> .... * E <start-time> ... */ +#include <inttypes.h> #include <string.h> #include <stdio.h> #include "aom_dsp/aom_dsp_common.h" @@ -50,7 +51,6 @@ if (num_read != 5) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read entry header. Read %d != 5", num_read); - return; } if (pars->update_parameters) { num_read = fscanf(file, "p %d %d %d %d %d %d %d %d %d %d %d %d\n", @@ -63,83 +63,70 @@ aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read entry params. Read %d != 12", num_read); - return; } if (!fscanf(file, "\tsY %d ", &pars->num_y_points)) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read num y points"); - return; } for (int i = 0; i < pars->num_y_points; ++i) { if (2 != fscanf(file, "%d %d", &pars->scaling_points_y[i][0], &pars->scaling_points_y[i][1])) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read y scaling points"); - return; } } if (!fscanf(file, "\n\tsCb %d", &pars->num_cb_points)) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read num cb points"); - return; } for (int i = 0; i < pars->num_cb_points; ++i) { if (2 != fscanf(file, "%d %d", &pars->scaling_points_cb[i][0], &pars->scaling_points_cb[i][1])) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read cb scaling points"); - return; } } if (!fscanf(file, "\n\tsCr %d", &pars->num_cr_points)) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read num cr points"); - return; } for (int i = 0; i < pars->num_cr_points; ++i) { if (2 != fscanf(file, "%d %d", &pars->scaling_points_cr[i][0], &pars->scaling_points_cr[i][1])) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read cr scaling points"); - return; } } if (fscanf(file, "\n\tcY")) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read Y coeffs header (cY)"); - return; } const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1); for (int i = 0; i < n; ++i) { if (1 != fscanf(file, "%d", &pars->ar_coeffs_y[i])) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read Y coeffs"); - return; } } if (fscanf(file, "\n\tcCb")) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read Cb coeffs header (cCb)"); - return; } for (int i = 0; i <= n; ++i) { if (1 != fscanf(file, "%d", &pars->ar_coeffs_cb[i])) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read Cb coeffs"); - return; } } if (fscanf(file, "\n\tcCr")) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable read to Cr coeffs header (cCr)"); - return; } for (int i = 0; i <= n; ++i) { if (1 != fscanf(file, "%d", &pars->ar_coeffs_cr[i])) { aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to read Cr coeffs"); - return; } } (void)fscanf(file, "\n"); @@ -276,74 +263,84 @@ aom_codec_err_t aom_film_grain_table_read( aom_film_grain_table_t *t, const char *filename, struct aom_internal_error_info *error_info) { - FILE *file = fopen(filename, "rb"); - if (!file) { - aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open %s", - filename); - return error_info->error_code; - } + FILE *const file = fopen(filename, "rb"); error_info->error_code = AOM_CODEC_OK; - // Read in one extra character as there should be white space after - // the header. - char magic[9]; - if (!fread(magic, 9, 1, file) || memcmp(magic, kFileMagic, 8)) { - aom_internal_error(error_info, AOM_CODEC_ERROR, - "Unable to read (or invalid) file magic"); - fclose(file); - return error_info->error_code; + if (!file) { + error_info->error_code = AOM_CODEC_ERROR; + return AOM_CODEC_ERROR; } - aom_film_grain_table_entry_t *prev_entry = NULL; - while (!feof(file)) { - aom_film_grain_table_entry_t *entry = aom_malloc(sizeof(*entry)); - if (!entry) { - aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, - "Unable to allocate grain table entry"); - break; + error_info->setjmp = 1; + if (setjmp(error_info->jmp) == 0) { + // Read in one extra character as there should be white space after + // the header. + char magic[9]; + if (!fread(magic, 9, 1, file) || memcmp(magic, kFileMagic, 8)) { + aom_internal_error(error_info, AOM_CODEC_ERROR, + "Unable to read (or invalid) file magic"); } - memset(entry, 0, sizeof(*entry)); - grain_table_entry_read(file, error_info, entry); - entry->next = NULL; - if (prev_entry) prev_entry->next = entry; - if (!t->head) t->head = entry; - t->tail = entry; - prev_entry = entry; + aom_film_grain_table_entry_t *prev_entry = NULL; + for (;;) { + // Check for end of file before attempting to read. + int c = fgetc(file); + if (c == EOF) { + break; + } + ungetc(c, file); - if (error_info->error_code != AOM_CODEC_OK) break; + aom_film_grain_table_entry_t *entry = aom_malloc(sizeof(*entry)); + if (!entry) { + aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, + "Unable to allocate grain table entry"); + } + memset(entry, 0, sizeof(*entry)); + grain_table_entry_read(file, error_info, entry); + entry->next = NULL; + + if (prev_entry) prev_entry->next = entry; + if (!t->head) t->head = entry; + t->tail = entry; + prev_entry = entry; + } } fclose(file); + error_info->setjmp = 0; + return error_info->error_code; } aom_codec_err_t aom_film_grain_table_write( const aom_film_grain_table_t *t, const char *filename, struct aom_internal_error_info *error_info) { + FILE *const file = fopen(filename, "wb"); error_info->error_code = AOM_CODEC_OK; - FILE *file = fopen(filename, "wb"); if (!file) { - aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open file %s", - filename); - return error_info->error_code; + error_info->error_code = AOM_CODEC_ERROR; + return AOM_CODEC_ERROR; } - if (!fwrite(kFileMagic, 8, 1, file)) { - aom_internal_error(error_info, AOM_CODEC_ERROR, - "Unable to write file magic"); - fclose(file); - return error_info->error_code; + error_info->setjmp = 1; + if (setjmp(error_info->jmp) == 0) { + if (!fwrite(kFileMagic, 8, 1, file)) { + aom_internal_error(error_info, AOM_CODEC_ERROR, + "Unable to write file magic"); + } + + fprintf(file, "\n"); + aom_film_grain_table_entry_t *entry = t->head; + while (entry) { + grain_table_entry_write(file, entry); + entry = entry->next; + } } - fprintf(file, "\n"); - aom_film_grain_table_entry_t *entry = t->head; - while (entry) { - grain_table_entry_write(file, entry); - entry = entry->next; - } fclose(file); + error_info->setjmp = 0; + return error_info->error_code; }
diff --git a/aom_dsp/prob.h b/aom_dsp/prob.h index bb2c4a9..8bec067 100644 --- a/aom_dsp/prob.h +++ b/aom_dsp/prob.h
@@ -18,7 +18,7 @@ #include "config/aom_config.h" #include "aom_dsp/aom_dsp_common.h" -#include "aom_dsp/entcode.h" + #include "aom_ports/bitops.h" #include "aom_ports/mem.h"
diff --git a/aom_dsp/riscv/mem_rvv.h b/aom_dsp/riscv/mem_rvv.h index e592690..6b44748 100644 --- a/aom_dsp/riscv/mem_rvv.h +++ b/aom_dsp/riscv/mem_rvv.h
@@ -100,6 +100,28 @@ *s6 = __riscv_vle16_v_i16mf2(s, vl); } +static inline void load_s16_4x8(const int16_t *s, int p, vint16mf2_t *const s0, + vint16mf2_t *const s1, vint16mf2_t *const s2, + vint16mf2_t *const s3, vint16mf2_t *const s4, + vint16mf2_t *const s5, vint16mf2_t *const s6, + vint16mf2_t *const s7, size_t vl) { + *s0 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s1 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s2 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s3 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s4 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s5 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s6 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s7 = __riscv_vle16_v_i16mf2(s, vl); +} + static inline void load_s16_8x7(const int16_t *s, int p, vint16m1_t *const s0, vint16m1_t *const s1, vint16m1_t *const s2, vint16m1_t *const s3, vint16m1_t *const s4, @@ -150,6 +172,33 @@ *s10 = __riscv_vle16_v_i16mf2(s, vl); } +static inline void load_s16_8x10(const int16_t *s, int p, vint16m1_t *const s0, + vint16m1_t *const s1, vint16m1_t *const s2, + vint16m1_t *const s3, vint16m1_t *const s4, + vint16m1_t *const s5, vint16m1_t *const s6, + vint16m1_t *const s7, vint16m1_t *const s8, + vint16m1_t *const s9, size_t vl) { + *s0 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s1 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s2 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s3 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s4 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s5 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s6 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s7 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s8 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s9 = __riscv_vle16_v_i16m1(s, vl); +} + static inline void load_s16_8x11(const int16_t *s, int p, vint16m1_t *const s0, vint16m1_t *const s1, vint16m1_t *const s2, vint16m1_t *const s3, vint16m1_t *const s4, @@ -295,4 +344,277 @@ __riscv_vse16_v_u16mf2(s, s3, vl); } +static inline void load_u8_8x4(const uint8_t *s, const ptrdiff_t p, + vuint8mf2_t *const s0, vuint8mf2_t *const s1, + vuint8mf2_t *const s2, vuint8mf2_t *const s3, + size_t vl) { + *s0 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s1 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s2 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s3 = __riscv_vle8_v_u8mf2(s, vl); +} + +static inline void load_u8_4x5(const uint8_t *s, const ptrdiff_t p, + vuint8mf4_t *const s0, vuint8mf4_t *const s1, + vuint8mf4_t *const s2, vuint8mf4_t *const s3, + vuint8mf4_t *const s4, size_t vl) { + *s0 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s1 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s2 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s3 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s4 = __riscv_vle8_v_u8mf4(s, vl); +} + +static inline void load_u8_4x7(const uint8_t *s, const ptrdiff_t p, + vuint8mf4_t *const s0, vuint8mf4_t *const s1, + vuint8mf4_t *const s2, vuint8mf4_t *const s3, + vuint8mf4_t *const s4, vuint8mf4_t *const s5, + vuint8mf4_t *const s6, size_t vl) { + *s0 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s1 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s2 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s3 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s4 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s5 = __riscv_vle8_v_u8mf4(s, vl); + s += p; + *s6 = __riscv_vle8_v_u8mf4(s, vl); +} + +static inline void load_u8_8x5(const uint8_t *s, const ptrdiff_t p, + vuint8mf2_t *const s0, vuint8mf2_t *const s1, + vuint8mf2_t *const s2, vuint8mf2_t *const s3, + vuint8mf2_t *const s4, size_t vl) { + *s0 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s1 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s2 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s3 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s4 = __riscv_vle8_v_u8mf2(s, vl); +} + +static inline void load_u8_8x7(const uint8_t *s, const ptrdiff_t p, + vuint8mf2_t *const s0, vuint8mf2_t *const s1, + vuint8mf2_t *const s2, vuint8mf2_t *const s3, + vuint8mf2_t *const s4, vuint8mf2_t *const s5, + vuint8mf2_t *const s6, size_t vl) { + *s0 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s1 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s2 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s3 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s4 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s5 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s6 = __riscv_vle8_v_u8mf2(s, vl); +} + +static inline void load_u8_8x8(const uint8_t *s, int p, vuint8mf2_t *const s0, + vuint8mf2_t *const s1, vuint8mf2_t *const s2, + vuint8mf2_t *const s3, vuint8mf2_t *const s4, + vuint8mf2_t *const s5, vuint8mf2_t *const s6, + vuint8mf2_t *const s7, size_t vl) { + *s0 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s1 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s2 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s3 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s4 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s5 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s6 = __riscv_vle8_v_u8mf2(s, vl); + s += p; + *s7 = __riscv_vle8_v_u8mf2(s, vl); +} + +static inline void load_stride_u8_8x8( + const uint8_t *s, int p, vuint8mf2_t *const s0, vuint8mf2_t *const s1, + vuint8mf2_t *const s2, vuint8mf2_t *const s3, vuint8mf2_t *const s4, + vuint8mf2_t *const s5, vuint8mf2_t *const s6, vuint8mf2_t *const s7, + size_t vl) { + *s0 = __riscv_vlse8_v_u8mf2(s + 0, p, vl); + *s1 = __riscv_vlse8_v_u8mf2(s + 1, p, vl); + *s2 = __riscv_vlse8_v_u8mf2(s + 2, p, vl); + *s3 = __riscv_vlse8_v_u8mf2(s + 3, p, vl); + *s4 = __riscv_vlse8_v_u8mf2(s + 4, p, vl); + *s5 = __riscv_vlse8_v_u8mf2(s + 5, p, vl); + *s6 = __riscv_vlse8_v_u8mf2(s + 6, p, vl); + *s7 = __riscv_vlse8_v_u8mf2(s + 7, p, vl); +} + +static inline void store_s16_8x8(int16_t *s, const ptrdiff_t p, + const vint16m1_t s0, const vint16m1_t s1, + const vint16m1_t s2, const vint16m1_t s3, + const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, + size_t vl) { + __riscv_vse16_v_i16m1(s, s0, vl); + s += p; + __riscv_vse16_v_i16m1(s, s1, vl); + s += p; + __riscv_vse16_v_i16m1(s, s2, vl); + s += p; + __riscv_vse16_v_i16m1(s, s3, vl); + s += p; + __riscv_vse16_v_i16m1(s, s4, vl); + s += p; + __riscv_vse16_v_i16m1(s, s5, vl); + s += p; + __riscv_vse16_v_i16m1(s, s6, vl); + s += p; + __riscv_vse16_v_i16m1(s, s7, vl); +} + +static inline void store_u16_8x8(uint16_t *s, const ptrdiff_t p, + const vuint16m1_t s0, const vuint16m1_t s1, + const vuint16m1_t s2, const vuint16m1_t s3, + const vuint16m1_t s4, const vuint16m1_t s5, + const vuint16m1_t s6, const vuint16m1_t s7, + size_t vl) { + __riscv_vse16_v_u16m1(s, s0, vl); + s += p; + __riscv_vse16_v_u16m1(s, s1, vl); + s += p; + __riscv_vse16_v_u16m1(s, s2, vl); + s += p; + __riscv_vse16_v_u16m1(s, s3, vl); + s += p; + __riscv_vse16_v_u16m1(s, s4, vl); + s += p; + __riscv_vse16_v_u16m1(s, s5, vl); + s += p; + __riscv_vse16_v_u16m1(s, s6, vl); + s += p; + __riscv_vse16_v_u16m1(s, s7, vl); +} + +static inline void load_u16_4x4(const uint16_t *s, const ptrdiff_t p, + vuint16mf2_t *const s0, vuint16mf2_t *const s1, + vuint16mf2_t *const s2, vuint16mf2_t *const s3, + size_t vl) { + *s0 = __riscv_vle16_v_u16mf2(s, vl); + s += p; + *s1 = __riscv_vle16_v_u16mf2(s, vl); + s += p; + *s2 = __riscv_vle16_v_u16mf2(s, vl); + s += p; + *s3 = __riscv_vle16_v_u16mf2(s, vl); +} + +static inline void load_u16_8x4(const uint16_t *s, const ptrdiff_t p, + vuint16m1_t *const s0, vuint16m1_t *const s1, + vuint16m1_t *const s2, vuint16m1_t *const s3, + size_t vl) { + *s0 = __riscv_vle16_v_u16m1(s, vl); + s += p; + *s1 = __riscv_vle16_v_u16m1(s, vl); + s += p; + *s2 = __riscv_vle16_v_u16m1(s, vl); + s += p; + *s3 = __riscv_vle16_v_u16m1(s, vl); +} + +static inline void store_u8_8x4(uint8_t *s, const ptrdiff_t p, + const vuint8mf2_t s0, const vuint8mf2_t s1, + const vuint8mf2_t s2, const vuint8mf2_t s3, + size_t vl) { + __riscv_vse8_v_u8mf2(s, s0, vl); + s += p; + __riscv_vse8_v_u8mf2(s, s1, vl); + s += p; + __riscv_vse8_v_u8mf2(s, s2, vl); + s += p; + __riscv_vse8_v_u8mf2(s, s3, vl); +} + +static inline void load_s16_4x3(const int16_t *s, int p, vint16mf2_t *const s0, + vint16mf2_t *const s1, vint16mf2_t *const s2, + size_t vl) { + *s0 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s1 = __riscv_vle16_v_i16mf2(s, vl); + s += p; + *s2 = __riscv_vle16_v_i16mf2(s, vl); +} + +static inline void load_s16_8x3(const int16_t *s, int p, vint16m1_t *const s0, + vint16m1_t *const s1, vint16m1_t *const s2, + size_t vl) { + *s0 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s1 = __riscv_vle16_v_i16m1(s, vl); + s += p; + *s2 = __riscv_vle16_v_i16m1(s, vl); +} + +static inline void load_s32_4x4(const int32_t *s, int p, vint32m1_t *const s0, + vint32m1_t *const s1, vint32m1_t *const s2, + vint32m1_t *const s3, size_t vl) { + *s0 = __riscv_vle32_v_i32m1(s, vl); + s += p; + *s1 = __riscv_vle32_v_i32m1(s, vl); + s += p; + *s2 = __riscv_vle32_v_i32m1(s, vl); + s += p; + *s3 = __riscv_vle32_v_i32m1(s, vl); +} + +static inline void store_s32_4x4(int32_t *s, int p, const vint32m1_t s0, + const vint32m1_t s1, const vint32m1_t s2, + const vint32m1_t s3, size_t vl) { + __riscv_vse32_v_i32m1(s, s0, vl); + s += p; + __riscv_vse32_v_i32m1(s, s1, vl); + s += p; + __riscv_vse32_v_i32m1(s, s2, vl); + s += p; + __riscv_vse32_v_i32m1(s, s3, vl); +} + +static inline void load_u32_4x4(const uint32_t *s, int p, vuint32m1_t *const s0, + vuint32m1_t *const s1, vuint32m1_t *const s2, + vuint32m1_t *const s3, size_t vl) { + *s0 = __riscv_vle32_v_u32m1(s, vl); + s += p; + *s1 = __riscv_vle32_v_u32m1(s, vl); + s += p; + *s2 = __riscv_vle32_v_u32m1(s, vl); + s += p; + *s3 = __riscv_vle32_v_u32m1(s, vl); +} + +static inline void store_u32_4x4(uint32_t *s, int p, const vuint32m1_t s0, + const vuint32m1_t s1, const vuint32m1_t s2, + const vuint32m1_t s3, size_t vl) { + __riscv_vse32_v_u32m1(s, s0, vl); + s += p; + __riscv_vse32_v_u32m1(s, s1, vl); + s += p; + __riscv_vse32_v_u32m1(s, s2, vl); + s += p; + __riscv_vse32_v_u32m1(s, s3, vl); +} + #endif // AOM_AOM_DSP_RISCV_MEM_RVV_H_
diff --git a/aom_dsp/simd/v256_intrinsics_c.h b/aom_dsp/simd/v256_intrinsics_c.h index 20d2709..cbcc1b0 100644 --- a/aom_dsp/simd/v256_intrinsics_c.h +++ b/aom_dsp/simd/v256_intrinsics_c.h
@@ -12,6 +12,7 @@ #ifndef AOM_AOM_DSP_SIMD_V256_INTRINSICS_C_H_ #define AOM_AOM_DSP_SIMD_V256_INTRINSICS_C_H_ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -693,11 +694,13 @@ SIMD_INLINE c_v256 c_v256_wideshuffle_8(c_v256 a, c_v256 b, c_v256 pattern) { c_v256 t; int c; - for (c = 0; c < 32; c++) - t.u8[c] = (pattern.u8[c] < 32 - ? b.u8 - : a.u8)[CONFIG_BIG_ENDIAN ? 31 - (pattern.u8[c] & 31) - : pattern.u8[c] & 31]; + for (c = 0; c < 32; c++) { + const bool from_b_flag = pattern.u8[c] < 32; + const uint8_t *const src = from_b_flag ? b.u8 : a.u8; + const int idx = + CONFIG_BIG_ENDIAN ? 31 - (pattern.u8[c] & 31) : (pattern.u8[c] & 31); + t.u8[c] = src[idx]; + } return t; }
diff --git a/aom_dsp/x86/convolve_avx2.h b/aom_dsp/x86/convolve_avx2.h index 7db8a2d..ffafa13 100644 --- a/aom_dsp/x86/convolve_avx2.h +++ b/aom_dsp/x86/convolve_avx2.h
@@ -16,9 +16,16 @@ #include "aom_ports/mem.h" +#include "aom_dsp/x86/mem_sse2.h" +#include "aom_dsp/x86/synonyms.h" + #include "av1/common/convolve.h" #include "av1/common/filter.h" +#define SECOND_32_BLK (32) +#define THIRD_32_BLK (32 << 1) +#define FOURTH_32_BLK (SECOND_32_BLK + THIRD_32_BLK) + // filters for 16 DECLARE_ALIGNED(32, static const uint8_t, filt_global_avx2[]) = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 0, 1, 1, @@ -47,6 +54,26 @@ }; DECLARE_ALIGNED(32, static const uint8_t, + filt1_global_sse2[16]) = { 0, 1, 1, 2, 2, 3, 3, 4, + 8, 9, 9, 10, 10, 11, 11, 12 }; + +DECLARE_ALIGNED(32, static const uint8_t, + filt2_global_sse2[16]) = { 2, 3, 3, 4, 4, 5, 5, 6, + 10, 11, 11, 12, 12, 13, 13, 14 }; + +DECLARE_ALIGNED(32, static const uint8_t, + filt3_global_sse2[16]) = { 0, 1, 1, 2, 8, 9, 9, 10, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +DECLARE_ALIGNED(32, static const uint8_t, + filt4_global_sse2[16]) = { 2, 3, 3, 4, 10, 11, 11, 12, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +DECLARE_ALIGNED(32, static const uint8_t, + filt5_global_sse2[16]) = { 0, 1, 1, 2, 4, 5, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +DECLARE_ALIGNED(32, static const uint8_t, filt1_global_avx2[32]) = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8 }; @@ -66,105 +93,259 @@ 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14 }; -#define CONVOLVE_SR_HORIZONTAL_FILTER_4TAP \ - for (i = 0; i < (im_h - 2); i += 2) { \ - __m256i data = _mm256_castsi128_si256( \ - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ - data = _mm256_inserti128_si256( \ - data, \ - _mm_loadu_si128( \ - (__m128i *)&src_ptr[(i * src_stride) + j + src_stride]), \ - 1); \ - __m256i res = convolve_lowbd_x_4tap(data, coeffs_h + 1, filt); \ - res = \ - _mm256_sra_epi16(_mm256_add_epi16(res, round_const_h), round_shift_h); \ - _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); \ - } \ - __m256i data_1 = _mm256_castsi128_si256( \ - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ - __m256i res = convolve_lowbd_x_4tap(data_1, coeffs_h + 1, filt); \ - res = _mm256_sra_epi16(_mm256_add_epi16(res, round_const_h), round_shift_h); \ +#define CONVOLVE_SR_HOR_FILTER_W4(CONVOLVE_LOWBD) \ + for (i = 0; i < (im_h - 2); i += 2) { \ + __m128i data = \ + load_8bit_8x2_to_1_reg_sse2(&src_ptr[(i * src_stride)], src_stride); \ + __m128i res = CONVOLVE_LOWBD(data, coeffs_h, filt); \ + res = _mm_srai_epi16(_mm_add_epi16(res, round_const_h), 2); \ + _mm_store_si128((__m128i *)&im_block[i * 4], res); \ + } \ + __m128i data_1 = _mm_loadl_epi64((__m128i *)&src_ptr[(i * src_stride)]); \ + __m128i res = CONVOLVE_LOWBD(data_1, coeffs_h, filt); \ + res = _mm_srai_epi16(_mm_add_epi16(res, round_const_h), 2); \ + _mm_storel_epi64((__m128i *)&im_block[i * 4], res); + +#define CONVOLVE_SR_HOR_FILTER_2TAP_W4 \ + CONVOLVE_SR_HOR_FILTER_W4(convolve_lowbd_x_2tap_ssse3) + +#define CONVOLVE_SR_HOR_FILTER_4TAP_W4 \ + CONVOLVE_SR_HOR_FILTER_W4(convolve_lowbd_x_4tap_ssse3) + +static inline void sr_2d_ver_round_and_store_w4(int w, __m256i res, + uint8_t *dst, int dst_stride, + __m256i round_const_v) { + const __m256i res_round = + _mm256_srai_epi32(_mm256_add_epi32(res, round_const_v), 11); + + const __m256i res_16bit = _mm256_packs_epi32(res_round, res_round); + const __m256i res_8b = _mm256_packus_epi16(res_16bit, res_16bit); + + const __m128i r0 = _mm256_castsi256_si128(res_8b); + const __m128i r1 = _mm256_extracti128_si256(res_8b, 1); + + __m128i *const p0 = (__m128i *)dst; + __m128i *const p1 = (__m128i *)(dst + dst_stride); + + if (w == 4) { + xx_storel_32(p0, r0); + xx_storel_32(p1, r1); + } else { + assert(w == 2); + *(uint16_t *)p0 = (uint16_t)_mm_cvtsi128_si32(r0); + *(uint16_t *)p1 = (uint16_t)_mm_cvtsi128_si32(r1); + } +} + +#define CONVOLVE_SR_VER_FILTER_2TAP_W4 \ + __m128i s[2]; \ + s[0] = _mm_loadl_epi64((__m128i *)(im_block + 0 * 4)); \ + \ + for (i = 0; i < h; i += 2) { \ + const int16_t *data = &im_block[i * 4]; \ + s[1] = _mm_loadl_epi64((__m128i *)(data + 1 * 4)); \ + const __m256i src_0 = _mm256_setr_m128i(s[0], s[1]); \ + s[0] = _mm_loadl_epi64((__m128i *)(data + 2 * 4)); \ + const __m256i src_1 = _mm256_setr_m128i(s[1], s[0]); \ + const __m256i ss = _mm256_unpacklo_epi16(src_0, src_1); \ + \ + const __m256i res = _mm256_madd_epi16(ss, coeffs_v[0]); \ + \ + sr_2d_ver_round_and_store_w4(w, res, dst_ptr, dst_stride, round_const_v); \ + dst_ptr += 2 * dst_stride; \ + } + +#define CONVOLVE_SR_VER_FILTER_4TAP_W4 \ + __m128i s[4]; \ + __m256i ss[2]; \ + s[0] = _mm_loadl_epi64((__m128i *)(im_block + 0 * 4)); \ + s[1] = _mm_loadl_epi64((__m128i *)(im_block + 1 * 4)); \ + s[2] = _mm_loadl_epi64((__m128i *)(im_block + 2 * 4)); \ + \ + const __m256i src_0 = _mm256_setr_m128i(s[0], s[1]); \ + const __m256i src_1 = _mm256_setr_m128i(s[1], s[2]); \ + \ + ss[0] = _mm256_unpacklo_epi16(src_0, src_1); \ + \ + for (i = 0; i < h; i += 2) { \ + const int16_t *data = &im_block[i * 4]; \ + s[3] = _mm_loadl_epi64((__m128i *)(data + 3 * 4)); \ + const __m256i src_2 = _mm256_setr_m128i(s[2], s[3]); \ + s[2] = _mm_loadl_epi64((__m128i *)(data + 4 * 4)); \ + const __m256i src_3 = _mm256_setr_m128i(s[3], s[2]); \ + ss[1] = _mm256_unpacklo_epi16(src_2, src_3); \ + \ + const __m256i res = convolve_4tap(ss, coeffs_v); \ + \ + sr_2d_ver_round_and_store_w4(w, res, dst_ptr, dst_stride, round_const_v); \ + dst_ptr += 2 * dst_stride; \ + \ + ss[0] = ss[1]; \ + } + +#define CONVOLVE_SR_VER_FILTER_6TAP_W4 \ + __m128i s[6]; \ + __m256i ss[3]; \ + s[0] = _mm_loadl_epi64((__m128i *)(im_block + 0 * 4)); \ + s[1] = _mm_loadl_epi64((__m128i *)(im_block + 1 * 4)); \ + s[2] = _mm_loadl_epi64((__m128i *)(im_block + 2 * 4)); \ + s[3] = _mm_loadl_epi64((__m128i *)(im_block + 3 * 4)); \ + s[4] = _mm_loadl_epi64((__m128i *)(im_block + 4 * 4)); \ + \ + const __m256i src_0 = _mm256_setr_m128i(s[0], s[1]); \ + const __m256i src_1 = _mm256_setr_m128i(s[1], s[2]); \ + const __m256i src_2 = _mm256_setr_m128i(s[2], s[3]); \ + const __m256i src_3 = _mm256_setr_m128i(s[3], s[4]); \ + \ + ss[0] = _mm256_unpacklo_epi16(src_0, src_1); \ + ss[1] = _mm256_unpacklo_epi16(src_2, src_3); \ + \ + for (i = 0; i < h; i += 2) { \ + const int16_t *data = &im_block[i * 4]; \ + s[5] = _mm_loadl_epi64((__m128i *)(data + 5 * 4)); \ + const __m256i src_4 = _mm256_setr_m128i(s[4], s[5]); \ + s[4] = _mm_loadl_epi64((__m128i *)(data + 6 * 4)); \ + const __m256i src_5 = _mm256_setr_m128i(s[5], s[4]); \ + ss[2] = _mm256_unpacklo_epi16(src_4, src_5); \ + \ + const __m256i res = convolve_6tap(ss, coeffs_v); \ + \ + sr_2d_ver_round_and_store_w4(w, res, dst_ptr, dst_stride, round_const_v); \ + dst_ptr += 2 * dst_stride; \ + \ + ss[0] = ss[1]; \ + ss[1] = ss[2]; \ + } + +#define CONVOLVE_SR_VER_FILTER_8TAP_W4 \ + __m128i s[8]; \ + __m256i ss[4]; \ + s[0] = _mm_loadl_epi64((__m128i *)(im_block + 0 * 4)); \ + s[1] = _mm_loadl_epi64((__m128i *)(im_block + 1 * 4)); \ + s[2] = _mm_loadl_epi64((__m128i *)(im_block + 2 * 4)); \ + s[3] = _mm_loadl_epi64((__m128i *)(im_block + 3 * 4)); \ + s[4] = _mm_loadl_epi64((__m128i *)(im_block + 4 * 4)); \ + s[5] = _mm_loadl_epi64((__m128i *)(im_block + 5 * 4)); \ + s[6] = _mm_loadl_epi64((__m128i *)(im_block + 6 * 4)); \ + \ + const __m256i src_0 = _mm256_setr_m128i(s[0], s[1]); \ + const __m256i src_1 = _mm256_setr_m128i(s[1], s[2]); \ + const __m256i src_2 = _mm256_setr_m128i(s[2], s[3]); \ + const __m256i src_3 = _mm256_setr_m128i(s[3], s[4]); \ + const __m256i src_4 = _mm256_setr_m128i(s[4], s[5]); \ + const __m256i src_5 = _mm256_setr_m128i(s[5], s[6]); \ + \ + ss[0] = _mm256_unpacklo_epi16(src_0, src_1); \ + ss[1] = _mm256_unpacklo_epi16(src_2, src_3); \ + ss[2] = _mm256_unpacklo_epi16(src_4, src_5); \ + \ + for (i = 0; i < h; i += 2) { \ + const int16_t *data = &im_block[i * 4]; \ + s[7] = _mm_loadl_epi64((__m128i *)(data + 7 * 4)); \ + const __m256i src_6 = _mm256_setr_m128i(s[6], s[7]); \ + s[6] = _mm_loadl_epi64((__m128i *)(data + 8 * 4)); \ + const __m256i src_7 = _mm256_setr_m128i(s[7], s[6]); \ + ss[3] = _mm256_unpacklo_epi16(src_6, src_7); \ + \ + const __m256i res = convolve(ss, coeffs_v); \ + \ + sr_2d_ver_round_and_store_w4(w, res, dst_ptr, dst_stride, round_const_v); \ + dst_ptr += 2 * dst_stride; \ + \ + ss[0] = ss[1]; \ + ss[1] = ss[2]; \ + ss[2] = ss[3]; \ + } + +#define CONVOLVE_SR_HORIZONTAL_FILTER(CONVOLVE_LOWBD) \ + for (i = 0; i < (im_h - 2); i += 2) { \ + __m256i data = _mm256_castsi128_si256( \ + _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ + data = _mm256_inserti128_si256( \ + data, \ + _mm_loadu_si128( \ + (__m128i *)&src_ptr[(i * src_stride) + j + src_stride]), \ + 1); \ + __m256i res = CONVOLVE_LOWBD(data, coeffs_h, filt); \ + res = _mm256_srai_epi16(_mm256_add_epi16(res, round_const_h), 2); \ + _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); \ + } \ + __m256i data_1 = _mm256_castsi128_si256( \ + _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ + __m256i res = CONVOLVE_LOWBD(data_1, coeffs_h, filt); \ + res = _mm256_srai_epi16(_mm256_add_epi16(res, round_const_h), 2); \ _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); +#define CONVOLVE_SR_HORIZONTAL_FILTER_2TAP \ + CONVOLVE_SR_HORIZONTAL_FILTER(convolve_lowbd_x_2tap) + +#define CONVOLVE_SR_HORIZONTAL_FILTER_4TAP \ + CONVOLVE_SR_HORIZONTAL_FILTER(convolve_lowbd_x_4tap) + +#define CONVOLVE_SR_HORIZONTAL_FILTER_6TAP \ + CONVOLVE_SR_HORIZONTAL_FILTER(convolve_lowbd_x_6tap) + +#define CONVOLVE_SR_HORIZONTAL_FILTER_8TAP \ + CONVOLVE_SR_HORIZONTAL_FILTER(convolve_lowbd_x) + +static inline void sr_2d_ver_round_and_store(__m256i res_a, __m256i res_b, + uint8_t *dst, int dst_stride, + __m256i round_const_v) { + const __m256i res_a_round = + _mm256_srai_epi32(_mm256_add_epi32(res_a, round_const_v), 11); + const __m256i res_b_round = + _mm256_srai_epi32(_mm256_add_epi32(res_b, round_const_v), 11); + const __m256i r16 = _mm256_packs_epi32(res_a_round, res_b_round); + const __m256i r8 = _mm256_packus_epi16(r16, r16); + + _mm_storel_epi64((__m128i *)dst, _mm256_castsi256_si128(r8)); + _mm_storel_epi64((__m128i *)(dst + dst_stride), + _mm256_extracti128_si256(r8, 1)); +} + +#define CONVOLVE_SR_VERTICAL_FILTER_2TAP \ + for (i = 0; i < h; i += 2) { \ + __m256i s[2]; \ + const int16_t *data = &im_block[i * im_stride]; \ + const __m256i s1 = _mm256_loadu_si256((__m256i *)(data + 0 * im_stride)); \ + const __m256i s2 = _mm256_loadu_si256((__m256i *)(data + 1 * im_stride)); \ + s[0] = _mm256_unpacklo_epi16(s1, s2); \ + s[1] = _mm256_unpackhi_epi16(s1, s2); \ + \ + __m256i res_a = _mm256_madd_epi16(s[0], coeffs_v[0]); \ + __m256i res_b = _mm256_madd_epi16(s[1], coeffs_v[0]); \ + \ + sr_2d_ver_round_and_store(res_a, res_b, dst_ptr, dst_stride, \ + round_const_v); \ + dst_ptr += 2 * dst_stride; \ + } + #define CONVOLVE_SR_VERTICAL_FILTER_4TAP \ __m256i s[6]; \ __m256i src_0 = _mm256_loadu_si256((__m256i *)(im_block + 0 * im_stride)); \ __m256i src_1 = _mm256_loadu_si256((__m256i *)(im_block + 1 * im_stride)); \ - __m256i src_2 = _mm256_loadu_si256((__m256i *)(im_block + 2 * im_stride)); \ - __m256i src_3 = _mm256_loadu_si256((__m256i *)(im_block + 3 * im_stride)); \ \ s[0] = _mm256_unpacklo_epi16(src_0, src_1); \ - s[1] = _mm256_unpacklo_epi16(src_2, src_3); \ - s[3] = _mm256_unpackhi_epi16(src_0, src_1); \ - s[4] = _mm256_unpackhi_epi16(src_2, src_3); \ + s[2] = _mm256_unpackhi_epi16(src_0, src_1); \ \ for (i = 0; i < h; i += 2) { \ const int16_t *data = &im_block[i * im_stride]; \ - const __m256i s4 = _mm256_loadu_si256((__m256i *)(data + 4 * im_stride)); \ - const __m256i s5 = _mm256_loadu_si256((__m256i *)(data + 5 * im_stride)); \ - s[2] = _mm256_unpacklo_epi16(s4, s5); \ - s[5] = _mm256_unpackhi_epi16(s4, s5); \ + const __m256i s4 = _mm256_loadu_si256((__m256i *)(data + 2 * im_stride)); \ + const __m256i s5 = _mm256_loadu_si256((__m256i *)(data + 3 * im_stride)); \ + s[1] = _mm256_unpacklo_epi16(s4, s5); \ + s[3] = _mm256_unpackhi_epi16(s4, s5); \ \ - __m256i res_a = convolve_4tap(s, coeffs_v + 1); \ - __m256i res_b = convolve_4tap(s + 3, coeffs_v + 1); \ + __m256i res_a = convolve_4tap(s, coeffs_v); \ + __m256i res_b = convolve_4tap(s + 2, coeffs_v); \ \ - res_a = \ - _mm256_sra_epi32(_mm256_add_epi32(res_a, sum_round_v), sum_shift_v); \ - res_b = \ - _mm256_sra_epi32(_mm256_add_epi32(res_b, sum_round_v), sum_shift_v); \ - const __m256i res_a_round = _mm256_sra_epi32( \ - _mm256_add_epi32(res_a, round_const_v), round_shift_v); \ - const __m256i res_b_round = _mm256_sra_epi32( \ - _mm256_add_epi32(res_b, round_const_v), round_shift_v); \ - const __m256i res_16bit = _mm256_packs_epi32(res_a_round, res_b_round); \ - const __m256i res_8b = _mm256_packus_epi16(res_16bit, res_16bit); \ - const __m128i res_0 = _mm256_castsi256_si128(res_8b); \ - const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); \ - \ - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride + j]; \ - __m128i *const p_1 = (__m128i *)&dst[i * dst_stride + j + dst_stride]; \ - if (w - j > 4) { \ - _mm_storel_epi64(p_0, res_0); \ - _mm_storel_epi64(p_1, res_1); \ - } else if (w == 4) { \ - xx_storel_32(p_0, res_0); \ - xx_storel_32(p_1, res_1); \ - } else { \ - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); \ - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); \ - } \ + sr_2d_ver_round_and_store(res_a, res_b, dst_ptr, dst_stride, \ + round_const_v); \ + dst_ptr += 2 * dst_stride; \ \ s[0] = s[1]; \ - s[1] = s[2]; \ - s[3] = s[4]; \ - s[4] = s[5]; \ + s[2] = s[3]; \ } -#define CONVOLVE_SR_HORIZONTAL_FILTER_6TAP \ - for (i = 0; i < (im_h - 2); i += 2) { \ - __m256i data = _mm256_castsi128_si256( \ - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ - data = _mm256_inserti128_si256( \ - data, \ - _mm_loadu_si128( \ - (__m128i *)&src_ptr[(i * src_stride) + j + src_stride]), \ - 1); \ - \ - __m256i res = convolve_lowbd_x_6tap(data, coeffs_h, filt); \ - res = \ - _mm256_sra_epi16(_mm256_add_epi16(res, round_const_h), round_shift_h); \ - _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); \ - } \ - \ - __m256i data_1 = _mm256_castsi128_si256( \ - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ - \ - __m256i res = convolve_lowbd_x_6tap(data_1, coeffs_h, filt); \ - \ - res = _mm256_sra_epi16(_mm256_add_epi16(res, round_const_h), round_shift_h); \ - \ - _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); - #define CONVOLVE_SR_VERTICAL_FILTER_6TAP \ __m256i src_0 = _mm256_loadu_si256((__m256i *)(im_block + 0 * im_stride)); \ __m256i src_1 = _mm256_loadu_si256((__m256i *)(im_block + 1 * im_stride)); \ @@ -190,34 +371,9 @@ __m256i res_a = convolve_6tap(s, coeffs_v); \ __m256i res_b = convolve_6tap(s + 3, coeffs_v); \ \ - res_a = \ - _mm256_sra_epi32(_mm256_add_epi32(res_a, sum_round_v), sum_shift_v); \ - res_b = \ - _mm256_sra_epi32(_mm256_add_epi32(res_b, sum_round_v), sum_shift_v); \ - \ - const __m256i res_a_round = _mm256_sra_epi32( \ - _mm256_add_epi32(res_a, round_const_v), round_shift_v); \ - const __m256i res_b_round = _mm256_sra_epi32( \ - _mm256_add_epi32(res_b, round_const_v), round_shift_v); \ - \ - const __m256i res_16bit = _mm256_packs_epi32(res_a_round, res_b_round); \ - const __m256i res_8b = _mm256_packus_epi16(res_16bit, res_16bit); \ - \ - const __m128i res_0 = _mm256_castsi256_si128(res_8b); \ - const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); \ - \ - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride + j]; \ - __m128i *const p_1 = (__m128i *)&dst[i * dst_stride + j + dst_stride]; \ - if (w - j > 4) { \ - _mm_storel_epi64(p_0, res_0); \ - _mm_storel_epi64(p_1, res_1); \ - } else if (w == 4) { \ - xx_storel_32(p_0, res_0); \ - xx_storel_32(p_1, res_1); \ - } else { \ - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); \ - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); \ - } \ + sr_2d_ver_round_and_store(res_a, res_b, dst_ptr, dst_stride, \ + round_const_v); \ + dst_ptr += 2 * dst_stride; \ \ s[0] = s[1]; \ s[1] = s[2]; \ @@ -226,31 +382,6 @@ s[4] = s[5]; \ } -#define CONVOLVE_SR_HORIZONTAL_FILTER_8TAP \ - for (i = 0; i < (im_h - 2); i += 2) { \ - __m256i data = _mm256_castsi128_si256( \ - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ - data = _mm256_inserti128_si256( \ - data, \ - _mm_loadu_si128( \ - (__m128i *)&src_ptr[(i * src_stride) + j + src_stride]), \ - 1); \ - \ - __m256i res = convolve_lowbd_x(data, coeffs_h, filt); \ - res = \ - _mm256_sra_epi16(_mm256_add_epi16(res, round_const_h), round_shift_h); \ - _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); \ - } \ - \ - __m256i data_1 = _mm256_castsi128_si256( \ - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + j])); \ - \ - __m256i res = convolve_lowbd_x(data_1, coeffs_h, filt); \ - \ - res = _mm256_sra_epi16(_mm256_add_epi16(res, round_const_h), round_shift_h); \ - \ - _mm256_store_si256((__m256i *)&im_block[i * im_stride], res); - #define CONVOLVE_SR_VERTICAL_FILTER_8TAP \ __m256i src_0 = _mm256_loadu_si256((__m256i *)(im_block + 0 * im_stride)); \ __m256i src_1 = _mm256_loadu_si256((__m256i *)(im_block + 1 * im_stride)); \ @@ -280,34 +411,9 @@ __m256i res_a = convolve(s, coeffs_v); \ __m256i res_b = convolve(s + 4, coeffs_v); \ \ - res_a = \ - _mm256_sra_epi32(_mm256_add_epi32(res_a, sum_round_v), sum_shift_v); \ - res_b = \ - _mm256_sra_epi32(_mm256_add_epi32(res_b, sum_round_v), sum_shift_v); \ - \ - const __m256i res_a_round = _mm256_sra_epi32( \ - _mm256_add_epi32(res_a, round_const_v), round_shift_v); \ - const __m256i res_b_round = _mm256_sra_epi32( \ - _mm256_add_epi32(res_b, round_const_v), round_shift_v); \ - \ - const __m256i res_16bit = _mm256_packs_epi32(res_a_round, res_b_round); \ - const __m256i res_8b = _mm256_packus_epi16(res_16bit, res_16bit); \ - \ - const __m128i res_0 = _mm256_castsi256_si128(res_8b); \ - const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); \ - \ - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride + j]; \ - __m128i *const p_1 = (__m128i *)&dst[i * dst_stride + j + dst_stride]; \ - if (w - j > 4) { \ - _mm_storel_epi64(p_0, res_0); \ - _mm_storel_epi64(p_1, res_1); \ - } else if (w == 4) { \ - xx_storel_32(p_0, res_0); \ - xx_storel_32(p_1, res_1); \ - } else { \ - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); \ - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); \ - } \ + sr_2d_ver_round_and_store(res_a, res_b, dst_ptr, dst_stride, \ + round_const_v); \ + dst_ptr += 2 * dst_stride; \ \ s[0] = s[1]; \ s[1] = s[2]; \ @@ -607,6 +713,174 @@ } \ } while (0) +static inline void prepare_coeffs_2t_ssse3( + const InterpFilterParams *const filter_params, const int32_t subpel_q4, + __m128i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((short)0xffff))); + + const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); + + // coeffs 3 4 3 4 3 4 3 4 + coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0806u)); +} + +static inline void prepare_coeffs_4t_ssse3( + const InterpFilterParams *const filter_params, const int32_t subpel_q4, + __m128i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((short)0xffff))); + + const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); + + // coeffs 2 3 2 3 2 3 2 3 + coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0604u)); + // coeffs 4 5 4 5 4 5 4 5 + coeffs[1] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0a08u)); +} + +static inline void prepare_coeffs_6t_ssse3( + const InterpFilterParams *const filter_params, const int32_t subpel_q4, + __m128i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((short)0xffff))); + + const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); + + // coeffs 2 3 2 3 2 3 2 3 + coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0402u)); + // coeffs 4 5 4 5 4 5 4 5 + coeffs[1] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0806u)); + // coeffs 5 6 5 6 5 6 5 6 + coeffs[2] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0c0au)); +} + +static inline void prepare_coeffs_ssse3( + const InterpFilterParams *const filter_params, const int32_t subpel_q4, + __m128i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((short)0xffff))); + + const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); + + // coeffs 0 1 0 1 0 1 0 1 + coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0200u)); + // coeffs 2 3 2 3 2 3 2 3 + coeffs[1] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0604u)); + // coeffs 4 5 4 5 4 5 4 5 + coeffs[2] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0a08u)); + // coeffs 6 7 6 7 6 7 6 7 + coeffs[3] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0e0cu)); +} + +static inline void prepare_coeffs_2t_lowbd( + const InterpFilterParams *const filter_params, const int subpel_q4, + __m256i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + const __m256i filter_coeffs = _mm256_broadcastsi128_si256(coeffs_8); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((int16_t)0xffff))); + + const __m256i coeffs_1 = _mm256_srai_epi16(filter_coeffs, 1); + + // coeffs 3 4 3 4 3 4 3 4 + coeffs[0] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0806u)); +} + +static inline void prepare_coeffs_4t_lowbd( + const InterpFilterParams *const filter_params, const int subpel_q4, + __m256i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + const __m256i filter_coeffs = _mm256_broadcastsi128_si256(coeffs_8); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((short)0xffff))); + + const __m256i coeffs_1 = _mm256_srai_epi16(filter_coeffs, 1); + + // coeffs 2 3 2 3 2 3 2 3 + coeffs[0] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0604u)); + // coeffs 4 5 4 5 4 5 4 5 + coeffs[1] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0a08u)); +} + +static inline void prepare_coeffs_6t_lowbd( + const InterpFilterParams *const filter_params, const int subpel_q4, + __m256i *const coeffs /* [4] */) { + const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); + const __m256i filter_coeffs = _mm256_broadcastsi128_si256(coeffs_8); + + // right shift all filter co-efficients by 1 to reduce the bits required. + // This extra right shift will be taken care of at the end while rounding + // the result. + // Since all filter co-efficients are even, this change will not affect the + // end result + assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), + _mm_set1_epi16((int16_t)0xffff))); + + const __m256i coeffs_1 = _mm256_srai_epi16(filter_coeffs, 1); + + // coeffs 1 2 1 2 1 2 1 2 + coeffs[0] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0402u)); + // coeffs 3 4 3 4 3 4 3 4 + coeffs[1] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0806u)); + // coeffs 5 6 5 6 5 6 5 6 + coeffs[2] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0c0au)); +} + static inline void prepare_coeffs_lowbd( const InterpFilterParams *const filter_params, const int subpel_q4, __m256i *const coeffs /* [4] */) { @@ -635,30 +909,31 @@ coeffs[3] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0e0cu)); } -static inline void prepare_coeffs_6t_lowbd( +static inline void prepare_coeffs_2t( const InterpFilterParams *const filter_params, const int subpel_q4, __m256i *const coeffs /* [4] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( + const int16_t *filter = av1_get_interp_filter_subpel_kernel( filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - const __m256i filter_coeffs = _mm256_broadcastsi128_si256(coeffs_8); - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((int16_t)0xffff))); + const __m128i coeff_8 = _mm_loadu_si128((__m128i *)(filter + 1)); + const __m256i coeff = _mm256_broadcastsi128_si256(coeff_8); - const __m256i coeffs_1 = _mm256_srai_epi16(filter_coeffs, 1); - - // coeffs 1 2 1 2 1 2 1 2 - coeffs[0] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0402u)); // coeffs 3 4 3 4 3 4 3 4 - coeffs[1] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0806u)); - // coeffs 5 6 5 6 5 6 5 6 - coeffs[2] = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0c0au)); + coeffs[0] = _mm256_shuffle_epi32(coeff, 0x55); +} + +static inline void prepare_coeffs_4t( + const InterpFilterParams *const filter_params, const int subpel_q4, + __m256i *const coeffs /* [4] */) { + const int16_t *filter = av1_get_interp_filter_subpel_kernel( + filter_params, subpel_q4 & SUBPEL_MASK); + + const __m128i coeff_8 = _mm_loadu_si128((__m128i *)filter); + const __m256i coeff = _mm256_broadcastsi128_si256(coeff_8); + // coeffs 2 3 2 3 2 3 2 3 + coeffs[0] = _mm256_shuffle_epi32(coeff, 0x55); + // coeffs 4 5 4 5 4 5 4 5 + coeffs[1] = _mm256_shuffle_epi32(coeff, 0xaa); } static inline void prepare_coeffs_6t( @@ -721,6 +996,38 @@ coeffs[5] = _mm256_shuffle_epi32(coeff, 0x55); // coeffs 10 11 10 11.. 10 11 } +static inline __m128i convolve_lowbd_4tap_ssse3(const __m128i ss[2], + const __m128i coeffs[2]) { + const __m128i res_01 = _mm_maddubs_epi16(ss[0], coeffs[0]); + const __m128i res_23 = _mm_maddubs_epi16(ss[1], coeffs[1]); + + return _mm_add_epi16(res_01, res_23); +} + +static inline __m128i convolve_lowbd_6tap_ssse3(const __m128i ss[3], + const __m128i coeffs[3]) { + const __m128i res_01 = _mm_maddubs_epi16(ss[0], coeffs[0]); + const __m128i res_23 = _mm_maddubs_epi16(ss[1], coeffs[1]); + const __m128i res_45 = _mm_maddubs_epi16(ss[2], coeffs[2]); + + const __m128i res = _mm_add_epi16(_mm_add_epi16(res_01, res_45), res_23); + + return res; +} + +static inline __m128i convolve_lowbd_ssse3(const __m128i ss[4], + const __m128i coeffs[4]) { + const __m128i res_01 = _mm_maddubs_epi16(ss[0], coeffs[0]); + const __m128i res_23 = _mm_maddubs_epi16(ss[1], coeffs[1]); + const __m128i res_45 = _mm_maddubs_epi16(ss[2], coeffs[2]); + const __m128i res_67 = _mm_maddubs_epi16(ss[3], coeffs[3]); + + const __m128i res = _mm_add_epi16(_mm_add_epi16(res_01, res_45), + _mm_add_epi16(res_23, res_67)); + + return res; +} + static inline __m256i convolve_lowbd(const __m256i *const s, const __m256i *const coeffs) { const __m256i res_01 = _mm256_maddubs_epi16(s[0], coeffs[0]); @@ -808,6 +1115,26 @@ return res; } +static inline __m128i convolve_lowbd_x_2tap_ssse3(const __m128i data, + const __m128i *const coeffs, + const __m128i *const filt) { + __m128i s; + s = _mm_shuffle_epi8(data, filt[0]); + + return _mm_maddubs_epi16(s, coeffs[0]); +} + +static inline __m128i convolve_lowbd_x_4tap_ssse3(const __m128i data, + const __m128i *const coeffs, + const __m128i *const filt) { + __m128i s[2]; + + s[0] = _mm_shuffle_epi8(data, filt[0]); + s[1] = _mm_shuffle_epi8(data, filt[1]); + + return convolve_lowbd_4tap_ssse3(s, coeffs); +} + static inline __m256i convolve_lowbd_x(const __m256i data, const __m256i *const coeffs, const __m256i *const filt) { @@ -844,6 +1171,15 @@ return convolve_lowbd_4tap(s, coeffs); } +static inline __m256i convolve_lowbd_x_2tap(const __m256i data, + const __m256i *const coeffs, + const __m256i *const filt) { + __m256i s; + s = _mm256_shuffle_epi8(data, filt[0]); + + return _mm256_maddubs_epi16(s, coeffs[0]); +} + static inline void add_store_aligned_256(CONV_BUF_TYPE *const dst, const __m256i *const res, const int do_average) { @@ -919,4 +1255,628 @@ return res_round; } +static inline __m256i round_sr_x_avx2(const __m256i data) { + // we can perform the below steps: + // data = (data + 2) >> 2 + // data = (data + 8) >> 4, + // in the below form as well + // data = (data + 0x22) >> 6 + const __m256i value = _mm256_set1_epi16(34); + const __m256i reg = _mm256_add_epi16(data, value); + return _mm256_srai_epi16(reg, 6); +} + +static inline __m128i convolve_x_4tap_4x2_ssse3(const uint8_t *const src, + const ptrdiff_t src_stride, + __m128i *const coeffs) { + __m128i data[2]; + const __m128i f_l0 = _mm_load_si128((__m128i const *)filt1_global_sse2); + const __m128i f_l1 = _mm_load_si128((__m128i const *)filt2_global_sse2); + const __m128i src_1 = + load_8bit_8x2_to_1_reg_sse2(src, (int)(sizeof(*src) * src_stride)); + + data[0] = _mm_shuffle_epi8(src_1, f_l0); + data[1] = _mm_shuffle_epi8(src_1, f_l1); + return convolve_lowbd_4tap_ssse3(data, coeffs); +} + +static inline __m128i round_sr_x_ssse3(const __m128i data) { + const __m128i val = _mm_set1_epi16(34); + const __m128i reg = _mm_add_epi16(data, val); + return _mm_srai_epi16(reg, 6); +} + +static inline void store_8bit_4x2_sse2(const __m128i reg, uint8_t *const dst, + const ptrdiff_t dst_stride) { + xx_storel_32(dst, reg); + *(uint32_t *)(dst + dst_stride) = + ((uint32_t)_mm_extract_epi16(reg, 3) << 16) | _mm_extract_epi16(reg, 2); +} + +static inline void pack_store_u8_4x2_sse2(const __m128i reg, uint8_t *const dst, + const ptrdiff_t dst_stride) { + const __m128i reg_pack = _mm_packus_epi16(reg, reg); + store_8bit_4x2_sse2(reg_pack, dst, dst_stride); +} + +static inline __m128i convolve_x_4tap_2x2_ssse3(const uint8_t *const src, + const ptrdiff_t src_stride, + __m128i *const coeffs) { + __m128i data[2]; + const __m128i f_0 = _mm_load_si128((__m128i const *)filt3_global_sse2); + const __m128i f_1 = _mm_load_si128((__m128i const *)filt4_global_sse2); + const __m128i reg = + load_8bit_8x2_to_1_reg_sse2(src, (int)(sizeof(*src) * src_stride)); + + data[0] = _mm_shuffle_epi8(reg, f_0); + data[1] = _mm_shuffle_epi8(reg, f_1); + return convolve_lowbd_4tap_ssse3(data, coeffs); +} + +static inline void pack_store_u8_2x2_sse2(const __m128i reg, uint8_t *const dst, + const ptrdiff_t dst_stride) { + const __m128i data = _mm_packus_epi16(reg, reg); + *(int16_t *)dst = (int16_t)_mm_cvtsi128_si32(data); + *(int16_t *)(dst + dst_stride) = (int16_t)_mm_extract_epi16(data, 1); +} + +static inline __m128i convolve_x_2tap_ssse3(const __m128i *data, + const __m128i *coeff) { + return _mm_maddubs_epi16(data[0], coeff[0]); +} + +static inline __m128i load8_x_4x2_sse4(const void *const src, + const ptrdiff_t offset) { + const __m128i s = _mm_cvtsi32_si128(loadu_int32(src)); + return _mm_insert_epi32(s, loadu_int32((uint8_t *)src + offset), 1); +} + +static inline __m128i load_x_u8_4x2_sse4(const uint8_t *const src, + const ptrdiff_t stride) { + return load8_x_4x2_sse4(src, sizeof(*src) * stride); +} + +static inline __m128i convolve_x_2tap_2x2_ssse3(const uint8_t *const src, + const ptrdiff_t stride, + const __m128i *coeffs) { + const __m128i flt = _mm_load_si128((__m128i const *)filt5_global_sse2); + const __m128i reg = load_x_u8_4x2_sse4(src, stride); + const __m128i data = _mm_shuffle_epi8(reg, flt); + return convolve_x_2tap_ssse3(&data, coeffs); +} + +static inline __m128i convolve_x_2tap_4x2_ssse3(const uint8_t *const src, + const ptrdiff_t stride, + const __m128i *coeffs) { + const __m128i flt = _mm_load_si128((__m128i const *)filt1_global_sse2); + const __m128i data = + load_8bit_8x2_to_1_reg_sse2(src, (int)(sizeof(*src) * stride)); + const __m128i res = _mm_shuffle_epi8(data, flt); + return convolve_x_2tap_ssse3(&res, coeffs); +} + +static inline void convolve_x_2tap_8x2_ssse3(const uint8_t *const src, + const ptrdiff_t stride, + const __m128i *coeffs, + __m128i *data) { + __m128i res[2]; + const __m128i reg_00 = _mm_loadu_si128((__m128i *)src); + const __m128i reg_10 = _mm_loadu_si128((__m128i *)(src + stride)); + const __m128i reg_01 = _mm_srli_si128(reg_00, 1); + const __m128i reg_11 = _mm_srli_si128(reg_10, 1); + res[0] = _mm_unpacklo_epi8(reg_00, reg_01); + res[1] = _mm_unpacklo_epi8(reg_10, reg_11); + + data[0] = convolve_x_2tap_ssse3(&res[0], coeffs); + data[1] = convolve_x_2tap_ssse3(&res[1], coeffs); +} + +static inline __m256i loadu_x_8bit_16x2_avx2(const void *const src, + const ptrdiff_t offset) { + const __m128i reg0 = _mm_loadu_si128((__m128i *)src); + const __m128i reg1 = _mm_loadu_si128((__m128i *)((uint8_t *)src + offset)); + return _mm256_setr_m128i(reg0, reg1); +} + +static inline __m256i convolve_x_2tap_avx2(const __m256i *data, + const __m256i *coeffs) { + return _mm256_maddubs_epi16(data[0], coeffs[0]); +} + +static inline void convolve_x_2tap_16x2_avx2(const uint8_t *const src, + const ptrdiff_t stride, + const __m256i *coeffs, + __m256i *data) { + const __m256i reg0 = loadu_x_8bit_16x2_avx2(src, stride); + const __m256i reg1 = loadu_x_8bit_16x2_avx2(src + 1, stride); + const __m256i res0 = _mm256_unpacklo_epi8(reg0, reg1); + const __m256i res1 = _mm256_unpackhi_epi8(reg0, reg1); + data[0] = convolve_x_2tap_avx2(&res0, coeffs); + data[1] = convolve_x_2tap_avx2(&res1, coeffs); +} + +static inline void store_u8_16x2_avx2(const __m256i src, uint8_t *const dst, + const ptrdiff_t stride) { + const __m128i reg0 = _mm256_castsi256_si128(src); + const __m128i reg1 = _mm256_extracti128_si256(src, 1); + _mm_storeu_si128((__m128i *)dst, reg0); + _mm_storeu_si128((__m128i *)((uint8_t *)dst + stride), reg1); +} + +static inline void store_u8_8x2_avx2(const __m256i src, uint8_t *const dst, + const ptrdiff_t stride) { + const __m128i reg0 = _mm256_castsi256_si128(src); + const __m128i reg1 = _mm256_extracti128_si256(src, 1); + _mm_storel_epi64((__m128i *)dst, reg0); + _mm_storel_epi64((__m128i *)(dst + stride), reg1); +} + +static inline void pack_store_16x2_avx2(const __m256i data0, + const __m256i data1, uint8_t *const dst, + const ptrdiff_t stride) { + const __m256i res = _mm256_packus_epi16(data0, data1); + store_u8_16x2_avx2(res, dst, stride); +} + +static inline void pack_store_8x2_avx2(const __m256i data, uint8_t *const dst, + const ptrdiff_t stride) { + const __m256i res = _mm256_packus_epi16(data, data); + store_u8_8x2_avx2(res, dst, stride); +} + +static inline void round_pack_store_16x2_avx2(const __m256i *data, + uint8_t *const dst, + const ptrdiff_t dst_stride) { + __m256i reg[2]; + + reg[0] = round_sr_x_avx2(data[0]); + reg[1] = round_sr_x_avx2(data[1]); + pack_store_16x2_avx2(reg[0], reg[1], dst, dst_stride); +} + +static inline void convolve_x_2tap_32_avx2(const uint8_t *const src, + const __m256i *coeffs, + __m256i *data) { + const __m256i res0 = _mm256_loadu_si256((__m256i *)src); + const __m256i res1 = _mm256_loadu_si256((__m256i *)(src + 1)); + const __m256i reg0 = _mm256_unpacklo_epi8(res0, res1); + const __m256i reg1 = _mm256_unpackhi_epi8(res0, res1); + + data[0] = convolve_x_2tap_avx2(®0, coeffs); + data[1] = convolve_x_2tap_avx2(®1, coeffs); +} + +static inline void pack_store_32_avx2(const __m256i data0, const __m256i data1, + uint8_t *const dst) { + const __m256i reg = _mm256_packus_epi16(data0, data1); + _mm256_storeu_si256((__m256i *)dst, reg); +} + +static inline void round_pack_store_32_avx2(const __m256i *data, + uint8_t *const dst) { + __m256i reg[2]; + + reg[0] = round_sr_x_avx2(data[0]); + reg[1] = round_sr_x_avx2(data[1]); + pack_store_32_avx2(reg[0], reg[1], dst); +} + +static inline void convolve_round_2tap_32_avx2(const uint8_t *const src, + const __m256i *coeffs, + uint8_t *const dst) { + __m256i data[2]; + + convolve_x_2tap_32_avx2(src, coeffs, data); + round_pack_store_32_avx2(data, dst); +} + +static inline void load_avg_store_2tap_32_avx2(const uint8_t *const src, + uint8_t *const dst) { + const __m256i res0 = _mm256_loadu_si256((__m256i *)src); + const __m256i res1 = _mm256_loadu_si256((__m256i *)(src + 1)); + const __m256i data = _mm256_avg_epu8(res0, res1); + _mm256_storeu_si256((__m256i *)dst, data); +} + +static inline __m256i load_convolve_8tap_8x2_avx2(const uint8_t *const src, + const ptrdiff_t stride, + const __m256i *coeffs, + const __m256i *flt) { + const __m256i res = loadu_x_8bit_16x2_avx2(src, stride); + return convolve_lowbd_x(res, coeffs, flt); +} + +static inline void load_convolve_8tap_16x2_avx2(const uint8_t *const src, + const int32_t src_stride, + const __m256i *coeffs, + const __m256i *flt, + __m256i *reg) { + reg[0] = load_convolve_8tap_8x2_avx2(src + 0, src_stride, coeffs, flt); + reg[1] = load_convolve_8tap_8x2_avx2(src + 8, src_stride, coeffs, flt); +} + +static inline void load_convolve_8tap_32_avx2(const uint8_t *const src, + const __m256i *coeffs, + const __m256i *filt, + __m256i *data) { + const __m256i reg_0 = _mm256_loadu_si256((__m256i *)src); + const __m256i reg_8 = _mm256_loadu_si256((__m256i *)(src + 8)); + + data[0] = convolve_lowbd_x(reg_0, coeffs, filt); + data[1] = convolve_lowbd_x(reg_8, coeffs, filt); +} + +static inline void load_convolve_round_8tap_32_avx2(const uint8_t *const src, + const __m256i *coeffs, + const __m256i *filt, + uint8_t *const dst) { + __m256i data[2]; + + load_convolve_8tap_32_avx2(src, coeffs, filt, data); + round_pack_store_32_avx2(data, dst); +} + +static inline void load_convolve_6tap_32_avx2(const uint8_t *const src, + const __m256i *coeffs, + const __m256i *filt, + __m256i *data) { + const __m256i reg0 = _mm256_loadu_si256((__m256i *)src); + const __m256i reg1 = _mm256_loadu_si256((__m256i *)(src + 8)); + + data[0] = convolve_lowbd_x_6tap(reg0, coeffs, filt); + data[1] = convolve_lowbd_x_6tap(reg1, coeffs, filt); +} + +static inline void convolve_sr_store_6tap_32_avx2(const uint8_t *const src, + const __m256i *coeffs, + const __m256i *filt, + uint8_t *const dst) { + __m256i data[2]; + + load_convolve_6tap_32_avx2(src, coeffs, filt, data); + round_pack_store_32_avx2(data, dst); +} + +static inline __m256i load_convolve_6tap_8x2_avx2(const uint8_t *const src, + const ptrdiff_t stride, + const __m256i *coeffs, + const __m256i *filt) { + const __m256i data = loadu_x_8bit_16x2_avx2(src, stride); + return convolve_lowbd_x_6tap(data, coeffs, filt); +} + +static inline void load_convolve_6tap_16x2_avx2(const uint8_t *const src, + const int32_t src_stride, + const __m256i *coeffs, + const __m256i *filt, + __m256i *data) { + data[0] = load_convolve_6tap_8x2_avx2(src + 0, src_stride, coeffs, filt); + data[1] = load_convolve_6tap_8x2_avx2(src + 8, src_stride, coeffs, filt); +} + +static inline __m128i round_sr_y_ssse3(const __m128i data) { + const __m128i value = _mm_set1_epi16(32); + const __m128i reg = _mm_add_epi16(data, value); + return _mm_srai_epi16(reg, FILTER_BITS - 1); +} + +static inline __m256i round_sr_y_avx2(const __m256i data) { + const __m256i value = _mm256_set1_epi16(32); + const __m256i reg = _mm256_add_epi16(data, value); + return _mm256_srai_epi16(reg, FILTER_BITS - 1); +} + +static inline void round_pack_store_y_8x2_avx2(const __m256i res, + uint8_t *const dst, + const ptrdiff_t dst_stride) { + __m256i r; + + r = round_sr_y_avx2(res); + pack_store_8x2_avx2(r, dst, dst_stride); +} + +static inline void round_pack_store_y_16x2_avx2(const __m256i res[2], + uint8_t *const dst, + const ptrdiff_t dst_stride) { + __m256i r[2]; + + r[0] = round_sr_y_avx2(res[0]); + r[1] = round_sr_y_avx2(res[1]); + pack_store_16x2_avx2(r[0], r[1], dst, dst_stride); +} + +static inline void round_pack_store_y_32_avx2(const __m256i res[2], + uint8_t *const dst) { + __m256i r[2]; + + r[0] = round_sr_y_avx2(res[0]); + r[1] = round_sr_y_avx2(res[1]); + pack_store_32_avx2(r[0], r[1], dst); +} + +static inline void round_pack_store_y_32x2_avx2(const __m256i res[4], + uint8_t *const dst, + const ptrdiff_t dst_stride) { + round_pack_store_y_32_avx2(res, dst); + round_pack_store_y_32_avx2(res + 2, dst + dst_stride); +} + +static inline void convolve_y_2tap_2x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i *coeffs, + __m128i d[2], __m128i *res) { + d[1] = _mm_cvtsi32_si128(loadu_int16(data + 1 * stride)); + const __m128i src_01a = _mm_unpacklo_epi16(d[0], d[1]); + d[0] = _mm_cvtsi32_si128(loadu_int16(data + 2 * stride)); + const __m128i src_12a = _mm_unpacklo_epi16(d[1], d[0]); + + const __m128i s = _mm_unpacklo_epi8(src_01a, src_12a); + + *res = _mm_maddubs_epi16(s, coeffs[0]); +} + +static inline void convolve_y_4tap_2x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i coeffs[2], + __m128i d[4], __m128i s[2], + __m128i *res) { + d[3] = _mm_cvtsi32_si128(loadu_int16(data + 3 * stride)); + const __m128i src_23a = _mm_unpacklo_epi16(d[2], d[3]); + d[2] = _mm_cvtsi32_si128(loadu_int16(data + 4 * stride)); + const __m128i src_34a = _mm_unpacklo_epi16(d[3], d[2]); + + s[1] = _mm_unpacklo_epi8(src_23a, src_34a); + + *res = convolve_lowbd_4tap_ssse3(s, coeffs); +} + +static inline void convolve_y_6tap_2x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i coeffs[3], + __m128i d[6], __m128i s[3], + __m128i *res) { + d[5] = _mm_cvtsi32_si128(loadu_int16(data + 5 * stride)); + const __m128i src_45a = _mm_unpacklo_epi16(d[4], d[5]); + d[4] = _mm_cvtsi32_si128(loadu_int16(data + 6 * stride)); + const __m128i src_56a = _mm_unpacklo_epi16(d[5], d[4]); + + s[2] = _mm_unpacklo_epi8(src_45a, src_56a); + + *res = convolve_lowbd_6tap_ssse3(s, coeffs); +} + +static inline void convolve_y_8tap_2x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i coeffs[4], + __m128i d[8], __m128i s[4], + __m128i *res) { + d[7] = _mm_cvtsi32_si128(loadu_int16(data + 7 * stride)); + const __m128i src_67a = _mm_unpacklo_epi16(d[6], d[7]); + d[6] = _mm_cvtsi32_si128(loadu_int16(data + 8 * stride)); + const __m128i src_78a = _mm_unpacklo_epi16(d[7], d[6]); + + s[3] = _mm_unpacklo_epi8(src_67a, src_78a); + + *res = convolve_lowbd_ssse3(s, coeffs); +} + +static inline void convolve_y_2tap_4x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i *coeffs, + __m128i d[2], __m128i *res) { + d[1] = _mm_cvtsi32_si128(loadu_int32(data + 1 * stride)); + const __m128i src_01a = _mm_unpacklo_epi32(d[0], d[1]); + d[0] = _mm_cvtsi32_si128(loadu_int32(data + 2 * stride)); + const __m128i src_12a = _mm_unpacklo_epi32(d[1], d[0]); + + const __m128i s = _mm_unpacklo_epi8(src_01a, src_12a); + + *res = _mm_maddubs_epi16(s, coeffs[0]); +} + +static inline void convolve_y_4tap_4x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i coeffs[2], + __m128i d[4], __m128i s[2], + __m128i *res) { + d[3] = _mm_cvtsi32_si128(loadu_int32(data + 3 * stride)); + const __m128i src_23a = _mm_unpacklo_epi32(d[2], d[3]); + d[2] = _mm_cvtsi32_si128(loadu_int32(data + 4 * stride)); + const __m128i src_34a = _mm_unpacklo_epi32(d[3], d[2]); + + s[1] = _mm_unpacklo_epi8(src_23a, src_34a); + + *res = convolve_lowbd_4tap_ssse3(s, coeffs); +} + +static inline void convolve_y_6tap_4x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i coeffs[3], + __m128i d[6], __m128i s[3], + __m128i *res) { + d[5] = _mm_cvtsi32_si128(loadu_int32(data + 5 * stride)); + const __m128i src_45a = _mm_unpacklo_epi32(d[4], d[5]); + d[4] = _mm_cvtsi32_si128(loadu_int32(data + 6 * stride)); + const __m128i src_56a = _mm_unpacklo_epi32(d[5], d[4]); + + s[2] = _mm_unpacklo_epi8(src_45a, src_56a); + + *res = convolve_lowbd_6tap_ssse3(s, coeffs); +} + +static inline void convolve_y_8tap_4x2_ssse3(const uint8_t *const data, + const ptrdiff_t stride, + const __m128i coeffs[4], + __m128i d[8], __m128i s[4], + __m128i *res) { + d[7] = _mm_cvtsi32_si128(loadu_int32(data + 7 * stride)); + const __m128i src_67a = _mm_unpacklo_epi32(d[6], d[7]); + d[6] = _mm_cvtsi32_si128(loadu_int32(data + 8 * stride)); + const __m128i src_78a = _mm_unpacklo_epi32(d[7], d[6]); + + s[3] = _mm_unpacklo_epi8(src_67a, src_78a); + + res[0] = convolve_lowbd_ssse3(s, coeffs); +} + +static inline void convolve_y_2tap_8x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i *coeffs, __m128i d[2], + __m256i *res) { + d[1] = _mm_loadu_si128((__m128i *)(data + 1 * stride)); + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + d[0] = _mm_loadu_si128((__m128i *)(data + 2 * stride)); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[0]); + + const __m256i s = _mm256_unpacklo_epi8(src_01a, src_12a); + + *res = _mm256_maddubs_epi16(s, coeffs[0]); +} + +static inline void convolve_y_4tap_8x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[2], + __m128i d[4], __m256i s[2], + __m256i *res) { + d[3] = _mm_loadu_si128((__m128i *)(data + 3 * stride)); + const __m256i src_23a = _mm256_setr_m128i(d[2], d[3]); + d[2] = _mm_loadu_si128((__m128i *)(data + 4 * stride)); + const __m256i src_34a = _mm256_setr_m128i(d[3], d[2]); + + s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); + + *res = convolve_lowbd_4tap(s, coeffs); +} + +static inline void convolve_y_6tap_8x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[3], + __m128i d[6], __m256i s[3], + __m256i *res) { + d[5] = _mm_loadu_si128((__m128i *)(data + 5 * stride)); + const __m256i src_45a = _mm256_setr_m128i(d[4], d[5]); + d[4] = _mm_loadu_si128((__m128i *)(data + 6 * stride)); + const __m256i src_56a = _mm256_setr_m128i(d[5], d[4]); + + s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); + + *res = convolve_lowbd_6tap(s, coeffs); +} + +static inline void convolve_y_8tap_8x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[4], + __m128i d[8], __m256i s[4], + __m256i *res) { + d[7] = _mm_loadu_si128((__m128i *)(data + 7 * stride)); + const __m256i src_67a = _mm256_setr_m128i(d[6], d[7]); + d[6] = _mm_loadu_si128((__m128i *)(data + 8 * stride)); + const __m256i src_78a = _mm256_setr_m128i(d[7], d[6]); + + s[3] = _mm256_unpacklo_epi8(src_67a, src_78a); + + *res = convolve_lowbd(s, coeffs); +} + +static inline void convolve_y_2tap_16x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i *coeffs, + __m128i d[2], __m256i res[2]) { + d[1] = _mm_loadu_si128((__m128i *)(data + 1 * stride)); + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + d[0] = _mm_loadu_si128((__m128i *)(data + 2 * stride)); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[0]); + + const __m256i s0 = _mm256_unpacklo_epi8(src_01a, src_12a); + const __m256i s1 = _mm256_unpackhi_epi8(src_01a, src_12a); + + res[0] = _mm256_maddubs_epi16(s0, coeffs[0]); + res[1] = _mm256_maddubs_epi16(s1, coeffs[0]); +} + +static inline void convolve_y_4tap_16x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[2], + __m128i d[4], __m256i s[4], + __m256i res[2]) { + d[3] = _mm_loadu_si128((__m128i *)(data + 3 * stride)); + const __m256i src_23a = _mm256_setr_m128i(d[2], d[3]); + d[2] = _mm_loadu_si128((__m128i *)(data + 4 * stride)); + const __m256i src_34a = _mm256_setr_m128i(d[3], d[2]); + + s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); + s[3] = _mm256_unpackhi_epi8(src_23a, src_34a); + + res[0] = convolve_lowbd_4tap(s, coeffs); + res[1] = convolve_lowbd_4tap(s + 2, coeffs); +} + +static inline void convolve_y_6tap_16x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[3], + __m128i d[6], __m256i s[6], + __m256i res[2]) { + d[5] = _mm_loadu_si128((__m128i *)(data + 5 * stride)); + const __m256i src_45a = _mm256_setr_m128i(d[4], d[5]); + d[4] = _mm_loadu_si128((__m128i *)(data + 6 * stride)); + const __m256i src_56a = _mm256_setr_m128i(d[5], d[4]); + + s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); + s[5] = _mm256_unpackhi_epi8(src_45a, src_56a); + + res[0] = convolve_lowbd_6tap(s, coeffs); + res[1] = convolve_lowbd_6tap(s + 3, coeffs); +} + +static inline void convolve_y_8tap_16x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[4], + __m128i d[8], __m256i s[8], + __m256i res[2]) { + d[7] = _mm_loadu_si128((__m128i *)(data + 7 * stride)); + const __m256i src_67a = _mm256_setr_m128i(d[6], d[7]); + d[6] = _mm_loadu_si128((__m128i *)(data + 8 * stride)); + const __m256i src_78a = _mm256_setr_m128i(d[7], d[6]); + + s[3] = _mm256_unpacklo_epi8(src_67a, src_78a); + s[7] = _mm256_unpackhi_epi8(src_67a, src_78a); + + res[0] = convolve_lowbd(s, coeffs); + res[1] = convolve_lowbd(s + 4, coeffs); +} + +static inline void convolve_y_2tap_32x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i *coeffs, + __m256i d[2], __m256i res[4]) { + d[1] = _mm256_loadu_si256((__m256i *)(data + 1 * stride)); + const __m256i s00 = _mm256_unpacklo_epi8(d[0], d[1]); + const __m256i s01 = _mm256_unpackhi_epi8(d[0], d[1]); + d[0] = _mm256_loadu_si256((__m256i *)(data + 2 * stride)); + const __m256i s10 = _mm256_unpacklo_epi8(d[1], d[0]); + const __m256i s11 = _mm256_unpackhi_epi8(d[1], d[0]); + + res[0] = _mm256_maddubs_epi16(s00, coeffs[0]); + res[1] = _mm256_maddubs_epi16(s01, coeffs[0]); + res[2] = _mm256_maddubs_epi16(s10, coeffs[0]); + res[3] = _mm256_maddubs_epi16(s11, coeffs[0]); +} + +static inline void convolve_y_4tap_32x2_avx2(const uint8_t *const data, + const ptrdiff_t stride, + const __m256i coeffs[2], + __m256i d[4], __m256i s1[4], + __m256i s2[4], __m256i res[4]) { + d[3] = _mm256_loadu_si256((__m256i *)(data + 3 * stride)); + s1[1] = _mm256_unpacklo_epi8(d[2], d[3]); + s1[3] = _mm256_unpackhi_epi8(d[2], d[3]); + d[2] = _mm256_loadu_si256((__m256i *)(data + 4 * stride)); + s2[1] = _mm256_unpacklo_epi8(d[3], d[2]); + s2[3] = _mm256_unpackhi_epi8(d[3], d[2]); + + res[0] = convolve_lowbd_4tap(s1, coeffs); + res[1] = convolve_lowbd_4tap(s1 + 2, coeffs); + res[2] = convolve_lowbd_4tap(s2, coeffs); + res[3] = convolve_lowbd_4tap(s2 + 2, coeffs); +} #endif // AOM_AOM_DSP_X86_CONVOLVE_AVX2_H_
diff --git a/aom_dsp/x86/quantize_x86.h b/aom_dsp/x86/quantize_x86.h index 217989e..deef583 100644 --- a/aom_dsp/x86/quantize_x86.h +++ b/aom_dsp/x86/quantize_x86.h
@@ -9,6 +9,7 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <assert.h> #include <emmintrin.h> #include "aom/aom_integer.h" @@ -125,7 +126,7 @@ } static inline __m128i load_coefficients(const tran_low_t *coeff_ptr) { - assert(sizeof(tran_low_t) == 4); + static_assert(sizeof(tran_low_t) == 4, ""); const __m128i coeff1 = _mm_load_si128((__m128i *)(coeff_ptr)); const __m128i coeff2 = _mm_load_si128((__m128i *)(coeff_ptr + 4)); return _mm_packs_epi32(coeff1, coeff2); @@ -133,7 +134,7 @@ static inline void store_coefficients(__m128i coeff_vals, tran_low_t *coeff_ptr) { - assert(sizeof(tran_low_t) == 4); + static_assert(sizeof(tran_low_t) == 4, ""); __m128i one = _mm_set1_epi16(1); __m128i coeff_vals_hi = _mm_mulhi_epi16(coeff_vals, one);
diff --git a/aom_dsp/x86/synonyms.h b/aom_dsp/x86/synonyms.h index 0f82982..21a5a53 100644 --- a/aom_dsp/x86/synonyms.h +++ b/aom_dsp/x86/synonyms.h
@@ -54,6 +54,11 @@ _mm_loadl_epi64((const __m128i *)hi)); } +static inline void xx_storel_16(void *const a, const __m128i v) { + const uint16_t val = (uint16_t)_mm_cvtsi128_si32(v); + memcpy(a, &val, sizeof(val)); +} + static inline void xx_storel_32(void *const a, const __m128i v) { const int val = _mm_cvtsi128_si32(v); memcpy(a, &val, sizeof(val));
diff --git a/aom_mem/aom_mem.h b/aom_mem/aom_mem.h index 15cc43d..b332139 100644 --- a/aom_mem/aom_mem.h +++ b/aom_mem/aom_mem.h
@@ -12,6 +12,8 @@ #ifndef AOM_AOM_MEM_AOM_MEM_H_ #define AOM_AOM_MEM_AOM_MEM_H_ +#include <assert.h> + #include "aom/aom_integer.h" #include "config/aom_config.h" @@ -57,6 +59,7 @@ #if CONFIG_DEBUG #define AOM_CHECK_MEM_ERROR(error_info, lval, expr) \ do { \ + assert((error_info)->setjmp); \ lval = (expr); \ if (!lval) \ aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, \ @@ -66,6 +69,7 @@ #else #define AOM_CHECK_MEM_ERROR(error_info, lval, expr) \ do { \ + assert((error_info)->setjmp); \ lval = (expr); \ if (!lval) \ aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, \
diff --git a/aom_scale/aom_scale_rtcd.pl b/aom_scale/aom_scale_rtcd.pl index bcdb898..1f2df8d 100644 --- a/aom_scale/aom_scale_rtcd.pl +++ b/aom_scale/aom_scale_rtcd.pl
@@ -19,8 +19,6 @@ add_proto qw/int aom_yv12_realloc_with_new_border/, "struct yv12_buffer_config *ybf, int new_border, int byte_alignment, bool alloc_pyramid, int num_planes"; -add_proto qw/void aom_yv12_extend_frame_borders/, "struct yv12_buffer_config *ybf, const int num_planes"; - add_proto qw/void aom_yv12_copy_frame/, "const struct yv12_buffer_config *src_bc, struct yv12_buffer_config *dst_bc, const int num_planes"; add_proto qw/void aom_yv12_copy_y/, "const struct yv12_buffer_config *src_ybc, struct yv12_buffer_config *dst_ybc, int use_crop";
diff --git a/aom_scale/generic/yv12extend.c b/aom_scale/generic/yv12extend.c index 019fbfb..8bc01f7 100644 --- a/aom_scale/generic/yv12extend.c +++ b/aom_scale/generic/yv12extend.c
@@ -144,42 +144,6 @@ extend_bottom_border ? bottom : 0, right, v_start, v_end); } -void aom_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf, - const int num_planes) { - assert(ybf->border % 2 == 0); - assert(ybf->y_height - ybf->y_crop_height < 16); - assert(ybf->y_width - ybf->y_crop_width < 16); - assert(ybf->y_height - ybf->y_crop_height >= 0); - assert(ybf->y_width - ybf->y_crop_width >= 0); - -#if CONFIG_AV1_HIGHBITDEPTH - if (ybf->flags & YV12_FLAG_HIGHBITDEPTH) { - for (int plane = 0; plane < num_planes; ++plane) { - const int is_uv = plane > 0; - const int plane_border = ybf->border >> is_uv; - extend_plane_high( - ybf->buffers[plane], ybf->strides[is_uv], ybf->crop_widths[is_uv], - ybf->crop_heights[is_uv], plane_border, plane_border, - plane_border + ybf->heights[is_uv] - ybf->crop_heights[is_uv], - plane_border + ybf->widths[is_uv] - ybf->crop_widths[is_uv], 0, - ybf->crop_heights[is_uv]); - } - return; - } -#endif - - for (int plane = 0; plane < num_planes; ++plane) { - const int is_uv = plane > 0; - const int plane_border = ybf->border >> is_uv; - extend_plane(ybf->buffers[plane], ybf->strides[is_uv], - ybf->crop_widths[is_uv], ybf->crop_heights[is_uv], - plane_border, plane_border, - plane_border + ybf->heights[is_uv] - ybf->crop_heights[is_uv], - plane_border + ybf->widths[is_uv] - ybf->crop_widths[is_uv], 0, - ybf->crop_heights[is_uv]); - } -} - static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size, const int num_planes) { const int ss_x = ybf->subsampling_x; @@ -254,7 +218,7 @@ plane_dst += dst_bc->strides[is_uv]; } } - aom_yv12_extend_frame_borders_c(dst_bc, num_planes); + aom_extend_frame_borders_c(dst_bc, num_planes); return; } #endif @@ -269,7 +233,7 @@ plane_dst += dst_bc->strides[is_uv]; } } - aom_yv12_extend_frame_borders_c(dst_bc, num_planes); + aom_extend_frame_borders_c(dst_bc, num_planes); } void aom_yv12_copy_y_c(const YV12_BUFFER_CONFIG *src_ybc, @@ -482,9 +446,6 @@ // Copy image buffer aom_yv12_copy_frame(ybf, &new_buf, num_planes); - // Extend up to new border - aom_extend_frame_borders(&new_buf, num_planes); - // Now free the old buffer and replace with the new aom_free_frame_buffer(ybf); *ybf = new_buf;
diff --git a/aom_util/aom_thread.c b/aom_util/aom_thread.c index 5a60617..c4b3bf7 100644 --- a/aom_util/aom_thread.c +++ b/aom_util/aom_thread.c
@@ -44,6 +44,7 @@ static THREADFN thread_loop(void *ptr) { AVxWorker *const worker = (AVxWorker *)ptr; +#ifdef HAVE_PTHREAD_SETNAME_NP #ifdef __APPLE__ if (worker->thread_name != NULL) { // Apple's version of pthread_setname_np takes one argument and operates on @@ -66,6 +67,7 @@ pthread_setname_np(pthread_self(), thread_name); } #endif +#endif pthread_mutex_lock(&worker->impl_->mutex_); for (;;) { while (worker->status_ == AVX_WORKER_STATUS_OK) { // wait in idling mode
diff --git a/apps/aomdec.c b/apps/aomdec.c index 9933f6a..e09ebfc 100644 --- a/apps/aomdec.c +++ b/apps/aomdec.c
@@ -9,6 +9,7 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h>
diff --git a/apps/aomenc.c b/apps/aomenc.c index 3caae90..c2b7d95 100644 --- a/apps/aomenc.c +++ b/apps/aomenc.c
@@ -14,6 +14,7 @@ #include "config/aom_config.h" #include <assert.h> +#include <inttypes.h> #include <limits.h> #include <math.h> #include <stdarg.h> @@ -958,13 +959,9 @@ } else if (strcmp(get_short_name_by_aom_encoder(global->codec), "av1") == 0) { // TODO(jingning): Reuse AV1 specific encoder configuration parameters. // Consider to expand this set for AV1 encoder control. -#if __STDC_VERSION__ >= 201112L - _Static_assert(NELEMENTS(av1_ctrl_args) == NELEMENTS(av1_arg_ctrl_map), - "The av1_ctrl_args and av1_arg_ctrl_map arrays must be of " - "the same size."); -#else - assert(NELEMENTS(av1_ctrl_args) == NELEMENTS(av1_arg_ctrl_map)); -#endif + static_assert(NELEMENTS(av1_ctrl_args) == NELEMENTS(av1_arg_ctrl_map), + "The av1_ctrl_args and av1_arg_ctrl_map arrays must be of " + "the same size."); ctrl_args = av1_ctrl_args; ctrl_args_map = av1_arg_ctrl_map; key_val_args = av1_key_val_args; @@ -1206,10 +1203,12 @@ } config->use_16bit_internal |= config->cfg.g_bit_depth > AOM_BITS_8; +#if CONFIG_REALTIME_ONLY if (global->usage == AOM_USAGE_REALTIME && config->cfg.g_lag_in_frames != 0) { aom_tools_warn("non-zero lag-in-frames option ignored in realtime mode.\n"); config->cfg.g_lag_in_frames = 0; } +#endif if (global->usage == AOM_USAGE_ALL_INTRA) { if (config->cfg.g_lag_in_frames != 0) {
diff --git a/av1/arg_defs.c b/av1/arg_defs.c index 81d00c4..ff0f03a 100644 --- a/av1/arg_defs.c +++ b/av1/arg_defs.c
@@ -81,7 +81,7 @@ { "fcc73", AOM_CICP_MC_FCC }, { "bt470bg", AOM_CICP_MC_BT_470_B_G }, { "bt601", AOM_CICP_MC_BT_601 }, - { "smpte240", AOM_CICP_CP_SMPTE_240 }, + { "smpte240", AOM_CICP_MC_SMPTE_240 }, { "ycgco", AOM_CICP_MC_SMPTE_YCGCO }, { "bt2020ncl", AOM_CICP_MC_BT_2020_NCL }, { "bt2020cl", AOM_CICP_MC_BT_2020_CL }, @@ -89,6 +89,9 @@ { "chromncl", AOM_CICP_MC_CHROMAT_NCL }, { "chromcl", AOM_CICP_MC_CHROMAT_CL }, { "ictcp", AOM_CICP_MC_ICTCP }, + { "ipt-c2", AOM_CICP_MC_IPT_C2 }, + { "ycgco-re", AOM_CICP_MC_YCGCO_RE }, + { "ycgco-ro", AOM_CICP_MC_YCGCO_RO }, { NULL, 0 } }; @@ -107,7 +110,7 @@ }; static const struct arg_enum_list transfer_characteristics_enum[] = { - { "unspecified", AOM_CICP_CP_UNSPECIFIED }, + { "unspecified", AOM_CICP_TC_UNSPECIFIED }, { "bt709", AOM_CICP_TC_BT_709 }, { "bt470m", AOM_CICP_TC_BT_470_M }, { "bt470bg", AOM_CICP_TC_BT_470_B_G }, @@ -326,7 +329,7 @@ .enable_tpl_model = ARG_DEF(NULL, "enable-tpl-model", 1, "RDO based on frame temporal dependency " "(0: off, 1: backward source based); " - "required for deltaq mode"), + "required for --deltaq-mode=1"), .enable_keyframe_filtering = ARG_DEF( NULL, "enable-keyframe-filtering", 1, "Apply temporal filtering on key frame " @@ -557,7 +560,7 @@ "Delta qindex mode (0: off, 1: deltaq objective (default), " "2: deltaq placeholder, 3: key frame visual quality, 4: user " "rating based visual quality optimization, 5: HDR video, 6: " - "Variance Boost all intra); requires --enable-tpl-model=1"), + "Variance Boost); --deltaq-mode=1 requires --enable-tpl-model=1"), .deltaq_strength = ARG_DEF(NULL, "deltaq-strength", 1, "Deltaq strength for" " --deltaq-mode=4 and --deltaq-mode=6 (%)"), @@ -641,11 +644,10 @@ .use_fixed_qp_offsets = ARG_DEF(NULL, "use-fixed-qp-offsets", 1, - "Enable fixed QP offsets for frames at different levels of the " - "pyramid. Selected automatically from --cq-level if " - "--fixed-qp-offsets is not provided. If this option is not " - "specified (default), offsets are adaptively chosen by the " - "encoder."), + "Controls how the encoder applies fixed QP offsets for frames at " + "different levels of the pyramid (0: adaptively-chosen offsets " + "from --cq-level if --fixed-qp-offsets is not provided " + "(default), 1: fixed QP offsets, 2: no QP offsets)"), .fixed_qp_offsets = ARG_DEF( NULL, "fixed-qp-offsets", 1, @@ -709,12 +711,15 @@ ARG_DEF(NULL, "sb-qp-sweep", 1, "When set to 1, enable the superblock level qp sweep for a " "given lambda to minimize the rdcost."), - .enable_low_complexity_decode = - ARG_DEF(NULL, "enable-low-complexity-decode", 1, - "Enable low complexity decode (0: false (default), 1: true)"), - .screen_detection_mode = - ARG_DEF(NULL, "screen-detection-mode", 1, - "Screen content detection mode (1: standard (default), " - "2: anti-aliased text and graphics aware)"), + .enable_low_complexity_decode = ARG_DEF( + NULL, "enable-low-complexity-decode", 1, + "Enable low complexity decode (0: false (default), 1: true). As of now, " + "this only supports good-quality encoding (speed 1 to 3) for vertical " + "videos between 608p and 720p."), + .screen_detection_mode = ARG_DEF( + NULL, "screen-detection-mode", 1, + "Screen content detection mode (1: standard (default in good quality and " + "realtime modes), 2: anti-aliased text and graphics aware (default in " + "all intra mode))"), #endif // CONFIG_AV1_ENCODER };
diff --git a/av1/av1.cmake b/av1/av1.cmake index 61533df..c440720 100644 --- a/av1/av1.cmake +++ b/av1/av1.cmake
@@ -94,6 +94,7 @@ if(CONFIG_HIGHWAY) list(APPEND AOM_AV1_COMMON_SOURCES "${AOM_ROOT}/av1/common/selfguided_hwy.h") + list(APPEND AOM_AV1_COMMON_SOURCES "${AOM_ROOT}/av1/common/warp_plane_hwy.h") endif() list(APPEND AOM_AV1_DECODER_SOURCES @@ -126,6 +127,8 @@ "${AOM_ROOT}/av1/encoder/allintra_vis.c" "${AOM_ROOT}/av1/encoder/allintra_vis.h" "${AOM_ROOT}/av1/encoder/enc_enums.h" + "${AOM_ROOT}/av1/encoder/av1_ext_ratectrl.c" + "${AOM_ROOT}/av1/encoder/av1_ext_ratectrl.h" "${AOM_ROOT}/av1/encoder/av1_fwd_txfm1d.c" "${AOM_ROOT}/av1/encoder/av1_fwd_txfm1d.h" "${AOM_ROOT}/av1/encoder/av1_fwd_txfm1d_cfg.h" @@ -321,8 +324,13 @@ "${AOM_ROOT}/av1/common/x86/wiener_convolve_avx2.c") if(CONFIG_HIGHWAY) + list(APPEND AOM_AV1_COMMON_INTRIN_SSE4_1 + "${AOM_ROOT}/av1/common/x86/warp_plane_hwy_sse4.cc") + list(APPEND AOM_AV1_COMMON_INTRIN_AVX2 + "${AOM_ROOT}/av1/common/x86/warp_plane_hwy_avx2.cc") list(APPEND AOM_AV1_COMMON_INTRIN_AVX512 - "${AOM_ROOT}/av1/common/x86/selfguided_hwy_avx512.cc") + "${AOM_ROOT}/av1/common/x86/selfguided_hwy_avx512.cc" + "${AOM_ROOT}/av1/common/x86/warp_plane_hwy_avx512.cc") endif() list(APPEND AOM_AV1_ENCODER_ASM_SSE2 "${AOM_ROOT}/av1/encoder/x86/dct_sse2.asm" @@ -454,11 +462,11 @@ "${AOM_ROOT}/av1/common/arm/av1_convolve_scale_neon_i8mm.c" "${AOM_ROOT}/av1/common/arm/compound_convolve_neon_i8mm.c" "${AOM_ROOT}/av1/common/arm/convolve_neon_i8mm.c" + "${AOM_ROOT}/av1/common/arm/reconintra_neon_i8mm.c" "${AOM_ROOT}/av1/common/arm/resize_neon_i8mm.c" "${AOM_ROOT}/av1/common/arm/warp_plane_neon_i8mm.c") list(APPEND AOM_AV1_COMMON_INTRIN_SVE - "${AOM_ROOT}/av1/common/arm/highbd_warp_plane_sve.c" "${AOM_ROOT}/av1/common/arm/warp_plane_sve.c") list(APPEND AOM_AV1_COMMON_INTRIN_SVE2 @@ -471,7 +479,9 @@ list(APPEND AOM_AV1_COMMON_INTRIN_RVV "${AOM_ROOT}/av1/common/riscv/cdef_block_rvv.c" + "${AOM_ROOT}/av1/common/riscv/compound_convolve_rvv.c" "${AOM_ROOT}/av1/common/riscv/convolve_rvv.c" + "${AOM_ROOT}/av1/common/riscv/highbd_compound_convolve_rvv.c" "${AOM_ROOT}/av1/common/riscv/highbd_convolve_rvv.c") if(CONFIG_THREE_PASS) @@ -542,6 +552,9 @@ "${AOM_ROOT}/av1/common/arm/highbd_warp_plane_neon.c" "${AOM_ROOT}/av1/common/arm/highbd_wiener_convolve_neon.c") + list(APPEND AOM_AV1_COMMON_INTRIN_SVE + "${AOM_ROOT}/av1/common/arm/highbd_warp_plane_sve.c") + list(APPEND AOM_AV1_COMMON_INTRIN_SVE2 "${AOM_ROOT}/av1/common/arm/highbd_compound_convolve_sve2.c" "${AOM_ROOT}/av1/common/arm/highbd_convolve_sve2.c") @@ -591,7 +604,8 @@ "${AOM_ROOT}/av1/common/restoration.h" "${AOM_ROOT}/av1/common/selfguided_hwy.h" "${AOM_ROOT}/av1/common/warped_motion.c" - "${AOM_ROOT}/av1/common/warped_motion.h") + "${AOM_ROOT}/av1/common/warped_motion.h" + "${AOM_ROOT}/av1/common/warp_plane_hwy.h") list(REMOVE_ITEM AOM_AV1_COMMON_INTRIN_SSE2 "${AOM_ROOT}/av1/common/x86/cfl_sse2.c" @@ -601,7 +615,8 @@ list(REMOVE_ITEM AOM_AV1_COMMON_INTRIN_SSE4_1 "${AOM_ROOT}/av1/common/x86/highbd_warp_plane_sse4.c" "${AOM_ROOT}/av1/common/x86/selfguided_sse4.c" - "${AOM_ROOT}/av1/common/x86/warp_plane_sse4.c") + "${AOM_ROOT}/av1/common/x86/warp_plane_sse4.c" + "${AOM_ROOT}/av1/common/x86/warp_plane_hwy_sse4.cc") list( REMOVE_ITEM AOM_AV1_COMMON_INTRIN_SSSE3 @@ -614,10 +629,12 @@ "${AOM_ROOT}/av1/common/x86/highbd_wiener_convolve_avx2.c" "${AOM_ROOT}/av1/common/x86/selfguided_avx2.c" "${AOM_ROOT}/av1/common/x86/warp_plane_avx2.c" + "${AOM_ROOT}/av1/common/x86/warp_plane_hwy_avx2.cc" "${AOM_ROOT}/av1/common/x86/wiener_convolve_avx2.c") list(REMOVE_ITEM AOM_AV1_COMMON_INTRIN_AVX512 - "${AOM_ROOT}/av1/common/x86/selfguided_hwy_avx512.cc") + "${AOM_ROOT}/av1/common/x86/selfguided_hwy_avx512.cc" + "${AOM_ROOT}/av1/common/x86/warp_plane_hwy_avx512.cc") list(REMOVE_ITEM AOM_AV1_COMMON_INTRIN_NEON "${AOM_ROOT}/av1/common/arm/cfl_neon.c" @@ -660,6 +677,7 @@ "${AOM_ROOT}/av1/encoder/arm/temporal_filter_neon_dotprod.c") list(REMOVE_ITEM AOM_AV1_ENCODER_INTRIN_SVE + "${AOM_ROOT}/av1/encoder/arm/highbd_pickrst_sve.c" "${AOM_ROOT}/av1/encoder/arm/pickrst_sve.c") list(REMOVE_ITEM AOM_AV1_ENCODER_SOURCES
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c index 07820d6..f9adad4 100644 --- a/av1/av1_cx_iface.c +++ b/av1/av1_cx_iface.c
@@ -8,8 +8,11 @@ * 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 <assert.h> #include <limits.h> #include <math.h> +#include <setjmp.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> @@ -34,6 +37,7 @@ #include "av1/common/enums.h" #include "av1/common/quant_common.h" #include "av1/common/scale.h" +#include "av1/encoder/av1_ext_ratectrl.h" #include "av1/encoder/bitstream.h" #include "av1/encoder/enc_enums.h" #include "av1/encoder/encoder.h" @@ -48,6 +52,33 @@ #include "common/args_helper.h" +// Creates a setjmp target using `CPI->common.error->jmp` and sets +// `CPI->common.error->setjmp = 1`. Returns `CPI->common.error->error_code` on +// longjmp. This macro expects `aom_codec_alg_priv_t *ctx` to be available. +// This should be accompanied by a call to DISABLE_SETJMP using the same CPI +// before going out of scope. +#define ENABLE_SETJMP(CPI) \ + do { \ + struct aom_internal_error_info *const enable_setjmp_error = \ + (CPI)->common.error; \ + if (setjmp(enable_setjmp_error->jmp)) { \ + enable_setjmp_error->setjmp = 0; \ + ctx->base.err_detail = enable_setjmp_error->has_detail \ + ? enable_setjmp_error->detail \ + : NULL; \ + return enable_setjmp_error->error_code; \ + } \ + enable_setjmp_error->setjmp = 1; \ + } while (0) + +// Sets CPI->common.error->setjmp = 0. +#define DISABLE_SETJMP(CPI) \ + do { \ + struct aom_internal_error_info *const enable_setjmp_error = \ + (CPI)->common.error; \ + enable_setjmp_error->setjmp = 0; \ + } while (0) + struct av1_extracfg { int cpu_used; unsigned int enable_auto_alt_ref; @@ -682,12 +713,6 @@ RANGE_CHECK_BOOL(extra_cfg, lossless); RANGE_CHECK_HI(extra_cfg, aq_mode, AQ_MODE_COUNT - 1); RANGE_CHECK_HI(extra_cfg, deltaq_mode, DELTA_Q_MODE_COUNT - 1); - - if (cfg->g_usage != ALLINTRA && - extra_cfg->deltaq_mode == DELTA_Q_VARIANCE_BOOST) { - ERROR("Variance Boost (deltaq_mode = 6) can only be set in all intra mode"); - } - RANGE_CHECK_HI(extra_cfg, deltalf_mode, 1); RANGE_CHECK_HI(extra_cfg, frame_periodic_boost, 1); #if CONFIG_REALTIME_ONLY @@ -820,7 +845,7 @@ } if (cfg->rc_end_usage == AOM_Q) { - RANGE_CHECK_HI(cfg, use_fixed_qp_offsets, 1); + RANGE_CHECK_HI(cfg, use_fixed_qp_offsets, 2); } else { if (cfg->use_fixed_qp_offsets > 0) { ERROR("--use_fixed_qp_offsets can only be used with --end-usage=q"); @@ -833,7 +858,7 @@ RANGE_CHECK(extra_cfg, transfer_characteristics, AOM_CICP_TC_BT_709, AOM_CICP_TC_HLG); RANGE_CHECK(extra_cfg, matrix_coefficients, AOM_CICP_MC_IDENTITY, - AOM_CICP_MC_ICTCP); + AOM_CICP_MC_YCGCO_RO); RANGE_CHECK(extra_cfg, color_range, 0, 1); /* Average corpus complexity is supported only in the case of single pass @@ -964,6 +989,10 @@ if (img->d_w != ctx->cfg.g_w || img->d_h != ctx->cfg.g_h) ERROR("Image size must match encoder init configuration size"); + assert(img->fmt & AOM_IMG_FMT_PLANAR); + if (!ctx->cfg.monochrome && img->fmt != AOM_IMG_FMT_NV12 && + img->stride[AOM_PLANE_U] != img->stride[AOM_PLANE_V]) + ERROR("Image U/V strides must match"); // 6.4.2 Color config semantics // If matrix_coefficients is equal to MC_IDENTITY, it is a requirement of @@ -1260,7 +1289,8 @@ q_cfg->deltaq_mode = extra_cfg->deltaq_mode; q_cfg->deltaq_strength = extra_cfg->deltaq_strength; q_cfg->use_fixed_qp_offsets = - cfg->use_fixed_qp_offsets && (rc_cfg->mode == AOM_Q); + (rc_cfg->mode == AOM_Q) ? cfg->use_fixed_qp_offsets : 0; + q_cfg->enable_hdr_deltaq = (q_cfg->deltaq_mode == DELTA_Q_HDR) && (cfg->g_bit_depth == AOM_BITS_10) && @@ -1337,11 +1367,15 @@ oxcf->speed = 7; // Now, low complexity decode mode is only supported for good-quality - // encoding speed 1 to 3. This can be further modified if needed. + // encoding speed 1 to 3 and for vertical videos with a resolution between + // 608p and 720p. This can be further modified if needed. + const int is_low_complexity_decode_mode_supported = + (cfg->g_usage == AOM_USAGE_GOOD_QUALITY) && + (oxcf->speed >= 1 && oxcf->speed <= 3) && + (AOMMIN(cfg->g_w, cfg->g_h) >= 608 && AOMMIN(cfg->g_w, cfg->g_h) <= 1080); oxcf->enable_low_complexity_decode = extra_cfg->enable_low_complexity_decode && - cfg->g_usage == AOM_USAGE_GOOD_QUALITY && oxcf->speed >= 1 && - oxcf->speed <= 3; + is_low_complexity_decode_mode_supported; // Set Color related configuration. color_cfg->color_primaries = extra_cfg->color_primaries; @@ -1351,10 +1385,22 @@ color_cfg->chroma_sample_position = extra_cfg->chroma_sample_position; // Set Group of frames configuration. - // Force lag_in_frames to 0 for REALTIME mode +#if CONFIG_REALTIME_ONLY + // When CONFIG_REALTIME_ONLY=1 and mode=REALTIME, then force lag_in_frames + // = 0. gf_cfg->lag_in_frames = (oxcf->mode == REALTIME) ? 0 : clamp(cfg->g_lag_in_frames, 0, MAX_LAG_BUFFERS); +#else + gf_cfg->lag_in_frames = clamp(cfg->g_lag_in_frames, 0, MAX_LAG_BUFFERS); +#endif + + // Modify lag_in_frames slightly for better coding performance (e.g. better + // temporal filtering result). + if (oxcf->mode == GOOD && gf_cfg->lag_in_frames >= 32 && + gf_cfg->lag_in_frames < 39) + gf_cfg->lag_in_frames = AOMMIN(39, MAX_LAG_BUFFERS); + gf_cfg->enable_auto_arf = extra_cfg->enable_auto_alt_ref; gf_cfg->enable_auto_brf = extra_cfg->enable_auto_bwd_ref; gf_cfg->min_gf_interval = extra_cfg->min_gf_interval; @@ -1575,7 +1621,7 @@ static aom_codec_err_t encoder_set_config(aom_codec_alg_priv_t *ctx, const aom_codec_enc_cfg_t *cfg) { aom_codec_err_t res; - int force_key = 0; + volatile int force_key = 0; if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) { if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS) @@ -1627,11 +1673,16 @@ bool is_sb_size_changed = false; av1_change_config_seq(ctx->ppi, &ctx->oxcf, &is_sb_size_changed); for (int i = 0; i < ctx->ppi->num_fp_contexts; i++) { - av1_change_config(ctx->ppi->parallel_cpi[i], &ctx->oxcf, - is_sb_size_changed); + AV1_COMP *const cpi = ctx->ppi->parallel_cpi[i]; + ENABLE_SETJMP(cpi); + av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed); + DISABLE_SETJMP(cpi); } if (ctx->ppi->cpi_lap != NULL) { - av1_change_config(ctx->ppi->cpi_lap, &ctx->oxcf, is_sb_size_changed); + AV1_COMP *const cpi = ctx->ppi->cpi_lap; + ENABLE_SETJMP(cpi); + av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed); + DISABLE_SETJMP(cpi); } } @@ -1677,31 +1728,26 @@ } static aom_codec_err_t update_encoder_cfg(aom_codec_alg_priv_t *ctx) { + // Disable denoiser for spatial layers. Bug: 485332522. + // TODO: bug 485332522 - Disable denoiser for spatial layers until + // more testing is done + if (ctx->ppi->cpi->svc.number_spatial_layers > 1) + ctx->extra_cfg.noise_sensitivity = 0; set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg); av1_check_fpmt_config(ctx->ppi, &ctx->oxcf); bool is_sb_size_changed = false; av1_change_config_seq(ctx->ppi, &ctx->oxcf, &is_sb_size_changed); for (int i = 0; i < ctx->ppi->num_fp_contexts; i++) { AV1_COMP *const cpi = ctx->ppi->parallel_cpi[i]; - struct aom_internal_error_info *const error = cpi->common.error; - if (setjmp(error->jmp)) { - error->setjmp = 0; - return error->error_code; - } - error->setjmp = 1; + ENABLE_SETJMP(cpi); av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed); - error->setjmp = 0; + DISABLE_SETJMP(cpi); } if (ctx->ppi->cpi_lap != NULL) { AV1_COMP *const cpi_lap = ctx->ppi->cpi_lap; - struct aom_internal_error_info *const error = cpi_lap->common.error; - if (setjmp(error->jmp)) { - error->setjmp = 0; - return error->error_code; - } - error->setjmp = 1; + ENABLE_SETJMP(cpi_lap); av1_change_config(cpi_lap, &ctx->oxcf, is_sb_size_changed); - error->setjmp = 0; + DISABLE_SETJMP(cpi_lap); } return AOM_CODEC_OK; } @@ -1716,6 +1762,20 @@ return res; } +static aom_codec_err_t ctrl_get_gop_info(aom_codec_alg_priv_t *ctx, + va_list args) { + aom_gop_info_t *const gop_info = va_arg(args, aom_gop_info_t *); + if (gop_info == NULL) return AOM_CODEC_INVALID_PARAM; + const GF_GROUP *const gf_group = &ctx->ppi->gf_group; + gop_info->gop_size = gf_group->size; + for (int i = 0; i < gf_group->size; ++i) { + gop_info->coding_index[i] = i; + gop_info->display_index[i] = gf_group->display_idx[i]; + gop_info->layer_depth[i] = gf_group->layer_depth[i]; + } + return AOM_CODEC_OK; +} + static aom_codec_err_t ctrl_set_cpuused(aom_codec_alg_priv_t *ctx, va_list args) { struct av1_extracfg extra_cfg = ctx->extra_cfg; @@ -1838,11 +1898,9 @@ return update_extra_cfg(ctx, &extra_cfg); } -static aom_codec_err_t handle_tuning(aom_codec_alg_priv_t *ctx, - struct av1_extracfg *extra_cfg) { +static aom_codec_err_t handle_tuning(struct av1_extracfg *extra_cfg) { if (extra_cfg->tuning == AOM_TUNE_IQ || extra_cfg->tuning == AOM_TUNE_SSIMULACRA2) { - if (ctx->cfg.g_usage != AOM_USAGE_ALL_INTRA) return AOM_CODEC_INCAPABLE; // Enable QMs as they've been found to be beneficial for images, when used // with alternative QM formulas: // - aom_get_qmlevel_allintra() @@ -1851,8 +1909,8 @@ extra_cfg->enable_qm = 1; extra_cfg->qm_min = QM_FIRST_IQ_SSIMULACRA2; extra_cfg->qm_max = QM_LAST_IQ_SSIMULACRA2; - // We can turn on sharpness, as frames do not have to serve as references to - // others. + // Sharpness has been found to be beneficial for images (better perceptual + // quality). extra_cfg->sharpness = 7; // Using the QM-PSNR metric was found to be beneficial for images (over the // default PSNR metric), as it correlates better with subjective image @@ -1886,7 +1944,7 @@ va_list args) { struct av1_extracfg extra_cfg = ctx->extra_cfg; extra_cfg.tuning = CAST(AOME_SET_TUNING, args); - aom_codec_err_t err = handle_tuning(ctx, &extra_cfg); + aom_codec_err_t err = handle_tuning(&extra_cfg); if (err != AOM_CODEC_OK) return err; return update_extra_cfg(ctx, &extra_cfg); } @@ -2959,14 +3017,20 @@ priv->extra_cfg = default_extra_cfg; // Special handling: - // By default, if omitted: --enable-cdef=1, --qm-min=5, and --qm-max=9 - // Here we set its default values to 0, 4, and 10 respectively when - // --allintra is turned on. - // However, if users set --enable-cdef, --qm-min, or --qm-max, either from - // the command line or aom_codec_control(), the encoder still respects it. + // By default, if omitted: --enable-cdef=1, --screen-detection-mode=1, + // --qm-min=5, and --qm-max=9. + // Here we set its default values to --enable-cdef=0, + // --screen-detection-mode=2, --qm-min=4, and --qm-max=10 when --allintra + // is turned on. + // However, if users set --enable-cdef, --screen-detection-mode, --qm-min, + // or --qm-max, either from the command line or aom_codec_control(), the + // encoder still respects it. if (priv->cfg.g_usage == AOM_USAGE_ALL_INTRA) { // CDEF has been found to blur images, so it's disabled in all-intra mode priv->extra_cfg.enable_cdef = 0; + // Enable "anti-aliased text and graphics aware" screen detection mode. + priv->extra_cfg.screen_detection_mode = + AOM_SCREEN_DETECTION_ANTIALIASING_AWARE; // These QM min/max values have been found to be beneficial for images, // when used with an alternative QM formula (see // aom_get_qmlevel_allintra()). @@ -2989,11 +3053,18 @@ reduce_ratio(&priv->timestamp_ratio); set_encoder_config(&priv->oxcf, &priv->cfg, &priv->extra_cfg); - if (priv->oxcf.rc_cfg.mode != AOM_CBR && - priv->oxcf.pass == AOM_RC_ONE_PASS && priv->oxcf.mode == GOOD) { - // Enable look ahead - enabled for AOM_Q, AOM_CQ, AOM_VBR + if (priv->oxcf.pass == AOM_RC_ONE_PASS) { + // Enable look ahead. +#if CONFIG_REALTIME_ONLY + // When CONFIG_REALTIME_ONLY=1 and mode=REALTIME, then force + // lag_in_frames = 0. + const int lag_in_frames = + (priv->oxcf.mode == REALTIME) ? 0 : (int)priv->cfg.g_lag_in_frames; +#else + const int lag_in_frames = (int)priv->cfg.g_lag_in_frames; +#endif *num_lap_buffers = - AOMMIN((int)priv->cfg.g_lag_in_frames, + AOMMIN(lag_in_frames, AOMMIN(MAX_LAP_BUFFERS, priv->oxcf.kf_cfg.key_freq_max + SCENE_CUT_KEY_TEST_INTERVAL)); if ((int)priv->cfg.g_lag_in_frames - (*num_lap_buffers) >= @@ -3101,6 +3172,7 @@ if (ctx->ppi) { AV1_PRIMARY *ppi = ctx->ppi; + av1_extrc_delete(&ppi->cpi->ext_ratectrl); for (int i = 0; i < MAX_PARALLEL_FRAMES - 1; i++) { if (ppi->parallel_frames_data[i].cx_data) { free(ppi->parallel_frames_data[i].cx_data); @@ -3258,6 +3330,8 @@ if (ppi->use_svc && ppi->cpi->svc.use_flexible_mode == 0 && flags == 0) av1_set_svc_fixed_mode(ppi->cpi); + ppi->b_freeze_internal_state = flags & AOM_EFLAG_FREEZE_INTERNAL_STATE; + // Note(yunqing): While applying encoding flags, always start from enabling // all, and then modifying according to the flags. Previous frame's flags are // overwritten. @@ -3296,6 +3370,16 @@ "Cannot calculate per-frame PSNR when g_lag_in_frames is nonzero"); } + // Don't attempt to freeze internal state when lag is non-zero in order to + // minimize risk of state leaking when e.g. multi-pass and b-frames are + // used. + if ((flags & AOM_EFLAG_FREEZE_INTERNAL_STATE) && + ctx->cfg.g_lag_in_frames != 0) { + aom_internal_error( + &ppi->error, AOM_CODEC_INCAPABLE, + "Cannot freeze internal state when g_lag_in_frames is nonzero"); + } + // Set up internal flags #if CONFIG_INTERNAL_STATS assert(ppi->b_calculate_psnr == 1); @@ -3490,7 +3574,10 @@ #endif // CONFIG_MULTITHREAD // Call for LAP stage - if (cpi_lap != NULL) { + if (cpi_lap != NULL && !is_one_pass_rt_lag_params(cpi)) { + if (cpi_lap->ppi->b_freeze_internal_state) { + av1_save_all_coding_context(cpi_lap); + } AV1_COMP_DATA cpi_lap_data = { 0 }; cpi_lap_data.flush = !img; cpi_lap_data.timestamp_ratio = &ctx->timestamp_ratio; @@ -3499,6 +3586,9 @@ aom_internal_error_copy(&ppi->error, cpi_lap->common.error); } av1_post_encode_updates(cpi_lap, &cpi_lap_data); + if (cpi_lap->ppi->b_freeze_internal_state) { + restore_all_coding_context(cpi_lap); + } } // Recalculate the maximum number of frames that can be encoded in @@ -3517,6 +3607,9 @@ cpi->ref_idx_to_skip = INVALID_IDX; cpi->ref_refresh_index = INVALID_IDX; cpi->refresh_idx_available = false; + if (cpi->ppi->b_freeze_internal_state) { + av1_save_all_coding_context(cpi); + } #if CONFIG_FPMT_TEST simulate_parallel_frame = @@ -3533,6 +3626,15 @@ #endif // CONFIG_FPMT_TEST if (!simulate_parallel_frame) { + // Add a range check here to give an error for frame_parallel_level + // array out-of-bounds access. + if (cpi->gf_frame_index >= MAX_STATIC_GF_GROUP_LENGTH) { + aom_internal_error(&ppi->error, AOM_CODEC_ERROR, + "cpi->gf_frame_index is out of range"); + } + + // May need a better way for checking the frame's frame_parallel_level, + // especially for the first frame of the following gop. if (ppi->gf_group.frame_parallel_level[cpi->gf_frame_index] == 0) { status = av1_get_compressed_data(cpi, &cpi_data); } else if (ppi->gf_group.frame_parallel_level[cpi->gf_frame_index] == @@ -3559,6 +3661,10 @@ ppi->seq_params_locked = 1; av1_post_encode_updates(cpi, &cpi_data); + if (cpi->ppi->b_freeze_internal_state) { + restore_all_coding_context(cpi); + } + #if CONFIG_ENTROPY_STATS if (ppi->cpi->oxcf.pass != 1 && !cpi->common.show_existing_frame) av1_accumulate_frame_counts(&ppi->aggregate_fc, &cpi->counts); @@ -3935,7 +4041,7 @@ AV1_PRIMARY *const ppi = ctx->ppi; AV1_COMP *const cpi = ppi->cpi; aom_svc_params_t *const params = va_arg(args, aom_svc_params_t *); - int64_t target_bandwidth = 0; + volatile int64_t target_bandwidth = 0; // Note svc.use_flexible_mode is set by AV1E_SET_SVC_REF_FRAME_CONFIG. When // it is false (the default) the actual limit is 3 for both spatial and // temporal layers. Given the order of these calls are unpredictable the @@ -3980,7 +4086,8 @@ if (ppi->number_spatial_layers > 1 || ppi->number_temporal_layers > 1) { unsigned int sl, tl; - ctx->ppi->use_svc = 1; + // Disable svc for lag_in_frames > 0. + if (cpi->oxcf.gf_cfg.lag_in_frames > 0) return AOM_CODEC_INVALID_PARAM; const int num_layers = ppi->number_spatial_layers * ppi->number_temporal_layers; for (int layer = 0; layer < num_layers; ++layer) { @@ -3991,6 +4098,7 @@ } } if (!av1_alloc_layer_context(cpi, num_layers)) return AOM_CODEC_MEM_ERROR; + ppi->use_svc = 1; for (sl = 0; sl < ppi->number_spatial_layers; ++sl) { for (tl = 0; tl < ppi->number_temporal_layers; ++tl) { @@ -4019,7 +4127,9 @@ ctx->oxcf.rc_cfg.target_bandwidth = oxcf->rc_cfg.target_bandwidth = target_bandwidth; set_primary_rc_buffer_sizes(oxcf, ppi); + ENABLE_SETJMP(cpi); av1_update_layer_context_change_config(cpi, target_bandwidth); + DISABLE_SETJMP(cpi); check_reset_rc_flag(cpi); } else { // Note av1_init_layer_context() relies on cpi->oxcf. The order of that @@ -4033,7 +4143,9 @@ seq_params->operating_points_cnt_minus_1 = ppi->number_spatial_layers * ppi->number_temporal_layers - 1; + ENABLE_SETJMP(cpi); av1_init_layer_context(cpi); + DISABLE_SETJMP(cpi); // update_encoder_cfg() is somewhat costly and this control may be called // multiple times, so update_encoder_cfg() is only called to ensure frame // and superblock sizes are updated before they're fixed by the first @@ -4164,6 +4276,55 @@ return update_extra_cfg(ctx, &extra_cfg); } +static aom_codec_err_t ctrl_set_external_rate_control(aom_codec_alg_priv_t *ctx, + va_list args) { + aom_rc_funcs_t funcs = *CAST(AV1E_SET_EXTERNAL_RATE_CONTROL, args); + AV1_COMP *cpi = ctx->ppi->cpi; + AOM_EXT_RATECTRL *ext_ratectrl = &cpi->ext_ratectrl; + const AV1EncoderConfig *oxcf = &cpi->oxcf; + if (oxcf->pass == AOM_RC_SECOND_PASS) { + const FRAME_INFO *frame_info = &cpi->frame_info; + aom_rc_config_t ratectrl_config; + aom_codec_err_t codec_status; + memset(&ratectrl_config, 0, sizeof(ratectrl_config)); + + ratectrl_config.frame_width = frame_info->frame_width; + ratectrl_config.frame_height = frame_info->frame_height; + ratectrl_config.show_frame_count = + cpi->ppi->twopass.firstpass_info.stats_count; + ratectrl_config.max_gf_interval = ctx->extra_cfg.max_gf_interval; + ratectrl_config.min_gf_interval = ctx->extra_cfg.min_gf_interval; + ratectrl_config.target_bitrate_kbps = + (int)(oxcf->rc_cfg.target_bandwidth / 1000); + ratectrl_config.frame_rate_num = ctx->cfg.g_timebase.den; + ratectrl_config.frame_rate_den = ctx->cfg.g_timebase.num; + ratectrl_config.overshoot_percent = oxcf->rc_cfg.over_shoot_pct; + ratectrl_config.undershoot_percent = oxcf->rc_cfg.under_shoot_pct; + ratectrl_config.min_base_q_index = oxcf->rc_cfg.best_allowed_q; + ratectrl_config.max_base_q_index = oxcf->rc_cfg.worst_allowed_q; + ratectrl_config.base_qp = ctx->extra_cfg.cq_level; + + const BLOCK_SIZE sb_size = av1_select_sb_size( + oxcf, frame_info->frame_width, frame_info->frame_height, + cpi->ppi->number_spatial_layers); + ratectrl_config.superblock_size = (sb_size == BLOCK_128X128) ? 128 : 64; + + if (ctx->cfg.rc_end_usage == AOM_VBR) { + ratectrl_config.rc_mode = AOM_RC_VBR; + } else if (ctx->cfg.rc_end_usage == AOM_Q) { + ratectrl_config.rc_mode = AOM_RC_QMODE; + } else if (ctx->cfg.rc_end_usage == AOM_CQ) { + ratectrl_config.rc_mode = AOM_RC_CQ; + } + + codec_status = av1_extrc_create(funcs, ratectrl_config, ext_ratectrl); + if (codec_status != AOM_CODEC_OK) { + return codec_status; + } + } + return AOM_CODEC_OK; +} + static aom_codec_err_t encoder_set_option(aom_codec_alg_priv_t *ctx, const char *name, const char *value) { if (ctx == NULL || name == NULL || value == NULL) @@ -4174,16 +4335,9 @@ size_t len = strlen(name) + strlen(value) + 4; char *const err_string = ctx->ppi->error.detail; -#if __STDC_VERSION__ >= 201112L - // We use the keyword _Static_assert because clang-cl does not allow the - // convenience macro static_assert to be used in function scope. See - // https://bugs.llvm.org/show_bug.cgi?id=48904. - _Static_assert(sizeof(ctx->ppi->error.detail) >= ARG_ERR_MSG_MAX_LEN, - "The size of the err_msg buffer for arg_match_helper must be " - "at least ARG_ERR_MSG_MAX_LEN"); -#else - assert(sizeof(ctx->ppi->error.detail) >= ARG_ERR_MSG_MAX_LEN); -#endif + static_assert(sizeof(ctx->ppi->error.detail) >= ARG_ERR_MSG_MAX_LEN, + "The size of the err_msg buffer for arg_match_helper must be " + "at least ARG_ERR_MSG_MAX_LEN"); argv[0] = aom_malloc(len * sizeof(argv[1][0])); if (!argv[0]) return AOM_CODEC_MEM_ERROR; @@ -4265,7 +4419,7 @@ } else if (arg_match_helper(&arg, &g_av1_codec_arg_defs.tune_metric, argv, err_string)) { extra_cfg.tuning = arg_parse_enum_helper(&arg, err_string); - err = handle_tuning(ctx, &extra_cfg); + err = handle_tuning(&extra_cfg); } #if CONFIG_TUNE_VMAF else if (arg_match_helper(&arg, &g_av1_codec_arg_defs.vmaf_model_path, argv, @@ -4865,6 +5019,7 @@ { AV1E_SET_SCREEN_CONTENT_DETECTION_MODE, ctrl_set_screen_content_detection_mode }, { AV1E_SET_ENABLE_ADAPTIVE_SHARPNESS, ctrl_set_enable_adaptive_sharpness }, + { AV1E_SET_EXTERNAL_RATE_CONTROL, ctrl_set_external_rate_control }, // Getters { AOME_GET_LAST_QUANTIZER, ctrl_get_quantizer }, @@ -4883,6 +5038,7 @@ { AV1E_GET_LUMA_CDEF_STRENGTH, ctrl_get_luma_cdef_strength }, { AV1E_GET_HIGH_MOTION_CONTENT_SCREEN_RTC, ctrl_get_high_motion_content_screen_rtc }, + { AV1E_GET_GOP_INFO, ctrl_get_gop_info }, CTRL_MAP_END, };
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c index 3ff284d..50baa48 100644 --- a/av1/av1_dx_iface.c +++ b/av1/av1_dx_iface.c
@@ -62,6 +62,7 @@ unsigned int is_annexb; int operating_point; int output_all_layers; + unsigned int frame_size_limit; AVxWorker *frame_worker; @@ -112,6 +113,8 @@ priv->tile_mode = 0; priv->decode_tile_row = -1; priv->decode_tile_col = -1; + + priv->frame_size_limit = 0; } return AOM_CODEC_OK; @@ -247,13 +250,9 @@ } } - if (aom_get_num_layers_from_operating_point_idc( - operating_point_idc0, &si->number_spatial_layers, - &si->number_temporal_layers) != AOM_CODEC_OK) { - return AOM_CODEC_ERROR; - } - - return AOM_CODEC_OK; + return aom_get_num_layers_from_operating_point_idc( + operating_point_idc0, &si->number_spatial_layers, + &si->number_temporal_layers); } static aom_codec_err_t decoder_peek_si_internal(const uint8_t *data, @@ -495,6 +494,7 @@ frame_worker_data->pbi->dec_tile_col = ctx->decode_tile_col; frame_worker_data->pbi->operating_point = ctx->operating_point; frame_worker_data->pbi->output_all_layers = ctx->output_all_layers; + frame_worker_data->pbi->frame_size_limit = ctx->frame_size_limit; frame_worker_data->pbi->ext_tile_debug = ctx->ext_tile_debug; frame_worker_data->pbi->row_mt = ctx->row_mt; frame_worker_data->pbi->is_fwd_kf_present = 0; @@ -952,8 +952,18 @@ AVxWorker *const worker = ctx->frame_worker; FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1; image2yuvconfig(&frame->img, &sd); - return av1_set_reference_dec(&frame_worker_data->pbi->common, frame->idx, - frame->use_external_ref, &sd); + + struct aom_internal_error_info *const error = + frame_worker_data->pbi->common.error; + if (setjmp(error->jmp)) { + error->setjmp = 0; + return error->error_code; + } + error->setjmp = 1; + av1_set_reference_dec(&frame_worker_data->pbi->common, frame->idx, + frame->use_external_ref, &sd); + error->setjmp = 0; + return AOM_CODEC_OK; } else { return AOM_CODEC_INVALID_PARAM; } @@ -1578,6 +1588,12 @@ return AOM_CODEC_OK; } +static aom_codec_err_t ctrl_set_frame_size_limit(aom_codec_alg_priv_t *ctx, + va_list args) { + ctx->frame_size_limit = va_arg(args, unsigned int); + return AOM_CODEC_OK; +} + static aom_codec_err_t ctrl_get_accounting(aom_codec_alg_priv_t *ctx, va_list args) { #if !CONFIG_ACCOUNTING @@ -1685,6 +1701,7 @@ { AV1D_SET_ROW_MT, ctrl_set_row_mt }, { AV1D_SET_EXT_REF_PTR, ctrl_set_ext_ref_ptr }, { AV1D_SET_SKIP_FILM_GRAIN, ctrl_set_skip_film_grain }, + { AOMD_SET_FRAME_SIZE_LIMIT, ctrl_set_frame_size_limit }, // Getters { AOMD_GET_FRAME_CORRUPTED, ctrl_get_frame_corrupted },
diff --git a/av1/common/arm/av1_convolve_scale_neon_dotprod.c b/av1/common/arm/av1_convolve_scale_neon_dotprod.c index 1551d3a..340987c 100644 --- a/av1/common/arm/av1_convolve_scale_neon_dotprod.c +++ b/av1/common/arm/av1_convolve_scale_neon_dotprod.c
@@ -28,7 +28,7 @@ #include "av1/common/filter.h" // clang-format off -DECLARE_ALIGNED(16, static const uint8_t, kScale2DotProdPermuteTbl[32]) = { +DECLARE_ALIGNED(16, const uint8_t, kScale2DotProdPermuteTbl[32]) = { 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9, 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 10, 11, 12, 13 };
diff --git a/av1/common/arm/av1_convolve_scale_neon_dotprod.h b/av1/common/arm/av1_convolve_scale_neon_dotprod.h new file mode 100644 index 0000000..6188124 --- /dev/null +++ b/av1/common/arm/av1_convolve_scale_neon_dotprod.h
@@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025, 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. + */ +#ifndef AOM_AV1_COMMON_ARM_AV1_CONVOLVE_SCALE_NEON_DOTPROD_H_ +#define AOM_AV1_COMMON_ARM_AV1_CONVOLVE_SCALE_NEON_DOTPROD_H_ + +#include "aom_ports/mem.h" + +DECLARE_ALIGNED(16, extern const uint8_t, kScale2DotProdPermuteTbl[32]); + +#endif // AOM_AV1_COMMON_ARM_AV1_CONVOLVE_SCALE_NEON_DOTPROD_H_
diff --git a/av1/common/arm/av1_convolve_scale_neon_i8mm.c b/av1/common/arm/av1_convolve_scale_neon_i8mm.c index eb5092b..8ae218bf 100644 --- a/av1/common/arm/av1_convolve_scale_neon_i8mm.c +++ b/av1/common/arm/av1_convolve_scale_neon_i8mm.c
@@ -23,17 +23,11 @@ #include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" #include "av1/common/arm/convolve_scale_neon.h" +#include "av1/common/arm/av1_convolve_scale_neon_dotprod.h" #include "av1/common/convolve.h" #include "av1/common/enums.h" #include "av1/common/filter.h" -// clang-format off -DECLARE_ALIGNED(16, static const uint8_t, kScale2DotProdPermuteTbl[32]) = { - 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9, - 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 10, 11, 12, 13 -}; -// clang-format on - static inline int16x4_t convolve8_4_h(const uint8x8_t s0, const uint8x8_t s1, const uint8x8_t s2, const uint8x8_t s3, const int8x8_t filter,
diff --git a/av1/common/arm/compound_convolve_neon.h b/av1/common/arm/compound_convolve_neon.h index e9837f0..66d0d58 100644 --- a/av1/common/arm/compound_convolve_neon.h +++ b/av1/common/arm/compound_convolve_neon.h
@@ -124,6 +124,46 @@ *d23_u8 = vqrshrun_n_s16(dst_23, FILTER_BITS - ROUND0_BITS); } +static inline void compute_dist_wtd_avg_8x2( + uint16x8_t dd0, uint16x8_t dd1, uint16x8_t d0, uint16x8_t d1, + const uint16_t fwd_offset, const uint16_t bck_offset, + const int16x8_t round_offset, uint8x8_t *d0_u8, uint8x8_t *d1_u8) { + uint32x4_t blend0_lo = vmull_n_u16(vget_low_u16(dd0), fwd_offset); + blend0_lo = vmlal_n_u16(blend0_lo, vget_low_u16(d0), bck_offset); + uint32x4_t blend0_hi = vmull_n_u16(vget_high_u16(dd0), fwd_offset); + blend0_hi = vmlal_n_u16(blend0_hi, vget_high_u16(d0), bck_offset); + + uint32x4_t blend1_lo = vmull_n_u16(vget_low_u16(dd1), fwd_offset); + blend1_lo = vmlal_n_u16(blend1_lo, vget_low_u16(d1), bck_offset); + uint32x4_t blend1_hi = vmull_n_u16(vget_high_u16(dd1), fwd_offset); + blend1_hi = vmlal_n_u16(blend1_hi, vget_high_u16(d1), bck_offset); + + uint16x8_t avg0 = vcombine_u16(vshrn_n_u32(blend0_lo, DIST_PRECISION_BITS), + vshrn_n_u32(blend0_hi, DIST_PRECISION_BITS)); + uint16x8_t avg1 = vcombine_u16(vshrn_n_u32(blend1_lo, DIST_PRECISION_BITS), + vshrn_n_u32(blend1_hi, DIST_PRECISION_BITS)); + + int16x8_t dst0 = vsubq_s16(vreinterpretq_s16_u16(avg0), round_offset); + int16x8_t dst1 = vsubq_s16(vreinterpretq_s16_u16(avg1), round_offset); + + *d0_u8 = vqrshrun_n_s16(dst0, FILTER_BITS - ROUND0_BITS); + *d1_u8 = vqrshrun_n_s16(dst1, FILTER_BITS - ROUND0_BITS); +} + +static inline void compute_basic_avg_8x2(uint16x8_t dd01, uint16x8_t dd23, + uint16x8_t d01, uint16x8_t d23, + const int16x8_t round_offset, + uint8x8_t *d0_u8, uint8x8_t *d1_u8) { + uint16x8_t avg0 = vhaddq_u16(dd01, d01); + uint16x8_t avg1 = vhaddq_u16(dd23, d23); + + int16x8_t dst0 = vsubq_s16(vreinterpretq_s16_u16(avg0), round_offset); + int16x8_t dst1 = vsubq_s16(vreinterpretq_s16_u16(avg1), round_offset); + + *d0_u8 = vqrshrun_n_s16(dst0, FILTER_BITS - ROUND0_BITS); + *d1_u8 = vqrshrun_n_s16(dst1, FILTER_BITS - ROUND0_BITS); +} + static inline void compute_dist_wtd_avg_8x4( uint16x8_t dd0, uint16x8_t dd1, uint16x8_t dd2, uint16x8_t dd3, uint16x8_t d0, uint16x8_t d1, uint16x8_t d2, uint16x8_t d3,
diff --git a/av1/common/arm/compound_convolve_neon_dotprod.c b/av1/common/arm/compound_convolve_neon_dotprod.c index dcf8da1..f4c55de 100644 --- a/av1/common/arm/compound_convolve_neon_dotprod.c +++ b/av1/common/arm/compound_convolve_neon_dotprod.c
@@ -13,16 +13,12 @@ #include <assert.h> #include "aom_dsp/arm/mem_neon.h" +#include "aom_dsp/arm/transpose_neon.h" #include "av1/common/arm/compound_convolve_neon.h" +#include "av1/common/arm/convolve_neon_dotprod.h" #include "config/aom_config.h" #include "config/av1_rtcd.h" -DECLARE_ALIGNED(16, static const uint8_t, dot_prod_permute_tbl[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 -}; - static inline int16x4_t convolve4_4_2d_h(uint8x16_t samples, const int8x8_t x_filter, const int32x4_t correction, @@ -97,7 +93,7 @@ int height = im_h; if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(dot_prod_permute_tbl); + const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl); // 4-tap filters are used for blocks having width <= 4. // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = @@ -137,7 +133,7 @@ dst_ptr += dst_stride; } while (--height != 0); } else { - const uint8x16x3_t permute_tbl = vld1q_u8_x3(dot_prod_permute_tbl); + const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = vshrn_n_s16(x_filter_s16, 1); @@ -349,7 +345,7 @@ int height = h; if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(dot_prod_permute_tbl); + const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl); // 4-tap filters are used for blocks having width <= 4. // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = @@ -386,7 +382,7 @@ height -= 4; } while (height != 0); } else { - const uint8x16x3_t permute_tbl = vld1q_u8_x3(dot_prod_permute_tbl); + const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = vshrn_n_s16(x_filter_s16, 1); @@ -469,7 +465,7 @@ int height = h; if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(dot_prod_permute_tbl); + const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl); // 4-tap filters are used for blocks having width <= 4. // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = @@ -506,7 +502,7 @@ height -= 4; } while (height != 0); } else { - const uint8x16x3_t permute_tbl = vld1q_u8_x3(dot_prod_permute_tbl); + const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = vshrn_n_s16(x_filter_s16, 1); @@ -586,7 +582,7 @@ int height = h; if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(dot_prod_permute_tbl); + const uint8x16_t permute_tbl = vld1q_u8(kDotProdPermuteTbl); // 4-tap filters are used for blocks having width <= 4. // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = @@ -614,7 +610,7 @@ height -= 4; } while (height != 0); } else { - const uint8x16x3_t permute_tbl = vld1q_u8_x3(dot_prod_permute_tbl); + const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); // Filter values are even, so halve to reduce intermediate precision reqs. const int8x8_t x_filter = vshrn_n_s16(x_filter_s16, 1); @@ -668,3 +664,1151 @@ subpel_x_qn, conv_params); } } + +static inline int16x4_t convolve8_4_y(const int8x16_t s0, const int8x16_t s1, + const int8x8_t filters) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); + int32x4_t sum = vdotq_lane_s32(acc, s0, filters, 0); + sum = vdotq_lane_s32(sum, s1, filters, 1); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint16x8_t convolve8_8_y(const int8x16_t s0_lo, + const int8x16_t s0_hi, + const int8x16_t s1_lo, + const int8x16_t s1_hi, + const int8x8_t filters, + const int16x8_t round_offset) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); + + int32x4_t sum0123 = vdotq_lane_s32(acc, s0_lo, filters, 0); + sum0123 = vdotq_lane_s32(sum0123, s1_lo, filters, 1); + + int32x4_t sum4567 = vdotq_lane_s32(acc, s0_hi, filters, 0); + sum4567 = vdotq_lane_s32(sum4567, s1_hi, filters, 1); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + + // We halved the filter values so -1 from right shift. + return vreinterpretq_u16_s16( + vrsraq_n_s16(round_offset, sum, ROUND0_BITS - 1)); +} + +static inline void dist_wtd_convolve_y_8tap_neon_dotprod( + const uint8_t *src_ptr, int src_stride, int w, int h, + const int16_t *y_filter_ptr, ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + if (w == 4) { + uint8x8_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(src_ptr, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6); + src_ptr += 7 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + + int8x16_t s0123, s1234, s2345, s3456; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_s8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_s8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_s8_4x4(s3, s4, s5, s6, &s3456); + + do { + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(src_ptr, src_stride, &t7, &t8, &t9, &tA); + + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + int8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_s8_4x4(s7, s8, s9, sA, &s789A); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT = { { s3456, s789A } }; + s4567 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + s5678 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + s6789 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); + int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); + int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + + // We halved the filter values so -1 from right shift. + int16x8_t d01 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1); + int16x8_t d23 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1); + + store_u16x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d01)); + store_u16x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d23)); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + s1234 = s5678; + s2345 = s6789; + s3456 = s789A; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + + uint8x8_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6); + s += 7 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample + // permute (see horizontal case) required before computing the dot + // product. + int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_s8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_s8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_s8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + + do { + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(s, src_stride, &t7, &t8, &t9, &tA); + + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, + s789A_lo, s789A_hi; + transpose_concat_elems_s8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; + s4567_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[0]); + s5678_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[1]); + s6789_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[2]); + + int8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; + s4567_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[0]); + s5678_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[1]); + s6789_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint16x8_t d0 = convolve8_8_y(s0123_lo, s0123_hi, s4567_lo, s4567_hi, + filter, round_offset_vec); + uint16x8_t d1 = convolve8_8_y(s1234_lo, s1234_hi, s5678_lo, s5678_hi, + filter, round_offset_vec); + uint16x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, + filter, round_offset_vec); + uint16x8_t d3 = convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, + filter, round_offset_vec); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_8tap_dist_wtd_avg_neon_dotprod( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + if (w == 4) { + uint8x8_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(src_ptr, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6); + src_ptr += 7 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + + int8x16_t s0123, s1234, s2345, s3456; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_s8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_s8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_s8_4x4(s3, s4, s5, s6, &s3456); + + do { + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(src_ptr, src_stride, &t7, &t8, &t9, &tA); + + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + int8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_s8_4x4(s7, s8, s9, sA, &s789A); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT = { { s3456, s789A } }; + s4567 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + s5678 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + s6789 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); + int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); + int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_dist_wtd_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), + d01, d23, fwd_offset, bck_offset, + round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + s1234 = s5678; + s2345 = s6789; + s3456 = s789A; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6); + s += 7 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample + // permute (see horizontal case) required before computing the dot + // product. + int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_s8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_s8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_s8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + + do { + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(s, src_stride, &t7, &t8, &t9, &tA); + + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, + s789A_lo, s789A_hi; + transpose_concat_elems_s8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; + s4567_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[0]); + s5678_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[1]); + s6789_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[2]); + + int8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; + s4567_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[0]); + s5678_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[1]); + s6789_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint16x8_t d0 = convolve8_8_y(s0123_lo, s0123_hi, s4567_lo, s4567_hi, + filter, round_offset_vec); + uint16x8_t d1 = convolve8_8_y(s1234_lo, s1234_hi, s5678_lo, s5678_hi, + filter, round_offset_vec); + uint16x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, + filter, round_offset_vec); + uint16x8_t d3 = convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, + filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, fwd_offset, + bck_offset, round_offset_vec, &d0_u8, &d1_u8, + &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_8tap_avg_neon_dotprod( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + if (w == 4) { + uint8x8_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(src_ptr, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6); + src_ptr += 7 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + + int8x16_t s0123, s1234, s2345, s3456; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_s8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_s8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_s8_4x4(s3, s4, s5, s6, &s3456); + + do { + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(src_ptr, src_stride, &t7, &t8, &t9, &tA); + + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + int8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_s8_4x4(s7, s8, s9, sA, &s789A); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT = { { s3456, s789A } }; + s4567 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + s5678 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + s6789 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); + int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); + int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_basic_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), d01, + d23, round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + s1234 = s5678; + s2345 = s6789; + s3456 = s789A; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6); + s += 7 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample + // permute (see horizontal case) required before computing the dot + // product. + int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_s8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_s8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_s8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + + do { + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(s, src_stride, &t7, &t8, &t9, &tA); + + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, + s789A_lo, s789A_hi; + transpose_concat_elems_s8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; + s4567_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[0]); + s5678_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[1]); + s6789_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[2]); + + int8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; + s4567_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[0]); + s5678_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[1]); + s6789_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint16x8_t d0 = convolve8_8_y(s0123_lo, s0123_hi, s4567_lo, s4567_hi, + filter, round_offset_vec); + uint16x8_t d1 = convolve8_8_y(s1234_lo, s1234_hi, s5678_lo, s5678_hi, + filter, round_offset_vec); + uint16x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, + filter, round_offset_vec); + uint16x8_t d3 = convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, + filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset_vec, &d0_u8, &d1_u8, &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline int16x4_t convolve4_4_y(const int8x16_t s0, + const int8x8_t filters) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); + + int32x4_t sum = vdotq_lane_s32(acc, s0, filters, 0); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint16x8_t convolve4_8_y(const int8x16_t s0, const int8x16_t s1, + const int8x8_t filters, + const int16x8_t round_offset) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); + + int32x4_t sum0123 = vdotq_lane_s32(acc, s0, filters, 0); + int32x4_t sum4567 = vdotq_lane_s32(acc, s1, filters, 0); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + + // We halved the filter values so -1 from right shift. + return vreinterpretq_u16_s16( + vrsraq_n_s16(round_offset, sum, ROUND0_BITS - 1)); +} + +static inline void dist_wtd_convolve_y_4tap_neon_dotprod( + const uint8_t *src_ptr, int src_stride, int w, int h, + const int16_t *y_filter_ptr, ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + int8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(src_ptr, src_stride, &t0, &t1, &t2, &t3); + src_ptr += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(src_ptr, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567; + transpose_concat_elems_s8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + int8x16_t s1234 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + + // We halved the filter values so -1 from right shift. + int16x8_t d01 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1); + int16x8_t d23 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1); + + store_u16x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d01)); + store_u16x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d23)); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(s, src_stride, &t0, &t1, &t2, &t3); + s += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(s, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_s8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + int8x16_t s1234_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + int8x16_t s1234_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + uint16x8_t d0 = + convolve4_8_y(s0123_lo, s0123_hi, filter, round_offset_vec); + uint16x8_t d1 = + convolve4_8_y(s1234_lo, s1234_hi, filter, round_offset_vec); + uint16x8_t d2 = + convolve4_8_y(s2345_lo, s2345_hi, filter, round_offset_vec); + uint16x8_t d3 = + convolve4_8_y(s3456_lo, s3456_hi, filter, round_offset_vec); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_4tap_avg_neon_dotprod( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + int8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(src_ptr, src_stride, &t0, &t1, &t2, &t3); + src_ptr += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(src_ptr, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567; + transpose_concat_elems_s8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + int8x16_t s1234 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_basic_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), d01, + d23, round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(s, src_stride, &t0, &t1, &t2, &t3); + s += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(s, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_s8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + int8x16_t s1234_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + int8x16_t s1234_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + uint16x8_t d0 = + convolve4_8_y(s0123_lo, s0123_hi, filter, round_offset_vec); + uint16x8_t d1 = + convolve4_8_y(s1234_lo, s1234_hi, filter, round_offset_vec); + uint16x8_t d2 = + convolve4_8_y(s2345_lo, s2345_hi, filter, round_offset_vec); + uint16x8_t d3 = + convolve4_8_y(s3456_lo, s3456_hi, filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset_vec, &d0_u8, &d1_u8, &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_4tap_dist_wtd_avg_neon_dotprod( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + int8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(src_ptr, src_stride, &t0, &t1, &t2, &t3); + src_ptr += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(src_ptr, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567; + transpose_concat_elems_s8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + int8x16_t s1234 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_dist_wtd_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), + d01, d23, fwd_offset, bck_offset, + round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(s, src_stride, &t0, &t1, &t2, &t3); + s += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(s, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_s8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + int8x16_t s1234_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + int8x16_t s1234_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + uint16x8_t d0 = + convolve4_8_y(s0123_lo, s0123_hi, filter, round_offset_vec); + uint16x8_t d1 = + convolve4_8_y(s1234_lo, s1234_hi, filter, round_offset_vec); + uint16x8_t d2 = + convolve4_8_y(s2345_lo, s2345_hi, filter, round_offset_vec); + uint16x8_t d3 = + convolve4_8_y(s3456_lo, s3456_hi, filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, fwd_offset, + bck_offset, round_offset_vec, &d0_u8, &d1_u8, + &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +void av1_dist_wtd_convolve_y_neon_dotprod( + const uint8_t *src, int src_stride, uint8_t *dst8, int dst8_stride, int w, + int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn, + ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_y, subpel_y_qn & SUBPEL_MASK); + + if (get_filter_tap(filter_params_y, subpel_y_qn) <= 4) { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_y_4tap_dist_wtd_avg_neon_dotprod( + src - src_stride, src_stride, dst8, dst8_stride, w, h, y_filter_ptr, + conv_params); + } else { + dist_wtd_convolve_y_4tap_avg_neon_dotprod(src - src_stride, src_stride, + dst8, dst8_stride, w, h, + y_filter_ptr, conv_params); + } + } else { + dist_wtd_convolve_y_4tap_neon_dotprod(src - src_stride, src_stride, w, h, + y_filter_ptr, conv_params); + } + } else { // filter tap >= 6 + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_y_8tap_dist_wtd_avg_neon_dotprod( + src - 3 * src_stride, src_stride, dst8, dst8_stride, w, h, + y_filter_ptr, conv_params); + } else { + dist_wtd_convolve_y_8tap_avg_neon_dotprod( + src - 3 * src_stride, src_stride, dst8, dst8_stride, w, h, + y_filter_ptr, conv_params); + } + } else { + dist_wtd_convolve_y_8tap_neon_dotprod(src - 3 * src_stride, src_stride, w, + h, y_filter_ptr, conv_params); + } + } +}
diff --git a/av1/common/arm/compound_convolve_neon_i8mm.c b/av1/common/arm/compound_convolve_neon_i8mm.c index 0589dfb..aa4438a 100644 --- a/av1/common/arm/compound_convolve_neon_i8mm.c +++ b/av1/common/arm/compound_convolve_neon_i8mm.c
@@ -13,22 +13,12 @@ #include <assert.h> #include "aom_dsp/arm/mem_neon.h" +#include "aom_dsp/arm/transpose_neon.h" #include "av1/common/arm/compound_convolve_neon.h" #include "config/aom_config.h" #include "config/av1_rtcd.h" - -DECLARE_ALIGNED(16, static const uint8_t, kDotProdPermuteTbl[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 -}; - -DECLARE_ALIGNED(16, static const uint8_t, kMatMulPermuteTbl[32]) = { - // clang-format off - 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9, - 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 - // clang-format on -}; +#include "convolve_neon_dotprod.h" +#include "convolve_neon_i8mm.h" static inline int16x4_t convolve6_4_2d_h(uint8x16_t samples, const int8x16_t x_filter, @@ -90,7 +80,7 @@ int height = im_h; if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src_ptr, src_stride, &s0, &s1, &s2, &s3); @@ -118,7 +108,7 @@ dst_ptr += dst_stride; } while (--height != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src_ptr; int16_t *d = dst_ptr; @@ -167,31 +157,29 @@ } static inline int16x8_t convolve8_8_2d_h(uint8x16_t samples, - const int8x8_t x_filter, - const uint8x16x3_t permute_tbl, - const int32x4_t horiz_const) { - uint8x16_t permuted_samples[3]; - int32x4_t sum[2]; + const int8x16_t x_filter, + const uint8x8_t f0, + const uint8x16x2_t permute_tbl, + const uint16x8_t horiz_const) { + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(samples, permute_tbl.val[0]), + vqtbl1q_u8(samples, permute_tbl.val[1]) }; - // Permute samples ready for dot product. - // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } - permuted_samples[0] = vqtbl1q_u8(samples, permute_tbl.val[0]); - // { 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 } - permuted_samples[1] = vqtbl1q_u8(samples, permute_tbl.val[1]); - // { 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 } - permuted_samples[2] = vqtbl1q_u8(samples, permute_tbl.val[2]); + // Calculate partial 7-tap convolution. + int32x4_t sum0123 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[0], x_filter); + int32x4_t sum4567 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[1], x_filter); + uint16x8_t sum = vreinterpretq_u16_s16( + vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567))); - // First 4 output values. - sum[0] = vusdotq_lane_s32(horiz_const, permuted_samples[0], x_filter, 0); - sum[0] = vusdotq_lane_s32(sum[0], permuted_samples[1], x_filter, 1); - // Second 4 output values. - sum[1] = vusdotq_lane_s32(horiz_const, permuted_samples[1], x_filter, 0); - sum[1] = vusdotq_lane_s32(sum[1], permuted_samples[2], x_filter, 1); + // Apply tap 0 and accumulate. + sum = vmlsl_u8(sum, vget_low_u8(samples), f0); - // Narrow and re-pack. + sum = vaddq_u16(sum, horiz_const); + // We halved the convolution filter values so -1 from the right shift. - return vcombine_s16(vshrn_n_s32(sum[0], ROUND0_BITS - 1), - vshrn_n_s32(sum[1], ROUND0_BITS - 1)); + return vreinterpretq_s16_u16(vshrq_n_u16(sum, ROUND0_BITS - 1)); } static inline void dist_wtd_convolve_2d_horiz_8tap_neon_i8mm( @@ -201,12 +189,23 @@ // A shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. // (The extra -1 is needed because we halved the filter values.) - const int32x4_t horiz_const = vdupq_n_s32((1 << (bd + FILTER_BITS - 2)) + - (1 << ((ROUND0_BITS - 1) - 1))); + const uint16x8_t horiz_const = vdupq_n_u16((1 << (bd + FILTER_BITS - 2)) + + (1 << ((ROUND0_BITS - 1) - 1))); - const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); + // Filter values are even, so halve to reduce intermediate precision reqs. - const int8x8_t x_filter = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + + // Stagger the filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); + const int8x16_t x_filter = + vqtbl1q_s8(vcombine_s8(x_filter_s8, vdup_n_s8(0)), filter_idx); + + // Since f0 is always negative and s0 is unsigned, subtract (unsigned) s0 * + // -f0 to avoid signed overflow. + const uint8x8_t f0 = vdup_n_u8(-x_filter_ptr[0] >> 1); const uint8_t *src_ptr = src; int16_t *dst_ptr = im_block; @@ -222,10 +221,14 @@ uint8x16_t s0, s1, s2, s3; load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); - int16x8_t d0 = convolve8_8_2d_h(s0, x_filter, permute_tbl, horiz_const); - int16x8_t d1 = convolve8_8_2d_h(s1, x_filter, permute_tbl, horiz_const); - int16x8_t d2 = convolve8_8_2d_h(s2, x_filter, permute_tbl, horiz_const); - int16x8_t d3 = convolve8_8_2d_h(s3, x_filter, permute_tbl, horiz_const); + int16x8_t d0 = + convolve8_8_2d_h(s0, x_filter, f0, permute_tbl, horiz_const); + int16x8_t d1 = + convolve8_8_2d_h(s1, x_filter, f0, permute_tbl, horiz_const); + int16x8_t d2 = + convolve8_8_2d_h(s2, x_filter, f0, permute_tbl, horiz_const); + int16x8_t d3 = + convolve8_8_2d_h(s3, x_filter, f0, permute_tbl, horiz_const); store_s16_8x4(d, dst_stride, d0, d1, d2, d3); @@ -246,7 +249,8 @@ do { uint8x16_t s0 = vld1q_u8(s); - int16x8_t d0 = convolve8_8_2d_h(s0, x_filter, permute_tbl, horiz_const); + int16x8_t d0 = + convolve8_8_2d_h(s0, x_filter, f0, permute_tbl, horiz_const); vst1q_s16(d, d0); @@ -366,10 +370,10 @@ return vreinterpretq_u16_s16(res); } -static inline uint16x8_t convolve8_8_x(uint8x16_t samples, - const int8x8_t x_filter, - const uint8x16x3_t permute_tbl, - const int32x4_t round_offset) { +static inline uint16x8_t convolve8_8_x_usdot(uint8x16_t samples, + const int8x8_t x_filter, + const uint8x16x3_t permute_tbl, + const int32x4_t round_offset) { uint8x16_t permuted_samples[3]; int32x4_t sum[2]; @@ -395,6 +399,32 @@ return vreinterpretq_u16_s16(res); } +static inline uint16x8_t convolve8_8_x_usmmla(uint8x16_t samples, + const int8x16_t x_filter, + const uint8x8_t f0, + const uint8x16x2_t permute_tbl, + const uint16x8_t horiz_const) { + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(samples, permute_tbl.val[0]), + vqtbl1q_u8(samples, permute_tbl.val[1]) }; + + // Calculate partial 7-tap convolution. + int32x4_t sum0123 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[0], x_filter); + int32x4_t sum4567 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[1], x_filter); + uint16x8_t sum = vreinterpretq_u16_s16( + vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567))); + + // Apply tap 0 and accumulate. + sum = vmlsl_u8(sum, vget_low_u8(samples), f0); + + sum = vaddq_u16(sum, horiz_const); + + // We halved the convolution filter values so -1 from the right shift. + return vshrq_n_u16(sum, ROUND0_BITS - 1); +} + static inline void dist_wtd_convolve_x_dist_wtd_avg_6tap_neon_i8mm( const uint8_t *src, int src_stride, uint16_t *dst, int dst_stride, uint8_t *dst8, int dst8_stride, int w, int h, const int16_t *x_filter_ptr, @@ -421,7 +451,7 @@ vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8); if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src, src_stride, &s0, &s1, &s2, &s3); @@ -451,7 +481,7 @@ h -= 4; } while (h != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src; uint16_t *d = dst; @@ -527,13 +557,13 @@ load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); uint16x8_t d0 = - convolve8_8_x(s0, x_filter, permute_tbl, round_offset_shim); + convolve8_8_x_usdot(s0, x_filter, permute_tbl, round_offset_shim); uint16x8_t d1 = - convolve8_8_x(s1, x_filter, permute_tbl, round_offset_shim); + convolve8_8_x_usdot(s1, x_filter, permute_tbl, round_offset_shim); uint16x8_t d2 = - convolve8_8_x(s2, x_filter, permute_tbl, round_offset_shim); + convolve8_8_x_usdot(s2, x_filter, permute_tbl, round_offset_shim); uint16x8_t d3 = - convolve8_8_x(s3, x_filter, permute_tbl, round_offset_shim); + convolve8_8_x_usdot(s3, x_filter, permute_tbl, round_offset_shim); uint16x8_t dd0, dd1, dd2, dd3; load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); @@ -582,7 +612,7 @@ vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8); if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src, src_stride, &s0, &s1, &s2, &s3); @@ -612,7 +642,7 @@ h -= 4; } while (h != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src; uint16_t *d = dst; @@ -668,12 +698,23 @@ // A shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. // (The extra -1 is needed because we halved the filter values.) - const int32x4_t round_offset_shim = vdupq_n_s32( + const uint16x8_t round_offset_shim = vdupq_n_u16( (round_offset << (ROUND0_BITS - 1)) + (1 << ((ROUND0_BITS - 1) - 1))); - const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); + // Filter values are even, so halve to reduce intermediate precision reqs. - const int8x8_t x_filter = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + + // Stagger the filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); + const int8x16_t x_filter = + vqtbl1q_s8(vcombine_s8(x_filter_s8, vdup_n_s8(0)), filter_idx); + + // Since f0 is always negative and s0 is unsigned, subtract (unsigned) s0 * + // -f0 to avoid signed overflow. + const uint8x8_t f0 = vdup_n_u8(-x_filter_ptr[0] >> 1); do { const uint8_t *s = src; @@ -685,14 +726,14 @@ uint8x16_t s0, s1, s2, s3; load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); - uint16x8_t d0 = - convolve8_8_x(s0, x_filter, permute_tbl, round_offset_shim); - uint16x8_t d1 = - convolve8_8_x(s1, x_filter, permute_tbl, round_offset_shim); - uint16x8_t d2 = - convolve8_8_x(s2, x_filter, permute_tbl, round_offset_shim); - uint16x8_t d3 = - convolve8_8_x(s3, x_filter, permute_tbl, round_offset_shim); + uint16x8_t d0 = convolve8_8_x_usmmla(s0, x_filter, f0, permute_tbl, + round_offset_shim); + uint16x8_t d1 = convolve8_8_x_usmmla(s1, x_filter, f0, permute_tbl, + round_offset_shim); + uint16x8_t d2 = convolve8_8_x_usmmla(s2, x_filter, f0, permute_tbl, + round_offset_shim); + uint16x8_t d3 = convolve8_8_x_usmmla(s3, x_filter, f0, permute_tbl, + round_offset_shim); uint16x8_t dd0, dd1, dd2, dd3; load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); @@ -739,7 +780,7 @@ vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8); if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src, src_stride, &s0, &s1, &s2, &s3); @@ -760,7 +801,7 @@ h -= 4; } while (h != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src; uint16_t *d = dst; @@ -805,12 +846,23 @@ // A shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. // (The extra -1 is needed because we halved the filter values.) - const int32x4_t round_offset_shim = vdupq_n_s32( + const uint16x8_t round_offset_shim = vdupq_n_u16( (round_offset << (ROUND0_BITS - 1)) + (1 << ((ROUND0_BITS - 1) - 1))); - const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); + // Filter values are even, so halve to reduce intermediate precision reqs. - const int8x8_t x_filter = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + + // Stagger the filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); + const int8x16_t x_filter = + vqtbl1q_s8(vcombine_s8(x_filter_s8, vdup_n_s8(0)), filter_idx); + + // Since f0 is always negative and s0 is unsigned, subtract (unsigned) s0 * + // -f0 to avoid signed overflow. + const uint8x8_t f0 = vdup_n_u8(-x_filter_ptr[0] >> 1); do { const uint8_t *s = src; @@ -821,14 +873,14 @@ uint8x16_t s0, s1, s2, s3; load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); - uint16x8_t d0 = - convolve8_8_x(s0, x_filter, permute_tbl, round_offset_shim); - uint16x8_t d1 = - convolve8_8_x(s1, x_filter, permute_tbl, round_offset_shim); - uint16x8_t d2 = - convolve8_8_x(s2, x_filter, permute_tbl, round_offset_shim); - uint16x8_t d3 = - convolve8_8_x(s3, x_filter, permute_tbl, round_offset_shim); + uint16x8_t d0 = convolve8_8_x_usmmla(s0, x_filter, f0, permute_tbl, + round_offset_shim); + uint16x8_t d1 = convolve8_8_x_usmmla(s1, x_filter, f0, permute_tbl, + round_offset_shim); + uint16x8_t d2 = convolve8_8_x_usmmla(s2, x_filter, f0, permute_tbl, + round_offset_shim); + uint16x8_t d3 = convolve8_8_x_usmmla(s3, x_filter, f0, permute_tbl, + round_offset_shim); store_u16_8x4(d, dst_stride, d0, d1, d2, d3); @@ -892,3 +944,988 @@ x_filter_ptr); } } + +static inline int16x4_t convolve8_4_y(const uint8x16_t s0, const uint8x16_t s1, + const int8x8_t filters) { + int32x4_t sum = vusdotq_lane_s32(vdupq_n_s32(0), s0, filters, 0); + sum = vusdotq_lane_s32(sum, s1, filters, 1); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint16x8_t convolve8_8_y(const uint8x16_t s0_lo, + const uint8x16_t s0_hi, + const uint8x16_t s1_lo, + const uint8x16_t s1_hi, + const int8x8_t filters, + const int16x8_t round_offset) { + int32x4_t sum0123 = vusdotq_lane_s32(vdupq_n_s32(0), s0_lo, filters, 0); + sum0123 = vusdotq_lane_s32(sum0123, s1_lo, filters, 1); + + int32x4_t sum4567 = vusdotq_lane_s32(vdupq_n_s32(0), s0_hi, filters, 0); + sum4567 = vusdotq_lane_s32(sum4567, s1_hi, filters, 1); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + + // We halved the filter values so -1 from right shift. + return vreinterpretq_u16_s16( + vrsraq_n_s16(round_offset, sum, ROUND0_BITS - 1)); +} + +static inline void dist_wtd_convolve_y_8tap_neon_i8mm( + const uint8_t *src_ptr, int src_stride, int w, int h, + const int16_t *y_filter_ptr, ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + if (w == 4) { + uint8x8_t s0, s1, s2, s3, s4, s5, s6; + load_u8_8x7(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); + src_ptr += 7 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // (see horizontal case) required before computing the dot product. + uint8x16_t s0123, s1234, s2345, s3456; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_u8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_u8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_u8_4x4(s3, s4, s5, s6, &s3456); + + do { + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(src_ptr, src_stride, &s7, &s8, &s9, &sA); + + uint8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_u8_4x4(s7, s8, s9, sA, &s789A); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT = { { s3456, s789A } }; + s4567 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + s5678 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + s6789 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); + int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); + int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + + // We halved the filter values so -1 from right shift. + int16x8_t d01 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1); + int16x8_t d23 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1); + + store_u16x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d01)); + store_u16x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d23)); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + s1234 = s5678; + s2345 = s6789; + s3456 = s789A; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + + uint8x8_t s0, s1, s2, s3, s4, s5, s6; + load_u8_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); + s += 7 * src_stride; + + // This operation combines a conventional transpose and the sample + // permute (see horizontal case) required before computing the dot + // product. + uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_u8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_u8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_u8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + + do { + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(s, src_stride, &s7, &s8, &s9, &sA); + + uint8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, + s789A_lo, s789A_hi; + transpose_concat_elems_u8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; + s4567_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[0]); + s5678_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[1]); + s6789_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[2]); + + uint8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; + s4567_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[0]); + s5678_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[1]); + s6789_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint16x8_t d0 = convolve8_8_y(s0123_lo, s0123_hi, s4567_lo, s4567_hi, + filter, round_offset_vec); + uint16x8_t d1 = convolve8_8_y(s1234_lo, s1234_hi, s5678_lo, s5678_hi, + filter, round_offset_vec); + uint16x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, + filter, round_offset_vec); + uint16x8_t d3 = convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, + filter, round_offset_vec); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_8tap_dist_wtd_avg_neon_i8mm( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + if (w == 4) { + uint8x8_t s0, s1, s2, s3, s4, s5, s6; + load_u8_8x7(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); + src_ptr += 7 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // (see horizontal case) required before computing the dot product. + uint8x16_t s0123, s1234, s2345, s3456; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_u8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_u8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_u8_4x4(s3, s4, s5, s6, &s3456); + + do { + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(src_ptr, src_stride, &s7, &s8, &s9, &sA); + + uint8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_u8_4x4(s7, s8, s9, sA, &s789A); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT = { { s3456, s789A } }; + s4567 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + s5678 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + s6789 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); + int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); + int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_dist_wtd_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), + d01, d23, fwd_offset, bck_offset, + round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + s1234 = s5678; + s2345 = s6789; + s3456 = s789A; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t s0, s1, s2, s3, s4, s5, s6; + load_u8_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); + s += 7 * src_stride; + + // This operation combines a conventional transpose and the sample + // permute (see horizontal case) required before computing the dot + // product. + uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_u8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_u8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_u8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + + do { + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(s, src_stride, &s7, &s8, &s9, &sA); + + uint8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, + s789A_lo, s789A_hi; + transpose_concat_elems_u8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; + s4567_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[0]); + s5678_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[1]); + s6789_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[2]); + + uint8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; + s4567_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[0]); + s5678_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[1]); + s6789_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint16x8_t d0 = convolve8_8_y(s0123_lo, s0123_hi, s4567_lo, s4567_hi, + filter, round_offset_vec); + uint16x8_t d1 = convolve8_8_y(s1234_lo, s1234_hi, s5678_lo, s5678_hi, + filter, round_offset_vec); + uint16x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, + filter, round_offset_vec); + uint16x8_t d3 = convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, + filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, fwd_offset, + bck_offset, round_offset_vec, &d0_u8, &d1_u8, + &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_8tap_avg_neon_i8mm( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + if (w == 4) { + uint8x8_t s0, s1, s2, s3, s4, s5, s6; + load_u8_8x7(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); + src_ptr += 7 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // (see horizontal case) required before computing the dot product. + uint8x16_t s0123, s1234, s2345, s3456; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_u8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_u8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_u8_4x4(s3, s4, s5, s6, &s3456); + + do { + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(src_ptr, src_stride, &s7, &s8, &s9, &sA); + + uint8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_u8_4x4(s7, s8, s9, sA, &s789A); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT = { { s3456, s789A } }; + s4567 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + s5678 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + s6789 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); + int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); + int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_basic_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), d01, + d23, round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + s1234 = s5678; + s2345 = s6789; + s3456 = s789A; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t s0, s1, s2, s3, s4, s5, s6; + load_u8_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6); + s += 7 * src_stride; + + // This operation combines a conventional transpose and the sample + // permute (see horizontal case) required before computing the dot + // product. + uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_u8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_u8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_u8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + + do { + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(s, src_stride, &s7, &s8, &s9, &sA); + + uint8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, + s789A_lo, s789A_hi; + transpose_concat_elems_u8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; + s4567_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[0]); + s5678_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[1]); + s6789_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[2]); + + uint8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; + s4567_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[0]); + s5678_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[1]); + s6789_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint16x8_t d0 = convolve8_8_y(s0123_lo, s0123_hi, s4567_lo, s4567_hi, + filter, round_offset_vec); + uint16x8_t d1 = convolve8_8_y(s1234_lo, s1234_hi, s5678_lo, s5678_hi, + filter, round_offset_vec); + uint16x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, + filter, round_offset_vec); + uint16x8_t d3 = convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, + filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset_vec, &d0_u8, &d1_u8, &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline int16x4_t convolve4_4_y(const uint8x16_t s0, + const int8x8_t filters) { + int32x4_t sum = vusdotq_lane_s32(vdupq_n_s32(0), s0, filters, 0); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint16x8_t convolve4_8_y(const uint8x16_t s0, const uint8x16_t s1, + const int8x8_t filters, + const int16x8_t round_offset) { + int32x4_t sum0123 = vusdotq_lane_s32(vdupq_n_s32(0), s0, filters, 0); + int32x4_t sum4567 = vusdotq_lane_s32(vdupq_n_s32(0), s1, filters, 0); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + + // We halved the filter values so -1 from right shift. + return vreinterpretq_u16_s16( + vrsraq_n_s16(round_offset, sum, ROUND0_BITS - 1)); +} + +static inline void dist_wtd_convolve_y_4tap_neon_i8mm( + const uint8_t *src_ptr, int src_stride, int w, int h, + const int16_t *y_filter_ptr, ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + uint8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(src_ptr, src_stride, &s0, &s1, &s2, &s3); + src_ptr += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(src_ptr, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567; + transpose_concat_elems_u8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + uint8x16_t s1234 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + + // We halved the filter values so -1 from right shift. + int16x8_t d01 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1); + int16x8_t d23 = + vrsraq_n_s16(round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1); + + store_u16x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d01)); + store_u16x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, + vreinterpretq_u16_s16(d23)); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3); + s += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(s, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_u8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + uint8x16_t s1234_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + uint8x16_t s1234_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + uint16x8_t d0 = + convolve4_8_y(s0123_lo, s0123_hi, filter, round_offset_vec); + uint16x8_t d1 = + convolve4_8_y(s1234_lo, s1234_hi, filter, round_offset_vec); + uint16x8_t d2 = + convolve4_8_y(s2345_lo, s2345_hi, filter, round_offset_vec); + uint16x8_t d3 = + convolve4_8_y(s3456_lo, s3456_hi, filter, round_offset_vec); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_4tap_avg_neon_i8mm( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + uint8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(src_ptr, src_stride, &s0, &s1, &s2, &s3); + src_ptr += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(src_ptr, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567; + transpose_concat_elems_u8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + uint8x16_t s1234 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_basic_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), d01, + d23, round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3); + s += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(s, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_u8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + uint8x16_t s1234_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + uint8x16_t s1234_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + uint16x8_t d0 = + convolve4_8_y(s0123_lo, s0123_hi, filter, round_offset_vec); + uint16x8_t d1 = + convolve4_8_y(s1234_lo, s1234_hi, filter, round_offset_vec); + uint16x8_t d2 = + convolve4_8_y(s2345_lo, s2345_hi, filter, round_offset_vec); + uint16x8_t d3 = + convolve4_8_y(s3456_lo, s3456_hi, filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset_vec, &d0_u8, &d1_u8, &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline void dist_wtd_convolve_y_4tap_dist_wtd_avg_neon_i8mm( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter_ptr, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const int16x8_t round_offset_vec = vdupq_n_s16(round_offset); + + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + uint8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(src_ptr, src_stride, &s0, &s1, &s2, &s3); + src_ptr += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(src_ptr, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567; + transpose_concat_elems_u8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + uint8x16_t s1234 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + + // We halved the filter values so -1 from right shift. + uint16x8_t d01 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d0, d1), ROUND0_BITS - 1)); + uint16x8_t d23 = vreinterpretq_u16_s16(vrsraq_n_s16( + round_offset_vec, vcombine_s16(d2, d3), ROUND0_BITS - 1)); + + uint16x4_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8; + compute_dist_wtd_avg_8x2(vcombine_u16(dd0, dd1), vcombine_u16(dd2, dd3), + d01, d23, fwd_offset, bck_offset, + round_offset_vec, &d0_u8, &d1_u8); + + store_u8x4_strided_x2(dst8_ptr + 0 * dst8_stride, dst8_stride, d0_u8); + store_u8x4_strided_x2(dst8_ptr + 2 * dst8_stride, dst8_stride, d1_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3); + s += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(s, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_u8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + uint8x16_t s1234_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + uint8x16_t s1234_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + uint16x8_t d0 = + convolve4_8_y(s0123_lo, s0123_hi, filter, round_offset_vec); + uint16x8_t d1 = + convolve4_8_y(s1234_lo, s1234_hi, filter, round_offset_vec); + uint16x8_t d2 = + convolve4_8_y(s2345_lo, s2345_hi, filter, round_offset_vec); + uint16x8_t d3 = + convolve4_8_y(s3456_lo, s3456_hi, filter, round_offset_vec); + + uint16x8_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3); + + uint8x8_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4(dd0, dd1, dd2, dd3, d0, d1, d2, d3, fwd_offset, + bck_offset, round_offset_vec, &d0_u8, &d1_u8, + &d2_u8, &d3_u8); + + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + dst8_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +void av1_dist_wtd_convolve_y_neon_i8mm( + const uint8_t *src, int src_stride, uint8_t *dst8, int dst8_stride, int w, + int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn, + ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_y, subpel_y_qn & SUBPEL_MASK); + + if (get_filter_tap(filter_params_y, subpel_y_qn) <= 4) { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_y_4tap_dist_wtd_avg_neon_i8mm( + src - src_stride, src_stride, dst8, dst8_stride, w, h, y_filter_ptr, + conv_params); + } else { + dist_wtd_convolve_y_4tap_avg_neon_i8mm(src - src_stride, src_stride, + dst8, dst8_stride, w, h, + y_filter_ptr, conv_params); + } + } else { + dist_wtd_convolve_y_4tap_neon_i8mm(src - src_stride, src_stride, w, h, + y_filter_ptr, conv_params); + } + } else { // filter tap >= 6 + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_y_8tap_dist_wtd_avg_neon_i8mm( + src - 3 * src_stride, src_stride, dst8, dst8_stride, w, h, + y_filter_ptr, conv_params); + } else { + dist_wtd_convolve_y_8tap_avg_neon_i8mm(src - 3 * src_stride, src_stride, + dst8, dst8_stride, w, h, + y_filter_ptr, conv_params); + } + } else { + dist_wtd_convolve_y_8tap_neon_i8mm(src - 3 * src_stride, src_stride, w, h, + y_filter_ptr, conv_params); + } + } +}
diff --git a/av1/common/arm/convolve_neon_dotprod.c b/av1/common/arm/convolve_neon_dotprod.c index 8d0d929..35cdf9e 100644 --- a/av1/common/arm/convolve_neon_dotprod.c +++ b/av1/common/arm/convolve_neon_dotprod.c
@@ -16,18 +16,19 @@ #include "aom_dsp/aom_dsp_common.h" #include "aom_dsp/arm/mem_neon.h" +#include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" #include "av1/common/arm/convolve_neon.h" #include "av1/common/convolve.h" #include "av1/common/filter.h" -DECLARE_ALIGNED(16, static const uint8_t, kDotProdPermuteTbl[48]) = { +DECLARE_ALIGNED(16, const uint8_t, kDotProdPermuteTbl[48]) = { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 }; -DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { +DECLARE_ALIGNED(16, const uint8_t, kDotProdMergeBlockTbl[48]) = { // Shift left and insert new last column in transposed 4x4 block. 1, 2, 3, 16, 5, 6, 7, 20, 9, 10, 11, 24, 13, 14, 15, 28, // Shift left and insert two new columns in transposed 4x4 block. @@ -64,7 +65,7 @@ sum = vdotq_laneq_s32(sum, perm_samples[1], filter, 1); sum = vdotq_laneq_s32(sum, perm_samples[2], filter, 2); - return vqrshrn_n_s32(sum, FILTER_BITS); + return vshrn_n_s32(sum, 1); } static inline uint8x8_t convolve12_8_x(uint8x16_t samples[2], @@ -105,9 +106,9 @@ sum4567 = vdotq_laneq_s32(sum4567, perm_samples[3], filter, 2); // Narrow and re-pack. - int16x8_t sum_s16 = vcombine_s16(vqrshrn_n_s32(sum0123, FILTER_BITS), - vqrshrn_n_s32(sum4567, FILTER_BITS)); - return vqmovun_s16(sum_s16); + int16x8_t sum_s16 = + vcombine_s16(vshrn_n_s32(sum0123, 1), vshrn_n_s32(sum4567, 1)); + return vqrshrun_n_s16(sum_s16, FILTER_BITS - 1); } static inline void convolve_x_sr_12tap_neon_dotprod( @@ -134,8 +135,8 @@ int16x4_t d2 = convolve12_4_x(s2, filter, permute_tbl); int16x4_t d3 = convolve12_4_x(s3, filter, permute_tbl); - uint8x8_t d01 = vqmovun_s16(vcombine_s16(d0, d1)); - uint8x8_t d23 = vqmovun_s16(vcombine_s16(d2, d3)); + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); @@ -387,57 +388,6 @@ } while (h != 0); } -static inline void transpose_concat_4x4(int8x8_t a0, int8x8_t a1, int8x8_t a2, - int8x8_t a3, int8x16_t *b) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, XX, XX, XX, XX - // a1: 10, 11, 12, 13, XX, XX, XX, XX - // a2: 20, 21, 22, 23, XX, XX, XX, XX - // a3: 30, 31, 32, 33, XX, XX, XX, XX - // - // b: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - - int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); - int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); - int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); - int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); - - int8x16_t a01 = vzipq_s8(a0q, a1q).val[0]; - int8x16_t a23 = vzipq_s8(a2q, a3q).val[0]; - - int16x8_t a0123 = - vzipq_s16(vreinterpretq_s16_s8(a01), vreinterpretq_s16_s8(a23)).val[0]; - - *b = vreinterpretq_s8_s16(a0123); -} - -static inline void transpose_concat_8x4(int8x8_t a0, int8x8_t a1, int8x8_t a2, - int8x8_t a3, int8x16_t *b0, - int8x16_t *b1) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, 04, 05, 06, 07 - // a1: 10, 11, 12, 13, 14, 15, 16, 17 - // a2: 20, 21, 22, 23, 24, 25, 26, 27 - // a3: 30, 31, 32, 33, 34, 35, 36, 37 - // - // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 - - int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); - int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); - int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); - int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); - - int8x16_t a01 = vzipq_s8(a0q, a1q).val[0]; - int8x16_t a23 = vzipq_s8(a2q, a3q).val[0]; - - int16x8x2_t a0123 = - vzipq_s16(vreinterpretq_s16_s8(a01), vreinterpretq_s16_s8(a23)); - - *b0 = vreinterpretq_s8_s16(a0123.val[0]); - *b1 = vreinterpretq_s8_s16(a0123.val[1]); -} - static inline int16x4_t convolve12_4_y(const int8x16_t s0, const int8x16_t s1, const int8x16_t s2, const int8x8_t filters_0_7, @@ -450,7 +400,7 @@ sum = vdotq_lane_s32(sum, s2, filters_4_11, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vshrn_n_s32(sum, 1); } static inline uint8x8_t convolve12_8_y( @@ -470,8 +420,9 @@ sum4567 = vdotq_lane_s32(sum4567, s2_hi, filters_4_11, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0123), vqmovn_s32(sum4567)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = + vcombine_s16(vshrn_n_s32(sum0123, 1), vshrn_n_s32(sum4567, 1)); + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve_y_sr_12tap_neon_dotprod( @@ -505,14 +456,14 @@ int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); int8x16_t s0123, s1234, s2345, s3456, s4567, s5678, s6789, s789A; - transpose_concat_4x4(s0, s1, s2, s3, &s0123); - transpose_concat_4x4(s1, s2, s3, s4, &s1234); - transpose_concat_4x4(s2, s3, s4, s5, &s2345); - transpose_concat_4x4(s3, s4, s5, s6, &s3456); - transpose_concat_4x4(s4, s5, s6, s7, &s4567); - transpose_concat_4x4(s5, s6, s7, s8, &s5678); - transpose_concat_4x4(s6, s7, s8, s9, &s6789); - transpose_concat_4x4(s7, s8, s9, sA, &s789A); + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_s8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_s8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_s8_4x4(s3, s4, s5, s6, &s3456); + transpose_concat_elems_s8_4x4(s4, s5, s6, s7, &s4567); + transpose_concat_elems_s8_4x4(s5, s6, s7, s8, &s5678); + transpose_concat_elems_s8_4x4(s6, s7, s8, s9, &s6789); + transpose_concat_elems_s8_4x4(s7, s8, s9, sA, &s789A); do { uint8x8_t tB, tC, tD, tE; @@ -524,7 +475,7 @@ int8x8_t sE = vreinterpret_s8_u8(vsub_u8(tE, vdup_n_u8(128))); int8x16_t s89AB, s9ABC, sABCD, sBCDE; - transpose_concat_4x4(sB, sC, sD, sE, &sBCDE); + transpose_concat_elems_s8_4x4(sB, sC, sD, sE, &sBCDE); // Merge new data into block from previous iteration. int8x16x2_t samples_LUT = { { s789A, sBCDE } }; @@ -540,8 +491,8 @@ convolve12_4_y(s2345, s6789, sABCD, filter_0_7, filter_4_11); int16x4_t d3 = convolve12_4_y(s3456, s789A, sBCDE, filter_0_7, filter_4_11); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, d23); @@ -591,14 +542,14 @@ int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, s3456_lo, s3456_hi, s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, s789A_lo, s789A_hi; - transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); - transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); - transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); - transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); - transpose_concat_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); - transpose_concat_8x4(s5, s6, s7, s8, &s5678_lo, &s5678_hi); - transpose_concat_8x4(s6, s7, s8, s9, &s6789_lo, &s6789_hi); - transpose_concat_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_s8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_s8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_s8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_elems_s8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + transpose_concat_elems_s8_8x4(s5, s6, s7, s8, &s5678_lo, &s5678_hi); + transpose_concat_elems_s8_8x4(s6, s7, s8, s9, &s6789_lo, &s6789_hi); + transpose_concat_elems_s8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); do { uint8x8_t tB, tC, tD, tE; @@ -611,7 +562,7 @@ int8x16_t s89AB_lo, s89AB_hi, s9ABC_lo, s9ABC_hi, sABCD_lo, sABCD_hi, sBCDE_lo, sBCDE_hi; - transpose_concat_8x4(sB, sC, sD, sE, &sBCDE_lo, &sBCDE_hi); + transpose_concat_elems_s8_8x4(sB, sC, sD, sE, &sBCDE_lo, &sBCDE_hi); // Merge new data into block from previous iteration. int8x16x2_t samples_LUT_lo = { { s789A_lo, sBCDE_lo } }; @@ -673,12 +624,13 @@ const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. - const int32x4_t acc = vdupq_n_s32(128 << FILTER_BITS); + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); int32x4_t sum = vdotq_lane_s32(acc, s0, filters, 0); sum = vdotq_lane_s32(sum, s1, filters, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vmovn_s32(sum); } static inline uint8x8_t convolve8_8_y(const int8x16_t s0_lo, @@ -688,7 +640,8 @@ const int8x8_t filters) { // The sample range transform and permutation are performed by the caller. // Accumulate into 128 << FILTER_BITS to account for range transform. - const int32x4_t acc = vdupq_n_s32(128 << FILTER_BITS); + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); int32x4_t sum0123 = vdotq_lane_s32(acc, s0_lo, filters, 0); sum0123 = vdotq_lane_s32(sum0123, s1_lo, filters, 1); @@ -697,14 +650,16 @@ sum4567 = vdotq_lane_s32(sum4567, s1_hi, filters, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0123), vqmovn_s32(sum4567)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve_y_sr_8tap_neon_dotprod( const uint8_t *src_ptr, int src_stride, uint8_t *dst_ptr, int dst_stride, int w, int h, const int16_t *y_filter_ptr) { - const int8x8_t filter = vmovn_s16(vld1q_s16(y_filter_ptr)); + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); @@ -723,25 +678,25 @@ int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); int8x16_t s0123, s1234, s2345, s3456; - transpose_concat_4x4(s0, s1, s2, s3, &s0123); - transpose_concat_4x4(s1, s2, s3, s4, &s1234); - transpose_concat_4x4(s2, s3, s4, s5, &s2345); - transpose_concat_4x4(s3, s4, s5, s6, &s3456); + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_s8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_s8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_s8_4x4(s3, s4, s5, s6, &s3456); do { - uint8x8_t t7, t8, t9, t10; - load_u8_8x4(src_ptr, src_stride, &t7, &t8, &t9, &t10); + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(src_ptr, src_stride, &t7, &t8, &t9, &tA); int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); - int8x8_t s10 = vreinterpret_s8_u8(vsub_u8(t10, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); - int8x16_t s4567, s5678, s6789, s78910; - transpose_concat_4x4(s7, s8, s9, s10, &s78910); + int8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_s8_4x4(s7, s8, s9, sA, &s789A); // Merge new data into block from previous iteration. - int8x16x2_t samples_LUT = { { s3456, s78910 } }; + int8x16x2_t samples_LUT = { { s3456, s789A } }; s4567 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); s5678 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); s6789 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); @@ -749,9 +704,10 @@ int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); - int16x4_t d3 = convolve8_4_y(s3456, s78910, filter); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, d23); @@ -761,7 +717,7 @@ s0123 = s4567; s1234 = s5678; s2345 = s6789; - s3456 = s78910; + s3456 = s789A; src_ptr += 4 * src_stride; dst_ptr += 4 * dst_stride; @@ -791,31 +747,31 @@ // product. int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, s3456_lo, s3456_hi; - transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); - transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); - transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); - transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_s8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_s8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_s8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); do { - uint8x8_t t7, t8, t9, t10; - load_u8_8x4(s, src_stride, &t7, &t8, &t9, &t10); + uint8x8_t t7, t8, t9, tA; + load_u8_8x4(s, src_stride, &t7, &t8, &t9, &tA); int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); - int8x8_t s10 = vreinterpret_s8_u8(vsub_u8(t10, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); int8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, - s78910_lo, s78910_hi; - transpose_concat_8x4(s7, s8, s9, s10, &s78910_lo, &s78910_hi); + s789A_lo, s789A_hi; + transpose_concat_elems_s8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); // Merge new data into block from previous iteration. - int8x16x2_t samples_LUT_lo = { { s3456_lo, s78910_lo } }; + int8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; s4567_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[0]); s5678_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[1]); s6789_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[2]); - int8x16x2_t samples_LUT_hi = { { s3456_hi, s78910_hi } }; + int8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; s4567_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[0]); s5678_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[1]); s6789_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[2]); @@ -827,7 +783,7 @@ uint8x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, filter); uint8x8_t d3 = - convolve8_8_y(s3456_lo, s3456_hi, s78910_lo, s78910_hi, filter); + convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, filter); store_u8_8x4(d, dst_stride, d0, d1, d2, d3); @@ -839,8 +795,170 @@ s1234_hi = s5678_hi; s2345_lo = s6789_lo; s2345_hi = s6789_hi; - s3456_lo = s78910_lo; - s3456_hi = s78910_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline int16x4_t convolve4_4_y(const int8x16_t s0, + const int8x8_t filters) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); + + int32x4_t sum = vdotq_lane_s32(acc, s0, filters, 0); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint8x8_t convolve4_8_y(const int8x16_t s0, const int8x16_t s1, + const int8x8_t filters) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + // (- 1 since we halved the filters.) + const int32x4_t acc = vdupq_n_s32(128 << (FILTER_BITS - 1)); + + int32x4_t sum0123 = vdotq_lane_s32(acc, s0, filters, 0); + int32x4_t sum4567 = vdotq_lane_s32(acc, s1, filters, 0); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); +} + +static inline void convolve_y_sr_4tap_neon_dotprod( + const uint8_t *src_ptr, int src_stride, uint8_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *y_filter_ptr) { + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + int8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(src_ptr, src_stride, &t0, &t1, &t2, &t3); + src_ptr += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123; + transpose_concat_elems_s8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(src_ptr, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567; + transpose_concat_elems_s8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + int8x16_t s1234 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456 = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); + + store_u8x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, d01); + store_u8x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, d23); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + uint8_t *d = dst_ptr; + + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(s, src_stride, &t0, &t1, &t2, &t3); + s += 4 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + int8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_s8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t t4, t5, t6, t7; + load_u8_8x4(s, src_stride, &t4, &t5, &t6, &t7); + + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + + int8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_s8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + int8x16_t s1234_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_lo = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + int8x16_t s1234_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[0]); + int8x16_t s2345_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[1]); + int8x16_t s3456_hi = vqtbl2q_s8(samples_LUT, merge_block_tbl.val[2]); + + uint8x8_t d0 = convolve4_8_y(s0123_lo, s0123_hi, filter); + uint8x8_t d1 = convolve4_8_y(s1234_lo, s1234_hi, filter); + uint8x8_t d2 = convolve4_8_y(s2345_lo, s2345_hi, filter); + uint8x8_t d3 = convolve4_8_y(s3456_lo, s3456_hi, filter); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; s += 4 * src_stride; d += 4 * dst_stride; @@ -864,27 +982,20 @@ } const int y_filter_taps = get_filter_tap(filter_params_y, subpel_y_qn); - - if (y_filter_taps <= 6) { - av1_convolve_y_sr_neon(src, src_stride, dst, dst_stride, w, h, - filter_params_y, subpel_y_qn); - return; - } - - const int vert_offset = y_filter_taps / 2 - 1; - src -= vert_offset * src_stride; - const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( filter_params_y, subpel_y_qn & SUBPEL_MASK); - if (y_filter_taps > 8) { - convolve_y_sr_12tap_neon_dotprod(src, src_stride, dst, dst_stride, w, h, - y_filter_ptr); - return; + if (y_filter_taps <= 4) { + convolve_y_sr_4tap_neon_dotprod(src - src_stride, src_stride, dst, + dst_stride, w, h, y_filter_ptr); + } else if (y_filter_taps == 12) { + convolve_y_sr_12tap_neon_dotprod(src - 5 * src_stride, src_stride, dst, + dst_stride, w, h, y_filter_ptr); + } else { + // 6-tap or 8-tap. + convolve_y_sr_8tap_neon_dotprod(src - 3 * src_stride, src_stride, dst, + dst_stride, w, h, y_filter_ptr); } - - convolve_y_sr_8tap_neon_dotprod(src, src_stride, dst, dst_stride, w, h, - y_filter_ptr); } static inline int16x4_t convolve12_4_2d_h(uint8x16_t samples,
diff --git a/av1/common/arm/convolve_neon_dotprod.h b/av1/common/arm/convolve_neon_dotprod.h new file mode 100644 index 0000000..2bdf365 --- /dev/null +++ b/av1/common/arm/convolve_neon_dotprod.h
@@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025, 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. + */ +#ifndef AOM_AV1_COMMON_ARM_CONVOLVE_NEON_DOTPROD_H_ +#define AOM_AV1_COMMON_ARM_CONVOLVE_NEON_DOTPROD_H_ + +#include "aom_ports/mem.h" + +DECLARE_ALIGNED(16, extern const uint8_t, kDotProdPermuteTbl[48]); +DECLARE_ALIGNED(16, extern const uint8_t, kDotProdMergeBlockTbl[48]); + +#endif // AOM_AV1_COMMON_ARM_CONVOLVE_NEON_DOTPROD_H_
diff --git a/av1/common/arm/convolve_neon_i8mm.c b/av1/common/arm/convolve_neon_i8mm.c index acd912e..1452841 100644 --- a/av1/common/arm/convolve_neon_i8mm.c +++ b/av1/common/arm/convolve_neon_i8mm.c
@@ -16,19 +16,32 @@ #include "aom_dsp/aom_dsp_common.h" #include "aom_dsp/arm/mem_neon.h" +#include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" #include "av1/common/arm/convolve_neon.h" +#include "av1/common/arm/convolve_neon_dotprod.h" #include "av1/common/arm/convolve_neon_i8mm.h" #include "av1/common/convolve.h" #include "av1/common/filter.h" -DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { - // Shift left and insert new last column in transposed 4x4 block. - 1, 2, 3, 16, 5, 6, 7, 20, 9, 10, 11, 24, 13, 14, 15, 28, - // Shift left and insert two new columns in transposed 4x4 block. - 2, 3, 16, 17, 6, 7, 20, 21, 10, 11, 24, 25, 14, 15, 28, 29, - // Shift left and insert three new columns in transposed 4x4 block. - 3, 16, 17, 18, 7, 20, 21, 22, 11, 24, 25, 26, 15, 28, 29, 30 +DECLARE_ALIGNED(16, const uint8_t, kMatMul6PermuteTbl[32]) = { + // clang-format off + 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9, + 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 + // clang-format on +}; + +DECLARE_ALIGNED(16, const uint8_t, kMatMul8PermuteTbl[32]) = { + // clang-format off + 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10, + 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 + // clang-format on +}; + +DECLARE_ALIGNED(16, const uint8_t, kFilterPermuteTbl[16]) = { + // clang-format off + 1, 2, 3, 4, 5, 6, 7, 16, 16, 1, 2, 3, 4, 5, 6, 7 + // clang-format on }; static inline int16x4_t convolve12_4_x(uint8x16_t samples[2], @@ -46,7 +59,7 @@ int32x4_t sum = vusmmlaq_s32(horiz_const, perm_samples[0], filter[0]); sum = vusmmlaq_s32(sum, perm_samples[1], filter[1]); - return vqrshrn_n_s32(sum, FILTER_BITS); + return vshrn_n_s32(sum, 1); } static inline uint8x8_t convolve12_8_x(uint8x16_t samples[2], @@ -71,9 +84,9 @@ sum4567 = vusmmlaq_s32(sum4567, perm_samples[3], filter[1]); // Narrow and re-pack. - int16x8_t sum_s16 = vcombine_s16(vqrshrn_n_s32(sum0123, FILTER_BITS), - vqrshrn_n_s32(sum4567, FILTER_BITS)); - return vqmovun_s16(sum_s16); + int16x8_t sum_s16 = + vcombine_s16(vshrn_n_s32(sum0123, 1), vshrn_n_s32(sum4567, 1)); + return vqrshrun_n_s16(sum_s16, FILTER_BITS - 1); } static inline void convolve_x_sr_12tap_neon_i8mm(const uint8_t *src, @@ -104,7 +117,7 @@ const int32x4_t horiz_const = vdupq_n_s32(1 << (ROUND0_BITS - 1)); if (w <= 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0[2], s1[2], s2[2], s3[2]; @@ -116,8 +129,8 @@ int16x4_t d2 = convolve12_4_x(s2, filter, permute_tbl, horiz_const); int16x4_t d3 = convolve12_4_x(s3, filter, permute_tbl, horiz_const); - uint8x8_t d01 = vqmovun_s16(vcombine_s16(d0, d1)); - uint8x8_t d23 = vqmovun_s16(vcombine_s16(d2, d3)); + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst + 2 * dst_stride, dst_stride, d23); @@ -127,7 +140,7 @@ h -= 4; } while (h != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src; @@ -157,35 +170,49 @@ } } -static inline uint8x8_t convolve8_8_x(uint8x16_t samples, const int8x8_t filter, - const uint8x16x3_t permute_tbl, - const int32x4_t horiz_const) { - // Permute samples ready for dot product. - // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } - // { 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 } - // { 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 } - uint8x16_t perm_samples[3] = { vqtbl1q_u8(samples, permute_tbl.val[0]), - vqtbl1q_u8(samples, permute_tbl.val[1]), - vqtbl1q_u8(samples, permute_tbl.val[2]) }; +static inline uint8x8_t convolve8_8_x(uint8x16_t samples, + const int8x16_t filter, + const uint8x8_t f0, + const uint8x16x2_t permute_tbl, + const int16x8_t horiz_const) { + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(samples, permute_tbl.val[0]), + vqtbl1q_u8(samples, permute_tbl.val[1]) }; - int32x4_t sum0123 = vusdotq_lane_s32(horiz_const, perm_samples[0], filter, 0); - sum0123 = vusdotq_lane_s32(sum0123, perm_samples[1], filter, 1); + // Calculate partial 7-tap convolution. + int32x4_t sum0123 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[0], filter); + int32x4_t sum4567 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[1], filter); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); - int32x4_t sum4567 = vusdotq_lane_s32(horiz_const, perm_samples[1], filter, 0); - sum4567 = vusdotq_lane_s32(sum4567, perm_samples[2], filter, 1); + // Apply tap 0 and accumulate. + sum = vreinterpretq_s16_u16( + vmlsl_u8(vreinterpretq_u16_s16(sum), vget_low_u8(samples), f0)); - int16x8_t sum_s16 = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + sum = vaddq_s16(sum, horiz_const); + // We halved the convolution filter values so - 1 from the right shift. - return vqrshrun_n_s16(sum_s16, FILTER_BITS - 1); + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve_x_sr_8tap_neon_i8mm( const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, int width, int height, const int16_t *filter_x, - const int32x4_t horiz_const) { + const int16x8_t horiz_const) { // Filter values are even, so halve to reduce intermediate precision reqs. - const int8x8_t x_filter = vshrn_n_s16(vld1q_s16(filter_x), 1); - const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(filter_x), 1); + + // Stagger the filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); + const int8x16_t x_filter = + vqtbl1q_s8(vcombine_s8(x_filter_s8, vdup_n_s8(0)), filter_idx); + + // Since f0 is always negative and s0 is unsigned, subtract (unsigned) s0 * + // -f0 to avoid signed overflow. + const uint8x8_t f0 = vdup_n_u8(-filter_x[0] >> 1); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); do { const uint8_t *s = src; @@ -196,10 +223,10 @@ uint8x16_t s0, s1, s2, s3; load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); - uint8x8_t d0 = convolve8_8_x(s0, x_filter, permute_tbl, horiz_const); - uint8x8_t d1 = convolve8_8_x(s1, x_filter, permute_tbl, horiz_const); - uint8x8_t d2 = convolve8_8_x(s2, x_filter, permute_tbl, horiz_const); - uint8x8_t d3 = convolve8_8_x(s3, x_filter, permute_tbl, horiz_const); + uint8x8_t d0 = convolve8_8_x(s0, x_filter, f0, permute_tbl, horiz_const); + uint8x8_t d1 = convolve8_8_x(s1, x_filter, f0, permute_tbl, horiz_const); + uint8x8_t d2 = convolve8_8_x(s2, x_filter, f0, permute_tbl, horiz_const); + uint8x8_t d3 = convolve8_8_x(s3, x_filter, f0, permute_tbl, horiz_const); store_u8_8x4(d, dst_stride, d0, d1, d2, d3); @@ -261,7 +288,7 @@ vcombine_s8(vext_s8(x_filter_s8, x_filter_s8, 1), x_filter_s8); if (width == 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0, s1, s2, s3; load_u8_16x4(src, src_stride, &s0, &s1, &s2, &s3); @@ -282,7 +309,7 @@ height -= 4; } while (height != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src; uint8_t *d = dst; @@ -334,11 +361,12 @@ // right shift by FILTER_BITS instead of two rounding right shifts: first by // ROUND0_BITS, and then subsequently by FILTER_BITS - ROUND0_BITS. // Halve the total because we will halve the filter values. - const int32x4_t horiz_const = vdupq_n_s32((1 << ((ROUND0_BITS - 1)) / 2)); + const int32x4_t horiz_const_s32 = vdupq_n_s32(1 << (ROUND0_BITS - 1) / 2); + const int16x8_t horiz_const_s16 = vdupq_n_s16(1 << (ROUND0_BITS - 1) / 2); if (filter_taps <= 6) { convolve_x_sr_6tap_neon_i8mm(src + 1, src_stride, dst, dst_stride, w, h, - x_filter_ptr, horiz_const); + x_filter_ptr, horiz_const_s32); return; } @@ -349,59 +377,7 @@ } convolve_x_sr_8tap_neon_i8mm(src, src_stride, dst, dst_stride, w, h, - x_filter_ptr, horiz_const); -} - -static inline void transpose_concat_4x4(uint8x8_t a0, uint8x8_t a1, - uint8x8_t a2, uint8x8_t a3, - uint8x16_t *b) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, XX, XX, XX, XX - // a1: 10, 11, 12, 13, XX, XX, XX, XX - // a2: 20, 21, 22, 23, XX, XX, XX, XX - // a3: 30, 31, 32, 33, XX, XX, XX, XX - // - // b: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - - uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); - uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); - uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); - uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); - - uint8x16_t a01 = vzipq_u8(a0q, a1q).val[0]; - uint8x16_t a23 = vzipq_u8(a2q, a3q).val[0]; - - uint16x8_t a0123 = - vzipq_u16(vreinterpretq_u16_u8(a01), vreinterpretq_u16_u8(a23)).val[0]; - - *b = vreinterpretq_u8_u16(a0123); -} - -static inline void transpose_concat_8x4(uint8x8_t a0, uint8x8_t a1, - uint8x8_t a2, uint8x8_t a3, - uint8x16_t *b0, uint8x16_t *b1) { - // Transpose 8-bit elements and concatenate result rows as follows: - // a0: 00, 01, 02, 03, 04, 05, 06, 07 - // a1: 10, 11, 12, 13, 14, 15, 16, 17 - // a2: 20, 21, 22, 23, 24, 25, 26, 27 - // a3: 30, 31, 32, 33, 34, 35, 36, 37 - // - // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 - // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 - - uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); - uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); - uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); - uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); - - uint8x16_t a01 = vzipq_u8(a0q, a1q).val[0]; - uint8x16_t a23 = vzipq_u8(a2q, a3q).val[0]; - - uint16x8x2_t a0123 = - vzipq_u16(vreinterpretq_u16_u8(a01), vreinterpretq_u16_u8(a23)); - - *b0 = vreinterpretq_u8_u16(a0123.val[0]); - *b1 = vreinterpretq_u8_u16(a0123.val[1]); + x_filter_ptr, horiz_const_s16); } static inline int16x4_t convolve12_4_y(const uint8x16_t s0, const uint8x16_t s1, @@ -413,7 +389,7 @@ sum = vusdotq_lane_s32(sum, s2, filters_4_11, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vshrn_n_s32(sum, 1); } static inline uint8x8_t convolve12_8_y( @@ -429,8 +405,9 @@ sum4567 = vusdotq_lane_s32(sum4567, s2_hi, filters_4_11, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0123), vqmovn_s32(sum4567)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = + vcombine_s16(vshrn_n_s32(sum0123, 1), vshrn_n_s32(sum4567, 1)); + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve_y_sr_12tap_neon_i8mm(const uint8_t *src_ptr, @@ -455,21 +432,21 @@ // This operation combines a conventional transpose and the sample permute // (see horizontal case) required before computing the dot product. uint8x16_t s0123, s1234, s2345, s3456, s4567, s5678, s6789, s789A; - transpose_concat_4x4(s0, s1, s2, s3, &s0123); - transpose_concat_4x4(s1, s2, s3, s4, &s1234); - transpose_concat_4x4(s2, s3, s4, s5, &s2345); - transpose_concat_4x4(s3, s4, s5, s6, &s3456); - transpose_concat_4x4(s4, s5, s6, s7, &s4567); - transpose_concat_4x4(s5, s6, s7, s8, &s5678); - transpose_concat_4x4(s6, s7, s8, s9, &s6789); - transpose_concat_4x4(s7, s8, s9, sA, &s789A); + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_u8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_u8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_u8_4x4(s3, s4, s5, s6, &s3456); + transpose_concat_elems_u8_4x4(s4, s5, s6, s7, &s4567); + transpose_concat_elems_u8_4x4(s5, s6, s7, s8, &s5678); + transpose_concat_elems_u8_4x4(s6, s7, s8, s9, &s6789); + transpose_concat_elems_u8_4x4(s7, s8, s9, sA, &s789A); do { uint8x8_t sB, sC, sD, sE; load_u8_8x4(src_ptr, src_stride, &sB, &sC, &sD, &sE); uint8x16_t s89AB, s9ABC, sABCD, sBCDE; - transpose_concat_4x4(sB, sC, sD, sE, &sBCDE); + transpose_concat_elems_u8_4x4(sB, sC, sD, sE, &sBCDE); // Merge new data into block from previous iteration. uint8x16x2_t samples_LUT = { { s789A, sBCDE } }; @@ -485,8 +462,8 @@ convolve12_4_y(s2345, s6789, sABCD, filter_0_7, filter_4_11); int16x4_t d3 = convolve12_4_y(s3456, s789A, sBCDE, filter_0_7, filter_4_11); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, d23); @@ -523,14 +500,14 @@ uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, s3456_lo, s3456_hi, s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, s789A_lo, s789A_hi; - transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); - transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); - transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); - transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); - transpose_concat_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); - transpose_concat_8x4(s5, s6, s7, s8, &s5678_lo, &s5678_hi); - transpose_concat_8x4(s6, s7, s8, s9, &s6789_lo, &s6789_hi); - transpose_concat_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_u8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_u8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_u8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_elems_u8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + transpose_concat_elems_u8_8x4(s5, s6, s7, s8, &s5678_lo, &s5678_hi); + transpose_concat_elems_u8_8x4(s6, s7, s8, s9, &s6789_lo, &s6789_hi); + transpose_concat_elems_u8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); do { uint8x8_t sB, sC, sD, sE; @@ -538,7 +515,7 @@ uint8x16_t s89AB_lo, s89AB_hi, s9ABC_lo, s9ABC_hi, sABCD_lo, sABCD_hi, sBCDE_lo, sBCDE_hi; - transpose_concat_8x4(sB, sC, sD, sE, &sBCDE_lo, &sBCDE_hi); + transpose_concat_elems_u8_8x4(sB, sC, sD, sE, &sBCDE_lo, &sBCDE_hi); // Merge new data into block from previous iteration. uint8x16x2_t samples_LUT_lo = { { s789A_lo, sBCDE_lo } }; @@ -602,7 +579,7 @@ sum = vusdotq_lane_s32(sum, s1, filters, 1); // Further narrowing and packing is performed by the caller. - return vqmovn_s32(sum); + return vmovn_s32(sum); } static inline uint8x8_t convolve8_8_y(const uint8x16_t s0_lo, @@ -617,8 +594,9 @@ sum4567 = vusdotq_lane_s32(sum4567, s1_hi, filters, 1); // Narrow and re-pack. - int16x8_t sum = vcombine_s16(vqmovn_s32(sum0123), vqmovn_s32(sum4567)); - return vqrshrun_n_s16(sum, FILTER_BITS); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); } static inline void convolve_y_sr_8tap_neon_i8mm(const uint8_t *src_ptr, @@ -626,7 +604,8 @@ uint8_t *dst_ptr, int dst_stride, int w, int h, const int16_t *y_filter_ptr) { - const int8x8_t filter = vmovn_s16(vld1q_s16(y_filter_ptr)); + // Filter values are even, so halve to reduce intermediate precision reqs. + const int8x8_t filter = vshrn_n_s16(vld1q_s16(y_filter_ptr), 1); const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); @@ -638,20 +617,20 @@ // This operation combines a conventional transpose and the sample permute // (see horizontal case) required before computing the dot product. uint8x16_t s0123, s1234, s2345, s3456; - transpose_concat_4x4(s0, s1, s2, s3, &s0123); - transpose_concat_4x4(s1, s2, s3, s4, &s1234); - transpose_concat_4x4(s2, s3, s4, s5, &s2345); - transpose_concat_4x4(s3, s4, s5, s6, &s3456); + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + transpose_concat_elems_u8_4x4(s1, s2, s3, s4, &s1234); + transpose_concat_elems_u8_4x4(s2, s3, s4, s5, &s2345); + transpose_concat_elems_u8_4x4(s3, s4, s5, s6, &s3456); do { - uint8x8_t s7, s8, s9, s10; - load_u8_8x4(src_ptr, src_stride, &s7, &s8, &s9, &s10); + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(src_ptr, src_stride, &s7, &s8, &s9, &sA); - uint8x16_t s4567, s5678, s6789, s78910; - transpose_concat_4x4(s7, s8, s9, s10, &s78910); + uint8x16_t s4567, s5678, s6789, s789A; + transpose_concat_elems_u8_4x4(s7, s8, s9, sA, &s789A); // Merge new data into block from previous iteration. - uint8x16x2_t samples_LUT = { { s3456, s78910 } }; + uint8x16x2_t samples_LUT = { { s3456, s789A } }; s4567 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); s5678 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); s6789 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); @@ -659,9 +638,10 @@ int16x4_t d0 = convolve8_4_y(s0123, s4567, filter); int16x4_t d1 = convolve8_4_y(s1234, s5678, filter); int16x4_t d2 = convolve8_4_y(s2345, s6789, filter); - int16x4_t d3 = convolve8_4_y(s3456, s78910, filter); - uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS); - uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS); + int16x4_t d3 = convolve8_4_y(s3456, s789A, filter); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); store_u8x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, d01); store_u8x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, d23); @@ -671,7 +651,7 @@ s0123 = s4567; s1234 = s5678; s2345 = s6789; - s3456 = s78910; + s3456 = s789A; src_ptr += 4 * src_stride; dst_ptr += 4 * dst_stride; @@ -692,26 +672,26 @@ // product. uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, s3456_lo, s3456_hi; - transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); - transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); - transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); - transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_elems_u8_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_elems_u8_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_elems_u8_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); do { - uint8x8_t s7, s8, s9, s10; - load_u8_8x4(s, src_stride, &s7, &s8, &s9, &s10); + uint8x8_t s7, s8, s9, sA; + load_u8_8x4(s, src_stride, &s7, &s8, &s9, &sA); uint8x16_t s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, s6789_hi, - s78910_lo, s78910_hi; - transpose_concat_8x4(s7, s8, s9, s10, &s78910_lo, &s78910_hi); + s789A_lo, s789A_hi; + transpose_concat_elems_u8_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); // Merge new data into block from previous iteration. - uint8x16x2_t samples_LUT_lo = { { s3456_lo, s78910_lo } }; + uint8x16x2_t samples_LUT_lo = { { s3456_lo, s789A_lo } }; s4567_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[0]); s5678_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[1]); s6789_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[2]); - uint8x16x2_t samples_LUT_hi = { { s3456_hi, s78910_hi } }; + uint8x16x2_t samples_LUT_hi = { { s3456_hi, s789A_hi } }; s4567_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[0]); s5678_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[1]); s6789_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[2]); @@ -723,7 +703,7 @@ uint8x8_t d2 = convolve8_8_y(s2345_lo, s2345_hi, s6789_lo, s6789_hi, filter); uint8x8_t d3 = - convolve8_8_y(s3456_lo, s3456_hi, s78910_lo, s78910_hi, filter); + convolve8_8_y(s3456_lo, s3456_hi, s789A_lo, s789A_hi, filter); store_u8_8x4(d, dst_stride, d0, d1, d2, d3); @@ -735,8 +715,140 @@ s1234_hi = s5678_hi; s2345_lo = s6789_lo; s2345_hi = s6789_hi; - s3456_lo = s78910_lo; - s3456_hi = s78910_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 8; + dst_ptr += 8; + w -= 8; + } while (w != 0); + } +} + +static inline int16x4_t convolve4_4_y(const uint8x16_t s0, + const int8x8_t filters) { + int32x4_t sum = vusdotq_lane_s32(vdupq_n_s32(0), s0, filters, 0); + + // Further narrowing and packing is performed by the caller. + return vmovn_s32(sum); +} + +static inline uint8x8_t convolve4_8_y(const uint8x16_t s0, const uint8x16_t s1, + const int8x8_t filters) { + int32x4_t sum0123 = vusdotq_lane_s32(vdupq_n_s32(0), s0, filters, 0); + int32x4_t sum4567 = vusdotq_lane_s32(vdupq_n_s32(0), s1, filters, 0); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); + // We halved the filter values so -1 from right shift. + return vqrshrun_n_s16(sum, FILTER_BITS - 1); +} + +static inline void convolve_y_sr_4tap_neon_i8mm(const uint8_t *src_ptr, + int src_stride, + uint8_t *dst_ptr, + int dst_stride, int w, int h, + const int16_t *y_filter_ptr) { + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16x8_t filter_s16 = + vcombine_s16(vld1_s16(y_filter_ptr + 2), vdup_n_s16(0)); + const int8x8_t filter = vshrn_n_s16(filter_s16, 1); + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + uint8x16x2_t samples_LUT; + + if (w == 4) { + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(src_ptr, src_stride, &s0, &s1, &s2, &s3); + src_ptr += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123; + transpose_concat_elems_u8_4x4(s0, s1, s2, s3, &s0123); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(src_ptr, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567; + transpose_concat_elems_u8_4x4(s4, s5, s6, s7, &s4567); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123; + samples_LUT.val[1] = s4567; + uint8x16_t s1234 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456 = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + int16x4_t d0 = convolve4_4_y(s0123, filter); + int16x4_t d1 = convolve4_4_y(s1234, filter); + int16x4_t d2 = convolve4_4_y(s2345, filter); + int16x4_t d3 = convolve4_4_y(s3456, filter); + // We halved the filter values so -1 from right shift. + uint8x8_t d01 = vqrshrun_n_s16(vcombine_s16(d0, d1), FILTER_BITS - 1); + uint8x8_t d23 = vqrshrun_n_s16(vcombine_s16(d2, d3), FILTER_BITS - 1); + + store_u8x4_strided_x2(dst_ptr + 0 * dst_stride, dst_stride, d01); + store_u8x4_strided_x2(dst_ptr + 2 * dst_stride, dst_stride, d23); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123 = s4567; + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const uint8_t *s = src_ptr; + uint8_t *d = dst_ptr; + + uint8x8_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3); + s += 4 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // required before computing the dot product. + uint8x16_t s0123_lo, s0123_hi; + transpose_concat_elems_u8_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + + do { + uint8x8_t s4, s5, s6, s7; + load_u8_8x4(s, src_stride, &s4, &s5, &s6, &s7); + + uint8x16_t s4567_lo, s4567_hi; + transpose_concat_elems_u8_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + + // Merge new data into block from previous iteration. + samples_LUT.val[0] = s0123_lo; + samples_LUT.val[1] = s4567_lo; + uint8x16_t s1234_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_lo = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + samples_LUT.val[0] = s0123_hi; + samples_LUT.val[1] = s4567_hi; + uint8x16_t s1234_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[0]); + uint8x16_t s2345_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[1]); + uint8x16_t s3456_hi = vqtbl2q_u8(samples_LUT, merge_block_tbl.val[2]); + + uint8x8_t d0 = convolve4_8_y(s0123_lo, s0123_hi, filter); + uint8x8_t d1 = convolve4_8_y(s1234_lo, s1234_hi, filter); + uint8x8_t d2 = convolve4_8_y(s2345_lo, s2345_hi, filter); + uint8x8_t d3 = convolve4_8_y(s3456_lo, s3456_hi, filter); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; s += 4 * src_stride; d += 4 * dst_stride; @@ -760,65 +872,70 @@ } const int y_filter_taps = get_filter_tap(filter_params_y, subpel_y_qn); - - if (y_filter_taps <= 6) { - av1_convolve_y_sr_neon(src, src_stride, dst, dst_stride, w, h, - filter_params_y, subpel_y_qn); - return; - } - - const int vert_offset = y_filter_taps / 2 - 1; - src -= vert_offset * src_stride; - const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( filter_params_y, subpel_y_qn & SUBPEL_MASK); - if (y_filter_taps > 8) { - convolve_y_sr_12tap_neon_i8mm(src, src_stride, dst, dst_stride, w, h, - y_filter_ptr); - return; + if (y_filter_taps <= 4) { + convolve_y_sr_4tap_neon_i8mm(src - src_stride, src_stride, dst, dst_stride, + w, h, y_filter_ptr); + } else if (y_filter_taps == 12) { + convolve_y_sr_12tap_neon_i8mm(src - 5 * src_stride, src_stride, dst, + dst_stride, w, h, y_filter_ptr); + } else { + // 6-tap or 8-tap. + convolve_y_sr_8tap_neon_i8mm(src - 3 * src_stride, src_stride, dst, + dst_stride, w, h, y_filter_ptr); } - convolve_y_sr_8tap_neon_i8mm(src, src_stride, dst, dst_stride, w, h, - y_filter_ptr); } static inline int16x8_t convolve8_8_2d_h(uint8x16_t samples, - const int8x8_t filters, - const uint8x16x3_t permute_tbl, - const int32x4_t horiz_const) { - // Permute samples ready for dot product. - // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } - // { 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 } - // { 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 } - uint8x16_t perm_samples[3] = { vqtbl1q_u8(samples, permute_tbl.val[0]), - vqtbl1q_u8(samples, permute_tbl.val[1]), - vqtbl1q_u8(samples, permute_tbl.val[2]) }; + const int8x16_t x_filter, + const uint8x8_t f0, + const uint8x16x2_t permute_tbl, + const int16x8_t horiz_const) { + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(samples, permute_tbl.val[0]), + vqtbl1q_u8(samples, permute_tbl.val[1]) }; - int32x4_t sum0123 = - vusdotq_lane_s32(horiz_const, perm_samples[0], filters, 0); - sum0123 = vusdotq_lane_s32(sum0123, perm_samples[1], filters, 1); + // Calculate partial 7-tap convolution. + int32x4_t sum0123 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[0], x_filter); + int32x4_t sum4567 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[1], x_filter); + int16x8_t sum = vcombine_s16(vmovn_s32(sum0123), vmovn_s32(sum4567)); - int32x4_t sum4567 = - vusdotq_lane_s32(horiz_const, perm_samples[1], filters, 0); - sum4567 = vusdotq_lane_s32(sum4567, perm_samples[2], filters, 1); + // Apply tap 0 and accumulate. + sum = vreinterpretq_s16_u16( + vmlsl_u8(vreinterpretq_u16_s16(sum), vget_low_u8(samples), f0)); - // Narrow and re-pack. + sum = vaddq_s16(sum, horiz_const); + // We halved the convolution filter values so -1 from the right shift. - return vcombine_s16(vshrn_n_s32(sum0123, ROUND0_BITS - 1), - vshrn_n_s32(sum4567, ROUND0_BITS - 1)); + return vshrq_n_s16(sum, ROUND0_BITS - 1); } static inline void convolve_2d_sr_horiz_8tap_neon_i8mm( const uint8_t *src, int src_stride, int16_t *im_block, int im_stride, int w, int im_h, const int16_t *x_filter_ptr) { // Filter values are even, so halve to reduce intermediate precision reqs. - const int8x8_t x_filter = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + const int8x8_t x_filter_s8 = vshrn_n_s16(vld1q_s16(x_filter_ptr), 1); + + // Stagger the filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const uint8x16_t filter_idx = vld1q_u8(kFilterPermuteTbl); + const int8x16_t x_filter = + vqtbl1q_s8(vcombine_s8(x_filter_s8, vdup_n_s8(0)), filter_idx); + + // Since f0 is always negative and s0 is unsigned, subtract (unsigned) s0 * + // -f0 to avoid signed overflow. + const uint8x8_t f0 = vdup_n_u8(-x_filter_ptr[0] >> 1); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); const int bd = 8; // This shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding // shifts - which are generally faster than rounding shifts on modern CPUs. // The outermost -1 is needed because we halved the filter values. - const int32x4_t horiz_const = vdupq_n_s32((1 << (bd + FILTER_BITS - 2)) + + const int16x8_t horiz_const = vdupq_n_s16((1 << (bd + FILTER_BITS - 2)) + (1 << ((ROUND0_BITS - 1) - 1))); const uint8_t *src_ptr = src; @@ -826,7 +943,6 @@ int dst_stride = im_stride; int height = im_h; - const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); do { const uint8_t *s = src_ptr; int16_t *d = dst_ptr; @@ -836,10 +952,14 @@ uint8x16_t s0, s1, s2, s3; load_u8_16x4(s, src_stride, &s0, &s1, &s2, &s3); - int16x8_t d0 = convolve8_8_2d_h(s0, x_filter, permute_tbl, horiz_const); - int16x8_t d1 = convolve8_8_2d_h(s1, x_filter, permute_tbl, horiz_const); - int16x8_t d2 = convolve8_8_2d_h(s2, x_filter, permute_tbl, horiz_const); - int16x8_t d3 = convolve8_8_2d_h(s3, x_filter, permute_tbl, horiz_const); + int16x8_t d0 = + convolve8_8_2d_h(s0, x_filter, f0, permute_tbl, horiz_const); + int16x8_t d1 = + convolve8_8_2d_h(s1, x_filter, f0, permute_tbl, horiz_const); + int16x8_t d2 = + convolve8_8_2d_h(s2, x_filter, f0, permute_tbl, horiz_const); + int16x8_t d3 = + convolve8_8_2d_h(s3, x_filter, f0, permute_tbl, horiz_const); store_s16_8x4(d, dst_stride, d0, d1, d2, d3); @@ -859,7 +979,8 @@ do { uint8x16_t s0 = vld1q_u8(s); - int16x8_t d0 = convolve8_8_2d_h(s0, x_filter, permute_tbl, horiz_const); + int16x8_t d0 = + convolve8_8_2d_h(s0, x_filter, f0, permute_tbl, horiz_const); vst1q_s16(d, d0); s += 8; @@ -1052,7 +1173,7 @@ const int32x4_t horiz_const = vdupq_n_s32((1 << (bd + FILTER_BITS - 2)) + (1 << ((ROUND0_BITS - 1) - 1))); const int16x8_t vert_const = vdupq_n_s16(1 << (bd - 1)); - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src; @@ -1129,7 +1250,7 @@ const int16x8_t vert_const = vdupq_n_s16(1 << (bd - 1)); if (w == 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); uint8x16_t h_s0, h_s1, h_s2; load_u8_16x3(src, src_stride, &h_s0, &h_s1, &h_s2); @@ -1172,7 +1293,7 @@ h -= 4; } while (h != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { int height = h;
diff --git a/av1/common/arm/convolve_neon_i8mm.h b/av1/common/arm/convolve_neon_i8mm.h index 71b7461..38e651f 100644 --- a/av1/common/arm/convolve_neon_i8mm.h +++ b/av1/common/arm/convolve_neon_i8mm.h
@@ -23,18 +23,9 @@ #include "aom_dsp/arm/mem_neon.h" #include "aom_ports/mem.h" -DECLARE_ALIGNED(16, static const uint8_t, kDotProdPermuteTbl[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 -}; - -DECLARE_ALIGNED(16, static const uint8_t, kMatMulPermuteTbl[32]) = { - // clang-format off - 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9, - 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 - // clang-format on -}; +DECLARE_ALIGNED(16, extern const uint8_t, kMatMul6PermuteTbl[32]); +DECLARE_ALIGNED(16, extern const uint8_t, kMatMul8PermuteTbl[32]); +DECLARE_ALIGNED(16, extern const uint8_t, kFilterPermuteTbl[16]); static inline int16x4_t convolve12_4_2d_h(uint8x16_t samples[2], const int8x16_t filter[2], @@ -110,7 +101,7 @@ vdupq_n_s32((1 << (bd + FILTER_BITS - 1)) + (1 << (ROUND0_BITS - 1))); if (w <= 4) { - const uint8x16_t permute_tbl = vld1q_u8(kMatMulPermuteTbl); + const uint8x16_t permute_tbl = vld1q_u8(kMatMul6PermuteTbl); do { uint8x16_t s0[2], s1[2], s2[2], s3[2]; @@ -141,7 +132,7 @@ } while (--h != 0); } else { - const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); do { const uint8_t *s = src_ptr;
diff --git a/av1/common/arm/convolve_sve2.c b/av1/common/arm/convolve_sve2.c index 536f441..ae00386 100644 --- a/av1/common/arm/convolve_sve2.c +++ b/av1/common/arm/convolve_sve2.c
@@ -23,9 +23,20 @@ #include "aom_dsp/arm/mem_neon.h" #include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" -#include "av1/common/arm/highbd_convolve_sve2.h" +#include "av1/common/arm/convolve_sve2.h" #include "av1/common/arm/convolve_neon_i8mm.h" +// clang-format off +DECLARE_ALIGNED(16, const uint16_t, kSVEDotProdMergeBlockTbl[24]) = { + // Shift left and insert new last column in transposed 4x4 block. + 1, 2, 3, 0, 5, 6, 7, 4, + // Shift left and insert two new columns in transposed 4x4 block. + 2, 3, 0, 1, 6, 7, 4, 5, + // Shift left and insert three new columns in transposed 4x4 block. + 3, 0, 1, 2, 7, 4, 5, 6, +}; +// clang-format on + static inline int32x4_t highbd_convolve12_4_2d_v(int16x8_t s0[2], int16x8_t s1[2], int16x8_t s2[2], @@ -52,7 +63,7 @@ const int bd = 8; const int16x8_t sub_const = vdupq_n_s16(1 << (bd - 1)); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -81,21 +92,21 @@ s6789[2], s789A[2]; // This operation combines a conventional transpose and the sample permute // required before computing the dot product. - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); - transpose_concat_4x4(s4, s5, s6, s7, s4567); - transpose_concat_4x4(s5, s6, s7, s8, s5678); - transpose_concat_4x4(s6, s7, s8, s9, s6789); - transpose_concat_4x4(s7, s8, s9, sA, s789A); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s4, s5, s6, s7, s4567); + transpose_concat_elems_s16_4x4(s5, s6, s7, s8, s5678); + transpose_concat_elems_s16_4x4(s6, s7, s8, s9, s6789); + transpose_concat_elems_s16_4x4(s7, s8, s9, sA, s789A); do { int16x4_t sB, sC, sD, sE; load_s16_4x4(s, src_stride, &sB, &sC, &sD, &sE); int16x8_t s89AB[2], s9ABC[2], sABCD[2], sBCDE[2]; - transpose_concat_4x4(sB, sC, sD, sE, sBCDE); + transpose_concat_elems_s16_4x4(sB, sC, sD, sE, sBCDE); // Merge new data into block from previous iteration. aom_tbl2x2_s16(s789A, sBCDE, merge_block_tbl.val[0], s89AB);
diff --git a/av1/common/arm/convolve_sve2.h b/av1/common/arm/convolve_sve2.h new file mode 100644 index 0000000..bd02153 --- /dev/null +++ b/av1/common/arm/convolve_sve2.h
@@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025, 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. + */ + +#ifndef AOM_AV1_COMMON_ARM_CONVOLVE_SVE2_H_ +#define AOM_AV1_COMMON_ARM_CONVOLVE_SVE2_H_ + +#include "aom_dsp/arm/aom_neon_sve2_bridge.h" +#include "aom_ports/mem.h" + +DECLARE_ALIGNED(16, extern const uint16_t, kSVEDotProdMergeBlockTbl[24]); + +static inline void aom_tbl2x2_s16(int16x8_t t0[2], int16x8_t t1[2], + uint16x8_t tbl, int16x8_t res[2]) { + res[0] = aom_tbl2_s16(t0[0], t1[0], tbl); + res[1] = aom_tbl2_s16(t0[1], t1[1], tbl); +} + +#endif // AOM_AV1_COMMON_ARM_CONVOLVE_SVE2_H_
diff --git a/av1/common/arm/highbd_compound_convolve_sve2.c b/av1/common/arm/highbd_compound_convolve_sve2.c index 668dfbf..e5fbe41 100644 --- a/av1/common/arm/highbd_compound_convolve_sve2.c +++ b/av1/common/arm/highbd_compound_convolve_sve2.c
@@ -18,20 +18,17 @@ #include "aom_dsp/aom_dsp_common.h" #include "aom_dsp/arm/aom_neon_sve_bridge.h" #include "aom_dsp/arm/aom_neon_sve2_bridge.h" +#include "aom_dsp/arm/highbd_convolve8_sve.h" #include "aom_dsp/arm/mem_neon.h" #include "aom_ports/mem.h" #include "av1/common/convolve.h" #include "av1/common/filter.h" #include "av1/common/filter.h" +#include "av1/common/arm/convolve_sve2.h" #include "av1/common/arm/highbd_compound_convolve_neon.h" #include "av1/common/arm/highbd_convolve_neon.h" #include "av1/common/arm/highbd_convolve_sve2.h" -DECLARE_ALIGNED(16, static const uint16_t, kDotProdTbl[32]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 0, 6, 7, 0, 1, 7, 0, 1, 2, -}; - static inline uint16x8_t highbd_12_convolve8_8_x(int16x8_t s0[8], int16x8_t filter, int64x2_t offset) { @@ -165,12 +162,6 @@ } while (height != 0); } -// clang-format off -DECLARE_ALIGNED(16, static const uint16_t, kDeinterleaveTbl[8]) = { - 0, 2, 4, 6, 1, 3, 5, 7, -}; -// clang-format on - static inline uint16x4_t highbd_12_convolve4_4_x(int16x8_t s0, int16x8_t filter, int64x2_t offset, uint16x8x2_t permute_tbl) { @@ -212,7 +203,7 @@ const int16x8_t filter = vcombine_s16(x_filter, vdup_n_s16(0)); if (width == 4) { - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); const int16_t *s = (const int16_t *)(src); @@ -304,7 +295,7 @@ const int16x8_t filter = vcombine_s16(x_filter, vdup_n_s16(0)); if (width == 4) { - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); const int16_t *s = (const int16_t *)(src); @@ -483,7 +474,7 @@ vdupq_n_s64((1 << (12 + FILTER_BITS)) + (1 << (12 + FILTER_BITS - 1))); const int16x8_t y_filter = vld1q_s16(y_filter_ptr); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -506,10 +497,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); do { int16x4_t s7, s8, s9, s10; @@ -517,7 +508,7 @@ int16x8_t s4567[2], s5678[2], s6789[2], s789A[2]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_4x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_4x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x2_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -559,10 +550,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); do { int16x8_t s7, s8, s9, s10; @@ -570,7 +561,7 @@ int16x8_t s4567[4], s5678[4], s6789[4], s789A[4]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_8x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_8x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x4_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -659,7 +650,7 @@ vdupq_n_s64((1 << (bd + FILTER_BITS)) + (1 << (bd + FILTER_BITS - 1))); const int16x8_t y_filter = vld1q_s16(y_filter_ptr); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -682,10 +673,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); do { int16x4_t s7, s8, s9, s10; @@ -693,7 +684,7 @@ int16x8_t s4567[2], s5678[2], s6789[2], s789A[2]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_4x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_4x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x2_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -735,10 +726,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); do { int16x8_t s7, s8, s9, s10; @@ -746,7 +737,7 @@ int16x8_t s4567[4], s5678[4], s6789[4], s789A[4]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_8x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_8x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x4_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -989,7 +980,7 @@ // a time and then process the last 3 rows separately. if (width == 4) { - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); const int16_t *s = (const int16_t *)(src); @@ -1084,7 +1075,7 @@ // a time and then process the last 3 rows separately. if (width == 4) { - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); const int16_t *s = (const int16_t *)(src); @@ -1210,7 +1201,7 @@ const int16x8_t y_filter = vld1q_s16(y_filter_ptr); const int64x2_t offset_s64 = vdupq_n_s64(offset); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -1234,10 +1225,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); do { int16x4_t s7, s8, s9, s10; @@ -1245,7 +1236,7 @@ int16x8_t s4567[2], s5678[2], s6789[2], s789A[2]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_4x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_4x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x2_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -1291,10 +1282,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); do { int16x8_t s7, s8, s9, s10; @@ -1302,7 +1293,7 @@ int16x8_t s4567[4], s5678[4], s6789[4], s789A[4]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_8x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_8x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x4_s16(s3456, s789A, merge_block_tbl.val[0], s4567);
diff --git a/av1/common/arm/highbd_convolve_sve2.c b/av1/common/arm/highbd_convolve_sve2.c index fcf9d7b..656d8d5 100644 --- a/av1/common/arm/highbd_convolve_sve2.c +++ b/av1/common/arm/highbd_convolve_sve2.c
@@ -18,17 +18,15 @@ #include "aom_dsp/aom_dsp_common.h" #include "aom_dsp/arm/aom_neon_sve_bridge.h" #include "aom_dsp/arm/aom_neon_sve2_bridge.h" +#include "aom_dsp/arm/highbd_convolve8_sve.h" #include "aom_dsp/arm/mem_neon.h" +#include "aom_dsp/arm/transpose_neon.h" #include "aom_ports/mem.h" #include "av1/common/convolve.h" #include "av1/common/filter.h" +#include "av1/common/arm/convolve_sve2.h" #include "av1/common/arm/highbd_convolve_sve2.h" -DECLARE_ALIGNED(16, static const uint16_t, kDotProdTbl[32]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 0, 6, 7, 0, 1, 7, 0, 1, 2, -}; - static inline uint16x4_t convolve12_4_x( int16x8_t s0, int16x8_t s1, int16x8_t filter_0_7, int16x8_t filter_4_11, const int64x2_t offset, uint16x8x4_t permute_tbl, uint16x4_t max) { @@ -110,7 +108,7 @@ const int16x8_t y_filter_0_7 = vld1q_s16(y_filter_ptr); const int16x8_t y_filter_4_11 = vld1q_s16(y_filter_ptr + 4); - uint16x8x4_t permute_tbl = vld1q_u16_x4(kDotProdTbl); + uint16x8x4_t permute_tbl = vld1q_u16_x4(kHbdDotProdTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = vreinterpretq_u16_u64(vcombine_u64( @@ -252,12 +250,6 @@ } while (height != 0); } -// clang-format off -DECLARE_ALIGNED(16, static const uint16_t, kDeinterleaveTbl[8]) = { - 0, 2, 4, 6, 1, 3, 5, 7, -}; -// clang-format on - static inline uint16x4_t convolve4_4_x(int16x8_t s0, int16x8_t filter, int64x2_t offset, uint16x8x2_t permute_tbl, @@ -304,7 +296,7 @@ if (width == 4) { const uint16x4_t max = vdup_n_u16((1 << bd) - 1); - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); const int16_t *s = (const int16_t *)(src); @@ -427,21 +419,6 @@ const int16x8_t y_filter_0_7 = vld1q_s16(y_filter_ptr); const int16x8_t y_filter_4_11 = vld1q_s16(y_filter_ptr + 4); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); - // Scale indices by size of the true vector length to avoid reading from an - // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. - uint16x8_t correction0 = - vreinterpretq_u16_u64(vdupq_n_u64(svcnth() * 0x0001000000000000ULL)); - merge_block_tbl.val[0] = vaddq_u16(merge_block_tbl.val[0], correction0); - - uint16x8_t correction1 = - vreinterpretq_u16_u64(vdupq_n_u64(svcnth() * 0x0001000100000000ULL)); - merge_block_tbl.val[1] = vaddq_u16(merge_block_tbl.val[1], correction1); - - uint16x8_t correction2 = - vreinterpretq_u16_u64(vdupq_n_u64(svcnth() * 0x0001000100010000ULL)); - merge_block_tbl.val[2] = vaddq_u16(merge_block_tbl.val[2], correction2); - const uint16x4_t max = vdup_n_u16((1 << bd) - 1); do { @@ -456,27 +433,24 @@ int16x8_t s0123[2], s1234[2], s2345[2], s3456[2], s4567[2], s5678[2], s6789[2], s789A[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); - transpose_concat_4x4(s4, s5, s6, s7, s4567); - transpose_concat_4x4(s5, s6, s7, s8, s5678); - transpose_concat_4x4(s6, s7, s8, s9, s6789); - transpose_concat_4x4(s7, s8, s9, sA, s789A); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s4, s5, s6, s7, s4567); + transpose_concat_elems_s16_4x4(s5, s6, s7, s8, s5678); + transpose_concat_elems_s16_4x4(s6, s7, s8, s9, s6789); + transpose_concat_elems_s16_4x4(s7, s8, s9, sA, s789A); do { int16x4_t sB, sC, sD, sE; load_s16_4x4(s, src_stride, &sB, &sC, &sD, &sE); int16x8_t s89AB[2], s9ABC[2], sABCD[2], sBCDE[2]; - transpose_concat_4x4(sB, sC, sD, sE, sBCDE); - - // Use the above transpose and reuse data from the previous loop to get - // the rest. - aom_tbl2x2_s16(s789A, sBCDE, merge_block_tbl.val[0], s89AB); - aom_tbl2x2_s16(s789A, sBCDE, merge_block_tbl.val[1], s9ABC); - aom_tbl2x2_s16(s789A, sBCDE, merge_block_tbl.val[2], sABCD); + transpose_concat_elems_s16_4x4(s8, s9, sA, sB, s89AB); + transpose_concat_elems_s16_4x4(s9, sA, sB, sC, s9ABC); + transpose_concat_elems_s16_4x4(sA, sB, sC, sD, sABCD); + transpose_concat_elems_s16_4x4(sB, sC, sD, sE, sBCDE); uint16x4_t d0 = highbd_convolve12_4_y(s0123, s4567, s89AB, y_filter_0_7, y_filter_4_11, max); @@ -508,6 +482,10 @@ s789A[0] = sBCDE[0]; s789A[1] = sBCDE[1]; + s8 = sC; + s9 = sD; + sA = sE; + s += 4 * src_stride; d += 4 * dst_stride; h -= 4; @@ -571,7 +549,7 @@ const int16x8_t y_filter = vld1q_s16(filter_y); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -597,10 +575,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); do { int16x4_t s7, s8, s9, s10; @@ -608,7 +586,7 @@ int16x8_t s4567[2], s5678[2], s6789[2], s789A[2]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_4x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_4x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x2_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -651,10 +629,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); do { int16x8_t s7, s8, s9, s10; @@ -662,7 +640,7 @@ int16x8_t s4567[4], s5678[4], s6789[4], s789A[4]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_8x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_8x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x4_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -757,10 +735,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); uint16x4_t d0 = highbd_convolve4_4_y(s0123, y_filter, max); uint16x4_t d1 = highbd_convolve4_4_y(s1234, y_filter, max); @@ -797,10 +775,10 @@ // This operation combines a conventional transpose and the sample // permute required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); uint16x8_t d0 = highbd_convolve4_8_y(s0123, y_filter, max); uint16x8_t d1 = highbd_convolve4_8_y(s1234, y_filter, max); @@ -944,7 +922,7 @@ const int16x8_t y_filter_0_7 = vld1q_s16(y_filter_ptr); const int16x8_t y_filter_4_11 = vld1q_s16(y_filter_ptr + 4); - uint16x8x4_t permute_tbl = vld1q_u16_x4(kDotProdTbl); + uint16x8x4_t permute_tbl = vld1q_u16_x4(kHbdDotProdTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = vreinterpretq_u16_u64(vcombine_u64( @@ -1131,7 +1109,7 @@ if (width == 4) { const int16_t *s = (const int16_t *)(src); - uint16x8x2_t permute_tbl = vld1q_u16_x2(kDotProdTbl); + uint16x8x2_t permute_tbl = vld1q_u16_x2(kHbdDotProdTbl); do { int16x8_t s0, s1, s2, s3; @@ -1214,7 +1192,7 @@ const int16x8_t y_filter_0_7 = vld1q_s16(y_filter_ptr); const int16x8_t y_filter_4_11 = vld1q_s16(y_filter_ptr + 4); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -1245,21 +1223,21 @@ s6789[2], s789A[2]; // This operation combines a conventional transpose and the sample permute // required before computing the dot product. - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); - transpose_concat_4x4(s4, s5, s6, s7, s4567); - transpose_concat_4x4(s5, s6, s7, s8, s5678); - transpose_concat_4x4(s6, s7, s8, s9, s6789); - transpose_concat_4x4(s7, s8, s9, sA, s789A); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s4, s5, s6, s7, s4567); + transpose_concat_elems_s16_4x4(s5, s6, s7, s8, s5678); + transpose_concat_elems_s16_4x4(s6, s7, s8, s9, s6789); + transpose_concat_elems_s16_4x4(s7, s8, s9, sA, s789A); do { int16x4_t sB, sC, sD, sE; load_s16_4x4(s, src_stride, &sB, &sC, &sD, &sE); int16x8_t s89AB[2], s9ABC[2], sABCD[2], sBCDE[2]; - transpose_concat_4x4(sB, sC, sD, sE, sBCDE); + transpose_concat_elems_s16_4x4(sB, sC, sD, sE, sBCDE); // Use the above transpose and reuse data from the previous loop to get // the rest. @@ -1357,7 +1335,7 @@ const int32x4_t shift = vdupq_n_s32(-conv_params->round_1); const int16x8_t y_filter = vld1q_s16(filter_y); - uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kDotProdMergeBlockTbl); + uint16x8x3_t merge_block_tbl = vld1q_u16_x3(kSVEDotProdMergeBlockTbl); // Scale indices by size of the true vector length to avoid reading from an // 'undefined' portion of a vector on a system with SVE vectors > 128-bit. uint16x8_t correction0 = @@ -1383,10 +1361,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); do { int16x4_t s7, s8, s9, s10; @@ -1394,7 +1372,7 @@ int16x8_t s4567[2], s5678[2], s6789[2], s789A[2]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_4x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_4x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x2_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -1442,10 +1420,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); do { int16x8_t s7, s8, s9, s10; @@ -1453,7 +1431,7 @@ int16x8_t s4567[4], s5678[4], s6789[4], s789A[4]; // Transpose and shuffle the 4 lines that were loaded. - transpose_concat_8x4(s7, s8, s9, s10, s789A); + transpose_concat_elems_s16_8x4(s7, s8, s9, s10, s789A); // Merge new data into block from previous iteration. aom_tbl2x4_s16(s3456, s789A, merge_block_tbl.val[0], s4567); @@ -1562,10 +1540,10 @@ // This operation combines a conventional transpose and the sample permute // required before computing the dot product. int16x8_t s0123[2], s1234[2], s2345[2], s3456[2]; - transpose_concat_4x4(s0, s1, s2, s3, s0123); - transpose_concat_4x4(s1, s2, s3, s4, s1234); - transpose_concat_4x4(s2, s3, s4, s5, s2345); - transpose_concat_4x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_4x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_4x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_4x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_4x4(s3, s4, s5, s6, s3456); uint16x4_t d0 = highbd_convolve4_4_2d_v(s0123, y_filter, shift, offset, max); @@ -1606,10 +1584,10 @@ // This operation combines a conventional transpose and the sample // permute required before computing the dot product. int16x8_t s0123[4], s1234[4], s2345[4], s3456[4]; - transpose_concat_8x4(s0, s1, s2, s3, s0123); - transpose_concat_8x4(s1, s2, s3, s4, s1234); - transpose_concat_8x4(s2, s3, s4, s5, s2345); - transpose_concat_8x4(s3, s4, s5, s6, s3456); + transpose_concat_elems_s16_8x4(s0, s1, s2, s3, s0123); + transpose_concat_elems_s16_8x4(s1, s2, s3, s4, s1234); + transpose_concat_elems_s16_8x4(s2, s3, s4, s5, s2345); + transpose_concat_elems_s16_8x4(s3, s4, s5, s6, s3456); uint16x8_t d0 = highbd_convolve4_8_2d_v(s0123, y_filter, shift, offset, max);
diff --git a/av1/common/arm/highbd_convolve_sve2.h b/av1/common/arm/highbd_convolve_sve2.h index abbad14..0b59e26 100644 --- a/av1/common/arm/highbd_convolve_sve2.h +++ b/av1/common/arm/highbd_convolve_sve2.h
@@ -16,70 +16,6 @@ #include "aom_dsp/arm/aom_neon_sve2_bridge.h" -// clang-format off -DECLARE_ALIGNED(16, static const uint16_t, kDotProdMergeBlockTbl[24]) = { - // Shift left and insert new last column in transposed 4x4 block. - 1, 2, 3, 0, 5, 6, 7, 4, - // Shift left and insert two new columns in transposed 4x4 block. - 2, 3, 0, 1, 6, 7, 4, 5, - // Shift left and insert three new columns in transposed 4x4 block. - 3, 0, 1, 2, 7, 4, 5, 6, -}; -// clang-format on - -static inline void transpose_concat_4x4(int16x4_t s0, int16x4_t s1, - int16x4_t s2, int16x4_t s3, - int16x8_t res[2]) { - // Transpose 16-bit elements and concatenate result rows as follows: - // s0: 00, 01, 02, 03 - // s1: 10, 11, 12, 13 - // s2: 20, 21, 22, 23 - // s3: 30, 31, 32, 33 - // - // res[0]: 00 10 20 30 01 11 21 31 - // res[1]: 02 12 22 32 03 13 23 33 - - int16x8_t s0q = vcombine_s16(s0, vdup_n_s16(0)); - int16x8_t s1q = vcombine_s16(s1, vdup_n_s16(0)); - int16x8_t s2q = vcombine_s16(s2, vdup_n_s16(0)); - int16x8_t s3q = vcombine_s16(s3, vdup_n_s16(0)); - - int32x4_t s01 = vreinterpretq_s32_s16(vzip1q_s16(s0q, s1q)); - int32x4_t s23 = vreinterpretq_s32_s16(vzip1q_s16(s2q, s3q)); - - int32x4x2_t s0123 = vzipq_s32(s01, s23); - - res[0] = vreinterpretq_s16_s32(s0123.val[0]); - res[1] = vreinterpretq_s16_s32(s0123.val[1]); -} - -static inline void transpose_concat_8x4(int16x8_t s0, int16x8_t s1, - int16x8_t s2, int16x8_t s3, - int16x8_t res[4]) { - // Transpose 16-bit elements and concatenate result rows as follows: - // s0: 00, 01, 02, 03, 04, 05, 06, 07 - // s1: 10, 11, 12, 13, 14, 15, 16, 17 - // s2: 20, 21, 22, 23, 24, 25, 26, 27 - // s3: 30, 31, 32, 33, 34, 35, 36, 37 - // - // res[0]: 00 10 20 30 01 11 21 31 - // res[1]: 02 12 22 32 03 13 23 33 - // res[2]: 04 14 24 34 05 15 25 35 - // res[3]: 06 16 26 36 07 17 27 37 - - int16x8x2_t tr01_16 = vzipq_s16(s0, s1); - int16x8x2_t tr23_16 = vzipq_s16(s2, s3); - int32x4x2_t tr01_32 = vzipq_s32(vreinterpretq_s32_s16(tr01_16.val[0]), - vreinterpretq_s32_s16(tr23_16.val[0])); - int32x4x2_t tr23_32 = vzipq_s32(vreinterpretq_s32_s16(tr01_16.val[1]), - vreinterpretq_s32_s16(tr23_16.val[1])); - - res[0] = vreinterpretq_s16_s32(tr01_32.val[0]); - res[1] = vreinterpretq_s16_s32(tr01_32.val[1]); - res[2] = vreinterpretq_s16_s32(tr23_32.val[0]); - res[3] = vreinterpretq_s16_s32(tr23_32.val[1]); -} - static inline void aom_tbl2x4_s16(int16x8_t t0[4], int16x8_t t1[4], uint16x8_t tbl, int16x8_t res[4]) { res[0] = aom_tbl2_s16(t0[0], t1[0], tbl); @@ -88,10 +24,4 @@ res[3] = aom_tbl2_s16(t0[3], t1[3], tbl); } -static inline void aom_tbl2x2_s16(int16x8_t t0[2], int16x8_t t1[2], - uint16x8_t tbl, int16x8_t res[2]) { - res[0] = aom_tbl2_s16(t0[0], t1[0], tbl); - res[1] = aom_tbl2_s16(t0[1], t1[1], tbl); -} - #endif // AOM_AV1_COMMON_ARM_HIGHBD_CONVOLVE_SVE2_H_
diff --git a/av1/common/arm/reconintra_neon.c b/av1/common/arm/reconintra_neon.c index 81eb224..9ac013f 100644 --- a/av1/common/arm/reconintra_neon.c +++ b/av1/common/arm/reconintra_neon.c
@@ -20,13 +20,11 @@ #include "aom_dsp/arm/sum_neon.h" #define MAX_UPSAMPLE_SZ 16 - -// TODO(aomedia:349436249): enable for armv7 after SIGBUS is fixed. -#if AOM_ARCH_AARCH64 +#define FILTER_INTRA_SCALE_BITS 4 // These kernels are a transposed version of those defined in reconintra.c, // with the absolute value of the negatives taken in the top row. -DECLARE_ALIGNED(16, const uint8_t, +DECLARE_ALIGNED(16, static const uint8_t, av1_filter_intra_taps_neon[FILTER_INTRA_MODES][7][8]) = { // clang-format off { @@ -77,7 +75,22 @@ // clang-format on }; -#define FILTER_INTRA_SCALE_BITS 4 +static inline uint8x8_t filter_intra_predictor( + uint8x8_t s0, uint8x8_t s1, uint8x8_t s2, uint8x8_t s3, uint8x8_t s4, + uint8x8_t s5, uint8x8_t s6, const uint8x8_t f0, const uint8x8_t f1, + const uint8x8_t f2, const uint8x8_t f3, const uint8x8_t f4, + const uint8x8_t f5, const uint8x8_t f6) { + uint16x8_t acc = vmull_u8(s1, f1); + // First row of each filter has all negative values so subtract. + acc = vmlsl_u8(acc, s0, f0); + acc = vmlal_u8(acc, s2, f2); + acc = vmlal_u8(acc, s3, f3); + acc = vmlal_u8(acc, s4, f4); + acc = vmlal_u8(acc, s5, f5); + acc = vmlal_u8(acc, s6, f6); + + return vqrshrun_n_s16(vreinterpretq_s16_u16(acc), FILTER_INTRA_SCALE_BITS); +} void av1_filter_intra_predictor_neon(uint8_t *dst, ptrdiff_t stride, TX_SIZE tx_size, const uint8_t *above, @@ -94,82 +107,118 @@ const uint8x8_t f5 = vld1_u8(av1_filter_intra_taps_neon[mode][5]); const uint8x8_t f6 = vld1_u8(av1_filter_intra_taps_neon[mode][6]); - uint8_t buffer[33][33]; - // Populate the top row in the scratch buffer with data from above. - memcpy(buffer[0], &above[-1], (width + 1) * sizeof(uint8_t)); - // Populate the first column in the scratch buffer with data from the left. - int r = 0; - do { - buffer[r + 1][0] = left[r]; - } while (++r < height); - // Computing 4 cols per iteration (instead of 8) for 8x<h> blocks is faster. if (width <= 8) { - r = 1; - do { - int c = 1; - uint8x8_t s0 = vld1_dup_u8(&buffer[r - 1][c - 1]); - uint8x8_t s5 = vld1_dup_u8(&buffer[r + 0][c - 1]); - uint8x8_t s6 = vld1_dup_u8(&buffer[r + 1][c - 1]); + uint8x8_t s0 = vdup_n_u8(above[-1]); + uint8x8_t s5 = vdup_n_u8(left[0]); + uint8x8_t s6 = vdup_n_u8(left[1]); + int c = 0; + do { + uint8x8_t s1234 = load_unaligned_u8_4x1(above + c); + uint8x8_t s1 = vdup_lane_u8(s1234, 0); + uint8x8_t s2 = vdup_lane_u8(s1234, 1); + uint8x8_t s3 = vdup_lane_u8(s1234, 2); + uint8x8_t s4 = vdup_lane_u8(s1234, 3); + + uint8x8_t res = filter_intra_predictor(s0, s1, s2, s3, s4, s5, s6, f0, f1, + f2, f3, f4, f5, f6); + + store_u8x4_strided_x2(dst + c, stride, res); + + s0 = s4; + s5 = vdup_lane_u8(res, 3); + s6 = vdup_lane_u8(res, 7); + + c += 4; + } while (c < width); + + int r = 2; + do { + s0 = vdup_n_u8(left[r - 1]); + s5 = vdup_n_u8(left[r + 0]); + s6 = vdup_n_u8(left[r + 1]); + + c = 0; do { - uint8x8_t s1234 = load_u8_4x1(&buffer[r - 1][c - 1] + 1); + uint8x8_t s1234 = load_u8_4x1(dst + (r - 1) * stride + c); uint8x8_t s1 = vdup_lane_u8(s1234, 0); uint8x8_t s2 = vdup_lane_u8(s1234, 1); uint8x8_t s3 = vdup_lane_u8(s1234, 2); uint8x8_t s4 = vdup_lane_u8(s1234, 3); - uint16x8_t sum = vmull_u8(s1, f1); - // First row of each filter has all negative values so subtract. - sum = vmlsl_u8(sum, s0, f0); - sum = vmlal_u8(sum, s2, f2); - sum = vmlal_u8(sum, s3, f3); - sum = vmlal_u8(sum, s4, f4); - sum = vmlal_u8(sum, s5, f5); - sum = vmlal_u8(sum, s6, f6); + uint8x8_t res = filter_intra_predictor(s0, s1, s2, s3, s4, s5, s6, f0, + f1, f2, f3, f4, f5, f6); - uint8x8_t res = - vqrshrun_n_s16(vreinterpretq_s16_u16(sum), FILTER_INTRA_SCALE_BITS); - - // Store buffer[r + 0][c] and buffer[r + 1][c]. - store_u8x4_strided_x2(&buffer[r][c], 33, res); - - store_u8x4_strided_x2(dst + (r - 1) * stride + c - 1, stride, res); + store_u8x4_strided_x2(dst + r * stride + c, stride, res); s0 = s4; s5 = vdup_lane_u8(res, 3); s6 = vdup_lane_u8(res, 7); + c += 4; - } while (c < width + 1); + } while (c < width); r += 2; - } while (r < height + 1); + } while (r < height); } else { - r = 1; - do { - int c = 1; - uint8x8_t s0_lo = vld1_dup_u8(&buffer[r - 1][c - 1]); - uint8x8_t s5_lo = vld1_dup_u8(&buffer[r + 0][c - 1]); - uint8x8_t s6_lo = vld1_dup_u8(&buffer[r + 1][c - 1]); + uint8x8_t s0_lo = vdup_n_u8(above[-1]); + uint8x8_t s5_lo = vdup_n_u8(left[0]); + uint8x8_t s6_lo = vdup_n_u8(left[1]); + int c = 0; + do { + uint8x8_t s1234 = vld1_u8(above + c); + uint8x8_t s1_lo = vdup_lane_u8(s1234, 0); + uint8x8_t s2_lo = vdup_lane_u8(s1234, 1); + uint8x8_t s3_lo = vdup_lane_u8(s1234, 2); + uint8x8_t s4_lo = vdup_lane_u8(s1234, 3); + + uint8x8_t res_lo = + filter_intra_predictor(s0_lo, s1_lo, s2_lo, s3_lo, s4_lo, s5_lo, + s6_lo, f0, f1, f2, f3, f4, f5, f6); + + uint8x8_t s0_hi = s4_lo; + uint8x8_t s1_hi = vdup_lane_u8(s1234, 4); + uint8x8_t s2_hi = vdup_lane_u8(s1234, 5); + uint8x8_t s3_hi = vdup_lane_u8(s1234, 6); + uint8x8_t s4_hi = vdup_lane_u8(s1234, 7); + uint8x8_t s5_hi = vdup_lane_u8(res_lo, 3); + uint8x8_t s6_hi = vdup_lane_u8(res_lo, 7); + + uint8x8_t res_hi = + filter_intra_predictor(s0_hi, s1_hi, s2_hi, s3_hi, s4_hi, s5_hi, + s6_hi, f0, f1, f2, f3, f4, f5, f6); + + uint32x2x2_t res = + vzip_u32(vreinterpret_u32_u8(res_lo), vreinterpret_u32_u8(res_hi)); + + vst1_u8(dst + 0 * stride + c, vreinterpret_u8_u32(res.val[0])); + vst1_u8(dst + 1 * stride + c, vreinterpret_u8_u32(res.val[1])); + + s0_lo = s4_hi; + s5_lo = vdup_lane_u8(res_hi, 3); + s6_lo = vdup_lane_u8(res_hi, 7); + c += 8; + } while (c < width); + + int r = 2; + do { + s0_lo = vdup_n_u8(left[r - 1]); + s5_lo = vdup_n_u8(left[r + 0]); + s6_lo = vdup_n_u8(left[r + 1]); + + c = 0; do { - uint8x8_t s1234 = vld1_u8(&buffer[r - 1][c - 1] + 1); + uint8x8_t s1234 = vld1_u8(dst + (r - 1) * stride + c); uint8x8_t s1_lo = vdup_lane_u8(s1234, 0); uint8x8_t s2_lo = vdup_lane_u8(s1234, 1); uint8x8_t s3_lo = vdup_lane_u8(s1234, 2); uint8x8_t s4_lo = vdup_lane_u8(s1234, 3); - uint16x8_t sum_lo = vmull_u8(s1_lo, f1); - // First row of each filter has all negative values so subtract. - sum_lo = vmlsl_u8(sum_lo, s0_lo, f0); - sum_lo = vmlal_u8(sum_lo, s2_lo, f2); - sum_lo = vmlal_u8(sum_lo, s3_lo, f3); - sum_lo = vmlal_u8(sum_lo, s4_lo, f4); - sum_lo = vmlal_u8(sum_lo, s5_lo, f5); - sum_lo = vmlal_u8(sum_lo, s6_lo, f6); - - uint8x8_t res_lo = vqrshrun_n_s16(vreinterpretq_s16_u16(sum_lo), - FILTER_INTRA_SCALE_BITS); + uint8x8_t res_lo = + filter_intra_predictor(s0_lo, s1_lo, s2_lo, s3_lo, s4_lo, s5_lo, + s6_lo, f0, f1, f2, f3, f4, f5, f6); uint8x8_t s0_hi = s4_lo; uint8x8_t s1_hi = vdup_lane_u8(s1234, 4); @@ -179,40 +228,26 @@ uint8x8_t s5_hi = vdup_lane_u8(res_lo, 3); uint8x8_t s6_hi = vdup_lane_u8(res_lo, 7); - uint16x8_t sum_hi = vmull_u8(s1_hi, f1); - // First row of each filter has all negative values so subtract. - sum_hi = vmlsl_u8(sum_hi, s0_hi, f0); - sum_hi = vmlal_u8(sum_hi, s2_hi, f2); - sum_hi = vmlal_u8(sum_hi, s3_hi, f3); - sum_hi = vmlal_u8(sum_hi, s4_hi, f4); - sum_hi = vmlal_u8(sum_hi, s5_hi, f5); - sum_hi = vmlal_u8(sum_hi, s6_hi, f6); - - uint8x8_t res_hi = vqrshrun_n_s16(vreinterpretq_s16_u16(sum_hi), - FILTER_INTRA_SCALE_BITS); + uint8x8_t res_hi = + filter_intra_predictor(s0_hi, s1_hi, s2_hi, s3_hi, s4_hi, s5_hi, + s6_hi, f0, f1, f2, f3, f4, f5, f6); uint32x2x2_t res = vzip_u32(vreinterpret_u32_u8(res_lo), vreinterpret_u32_u8(res_hi)); - vst1_u8(&buffer[r + 0][c], vreinterpret_u8_u32(res.val[0])); - vst1_u8(&buffer[r + 1][c], vreinterpret_u8_u32(res.val[1])); - - vst1_u8(dst + (r - 1) * stride + c - 1, - vreinterpret_u8_u32(res.val[0])); - vst1_u8(dst + (r + 0) * stride + c - 1, - vreinterpret_u8_u32(res.val[1])); + vst1_u8(dst + (r + 0) * stride + c, vreinterpret_u8_u32(res.val[0])); + vst1_u8(dst + (r + 1) * stride + c, vreinterpret_u8_u32(res.val[1])); s0_lo = s4_hi; s5_lo = vdup_lane_u8(res_hi, 3); s6_lo = vdup_lane_u8(res_hi, 7); c += 8; - } while (c < width + 1); + } while (c < width); r += 2; - } while (r < height + 1); + } while (r < height); } } -#endif // AOM_ARCH_AARCH64 void av1_filter_intra_edge_neon(uint8_t *p, int sz, int strength) { if (!strength) return;
diff --git a/av1/common/arm/reconintra_neon_i8mm.c b/av1/common/arm/reconintra_neon_i8mm.c new file mode 100644 index 0000000..21b7170 --- /dev/null +++ b/av1/common/arm/reconintra_neon_i8mm.c
@@ -0,0 +1,182 @@ +/* + * Copyright (c) 2025, 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 <arm_neon.h> +#include <assert.h> + +#include "config/aom_config.h" +#include "config/av1_rtcd.h" + +#include "aom_dsp/arm/mem_neon.h" + +#define FILTER_INTRA_SCALE_BITS 4 + +// The input arrays are reordered compared to the C implementation: the first +// four vectors contain the lower elements of the original filter, the next four +// vectors contain the upper elements. This layout allows all 8 multiplications +// to accumulate into a single element using USDOT instructions, instead of +// having two partial sums in adjacent vector elements and needing to combine +// them with an additional pairwise add. +DECLARE_ALIGNED(16, static const int8_t, + av1_filter_intra_taps_neon_i8mm[FILTER_INTRA_MODES][8][8]) = { + { + { -6, 10, 0, 0, -5, 2, 10, 0 }, + { -3, 1, 1, 10, -3, 1, 1, 2 }, + { -4, 6, 0, 0, -3, 2, 6, 0 }, + { -3, 2, 2, 6, -3, 1, 2, 2 }, + { 0, 12, 0, 0, 0, 9, 0, 0 }, + { 0, 7, 0, 0, 10, 5, 0, 0 }, + { 0, 2, 12, 0, 0, 2, 9, 0 }, + { 0, 2, 7, 0, 6, 3, 5, 0 }, + }, + { + { -10, 16, 0, 0, -6, 0, 16, 0 }, + { -4, 0, 0, 16, -2, 0, 0, 0 }, + { -10, 16, 0, 0, -6, 0, 16, 0 }, + { -4, 0, 0, 16, -2, 0, 0, 0 }, + { 0, 10, 0, 0, 0, 6, 0, 0 }, + { 0, 4, 0, 0, 16, 2, 0, 0 }, + { 0, 0, 10, 0, 0, 0, 6, 0 }, + { 0, 0, 4, 0, 16, 0, 2, 0 }, + }, + { + { -8, 8, 0, 0, -8, 0, 8, 0 }, + { -8, 0, 0, 8, -8, 0, 0, 0 }, + { -4, 4, 0, 0, -4, 0, 4, 0 }, + { -4, 0, 0, 4, -4, 0, 0, 0 }, + { 0, 16, 0, 0, 0, 16, 0, 0 }, + { 0, 16, 0, 0, 8, 16, 0, 0 }, + { 0, 0, 16, 0, 0, 0, 16, 0 }, + { 0, 0, 16, 0, 4, 0, 16, 0 }, + }, + { + { -2, 8, 0, 0, -1, 3, 8, 0 }, + { -1, 2, 3, 8, 0, 1, 2, 3 }, + { -1, 4, 0, 0, -1, 3, 4, 0 }, + { -1, 2, 3, 4, -1, 2, 2, 3 }, + { 0, 10, 0, 0, 0, 6, 0, 0 }, + { 0, 4, 0, 0, 8, 2, 0, 0 }, + { 0, 3, 10, 0, 0, 4, 6, 0 }, + { 0, 4, 4, 0, 4, 3, 3, 0 }, + }, + { + { -12, 14, 0, 0, -10, 0, 14, 0 }, + { -9, 0, 0, 14, -8, 0, 0, 0 }, + { -10, 12, 0, 0, -9, 1, 12, 0 }, + { -8, 0, 0, 12, -7, 0, 0, 1 }, + { 0, 14, 0, 0, 0, 12, 0, 0 }, + { 0, 11, 0, 0, 14, 10, 0, 0 }, + { 0, 0, 14, 0, 0, 0, 12, 0 }, + { 0, 1, 11, 0, 12, 1, 9, 0 }, + }, +}; + +static inline uint8x8_t filter_intra_predictor(uint8x16_t p_lo, uint8x16_t p_hi, + const int8x16_t f01, + const int8x16_t f23, + const int8x16_t f45, + const int8x16_t f67) { + int32x4_t acc_0123 = vusdotq_s32(vdupq_n_s32(0), p_lo, f01); + acc_0123 = vusdotq_s32(acc_0123, p_hi, f23); + + int32x4_t acc_4567 = vusdotq_s32(vdupq_n_s32(0), p_lo, f45); + acc_4567 = vusdotq_s32(acc_4567, p_hi, f67); + + const int16x8_t acc = vcombine_s16(vmovn_s32(acc_0123), vmovn_s32(acc_4567)); + + return vqrshrun_n_s16(acc, FILTER_INTRA_SCALE_BITS); +} + +void av1_filter_intra_predictor_neon_i8mm(uint8_t *dst, ptrdiff_t stride, + TX_SIZE tx_size, const uint8_t *above, + const uint8_t *left, int mode) { + const int bw = tx_size_wide[tx_size]; + const int bh = tx_size_high[tx_size]; + + if (bw == 4 || (bw == 8 && bh < 16) || (bw == 16 && bh <= 4) || bw == 32) { + av1_filter_intra_predictor_neon(dst, stride, tx_size, above, left, mode); + return; + } + + assert(bw <= 32 && bh <= 32); + + const int8x16_t f01 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][0]); + const int8x16_t f45 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][2]); + const int8x16_t f23 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][4]); + const int8x16_t f67 = vld1q_s8(av1_filter_intra_taps_neon_i8mm[mode][6]); + + // indexes : 0, 19, 23, -1 + uint8x16_t p_hi_idx = vreinterpretq_u8_u32(vdupq_n_u32(0xFF171300)); + + uint64_t l01 = ((uint64_t)left[0] << 24) | ((uint64_t)left[1] << 56); + uint8x16_t l = vreinterpretq_u8_u64(vdupq_n_u64(l01)); + + int c = 0; + do { + const uint8_t *ptr = above + c - 1; + uint32_t lo = + ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | ((uint32_t)ptr[3] << 24); + uint8x16_t p_lo = vreinterpretq_u8_u32(vdupq_n_u32(lo)); + + uint8x16x2_t hi; + hi.val[0] = vdupq_n_u8(ptr[4]); + hi.val[1] = l; + uint8x16_t p_hi = vqtbl2q_u8(hi, p_hi_idx); + + const uint8x8_t res = + filter_intra_predictor(p_lo, p_hi, f01, f23, f45, f67); + + store_u8x4_strided_x2(dst + c, stride, res); + + l = vcombine_u8(res, res); + + c += 4; + } while (c < bw); + + dst += 2 * stride; + int r = 2; + while (r < bh) { + const uint8_t *ptr = dst - stride; + uint32_t lo = + left[r - 1] | (ptr[0] << 8) | (ptr[1] << 16) | ((uint32_t)ptr[2] << 24); + uint32_t hi = ptr[3] | (left[r] << 8) | (left[r + 1] << 16); + uint8x16_t p_lo = vreinterpretq_u8_u32(vdupq_n_u32(lo)); + uint8x16_t p_hi = vreinterpretq_u8_u32(vdupq_n_u32(hi)); + + uint8x8_t res = filter_intra_predictor(p_lo, p_hi, f01, f23, f45, f67); + + store_u8x4_strided_x2(dst, stride, res); + + l = vcombine_u8(res, res); + + c = 4; + while (c < bw) { + ptr = dst - stride + c - 1; + lo = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | ((uint32_t)ptr[3] << 24); + p_lo = vreinterpretq_u8_u32(vdupq_n_u32(lo)); + + uint8x16x2_t hi_v; + hi_v.val[0] = vdupq_n_u8(ptr[4]); + hi_v.val[1] = l; + p_hi = vqtbl2q_u8(hi_v, p_hi_idx); + + res = filter_intra_predictor(p_lo, p_hi, f01, f23, f45, f67); + + store_u8x4_strided_x2(dst + c, stride, res); + + l = vcombine_u8(res, res); + c += 4; + } + + r += 2; + dst += 2 * stride; + } +}
diff --git a/av1/common/arm/warp_plane_neon.c b/av1/common/arm/warp_plane_neon.c index 497273b..f7b393a 100644 --- a/av1/common/arm/warp_plane_neon.c +++ b/av1/common/arm/warp_plane_neon.c
@@ -143,31 +143,6 @@ return horizontal_filter_8x1_f1_beta0(in, f_s16); } -static AOM_FORCE_INLINE void vertical_filter_4x1_f1(const int16x8_t *src, - int32x4_t *res, int sy) { - int16x4_t s0 = vget_low_s16(src[0]); - int16x4_t s1 = vget_low_s16(src[1]); - int16x4_t s2 = vget_low_s16(src[2]); - int16x4_t s3 = vget_low_s16(src[3]); - int16x4_t s4 = vget_low_s16(src[4]); - int16x4_t s5 = vget_low_s16(src[5]); - int16x4_t s6 = vget_low_s16(src[6]); - int16x4_t s7 = vget_low_s16(src[7]); - - int16x8_t f = vld1q_s16(av1_warped_filter[sy >> WARPEDDIFF_PREC_BITS]); - - int32x4_t m0123 = vmull_lane_s16(s0, vget_low_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, s1, vget_low_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, s2, vget_low_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, s3, vget_low_s16(f), 3); - m0123 = vmlal_lane_s16(m0123, s4, vget_high_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, s5, vget_high_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, s6, vget_high_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, s7, vget_high_s16(f), 3); - - *res = m0123; -} - static AOM_FORCE_INLINE void vertical_filter_4x1_f4(const int16x8_t *src, int32x4_t *res, int sy, int gamma) { @@ -194,43 +169,6 @@ *res = horizontal_add_4d_s32x4(m0123_pairs); } -static AOM_FORCE_INLINE void vertical_filter_8x1_f1(const int16x8_t *src, - int32x4_t *res_low, - int32x4_t *res_high, - int sy) { - int16x8_t s0 = src[0]; - int16x8_t s1 = src[1]; - int16x8_t s2 = src[2]; - int16x8_t s3 = src[3]; - int16x8_t s4 = src[4]; - int16x8_t s5 = src[5]; - int16x8_t s6 = src[6]; - int16x8_t s7 = src[7]; - - int16x8_t f = vld1q_s16(av1_warped_filter[sy >> WARPEDDIFF_PREC_BITS]); - - int32x4_t m0123 = vmull_lane_s16(vget_low_s16(s0), vget_low_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s1), vget_low_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s2), vget_low_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s3), vget_low_s16(f), 3); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s4), vget_high_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s5), vget_high_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s6), vget_high_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s7), vget_high_s16(f), 3); - - int32x4_t m4567 = vmull_lane_s16(vget_high_s16(s0), vget_low_s16(f), 0); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s1), vget_low_s16(f), 1); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s2), vget_low_s16(f), 2); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s3), vget_low_s16(f), 3); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s4), vget_high_s16(f), 0); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s5), vget_high_s16(f), 1); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s6), vget_high_s16(f), 2); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s7), vget_high_s16(f), 3); - - *res_low = m0123; - *res_high = m4567; -} - static AOM_FORCE_INLINE void vertical_filter_8x1_f8(const int16x8_t *src, int32x4_t *res_low, int32x4_t *res_high, int sy, @@ -272,13 +210,106 @@ *res_high = horizontal_add_4d_s32x4(m4567_pairs); } +static AOM_FORCE_INLINE void warp_affine_horizontal_neon( + const uint8_t *ref, int width, int height, int stride, int p_width, + int p_height, int16_t alpha, int16_t beta, const int64_t x4, + const int64_t y4, const int i, int16x8_t tmp[]) { + const int height_limit = AOMMIN(8, p_height - i) + 7; + + int32_t ix4 = (int32_t)(x4 >> WARPEDMODEL_PREC_BITS); + int32_t iy4 = (int32_t)(y4 >> WARPEDMODEL_PREC_BITS); + + int32_t sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + sx4 += alpha * (-4) + beta * (-4) + (1 << (WARPEDDIFF_PREC_BITS - 1)) + + (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS); + sx4 &= ~((1 << WARP_PARAM_REDUCE_BITS) - 1); + + if (warp_affine_special_case(ref, ix4, iy4, width, height, stride, + height_limit, tmp)) { + return; + } + + static const uint8_t kIotaArr[] = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 }; + const uint8x16_t indx = vld1q_u8(kIotaArr); + + const int out_of_boundary_left = -(ix4 - 6); + const int out_of_boundary_right = (ix4 + 8) - width; + + if (p_width == 4) { + if (beta == 0) { + if (alpha == 0) { + int16x8_t f_s16 = + vld1q_s16(av1_warped_filter[sx4 >> WARPEDDIFF_PREC_BITS]); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_beta0, f_s16); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, sx4, alpha); + } + } else { + if (alpha == 0) { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1, + (sx4 + beta * (k - 3))); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, (sx4 + beta * (k - 3)), + alpha); + } + } + } else { + if (beta == 0) { + if (alpha == 0) { + int16x8_t f_s16 = + vld1q_s16(av1_warped_filter[sx4 >> WARPEDDIFF_PREC_BITS]); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_beta0, f_s16); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, sx4, alpha); + } + } else { + if (alpha == 0) { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1, + (sx4 + beta * (k - 3))); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, (sx4 + beta * (k - 3)), + alpha); + } + } + } +} + void av1_warp_affine_neon(const int32_t *mat, const uint8_t *ref, int width, int height, int stride, uint8_t *pred, int p_col, int p_row, int p_width, int p_height, int p_stride, int subsampling_x, int subsampling_y, ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, int16_t delta) { - av1_warp_affine_common(mat, ref, width, height, stride, pred, p_col, p_row, - p_width, p_height, p_stride, subsampling_x, - subsampling_y, conv_params, alpha, beta, gamma, delta); + const int w0 = conv_params->fwd_offset; + const int w1 = conv_params->bck_offset; + const int is_compound = conv_params->is_compound; + uint16_t *const dst = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + const int do_average = conv_params->do_average; + const int use_dist_wtd_comp_avg = conv_params->use_dist_wtd_comp_avg; + + assert(IMPLIES(is_compound, dst != NULL)); + assert(IMPLIES(do_average, is_compound)); + + for (int i = 0; i < p_height; i += 8) { + for (int j = 0; j < p_width; j += 8) { + const int32_t src_x = (p_col + j + 4) << subsampling_x; + const int32_t src_y = (p_row + i + 4) << subsampling_y; + const int64_t dst_x = + (int64_t)mat[2] * src_x + (int64_t)mat[3] * src_y + (int64_t)mat[0]; + const int64_t dst_y = + (int64_t)mat[4] * src_x + (int64_t)mat[5] * src_y + (int64_t)mat[1]; + + const int64_t x4 = dst_x >> subsampling_x; + const int64_t y4 = dst_y >> subsampling_y; + + int16x8_t tmp[15]; + warp_affine_horizontal_neon(ref, width, height, stride, p_width, p_height, + alpha, beta, x4, y4, i, tmp); + warp_affine_vertical(pred, p_width, p_height, p_stride, is_compound, dst, + dst_stride, do_average, use_dist_wtd_comp_avg, gamma, + delta, y4, i, j, tmp, w0, w1); + } + } }
diff --git a/av1/common/arm/warp_plane_neon.h b/av1/common/arm/warp_plane_neon.h index 2909df7..0bf5c03 100644 --- a/av1/common/arm/warp_plane_neon.h +++ b/av1/common/arm/warp_plane_neon.h
@@ -24,41 +24,91 @@ #include "av1/common/warped_motion.h" #include "av1/common/scale.h" -static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f4(const uint8x16_t in, - int sx, int alpha); - -static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f8(const uint8x16_t in, - int sx, int alpha); - -static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1(const uint8x16_t in, - int sx); - -static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1(const uint8x16_t in, - int sx); - -static AOM_FORCE_INLINE int16x8_t -horizontal_filter_4x1_f1_beta0(const uint8x16_t in, int16x8_t f_s16); - -static AOM_FORCE_INLINE int16x8_t -horizontal_filter_8x1_f1_beta0(const uint8x16_t in, int16x8_t f_s16); - -static AOM_FORCE_INLINE void vertical_filter_4x1_f1(const int16x8_t *src, - int32x4_t *res, int sy); - static AOM_FORCE_INLINE void vertical_filter_4x1_f4(const int16x8_t *src, int32x4_t *res, int sy, int gamma); -static AOM_FORCE_INLINE void vertical_filter_8x1_f1(const int16x8_t *src, - int32x4_t *res_low, - int32x4_t *res_high, - int sy); - static AOM_FORCE_INLINE void vertical_filter_8x1_f8(const int16x8_t *src, int32x4_t *res_low, int32x4_t *res_high, int sy, int gamma); +static AOM_FORCE_INLINE void vertical_filter_4x1_f1(const int16x8_t *src, + int32x4_t *res, int sy) { + int16_t *f_ptr = + (int16_t *)(av1_warped_filter + (sy >> WARPEDDIFF_PREC_BITS)); + int16x8_t f = vld1q_s16(f_ptr); + + int32x4_t m0123; + if (f_ptr[0] != 0) { + m0123 = vmull_lane_s16(vget_low_s16(src[0]), vget_low_s16(f), 0); + } else { + m0123 = vdupq_n_s32(0); + } + if (f_ptr[1] != 0) { + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[1]), vget_low_s16(f), 1); + } + + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[2]), vget_low_s16(f), 2); + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[3]), vget_low_s16(f), 3); + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[4]), vget_high_s16(f), 0); + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[5]), vget_high_s16(f), 1); + + if (f_ptr[6] != 0) { + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[6]), vget_high_s16(f), 2); + } + if (f_ptr[7] != 0) { + m0123 = vmlal_lane_s16(m0123, vget_low_s16(src[7]), vget_high_s16(f), 3); + } + + *res = m0123; +} + +static AOM_FORCE_INLINE void vertical_filter_8x1_f1(const int16x8_t *s, + int32x4_t *res_low, + int32x4_t *res_high, + int sy) { + int16_t *f_ptr = + (int16_t *)(av1_warped_filter + (sy >> WARPEDDIFF_PREC_BITS)); + int16x8_t f = vld1q_s16(f_ptr); + + int32x4_t m0123, m4567; + if (f_ptr[0] != 0) { + m0123 = vmull_lane_s16(vget_low_s16(s[0]), vget_low_s16(f), 0); + m4567 = vmull_lane_s16(vget_high_s16(s[0]), vget_low_s16(f), 0); + } else { + m0123 = vdupq_n_s32(0); + m4567 = vdupq_n_s32(0); + } + if (f_ptr[1] != 0) { + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[1]), vget_low_s16(f), 1); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[1]), vget_low_s16(f), 1); + } + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[2]), vget_low_s16(f), 2); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[2]), vget_low_s16(f), 2); + + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[3]), vget_low_s16(f), 3); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[3]), vget_low_s16(f), 3); + + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[4]), vget_high_s16(f), 0); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[4]), vget_high_s16(f), 0); + + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[5]), vget_high_s16(f), 1); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[5]), vget_high_s16(f), 1); + + if (f_ptr[6] != 0) { + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[6]), vget_high_s16(f), 2); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[6]), vget_high_s16(f), 2); + } + if (f_ptr[7] != 0) { + m0123 = vmlal_lane_s16(m0123, vget_low_s16(s[7]), vget_high_s16(f), 3); + m4567 = vmlal_lane_s16(m4567, vget_high_s16(s[7]), vget_high_s16(f), 3); + } + + *res_low = m0123; + *res_high = m4567; +} + static AOM_FORCE_INLINE void load_filters_4(int16x8_t out[], int offset, int stride) { out[0] = vld1q_s16( @@ -95,21 +145,12 @@ return clamp(iy, 0, height - 1); } -static AOM_FORCE_INLINE void warp_affine_horizontal( - const uint8_t *ref, int width, int height, int stride, int p_width, - int p_height, int16_t alpha, int16_t beta, const int64_t x4, - const int64_t y4, const int i, int16x8_t tmp[]) { +static inline bool warp_affine_special_case(const uint8_t *ref, int32_t ix4, + int32_t iy4, int width, int height, + int stride, const int height_limit, + int16x8_t tmp[]) { const int bd = 8; const int reduce_bits_horiz = ROUND0_BITS; - const int height_limit = AOMMIN(8, p_height - i) + 7; - - int32_t ix4 = (int32_t)(x4 >> WARPEDMODEL_PREC_BITS); - int32_t iy4 = (int32_t)(y4 >> WARPEDMODEL_PREC_BITS); - - int32_t sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); - sx4 += alpha * (-4) + beta * (-4) + (1 << (WARPEDDIFF_PREC_BITS - 1)) + - (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS); - sx4 &= ~((1 << WARP_PARAM_REDUCE_BITS) - 1); if (ix4 <= -7) { for (int k = 0; k < height_limit; ++k) { @@ -119,7 +160,7 @@ ref[iy * stride] * (1 << (FILTER_BITS - reduce_bits_horiz)); tmp[k] = vdupq_n_s16(dup_val); } - return; + return true; } else if (ix4 >= width + 6) { for (int k = 0; k < height_limit; ++k) { int iy = clamp_iy(iy4 + k - 7, height); @@ -128,15 +169,11 @@ (1 << (FILTER_BITS - reduce_bits_horiz)); tmp[k] = vdupq_n_s16(dup_val); } - return; + return true; } - static const uint8_t kIotaArr[] = { 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15 }; - const uint8x16_t indx = vld1q_u8(kIotaArr); - - const int out_of_boundary_left = -(ix4 - 6); - const int out_of_boundary_right = (ix4 + 8) - width; + return false; +} #define APPLY_HORIZONTAL_SHIFT(fn, ...) \ do { \ @@ -172,45 +209,6 @@ } \ } while (0) - if (p_width == 4) { - if (beta == 0) { - if (alpha == 0) { - int16x8_t f_s16 = - vld1q_s16(av1_warped_filter[sx4 >> WARPEDDIFF_PREC_BITS]); - APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_beta0, f_s16); - } else { - APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, sx4, alpha); - } - } else { - if (alpha == 0) { - APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1, - (sx4 + beta * (k - 3))); - } else { - APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, (sx4 + beta * (k - 3)), - alpha); - } - } - } else { - if (beta == 0) { - if (alpha == 0) { - int16x8_t f_s16 = - vld1q_s16(av1_warped_filter[sx4 >> WARPEDDIFF_PREC_BITS]); - APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_beta0, f_s16); - } else { - APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, sx4, alpha); - } - } else { - if (alpha == 0) { - APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1, - (sx4 + beta * (k - 3))); - } else { - APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, (sx4 + beta * (k - 3)), - alpha); - } - } - } -} - static AOM_FORCE_INLINE void warp_affine_vertical( uint8_t *pred, int p_width, int p_height, int p_stride, int is_compound, uint16_t *dst, int dst_stride, int do_average, int use_dist_wtd_comp_avg, @@ -339,43 +337,4 @@ } } -static AOM_FORCE_INLINE void av1_warp_affine_common( - const int32_t *mat, const uint8_t *ref, int width, int height, int stride, - uint8_t *pred, int p_col, int p_row, int p_width, int p_height, - int p_stride, int subsampling_x, int subsampling_y, - ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, - int16_t delta) { - const int w0 = conv_params->fwd_offset; - const int w1 = conv_params->bck_offset; - const int is_compound = conv_params->is_compound; - uint16_t *const dst = conv_params->dst; - const int dst_stride = conv_params->dst_stride; - const int do_average = conv_params->do_average; - const int use_dist_wtd_comp_avg = conv_params->use_dist_wtd_comp_avg; - - assert(IMPLIES(is_compound, dst != NULL)); - assert(IMPLIES(do_average, is_compound)); - - for (int i = 0; i < p_height; i += 8) { - for (int j = 0; j < p_width; j += 8) { - const int32_t src_x = (p_col + j + 4) << subsampling_x; - const int32_t src_y = (p_row + i + 4) << subsampling_y; - const int64_t dst_x = - (int64_t)mat[2] * src_x + (int64_t)mat[3] * src_y + (int64_t)mat[0]; - const int64_t dst_y = - (int64_t)mat[4] * src_x + (int64_t)mat[5] * src_y + (int64_t)mat[1]; - - const int64_t x4 = dst_x >> subsampling_x; - const int64_t y4 = dst_y >> subsampling_y; - - int16x8_t tmp[15]; - warp_affine_horizontal(ref, width, height, stride, p_width, p_height, - alpha, beta, x4, y4, i, tmp); - warp_affine_vertical(pred, p_width, p_height, p_stride, is_compound, dst, - dst_stride, do_average, use_dist_wtd_comp_avg, gamma, - delta, y4, i, j, tmp, w0, w1); - } - } -} - #endif // AOM_AV1_COMMON_ARM_WARP_PLANE_NEON_H_
diff --git a/av1/common/arm/warp_plane_neon_i8mm.c b/av1/common/arm/warp_plane_neon_i8mm.c index 15ac004..b75f5e7 100644 --- a/av1/common/arm/warp_plane_neon_i8mm.c +++ b/av1/common/arm/warp_plane_neon_i8mm.c
@@ -10,11 +10,11 @@ */ #include "warp_plane_neon.h" +#include "convolve_neon_dotprod.h" +#include "convolve_neon_i8mm.h" -DECLARE_ALIGNED(16, static const uint8_t, usdot_permute_idx[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 +DECLARE_ALIGNED(16, const uint8_t, kTblIdx0_3[16]) = { + 0, -1, -1, -1, 1, -1, -1, -1, 2, -1, -1, -1, 3, -1, -1, -1, }; static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f4(const uint8x16_t in, @@ -84,14 +84,56 @@ return vreinterpretq_s16_u16(res); } -static AOM_FORCE_INLINE int16x8_t -horizontal_filter_4x1_f1_beta0(const uint8x16_t in, int16x8_t f_s16) { +static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1_6tap_beta0( + const uint8x16_t in, const int8x16_t filter, const uint8x16_t perm_tbl) { const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + // Permute samples ready for matrix multiply. + // { 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9 } + const uint8x16_t perm_samples = vqtbl1q_u8(in, perm_tbl); + + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum = vusmmlaq_s32(add_const, perm_samples, filter); + + uint16x8_t res = + vcombine_u16(vqrshrun_n_s32(sum, ROUND0_BITS), vdup_n_u16(0)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1_8tap_beta0( + const uint8x16_t in, const int8x16_t filter, const int32x4_t f0, + const uint8x16_t perm_tbl, const uint8x16_t tbl_idx0_3) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + const uint8x16_t perm_samples = vqtbl1q_u8(in, perm_tbl); + // Get samples 0..3 to apply tap 0 after matrix multiply. + const int32x4_t samples_0_3 = + vreinterpretq_s32_u8(vqtbl1q_u8(in, tbl_idx0_3)); + + // Calculate partial 7-tap convolution. + int32x4_t sum = vusmmlaq_s32(add_const, perm_samples, filter); + // Apply tap 0 and accumulate. + sum = vmlaq_s32(sum, samples_0_3, f0); + + uint16x8_t res = + vcombine_u16(vqrshrun_n_s32(sum, ROUND0_BITS), vdup_n_u16(0)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1(const uint8x16_t in, + int sx) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); int8x16_t f_s8 = vcombine_s8(vmovn_s16(f_s16), vmovn_s16(f_s16)); - uint8x16_t perm0 = vld1q_u8(&usdot_permute_idx[0]); - uint8x16_t perm1 = vld1q_u8(&usdot_permute_idx[16]); + uint8x16_t perm0 = vld1q_u8(&kDotProdPermuteTbl[0]); + uint8x16_t perm1 = vld1q_u8(&kDotProdPermuteTbl[16]); // Permute samples ready for dot product. // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } @@ -107,21 +149,63 @@ return vreinterpretq_s16_u16(res); } -static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1(const uint8x16_t in, - int sx) { - int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); - return horizontal_filter_4x1_f1_beta0(in, f_s16); -} - -static AOM_FORCE_INLINE int16x8_t -horizontal_filter_8x1_f1_beta0(const uint8x16_t in, int16x8_t f_s16) { +static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1_6tap_beta0( + const uint8x16_t in, const int8x16_t filter, const uint8x16x2_t perm_tbl) { const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + // Permute samples ready for matrix multiply. + // { 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9 } + // { 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(in, perm_tbl.val[0]), + vqtbl1q_u8(in, perm_tbl.val[1]) }; + + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum0123 = vusmmlaq_s32(add_const, perm_samples[0], filter); + int32x4_t sum4567 = vusmmlaq_s32(add_const, perm_samples[1], filter); + + uint16x8_t res = vcombine_u16(vqrshrun_n_s32(sum0123, ROUND0_BITS), + vqrshrun_n_s32(sum4567, ROUND0_BITS)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1_8tap_beta0( + const uint8x16_t in, const int8x16_t filter, const int16x4_t f0, + const uint8x16x2_t perm_tbl) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(in, perm_tbl.val[0]), + vqtbl1q_u8(in, perm_tbl.val[1]) }; + // Get samples 0..7 to apply tap 0 after matrix multiply. + int16x8_t samples_0_7 = vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(in))); + + // Calculate partial 7-tap convolution. + int32x4_t sum0123 = vusmmlaq_s32(add_const, perm_samples[0], filter); + int32x4_t sum4567 = vusmmlaq_s32(add_const, perm_samples[1], filter); + // Apply tap 0 and accumulate. + sum0123 = vmlal_s16(sum0123, vget_low_s16(samples_0_7), f0); + sum4567 = vmlal_s16(sum4567, vget_high_s16(samples_0_7), f0); + + uint16x8_t res = vcombine_u16(vqrshrun_n_s32(sum0123, ROUND0_BITS), + vqrshrun_n_s32(sum4567, ROUND0_BITS)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1(const uint8x16_t in, + int sx) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); int8x16_t f_s8 = vcombine_s8(vmovn_s16(f_s16), vmovn_s16(f_s16)); - uint8x16_t perm0 = vld1q_u8(&usdot_permute_idx[0]); - uint8x16_t perm1 = vld1q_u8(&usdot_permute_idx[16]); - uint8x16_t perm2 = vld1q_u8(&usdot_permute_idx[32]); + uint8x16_t perm0 = vld1q_u8(&kDotProdPermuteTbl[0]); + uint8x16_t perm1 = vld1q_u8(&kDotProdPermuteTbl[16]); + uint8x16_t perm2 = vld1q_u8(&kDotProdPermuteTbl[32]); // Permute samples ready for dot product. // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } @@ -142,37 +226,6 @@ return vreinterpretq_s16_u16(res); } -static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1(const uint8x16_t in, - int sx) { - int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); - return horizontal_filter_8x1_f1_beta0(in, f_s16); -} - -static AOM_FORCE_INLINE void vertical_filter_4x1_f1(const int16x8_t *src, - int32x4_t *res, int sy) { - int16x4_t s0 = vget_low_s16(src[0]); - int16x4_t s1 = vget_low_s16(src[1]); - int16x4_t s2 = vget_low_s16(src[2]); - int16x4_t s3 = vget_low_s16(src[3]); - int16x4_t s4 = vget_low_s16(src[4]); - int16x4_t s5 = vget_low_s16(src[5]); - int16x4_t s6 = vget_low_s16(src[6]); - int16x4_t s7 = vget_low_s16(src[7]); - - int16x8_t f = vld1q_s16(av1_warped_filter[sy >> WARPEDDIFF_PREC_BITS]); - - int32x4_t m0123 = vmull_lane_s16(s0, vget_low_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, s1, vget_low_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, s2, vget_low_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, s3, vget_low_s16(f), 3); - m0123 = vmlal_lane_s16(m0123, s4, vget_high_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, s5, vget_high_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, s6, vget_high_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, s7, vget_high_s16(f), 3); - - *res = m0123; -} - static AOM_FORCE_INLINE void vertical_filter_4x1_f4(const int16x8_t *src, int32x4_t *res, int sy, int gamma) { @@ -199,43 +252,6 @@ *res = horizontal_add_4d_s32x4(m0123_pairs); } -static AOM_FORCE_INLINE void vertical_filter_8x1_f1(const int16x8_t *src, - int32x4_t *res_low, - int32x4_t *res_high, - int sy) { - int16x8_t s0 = src[0]; - int16x8_t s1 = src[1]; - int16x8_t s2 = src[2]; - int16x8_t s3 = src[3]; - int16x8_t s4 = src[4]; - int16x8_t s5 = src[5]; - int16x8_t s6 = src[6]; - int16x8_t s7 = src[7]; - - int16x8_t f = vld1q_s16(av1_warped_filter[sy >> WARPEDDIFF_PREC_BITS]); - - int32x4_t m0123 = vmull_lane_s16(vget_low_s16(s0), vget_low_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s1), vget_low_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s2), vget_low_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s3), vget_low_s16(f), 3); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s4), vget_high_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s5), vget_high_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s6), vget_high_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s7), vget_high_s16(f), 3); - - int32x4_t m4567 = vmull_lane_s16(vget_high_s16(s0), vget_low_s16(f), 0); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s1), vget_low_s16(f), 1); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s2), vget_low_s16(f), 2); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s3), vget_low_s16(f), 3); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s4), vget_high_s16(f), 0); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s5), vget_high_s16(f), 1); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s6), vget_high_s16(f), 2); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s7), vget_high_s16(f), 3); - - *res_low = m0123; - *res_high = m4567; -} - static AOM_FORCE_INLINE void vertical_filter_8x1_f8(const int16x8_t *src, int32x4_t *res_low, int32x4_t *res_high, int sy, @@ -277,6 +293,157 @@ *res_high = horizontal_add_4d_s32x4(m4567_pairs); } +static AOM_FORCE_INLINE void warp_affine_horizontal_neon_i8mm( + const uint8_t *ref, int width, int height, int stride, int p_width, + int p_height, int16_t alpha, int16_t beta, const int64_t x4, + const int64_t y4, const int i, int16x8_t tmp[]) { + const int height_limit = AOMMIN(8, p_height - i) + 7; + + int32_t ix4 = (int32_t)(x4 >> WARPEDMODEL_PREC_BITS); + int32_t iy4 = (int32_t)(y4 >> WARPEDMODEL_PREC_BITS); + + int32_t sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + sx4 += alpha * (-4) + beta * (-4) + (1 << (WARPEDDIFF_PREC_BITS - 1)) + + (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS); + sx4 &= ~((1 << WARP_PARAM_REDUCE_BITS) - 1); + + if (warp_affine_special_case(ref, ix4, iy4, width, height, stride, + height_limit, tmp)) { + return; + } + + static const uint8_t kIotaArr[] = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 }; + const uint8x16_t indx = vld1q_u8(kIotaArr); + + const int out_of_boundary_left = -(ix4 - 6); + const int out_of_boundary_right = (ix4 + 8) - width; + + if (p_width == 4) { + if (beta == 0) { + if (alpha == 0) { + int16_t *f_ptr = + (int16_t *)(av1_warped_filter + (sx4 >> WARPEDDIFF_PREC_BITS)); + int16x8_t f_s16 = vld1q_s16(f_ptr); + const int8x8_t x_filter = vmovn_s16(f_s16); + if ((f_ptr[0] | f_ptr[1]) == 0) { + uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl = vaddq_u8(perm_tbl, vdupq_n_u8(2)); + // Stagger filter for use with the matrix multiply instructions. + // { f2, f3, f4, f5, f6, f7, 0, 0, 0, f2, f3, f4, f5, f6, f7, 0 } + const int8x16_t filter = vcombine_s8(vext_s8(x_filter, x_filter, 2), + vext_s8(x_filter, x_filter, 1)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[0] | f_ptr[7]) == 0) { + uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl = vaddq_u8(perm_tbl, vdupq_n_u8(1)); + // Stagger filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, 0, 0, 0, f1, f2, f3, f4, f5, f6, 0 } + const int8x16_t filter = + vcombine_s8(vext_s8(x_filter, x_filter, 1), x_filter); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[6] | f_ptr[7]) == 0) { + const uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); + // Stagger filter for use with the matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t filter = + vcombine_s8(x_filter, vext_s8(x_filter, x_filter, 7)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_6tap_beta0, filter, + perm_tbl); + } else { + const uint8x16_t perm_tbl = vld1q_u8(kMatMul8PermuteTbl); + const uint8x16_t tbl_idx0_3 = vld1q_u8(kTblIdx0_3); + + // Stagger filter for use with the matrix multiply + // instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const int8x16_t filter = vcombine_s8( + vext_s8(x_filter, vdup_n_s8(0), 1), vset_lane_s8(0, x_filter, 0)); + const int32x4_t f0 = vdupq_n_s32(f_ptr[0]); + + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_8tap_beta0, filter, + f0, perm_tbl, tbl_idx0_3); + } + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, sx4, alpha); + } + } else { + if (alpha == 0) { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1, + (sx4 + beta * (k - 3))); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, (sx4 + beta * (k - 3)), + alpha); + } + } + } else { + if (beta == 0) { + if (alpha == 0) { + int16_t *f_ptr = + (int16_t *)(av1_warped_filter + (sx4 >> WARPEDDIFF_PREC_BITS)); + int16x8_t f_s16 = vld1q_s16(f_ptr); + const int8x8_t x_filter = vmovn_s16(f_s16); + if ((f_ptr[0] | f_ptr[1]) == 0) { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl.val[0] = vaddq_u8(perm_tbl.val[0], vdupq_n_u8(2)); + perm_tbl.val[1] = vaddq_u8(perm_tbl.val[1], vdupq_n_u8(2)); + // Stagger filter for use with the matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t filter = vcombine_s8(vext_s8(x_filter, x_filter, 2), + vext_s8(x_filter, x_filter, 1)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[0] | f_ptr[7]) == 0) { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl.val[0] = vaddq_u8(perm_tbl.val[0], vdupq_n_u8(1)); + perm_tbl.val[1] = vaddq_u8(perm_tbl.val[1], vdupq_n_u8(1)); + // Stagger filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, 0, 0, 0, f1, f2, f3, f4, f5, f6, 0 } + const int8x16_t filter = + vcombine_s8(vext_s8(x_filter, x_filter, 1), x_filter); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[6] | f_ptr[7]) == 0) { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); + // Stagger filter for use with the matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t filter = + vcombine_s8(x_filter, vext_s8(x_filter, x_filter, 7)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_6tap_beta0, filter, + perm_tbl); + } else { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); + // Stagger filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const int8x16_t filter = vcombine_s8( + vext_s8(x_filter, vdup_n_s8(0), 1), vset_lane_s8(0, x_filter, 0)); + + const int16x4_t f0 = vdup_n_s16(f_ptr[0]); + + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_8tap_beta0, filter, + f0, perm_tbl); + } + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, sx4, alpha); + } + } else { + if (alpha == 0) { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1, + (sx4 + beta * (k - 3))); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, (sx4 + beta * (k - 3)), + alpha); + } + } + } +} + void av1_warp_affine_neon_i8mm(const int32_t *mat, const uint8_t *ref, int width, int height, int stride, uint8_t *pred, int p_col, int p_row, int p_width, int p_height, @@ -284,7 +451,35 @@ int subsampling_y, ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, int16_t delta) { - av1_warp_affine_common(mat, ref, width, height, stride, pred, p_col, p_row, - p_width, p_height, p_stride, subsampling_x, - subsampling_y, conv_params, alpha, beta, gamma, delta); + const int w0 = conv_params->fwd_offset; + const int w1 = conv_params->bck_offset; + const int is_compound = conv_params->is_compound; + uint16_t *const dst = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + const int do_average = conv_params->do_average; + const int use_dist_wtd_comp_avg = conv_params->use_dist_wtd_comp_avg; + + assert(IMPLIES(is_compound, dst != NULL)); + assert(IMPLIES(do_average, is_compound)); + + for (int i = 0; i < p_height; i += 8) { + for (int j = 0; j < p_width; j += 8) { + const int32_t src_x = (p_col + j + 4) << subsampling_x; + const int32_t src_y = (p_row + i + 4) << subsampling_y; + const int64_t dst_x = + (int64_t)mat[2] * src_x + (int64_t)mat[3] * src_y + (int64_t)mat[0]; + const int64_t dst_y = + (int64_t)mat[4] * src_x + (int64_t)mat[5] * src_y + (int64_t)mat[1]; + + const int64_t x4 = dst_x >> subsampling_x; + const int64_t y4 = dst_y >> subsampling_y; + + int16x8_t tmp[15]; + warp_affine_horizontal_neon_i8mm(ref, width, height, stride, p_width, + p_height, alpha, beta, x4, y4, i, tmp); + warp_affine_vertical(pred, p_width, p_height, p_stride, is_compound, dst, + dst_stride, do_average, use_dist_wtd_comp_avg, gamma, + delta, y4, i, j, tmp, w0, w1); + } + } }
diff --git a/av1/common/arm/warp_plane_neon_i8mm.h b/av1/common/arm/warp_plane_neon_i8mm.h new file mode 100644 index 0000000..99355dc --- /dev/null +++ b/av1/common/arm/warp_plane_neon_i8mm.h
@@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025, 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. + */ +#ifndef AOM_AV1_COMMON_ARM_WARP_PLANE_NEON_I8MM_H_ +#define AOM_AV1_COMMON_ARM_WARP_PLANE_NEON_I8MM_H_ + +#include "aom_ports/mem.h" + +DECLARE_ALIGNED(16, extern const uint8_t, kTblIdx0_3[16]); + +#endif // AOM_AV1_COMMON_ARM_WARP_PLANE_NEON_I8MM_H_
diff --git a/av1/common/arm/warp_plane_sve.c b/av1/common/arm/warp_plane_sve.c index 10aee35..3d21072 100644 --- a/av1/common/arm/warp_plane_sve.c +++ b/av1/common/arm/warp_plane_sve.c
@@ -12,13 +12,10 @@ #include <arm_neon.h> #include "aom_dsp/arm/aom_neon_sve_bridge.h" +#include "convolve_neon_dotprod.h" +#include "convolve_neon_i8mm.h" #include "warp_plane_neon.h" - -DECLARE_ALIGNED(16, static const uint8_t, usdot_permute_idx[48]) = { - 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, - 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, - 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 -}; +#include "warp_plane_neon_i8mm.h" static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f4(const uint8x16_t in, int sx, int alpha) { @@ -87,14 +84,56 @@ return vreinterpretq_s16_u16(res); } -static AOM_FORCE_INLINE int16x8_t -horizontal_filter_4x1_f1_beta0(const uint8x16_t in, int16x8_t f_s16) { +static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1_6tap_beta0( + const uint8x16_t in, const int8x16_t filter, const uint8x16_t perm_tbl) { const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + // Permute samples ready for matrix multiply. + // { 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9 } + const uint8x16_t perm_samples = vqtbl1q_u8(in, perm_tbl); + + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum = vusmmlaq_s32(add_const, perm_samples, filter); + + uint16x8_t res = + vcombine_u16(vqrshrun_n_s32(sum, ROUND0_BITS), vdup_n_u16(0)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1_8tap_beta0( + const uint8x16_t in, const int8x16_t filter, const int32x4_t f0, + const uint8x16_t perm_tbl, const uint8x16_t tbl_idx0_3) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + const uint8x16_t perm_samples = vqtbl1q_u8(in, perm_tbl); + // Get samples 0..3 to apply tap 0 after matrix multiply. + const int32x4_t samples_0_3 = + vreinterpretq_s32_u8(vqtbl1q_u8(in, tbl_idx0_3)); + + // Calculate partial 7-tap convolution. + int32x4_t sum = vusmmlaq_s32(add_const, perm_samples, filter); + // Apply tap 0 and accumulate. + sum = vmlaq_s32(sum, samples_0_3, f0); + + uint16x8_t res = + vcombine_u16(vqrshrun_n_s32(sum, ROUND0_BITS), vdup_n_u16(0)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1(const uint8x16_t in, + int sx) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); int8x16_t f_s8 = vcombine_s8(vmovn_s16(f_s16), vmovn_s16(f_s16)); - uint8x16_t perm0 = vld1q_u8(&usdot_permute_idx[0]); - uint8x16_t perm1 = vld1q_u8(&usdot_permute_idx[16]); + uint8x16_t perm0 = vld1q_u8(&kDotProdPermuteTbl[0]); + uint8x16_t perm1 = vld1q_u8(&kDotProdPermuteTbl[16]); // Permute samples ready for dot product. // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } @@ -110,21 +149,63 @@ return vreinterpretq_s16_u16(res); } -static AOM_FORCE_INLINE int16x8_t horizontal_filter_4x1_f1(const uint8x16_t in, - int sx) { - int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); - return horizontal_filter_4x1_f1_beta0(in, f_s16); -} - -static AOM_FORCE_INLINE int16x8_t -horizontal_filter_8x1_f1_beta0(const uint8x16_t in, int16x8_t f_s16) { +static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1_6tap_beta0( + const uint8x16_t in, const int8x16_t filter, const uint8x16x2_t perm_tbl) { const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + // Permute samples ready for matrix multiply. + // { 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9 } + // { 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(in, perm_tbl.val[0]), + vqtbl1q_u8(in, perm_tbl.val[1]) }; + + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum0123 = vusmmlaq_s32(add_const, perm_samples[0], filter); + int32x4_t sum4567 = vusmmlaq_s32(add_const, perm_samples[1], filter); + + uint16x8_t res = vcombine_u16(vqrshrun_n_s32(sum0123, ROUND0_BITS), + vqrshrun_n_s32(sum4567, ROUND0_BITS)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1_8tap_beta0( + const uint8x16_t in, const int8x16_t filter, const int16x4_t f0, + const uint8x16x2_t perm_tbl) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + // Permute samples ready for matrix multiply. + // { 1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9, 10 } + // { 5, 6, 7, 8, 9, 10, 11, 12, 7, 8, 9, 10, 11, 12, 13, 14 } + uint8x16_t perm_samples[2] = { vqtbl1q_u8(in, perm_tbl.val[0]), + vqtbl1q_u8(in, perm_tbl.val[1]) }; + // Get samples 0..7 to apply tap 0 after matrix multiply. + int16x8_t samples_0_7 = vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(in))); + + // Calculate partial 7-tap convolution. + int32x4_t sum0123 = vusmmlaq_s32(add_const, perm_samples[0], filter); + int32x4_t sum4567 = vusmmlaq_s32(add_const, perm_samples[1], filter); + // Apply tap 0 and accumulate. + sum0123 = vmlal_s16(sum0123, vget_low_s16(samples_0_7), f0); + sum4567 = vmlal_s16(sum4567, vget_high_s16(samples_0_7), f0); + + uint16x8_t res = vcombine_u16(vqrshrun_n_s32(sum0123, ROUND0_BITS), + vqrshrun_n_s32(sum4567, ROUND0_BITS)); + + return vreinterpretq_s16_u16(res); +} + +static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1(const uint8x16_t in, + int sx) { + const int32x4_t add_const = vdupq_n_s32(1 << (8 + FILTER_BITS - 1)); + + int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); int8x16_t f_s8 = vcombine_s8(vmovn_s16(f_s16), vmovn_s16(f_s16)); - uint8x16_t perm0 = vld1q_u8(&usdot_permute_idx[0]); - uint8x16_t perm1 = vld1q_u8(&usdot_permute_idx[16]); - uint8x16_t perm2 = vld1q_u8(&usdot_permute_idx[32]); + uint8x16_t perm0 = vld1q_u8(&kDotProdPermuteTbl[0]); + uint8x16_t perm1 = vld1q_u8(&kDotProdPermuteTbl[16]); + uint8x16_t perm2 = vld1q_u8(&kDotProdPermuteTbl[32]); // Permute samples ready for dot product. // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } @@ -145,37 +226,6 @@ return vreinterpretq_s16_u16(res); } -static AOM_FORCE_INLINE int16x8_t horizontal_filter_8x1_f1(const uint8x16_t in, - int sx) { - int16x8_t f_s16 = vld1q_s16(av1_warped_filter[sx >> WARPEDDIFF_PREC_BITS]); - return horizontal_filter_8x1_f1_beta0(in, f_s16); -} - -static AOM_FORCE_INLINE void vertical_filter_4x1_f1(const int16x8_t *src, - int32x4_t *res, int sy) { - int16x4_t s0 = vget_low_s16(src[0]); - int16x4_t s1 = vget_low_s16(src[1]); - int16x4_t s2 = vget_low_s16(src[2]); - int16x4_t s3 = vget_low_s16(src[3]); - int16x4_t s4 = vget_low_s16(src[4]); - int16x4_t s5 = vget_low_s16(src[5]); - int16x4_t s6 = vget_low_s16(src[6]); - int16x4_t s7 = vget_low_s16(src[7]); - - int16x8_t f = vld1q_s16(av1_warped_filter[sy >> WARPEDDIFF_PREC_BITS]); - - int32x4_t m0123 = vmull_lane_s16(s0, vget_low_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, s1, vget_low_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, s2, vget_low_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, s3, vget_low_s16(f), 3); - m0123 = vmlal_lane_s16(m0123, s4, vget_high_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, s5, vget_high_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, s6, vget_high_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, s7, vget_high_s16(f), 3); - - *res = m0123; -} - static AOM_FORCE_INLINE void vertical_filter_4x1_f4(const int16x8_t *src, int32x4_t *res, int sy, int gamma) { @@ -199,43 +249,6 @@ *res = vcombine_s32(vmovn_s64(m01), vmovn_s64(m23)); } -static AOM_FORCE_INLINE void vertical_filter_8x1_f1(const int16x8_t *src, - int32x4_t *res_low, - int32x4_t *res_high, - int sy) { - int16x8_t s0 = src[0]; - int16x8_t s1 = src[1]; - int16x8_t s2 = src[2]; - int16x8_t s3 = src[3]; - int16x8_t s4 = src[4]; - int16x8_t s5 = src[5]; - int16x8_t s6 = src[6]; - int16x8_t s7 = src[7]; - - int16x8_t f = vld1q_s16(av1_warped_filter[sy >> WARPEDDIFF_PREC_BITS]); - - int32x4_t m0123 = vmull_lane_s16(vget_low_s16(s0), vget_low_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s1), vget_low_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s2), vget_low_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s3), vget_low_s16(f), 3); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s4), vget_high_s16(f), 0); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s5), vget_high_s16(f), 1); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s6), vget_high_s16(f), 2); - m0123 = vmlal_lane_s16(m0123, vget_low_s16(s7), vget_high_s16(f), 3); - - int32x4_t m4567 = vmull_lane_s16(vget_high_s16(s0), vget_low_s16(f), 0); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s1), vget_low_s16(f), 1); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s2), vget_low_s16(f), 2); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s3), vget_low_s16(f), 3); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s4), vget_high_s16(f), 0); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s5), vget_high_s16(f), 1); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s6), vget_high_s16(f), 2); - m4567 = vmlal_lane_s16(m4567, vget_high_s16(s7), vget_high_s16(f), 3); - - *res_low = m0123; - *res_high = m4567; -} - static AOM_FORCE_INLINE void vertical_filter_8x1_f8(const int16x8_t *src, int32x4_t *res_low, int32x4_t *res_high, int sy, @@ -271,13 +284,192 @@ *res_high = vcombine_s32(vmovn_s64(m45), vmovn_s64(m67)); } +static AOM_FORCE_INLINE void warp_affine_horizontal_sve( + const uint8_t *ref, int width, int height, int stride, int p_width, + int p_height, int16_t alpha, int16_t beta, const int64_t x4, + const int64_t y4, const int i, int16x8_t tmp[]) { + const int height_limit = AOMMIN(8, p_height - i) + 7; + + int32_t ix4 = (int32_t)(x4 >> WARPEDMODEL_PREC_BITS); + int32_t iy4 = (int32_t)(y4 >> WARPEDMODEL_PREC_BITS); + + int32_t sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + sx4 += alpha * (-4) + beta * (-4) + (1 << (WARPEDDIFF_PREC_BITS - 1)) + + (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS); + sx4 &= ~((1 << WARP_PARAM_REDUCE_BITS) - 1); + + if (warp_affine_special_case(ref, ix4, iy4, width, height, stride, + height_limit, tmp)) { + return; + } + + static const uint8_t kIotaArr[] = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 }; + const uint8x16_t indx = vld1q_u8(kIotaArr); + + const int out_of_boundary_left = -(ix4 - 6); + const int out_of_boundary_right = (ix4 + 8) - width; + + if (p_width == 4) { + if (beta == 0) { + if (alpha == 0) { + int16_t *f_ptr = + (int16_t *)(av1_warped_filter + (sx4 >> WARPEDDIFF_PREC_BITS)); + int16x8_t f_s16 = vld1q_s16(f_ptr); + const int8x8_t x_filter = vmovn_s16(f_s16); + if ((f_ptr[0] | f_ptr[1]) == 0) { + uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl = vaddq_u8(perm_tbl, vdupq_n_u8(2)); + // Stagger filter for use with the matrix multiply instructions. + // { f2, f3, f4, f5, f6, f7, 0, 0, 0, f2, f3, f4, f5, f6, f7, 0 } + const int8x16_t filter = vcombine_s8(vext_s8(x_filter, x_filter, 2), + vext_s8(x_filter, x_filter, 1)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[0] | f_ptr[7]) == 0) { + uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl = vaddq_u8(perm_tbl, vdupq_n_u8(1)); + // Stagger filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, 0, 0, 0, f1, f2, f3, f4, f5, f6, 0 } + const int8x16_t filter = + vcombine_s8(vext_s8(x_filter, x_filter, 1), x_filter); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[6] | f_ptr[7]) == 0) { + const uint8x16_t perm_tbl = vld1q_u8(kMatMul6PermuteTbl); + // Stagger filter for use with the matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t filter = + vcombine_s8(x_filter, vext_s8(x_filter, x_filter, 7)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_6tap_beta0, filter, + perm_tbl); + } else { + const uint8x16_t perm_tbl = vld1q_u8(kMatMul8PermuteTbl); + const uint8x16_t tbl_idx0_3 = vld1q_u8(kTblIdx0_3); + + // Stagger filter for use with the matrix multiply + // instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const int8x16_t filter = vcombine_s8( + vext_s8(x_filter, vdup_n_s8(0), 1), vset_lane_s8(0, x_filter, 0)); + const int32x4_t f0 = vdupq_n_s32(f_ptr[0]); + + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1_8tap_beta0, filter, + f0, perm_tbl, tbl_idx0_3); + } + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, sx4, alpha); + } + } else { + if (alpha == 0) { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f1, + (sx4 + beta * (k - 3))); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_4x1_f4, (sx4 + beta * (k - 3)), + alpha); + } + } + } else { + if (beta == 0) { + if (alpha == 0) { + int16_t *f_ptr = + (int16_t *)(av1_warped_filter + (sx4 >> WARPEDDIFF_PREC_BITS)); + int16x8_t f_s16 = vld1q_s16(f_ptr); + const int8x8_t x_filter = vmovn_s16(f_s16); + if ((f_ptr[0] | f_ptr[1]) == 0) { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl.val[0] = vaddq_u8(perm_tbl.val[0], vdupq_n_u8(2)); + perm_tbl.val[1] = vaddq_u8(perm_tbl.val[1], vdupq_n_u8(2)); + // Stagger filter for use with the matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t filter = vcombine_s8(vext_s8(x_filter, x_filter, 2), + vext_s8(x_filter, x_filter, 1)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[0] | f_ptr[7]) == 0) { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); + // Offset the permutation table to match filter layout. + perm_tbl.val[0] = vaddq_u8(perm_tbl.val[0], vdupq_n_u8(1)); + perm_tbl.val[1] = vaddq_u8(perm_tbl.val[1], vdupq_n_u8(1)); + // Stagger filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, 0, 0, 0, f1, f2, f3, f4, f5, f6, 0 } + const int8x16_t filter = + vcombine_s8(vext_s8(x_filter, x_filter, 1), x_filter); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_6tap_beta0, filter, + perm_tbl); + } else if ((f_ptr[6] | f_ptr[7]) == 0) { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul6PermuteTbl); + // Stagger filter for use with the matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t filter = + vcombine_s8(x_filter, vext_s8(x_filter, x_filter, 7)); + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_6tap_beta0, filter, + perm_tbl); + } else { + uint8x16x2_t perm_tbl = vld1q_u8_x2(kMatMul8PermuteTbl); + // Stagger filter for use with the matrix multiply instructions. + // { f1, f2, f3, f4, f5, f6, f7, 0, 0, f1, f2, f3, f4, f5, f6, f7 } + const int8x16_t filter = vcombine_s8( + vext_s8(x_filter, vdup_n_s8(0), 1), vset_lane_s8(0, x_filter, 0)); + + const int16x4_t f0 = vdup_n_s16(f_ptr[0]); + + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1_8tap_beta0, filter, + f0, perm_tbl); + } + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, sx4, alpha); + } + } else { + if (alpha == 0) { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f1, + (sx4 + beta * (k - 3))); + } else { + APPLY_HORIZONTAL_SHIFT(horizontal_filter_8x1_f8, (sx4 + beta * (k - 3)), + alpha); + } + } + } +} + void av1_warp_affine_sve(const int32_t *mat, const uint8_t *ref, int width, int height, int stride, uint8_t *pred, int p_col, int p_row, int p_width, int p_height, int p_stride, int subsampling_x, int subsampling_y, ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, int16_t delta) { - av1_warp_affine_common(mat, ref, width, height, stride, pred, p_col, p_row, - p_width, p_height, p_stride, subsampling_x, - subsampling_y, conv_params, alpha, beta, gamma, delta); + const int w0 = conv_params->fwd_offset; + const int w1 = conv_params->bck_offset; + const int is_compound = conv_params->is_compound; + uint16_t *const dst = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + const int do_average = conv_params->do_average; + const int use_dist_wtd_comp_avg = conv_params->use_dist_wtd_comp_avg; + + assert(IMPLIES(is_compound, dst != NULL)); + assert(IMPLIES(do_average, is_compound)); + + for (int i = 0; i < p_height; i += 8) { + for (int j = 0; j < p_width; j += 8) { + const int32_t src_x = (p_col + j + 4) << subsampling_x; + const int32_t src_y = (p_row + i + 4) << subsampling_y; + const int64_t dst_x = + (int64_t)mat[2] * src_x + (int64_t)mat[3] * src_y + (int64_t)mat[0]; + const int64_t dst_y = + (int64_t)mat[4] * src_x + (int64_t)mat[5] * src_y + (int64_t)mat[1]; + + const int64_t x4 = dst_x >> subsampling_x; + const int64_t y4 = dst_y >> subsampling_y; + + int16x8_t tmp[15]; + warp_affine_horizontal_sve(ref, width, height, stride, p_width, p_height, + alpha, beta, x4, y4, i, tmp); + warp_affine_vertical(pred, p_width, p_height, p_stride, is_compound, dst, + dst_stride, do_average, use_dist_wtd_comp_avg, gamma, + delta, y4, i, j, tmp, w0, w1); + } + } }
diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h index 0961c44..e083c2b 100644 --- a/av1/common/av1_common_int.h +++ b/av1/common/av1_common_int.h
@@ -139,6 +139,7 @@ unsigned int ref_display_order_hint[INTER_REFS_PER_FRAME]; // Frame's level within the hierarchical structure. unsigned int pyramid_level; + int base_qindex; MV_REF *mvs; uint8_t *seg_map; struct segmentation seg; @@ -703,8 +704,9 @@ /*! * Flag indicating whether quantization matrices are being used: - * - If true, qm_level_y, qm_level_u and qm_level_v indicate the level - * indices to be used to access appropriate global quant matrix tables. + * - If true, qmatrix_level_y, qmatrix_level_u and qmatrix_level_v indicate + * the level indices to be used to access appropriate global quant matrix + * tables. * - If false, we implicitly use level index 'NUM_QM_LEVELS - 1'. */ bool using_qmatrix;
diff --git a/av1/common/av1_inv_txfm1d.c b/av1/common/av1_inv_txfm1d.c index 37c2091..49380e8 100644 --- a/av1/common/av1_inv_txfm1d.c +++ b/av1/common/av1_inv_txfm1d.c
@@ -657,12 +657,12 @@ const int8_t *stage_range) { int bit = cos_bit; const int32_t *sinpi = sinpi_arr(bit); - int32_t s0, s1, s2, s3, s4, s5, s6, s7; + int64_t s0, s1, s2, s3, s4, s5, s6, s7; - int32_t x0 = input[0]; - int32_t x1 = input[1]; - int32_t x2 = input[2]; - int32_t x3 = input[3]; + int64_t x0 = input[0]; + int64_t x1 = input[1]; + int64_t x2 = input[2]; + int64_t x3 = input[3]; if (!(x0 | x1 | x2 | x3)) { output[0] = output[1] = output[2] = output[3] = 0; @@ -672,37 +672,37 @@ assert(sinpi[1] + sinpi[2] == sinpi[4]); // stage 1 - s0 = range_check_value(sinpi[1] * x0, stage_range[1] + bit); - s1 = range_check_value(sinpi[2] * x0, stage_range[1] + bit); - s2 = range_check_value(sinpi[3] * x1, stage_range[1] + bit); - s3 = range_check_value(sinpi[4] * x2, stage_range[1] + bit); - s4 = range_check_value(sinpi[1] * x2, stage_range[1] + bit); - s5 = range_check_value(sinpi[2] * x3, stage_range[1] + bit); - s6 = range_check_value(sinpi[4] * x3, stage_range[1] + bit); + s0 = range_check_value64(sinpi[1] * x0, stage_range[1] + bit); + s1 = range_check_value64(sinpi[2] * x0, stage_range[1] + bit); + s2 = range_check_value64(sinpi[3] * x1, stage_range[1] + bit); + s3 = range_check_value64(sinpi[4] * x2, stage_range[1] + bit); + s4 = range_check_value64(sinpi[1] * x2, stage_range[1] + bit); + s5 = range_check_value64(sinpi[2] * x3, stage_range[1] + bit); + s6 = range_check_value64(sinpi[4] * x3, stage_range[1] + bit); // stage 2 // NOTICE: (x0 - x2) here may use one extra bit compared to the // opt_range_row/col specified in av1_gen_inv_stage_range() - s7 = range_check_value((x0 - x2) + x3, stage_range[2]); + s7 = range_check_value64((x0 - x2) + x3, stage_range[2]); // stage 3 - s0 = range_check_value(s0 + s3, stage_range[3] + bit); - s1 = range_check_value(s1 - s4, stage_range[3] + bit); - s3 = range_check_value(s2, stage_range[3] + bit); - s2 = range_check_value(sinpi[3] * s7, stage_range[3] + bit); + s0 = range_check_value64(s0 + s3, stage_range[3] + bit); + s1 = range_check_value64(s1 - s4, stage_range[3] + bit); + s3 = range_check_value64(s2, stage_range[3] + bit); + s2 = range_check_value64(sinpi[3] * s7, stage_range[3] + bit); // stage 4 - s0 = range_check_value(s0 + s5, stage_range[4] + bit); - s1 = range_check_value(s1 - s6, stage_range[4] + bit); + s0 = range_check_value64(s0 + s5, stage_range[4] + bit); + s1 = range_check_value64(s1 - s6, stage_range[4] + bit); // stage 5 - x0 = range_check_value(s0 + s3, stage_range[5] + bit); - x1 = range_check_value(s1 + s3, stage_range[5] + bit); - x2 = range_check_value(s2, stage_range[5] + bit); - x3 = range_check_value(s0 + s1, stage_range[5] + bit); + x0 = range_check_value64(s0 + s3, stage_range[5] + bit); + x1 = range_check_value64(s1 + s3, stage_range[5] + bit); + x2 = range_check_value64(s2, stage_range[5] + bit); + x3 = range_check_value64(s0 + s1, stage_range[5] + bit); // stage 6 - x3 = range_check_value(x3 - s3, stage_range[6] + bit); + x3 = range_check_value64(x3 - s3, stage_range[6] + bit); output[0] = round_shift(x0, bit); output[1] = round_shift(x1, bit);
diff --git a/av1/common/av1_rtcd_defs.pl b/av1/common/av1_rtcd_defs.pl index 4c90393..a3d0d12 100644 --- a/av1/common/av1_rtcd_defs.pl +++ b/av1/common/av1_rtcd_defs.pl
@@ -126,12 +126,7 @@ # FILTER_INTRA predictor functions add_proto qw/void av1_filter_intra_predictor/, "uint8_t *dst, ptrdiff_t stride, TX_SIZE tx_size, const uint8_t *above, const uint8_t *left, int mode"; -# TODO(aomedia:349436249): enable NEON for armv7 after SIGBUS is fixed. -if (aom_config("AOM_ARCH_ARM") eq "yes" && aom_config("AOM_ARCH_AARCH64") eq "") { - specialize qw/av1_filter_intra_predictor sse4_1/; -} else { - specialize qw/av1_filter_intra_predictor sse4_1 neon/; -} +specialize qw/av1_filter_intra_predictor sse4_1 neon neon_i8mm/; # High bitdepth functions @@ -557,6 +552,9 @@ if ((aom_config("CONFIG_REALTIME_ONLY") ne "yes") || (aom_config("CONFIG_AV1_DECODER") eq "yes")) { add_proto qw/void av1_warp_affine/, "const int32_t *mat, const uint8_t *ref, int width, int height, int stride, uint8_t *pred, int p_col, int p_row, int p_width, int p_height, int p_stride, int subsampling_x, int subsampling_y, ConvolveParams *conv_params, int16_t alpha, int16_t beta, int16_t gamma, int16_t delta"; specialize qw/av1_warp_affine sse4_1 avx2 neon neon_i8mm sve/; + if (aom_config("CONFIG_HIGHWAY") eq "yes") { + specialize qw/av1_warp_affine avx512/; + } } # LOOP_RESTORATION functions @@ -610,15 +608,15 @@ specialize qw/av1_convolve_y_sr sse2 avx2 neon neon_dotprod neon_i8mm rvv/; specialize qw/av1_convolve_y_sr_intrabc neon rvv/; specialize qw/av1_convolve_2d_scale sse4_1 neon neon_dotprod neon_i8mm/; - specialize qw/av1_dist_wtd_convolve_2d ssse3 avx2 neon neon_dotprod neon_i8mm/; - specialize qw/av1_dist_wtd_convolve_2d_copy sse2 avx2 neon/; - specialize qw/av1_dist_wtd_convolve_x sse2 avx2 neon neon_dotprod neon_i8mm/; - specialize qw/av1_dist_wtd_convolve_y sse2 avx2 neon/; + specialize qw/av1_dist_wtd_convolve_2d ssse3 avx2 neon neon_dotprod neon_i8mm rvv/; + specialize qw/av1_dist_wtd_convolve_2d_copy sse2 avx2 neon rvv/; + specialize qw/av1_dist_wtd_convolve_x sse2 avx2 neon neon_dotprod neon_i8mm rvv/; + specialize qw/av1_dist_wtd_convolve_y sse2 avx2 neon neon_dotprod neon_i8mm rvv/; if(aom_config("CONFIG_AV1_HIGHBITDEPTH") eq "yes") { - specialize qw/av1_highbd_dist_wtd_convolve_2d sse4_1 avx2 neon sve2/; - specialize qw/av1_highbd_dist_wtd_convolve_x sse4_1 avx2 neon sve2/; - specialize qw/av1_highbd_dist_wtd_convolve_y sse4_1 avx2 neon sve2/; - specialize qw/av1_highbd_dist_wtd_convolve_2d_copy sse4_1 avx2 neon/; + specialize qw/av1_highbd_dist_wtd_convolve_2d sse4_1 avx2 neon sve2 rvv/; + specialize qw/av1_highbd_dist_wtd_convolve_x sse4_1 avx2 neon sve2 rvv/; + specialize qw/av1_highbd_dist_wtd_convolve_y sse4_1 avx2 neon sve2 rvv/; + specialize qw/av1_highbd_dist_wtd_convolve_2d_copy sse4_1 avx2 neon rvv/; specialize qw/av1_highbd_convolve_2d_sr ssse3 avx2 neon sve2 rvv/; specialize qw/av1_highbd_convolve_2d_sr_intrabc neon rvv/; specialize qw/av1_highbd_convolve_x_sr ssse3 avx2 neon sve2 rvv/;
diff --git a/av1/common/av1_txfm.c b/av1/common/av1_txfm.c index 751851c..854e85b 100644 --- a/av1/common/av1_txfm.c +++ b/av1/common/av1_txfm.c
@@ -9,6 +9,8 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <inttypes.h> + #include "config/aom_dsp_rtcd.h" #include "config/av1_rtcd.h"
diff --git a/av1/common/av1_txfm.h b/av1/common/av1_txfm.h index c3c3123..8603c3d 100644 --- a/av1/common/av1_txfm.h +++ b/av1/common/av1_txfm.h
@@ -13,6 +13,7 @@ #define AOM_AV1_COMMON_AV1_TXFM_H_ #include <assert.h> +#include <inttypes.h> #include <math.h> #include <stdio.h> @@ -94,6 +95,26 @@ return value; } +static inline int64_t range_check_value64(int64_t value, int8_t bit) { +#if CONFIG_COEFFICIENT_RANGE_CHECKING + const int64_t max_value = (1LL << (bit - 1)) - 1; + const int64_t min_value = -(1LL << (bit - 1)); + if (value < min_value || value > max_value) { + fprintf(stderr, "coeff out of bit range, value: %" PRId64 " bit %d\n", + value, bit); +#if !CONFIG_AV1_ENCODER + assert(0); +#endif + } +#endif // CONFIG_COEFFICIENT_RANGE_CHECKING +#if DO_RANGE_CHECK_CLAMP + bit = AOMMIN(bit, 63); + return clamp64(value, -(1LL << (bit - 1)), (1LL << (bit - 1)) - 1); +#endif // DO_RANGE_CHECK_CLAMP + (void)bit; + return value; +} + static inline int32_t round_shift(int64_t value, int bit) { assert(bit >= 1); return (int32_t)((value + (1ll << (bit - 1))) >> bit);
diff --git a/av1/common/blockd.h b/av1/common/blockd.h index b95235f..db91270 100644 --- a/av1/common/blockd.h +++ b/av1/common/blockd.h
@@ -925,6 +925,11 @@ * 'cpi->tile_thr_data[t].td->mb.tmp_pred_bufs'. */ uint8_t *tmp_obmc_bufs[2]; + + /*! + * Temporary buffer used for upsampled prediction. + */ + uint8_t *tmp_upsample_pred; } MACROBLOCKD; /*!\cond */
diff --git a/av1/common/common.h b/av1/common/common.h index 8bcfed9..598db30 100644 --- a/av1/common/common.h +++ b/av1/common/common.h
@@ -27,17 +27,17 @@ #endif // Only need this for fixed-size arrays, for structs just assign. -#define av1_copy(dest, src) \ - do { \ - assert(sizeof(dest) == sizeof(src)); \ - memcpy(dest, src, sizeof(src)); \ +#define av1_copy(dest, src) \ + do { \ + static_assert(sizeof(dest) == sizeof(src), ""); \ + memcpy(dest, src, sizeof(src)); \ } while (0) // Use this for variably-sized arrays. -#define av1_copy_array(dest, src, n) \ - do { \ - assert(sizeof(*(dest)) == sizeof(*(src))); \ - memcpy(dest, src, n * sizeof(*(src))); \ +#define av1_copy_array(dest, src, n) \ + do { \ + static_assert(sizeof(*(dest)) == sizeof(*(src)), ""); \ + memcpy(dest, src, n * sizeof(*(src))); \ } while (0) #define av1_zero(dest) memset(&(dest), 0, sizeof(dest))
diff --git a/av1/common/filter.h b/av1/common/filter.h index d1b76bd..fb85ab2 100644 --- a/av1/common/filter.h +++ b/av1/common/filter.h
@@ -301,20 +301,16 @@ if (filter_params->taps == 12) { return 12; } - if (filter[0] | filter[7]) { + if (filter[0] || filter[7]) { return 8; } - if (filter[1] | filter[6]) { + if (filter[1] || filter[6]) { return 6; } -#if CONFIG_SVT_AV1 - if (filter[2] | filter[5]) { + if (filter[2] || filter[5]) { return 4; } return 2; -#else - return 4; -#endif } #ifdef __cplusplus
diff --git a/av1/common/idct.h b/av1/common/idct.h index c02b70e..2dda093 100644 --- a/av1/common/idct.h +++ b/av1/common/idct.h
@@ -12,6 +12,8 @@ #ifndef AOM_AV1_COMMON_IDCT_H_ #define AOM_AV1_COMMON_IDCT_H_ +#include <assert.h> + #include "config/aom_config.h" #include "av1/common/blockd.h" @@ -40,7 +42,7 @@ int eob, int bd); static inline const int32_t *cast_to_int32(const tran_low_t *input) { - assert(sizeof(int32_t) == sizeof(tran_low_t)); + static_assert(sizeof(int32_t) == sizeof(tran_low_t), ""); return (const int32_t *)input; }
diff --git a/av1/common/mvref_common.c b/av1/common/mvref_common.c index fdb2c8d..3f86c1d 100644 --- a/av1/common/mvref_common.c +++ b/av1/common/mvref_common.c
@@ -844,6 +844,7 @@ cm->cur_frame->order_hint = cm->current_frame.order_hint; cm->cur_frame->display_order_hint = cm->current_frame.display_order_hint; cm->cur_frame->pyramid_level = cm->current_frame.pyramid_level; + cm->cur_frame->base_qindex = cm->quant_params.base_qindex; cm->cur_frame->filter_level[0] = -1; cm->cur_frame->filter_level[1] = -1; MV_REFERENCE_FRAME ref_frame;
diff --git a/av1/common/quant_common.h b/av1/common/quant_common.h index 965a746..6dc82c0 100644 --- a/av1/common/quant_common.h +++ b/av1/common/quant_common.h
@@ -116,17 +116,17 @@ qm_level = 10; } else if (qindex <= 60) { qm_level = 9; - } else if (qindex <= 100) { + } else if (qindex <= 90) { qm_level = 8; } else if (qindex <= 120) { qm_level = 7; - } else if (qindex <= 140) { + } else if (qindex <= 130) { qm_level = 6; - } else if (qindex <= 160) { + } else if (qindex <= 140) { qm_level = 5; - } else if (qindex <= 200) { + } else if (qindex <= 160) { qm_level = 4; - } else if (qindex <= 220) { + } else if (qindex <= 200) { qm_level = 3; } else { qm_level = 2;
diff --git a/av1/common/riscv/compound_convolve_rvv.c b/av1/common/riscv/compound_convolve_rvv.c new file mode 100644 index 0000000..c1eb587 --- /dev/null +++ b/av1/common/riscv/compound_convolve_rvv.c
@@ -0,0 +1,2964 @@ +/* + * Copyright (c) 2026, 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 <assert.h> +#include <riscv_vector.h> + +#include "av1/common/riscv/compound_convolve_rvv.h" +#include "config/aom_config.h" +#include "config/av1_rtcd.h" + +static uint16_t trans_data[64] = { + 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, 7, 15, 23, 31, 39, 47, 55, 63, +}; + +static inline void transpose_elems_inplace_s16_8x8_rvv( + vint16m1_t *d0, vint16m1_t *d1, vint16m1_t *d2, vint16m1_t *d3, + vint16m1_t *d4, vint16m1_t *d5, vint16m1_t *d6, vint16m1_t *d7, + const vuint16m8_t trans_index) { + vint16m8_t matrix_8x8 = __riscv_vundefined_i16m8(); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 0, *d0); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 1, *d1); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 2, *d2); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 3, *d3); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 4, *d4); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 5, *d5); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 6, *d6); + matrix_8x8 = __riscv_vset_v_i16m1_i16m8(matrix_8x8, 7, *d7); + + // transpose_elems_inplace_s16_8x8 + vint16m8_t trans_matrix = + __riscv_vrgather_vv_i16m8(matrix_8x8, trans_index, 64); + + *d0 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 0); + *d1 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 1); + *d2 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 2); + *d3 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 3); + *d4 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 4); + *d5 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 5); + *d6 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 6); + *d7 = __riscv_vget_v_i16m8_i16m1(trans_matrix, 7); +} + +static inline vint16m1_t convolve4_4_2d_h_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const int16_t *filter, const int16_t horiz_const, + size_t vl) { + vint16m1_t sum = __riscv_vmul_vx_i16m1(s0, filter[0], vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[1], s1, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[2], s2, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[3], s3, vl); + sum = __riscv_vadd_vx_i16m1(sum, horiz_const, vl); + + // We halved the convolution filter values so -1 from the right shift. + return __riscv_vsra_vx_i16m1(sum, ROUND0_BITS - 1, vl); +} + +static inline vint16m1_t convolve8_8_2d_h_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, const int16_t *filter, + const int16_t horiz_const, size_t vl) { + vint16m1_t sum = __riscv_vmul_vx_i16m1(s0, filter[0], vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[1], s1, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[2], s2, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[3], s3, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[4], s4, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[5], s5, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[6], s6, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[7], s7, vl); + sum = __riscv_vadd_vx_i16m1(sum, horiz_const, vl); + + // We halved the convolution filter values so -1 from the right shift. + return __riscv_vsra_vx_i16m1(sum, ROUND0_BITS - 1, vl); +} + +static inline void dist_wtd_convolve_2d_horiz_rvv( + const uint8_t *src, int src_stride, int16_t *im_block, const int im_stride, + const int16_t *x_filter_ptr, const int im_h, int w) { + const int bd = 8; + // A shim of 1 << ((ROUND0_BITS - 1) - 1) enables us to use non-rounding + // shifts - which are generally faster than rounding shifts on modern CPUs. + // (The extra -1 is needed because we halved the filter values.) + const int16_t horiz_const = + ((1 << (bd + FILTER_BITS - 2)) + (1 << ((ROUND0_BITS - 1) - 1))); + + const uint8_t *src_ptr = src; + int16_t *dst_ptr = im_block; + int dst_stride = im_stride; + int height = im_h; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // 4-tap filters are used for blocks having width <= 4. + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16_t *x_filter = x_filter_ptr + 2; + int16_t filter[4]; + for (int i = 0; i < 4; i++) filter[i] = *x_filter++ >> 1; + + src_ptr += 2; + + do { + // load + vuint8mf2_t t0 = __riscv_vle8_v_u8mf2(src_ptr + 0 * src_stride, + 8); // a0 a1 a2 a3 a4 a5 a6 a7 + vuint8mf2_t t1 = __riscv_vle8_v_u8mf2(src_ptr + 1 * src_stride, + 8); // b0 b1 b2 b3 b4 b5 b6 b7 + + __builtin_prefetch(dst_ptr + 0 * dst_stride); + __builtin_prefetch(dst_ptr + 1 * dst_stride); + + // widen to 16-bit + vint16m1_t s00 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, 8)); + vint16m1_t s01 = + __riscv_vslide1down_vx_i16m1(s00, 0, 8); // a1 a2 a3 a4 a5 a6 a7 0 + vint16m1_t s02 = + __riscv_vslide1down_vx_i16m1(s01, 0, 8); // a2 a3 a4 a5 a6 a7 0 0 + vint16m1_t s03 = + __riscv_vslide1down_vx_i16m1(s02, 0, 8); // a3 a4 a5 a6 a7 0 0 0 + + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, 8)); + vint16m1_t s11 = + __riscv_vslide1down_vx_i16m1(s10, 0, 8); // b1 b2 b3 b4 b5 b6 b7 0 + vint16m1_t s12 = + __riscv_vslide1down_vx_i16m1(s11, 0, 8); // b2 b3 b4 b5 b6 b7 0 0 + vint16m1_t s13 = + __riscv_vslide1down_vx_i16m1(s12, 0, 8); // b3 b4 b5 b6 b7 0 0 0 + + // perform convolution + vint16m1_t d0 = + convolve4_4_2d_h_rvv(s00, s01, s02, s03, filter, horiz_const, vl); + vint16m1_t d1 = + convolve4_4_2d_h_rvv(s10, s11, s12, s13, filter, horiz_const, vl); + + // store result + __riscv_vse16_v_i16m1(dst_ptr + 0 * dst_stride, d0, vl); + __riscv_vse16_v_i16m1(dst_ptr + 1 * dst_stride, d1, vl); + src_ptr += 2 * src_stride; + dst_ptr += 2 * dst_stride; + height -= 2; + } while (height >= 2); + + if (height > 0) { + vuint8mf2_t t0 = __riscv_vle8_v_u8mf2(src_ptr + 0 * src_stride, + 8); // a0 a1 a2 a3 a4 a5 a6 a7 + + // widen to 16-bit + vint16m1_t s00 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, 8)); + vint16m1_t s01 = + __riscv_vslide1down_vx_i16m1(s00, 0, 8); // a1 a2 a3 a4 a5 a6 a7 0 + vint16m1_t s02 = + __riscv_vslide1down_vx_i16m1(s01, 0, 8); // a2 a3 a4 a5 a6 a7 0 0 + vint16m1_t s03 = + __riscv_vslide1down_vx_i16m1(s02, 0, 8); // a3 a4 a5 a6 a7 0 0 0 + + // perform convolution + vint16m1_t d0 = + convolve4_4_2d_h_rvv(s00, s01, s02, s03, filter, horiz_const, vl); + + // store result + __riscv_vse16_v_i16m1(dst_ptr + 0 * dst_stride, d0, vl); + } + } else { + // Filter values are even, so halve to reduce intermediate precision reqs. + int16_t filter[8]; + for (int i = 0; i < 8; i++) filter[i] = *x_filter_ptr++ >> 1; + + // for VLEN = 128 case + if ((vl == 8) && (w > 8) && (height >= 8)) { + vuint16m8_t trans_index = __riscv_vle16_v_u16m8(trans_data, 64); + + while (height >= 8) { + const uint8_t *s = src_ptr; + int16_t *d = dst_ptr; + int width = w; + + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + __builtin_prefetch(d + 4 * dst_stride); + __builtin_prefetch(d + 5 * dst_stride); + __builtin_prefetch(d + 6 * dst_stride); + __builtin_prefetch(d + 7 * dst_stride); + + s += 7; + + do { + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s7 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vint16m1_t d0 = convolve8_8_2d_h_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, horiz_const, vl); + vint16m1_t d1 = convolve8_8_2d_h_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + filter, horiz_const, vl); + vint16m1_t d2 = convolve8_8_2d_h_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + filter, horiz_const, vl); + vint16m1_t d3 = convolve8_8_2d_h_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + filter, horiz_const, vl); + vint16m1_t d4 = convolve8_8_2d_h_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + filter, horiz_const, vl); + vint16m1_t d5 = convolve8_8_2d_h_rvv(s5, s6, s7, s8, s9, s10, s11, + s12, filter, horiz_const, vl); + vint16m1_t d6 = convolve8_8_2d_h_rvv(s6, s7, s8, s9, s10, s11, s12, + s13, filter, horiz_const, vl); + vint16m1_t d7 = convolve8_8_2d_h_rvv(s7, s8, s9, s10, s11, s12, s13, + s14, filter, horiz_const, vl); + + // transpose + transpose_elems_inplace_s16_8x8_rvv(&d0, &d1, &d2, &d3, &d4, &d5, &d6, + &d7, trans_index); + + // Store result + store_s16_8x8(d, dst_stride, d0, d1, d2, d3, d4, d5, d6, d7, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8; + d += 8; + width -= 8; + } while (width != 0); + src_ptr += 8 * src_stride; + dst_ptr += 8 * dst_stride; + height -= 8; + } + } + + while (height > 0) { + const uint8_t *s = src_ptr; + int16_t *d = dst_ptr; + int width = w; + + __builtin_prefetch(d); + + do { + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_u8_8x8(s, 1, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit integers + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vint16m1_t d0 = convolve8_8_2d_h_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, horiz_const, vl); + + // Store result + __riscv_vse16_v_i16m1(d, d0, vl); + + s += vl; + d += vl; + width -= vl; + } while (width > 0); + src_ptr += src_stride; + dst_ptr += dst_stride; + height--; + } + } +} + +void av1_dist_wtd_convolve_2d_rvv(const uint8_t *src, int src_stride, + uint8_t *dst8, int dst8_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, + const int subpel_x_qn, const int subpel_y_qn, + ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + DECLARE_ALIGNED(16, int16_t, + im_block[(MAX_SB_SIZE + SUBPEL_TAPS - 1) * MAX_SB_SIZE]); + + const int y_filter_taps = get_filter_tap(filter_params_y, subpel_y_qn); + const int clamped_y_taps = y_filter_taps < 6 ? 6 : y_filter_taps; + + const int im_h = h + clamped_y_taps - 1; + const int im_stride = MAX_SB_SIZE; + const int vert_offset = clamped_y_taps / 2 - 1; + const int horiz_offset = filter_params_x->taps / 2 - 1; + const uint8_t *src_ptr = src - vert_offset * src_stride - horiz_offset; + const int16_t *x_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_x, subpel_x_qn & SUBPEL_MASK); + const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_y, subpel_y_qn & SUBPEL_MASK); + + dist_wtd_convolve_2d_horiz_rvv(src_ptr, src_stride, im_block, im_stride, + x_filter_ptr, im_h, w); + + if (clamped_y_taps == 6) { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_2d_vert_6tap_dist_wtd_avg_rvv( + im_block, im_stride, dst8, dst8_stride, conv_params, y_filter_ptr, + h, w); + } else { + dist_wtd_convolve_2d_vert_6tap_avg_rvv(im_block, im_stride, dst8, + dst8_stride, conv_params, + y_filter_ptr, h, w); + } + } else { + dist_wtd_convolve_2d_vert_6tap_rvv(im_block, im_stride, conv_params, + y_filter_ptr, h, w); + } + } else { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_2d_vert_8tap_dist_wtd_avg_rvv( + im_block, im_stride, dst8, dst8_stride, conv_params, y_filter_ptr, + h, w); + } else { + dist_wtd_convolve_2d_vert_8tap_avg_rvv(im_block, im_stride, dst8, + dst8_stride, conv_params, + y_filter_ptr, h, w); + } + } else { + dist_wtd_convolve_2d_vert_8tap_rvv(im_block, im_stride, conv_params, + y_filter_ptr, h, w); + } + } +} + +static inline void dist_wtd_convolve_2d_copy_dist_wtd_avg_rvv( + const uint8_t *src, int src_stride, uint8_t *dst8, int dst8_stride, int w, + int h, ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const uint16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint8_t shift_by_bits = 1 << (FILTER_BITS - ROUND0_BITS); + + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int height = h; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + vuint16mf2_t round_offset_u16 = __riscv_vmv_v_x_u16mf2(round_offset, vl); + do { + // load 4 block of uint8_t + vuint8mf4_t s0 = __riscv_vle8_v_u8mf4(src + 0 * src_stride, vl); + vuint8mf4_t s1 = __riscv_vle8_v_u8mf4(src + 1 * src_stride, vl); + vuint8mf4_t s2 = __riscv_vle8_v_u8mf4(src + 2 * src_stride, vl); + vuint8mf4_t s3 = __riscv_vle8_v_u8mf4(src + 3 * src_stride, vl); + + // apply shift and add round offset + vuint16mf2_t d0 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s0, vl); + vuint16mf2_t d1 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s1, vl); + vuint16mf2_t d2 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s2, vl); + vuint16mf2_t d3 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s3, vl); + + // load existing dst values + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + // compute distance-weighted average + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, &d0_u8, + &d1_u8, &d2_u8, &d3_u8, vl); + + // store results + __riscv_vse8_v_u8mf4(dst8 + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(dst8 + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(dst8 + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(dst8 + 3 * dst8_stride, d3_u8, vl); + + // update pointers + src += 4 * src_stride; + dst += 4 * dst_stride; + dst8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + } else { + vuint16m1_t round_offset_u16 = __riscv_vmv_v_x_u16m1(round_offset, vl); + do { + const uint8_t *s = src; + CONV_BUF_TYPE *d = dst; + uint8_t *d_u8 = dst8; + int width = w; + + do { + // load 4 block of uint8_t + vuint8mf2_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3, vl); + + // apply shift and add round offset + vuint16m1_t d0 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s0, vl); + vuint16m1_t d1 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s1, vl); + vuint16m1_t d2 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s2, vl); + vuint16m1_t d3 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s3, vl); + + // load existing dst values + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + // compute distance-weighted average + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + + // store results + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + + // update pointers + s += vl; + d += vl; + d_u8 += vl; + width -= vl; + } while (width > 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + dst8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + } +} + +static inline void dist_wtd_convolve_2d_copy_avg_rvv( + const uint8_t *src, int src_stride, uint8_t *dst8, int dst8_stride, int w, + int h, ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const uint16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint8_t shift_by_bits = 1 << (FILTER_BITS - ROUND0_BITS); + + CONV_BUF_TYPE *dst = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int height = h; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + vuint16mf2_t round_offset_u16 = __riscv_vmv_v_x_u16mf2(round_offset, vl); + do { + // load 4 block of uint8_t + vuint8mf4_t s0 = __riscv_vle8_v_u8mf4(src + 0 * src_stride, vl); + vuint8mf4_t s1 = __riscv_vle8_v_u8mf4(src + 1 * src_stride, vl); + vuint8mf4_t s2 = __riscv_vle8_v_u8mf4(src + 2 * src_stride, vl); + vuint8mf4_t s3 = __riscv_vle8_v_u8mf4(src + 3 * src_stride, vl); + + // apply shift and add round offset + vuint16mf2_t d0 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s0, vl); + vuint16mf2_t d1 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s1, vl); + vuint16mf2_t d2 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s2, vl); + vuint16mf2_t d3 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s3, vl); + + // load existing dst values + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + // compute basic average + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // store result + __riscv_vse8_v_u8mf4(dst8 + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(dst8 + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(dst8 + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(dst8 + 3 * dst8_stride, d3_u8, vl); + + // update pointers + src += 4 * src_stride; + dst += 4 * dst_stride; + dst8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + } else { + vuint16m1_t round_offset_u16 = __riscv_vmv_v_x_u16m1(round_offset, vl); + do { + const uint8_t *s = src; + CONV_BUF_TYPE *d = dst; + uint8_t *d_u8 = dst8; + int width = w; + + do { + // load 4 block of uint8_t + vuint8mf2_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3, vl); + + // apply shift and add round offset + vuint16m1_t d0 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s0, vl); + vuint16m1_t d1 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s1, vl); + vuint16m1_t d2 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s2, vl); + vuint16m1_t d3 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s3, vl); + + // load existing dst values + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + // compute basic average + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // store results + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + + // update pointers + s += vl; + d += vl; + d_u8 += vl; + width -= vl; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + dst8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + } +} + +static inline void dist_wtd_convolve_2d_copy_rvv(const uint8_t *src, + int src_stride, int w, int h, + ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const uint16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint8_t shift_by_bits = 1 << (FILTER_BITS - ROUND0_BITS); + + CONV_BUF_TYPE *dst = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int height = h; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + vuint16mf2_t round_offset_u16 = __riscv_vmv_v_x_u16mf2(round_offset, vl); + do { + // load 4 block of uint8_t + vuint8mf4_t s0 = __riscv_vle8_v_u8mf4(src + 0 * src_stride, vl); + vuint8mf4_t s1 = __riscv_vle8_v_u8mf4(src + 1 * src_stride, vl); + vuint8mf4_t s2 = __riscv_vle8_v_u8mf4(src + 2 * src_stride, vl); + vuint8mf4_t s3 = __riscv_vle8_v_u8mf4(src + 3 * src_stride, vl); + + // apply shift and add round offset + vuint16mf2_t d0 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s0, vl); + vuint16mf2_t d1 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s1, vl); + vuint16mf2_t d2 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s2, vl); + vuint16mf2_t d3 = + __riscv_vwmaccu_vx_u16mf2(round_offset_u16, shift_by_bits, s3, vl); + + // store result + store_u16_4x4(dst, dst_stride, d0, d1, d2, d3, vl); + + // update pointers + src += 4 * src_stride; + dst += 4 * dst_stride; + height -= 4; + } while (height != 0); + } else { + vuint16m1_t round_offset_u16 = __riscv_vmv_v_x_u16m1(round_offset, vl); + do { + const uint8_t *s = src; + CONV_BUF_TYPE *d = dst; + int width = w; + + do { + // load 4 block of uint8_t + vuint8mf2_t s0, s1, s2, s3; + load_u8_8x4(s, src_stride, &s0, &s1, &s2, &s3, vl); + + // apply shift and add round offset + vuint16m1_t d0 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s0, vl); + vuint16m1_t d1 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s1, vl); + vuint16m1_t d2 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s2, vl); + vuint16m1_t d3 = + __riscv_vwmaccu_vx_u16m1(round_offset_u16, shift_by_bits, s3, vl); + + // store results + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + // update pointers + s += vl; + d += vl; + width -= vl; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + height -= 4; + } while (height != 0); + } +} + +void av1_dist_wtd_convolve_2d_copy_rvv(const uint8_t *src, int src_stride, + uint8_t *dst8, int dst8_stride, int w, + int h, ConvolveParams *conv_params) { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_2d_copy_dist_wtd_avg_rvv( + src, src_stride, dst8, dst8_stride, w, h, conv_params); + } else { + dist_wtd_convolve_2d_copy_avg_rvv(src, src_stride, dst8, dst8_stride, w, + h, conv_params); + } + } else { + dist_wtd_convolve_2d_copy_rvv(src, src_stride, w, h, conv_params); + } +} + +static inline vuint16m1_t convolve4_4_x_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const int16_t *filter, const int16_t round_offset, + size_t vl) { + vint16m1_t sum = __riscv_vmul_vx_i16m1(s0, filter[0], vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[1], s1, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[2], s2, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[3], s3, vl); + + // Right shift with rounding: vrsra_n_s16(round_offset, sum, ROUND0_BITS - 1) + // vrsra_n_s16(a, b, n) = a + ((b + (1 << (n-1))) >> n) + // We halved the convolution filter values so -1 from the right shift. + vint16m1_t res = __riscv_vadd_vx_i16m1(sum, 1 << ((ROUND0_BITS - 1) - 1), vl); + res = __riscv_vsra_vx_i16m1(res, ROUND0_BITS - 1, vl); + res = __riscv_vadd_vx_i16m1(res, round_offset, vl); + + // Reinterpret as uint16 + return __riscv_vreinterpret_v_i16m1_u16m1(res); +} + +static inline vuint16m1_t convolve8_8_x_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, const int16_t *filter, + const int16_t round_offset, size_t vl) { + vint16m1_t sum = __riscv_vmul_vx_i16m1(s0, filter[0], vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[1], s1, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[2], s2, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[3], s3, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[4], s4, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[5], s5, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[6], s6, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[7], s7, vl); + + vint16m1_t res = __riscv_vadd_vx_i16m1(sum, 1 << ((ROUND0_BITS - 1) - 1), vl); + res = __riscv_vsra_vx_i16m1(res, ROUND0_BITS - 1, vl); + res = __riscv_vadd_vx_i16m1(res, round_offset, vl); + + // Reinterpret as uint16 + return __riscv_vreinterpret_v_i16m1_u16m1(res); +} + +static inline void transpose_elems_inplace_u16_8x8_rvv( + vuint16m1_t *d0, vuint16m1_t *d1, vuint16m1_t *d2, vuint16m1_t *d3, + vuint16m1_t *d4, vuint16m1_t *d5, vuint16m1_t *d6, vuint16m1_t *d7, + const vuint16m8_t trans_index) { + vuint16m8_t matrix_8x8 = __riscv_vundefined_u16m8(); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 0, *d0); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 1, *d1); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 2, *d2); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 3, *d3); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 4, *d4); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 5, *d5); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 6, *d6); + matrix_8x8 = __riscv_vset_v_u16m1_u16m8(matrix_8x8, 7, *d7); + + // transpose_elems_inplace_u16_8x8 + vuint16m8_t trans_matrix = + __riscv_vrgather_vv_u16m8(matrix_8x8, trans_index, 64); + + *d0 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 0); + *d1 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 1); + *d2 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 2); + *d3 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 3); + *d4 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 4); + *d5 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 5); + *d6 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 6); + *d7 = __riscv_vget_v_u16m8_u16m1(trans_matrix, 7); +} + +static inline void dist_wtd_convolve_x_dist_wtd_avg_rvv( + const uint8_t *src, int src_stride, uint8_t *dst8, int dst8_stride, int w, + int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn, + ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + // Horizontal filter. + const int16_t *x_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_x, subpel_x_qn & SUBPEL_MASK); + + const int horiz_offset = filter_params_x->taps / 2 - 1; + const uint8_t *src_ptr = src - horiz_offset; + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + uint8_t *dst8_ptr = dst8; + int dst_stride = conv_params->dst_stride; + int height = h; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // 4-tap filters are used for blocks having width <= 4. + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16_t *x_filter = x_filter_ptr + 2; + int16_t filter[4]; + for (int i = 0; i < 4; i++) filter[i] = *x_filter++ >> 1; + + src_ptr += 2; + + do { + // Load + vuint8mf2_t t0 = __riscv_vle8_v_u8mf2(src_ptr + 0 * src_stride, + 8); // a0 a1 a2 a3 a4 a5 a6 a7 + vuint8mf2_t t1 = __riscv_vle8_v_u8mf2(src_ptr + 1 * src_stride, + 8); // b0 b1 b2 b3 b4 b5 b6 b7 + + __builtin_prefetch(dst_ptr); + __builtin_prefetch(dst8_ptr); + + // Widen to 16-bit + vint16m1_t s00 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, 8)); + vint16m1_t s01 = + __riscv_vslide1down_vx_i16m1(s00, 0, 8); // a1 a2 a3 a4 a5 a6 a7 0 + vint16m1_t s02 = + __riscv_vslide1down_vx_i16m1(s01, 0, 8); // a2 a3 a4 a5 a6 a7 0 0 + vint16m1_t s03 = + __riscv_vslide1down_vx_i16m1(s02, 0, 8); // a3 a4 a5 a6 a7 0 0 0 + + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, 8)); + vint16m1_t s11 = + __riscv_vslide1down_vx_i16m1(s10, 0, 8); // b1 b2 b3 b4 b5 b6 b7 0 + vint16m1_t s12 = + __riscv_vslide1down_vx_i16m1(s11, 0, 8); // b2 b3 b4 b5 b6 b7 0 0 + vint16m1_t s13 = + __riscv_vslide1down_vx_i16m1(s12, 0, 8); // b3 b4 b5 b6 b7 0 0 0 + + // Perform convolution + vuint8mf2_t d01, d11; + vuint16m1_t d0 = + convolve4_4_x_rvv(s00, s01, s02, s03, filter, round_offset, vl); + vuint16m1_t d1 = + convolve4_4_x_rvv(s10, s11, s12, s13, filter, round_offset, vl); + + // weighted average + vuint16m1_t dd0 = __riscv_vle16_v_u16m1(dst_ptr + 0 * dst_stride, vl); + vuint16m1_t dd1 = __riscv_vle16_v_u16m1(dst_ptr + 1 * dst_stride, vl); + compute_dist_wtd_avg_4x1_rvv(dd0, d0, fwd_offset, bck_offset, + round_offset, &d01, vl); + compute_dist_wtd_avg_4x1_rvv(dd1, d1, fwd_offset, bck_offset, + round_offset, &d11, vl); + + // Store result + __riscv_vse8_v_u8mf2(dst8_ptr + 0 * dst8_stride, d01, vl); + __riscv_vse8_v_u8mf2(dst8_ptr + 1 * dst8_stride, d11, vl); + + src_ptr += 2 * src_stride; + dst_ptr += 2 * dst_stride; + dst8_ptr += 2 * dst8_stride; + height -= 2; + } while (height >= 2); + } else { + // Filter values are even, so halve to reduce intermediate precision reqs. + int16_t filter[8]; + for (int i = 0; i < 8; i++) filter[i] = *x_filter_ptr++ >> 1; + + // for VLEN = 128 case + if ((w > 8) && (vl == 8) && (height >= 8)) { + vuint16m8_t trans_index = __riscv_vle16_v_u16m8(trans_data, 64); + + while (height >= 8) { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int width = w; + + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + __builtin_prefetch(d + 4 * dst_stride); + __builtin_prefetch(d + 5 * dst_stride); + __builtin_prefetch(d + 6 * dst_stride); + __builtin_prefetch(d + 7 * dst_stride); + + s += 7; + + do { + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s7 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_x_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, round_offset, vl); + vuint16m1_t d1 = convolve8_8_x_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + filter, round_offset, vl); + vuint16m1_t d2 = convolve8_8_x_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + filter, round_offset, vl); + vuint16m1_t d3 = convolve8_8_x_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + filter, round_offset, vl); + vuint16m1_t d4 = convolve8_8_x_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + filter, round_offset, vl); + vuint16m1_t d5 = convolve8_8_x_rvv(s5, s6, s7, s8, s9, s10, s11, s12, + filter, round_offset, vl); + vuint16m1_t d6 = convolve8_8_x_rvv(s6, s7, s8, s9, s10, s11, s12, s13, + filter, round_offset, vl); + vuint16m1_t d7 = convolve8_8_x_rvv(s7, s8, s9, s10, s11, s12, s13, + s14, filter, round_offset, vl); + + // transpose + transpose_elems_inplace_u16_8x8_rvv(&d0, &d1, &d2, &d3, &d4, &d5, &d6, + &d7, trans_index); + + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + + vuint16m1_t dd4, dd5, dd6, dd7; + load_u16_8x4(d + 4 * dst_stride, dst_stride, &dd4, &dd5, &dd6, &dd7, + vl); + + vuint8mf2_t d4_u8, d5_u8, d6_u8, d7_u8; + compute_dist_wtd_avg_8x4_rvv(dd4, dd5, dd6, dd7, d4, d5, d6, d7, + fwd_offset, bck_offset, round_offset, + &d4_u8, &d5_u8, &d6_u8, &d7_u8, vl); + store_u8_8x4(d_u8 + 4 * dst8_stride, dst8_stride, d4_u8, d5_u8, d6_u8, + d7_u8, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8; + d += 8; + d_u8 += 8; + width -= 8; + } while (width != 0); + src_ptr += 8 * src_stride; + dst_ptr += 8 * dst_stride; + dst8_ptr += 8 * dst8_stride; + height -= 8; + } + } + + while (height > 0) { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int width = w; + + do { + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_u8_8x8(s, 1, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit integers + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_x_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, round_offset, vl); + vuint16m1_t dd0 = __riscv_vle16_v_u16m1(d, vl); + + vuint8mf2_t d0_u8; + compute_dist_wtd_avg_8x1_rvv(dd0, d0, fwd_offset, bck_offset, + round_offset, &d0_u8, vl); + + // Store result + __riscv_vse8_v_u8mf2(d_u8, d0_u8, vl); + + s += vl; + d += vl; + d_u8 += vl; + width -= vl; + } while (width > 0); + src_ptr += src_stride; + dst_ptr += dst_stride; + dst8_ptr += dst8_stride; + height--; + } + } +} + +static inline void dist_wtd_convolve_x_avg_rvv( + const uint8_t *src, int src_stride, uint8_t *dst8, int dst8_stride, int w, + int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + // Horizontal filter. + const int16_t *x_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_x, subpel_x_qn & SUBPEL_MASK); + + const int horiz_offset = filter_params_x->taps / 2 - 1; + const uint8_t *src_ptr = src - horiz_offset; + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + uint8_t *dst8_ptr = dst8; + int dst_stride = conv_params->dst_stride; + int height = h; + + size_t vl = __riscv_vsetvl_e16m1(w); + if (w == 4) { + // 4-tap filters are used for blocks having width <= 4. + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16_t *x_filter = x_filter_ptr + 2; + int16_t filter[4]; + for (int i = 0; i < 4; i++) filter[i] = *x_filter++ >> 1; + + src_ptr += 2; + + do { + // Load + vuint8mf2_t t0 = __riscv_vle8_v_u8mf2(src_ptr + 0 * src_stride, + 8); // a0 a1 a2 a3 a4 a5 a6 a7 + vuint8mf2_t t1 = __riscv_vle8_v_u8mf2(src_ptr + 1 * src_stride, + 8); // b0 b1 b2 b3 b4 b5 b6 b7 + + __builtin_prefetch(dst_ptr); + __builtin_prefetch(dst8_ptr); + + // Widen to 16-bit + vint16m1_t s00 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, 8)); + vint16m1_t s01 = + __riscv_vslide1down_vx_i16m1(s00, 0, 8); // a1 a2 a3 a4 a5 a6 a7 0 + vint16m1_t s02 = + __riscv_vslide1down_vx_i16m1(s01, 0, 8); // a2 a3 a4 a5 a6 a7 0 0 + vint16m1_t s03 = + __riscv_vslide1down_vx_i16m1(s02, 0, 8); // a3 a4 a5 a6 a7 0 0 0 + + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, 8)); + vint16m1_t s11 = + __riscv_vslide1down_vx_i16m1(s10, 0, 8); // b1 b2 b3 b4 b5 b6 b7 0 + vint16m1_t s12 = + __riscv_vslide1down_vx_i16m1(s11, 0, 8); // b2 b3 b4 b5 b6 b7 0 0 + vint16m1_t s13 = + __riscv_vslide1down_vx_i16m1(s12, 0, 8); // b3 b4 b5 b6 b7 0 0 0 + + // Perform convolution + vuint8mf2_t d01, d11; + vuint16m1_t d0 = + convolve4_4_x_rvv(s00, s01, s02, s03, filter, round_offset, vl); + vuint16m1_t d1 = + convolve4_4_x_rvv(s10, s11, s12, s13, filter, round_offset, vl); + + // average + vuint16m1_t dd0 = __riscv_vle16_v_u16m1(dst_ptr + 0 * dst_stride, vl); + vuint16m1_t dd1 = __riscv_vle16_v_u16m1(dst_ptr + 1 * dst_stride, vl); + compute_basic_avg_4x1_rvv(dd0, d0, round_offset, &d01, vl); + compute_basic_avg_4x1_rvv(dd1, d1, round_offset, &d11, vl); + + // Store result + __riscv_vse8_v_u8mf2(dst8_ptr + 0 * dst8_stride, d01, vl); + __riscv_vse8_v_u8mf2(dst8_ptr + 1 * dst8_stride, d11, vl); + + src_ptr += 2 * src_stride; + dst_ptr += 2 * dst_stride; + dst8_ptr += 2 * dst8_stride; + height -= 2; + } while (height >= 2); + } else { + // Filter values are even, so halve to reduce intermediate precision reqs. + int16_t filter[8]; + for (int i = 0; i < 8; i++) filter[i] = *x_filter_ptr++ >> 1; + + // for VLEN = 128 case + if ((w > 8) && (vl == 8) && (height >= 8)) { + vuint16m8_t trans_index = __riscv_vle16_v_u16m8(trans_data, 64); + + while (height >= 8) { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int width = w; + + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + __builtin_prefetch(d + 4 * dst_stride); + __builtin_prefetch(d + 5 * dst_stride); + __builtin_prefetch(d + 6 * dst_stride); + __builtin_prefetch(d + 7 * dst_stride); + + s += 7; + + do { + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s7 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_x_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, round_offset, vl); + vuint16m1_t d1 = convolve8_8_x_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + filter, round_offset, vl); + vuint16m1_t d2 = convolve8_8_x_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + filter, round_offset, vl); + vuint16m1_t d3 = convolve8_8_x_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + filter, round_offset, vl); + vuint16m1_t d4 = convolve8_8_x_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + filter, round_offset, vl); + vuint16m1_t d5 = convolve8_8_x_rvv(s5, s6, s7, s8, s9, s10, s11, s12, + filter, round_offset, vl); + vuint16m1_t d6 = convolve8_8_x_rvv(s6, s7, s8, s9, s10, s11, s12, s13, + filter, round_offset, vl); + vuint16m1_t d7 = convolve8_8_x_rvv(s7, s8, s9, s10, s11, s12, s13, + s14, filter, round_offset, vl); + + // transpose + transpose_elems_inplace_u16_8x8_rvv(&d0, &d1, &d2, &d3, &d4, &d5, &d6, + &d7, trans_index); + + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, + &d3_u8, vl); + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + + vuint16m1_t dd4, dd5, dd6, dd7; + load_u16_8x4(d + 4 * dst_stride, dst_stride, &dd4, &dd5, &dd6, &dd7, + vl); + + vuint8mf2_t d4_u8, d5_u8, d6_u8, d7_u8; + compute_basic_avg_8x4_rvv(dd4, dd5, dd6, dd7, d4, d5, d6, d7, + round_offset, &d4_u8, &d5_u8, &d6_u8, + &d7_u8, vl); + store_u8_8x4(d_u8 + 4 * dst8_stride, dst8_stride, d4_u8, d5_u8, d6_u8, + d7_u8, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8; + d += 8; + d_u8 += 8; + width -= 8; + } while (width != 0); + src_ptr += 8 * src_stride; + dst_ptr += 8 * dst_stride; + dst8_ptr += 8 * dst8_stride; + height -= 8; + } + } + + while (height > 0) { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int width = w; + + do { + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_u8_8x8(s, 1, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit integers + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_x_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, round_offset, vl); + vuint16m1_t dd0 = __riscv_vle16_v_u16m1(d, vl); + + vuint8mf2_t d0_u8; + compute_basic_avg_8x1_rvv(dd0, d0, round_offset, &d0_u8, vl); + + // Store result + __riscv_vse8_v_u8mf2(d_u8, d0_u8, vl); + + s += vl; + d += vl; + d_u8 += vl; + width -= vl; + } while (width > 0); + src_ptr += src_stride; + dst_ptr += dst_stride; + dst8_ptr += dst8_stride; + height--; + } + } +} + +static inline void dist_wtd_convolve_x_rvv( + const uint8_t *src, int src_stride, int w, int h, + const InterpFilterParams *filter_params_x, const int subpel_x_qn, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + // Horizontal filter. + const int16_t *x_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_x, subpel_x_qn & SUBPEL_MASK); + + const int horiz_offset = filter_params_x->taps / 2 - 1; + const uint8_t *src_ptr = src - horiz_offset; + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + int dst_stride = conv_params->dst_stride; + int height = h; + + size_t vl = __riscv_vsetvl_e16m1(w); + if (w == 4) { + // 4-tap filters are used for blocks having width <= 4. + // Filter values are even, so halve to reduce intermediate precision reqs. + const int16_t *x_filter = x_filter_ptr + 2; + int16_t filter[4]; + for (int i = 0; i < 4; i++) filter[i] = *(x_filter + i) >> 1; + + src_ptr += 2; + + do { + // Load + vuint8mf2_t t0 = __riscv_vle8_v_u8mf2(src_ptr + 0 * src_stride, + 8); // a0 a1 a2 a3 a4 a5 a6 a7 + vuint8mf2_t t1 = __riscv_vle8_v_u8mf2(src_ptr + 1 * src_stride, + 8); // b0 b1 b2 b3 b4 b5 b6 b7 + + __builtin_prefetch(dst_ptr); + + // Widen to 16-bit + vint16m1_t s00 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, 8)); + vint16m1_t s01 = + __riscv_vslide1down_vx_i16m1(s00, 0, 8); // a1 a2 a3 a4 a5 a6 a7 0 + vint16m1_t s02 = + __riscv_vslide1down_vx_i16m1(s01, 0, 8); // a2 a3 a4 a5 a6 a7 0 0 + vint16m1_t s03 = + __riscv_vslide1down_vx_i16m1(s02, 0, 8); // a3 a4 a5 a6 a7 0 0 0 + + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, 8)); + vint16m1_t s11 = + __riscv_vslide1down_vx_i16m1(s10, 0, 8); // b1 b2 b3 b4 b5 b6 b7 0 + vint16m1_t s12 = + __riscv_vslide1down_vx_i16m1(s11, 0, 8); // b2 b3 b4 b5 b6 b7 0 0 + vint16m1_t s13 = + __riscv_vslide1down_vx_i16m1(s12, 0, 8); // b3 b4 b5 b6 b7 0 0 0 + + // Perform convolution + vuint16m1_t d0 = + convolve4_4_x_rvv(s00, s01, s02, s03, filter, round_offset, vl); + vuint16m1_t d1 = + convolve4_4_x_rvv(s10, s11, s12, s13, filter, round_offset, vl); + + // Store result + __riscv_vse16_v_u16m1(dst_ptr + 0 * dst_stride, d0, vl); + __riscv_vse16_v_u16m1(dst_ptr + 1 * dst_stride, d1, vl); + + src_ptr += 2 * src_stride; + dst_ptr += 2 * dst_stride; + height -= 2; + } while (height >= 2); + } else { + // Filter values are even, so halve to reduce intermediate precision reqs. + int16_t filter[8]; + for (int i = 0; i < 8; i++) filter[i] = *x_filter_ptr++ >> 1; + + // for VLEN = 128 case + if ((w > 8) && (vl == 8) && (height >= 8)) { + vuint16m8_t trans_index = __riscv_vle16_v_u16m8(trans_data, 64); + + while (height >= 8) { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int width = w; + + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + __builtin_prefetch(d + 4 * dst_stride); + __builtin_prefetch(d + 5 * dst_stride); + __builtin_prefetch(d + 6 * dst_stride); + __builtin_prefetch(d + 7 * dst_stride); + + s += 7; + + do { + load_stride_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, + &t7, vl); + + vint16m1_t s7 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = __riscv_vreinterpret_v_u16m1_i16m1( + __riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_x_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, round_offset, vl); + vuint16m1_t d1 = convolve8_8_x_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + filter, round_offset, vl); + vuint16m1_t d2 = convolve8_8_x_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + filter, round_offset, vl); + vuint16m1_t d3 = convolve8_8_x_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + filter, round_offset, vl); + vuint16m1_t d4 = convolve8_8_x_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + filter, round_offset, vl); + vuint16m1_t d5 = convolve8_8_x_rvv(s5, s6, s7, s8, s9, s10, s11, s12, + filter, round_offset, vl); + vuint16m1_t d6 = convolve8_8_x_rvv(s6, s7, s8, s9, s10, s11, s12, s13, + filter, round_offset, vl); + vuint16m1_t d7 = convolve8_8_x_rvv(s7, s8, s9, s10, s11, s12, s13, + s14, filter, round_offset, vl); + + // transpose + transpose_elems_inplace_u16_8x8_rvv(&d0, &d1, &d2, &d3, &d4, &d5, &d6, + &d7, trans_index); + + // Store result + store_u16_8x8(d, dst_stride, d0, d1, d2, d3, d4, d5, d6, d7, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8; + d += 8; + width -= 8; + } while (width != 0); + src_ptr += 8 * src_stride; + dst_ptr += 8 * dst_stride; + height -= 8; + } + } + + while (height > 0) { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int width = w; + + __builtin_prefetch(d); + + do { + // Load + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6, t7; + load_u8_8x8(s, 1, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit integers + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_x_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + filter, round_offset, vl); + + // Store result + __riscv_vse16_v_u16m1(d, d0, vl); + + s += vl; + d += vl; + width -= vl; + } while (width > 0); + src_ptr += src_stride; + dst_ptr += dst_stride; + height--; + } + } +} + +void av1_dist_wtd_convolve_x_rvv(const uint8_t *src, int src_stride, + uint8_t *dst8, int dst8_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const int subpel_x_qn, + ConvolveParams *conv_params) { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_x_dist_wtd_avg_rvv(src, src_stride, dst8, dst8_stride, + w, h, filter_params_x, subpel_x_qn, + conv_params); + } else { + dist_wtd_convolve_x_avg_rvv(src, src_stride, dst8, dst8_stride, w, h, + filter_params_x, subpel_x_qn, conv_params); + } + } else { + dist_wtd_convolve_x_rvv(src, src_stride, w, h, filter_params_x, subpel_x_qn, + conv_params); + } +} + +static inline vuint16mf2_t convolve6_4_y_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const int16_t *filter, const int16_t round_offset, size_t vl) { + // Filter values at indices 0 and 7 are 0. + vint16mf2_t sum = __riscv_vmul_vx_i16mf2(s0, filter[1], vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[2], s1, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[3], s2, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[4], s3, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[5], s4, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[6], s5, vl); + + // Right shift with rounding: vrsra_n_s16(round_offset, sum, ROUND0_BITS - 1) + // vrsra_n_s16(a, b, n) = a + ((b + (1 << (n-1))) >> n) + // We halved the convolution filter values so -1 from the right shift. + vint16mf2_t res = + __riscv_vadd_vx_i16mf2(sum, 1 << ((ROUND0_BITS - 1) - 1), vl); + res = __riscv_vsra_vx_i16mf2(res, ROUND0_BITS - 1, vl); + res = __riscv_vadd_vx_i16mf2(res, round_offset, vl); + + // Reinterpret as uint16 + return __riscv_vreinterpret_v_i16mf2_u16mf2(res); +} + +static inline vuint16m1_t convolve6_8_y_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const int16_t *filter, const int16_t round_offset, size_t vl) { + // Filter values at indices 0 and 7 are 0. + vint16m1_t sum = __riscv_vmul_vx_i16m1(s0, filter[1], vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[2], s1, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[3], s2, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[4], s3, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[5], s4, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[6], s5, vl); + + // Right shift with rounding: vrsra_n_s16(round_offset, sum, ROUND0_BITS - 1) + // vrsra_n_s16(a, b, n) = a + ((b + (1 << (n-1))) >> n) + // We halved the convolution filter values so -1 from the right shift. + vint16m1_t res = __riscv_vadd_vx_i16m1(sum, 1 << ((ROUND0_BITS - 1) - 1), vl); + res = __riscv_vsra_vx_i16m1(res, ROUND0_BITS - 1, vl); + res = __riscv_vadd_vx_i16m1(res, round_offset, vl); + + // Reinterpret as uint16 + return __riscv_vreinterpret_v_i16m1_u16m1(res); +} + +static inline void dist_wtd_convolve_y_6tap_dist_wtd_avg_rvv( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int width = w; + + if (w == 4 || h == 4) { + size_t vl = __riscv_vsetvl_e16m1(4); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // Load initial 5 rows of data + vuint8mf4_t t0, t1, t2, t3, t4; + load_u8_4x5(s, src_stride, &t0, &t1, &t2, &t3, &t4, vl); + + // Convert to 16-bit + vint16mf2_t s0 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s1 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s2 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s3 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + vint16mf2_t s4 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t4, vl)); + + s += 5 * src_stride; + + do { + // Load next rows of data + vuint8mf4_t t5 = __riscv_vle8_v_u8mf4(s + 0 * src_stride, vl); + vuint8mf4_t t6 = __riscv_vle8_v_u8mf4(s + 1 * src_stride, vl); + vuint8mf4_t t7 = __riscv_vle8_v_u8mf4(s + 2 * src_stride, vl); + vuint8mf4_t t8 = __riscv_vle8_v_u8mf4(s + 3 * src_stride, vl); + + // Convert to 16-bit + vint16mf2_t s5 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t5, vl)); + vint16mf2_t s6 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t6, vl)); + vint16mf2_t s7 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t7, vl)); + vint16mf2_t s8 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t8, vl)); + + // Perform convolution + vuint16mf2_t d0 = convolve6_4_y_rvv(s0, s1, s2, s3, s4, s5, y_filter, + round_offset, vl); + vuint16mf2_t d1 = convolve6_4_y_rvv(s1, s2, s3, s4, s5, s6, y_filter, + round_offset, vl); + vuint16mf2_t d2 = convolve6_4_y_rvv(s2, s3, s4, s5, s6, s7, y_filter, + round_offset, vl); + vuint16mf2_t d3 = convolve6_4_y_rvv(s3, s4, s5, s6, s7, s8, y_filter, + round_offset, vl); + + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + + // Store result + __riscv_vse8_v_u8mf4(d_u8 + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 3 * dst8_stride, d3_u8, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 4; + dst_ptr += 4; + dst8_ptr += 4; + width -= 4; + } while (width != 0); + } else { + size_t vl = __riscv_vsetvl_e16m1(w); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // Load initial 5 rows of data + vuint8mf2_t t0, t1, t2, t3, t4; + load_u8_8x5(s, src_stride, &t0, &t1, &t2, &t3, &t4, vl); + + // Convert to 16-bit + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + + s += 5 * src_stride; + + do { + vuint8mf2_t t5, t6, t7; + load_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s8 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s9 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s11 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s12 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve6_8_y_rvv(s0, s1, s2, s3, s4, s5, y_filter, + round_offset, vl); + vuint16m1_t d1 = convolve6_8_y_rvv(s1, s2, s3, s4, s5, s6, y_filter, + round_offset, vl); + vuint16m1_t d2 = convolve6_8_y_rvv(s2, s3, s4, s5, s6, s7, y_filter, + round_offset, vl); + vuint16m1_t d3 = convolve6_8_y_rvv(s3, s4, s5, s6, s7, s8, y_filter, + round_offset, vl); + vuint16m1_t d4 = convolve6_8_y_rvv(s4, s5, s6, s7, s8, s9, y_filter, + round_offset, vl); + vuint16m1_t d5 = convolve6_8_y_rvv(s5, s6, s7, s8, s9, s10, y_filter, + round_offset, vl); + vuint16m1_t d6 = convolve6_8_y_rvv(s6, s7, s8, s9, s10, s11, y_filter, + round_offset, vl); + vuint16m1_t d7 = convolve6_8_y_rvv(s7, s8, s9, s10, s11, s12, y_filter, + round_offset, vl); + + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + vuint16m1_t dd4, dd5, dd6, dd7; + load_u16_8x4(d + 4 * dst_stride, dst_stride, &dd4, &dd5, &dd6, &dd7, + vl); + + vuint8mf2_t d4_u8, d5_u8, d6_u8, d7_u8; + compute_dist_wtd_avg_8x4_rvv(dd4, dd5, dd6, dd7, d4, d5, d6, d7, + fwd_offset, bck_offset, round_offset, + &d4_u8, &d5_u8, &d6_u8, &d7_u8, vl); + store_u8_8x4(d_u8, dst8_stride, d4_u8, d5_u8, d6_u8, d7_u8, vl); + d_u8 += 4 * dst8_stride; + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s += 8 * src_stride; + d += 8 * dst_stride; + height -= 8; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + width -= vl; + } while (width > 0); + } +} + +static inline void dist_wtd_convolve_y_6tap_avg_rvv( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int width = w; + + if (w == 4 || h == 4) { + size_t vl = __riscv_vsetvl_e16m1(4); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // Load initial 5 rows of data + vuint8mf4_t t0, t1, t2, t3, t4; + load_u8_4x5(s, src_stride, &t0, &t1, &t2, &t3, &t4, vl); + + // Convert to 16-bit + vint16mf2_t s0 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s1 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s2 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s3 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + vint16mf2_t s4 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t4, vl)); + + s += 5 * src_stride; + + do { + // Load next rows of data + vuint8mf4_t t5 = __riscv_vle8_v_u8mf4(s + 0 * src_stride, vl); + vuint8mf4_t t6 = __riscv_vle8_v_u8mf4(s + 1 * src_stride, vl); + vuint8mf4_t t7 = __riscv_vle8_v_u8mf4(s + 2 * src_stride, vl); + vuint8mf4_t t8 = __riscv_vle8_v_u8mf4(s + 3 * src_stride, vl); + + // Convert to 16-bit + vint16mf2_t s5 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t5, vl)); + vint16mf2_t s6 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t6, vl)); + vint16mf2_t s7 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t7, vl)); + vint16mf2_t s8 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t8, vl)); + + // Perform convolution + vuint16mf2_t d0 = convolve6_4_y_rvv(s0, s1, s2, s3, s4, s5, y_filter, + round_offset, vl); + vuint16mf2_t d1 = convolve6_4_y_rvv(s1, s2, s3, s4, s5, s6, y_filter, + round_offset, vl); + vuint16mf2_t d2 = convolve6_4_y_rvv(s2, s3, s4, s5, s6, s7, y_filter, + round_offset, vl); + vuint16mf2_t d3 = convolve6_4_y_rvv(s3, s4, s5, s6, s7, s8, y_filter, + round_offset, vl); + + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // Store result + __riscv_vse8_v_u8mf4(d_u8 + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 3 * dst8_stride, d3_u8, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 4; + dst_ptr += 4; + dst8_ptr += 4; + width -= 4; + } while (width != 0); + } else { + size_t vl = __riscv_vsetvl_e16m1(w); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // Load initial 5 rows of data + vuint8mf2_t t0, t1, t2, t3, t4; + load_u8_8x5(s, src_stride, &t0, &t1, &t2, &t3, &t4, vl); + + // Convert to 16-bit + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + + s += 5 * src_stride; + + do { + vuint8mf2_t t5, t6, t7; + load_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s8 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s9 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s11 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s12 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve6_8_y_rvv(s0, s1, s2, s3, s4, s5, y_filter, + round_offset, vl); + vuint16m1_t d1 = convolve6_8_y_rvv(s1, s2, s3, s4, s5, s6, y_filter, + round_offset, vl); + vuint16m1_t d2 = convolve6_8_y_rvv(s2, s3, s4, s5, s6, s7, y_filter, + round_offset, vl); + vuint16m1_t d3 = convolve6_8_y_rvv(s3, s4, s5, s6, s7, s8, y_filter, + round_offset, vl); + vuint16m1_t d4 = convolve6_8_y_rvv(s4, s5, s6, s7, s8, s9, y_filter, + round_offset, vl); + vuint16m1_t d5 = convolve6_8_y_rvv(s5, s6, s7, s8, s9, s10, y_filter, + round_offset, vl); + vuint16m1_t d6 = convolve6_8_y_rvv(s6, s7, s8, s9, s10, s11, y_filter, + round_offset, vl); + vuint16m1_t d7 = convolve6_8_y_rvv(s7, s8, s9, s10, s11, s12, y_filter, + round_offset, vl); + + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + vuint16m1_t dd4, dd5, dd6, dd7; + load_u16_8x4(d + 4 * dst_stride, dst_stride, &dd4, &dd5, &dd6, &dd7, + vl); + + vuint8mf2_t d4_u8, d5_u8, d6_u8, d7_u8; + compute_basic_avg_8x4_rvv(dd4, dd5, dd6, dd7, d4, d5, d6, d7, + round_offset, &d4_u8, &d5_u8, &d6_u8, &d7_u8, + vl); + store_u8_8x4(d_u8, dst8_stride, d4_u8, d5_u8, d6_u8, d7_u8, vl); + d_u8 += 4 * dst8_stride; + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s += 8 * src_stride; + d += 8 * dst_stride; + height -= 8; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + width -= vl; + } while (width > 0); + } +} + +static inline void dist_wtd_convolve_y_6tap_rvv(const uint8_t *src_ptr, + int src_stride, int w, int h, + const int16_t *y_filter, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int width = w; + + if (w == 4 || h == 4) { + size_t vl = __riscv_vsetvl_e16m1(4); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int height = h; + + // Load initial 5 rows of data + vuint8mf4_t t0, t1, t2, t3, t4; + load_u8_4x5(s, src_stride, &t0, &t1, &t2, &t3, &t4, vl); + + // Convert to 16-bit + vint16mf2_t s0 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s1 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s2 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s3 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + vint16mf2_t s4 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t4, vl)); + + s += 5 * src_stride; + + do { + // Load next rows of data + vuint8mf4_t t5 = __riscv_vle8_v_u8mf4(s + 0 * src_stride, vl); + vuint8mf4_t t6 = __riscv_vle8_v_u8mf4(s + 1 * src_stride, vl); + vuint8mf4_t t7 = __riscv_vle8_v_u8mf4(s + 2 * src_stride, vl); + vuint8mf4_t t8 = __riscv_vle8_v_u8mf4(s + 3 * src_stride, vl); + + // Convert to 16-bit + vint16mf2_t s5 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t5, vl)); + vint16mf2_t s6 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t6, vl)); + vint16mf2_t s7 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t7, vl)); + vint16mf2_t s8 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t8, vl)); + + // Perform convolution + vuint16mf2_t d0 = convolve6_4_y_rvv(s0, s1, s2, s3, s4, s5, y_filter, + round_offset, vl); + vuint16mf2_t d1 = convolve6_4_y_rvv(s1, s2, s3, s4, s5, s6, y_filter, + round_offset, vl); + vuint16mf2_t d2 = convolve6_4_y_rvv(s2, s3, s4, s5, s6, s7, y_filter, + round_offset, vl); + vuint16mf2_t d3 = convolve6_4_y_rvv(s3, s4, s5, s6, s7, s8, y_filter, + round_offset, vl); + + // Store result + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 4; + dst_ptr += 4; + width -= 4; + } while (width != 0); + } else { + size_t vl = __riscv_vsetvl_e16m1(w); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int height = h; + + // Load initial 5 rows of data + vuint8mf2_t t0, t1, t2, t3, t4; + load_u8_8x5(s, src_stride, &t0, &t1, &t2, &t3, &t4, vl); + + // Convert to 16-bit + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + + s += 5 * src_stride; + + do { + vuint8mf2_t t5, t6, t7; + load_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s8 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s9 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s11 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s12 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + // Perform convolution + vuint16m1_t d0 = convolve6_8_y_rvv(s0, s1, s2, s3, s4, s5, y_filter, + round_offset, vl); + vuint16m1_t d1 = convolve6_8_y_rvv(s1, s2, s3, s4, s5, s6, y_filter, + round_offset, vl); + vuint16m1_t d2 = convolve6_8_y_rvv(s2, s3, s4, s5, s6, s7, y_filter, + round_offset, vl); + vuint16m1_t d3 = convolve6_8_y_rvv(s3, s4, s5, s6, s7, s8, y_filter, + round_offset, vl); + vuint16m1_t d4 = convolve6_8_y_rvv(s4, s5, s6, s7, s8, s9, y_filter, + round_offset, vl); + vuint16m1_t d5 = convolve6_8_y_rvv(s5, s6, s7, s8, s9, s10, y_filter, + round_offset, vl); + vuint16m1_t d6 = convolve6_8_y_rvv(s6, s7, s8, s9, s10, s11, y_filter, + round_offset, vl); + vuint16m1_t d7 = convolve6_8_y_rvv(s7, s8, s9, s10, s11, s12, y_filter, + round_offset, vl); + + // Store result + store_u16_8x8(d, dst_stride, d0, d1, d2, d3, d4, d5, d6, d7, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s += 8 * src_stride; + d += 8 * dst_stride; + height -= 8; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + width -= vl; + } while (width > 0); + } +} + +static inline vuint16mf2_t convolve8_4_y_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const vint16mf2_t s6, const vint16mf2_t s7, const int16_t *filter, + const int16_t round_offset, size_t vl) { + // Filter values at indices 0 and 7 are 0. + vint16mf2_t sum = __riscv_vmul_vx_i16mf2(s0, filter[0], vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[1], s1, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[2], s2, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[3], s3, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[4], s4, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[5], s5, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[6], s6, vl); + sum = __riscv_vmacc_vx_i16mf2(sum, filter[7], s7, vl); + + // Right shift with rounding: vrsra_n_s16(round_offset, sum, ROUND0_BITS - 1) + // vrsra_n_s16(a, b, n) = a + ((b + (1 << (n-1))) >> n) + // We halved the convolution filter values so -1 from the right shift. + vint16mf2_t res = + __riscv_vadd_vx_i16mf2(sum, 1 << ((ROUND0_BITS - 1) - 1), vl); + res = __riscv_vsra_vx_i16mf2(res, ROUND0_BITS - 1, vl); + res = __riscv_vadd_vx_i16mf2(res, round_offset, vl); + + // Reinterpret as uint16 + return __riscv_vreinterpret_v_i16mf2_u16mf2(res); +} + +static inline vuint16m1_t convolve8_8_y_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, const int16_t *filter, + const int16_t round_offset, size_t vl) { + // Filter values at indices 0 and 7 are 0. + vint16m1_t sum = __riscv_vmul_vx_i16m1(s0, filter[0], vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[1], s1, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[2], s2, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[3], s3, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[4], s4, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[5], s5, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[6], s6, vl); + sum = __riscv_vmacc_vx_i16m1(sum, filter[7], s7, vl); + + // Right shift with rounding: vrsra_n_s16(round_offset, sum, ROUND0_BITS - 1) + // vrsra_n_s16(a, b, n) = a + ((b + (1 << (n-1))) >> n) + // We halved the convolution filter values so -1 from the right shift. + vint16m1_t res = __riscv_vadd_vx_i16m1(sum, 1 << ((ROUND0_BITS - 1) - 1), vl); + res = __riscv_vsra_vx_i16m1(res, ROUND0_BITS - 1, vl); + res = __riscv_vadd_vx_i16m1(res, round_offset, vl); + + // Reinterpret as uint16 + return __riscv_vreinterpret_v_i16m1_u16m1(res); +} + +static inline void dist_wtd_convolve_y_8tap_dist_wtd_avg_rvv( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int width = w; + + if (w == 4 || h == 4) { + size_t vl = __riscv_vsetvl_e16m1(4); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + __builtin_prefetch(s + 0 * src_stride); + __builtin_prefetch(s + 1 * src_stride); + __builtin_prefetch(s + 2 * src_stride); + __builtin_prefetch(s + 3 * src_stride); + + // Load initial 7 rows of data + vuint8mf4_t t0, t1, t2, t3, t4, t5, t6; + load_u8_4x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, vl); + + // Convert to 16-bit + vint16mf2_t s0 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s1 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s2 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s3 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + vint16mf2_t s4 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t4, vl)); + vint16mf2_t s5 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t5, vl)); + vint16mf2_t s6 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t6, vl)); + + s += 7 * src_stride; + + do { + // Load next rows of data + t0 = __riscv_vle8_v_u8mf4(s + 0 * src_stride, vl); + t1 = __riscv_vle8_v_u8mf4(s + 1 * src_stride, vl); + t2 = __riscv_vle8_v_u8mf4(s + 2 * src_stride, vl); + t3 = __riscv_vle8_v_u8mf4(s + 3 * src_stride, vl); + + // Convert to 16-bit + vint16mf2_t s7 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s8 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s9 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s10 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + + // Perform convolution + vuint16mf2_t d0 = convolve8_4_y_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, round_offset, vl); + vuint16mf2_t d1 = convolve8_4_y_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, round_offset, vl); + vuint16mf2_t d2 = convolve8_4_y_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, round_offset, vl); + vuint16mf2_t d3 = convolve8_4_y_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, round_offset, vl); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + + __builtin_prefetch(d_u8 + 0 * dst8_stride); + __builtin_prefetch(d_u8 + 1 * dst8_stride); + __builtin_prefetch(d_u8 + 2 * dst8_stride); + __builtin_prefetch(d_u8 + 3 * dst8_stride); + + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + + // Store result + __riscv_vse8_v_u8mf4(d_u8 + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 3 * dst8_stride, d3_u8, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s5 = __riscv_vmv_v_v_i16mf2(s9, vl); + s6 = __riscv_vmv_v_v_i16mf2(s10, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 4; + dst_ptr += 4; + dst8_ptr += 4; + width -= 4; + } while (width != 0); + } else { + size_t vl = __riscv_vsetvl_e16m1(w); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + __builtin_prefetch(s + 0 * src_stride); + __builtin_prefetch(s + 1 * src_stride); + __builtin_prefetch(s + 2 * src_stride); + __builtin_prefetch(s + 3 * src_stride); + __builtin_prefetch(s + 4 * src_stride); + __builtin_prefetch(s + 5 * src_stride); + __builtin_prefetch(s + 6 * src_stride); + + // Load initial 7 rows of data + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, vl); + + // Convert to 16-bit + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + s += 7 * src_stride; + + do { + vuint8mf2_t t7; + load_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_y_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, round_offset, vl); + vuint16m1_t d1 = convolve8_8_y_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, round_offset, vl); + vuint16m1_t d2 = convolve8_8_y_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, round_offset, vl); + vuint16m1_t d3 = convolve8_8_y_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, round_offset, vl); + vuint16m1_t d4 = convolve8_8_y_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + y_filter, round_offset, vl); + vuint16m1_t d5 = convolve8_8_y_rvv(s5, s6, s7, s8, s9, s10, s11, s12, + y_filter, round_offset, vl); + vuint16m1_t d6 = convolve8_8_y_rvv(s6, s7, s8, s9, s10, s11, s12, s13, + y_filter, round_offset, vl); + vuint16m1_t d7 = convolve8_8_y_rvv(s7, s8, s9, s10, s11, s12, s13, s14, + y_filter, round_offset, vl); + + __builtin_prefetch(d_u8 + 0 * dst8_stride); + __builtin_prefetch(d_u8 + 1 * dst8_stride); + __builtin_prefetch(d_u8 + 2 * dst8_stride); + __builtin_prefetch(d_u8 + 3 * dst8_stride); + + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + vuint16m1_t dd4, dd5, dd6, dd7; + load_u16_8x4(d + 4 * dst_stride, dst_stride, &dd4, &dd5, &dd6, &dd7, + vl); + + vuint8mf2_t d4_u8, d5_u8, d6_u8, d7_u8; + compute_dist_wtd_avg_8x4_rvv(dd4, dd5, dd6, dd7, d4, d5, d6, d7, + fwd_offset, bck_offset, round_offset, + &d4_u8, &d5_u8, &d6_u8, &d7_u8, vl); + store_u8_8x4(d_u8, dst8_stride, d4_u8, d5_u8, d6_u8, d7_u8, vl); + d_u8 += 4 * dst8_stride; + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8 * src_stride; + d += 8 * dst_stride; + height -= 8; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + width -= vl; + } while (width > 0); + } +} + +static inline void dist_wtd_convolve_y_8tap_avg_rvv( + const uint8_t *src_ptr, int src_stride, uint8_t *dst8_ptr, + const int dst8_stride, int w, int h, const int16_t *y_filter, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int width = w; + + if (w == 4 || h == 4) { + size_t vl = __riscv_vsetvl_e16m1(4); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + __builtin_prefetch(s + 0 * src_stride); + __builtin_prefetch(s + 1 * src_stride); + __builtin_prefetch(s + 2 * src_stride); + __builtin_prefetch(s + 3 * src_stride); + + // Load initial 7 rows of data + vuint8mf4_t t0, t1, t2, t3, t4, t5, t6; + load_u8_4x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, vl); + + // Convert to 16-bit + vint16mf2_t s0 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s1 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s2 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s3 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + vint16mf2_t s4 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t4, vl)); + vint16mf2_t s5 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t5, vl)); + vint16mf2_t s6 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t6, vl)); + + s += 7 * src_stride; + + do { + // Load next rows of data + t0 = __riscv_vle8_v_u8mf4(s + 0 * src_stride, vl); + t1 = __riscv_vle8_v_u8mf4(s + 1 * src_stride, vl); + t2 = __riscv_vle8_v_u8mf4(s + 2 * src_stride, vl); + t3 = __riscv_vle8_v_u8mf4(s + 3 * src_stride, vl); + + // Convert to 16-bit + vint16mf2_t s7 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s8 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s9 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s10 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + + // Perform convolution + vuint16mf2_t d0 = convolve8_4_y_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, round_offset, vl); + vuint16mf2_t d1 = convolve8_4_y_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, round_offset, vl); + vuint16mf2_t d2 = convolve8_4_y_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, round_offset, vl); + vuint16mf2_t d3 = convolve8_4_y_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, round_offset, vl); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + + __builtin_prefetch(d_u8 + 0 * dst8_stride); + __builtin_prefetch(d_u8 + 1 * dst8_stride); + __builtin_prefetch(d_u8 + 2 * dst8_stride); + __builtin_prefetch(d_u8 + 3 * dst8_stride); + + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // Store result + __riscv_vse8_v_u8mf4(d_u8 + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(d_u8 + 3 * dst8_stride, d3_u8, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s5 = __riscv_vmv_v_v_i16mf2(s9, vl); + s6 = __riscv_vmv_v_v_i16mf2(s10, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + d_u8 += 4 * dst8_stride; + height -= 4; + } while (height != 0); + src_ptr += 4; + dst_ptr += 4; + dst8_ptr += 4; + width -= 4; + } while (width != 0); + } else { + size_t vl = __riscv_vsetvl_e16m1(w); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + __builtin_prefetch(s + 0 * src_stride); + __builtin_prefetch(s + 1 * src_stride); + __builtin_prefetch(s + 2 * src_stride); + __builtin_prefetch(s + 3 * src_stride); + __builtin_prefetch(s + 4 * src_stride); + __builtin_prefetch(s + 5 * src_stride); + __builtin_prefetch(s + 6 * src_stride); + + // Load initial 7 rows of data + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, vl); + + // Convert to 16-bit + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + s += 7 * src_stride; + + do { + vuint8mf2_t t7; + load_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_y_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, round_offset, vl); + vuint16m1_t d1 = convolve8_8_y_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, round_offset, vl); + vuint16m1_t d2 = convolve8_8_y_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, round_offset, vl); + vuint16m1_t d3 = convolve8_8_y_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, round_offset, vl); + vuint16m1_t d4 = convolve8_8_y_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + y_filter, round_offset, vl); + vuint16m1_t d5 = convolve8_8_y_rvv(s5, s6, s7, s8, s9, s10, s11, s12, + y_filter, round_offset, vl); + vuint16m1_t d6 = convolve8_8_y_rvv(s6, s7, s8, s9, s10, s11, s12, s13, + y_filter, round_offset, vl); + vuint16m1_t d7 = convolve8_8_y_rvv(s7, s8, s9, s10, s11, s12, s13, s14, + y_filter, round_offset, vl); + + __builtin_prefetch(d_u8 + 0 * dst8_stride); + __builtin_prefetch(d_u8 + 1 * dst8_stride); + __builtin_prefetch(d_u8 + 2 * dst8_stride); + __builtin_prefetch(d_u8 + 3 * dst8_stride); + + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + vuint16m1_t dd4, dd5, dd6, dd7; + load_u16_8x4(d + 4 * dst_stride, dst_stride, &dd4, &dd5, &dd6, &dd7, + vl); + + vuint8mf2_t d4_u8, d5_u8, d6_u8, d7_u8; + compute_basic_avg_8x4_rvv(dd4, dd5, dd6, dd7, d4, d5, d6, d7, + round_offset, &d4_u8, &d5_u8, &d6_u8, &d7_u8, + vl); + store_u8_8x4(d_u8, dst8_stride, d4_u8, d5_u8, d6_u8, d7_u8, vl); + d_u8 += 4 * dst8_stride; + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8 * src_stride; + d += 8 * dst_stride; + height -= 8; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + width -= vl; + } while (width > 0); + } +} + +static inline void dist_wtd_convolve_y_8tap_rvv(const uint8_t *src_ptr, + int src_stride, int w, int h, + const int16_t *y_filter, + ConvolveParams *conv_params) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + int width = w; + + if (w == 4 || h == 4) { + size_t vl = __riscv_vsetvl_e16m1(4); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int height = h; + + __builtin_prefetch(s + 0 * src_stride); + __builtin_prefetch(s + 1 * src_stride); + __builtin_prefetch(s + 2 * src_stride); + __builtin_prefetch(s + 3 * src_stride); + + // Load initial 7 rows of data + vuint8mf4_t t0, t1, t2, t3, t4, t5, t6; + load_u8_4x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, vl); + + // Convert to 16-bit + vint16mf2_t s0 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s1 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s2 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s3 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + vint16mf2_t s4 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t4, vl)); + vint16mf2_t s5 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t5, vl)); + vint16mf2_t s6 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t6, vl)); + + s += 7 * src_stride; + + do { + // Load next rows of data + t0 = __riscv_vle8_v_u8mf4(s + 0 * src_stride, vl); + t1 = __riscv_vle8_v_u8mf4(s + 1 * src_stride, vl); + t2 = __riscv_vle8_v_u8mf4(s + 2 * src_stride, vl); + t3 = __riscv_vle8_v_u8mf4(s + 3 * src_stride, vl); + + // Convert to 16-bit + vint16mf2_t s7 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t0, vl)); + vint16mf2_t s8 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t1, vl)); + vint16mf2_t s9 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t2, vl)); + vint16mf2_t s10 = __riscv_vreinterpret_v_u16mf2_i16mf2( + __riscv_vzext_vf2_u16mf2(t3, vl)); + + // Perform convolution + vuint16mf2_t d0 = convolve8_4_y_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, round_offset, vl); + vuint16mf2_t d1 = convolve8_4_y_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, round_offset, vl); + vuint16mf2_t d2 = convolve8_4_y_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, round_offset, vl); + vuint16mf2_t d3 = convolve8_4_y_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, round_offset, vl); + + // Store result + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s5 = __riscv_vmv_v_v_i16mf2(s9, vl); + s6 = __riscv_vmv_v_v_i16mf2(s10, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += 4; + dst_ptr += 4; + width -= 4; + } while (width != 0); + } else { + size_t vl = __riscv_vsetvl_e16m1(w); + do { + const uint8_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int height = h; + + __builtin_prefetch(s + 0 * src_stride); + __builtin_prefetch(s + 1 * src_stride); + __builtin_prefetch(s + 2 * src_stride); + __builtin_prefetch(s + 3 * src_stride); + __builtin_prefetch(s + 4 * src_stride); + __builtin_prefetch(s + 5 * src_stride); + __builtin_prefetch(s + 6 * src_stride); + + // Load initial 7 rows of data + vuint8mf2_t t0, t1, t2, t3, t4, t5, t6; + load_u8_8x7(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, vl); + + // Convert to 16-bit + vint16m1_t s0 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s1 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s2 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s3 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s4 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s5 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s6 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + + s += 7 * src_stride; + + do { + vuint8mf2_t t7; + load_u8_8x8(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, vl); + + // Convert to 16-bit + vint16m1_t s7 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t0, vl)); + vint16m1_t s8 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t1, vl)); + vint16m1_t s9 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t2, vl)); + vint16m1_t s10 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t3, vl)); + vint16m1_t s11 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t4, vl)); + vint16m1_t s12 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t5, vl)); + vint16m1_t s13 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t6, vl)); + vint16m1_t s14 = + __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2_u16m1(t7, vl)); + + __builtin_prefetch(d + 0 * dst_stride); + __builtin_prefetch(d + 1 * dst_stride); + __builtin_prefetch(d + 2 * dst_stride); + __builtin_prefetch(d + 3 * dst_stride); + + // Perform convolution + vuint16m1_t d0 = convolve8_8_y_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, round_offset, vl); + vuint16m1_t d1 = convolve8_8_y_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, round_offset, vl); + vuint16m1_t d2 = convolve8_8_y_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, round_offset, vl); + vuint16m1_t d3 = convolve8_8_y_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, round_offset, vl); + vuint16m1_t d4 = convolve8_8_y_rvv(s4, s5, s6, s7, s8, s9, s10, s11, + y_filter, round_offset, vl); + vuint16m1_t d5 = convolve8_8_y_rvv(s5, s6, s7, s8, s9, s10, s11, s12, + y_filter, round_offset, vl); + vuint16m1_t d6 = convolve8_8_y_rvv(s6, s7, s8, s9, s10, s11, s12, s13, + y_filter, round_offset, vl); + vuint16m1_t d7 = convolve8_8_y_rvv(s7, s8, s9, s10, s11, s12, s13, s14, + y_filter, round_offset, vl); + + // Store result + store_u16_8x8(d, dst_stride, d0, d1, d2, d3, d4, d5, d6, d7, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16m1(s8, vl); + s1 = __riscv_vmv_v_v_i16m1(s9, vl); + s2 = __riscv_vmv_v_v_i16m1(s10, vl); + s3 = __riscv_vmv_v_v_i16m1(s11, vl); + s4 = __riscv_vmv_v_v_i16m1(s12, vl); + s5 = __riscv_vmv_v_v_i16m1(s13, vl); + s6 = __riscv_vmv_v_v_i16m1(s14, vl); + s += 8 * src_stride; + d += 8 * dst_stride; + height -= 8; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + width -= vl; + } while (width > 0); + } +} +void av1_dist_wtd_convolve_y_rvv(const uint8_t *src, int src_stride, + uint8_t *dst8, int dst8_stride, int w, int h, + const InterpFilterParams *filter_params_y, + const int subpel_y_qn, + ConvolveParams *conv_params) { + assert(w % 4 == 0); + assert(h % 4 == 0); + + // Vertical filter. + const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_y, subpel_y_qn & SUBPEL_MASK); + // Filter values are even, so downshift by 1 to reduce intermediate + // precision requirements. + int16_t y_filter[8]; + for (int i = 0; i < 8; i++) y_filter[i] = *y_filter_ptr++ >> 1; + + const int vert_offset = filter_params_y->taps / 2 - 1; + const uint8_t *src_ptr = src - (vert_offset * src_stride); + + if (get_filter_tap(filter_params_y, subpel_y_qn) <= 6) { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_y_6tap_dist_wtd_avg_rvv(src_ptr + src_stride, + src_stride, dst8, dst8_stride, + w, h, y_filter, conv_params); + } else { + dist_wtd_convolve_y_6tap_avg_rvv(src_ptr + src_stride, src_stride, dst8, + dst8_stride, w, h, y_filter, + conv_params); + } + } else { + dist_wtd_convolve_y_6tap_rvv(src_ptr + src_stride, src_stride, w, h, + y_filter, conv_params); + } + } else { + if (conv_params->do_average) { + if (UNLIKELY(conv_params->use_dist_wtd_comp_avg)) { + dist_wtd_convolve_y_8tap_dist_wtd_avg_rvv(src_ptr, src_stride, dst8, + dst8_stride, w, h, y_filter, + conv_params); + } else { + dist_wtd_convolve_y_8tap_avg_rvv(src_ptr, src_stride, dst8, dst8_stride, + w, h, y_filter, conv_params); + } + } else { + dist_wtd_convolve_y_8tap_rvv(src_ptr, src_stride, w, h, y_filter, + conv_params); + } + } +}
diff --git a/av1/common/riscv/compound_convolve_rvv.h b/av1/common/riscv/compound_convolve_rvv.h new file mode 100644 index 0000000..61e71fc --- /dev/null +++ b/av1/common/riscv/compound_convolve_rvv.h
@@ -0,0 +1,1140 @@ +/* + * Copyright (c) 2026, 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. + */ + +#ifndef AOM_AV1_COMMON_RISCV_COMPOUND_CONVOLVE_RVV_H_ +#define AOM_AV1_COMMON_RISCV_COMPOUND_CONVOLVE_RVV_H_ + +#include <riscv_vector.h> + +#include "aom_dsp/riscv/mem_rvv.h" +#include "av1/common/convolve.h" +#include "av1/common/enums.h" +#include "av1/common/filter.h" + +static inline void compute_dist_wtd_avg_4x1_rvv(const vuint16m1_t dd0, + const vuint16m1_t d0, + const uint16_t fwd_offset, + const uint16_t bck_offset, + const int16_t round_offset, + vuint8mf2_t *d0_u8, size_t vl) { + // blend0 = dd0 * fwd_offset + d0 * bck_offset (32-bit accumulation) + vuint32m2_t vblend0 = __riscv_vwmulu_vx_u32m2(dd0, fwd_offset, vl); + vblend0 = __riscv_vwmaccu_vx_u32m2(vblend0, bck_offset, d0, vl); + + // avg0 = vblend0 >> DIST_PRECISION_BITS (narrow to 16-bit) + vuint16m1_t vavg0 = __riscv_vnsrl_wx_u16m1(vblend0, DIST_PRECISION_BITS, vl); + + // dst0 = (int16_t)vavg0 - round_offset + vint16m1_t vdst0 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg0), round_offset, vl); + + // Pack to 8 lanes for rounding and narrowing + vint16m1_t vec_zero = __riscv_vmv_s_x_i16m1(0, vl); + vdst0 = + __riscv_vslideup_vx_i16m1(vdst0, vec_zero, 4, 8); // upper 4 lanes = 0 + vuint16m1_t d0_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst0, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, + __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_basic_avg_4x1_rvv(const vuint16m1_t dd0, + const vuint16m1_t d0, + const int16_t round_offset, + vuint8mf2_t *d0_u8, size_t vl) { + // avg0 = (dd0 + d0) >> 1 (vector halving add, no rounding) +#if __riscv_v_intrinsic == 11000 + vuint16m1_t vavg0 = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(dd0, d0, vl), 1, vl); +#elif __riscv_v_intrinsic >= 12000 + vuint16m1_t vavg0 = __riscv_vaaddu_vv_u16m1(dd0, d0, __RISCV_VXRM_RDN, vl); +#endif + + // dst0 = (int16_t)vavg0 - round_offset + vint16m1_t vdst0 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg0), round_offset, vl); + + // Pack to 8 lanes for rounding and narrowing + vint16m1_t vec_zero = __riscv_vmv_s_x_i16m1(0, vl); + vdst0 = + __riscv_vslideup_vx_i16m1(vdst0, vec_zero, 4, 8); // upper 4 lanes = 0 + vuint16m1_t d0_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst0, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, + __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_dist_wtd_avg_8x1_rvv(const vuint16m1_t dd0, + const vuint16m1_t d0, + const uint16_t fwd_offset, + const uint16_t bck_offset, + const int16_t round_offset, + vuint8mf2_t *d0_u8, size_t vl) { + // blend0 = dd0 * fwd_offset + d0 * bck_offset (32-bit accumulation) + vuint32m2_t vblend0 = __riscv_vwmulu_vx_u32m2(dd0, fwd_offset, vl); + vblend0 = __riscv_vwmaccu_vx_u32m2(vblend0, bck_offset, d0, vl); + + // avg0 = vblend0 >> DIST_PRECISION_BITS (narrow to 16-bit) + vuint16m1_t vavg0 = __riscv_vnsrl_wx_u16m1(vblend0, DIST_PRECISION_BITS, vl); + + // dst0 = (int16_t)vavg0 - round_offset + vint16m1_t vdst0 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg0), round_offset, vl); + + // Rounding shift right and narrow to u8 + vuint16m1_t d0_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst0, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, + __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_basic_avg_8x1_rvv(const vuint16m1_t dd0, + const vuint16m1_t d0, + const int16_t round_offset, + vuint8mf2_t *d0_u8, size_t vl) { + // avg0 = (dd0 + d0) >> 1 (vector halving add, no rounding) +#if __riscv_v_intrinsic == 11000 + vuint16m1_t vavg0 = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(dd0, d0, vl), 1, vl); +#elif __riscv_v_intrinsic >= 12000 + vuint16m1_t vavg0 = __riscv_vaaddu_vv_u16m1(dd0, d0, __RISCV_VXRM_RDN, vl); +#endif + + // dst0 = (int16_t)vavg0 - round_offset + vint16m1_t vdst0 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg0), round_offset, vl); + + // Rounding shift right and narrow to u8 + vuint16m1_t d0_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst0, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, FILTER_BITS - ROUND0_BITS, + __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_dist_wtd_avg_4x4_rvv( + const vuint16mf2_t dd0, const vuint16mf2_t dd1, const vuint16mf2_t dd2, + const vuint16mf2_t dd3, const vuint16mf2_t d0, const vuint16mf2_t d1, + const vuint16mf2_t d2, const vuint16mf2_t d3, const uint16_t fwd_offset, + const uint16_t bck_offset, const int16_t round_offset, vuint8mf4_t *d0_u8, + vuint8mf4_t *d1_u8, vuint8mf4_t *d2_u8, vuint8mf4_t *d3_u8, size_t vl) { + // blend = dd* * fwd_offset + d* * bck_offset (32-bit accumulation) + vuint32m1_t vblend0 = __riscv_vwmulu_vx_u32m1(dd0, fwd_offset, vl); + vblend0 = __riscv_vwmaccu_vx_u32m1(vblend0, bck_offset, d0, vl); + + vuint32m1_t vblend1 = __riscv_vwmulu_vx_u32m1(dd1, fwd_offset, vl); + vblend1 = __riscv_vwmaccu_vx_u32m1(vblend1, bck_offset, d1, vl); + + vuint32m1_t vblend2 = __riscv_vwmulu_vx_u32m1(dd2, fwd_offset, vl); + vblend2 = __riscv_vwmaccu_vx_u32m1(vblend2, bck_offset, d2, vl); + + vuint32m1_t vblend3 = __riscv_vwmulu_vx_u32m1(dd3, fwd_offset, vl); + vblend3 = __riscv_vwmaccu_vx_u32m1(vblend3, bck_offset, d3, vl); + + // avg = blend >> DIST_PRECISION_BITS (narrow to 16-bit) + vuint16mf2_t vavg0 = + __riscv_vnsrl_wx_u16mf2(vblend0, DIST_PRECISION_BITS, vl); + vuint16mf2_t vavg1 = + __riscv_vnsrl_wx_u16mf2(vblend1, DIST_PRECISION_BITS, vl); + vuint16mf2_t vavg2 = + __riscv_vnsrl_wx_u16mf2(vblend2, DIST_PRECISION_BITS, vl); + vuint16mf2_t vavg3 = + __riscv_vnsrl_wx_u16mf2(vblend3, DIST_PRECISION_BITS, vl); + + // dst = vavg - round_offset + vint16mf2_t vdst0 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg0), round_offset, vl); + vint16mf2_t vdst1 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg1), round_offset, vl); + vint16mf2_t vdst2 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg2), round_offset, vl); + vint16mf2_t vdst3 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg3), round_offset, vl); + + // Saturating Rounded Shift right and narrow to u8 + const int shift = FILTER_BITS - ROUND0_BITS; + vuint16mf2_t d0_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst0, 0, vl)); + vuint16mf2_t d1_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst1, 0, vl)); + vuint16mf2_t d2_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst2, 0, vl)); + vuint16mf2_t d3_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst3, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf4(d0_clip, shift, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf4(d1_clip, shift, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf4(d2_clip, shift, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf4(d3_clip, shift, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf4(d0_clip, shift, __RISCV_VXRM_RNU, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf4(d1_clip, shift, __RISCV_VXRM_RNU, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf4(d2_clip, shift, __RISCV_VXRM_RNU, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf4(d3_clip, shift, __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_basic_avg_4x4_rvv( + const vuint16mf2_t dd0, const vuint16mf2_t dd1, const vuint16mf2_t dd2, + const vuint16mf2_t dd3, const vuint16mf2_t d0, const vuint16mf2_t d1, + const vuint16mf2_t d2, const vuint16mf2_t d3, const int16_t round_offset, + vuint8mf4_t *d0_u8, vuint8mf4_t *d1_u8, vuint8mf4_t *d2_u8, + vuint8mf4_t *d3_u8, size_t vl) { + // avg = (dd + d) >> 1 (vector halving add) +#if __riscv_v_intrinsic == 11000 + vuint16mf2_t vavg0 = + __riscv_vnsrl_wx_u16mf2(__riscv_vwaddu_vv_u32m1(dd0, d0, vl), 1, vl); + vuint16mf2_t vavg1 = + __riscv_vnsrl_wx_u16mf2(__riscv_vwaddu_vv_u32m1(dd1, d1, vl), 1, vl); + vuint16mf2_t vavg2 = + __riscv_vnsrl_wx_u16mf2(__riscv_vwaddu_vv_u32m1(dd2, d2, vl), 1, vl); + vuint16mf2_t vavg3 = + __riscv_vnsrl_wx_u16mf2(__riscv_vwaddu_vv_u32m1(dd3, d3, vl), 1, vl); +#elif __riscv_v_intrinsic >= 12000 + vuint16mf2_t vavg0 = __riscv_vaaddu_vv_u16mf2(dd0, d0, __RISCV_VXRM_RDN, vl); + vuint16mf2_t vavg1 = __riscv_vaaddu_vv_u16mf2(dd1, d1, __RISCV_VXRM_RDN, vl); + vuint16mf2_t vavg2 = __riscv_vaaddu_vv_u16mf2(dd2, d2, __RISCV_VXRM_RDN, vl); + vuint16mf2_t vavg3 = __riscv_vaaddu_vv_u16mf2(dd3, d3, __RISCV_VXRM_RDN, vl); +#endif + + // dst_01 = (int16x8_t) vavg01 - round_offset + vint16mf2_t vdst0 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg0), round_offset, 8); + vint16mf2_t vdst1 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg1), round_offset, 8); + vint16mf2_t vdst2 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg2), round_offset, 8); + vint16mf2_t vdst3 = __riscv_vsub_vx_i16mf2( + __riscv_vreinterpret_v_u16mf2_i16mf2(vavg3), round_offset, 8); + + // Saturating Rounded Shift right and narrow to u8 + const int shift = FILTER_BITS - ROUND0_BITS; + vuint16mf2_t d0_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst0, 0, vl)); + vuint16mf2_t d1_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst1, 0, vl)); + vuint16mf2_t d2_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst2, 0, vl)); + vuint16mf2_t d3_clip = __riscv_vreinterpret_v_i16mf2_u16mf2( + __riscv_vmax_vx_i16mf2(vdst3, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf4(d0_clip, shift, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf4(d1_clip, shift, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf4(d2_clip, shift, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf4(d3_clip, shift, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf4(d0_clip, shift, __RISCV_VXRM_RNU, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf4(d1_clip, shift, __RISCV_VXRM_RNU, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf4(d2_clip, shift, __RISCV_VXRM_RNU, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf4(d3_clip, shift, __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_dist_wtd_avg_8x4_rvv( + const vuint16m1_t dd0, const vuint16m1_t dd1, const vuint16m1_t dd2, + const vuint16m1_t dd3, const vuint16m1_t d0, const vuint16m1_t d1, + const vuint16m1_t d2, const vuint16m1_t d3, const uint16_t fwd_offset, + const uint16_t bck_offset, const int16_t round_offset, vuint8mf2_t *d0_u8, + vuint8mf2_t *d1_u8, vuint8mf2_t *d2_u8, vuint8mf2_t *d3_u8, size_t vl) { + // blend = dd* * fwd_offset + d* * bck_offset (32-bit accumulation) + vuint32m2_t vblend0 = __riscv_vwmulu_vx_u32m2(dd0, fwd_offset, vl); + vblend0 = __riscv_vwmaccu_vx_u32m2(vblend0, bck_offset, d0, vl); + + vuint32m2_t vblend1 = __riscv_vwmulu_vx_u32m2(dd1, fwd_offset, vl); + vblend1 = __riscv_vwmaccu_vx_u32m2(vblend1, bck_offset, d1, vl); + + vuint32m2_t vblend2 = __riscv_vwmulu_vx_u32m2(dd2, fwd_offset, vl); + vblend2 = __riscv_vwmaccu_vx_u32m2(vblend2, bck_offset, d2, vl); + + vuint32m2_t vblend3 = __riscv_vwmulu_vx_u32m2(dd3, fwd_offset, vl); + vblend3 = __riscv_vwmaccu_vx_u32m2(vblend3, bck_offset, d3, vl); + + // avg = blend >> DIST_PRECISION_BITS (narrow to 16-bit) + vuint16m1_t vavg0 = __riscv_vnsrl_wx_u16m1(vblend0, DIST_PRECISION_BITS, vl); + vuint16m1_t vavg1 = __riscv_vnsrl_wx_u16m1(vblend1, DIST_PRECISION_BITS, vl); + vuint16m1_t vavg2 = __riscv_vnsrl_wx_u16m1(vblend2, DIST_PRECISION_BITS, vl); + vuint16m1_t vavg3 = __riscv_vnsrl_wx_u16m1(vblend3, DIST_PRECISION_BITS, vl); + + // dst = vavg - round_offset + vint16m1_t vdst0 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg0), round_offset, vl); + vint16m1_t vdst1 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg1), round_offset, vl); + vint16m1_t vdst2 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg2), round_offset, vl); + vint16m1_t vdst3 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg3), round_offset, vl); + + // Saturating Rounded Shift right and narrow to u8 + const int shift = FILTER_BITS - ROUND0_BITS; + vuint16m1_t d0_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst0, 0, vl)); + vuint16m1_t d1_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst1, 0, vl)); + vuint16m1_t d2_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst2, 0, vl)); + vuint16m1_t d3_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst3, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, shift, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf2(d1_clip, shift, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf2(d2_clip, shift, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf2(d3_clip, shift, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, shift, __RISCV_VXRM_RNU, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf2(d1_clip, shift, __RISCV_VXRM_RNU, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf2(d2_clip, shift, __RISCV_VXRM_RNU, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf2(d3_clip, shift, __RISCV_VXRM_RNU, vl); +#endif +} + +static inline void compute_basic_avg_8x4_rvv( + const vuint16m1_t dd0, const vuint16m1_t dd1, const vuint16m1_t dd2, + const vuint16m1_t dd3, const vuint16m1_t d0, const vuint16m1_t d1, + const vuint16m1_t d2, const vuint16m1_t d3, const int16_t round_offset, + vuint8mf2_t *d0_u8, vuint8mf2_t *d1_u8, vuint8mf2_t *d2_u8, + vuint8mf2_t *d3_u8, size_t vl) { + // avg = (dd + d) >> 1 (vector halving add) +#if __riscv_v_intrinsic == 11000 + vuint16m1_t vavg0 = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(dd0, d0, vl), 1, vl); + vuint16m1_t vavg1 = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(dd1, d1, vl), 1, vl); + vuint16m1_t vavg2 = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(dd2, d2, vl), 1, vl); + vuint16m1_t vavg3 = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(dd3, d3, vl), 1, vl); +#elif __riscv_v_intrinsic >= 12000 + vuint16m1_t vavg0 = __riscv_vaaddu_vv_u16m1(dd0, d0, __RISCV_VXRM_RDN, vl); + vuint16m1_t vavg1 = __riscv_vaaddu_vv_u16m1(dd1, d1, __RISCV_VXRM_RDN, vl); + vuint16m1_t vavg2 = __riscv_vaaddu_vv_u16m1(dd2, d2, __RISCV_VXRM_RDN, vl); + vuint16m1_t vavg3 = __riscv_vaaddu_vv_u16m1(dd3, d3, __RISCV_VXRM_RDN, vl); +#endif + + // vdst = (int16x8_t) vavg - round_offset + vint16m1_t vdst0 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg0), round_offset, vl); + vint16m1_t vdst1 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg1), round_offset, vl); + vint16m1_t vdst2 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg2), round_offset, vl); + vint16m1_t vdst3 = __riscv_vsub_vx_i16m1( + __riscv_vreinterpret_v_u16m1_i16m1(vavg3), round_offset, vl); + + // Saturating Rounded Shift right and narrow to u8 + const int shift = FILTER_BITS - ROUND0_BITS; + vuint16m1_t d0_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst0, 0, vl)); + vuint16m1_t d1_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst1, 0, vl)); + vuint16m1_t d2_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst2, 0, vl)); + vuint16m1_t d3_clip = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(vdst3, 0, vl)); +#if __riscv_v_intrinsic == 11000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, shift, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf2(d1_clip, shift, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf2(d2_clip, shift, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf2(d3_clip, shift, vl); +#elif __riscv_v_intrinsic >= 12000 + *d0_u8 = __riscv_vnclipu_wx_u8mf2(d0_clip, shift, __RISCV_VXRM_RNU, vl); + *d1_u8 = __riscv_vnclipu_wx_u8mf2(d1_clip, shift, __RISCV_VXRM_RNU, vl); + *d2_u8 = __riscv_vnclipu_wx_u8mf2(d2_clip, shift, __RISCV_VXRM_RNU, vl); + *d3_u8 = __riscv_vnclipu_wx_u8mf2(d3_clip, shift, __RISCV_VXRM_RNU, vl); +#endif +} + +static inline vuint16mf2_t convolve6_4_2d_v_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const int16_t *filter, const int32_t offset_const) { + // Filter values at indices 0 and 7 are 0. + vint32m1_t sum = __riscv_vwmul_vx_i32m1(s0, filter[1], 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[2], s1, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[3], s2, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[4], s3, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[5], s4, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[6], s5, 4); + sum = __riscv_vadd_vx_i32m1(sum, offset_const, 4); + + // Round and shift + vuint32m1_t d0 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, 4)); + +#if __riscv_v_intrinsic == 11000 + return __riscv_vnclipu_wx_u16mf2(d0, COMPOUND_ROUND1_BITS, 4); +#elif __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16mf2(d0, COMPOUND_ROUND1_BITS, __RISCV_VXRM_RNU, + 4); +#endif +} + +static inline vuint16m1_t convolve6_8_2d_v_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const int16_t *filter, const int32_t offset_const, size_t vl) { + // Filter values at indices 0 and 7 are 0. + vint32m2_t sum = __riscv_vwmul_vx_i32m2(s0, filter[1], vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[2], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[3], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[4], s3, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[5], s4, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[6], s5, vl); + sum = __riscv_vadd_vx_i32m2(sum, offset_const, vl); + + // Round and shift + vuint32m2_t d0 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); + +#if __riscv_v_intrinsic == 11000 + return __riscv_vnclipu_wx_u16m1(d0, COMPOUND_ROUND1_BITS, vl); +#elif __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16m1(d0, COMPOUND_ROUND1_BITS, __RISCV_VXRM_RNU, + vl); +#endif +} + +static inline void dist_wtd_convolve_2d_vert_6tap_dist_wtd_avg_rvv( + int16_t *src_ptr, const int src_stride, uint8_t *dst8_ptr, int dst8_stride, + ConvolveParams *conv_params, const int16_t *y_filter, int h, int w) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int32_t offset_const = 1 << offset_bits; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // load initial 5 rows of data + vint16mf2_t s0, s1, s2, s3, s4; + load_s16_4x5(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + src_ptr += 5 * src_stride; + + do { + // load next 4 rows of data + vint16mf2_t s5, s6, s7, s8; + load_s16_4x4(src_ptr, src_stride, &s5, &s6, &s7, &s8, vl); + + // perform 6-tap convolution + vuint16mf2_t d0, d1, d2, d3; + d0 = convolve6_4_2d_v_rvv(s0, s1, s2, s3, s4, s5, y_filter, offset_const); + d1 = convolve6_4_2d_v_rvv(s1, s2, s3, s4, s5, s6, y_filter, offset_const); + d2 = convolve6_4_2d_v_rvv(s2, s3, s4, s5, s6, s7, y_filter, offset_const); + d3 = convolve6_4_2d_v_rvv(s3, s4, s5, s6, s7, s8, y_filter, offset_const); + + // weighted average + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, &d0_u8, + &d1_u8, &d2_u8, &d3_u8, vl); + + // store results + __riscv_vse8_v_u8mf4(dst8_ptr + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 3 * dst8_stride, d3_u8, vl); + + // update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int16_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // Load initial 5 rows of data + vint16m1_t s0, s1, s2, s3, s4; + load_s16_8x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + // load next 4 rows of data + vint16m1_t s5, s6, s7, s8; + load_s16_8x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + // perform 6-tap convolution + vuint16m1_t d0, d1, d2, d3; + d0 = convolve6_8_2d_v_rvv(s0, s1, s2, s3, s4, s5, y_filter, + offset_const, vl); + d1 = convolve6_8_2d_v_rvv(s1, s2, s3, s4, s5, s6, y_filter, + offset_const, vl); + d2 = convolve6_8_2d_v_rvv(s2, s3, s4, s5, s6, s7, y_filter, + offset_const, vl); + d3 = convolve6_8_2d_v_rvv(s3, s4, s5, s6, s7, s8, y_filter, + offset_const, vl); + + // weighted average + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + + // store results + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + // update sliding window + s0 = __riscv_vmv_v_v_i16m1(s4, vl); + s1 = __riscv_vmv_v_v_i16m1(s5, vl); + s2 = __riscv_vmv_v_v_i16m1(s6, vl); + s3 = __riscv_vmv_v_v_i16m1(s7, vl); + s4 = __riscv_vmv_v_v_i16m1(s8, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline void dist_wtd_convolve_2d_vert_6tap_avg_rvv( + int16_t *src_ptr, const int src_stride, uint8_t *dst8_ptr, int dst8_stride, + ConvolveParams *conv_params, const int16_t *y_filter, int h, int w) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int32_t offset_const = 1 << offset_bits; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // load initial 5 rows of data + vint16mf2_t s0, s1, s2, s3, s4; + load_s16_4x5(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + src_ptr += 5 * src_stride; + + do { + // load next 4 rows of data + vint16mf2_t s5, s6, s7, s8; + load_s16_4x4(src_ptr, src_stride, &s5, &s6, &s7, &s8, vl); + + // perform 6-tap convolution + vuint16mf2_t d0, d1, d2, d3; + d0 = convolve6_4_2d_v_rvv(s0, s1, s2, s3, s4, s5, y_filter, offset_const); + d1 = convolve6_4_2d_v_rvv(s1, s2, s3, s4, s5, s6, y_filter, offset_const); + d2 = convolve6_4_2d_v_rvv(s2, s3, s4, s5, s6, s7, y_filter, offset_const); + d3 = convolve6_4_2d_v_rvv(s3, s4, s5, s6, s7, s8, y_filter, offset_const); + + // average + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // Store result + __riscv_vse8_v_u8mf4(dst8_ptr + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 3 * dst8_stride, d3_u8, vl); + dst8_ptr += 4 * dst8_stride; + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int16_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // Load initial 5 rows of data + vint16m1_t s0, s1, s2, s3, s4; + load_s16_8x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + // load next 4 rows of data + vint16m1_t s5, s6, s7, s8; + load_s16_8x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + // perform 6-tap convolution + vuint16m1_t d0, d1, d2, d3; + d0 = convolve6_8_2d_v_rvv(s0, s1, s2, s3, s4, s5, y_filter, + offset_const, vl); + d1 = convolve6_8_2d_v_rvv(s1, s2, s3, s4, s5, s6, y_filter, + offset_const, vl); + d2 = convolve6_8_2d_v_rvv(s2, s3, s4, s5, s6, s7, y_filter, + offset_const, vl); + d3 = convolve6_8_2d_v_rvv(s3, s4, s5, s6, s7, s8, y_filter, + offset_const, vl); + + // average + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // store results + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + // update sliding window + s0 = __riscv_vmv_v_v_i16m1(s4, vl); + s1 = __riscv_vmv_v_v_i16m1(s5, vl); + s2 = __riscv_vmv_v_v_i16m1(s6, vl); + s3 = __riscv_vmv_v_v_i16m1(s7, vl); + s4 = __riscv_vmv_v_v_i16m1(s8, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline void dist_wtd_convolve_2d_vert_6tap_rvv( + int16_t *src_ptr, const int src_stride, ConvolveParams *conv_params, + const int16_t *y_filter, int h, int w) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int32_t offset_const = 1 << offset_bits; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // load initial 5 rows of data + vint16mf2_t s0, s1, s2, s3, s4; + load_s16_4x5(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + src_ptr += 5 * src_stride; + + do { + // load next 4 rows of data + vint16mf2_t s5, s6, s7, s8; + load_s16_4x4(src_ptr, src_stride, &s5, &s6, &s7, &s8, vl); + + // perform 6-tap convolution + vuint16mf2_t d0, d1, d2, d3; + d0 = convolve6_4_2d_v_rvv(s0, s1, s2, s3, s4, s5, y_filter, offset_const); + d1 = convolve6_4_2d_v_rvv(s1, s2, s3, s4, s5, s6, y_filter, offset_const); + d2 = convolve6_4_2d_v_rvv(s2, s3, s4, s5, s6, s7, y_filter, offset_const); + d3 = convolve6_4_2d_v_rvv(s3, s4, s5, s6, s7, s8, y_filter, offset_const); + + // Store result + store_u16_4x4(dst_ptr, dst_stride, d0, d1, d2, d3, vl); + + // Update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int16_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int height = h; + + // Load initial 5 rows of data + vint16m1_t s0, s1, s2, s3, s4; + load_s16_8x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + // load next 4 rows of data + vint16m1_t s5, s6, s7, s8; + load_s16_8x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + // perform 6-tap convolution + vuint16m1_t d0, d1, d2, d3; + d0 = convolve6_8_2d_v_rvv(s0, s1, s2, s3, s4, s5, y_filter, + offset_const, vl); + d1 = convolve6_8_2d_v_rvv(s1, s2, s3, s4, s5, s6, y_filter, + offset_const, vl); + d2 = convolve6_8_2d_v_rvv(s2, s3, s4, s5, s6, s7, y_filter, + offset_const, vl); + d3 = convolve6_8_2d_v_rvv(s3, s4, s5, s6, s7, s8, y_filter, + offset_const, vl); + + // store results + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + // update sliding window + s0 = __riscv_vmv_v_v_i16m1(s4, vl); + s1 = __riscv_vmv_v_v_i16m1(s5, vl); + s2 = __riscv_vmv_v_v_i16m1(s6, vl); + s3 = __riscv_vmv_v_v_i16m1(s7, vl); + s4 = __riscv_vmv_v_v_i16m1(s8, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline vuint16mf2_t convolve8_4_2d_v_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const vint16mf2_t s6, const vint16mf2_t s7, const int16_t *filter, + const int32_t offset_const) { + vint32m1_t sum = __riscv_vwmul_vx_i32m1(s0, filter[0], 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[1], s1, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[2], s2, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[3], s3, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[4], s4, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[5], s5, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[6], s6, 4); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[7], s7, 4); + sum = __riscv_vadd_vx_i32m1(sum, offset_const, 4); + + // Round and shift + vuint32m1_t d0 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, 4)); + +#if __riscv_v_intrinsic == 11000 + return __riscv_vnclipu_wx_u16mf2(d0, COMPOUND_ROUND1_BITS, 4); +#elif __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16mf2(d0, COMPOUND_ROUND1_BITS, __RISCV_VXRM_RNU, + 4); +#endif +} + +static inline vuint16m1_t convolve8_8_2d_v_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, const int16_t *filter, + const int32_t offset_const, size_t vl) { + vint32m2_t sum = __riscv_vwmul_vx_i32m2(s0, filter[0], vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[3], s3, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[4], s4, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[5], s5, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[6], s6, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[7], s7, vl); + sum = __riscv_vadd_vx_i32m2(sum, offset_const, vl); + + // Round and shift + vuint32m2_t d0 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); + +#if __riscv_v_intrinsic == 11000 + return __riscv_vnclipu_wx_u16m1(d0, COMPOUND_ROUND1_BITS, vl); +#elif __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16m1(d0, COMPOUND_ROUND1_BITS, __RISCV_VXRM_RNU, + vl); +#endif +} + +static inline void dist_wtd_convolve_2d_vert_8tap_dist_wtd_avg_rvv( + int16_t *src_ptr, const int src_stride, uint8_t *dst8_ptr, int dst8_stride, + ConvolveParams *conv_params, const int16_t *y_filter, int h, int w) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int32_t offset_const = 1 << offset_bits; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint16_t fwd_offset = conv_params->fwd_offset; + const uint16_t bck_offset = conv_params->bck_offset; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // load initial 7 rows of data + vint16mf2_t s0, s1, s2, s3, s4, s5, s6; + load_s16_4x7(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + src_ptr += 7 * src_stride; + + do { + // load next 4 rows of data + vint16mf2_t s7, s8, s9, s10; + load_s16_4x4(src_ptr, src_stride, &s7, &s8, &s9, &s10, vl); + + // Perform convolution + vuint16mf2_t d0, d1, d2, d3; + d0 = convolve8_4_2d_v_rvv(s0, s1, s2, s3, s4, s5, s6, s7, y_filter, + offset_const); + d1 = convolve8_4_2d_v_rvv(s1, s2, s3, s4, s5, s6, s7, s8, y_filter, + offset_const); + d2 = convolve8_4_2d_v_rvv(s2, s3, s4, s5, s6, s7, s8, s9, y_filter, + offset_const); + d3 = convolve8_4_2d_v_rvv(s3, s4, s5, s6, s7, s8, s9, s10, y_filter, + offset_const); + + // weighted average + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, &d0_u8, + &d1_u8, &d2_u8, &d3_u8, vl); + + // store results + __riscv_vse8_v_u8mf4(dst8_ptr + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 3 * dst8_stride, d3_u8, vl); + + // update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s5 = __riscv_vmv_v_v_i16mf2(s9, vl); + s6 = __riscv_vmv_v_v_i16mf2(s10, vl); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + dst8_ptr += 4 * dst8_stride; + h -= 4; + } while (h != 0); + } else { + do { + int16_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // load initial 7 rows of data + vint16m1_t s0, s1, s2, s3, s4, s5, s6; + load_s16_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + // load next 4 rows of data + vint16m1_t s7, s8, s9, s10; + load_s16_8x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + // perform convolution + vuint16m1_t d0 = convolve8_8_2d_v_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, offset_const, vl); + vuint16m1_t d1 = convolve8_8_2d_v_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, offset_const, vl); + vuint16m1_t d2 = convolve8_8_2d_v_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, offset_const, vl); + vuint16m1_t d3 = convolve8_8_2d_v_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, offset_const, vl); + + // weighted average + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_dist_wtd_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + fwd_offset, bck_offset, round_offset, + &d0_u8, &d1_u8, &d2_u8, &d3_u8, vl); + + // store results + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + // update sliding window + s0 = __riscv_vmv_v_v_i16m1(s4, vl); + s1 = __riscv_vmv_v_v_i16m1(s5, vl); + s2 = __riscv_vmv_v_v_i16m1(s6, vl); + s3 = __riscv_vmv_v_v_i16m1(s7, vl); + s4 = __riscv_vmv_v_v_i16m1(s8, vl); + s5 = __riscv_vmv_v_v_i16m1(s9, vl); + s6 = __riscv_vmv_v_v_i16m1(s10, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline void dist_wtd_convolve_2d_vert_8tap_avg_rvv( + int16_t *src_ptr, const int src_stride, uint8_t *dst8_ptr, int dst8_stride, + ConvolveParams *conv_params, const int16_t *y_filter, int h, int w) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int32_t offset_const = 1 << offset_bits; + const int16_t round_offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // load initial 7 rows of data + vint16mf2_t s0, s1, s2, s3, s4, s5, s6; + load_s16_4x7(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + src_ptr += 7 * src_stride; + + do { + // load next 4 rows of data + vint16mf2_t s7, s8, s9, s10; + load_s16_4x4(src_ptr, src_stride, &s7, &s8, &s9, &s10, vl); + + // Perform convolution + vuint16mf2_t d0, d1, d2, d3; + d0 = convolve8_4_2d_v_rvv(s0, s1, s2, s3, s4, s5, s6, s7, y_filter, + offset_const); + d1 = convolve8_4_2d_v_rvv(s1, s2, s3, s4, s5, s6, s7, s8, y_filter, + offset_const); + d2 = convolve8_4_2d_v_rvv(s2, s3, s4, s5, s6, s7, s8, s9, y_filter, + offset_const); + d3 = convolve8_4_2d_v_rvv(s3, s4, s5, s6, s7, s8, s9, s10, y_filter, + offset_const); + + // average + vuint16mf2_t dd0, dd1, dd2, dd3; + load_u16_4x4(dst_ptr, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf4_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_4x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // Store result + __riscv_vse8_v_u8mf4(dst8_ptr + 0 * dst8_stride, d0_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 1 * dst8_stride, d1_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 2 * dst8_stride, d2_u8, vl); + __riscv_vse8_v_u8mf4(dst8_ptr + 3 * dst8_stride, d3_u8, vl); + dst8_ptr += 4 * dst8_stride; + + // update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s5 = __riscv_vmv_v_v_i16mf2(s9, vl); + s6 = __riscv_vmv_v_v_i16mf2(s10, vl); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int16_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + uint8_t *d_u8 = dst8_ptr; + int height = h; + + // load initial 7 rows of data + vint16m1_t s0, s1, s2, s3, s4, s5, s6; + load_s16_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + // load next 4 rows of data + vint16m1_t s7, s8, s9, s10; + load_s16_8x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + // perform convolution + vuint16m1_t d0 = convolve8_8_2d_v_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, offset_const, vl); + vuint16m1_t d1 = convolve8_8_2d_v_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, offset_const, vl); + vuint16m1_t d2 = convolve8_8_2d_v_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, offset_const, vl); + vuint16m1_t d3 = convolve8_8_2d_v_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, offset_const, vl); + + // average + vuint16m1_t dd0, dd1, dd2, dd3; + load_u16_8x4(d, dst_stride, &dd0, &dd1, &dd2, &dd3, vl); + + vuint8mf2_t d0_u8, d1_u8, d2_u8, d3_u8; + compute_basic_avg_8x4_rvv(dd0, dd1, dd2, dd3, d0, d1, d2, d3, + round_offset, &d0_u8, &d1_u8, &d2_u8, &d3_u8, + vl); + + // store results + store_u8_8x4(d_u8, dst8_stride, d0_u8, d1_u8, d2_u8, d3_u8, vl); + d_u8 += 4 * dst8_stride; + + // update sliding window + s0 = __riscv_vmv_v_v_i16m1(s4, vl); + s1 = __riscv_vmv_v_v_i16m1(s5, vl); + s2 = __riscv_vmv_v_v_i16m1(s6, vl); + s3 = __riscv_vmv_v_v_i16m1(s7, vl); + s4 = __riscv_vmv_v_v_i16m1(s8, vl); + s5 = __riscv_vmv_v_v_i16m1(s9, vl); + s6 = __riscv_vmv_v_v_i16m1(s10, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + dst8_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline void dist_wtd_convolve_2d_vert_8tap_rvv( + int16_t *src_ptr, const int src_stride, ConvolveParams *conv_params, + const int16_t *y_filter, int h, int w) { + const int bd = 8; + const int offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + const int32_t offset_const = 1 << offset_bits; + + CONV_BUF_TYPE *dst_ptr = conv_params->dst; + const int dst_stride = conv_params->dst_stride; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + // load initial 7 rows of data + vint16mf2_t s0, s1, s2, s3, s4, s5, s6; + load_s16_4x7(src_ptr, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + src_ptr += 7 * src_stride; + + do { + // load next 4 rows of data + vint16mf2_t s7, s8, s9, s10; + load_s16_4x4(src_ptr, src_stride, &s7, &s8, &s9, &s10, vl); + + // perform convolution + vuint16mf2_t d0, d1, d2, d3; + d0 = convolve8_4_2d_v_rvv(s0, s1, s2, s3, s4, s5, s6, s7, y_filter, + offset_const); + d1 = convolve8_4_2d_v_rvv(s1, s2, s3, s4, s5, s6, s7, s8, y_filter, + offset_const); + d2 = convolve8_4_2d_v_rvv(s2, s3, s4, s5, s6, s7, s8, s9, y_filter, + offset_const); + d3 = convolve8_4_2d_v_rvv(s3, s4, s5, s6, s7, s8, s9, s10, y_filter, + offset_const); + + // store result + store_u16_4x4(dst_ptr, dst_stride, d0, d1, d2, d3, vl); + + // update sliding window + s0 = __riscv_vmv_v_v_i16mf2(s4, vl); + s1 = __riscv_vmv_v_v_i16mf2(s5, vl); + s2 = __riscv_vmv_v_v_i16mf2(s6, vl); + s3 = __riscv_vmv_v_v_i16mf2(s7, vl); + s4 = __riscv_vmv_v_v_i16mf2(s8, vl); + s5 = __riscv_vmv_v_v_i16mf2(s9, vl); + s6 = __riscv_vmv_v_v_i16mf2(s10, vl); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int16_t *s = src_ptr; + CONV_BUF_TYPE *d = dst_ptr; + int height = h; + + // load initial 7 rows of data + vint16m1_t s0, s1, s2, s3, s4, s5, s6; + load_s16_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + // load next 4 rows of data + vint16m1_t s7, s8, s9, s10; + load_s16_8x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + // perform convolution + vuint16m1_t d0 = convolve8_8_2d_v_rvv(s0, s1, s2, s3, s4, s5, s6, s7, + y_filter, offset_const, vl); + vuint16m1_t d1 = convolve8_8_2d_v_rvv(s1, s2, s3, s4, s5, s6, s7, s8, + y_filter, offset_const, vl); + vuint16m1_t d2 = convolve8_8_2d_v_rvv(s2, s3, s4, s5, s6, s7, s8, s9, + y_filter, offset_const, vl); + vuint16m1_t d3 = convolve8_8_2d_v_rvv(s3, s4, s5, s6, s7, s8, s9, s10, + y_filter, offset_const, vl); + + // store results + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + // update sliding window + s0 = __riscv_vmv_v_v_i16m1(s4, vl); + s1 = __riscv_vmv_v_v_i16m1(s5, vl); + s2 = __riscv_vmv_v_v_i16m1(s6, vl); + s3 = __riscv_vmv_v_v_i16m1(s7, vl); + s4 = __riscv_vmv_v_v_i16m1(s8, vl); + s5 = __riscv_vmv_v_v_i16m1(s9, vl); + s6 = __riscv_vmv_v_v_i16m1(s10, vl); + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +#endif // AOM_AV1_COMMON_RISCV_COMPOUND_CONVOLVE_RVV_H_
diff --git a/av1/common/riscv/highbd_compound_convolve_rvv.c b/av1/common/riscv/highbd_compound_convolve_rvv.c new file mode 100644 index 0000000..9f2f869 --- /dev/null +++ b/av1/common/riscv/highbd_compound_convolve_rvv.c
@@ -0,0 +1,1386 @@ +/* + * Copyright (c) 2026, 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 <riscv_vector.h> +#include <assert.h> + +#include "config/aom_config.h" +#include "config/av1_rtcd.h" + +#include "av1/common/convolve.h" +#include "av1/common/filter.h" +#include "av1/common/riscv/highbd_compound_convolve_rvv.h" + +static inline vuint16mf2_t highbd_convolve6_4_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const int16_t *filter, const vint32m1_t offset_vec, + const int32_t round_bits, const size_t vl) { + // Values at indices 0 and 7 of filter are zero. + vint32m1_t sum = __riscv_vwmacc_vx_i32m1(offset_vec, filter[1], s0, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[2], s1, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[3], s2, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[4], s3, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[5], s4, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[6], s5, vl); + + vuint32m1_t sum_u32 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16mf2_t res = + __riscv_vnclipu_wx_u16mf2(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16mf2_t res = __riscv_vnsrl_wx_u16mf2(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline vuint16m1_t highbd_convolve6_8_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const int16_t *filter, const vint32m2_t offset_vec, + const int32_t round_bits, const size_t vl) { + // Values at indices 0 and 7 of filter are zero. + vint32m2_t sum = __riscv_vwmacc_vx_i32m2(offset_vec, filter[1], s0, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[2], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[3], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[4], s3, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[5], s4, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[6], s5, vl); + + vuint32m2_t sum_u32 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t res = + __riscv_vnclipu_wx_u16m1(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16m1_t res = __riscv_vnsrl_wx_u16m1(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline void highbd_dist_wtd_convolve_x_6tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *x_filter_ptr, const int offset, + const int round_bits) { + int height = h; + const size_t vl = __riscv_vsetvl_e16m1(w); + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + + do { + int width = w; + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + do { + vint16m1_t s00, s01, s02, s03, s04, s05; + vint16m1_t s10, s11, s12, s13, s14, s15; + vint16m1_t s20, s21, s22, s23, s24, s25; + vint16m1_t s30, s31, s32, s33, s34, s35; + + // Load 6 consecutive 8-element vectors from 4 rows + load_s16_8x6(s + 0 * src_stride, 1, &s00, &s01, &s02, &s03, &s04, &s05, + vl); + load_s16_8x6(s + 1 * src_stride, 1, &s10, &s11, &s12, &s13, &s14, &s15, + vl); + load_s16_8x6(s + 2 * src_stride, 1, &s20, &s21, &s22, &s23, &s24, &s25, + vl); + load_s16_8x6(s + 3 * src_stride, 1, &s30, &s31, &s32, &s33, &s34, &s35, + vl); + + // Convolve each row + vuint16m1_t d0 = + highbd_convolve6_8_rvv(s00, s01, s02, s03, s04, s05, x_filter_ptr, + offset_vec, round_bits, vl); + vuint16m1_t d1 = + highbd_convolve6_8_rvv(s10, s11, s12, s13, s14, s15, x_filter_ptr, + offset_vec, round_bits, vl); + vuint16m1_t d2 = + highbd_convolve6_8_rvv(s20, s21, s22, s23, s24, s25, x_filter_ptr, + offset_vec, round_bits, vl); + vuint16m1_t d3 = + highbd_convolve6_8_rvv(s30, s31, s32, s33, s34, s35, x_filter_ptr, + offset_vec, round_bits, vl); + + // Store results with stride + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + s += vl; + d += vl; + width -= vl; + } while (width > 0); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + height -= 4; + } while (height != 0); +} + +static inline vuint16mf2_t highbd_convolve8_4_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const vint16mf2_t s6, const vint16mf2_t s7, const int16_t *filter, + const vint32m1_t offset_vec, const int32_t round_bits, const size_t vl) { + vint32m1_t sum = __riscv_vwmacc_vx_i32m1(offset_vec, filter[0], s0, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[3], s3, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[4], s4, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[5], s5, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[6], s6, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[7], s7, vl); + + vuint32m1_t sum_u32 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16mf2_t res = + __riscv_vnclipu_wx_u16mf2(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16mf2_t res = __riscv_vnsrl_wx_u16mf2(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline vuint16m1_t highbd_convolve8_8_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, const int16_t *filter, + const vint32m2_t offset_vec, const int32_t round_bits, const size_t vl) { + vint32m2_t sum = __riscv_vwmacc_vx_i32m2(offset_vec, filter[0], s0, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[3], s3, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[4], s4, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[5], s5, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[6], s6, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[7], s7, vl); + + vuint32m2_t sum_u32 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t res = + __riscv_vnclipu_wx_u16m1(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16m1_t res = __riscv_vnsrl_wx_u16m1(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline vuint16mf2_t highbd_convolve4_4_x_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const int16_t *filter, const vint32m1_t offset_vec, + const int round_bits, const size_t vl) { + vint32m1_t sum = __riscv_vwmacc_vx_i32m1(offset_vec, filter[0], s0, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[3], s3, vl); + + vuint32m1_t sum_u32 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16mf2_t res = + __riscv_vnclipu_wx_u16mf2(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16mf2_t res = __riscv_vnsrl_wx_u16mf2(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline void highbd_dist_wtd_convolve_x_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *x_filter_ptr, const int offset, + const int round_bits) { + size_t vl = __riscv_vsetvl_e16m1(w); + if (w == 4) { + // 4-tap filters are used for blocks having width == 4. + const int16_t *filter = x_filter_ptr + 2; // Skip first 2 taps + const int16_t *s = (const int16_t *)(src_ptr + 2); + uint16_t *d = dst_ptr; + vint32m1_t offset_vec = __riscv_vmv_v_x_i32m1(offset, vl); + + do { + // Load 4 taps for 4 rows + vint16mf2_t s00, s01, s02, s03; + vint16mf2_t s10, s11, s12, s13; + vint16mf2_t s20, s21, s22, s23; + vint16mf2_t s30, s31, s32, s33; + + load_s16_4x4(s + 0 * src_stride, 1, &s00, &s01, &s02, &s03, vl); + load_s16_4x4(s + 1 * src_stride, 1, &s10, &s11, &s12, &s13, vl); + load_s16_4x4(s + 2 * src_stride, 1, &s20, &s21, &s22, &s23, vl); + load_s16_4x4(s + 3 * src_stride, 1, &s30, &s31, &s32, &s33, vl); + + // Convolve each row + vuint16mf2_t d0 = highbd_convolve4_4_x_rvv(s00, s01, s02, s03, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d1 = highbd_convolve4_4_x_rvv(s10, s11, s12, s13, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d2 = highbd_convolve4_4_x_rvv(s20, s21, s22, s23, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d3 = highbd_convolve4_4_x_rvv(s30, s31, s32, s33, filter, + offset_vec, round_bits, vl); + + // Store results + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + // 8-tap filter path + const int16_t *filter = x_filter_ptr; + int height = h; + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + + do { + int width = w; + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + do { + vint16m1_t s00, s01, s02, s03, s04, s05, s06, s07; + vint16m1_t s10, s11, s12, s13, s14, s15, s16, s17; + vint16m1_t s20, s21, s22, s23, s24, s25, s26, s27; + vint16m1_t s30, s31, s32, s33, s34, s35, s36, s37; + + // Load elements for each of 4 rows + load_s16_8x8(s + 0 * src_stride, 1, &s00, &s01, &s02, &s03, &s04, &s05, + &s06, &s07, vl); + load_s16_8x8(s + 1 * src_stride, 1, &s10, &s11, &s12, &s13, &s14, &s15, + &s16, &s17, vl); + load_s16_8x8(s + 2 * src_stride, 1, &s20, &s21, &s22, &s23, &s24, &s25, + &s26, &s27, vl); + load_s16_8x8(s + 3 * src_stride, 1, &s30, &s31, &s32, &s33, &s34, &s35, + &s36, &s37, vl); + + // Convolve each row + vuint16m1_t d0 = + highbd_convolve8_8_rvv(s00, s01, s02, s03, s04, s05, s06, s07, + filter, offset_vec, round_bits, vl); + vuint16m1_t d1 = + highbd_convolve8_8_rvv(s10, s11, s12, s13, s14, s15, s16, s17, + filter, offset_vec, round_bits, vl); + vuint16m1_t d2 = + highbd_convolve8_8_rvv(s20, s21, s22, s23, s24, s25, s26, s27, + filter, offset_vec, round_bits, vl); + vuint16m1_t d3 = + highbd_convolve8_8_rvv(s30, s31, s32, s33, s34, s35, s36, s37, + filter, offset_vec, round_bits, vl); + + // Store results + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + s += vl; + d += vl; + width -= vl; + } while (width > 0); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + height -= 4; + } while (height != 0); + } +} + +void av1_highbd_dist_wtd_convolve_x_rvv( + const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w, + int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn, + ConvolveParams *conv_params, int bd) { + DECLARE_ALIGNED(16, uint16_t, + im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * MAX_SB_SIZE]); + CONV_BUF_TYPE *dst16 = conv_params->dst; + const int x_filter_taps = get_filter_tap(filter_params_x, subpel_x_qn); + int dst16_stride = conv_params->dst_stride; + const int im_stride = MAX_SB_SIZE; + const int horiz_offset = filter_params_x->taps / 2 - 1; + assert(FILTER_BITS == COMPOUND_ROUND1_BITS); + const int offset_convolve = (1 << (conv_params->round_0 - 1)) + + (1 << (bd + FILTER_BITS)) + + (1 << (bd + FILTER_BITS - 1)); + + const int16_t *x_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_x, subpel_x_qn & SUBPEL_MASK); + + src -= horiz_offset; + int round_bits, shift_bits, offset_bits; + + // horizontal filter + if (bd == 12) { + round_bits = ROUND0_BITS + 2; + shift_bits = ROUND_SHIFT - 2; + offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS - 2; + } else { + round_bits = ROUND0_BITS; + shift_bits = ROUND_SHIFT; + offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + } + + if (conv_params->do_average) { + if (x_filter_taps <= 6 && w != 4) { + highbd_dist_wtd_convolve_x_6tap_rvv(src + 1, src_stride, im_block, + im_stride, w, h, x_filter_ptr, + offset_convolve, round_bits); + } else { + highbd_dist_wtd_convolve_x_rvv(src, src_stride, im_block, im_stride, w, h, + x_filter_ptr, offset_convolve, round_bits); + } + if (conv_params->use_dist_wtd_comp_avg) { + highbd_dist_wtd_comp_avg_rvv(im_block, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } else { + highbd_comp_avg_rvv(im_block, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } + } else { + if (x_filter_taps <= 6 && w != 4) { + highbd_dist_wtd_convolve_x_6tap_rvv(src + 1, src_stride, dst16, + dst16_stride, w, h, x_filter_ptr, + offset_convolve, round_bits); + } else { + highbd_dist_wtd_convolve_x_rvv(src, src_stride, dst16, dst16_stride, w, h, + x_filter_ptr, offset_convolve, round_bits); + } + } +} + +static inline void highbd_dist_wtd_convolve_y_6tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *y_filter_ptr, const int offset, + const int round_bits) { + const int16_t *filter = y_filter_ptr; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + vint32m1_t offset_vec = __riscv_vmv_v_x_i32m1(offset, vl); + + // Load initial 5 rows for 6-tap filter + vint16mf2_t s0, s1, s2, s3, s4; + load_s16_4x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + // Load next 4 rows + vint16mf2_t s5, s6, s7, s8; + load_s16_4x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + // Convolve 4 output rows + vuint16mf2_t d0 = highbd_convolve6_4_rvv(s0, s1, s2, s3, s4, s5, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d1 = highbd_convolve6_4_rvv(s1, s2, s3, s4, s5, s6, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d2 = highbd_convolve6_4_rvv(s2, s3, s4, s5, s6, s7, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d3 = highbd_convolve6_4_rvv(s3, s4, s5, s6, s7, s8, filter, + offset_vec, round_bits, vl); + + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Shift window: s0-s4 become s4-s8 for next iteration + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + do { + int height = h; + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + // Load initial 5 rows for 6-tap filter + vint16m1_t s0, s1, s2, s3, s4; + load_s16_8x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + // Load next 4 rows + vint16m1_t s5, s6, s7, s8; + load_s16_8x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + // Convolve 4 output rows + vuint16m1_t d0 = highbd_convolve6_8_rvv(s0, s1, s2, s3, s4, s5, filter, + offset_vec, round_bits, vl); + vuint16m1_t d1 = highbd_convolve6_8_rvv(s1, s2, s3, s4, s5, s6, filter, + offset_vec, round_bits, vl); + vuint16m1_t d2 = highbd_convolve6_8_rvv(s2, s3, s4, s5, s6, s7, filter, + offset_vec, round_bits, vl); + vuint16m1_t d3 = highbd_convolve6_8_rvv(s3, s4, s5, s6, s7, s8, filter, + offset_vec, round_bits, vl); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Shift window: s0-s4 become s4-s8 for next iteration + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline vuint16mf2_t highbd_convolve4_4_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const int16_t *filter, const int32_t offset, + const int32_t round_bits, size_t vl) { + vint32m1_t sum = __riscv_vwmul_vx_i32m1(s0, filter[0], vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, filter[3], s3, vl); + sum = __riscv_vadd_vx_i32m1(sum, offset, vl); + + vuint32m1_t sum_u32 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16mf2_t res = + __riscv_vnclipu_wx_u16mf2(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16mf2_t res = __riscv_vnsrl_wx_u16mf2(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline vuint16m1_t highbd_convolve4_8_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const int16_t *filter, const int32_t offset, + const int32_t round_bits, size_t vl) { + vint32m2_t sum = __riscv_vwmul_vx_i32m2(s0, filter[0], vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, filter[3], s3, vl); + sum = __riscv_vadd_vx_i32m2(sum, offset, vl); + + vuint32m2_t sum_u32 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t res = + __riscv_vnclipu_wx_u16m1(sum_u32, round_bits, __RISCV_VXRM_RDN, vl); +#else + vuint16m1_t res = __riscv_vnsrl_wx_u16m1(sum_u32, round_bits, vl); +#endif + return res; +} + +static inline void highbd_dist_wtd_convolve_y_4tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *y_filter_ptr, const int offset, + const int round_bits) { + const int16_t *filter = y_filter_ptr + 2; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + // Load initial 3 rows + vint16mf2_t s0, s1, s2; + load_s16_4x3(s, src_stride, &s0, &s1, &s2, vl); + s += 3 * src_stride; + + do { + // Load next 4 rows + vint16mf2_t s3, s4, s5, s6; + load_s16_4x4(s, src_stride, &s3, &s4, &s5, &s6, vl); + + // Convolve 4 output rows + vuint16mf2_t d0 = highbd_convolve4_4_rvv(s0, s1, s2, s3, filter, offset, + round_bits, vl); + vuint16mf2_t d1 = highbd_convolve4_4_rvv(s1, s2, s3, s4, filter, offset, + round_bits, vl); + vuint16mf2_t d2 = highbd_convolve4_4_rvv(s2, s3, s4, s5, filter, offset, + round_bits, vl); + vuint16mf2_t d3 = highbd_convolve4_4_rvv(s3, s4, s5, s6, filter, offset, + round_bits, vl); + + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Shift window + s0 = s4; + s1 = s5; + s2 = s6; + + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + do { + int height = h; + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + // Load initial 3 rows + vint16m1_t s0, s1, s2; + load_s16_8x3(s, src_stride, &s0, &s1, &s2, vl); + s += 3 * src_stride; + + do { + // Load next 4 rows + vint16m1_t s3, s4, s5, s6; + load_s16_8x4(s, src_stride, &s3, &s4, &s5, &s6, vl); + + // Convolve 4 output rows + vuint16m1_t d0 = highbd_convolve4_8_rvv(s0, s1, s2, s3, filter, offset, + round_bits, vl); + vuint16m1_t d1 = highbd_convolve4_8_rvv(s1, s2, s3, s4, filter, offset, + round_bits, vl); + vuint16m1_t d2 = highbd_convolve4_8_rvv(s2, s3, s4, s5, filter, offset, + round_bits, vl); + vuint16m1_t d3 = highbd_convolve4_8_rvv(s3, s4, s5, s6, filter, offset, + round_bits, vl); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Shift window + s0 = s4; + s1 = s5; + s2 = s6; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +static inline void highbd_dist_wtd_convolve_y_8tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *y_filter_ptr, const int offset, + const int32_t round_bits) { + const int16_t *filter = y_filter_ptr; + size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + vint32m1_t offset_vec = __riscv_vmv_v_x_i32m1(offset, vl); + + // Load initial 7 rows + vint16mf2_t s0, s1, s2, s3, s4, s5, s6; + load_s16_4x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + // Load next 4 rows + vint16mf2_t s7, s8, s9, s10; + load_s16_4x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + // Convolve 4 output rows + vuint16mf2_t d0 = highbd_convolve8_4_rvv( + s0, s1, s2, s3, s4, s5, s6, s7, filter, offset_vec, round_bits, vl); + vuint16mf2_t d1 = highbd_convolve8_4_rvv( + s1, s2, s3, s4, s5, s6, s7, s8, filter, offset_vec, round_bits, vl); + vuint16mf2_t d2 = highbd_convolve8_4_rvv( + s2, s3, s4, s5, s6, s7, s8, s9, filter, offset_vec, round_bits, vl); + vuint16mf2_t d3 = highbd_convolve8_4_rvv( + s3, s4, s5, s6, s7, s8, s9, s10, filter, offset_vec, round_bits, vl); + + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Shift window + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s5 = s9; + s6 = s10; + + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + do { + int height = h; + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + // Load initial 7 rows + vint16m1_t s0, s1, s2, s3, s4, s5, s6; + load_s16_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + // Load next 4 rows + vint16m1_t s7, s8, s9, s10; + load_s16_8x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + // Convolve 4 output rows + vuint16m1_t d0 = highbd_convolve8_8_rvv( + s0, s1, s2, s3, s4, s5, s6, s7, filter, offset_vec, round_bits, vl); + vuint16m1_t d1 = highbd_convolve8_8_rvv( + s1, s2, s3, s4, s5, s6, s7, s8, filter, offset_vec, round_bits, vl); + vuint16m1_t d2 = highbd_convolve8_8_rvv( + s2, s3, s4, s5, s6, s7, s8, s9, filter, offset_vec, round_bits, vl); + vuint16m1_t d3 = + highbd_convolve8_8_rvv(s3, s4, s5, s6, s7, s8, s9, s10, filter, + offset_vec, round_bits, vl); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Shift window + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s5 = s9; + s6 = s10; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src_ptr += vl; + dst_ptr += vl; + w -= vl; + } while (w > 0); + } +} + +void av1_highbd_dist_wtd_convolve_y_rvv( + const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w, + int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn, + ConvolveParams *conv_params, int bd) { + DECLARE_ALIGNED(16, uint16_t, + im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * MAX_SB_SIZE]); + CONV_BUF_TYPE *dst16 = conv_params->dst; + const int y_filter_taps = get_filter_tap(filter_params_y, subpel_y_qn); + int dst16_stride = conv_params->dst_stride; + const int im_stride = MAX_SB_SIZE; + const int vert_offset = filter_params_y->taps / 2 - 1; + assert(FILTER_BITS == COMPOUND_ROUND1_BITS); + const int round_offset_conv = (1 << (conv_params->round_0 - 1)) + + (1 << (bd + FILTER_BITS)) + + (1 << (bd + FILTER_BITS - 1)); + + const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_y, subpel_y_qn & SUBPEL_MASK); + + src -= vert_offset * src_stride; + int round_bits, shift_bits, offset_bits; + + if (bd == 12) { + round_bits = ROUND0_BITS + 2; + shift_bits = ROUND_SHIFT - 2; + offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS - 2; + } else { + round_bits = ROUND0_BITS; + shift_bits = ROUND_SHIFT; + offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + } + + if (conv_params->do_average) { + if (y_filter_taps <= 4) { + highbd_dist_wtd_convolve_y_4tap_rvv( + src + 2 * src_stride, src_stride, im_block, im_stride, w, h, + y_filter_ptr, round_offset_conv, round_bits); + } else if (y_filter_taps == 6) { + highbd_dist_wtd_convolve_y_6tap_rvv( + src + src_stride, src_stride, im_block, im_stride, w, h, y_filter_ptr, + round_offset_conv, round_bits); + } else { + highbd_dist_wtd_convolve_y_8tap_rvv(src, src_stride, im_block, im_stride, + w, h, y_filter_ptr, round_offset_conv, + round_bits); + } + if (conv_params->use_dist_wtd_comp_avg) { + highbd_dist_wtd_comp_avg_rvv(im_block, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } else { + highbd_comp_avg_rvv(im_block, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } + } else { + if (y_filter_taps <= 4) { + highbd_dist_wtd_convolve_y_4tap_rvv( + src + 2 * src_stride, src_stride, dst16, dst16_stride, w, h, + y_filter_ptr, round_offset_conv, round_bits); + } else if (y_filter_taps == 6) { + highbd_dist_wtd_convolve_y_6tap_rvv(src + src_stride, src_stride, dst16, + dst16_stride, w, h, y_filter_ptr, + round_offset_conv, round_bits); + } else { + highbd_dist_wtd_convolve_y_8tap_rvv(src, src_stride, dst16, dst16_stride, + w, h, y_filter_ptr, round_offset_conv, + round_bits); + } + } +} + +static inline void highbd_2d_copy_rvv(const uint16_t *src_ptr, int src_stride, + uint16_t *dst_ptr, int dst_stride, int w, + int h, int round_bits, const int offset) { + assert(h % 4 == 0); + + const uint16_t round_val = (1 << round_bits); + int height = h; + + if (w <= 4) { + const size_t vl = __riscv_vsetvl_e16mf2(w); + vuint16mf2_t vec_offset = __riscv_vmv_v_x_u16mf2(offset, vl); + + do { + vuint16mf2_t s0, s1, s2, s3; + load_u16_4x4(src_ptr, src_stride, &s0, &s1, &s2, &s3, vl); + + vuint16mf2_t d0 = __riscv_vmacc_vx_u16mf2(vec_offset, round_val, s0, vl); + vuint16mf2_t d1 = __riscv_vmacc_vx_u16mf2(vec_offset, round_val, s1, vl); + vuint16mf2_t d2 = __riscv_vmacc_vx_u16mf2(vec_offset, round_val, s2, vl); + vuint16mf2_t d3 = __riscv_vmacc_vx_u16mf2(vec_offset, round_val, s3, vl); + + store_u16_4x4(dst_ptr, dst_stride, d0, d1, d2, d3, vl); + + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + height -= 4; + } while (height > 0); + } else { + size_t vl_max = __riscv_vsetvlmax_e16m1(); + vuint16m1_t vec_offset = __riscv_vmv_v_x_u16m1(offset, vl_max); + + do { + const uint16_t *s = src_ptr; + uint16_t *d = dst_ptr; + int width = w; + + do { + size_t vl = __riscv_vsetvl_e16m1(width); + vuint16m1_t s0, s1, s2, s3; + load_u16_8x4(s, src_stride, &s0, &s1, &s2, &s3, vl); + + // apply shift and add round offset + vuint16m1_t d0 = __riscv_vmacc_vx_u16m1(vec_offset, round_val, s0, vl); + vuint16m1_t d1 = __riscv_vmacc_vx_u16m1(vec_offset, round_val, s1, vl); + vuint16m1_t d2 = __riscv_vmacc_vx_u16m1(vec_offset, round_val, s2, vl); + vuint16m1_t d3 = __riscv_vmacc_vx_u16m1(vec_offset, round_val, s3, vl); + + // store results + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + s += vl; + d += vl; + width -= vl; + } while (width > 0); + src_ptr += 4 * src_stride; + dst_ptr += 4 * dst_stride; + height -= 4; + } while (height > 0); + } +} + +void av1_highbd_dist_wtd_convolve_2d_copy_rvv(const uint16_t *src, + int src_stride, uint16_t *dst, + int dst_stride, int w, int h, + ConvolveParams *conv_params, + int bd) { + DECLARE_ALIGNED(16, uint16_t, + im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * MAX_SB_SIZE]); + + const int im_stride = MAX_SB_SIZE; + CONV_BUF_TYPE *dst16 = conv_params->dst; + int dst16_stride = conv_params->dst_stride; + const int offset_bits = bd + 2 * FILTER_BITS - conv_params->round_0; + const int round_offset = (1 << (offset_bits - conv_params->round_1)) + + (1 << (offset_bits - conv_params->round_1 - 1)); + const int round_bits = + 2 * FILTER_BITS - conv_params->round_0 - conv_params->round_1; + assert(round_bits >= 0); + + if (conv_params->do_average) { + highbd_2d_copy_rvv(src, src_stride, im_block, im_stride, w, h, round_bits, + round_offset); + } else { + highbd_2d_copy_rvv(src, src_stride, dst16, dst16_stride, w, h, round_bits, + round_offset); + } + + if (conv_params->do_average) { + int shift_bits; + if (bd == 12) { + shift_bits = ROUND_SHIFT - 2; + } else { + shift_bits = ROUND_SHIFT; + } + if (conv_params->use_dist_wtd_comp_avg) { + highbd_dist_wtd_comp_avg_rvv(im_block, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } else { + highbd_comp_avg_rvv(im_block, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } + } +} + +static inline vuint16mf2_t highbd_convolve6_4_2d_v_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const int16_t *y_filter, const vint32m1_t offset, size_t vl) { + // Values at indices 0 and 7 of y_filter are zero. + // Accumulate widening multiplications into the offset vector. + vint32m1_t sum = __riscv_vwmacc_vx_i32m1(offset, y_filter[1], s0, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[2], s1, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[3], s2, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[4], s3, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[5], s4, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[6], s5, vl); + + vuint32m1_t sum_u32 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16mf2(sum_u32, COMPOUND_ROUND1_BITS, + __RISCV_VXRM_RNU, vl); +#else + return __riscv_vnclipu_wx_u16mf2(sum_u32, COMPOUND_ROUND1_BITS, vl); +#endif +} + +static inline vuint16m1_t highbd_convolve6_8_2d_v_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const int16_t *y_filter, const vint32m2_t offset, size_t vl) { + // Values at indices 0 and 7 of y_filter are zero. + // Perform widening multiply-accumulate starting with the offset vector. + vint32m2_t sum = __riscv_vwmacc_vx_i32m2(offset, y_filter[1], s0, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[2], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[3], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[4], s3, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[5], s4, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[6], s5, vl); + + vuint32m2_t sum_u32 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16m1(sum_u32, COMPOUND_ROUND1_BITS, + __RISCV_VXRM_RNU, vl); +#else + return __riscv_vnclipu_wx_u16m1(sum_u32, COMPOUND_ROUND1_BITS, vl); +#endif +} + +static inline void highbd_dist_wtd_convolve_2d_vert_6tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *y_filter_ptr, int offset) { + size_t vl = __riscv_vsetvl_e16m1(w); + if (w == 4) { + // Broadcast offset to a 32-bit vector + vint32m1_t offset_vec = __riscv_vmv_v_x_i32m1(offset, vl); + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + // Load initial 5 rows + vint16mf2_t s0, s1, s2, s3, s4; + load_s16_4x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + vint16mf2_t s5, s6, s7, s8; + load_s16_4x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + // Perform 6-tap convolution for 4 rows + vuint16mf2_t d0 = highbd_convolve6_4_2d_v_rvv( + s0, s1, s2, s3, s4, s5, y_filter_ptr, offset_vec, vl); + vuint16mf2_t d1 = highbd_convolve6_4_2d_v_rvv( + s1, s2, s3, s4, s5, s6, y_filter_ptr, offset_vec, vl); + vuint16mf2_t d2 = highbd_convolve6_4_2d_v_rvv( + s2, s3, s4, s5, s6, s7, y_filter_ptr, offset_vec, vl); + vuint16mf2_t d3 = highbd_convolve6_4_2d_v_rvv( + s3, s4, s5, s6, s7, s8, y_filter_ptr, offset_vec, vl); + + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Update sliding window state + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + // w > 4 case + do { + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + int height = h; + + vint16m1_t s0, s1, s2, s3, s4; + load_s16_8x5(s, src_stride, &s0, &s1, &s2, &s3, &s4, vl); + s += 5 * src_stride; + + do { + vint16m1_t s5, s6, s7, s8; + load_s16_8x4(s, src_stride, &s5, &s6, &s7, &s8, vl); + + vuint16m1_t d0 = highbd_convolve6_8_2d_v_rvv( + s0, s1, s2, s3, s4, s5, y_filter_ptr, offset_vec, vl); + vuint16m1_t d1 = highbd_convolve6_8_2d_v_rvv( + s1, s2, s3, s4, s5, s6, y_filter_ptr, offset_vec, vl); + vuint16m1_t d2 = highbd_convolve6_8_2d_v_rvv( + s2, s3, s4, s5, s6, s7, y_filter_ptr, offset_vec, vl); + vuint16m1_t d3 = highbd_convolve6_8_2d_v_rvv( + s3, s4, s5, s6, s7, s8, y_filter_ptr, offset_vec, vl); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + + src_ptr += vl; + dst_ptr += vl; + w -= (int)vl; + } while (w > 0); + } +} + +static inline vuint16mf2_t highbd_convolve8_4_2d_v_rvv( + const vint16mf2_t s0, const vint16mf2_t s1, const vint16mf2_t s2, + const vint16mf2_t s3, const vint16mf2_t s4, const vint16mf2_t s5, + const vint16mf2_t s6, const vint16mf2_t s7, const int16_t *y_filter, + const vint32m1_t offset, size_t vl) { + // Perform widening multiply-accumulate for all 8 taps. + // Accumulate directly into the offset vector. + vint32m1_t sum = __riscv_vwmacc_vx_i32m1(offset, y_filter[0], s0, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[3], s3, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[4], s4, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[5], s5, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[6], s6, vl); + sum = __riscv_vwmacc_vx_i32m1(sum, y_filter[7], s7, vl); + + // NEON vqrshrun_n_s32 -> RVV vnclipu_wx (with default RNU rounding) + vuint32m1_t sum_u32 = + __riscv_vreinterpret_v_i32m1_u32m1(__riscv_vmax_vx_i32m1(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16mf2(sum_u32, COMPOUND_ROUND1_BITS, + __RISCV_VXRM_RNU, vl); +#else + return __riscv_vnclipu_wx_u16mf2(sum_u32, COMPOUND_ROUND1_BITS, vl); +#endif +} + +static inline vuint16m1_t highbd_convolve8_8_2d_v_rvv( + const vint16m1_t s0, const vint16m1_t s1, const vint16m1_t s2, + const vint16m1_t s3, const vint16m1_t s4, const vint16m1_t s5, + const vint16m1_t s6, const vint16m1_t s7, const int16_t *y_filter, + const vint32m2_t offset, size_t vl) { + // Perform widening multiply-accumulate for all 8 taps. + vint32m2_t sum = __riscv_vwmacc_vx_i32m2(offset, y_filter[0], s0, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[1], s1, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[2], s2, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[3], s3, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[4], s4, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[5], s5, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[6], s6, vl); + sum = __riscv_vwmacc_vx_i32m2(sum, y_filter[7], s7, vl); + + // NEON vqrshrun_n_s32 -> RVV vnclipu_wx (with default RNU rounding) + vuint32m2_t sum_u32 = + __riscv_vreinterpret_v_i32m2_u32m2(__riscv_vmax_vx_i32m2(sum, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + return __riscv_vnclipu_wx_u16m1(sum_u32, COMPOUND_ROUND1_BITS, + __RISCV_VXRM_RNU, vl); +#else + return __riscv_vnclipu_wx_u16m1(sum_u32, COMPOUND_ROUND1_BITS, vl); +#endif +} + +static inline void highbd_dist_wtd_convolve_2d_vert_8tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *y_filter_ptr, int offset) { + size_t vl = __riscv_vsetvl_e16m1(w); + if (w == 4) { + vint32m1_t offset_vec = __riscv_vmv_v_x_i32m1(offset, vl); + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + + // Load initial 7 rows for 8-tap filter + vint16mf2_t s0, s1, s2, s3, s4, s5, s6; + load_s16_4x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + vint16mf2_t s7, s8, s9, s10; + load_s16_4x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + // Perform 8-tap convolution for 4 rows + vuint16mf2_t d0 = highbd_convolve8_4_2d_v_rvv( + s0, s1, s2, s3, s4, s5, s6, s7, y_filter_ptr, offset_vec, vl); + vuint16mf2_t d1 = highbd_convolve8_4_2d_v_rvv( + s1, s2, s3, s4, s5, s6, s7, s8, y_filter_ptr, offset_vec, vl); + vuint16mf2_t d2 = highbd_convolve8_4_2d_v_rvv( + s2, s3, s4, s5, s6, s7, s8, s9, y_filter_ptr, offset_vec, vl); + vuint16mf2_t d3 = highbd_convolve8_4_2d_v_rvv( + s3, s4, s5, s6, s7, s8, s9, s10, y_filter_ptr, offset_vec, vl); + + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + // Update sliding window state + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s5 = s9; + s6 = s10; + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } while (h != 0); + } else { + // w > 4 case + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + do { + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + int height = h; + + vint16m1_t s0, s1, s2, s3, s4, s5, s6; + load_s16_8x7(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, vl); + s += 7 * src_stride; + + do { + vint16m1_t s7, s8, s9, s10; + load_s16_8x4(s, src_stride, &s7, &s8, &s9, &s10, vl); + + vuint16m1_t d0 = highbd_convolve8_8_2d_v_rvv( + s0, s1, s2, s3, s4, s5, s6, s7, y_filter_ptr, offset_vec, vl); + vuint16m1_t d1 = highbd_convolve8_8_2d_v_rvv( + s1, s2, s3, s4, s5, s6, s7, s8, y_filter_ptr, offset_vec, vl); + vuint16m1_t d2 = highbd_convolve8_8_2d_v_rvv( + s2, s3, s4, s5, s6, s7, s8, s9, y_filter_ptr, offset_vec, vl); + vuint16m1_t d3 = highbd_convolve8_8_2d_v_rvv( + s3, s4, s5, s6, s7, s8, s9, s10, y_filter_ptr, offset_vec, vl); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s5 = s9; + s6 = s10; + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + + src_ptr += vl; + dst_ptr += vl; + w -= (int)vl; + } while (w > 0); + } +} + +static inline void highbd_dist_wtd_convolve_2d_horiz_6tap_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *x_filter_ptr, const int offset, + const int32_t round_bits) { + const int16_t *filter = x_filter_ptr; + + size_t vl = __riscv_vsetvl_e16m1(w); + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + do { + const int16_t *s = (const int16_t *)src_ptr; + uint16_t *d = dst_ptr; + int height = h; + + // Process 4 rows at a time to reduce loop overhead + while (height >= 4) { + vint16m1_t s00, s01, s02, s03, s04, s05; + vint16m1_t s10, s11, s12, s13, s14, s15; + vint16m1_t s20, s21, s22, s23, s24, s25; + vint16m1_t s30, s31, s32, s33, s34, s35; + + // Load 6 vectors per row with stride 1 (horizontal overlap) + load_s16_8x6(s + 0 * src_stride, 1, &s00, &s01, &s02, &s03, &s04, &s05, + vl); + load_s16_8x6(s + 1 * src_stride, 1, &s10, &s11, &s12, &s13, &s14, &s15, + vl); + load_s16_8x6(s + 2 * src_stride, 1, &s20, &s21, &s22, &s23, &s24, &s25, + vl); + load_s16_8x6(s + 3 * src_stride, 1, &s30, &s31, &s32, &s33, &s34, &s35, + vl); + + vuint16m1_t d0 = highbd_convolve6_8_rvv( + s00, s01, s02, s03, s04, s05, filter, offset_vec, round_bits, vl); + vuint16m1_t d1 = highbd_convolve6_8_rvv( + s10, s11, s12, s13, s14, s15, filter, offset_vec, round_bits, vl); + vuint16m1_t d2 = highbd_convolve6_8_rvv( + s20, s21, s22, s23, s24, s25, filter, offset_vec, round_bits, vl); + vuint16m1_t d3 = highbd_convolve6_8_rvv( + s30, s31, s32, s33, s34, s35, filter, offset_vec, round_bits, vl); + + store_u16_8x4(d, dst_stride, d0, d1, d2, d3, vl); + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } + + // Handle remaining rows + while (height > 0) { + vint16m1_t s00, s01, s02, s03, s04, s05; + load_s16_8x6(s, 1, &s00, &s01, &s02, &s03, &s04, &s05, vl); + vuint16m1_t d0 = highbd_convolve6_8_rvv( + s00, s01, s02, s03, s04, s05, filter, offset_vec, round_bits, vl); + __riscv_vse16_v_u16m1(d, d0, vl); + + s += src_stride; + d += dst_stride; + height--; + } + + src_ptr += vl; + dst_ptr += vl; + w -= (int)vl; + } while (w > 0); +} + +static inline void highbd_dist_wtd_convolve_2d_horiz_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, const int16_t *x_filter_ptr, const int offset, + const int32_t round_bits) { + assert(h >= 5); + + if (w == 4) { + size_t vl = __riscv_vsetvl_e16mf2(4); + vint32m1_t offset_vec = __riscv_vmv_v_x_i32m1(offset, vl); + const int16_t *filter = x_filter_ptr + 2; // Use middle 4 taps + const int16_t *s = (const int16_t *)(src_ptr + 1); + uint16_t *d = dst_ptr; + + while (h >= 4) { + vint16mf2_t s00, s01, s02, s03; + vint16mf2_t s10, s11, s12, s13; + vint16mf2_t s20, s21, s22, s23; + vint16mf2_t s30, s31, s32, s33; + + load_s16_4x4(s + 0 * src_stride, 1, &s00, &s01, &s02, &s03, vl); + load_s16_4x4(s + 1 * src_stride, 1, &s10, &s11, &s12, &s13, vl); + load_s16_4x4(s + 2 * src_stride, 1, &s20, &s21, &s22, &s23, vl); + load_s16_4x4(s + 3 * src_stride, 1, &s30, &s31, &s32, &s33, vl); + + vuint16mf2_t d0 = highbd_convolve4_4_x_rvv(s00, s01, s02, s03, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d1 = highbd_convolve4_4_x_rvv(s10, s11, s12, s13, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d2 = highbd_convolve4_4_x_rvv(s20, s21, s22, s23, filter, + offset_vec, round_bits, vl); + vuint16mf2_t d3 = highbd_convolve4_4_x_rvv(s30, s31, s32, s33, filter, + offset_vec, round_bits, vl); + + store_u16_4x4(d, dst_stride, d0, d1, d2, d3, vl); + + s += 4 * src_stride; + d += 4 * dst_stride; + h -= 4; + } + + while (h > 0) { + vint16mf2_t s00, s01, s02, s03; + load_s16_4x4(s, 1, &s00, &s01, &s02, &s03, vl); + vuint16mf2_t d0 = highbd_convolve4_4_x_rvv(s00, s01, s02, s03, filter, + offset_vec, round_bits, vl); + __riscv_vse16_v_u16mf2(d, d0, vl); + s += src_stride; + d += dst_stride; + h--; + } + } else { + // w > 4 case using 8-tap filter + const int16_t *filter = x_filter_ptr; + size_t vl = __riscv_vsetvl_e16m1(w); + vint32m2_t offset_vec = __riscv_vmv_v_x_i32m2(offset, vl); + do { + const int16_t *s_base = (const int16_t *)src_ptr; + uint16_t *d_base = dst_ptr; + int height = h; + + while (height >= 4) { + vint16m1_t s00, s01, s02, s03, s04, s05, s06, s07; + vint16m1_t s10, s11, s12, s13, s14, s15, s16, s17; + vint16m1_t s20, s21, s22, s23, s24, s25, s26, s27; + vint16m1_t s30, s31, s32, s33, s34, s35, s36, s37; + + load_s16_8x8(s_base + 0 * src_stride, 1, &s00, &s01, &s02, &s03, &s04, + &s05, &s06, &s07, vl); + load_s16_8x8(s_base + 1 * src_stride, 1, &s10, &s11, &s12, &s13, &s14, + &s15, &s16, &s17, vl); + load_s16_8x8(s_base + 2 * src_stride, 1, &s20, &s21, &s22, &s23, &s24, + &s25, &s26, &s27, vl); + load_s16_8x8(s_base + 3 * src_stride, 1, &s30, &s31, &s32, &s33, &s34, + &s35, &s36, &s37, vl); + + vuint16m1_t d0 = + highbd_convolve8_8_rvv(s00, s01, s02, s03, s04, s05, s06, s07, + filter, offset_vec, round_bits, vl); + vuint16m1_t d1 = + highbd_convolve8_8_rvv(s10, s11, s12, s13, s14, s15, s16, s17, + filter, offset_vec, round_bits, vl); + vuint16m1_t d2 = + highbd_convolve8_8_rvv(s20, s21, s22, s23, s24, s25, s26, s27, + filter, offset_vec, round_bits, vl); + vuint16m1_t d3 = + highbd_convolve8_8_rvv(s30, s31, s32, s33, s34, s35, s36, s37, + filter, offset_vec, round_bits, vl); + + store_u16_8x4(d_base, dst_stride, d0, d1, d2, d3, vl); + + s_base += 4 * src_stride; + d_base += 4 * dst_stride; + height -= 4; + } + + while (height > 0) { + vint16m1_t s00, s01, s02, s03, s04, s05, s06, s07; + load_s16_8x8(s_base, 1, &s00, &s01, &s02, &s03, &s04, &s05, &s06, &s07, + vl); + vuint16m1_t d0 = + highbd_convolve8_8_rvv(s00, s01, s02, s03, s04, s05, s06, s07, + filter, offset_vec, round_bits, vl); + __riscv_vse16_v_u16m1(d_base, d0, vl); + s_base += src_stride; + d_base += dst_stride; + height--; + } + + src_ptr += vl; + dst_ptr += vl; + w -= (int)vl; + } while (w > 0); + } +} + +void av1_highbd_dist_wtd_convolve_2d_rvv( + const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w, + int h, const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, const int subpel_x_qn, + const int subpel_y_qn, ConvolveParams *conv_params, int bd) { + DECLARE_ALIGNED(16, uint16_t, + im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * MAX_SB_SIZE]); + DECLARE_ALIGNED(16, uint16_t, + im_block2[(MAX_SB_SIZE + MAX_FILTER_TAP) * MAX_SB_SIZE]); + + CONV_BUF_TYPE *dst16 = conv_params->dst; + int dst16_stride = conv_params->dst_stride; + const int x_filter_taps = get_filter_tap(filter_params_x, subpel_x_qn); + const int clamped_x_taps = x_filter_taps < 6 ? 6 : x_filter_taps; + const int y_filter_taps = get_filter_tap(filter_params_y, subpel_y_qn); + const int clamped_y_taps = y_filter_taps < 6 ? 6 : y_filter_taps; + + const int im_h = h + clamped_y_taps - 1; + const int im_stride = MAX_SB_SIZE; + const int vert_offset = clamped_y_taps / 2 - 1; + const int horiz_offset = clamped_x_taps / 2 - 1; + + const int round_offset_conv_x = + (1 << (bd + FILTER_BITS - 1)) + (1 << (conv_params->round_0 - 1)); + const int y_offset_bits = bd + 2 * FILTER_BITS - conv_params->round_0; + const int round_offset_conv_y = (1 << y_offset_bits); + + const uint16_t *src_ptr = src - vert_offset * src_stride - horiz_offset; + + const int16_t *x_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_x, subpel_x_qn & SUBPEL_MASK); + const int16_t *y_filter_ptr = av1_get_interp_filter_subpel_kernel( + filter_params_y, subpel_y_qn & SUBPEL_MASK); + + // 1. Horizontal Filter Pass + int round_bits; + if (bd == 12) { + round_bits = ROUND0_BITS + 2; + } else { + round_bits = ROUND0_BITS; + } + if (x_filter_taps <= 6 && w != 4) { + highbd_dist_wtd_convolve_2d_horiz_6tap_rvv(src_ptr, src_stride, im_block, + im_stride, w, im_h, x_filter_ptr, + round_offset_conv_x, round_bits); + } else { + highbd_dist_wtd_convolve_2d_horiz_rvv(src_ptr, src_stride, im_block, + im_stride, w, im_h, x_filter_ptr, + round_offset_conv_x, round_bits); + } + + // 2. Vertical Filter Pass + if (y_filter_taps <= 6) { + if (conv_params->do_average) { + highbd_dist_wtd_convolve_2d_vert_6tap_rvv(im_block, im_stride, im_block2, + im_stride, w, h, y_filter_ptr, + round_offset_conv_y); + } else { + highbd_dist_wtd_convolve_2d_vert_6tap_rvv( + im_block, im_stride, dst16, dst16_stride, w, h, y_filter_ptr, + round_offset_conv_y); + } + } else { + if (conv_params->do_average) { + highbd_dist_wtd_convolve_2d_vert_8tap_rvv(im_block, im_stride, im_block2, + im_stride, w, h, y_filter_ptr, + round_offset_conv_y); + } else { + highbd_dist_wtd_convolve_2d_vert_8tap_rvv( + im_block, im_stride, dst16, dst16_stride, w, h, y_filter_ptr, + round_offset_conv_y); + } + } + + // 3. Compound Averaging + if (conv_params->do_average) { + int shift_bits, offset_bits; + if (bd == 12) { + shift_bits = ROUND_SHIFT - 2; + offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS - 2; + } else { + shift_bits = ROUND_SHIFT; + offset_bits = bd + 2 * FILTER_BITS - ROUND0_BITS; + } + if (conv_params->use_dist_wtd_comp_avg) { + highbd_dist_wtd_comp_avg_rvv(im_block2, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } else { + highbd_comp_avg_rvv(im_block2, im_stride, dst, dst_stride, w, h, + conv_params, bd, shift_bits, offset_bits); + } + } +}
diff --git a/av1/common/riscv/highbd_compound_convolve_rvv.h b/av1/common/riscv/highbd_compound_convolve_rvv.h new file mode 100644 index 0000000..67ae66f --- /dev/null +++ b/av1/common/riscv/highbd_compound_convolve_rvv.h
@@ -0,0 +1,190 @@ +/* + * Copyright (c) 2026, 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. + */ + +#ifndef AOM_AV1_COMMON_RISCV_HIGHBD_COMPOUND_CONVOLVE_RVV_H_ +#define AOM_AV1_COMMON_RISCV_HIGHBD_COMPOUND_CONVOLVE_RVV_H_ + +#include <riscv_vector.h> +#include <assert.h> + +#include "config/aom_config.h" +#include "config/av1_rtcd.h" +#include "aom_dsp/riscv/mem_rvv.h" +#include "aom_ports/mem.h" + +#define ROUND_SHIFT 2 * FILTER_BITS - ROUND0_BITS - COMPOUND_ROUND1_BITS + +static inline void highbd_comp_avg_rvv(const uint16_t *src_ptr, int src_stride, + uint16_t *dst_ptr, int dst_stride, int w, + int h, ConvolveParams *conv_params, + const int bd, const int shift_bits, + const int offset_bits) { + const uint16_t offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint16_t max_val = (1 << bd) - 1; + const int32_t round_shift_val = 1 << (shift_bits - 1); + + CONV_BUF_TYPE *ref_ptr = conv_params->dst; + const int ref_stride = conv_params->dst_stride; + const size_t vl = __riscv_vsetvl_e16m1(w); + + if (w == 4) { + do { + vuint16m1_t src = __riscv_vle16_v_u16m1(src_ptr, vl); + vuint16m1_t ref = __riscv_vle16_v_u16m1(ref_ptr, vl); + +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t avg = __riscv_vaaddu_vv_u16m1(src, ref, __RISCV_VXRM_RDN, vl); +#else + vuint16m1_t avg = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(src, ref, vl), 1, vl); +#endif + vint32m2_t diff = __riscv_vreinterpret_v_u32m2_i32m2( + __riscv_vwsubu_vx_u32m2(avg, offset, vl)); + diff = __riscv_vadd_vx_i32m2(diff, round_shift_val, vl); + vint16m1_t d0 = __riscv_vnsra_wx_i16m1(diff, shift_bits, vl); + vuint16m1_t d0_u16 = + __riscv_vreinterpret_v_i16m1_u16m1(__riscv_vmax_vx_i16m1(d0, 0, vl)); + d0_u16 = __riscv_vminu_vx_u16m1(d0_u16, max_val, vl); + + __riscv_vse16_v_u16m1(dst_ptr, d0_u16, vl); + + src_ptr += src_stride; + ref_ptr += ref_stride; + dst_ptr += dst_stride; + } while (--h != 0); + } else { + do { + int width = w; + const uint16_t *src_line = src_ptr; + const uint16_t *ref_line = (const uint16_t *)ref_ptr; + uint16_t *dst_line = dst_ptr; + + while (width > 0) { + vuint16m1_t s = __riscv_vle16_v_u16m1(src_line, vl); + vuint16m1_t r = __riscv_vle16_v_u16m1(ref_line, vl); + +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t avg = __riscv_vaaddu_vv_u16m1(s, r, __RISCV_VXRM_RDN, vl); +#else + vuint16m1_t avg = + __riscv_vnsrl_wx_u16m1(__riscv_vwaddu_vv_u32m2(s, r, vl), 1, vl); +#endif + vint32m2_t diff = __riscv_vreinterpret_v_u32m2_i32m2( + __riscv_vwsubu_vx_u32m2(avg, offset, vl)); + diff = __riscv_vadd_vx_i32m2(diff, round_shift_val, vl); + vint16m1_t d0 = __riscv_vnsra_wx_i16m1(diff, shift_bits, vl); + vuint16m1_t d0_u16 = __riscv_vreinterpret_v_i16m1_u16m1( + __riscv_vmax_vx_i16m1(d0, 0, vl)); + d0_u16 = __riscv_vminu_vx_u16m1(d0_u16, max_val, vl); + + __riscv_vse16_v_u16m1(dst_line, d0_u16, vl); + + src_line += vl; + ref_line += vl; + dst_line += vl; + width -= vl; + } + + src_ptr += src_stride; + ref_ptr += ref_stride; + dst_ptr += dst_stride; + } while (--h != 0); + } +} + +static inline void highbd_dist_wtd_comp_avg_rvv( + const uint16_t *src_ptr, int src_stride, uint16_t *dst_ptr, int dst_stride, + int w, int h, ConvolveParams *conv_params, const int bd, + const int shift_bits, const int offset_bits) { + const int offset = (1 << (offset_bits - COMPOUND_ROUND1_BITS)) + + (1 << (offset_bits - COMPOUND_ROUND1_BITS - 1)); + const uint16_t max_val = (1 << bd) - 1; + const uint16_t fwd = conv_params->fwd_offset; + const uint16_t bck = conv_params->bck_offset; + + CONV_BUF_TYPE *ref_ptr = conv_params->dst; + const int ref_stride = conv_params->dst_stride; + + const size_t vl = __riscv_vsetvl_e16m1(w); + vuint32m2_t offset_vec = __riscv_vmv_v_x_u32m2((uint32_t)offset, vl); + if (w == 4) { + do { + vuint16m1_t src = __riscv_vle16_v_u16m1(src_ptr, vl); + vuint16m1_t ref = __riscv_vle16_v_u16m1(ref_ptr, vl); + + vuint32m2_t wtd = __riscv_vwmulu_vx_u32m2(ref, fwd, vl); + wtd = __riscv_vwmaccu_vx_u32m2(wtd, bck, src, vl); + wtd = __riscv_vsrl_vx_u32m2(wtd, DIST_PRECISION_BITS, vl); + + vint32m2_t diff = __riscv_vreinterpret_v_u32m2_i32m2( + __riscv_vsub_vv_u32m2(wtd, offset_vec, vl)); + + vuint32m2_t d0 = __riscv_vreinterpret_v_i32m2_u32m2( + __riscv_vmax_vx_i32m2(diff, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t d0_u16 = + __riscv_vnclipu_wx_u16m1(d0, shift_bits, __RISCV_VXRM_RNU, vl); +#else + vuint16m1_t d0_u16 = __riscv_vnclipu_wx_u16m1(d0, shift_bits, vl); +#endif + d0_u16 = __riscv_vminu_vx_u16m1(d0_u16, max_val, vl); + + __riscv_vse16_v_u16m1(dst_ptr, d0_u16, vl); + + src_ptr += src_stride; + dst_ptr += dst_stride; + ref_ptr += ref_stride; + } while (--h != 0); + } else { + do { + int width = w; + const uint16_t *src_line = src_ptr; + const uint16_t *ref_line = (const uint16_t *)ref_ptr; + uint16_t *dst_line = dst_ptr; + + while (width > 0) { + vuint16m1_t s = __riscv_vle16_v_u16m1(src_line, vl); + vuint16m1_t r = __riscv_vle16_v_u16m1(ref_line, vl); + + vuint32m2_t wtd = __riscv_vwmulu_vx_u32m2(r, fwd, vl); + wtd = __riscv_vwmaccu_vx_u32m2(wtd, bck, s, vl); + wtd = __riscv_vsrl_vx_u32m2(wtd, DIST_PRECISION_BITS, vl); + + vint32m2_t diff = __riscv_vreinterpret_v_u32m2_i32m2( + __riscv_vsub_vv_u32m2(wtd, offset_vec, vl)); + + vuint32m2_t d0 = __riscv_vreinterpret_v_i32m2_u32m2( + __riscv_vmax_vx_i32m2(diff, 0, vl)); +#if __riscv_v_intrinsic >= 12000 + vuint16m1_t d0_u16 = + __riscv_vnclipu_wx_u16m1(d0, shift_bits, __RISCV_VXRM_RNU, vl); +#else + vuint16m1_t d0_u16 = __riscv_vnclipu_wx_u16m1(d0, shift_bits, vl); +#endif + d0_u16 = __riscv_vminu_vx_u16m1(d0_u16, max_val, vl); + + __riscv_vse16_v_u16m1(dst_line, d0_u16, vl); + + src_line += vl; + ref_line += vl; + dst_line += vl; + width -= vl; + } + + src_ptr += src_stride; + dst_ptr += dst_stride; + ref_ptr += ref_stride; + } while (--h != 0); + } +} + +#endif // AOM_AV1_COMMON_RISCV_HIGHBD_COMPOUND_CONVOLVE_RVV_H_
diff --git a/av1/common/seg_common.h b/av1/common/seg_common.h index 3ba5052..3b88893 100644 --- a/av1/common/seg_common.h +++ b/av1/common/seg_common.h
@@ -12,6 +12,8 @@ #ifndef AOM_AV1_COMMON_SEG_COMMON_H_ #define AOM_AV1_COMMON_SEG_COMMON_H_ +#include <string.h> + #include "aom_dsp/prob.h" #ifdef __cplusplus
diff --git a/av1/common/warp_plane_hwy.h b/av1/common/warp_plane_hwy.h new file mode 100644 index 0000000..61d5fc6 --- /dev/null +++ b/av1/common/warp_plane_hwy.h
@@ -0,0 +1,1487 @@ +/* + * Copyright (c) 2025, 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. + */ + +#ifndef AOM_AV1_COMMON_WARP_PLANE_HWY_H_ +#define AOM_AV1_COMMON_WARP_PLANE_HWY_H_ + +#include "av1/common/warped_motion.h" +#include "config/av1_rtcd.h" +#include "third_party/highway/hwy/highway.h" + +HWY_BEFORE_NAMESPACE(); + +namespace { +namespace HWY_NAMESPACE { + +namespace hn = hwy::HWY_NAMESPACE; + +constexpr hn::ScalableTag<uint8_t> uint8xN_tag; +constexpr hn::ScalableTag<uint16_t> uint16xN_tag; + +constexpr hn::ScalableTag<int16_t> int16xN_tag; +constexpr hn::ScalableTag<int32_t> int32xN_tag; +constexpr hn::ScalableTag<int64_t> int64xN_tag; + +constexpr hn::CappedTag<uint8_t, 32> uint8x32_tag; +constexpr hn::CappedTag<int16_t, 16> int16x16_tag; + +constexpr hn::FixedTag<uint8_t, 4> uint8x4_tag; +constexpr hn::FixedTag<uint8_t, 8> uint8x8_tag; +constexpr hn::FixedTag<uint8_t, 16> uint8x16_tag; +constexpr hn::FixedTag<uint16_t, 4> uint16x4_tag; +constexpr hn::FixedTag<uint16_t, 8> uint16x8_tag; + +constexpr hn::FixedTag<int8_t, 8> int8x8_tag; +constexpr hn::FixedTag<int8_t, 16> int8x16_tag; +constexpr hn::FixedTag<int16_t, 8> int16x8_tag; +constexpr hn::FixedTag<int32_t, 4> int32x4_tag; +constexpr hn::FixedTag<int64_t, 2> int64x2_tag; + +constexpr hn::ScalableTag<int8_t> coeff_tag; + +using IVec16 = hn::Vec<decltype(int16xN_tag)>; +using IVec32 = hn::Vec<decltype(int32xN_tag)>; +using IVec8x16 = hn::Vec<decltype(int8x16_tag)>; + +template <typename D> +HWY_ATTR inline void FilterPixelsHorizontal(D tag, const hn::VFromD<D> src, + int16_t *HWY_RESTRICT horz_out, + int8_t *HWY_RESTRICT coeff, + const IVec16 round_const, + const int shift, int row) { + constexpr hn::Repartition<int8_t, D> int8_tag; + constexpr hn::Repartition<int16_t, D> result_tag; + constexpr hn::Repartition<uint16_t, D> unsigned_result_tag; + // N.B. coeffs are stored to support the maximum vector width, which may not + // be the vector width being filtered on now. + const auto coeff0 = hn::Load(int8_tag, coeff + hn::MaxLanes(coeff_tag) * 0); + const auto coeff1 = hn::Load(int8_tag, coeff + hn::MaxLanes(coeff_tag) * 1); + const auto coeff2 = hn::Load(int8_tag, coeff + hn::MaxLanes(coeff_tag) * 2); + const auto coeff3 = hn::Load(int8_tag, coeff + hn::MaxLanes(coeff_tag) * 3); + + const auto shuffle0 = hn::Dup128VecFromValues( + uint8xN_tag, 0, 2, 2, 4, 4, 6, 6, 8, 1, 3, 3, 5, 5, 7, 7, 9 // + ); + const auto shuffle1 = hn::Dup128VecFromValues( + uint8xN_tag, 4, 6, 6, 8, 8, 10, 10, 12, 5, 7, 7, 9, 9, 11, 11, 13 // + ); + const auto shuffle2 = hn::Dup128VecFromValues( + uint8xN_tag, 1, 3, 3, 5, 5, 7, 7, 9, 2, 4, 4, 6, 6, 8, 8, 10 // + ); + const auto shuffle3 = hn::Dup128VecFromValues( + uint8xN_tag, 5, 7, 7, 9, 9, 11, 11, 13, 6, 8, 8, 10, 10, 12, 12, 14 // + ); + + const auto src_0 = + hn::TableLookupBytes(src, hn::ResizeBitCast(tag, shuffle0)); + const auto src_1 = + hn::TableLookupBytes(src, hn::ResizeBitCast(tag, shuffle1)); + const auto src_2 = + hn::TableLookupBytes(src, hn::ResizeBitCast(tag, shuffle2)); + const auto src_3 = + hn::TableLookupBytes(src, hn::ResizeBitCast(tag, shuffle3)); + + const auto res_02 = hn::SatWidenMulPairwiseAdd(result_tag, src_0, coeff0); + const auto res_46 = hn::SatWidenMulPairwiseAdd(result_tag, src_1, coeff1); + const auto res_13 = hn::SatWidenMulPairwiseAdd(result_tag, src_2, coeff2); + const auto res_57 = hn::SatWidenMulPairwiseAdd(result_tag, src_3, coeff3); + + const auto res_even = hn::Add(res_02, res_46); + const auto res_odd = hn::Add(res_13, res_57); + + const auto res = hn::Add(hn::Add(res_even, res_odd), + hn::ResizeBitCast(result_tag, round_const)); + + hn::Store(hn::BitCast(result_tag, + hn::ShiftRightSame( + hn::BitCast(unsigned_result_tag, res), shift)), + result_tag, horz_out + row * hn::MaxLanes(int16x8_tag)); +} + +HWY_ATTR HWY_INLINE IVec8x16 LoadAV1Filter8Bit(unsigned int offset) { + return hn::LoadN(int8x16_tag, av1_filter_8bit[offset >> WARPEDDIFF_PREC_BITS], + 8); +} + +template <typename D> +HWY_ATTR HWY_INLINE hn::VFromD<D> LoadAV1Filter8BitLower(D int8_tag, + unsigned int offset) { + return hn::LoadN(int8_tag, av1_filter_8bit[offset >> WARPEDDIFF_PREC_BITS], + 8); +} + +template <int Block, typename D> +HWY_ATTR HWY_INLINE hn::VFromD<D> LoadAV1Filter8BitUpper(D int8_tag, + unsigned int offset, + hn::VFromD<D> src) { + (void)int8_tag; + return hn::InsertBlock<Block>( + src, hn::LoadN(int8x16_tag, + av1_filter_8bit[offset >> WARPEDDIFF_PREC_BITS], 8)); +} + +template <typename D> +HWY_ATTR inline void PrepareHorizontalFilterCoefficients( + D int16_tag, int alpha, int beta, int sx, int8_t *HWY_RESTRICT coeff) { + constexpr auto int8_tag = hn::Repartition<int8_t, D>(); + constexpr auto int32_tag = hn::Repartition<int32_t, D>(); + constexpr auto int64_tag = hn::Repartition<int64_t, D>(); + + auto tmp_0 = LoadAV1Filter8BitLower(int8_tag, sx + 0 * alpha); + auto tmp_1 = LoadAV1Filter8BitLower(int8_tag, sx + 1 * alpha); + auto tmp_2 = LoadAV1Filter8BitLower(int8_tag, sx + 2 * alpha); + auto tmp_3 = LoadAV1Filter8BitLower(int8_tag, sx + 3 * alpha); + auto tmp_4 = LoadAV1Filter8BitLower(int8_tag, sx + 4 * alpha); + auto tmp_5 = LoadAV1Filter8BitLower(int8_tag, sx + 5 * alpha); + auto tmp_6 = LoadAV1Filter8BitLower(int8_tag, sx + 6 * alpha); + auto tmp_7 = LoadAV1Filter8BitLower(int8_tag, sx + 7 * alpha); + + if constexpr (int16_tag.MaxBlocks() >= 2) { + tmp_0 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 0 * alpha, tmp_0); + tmp_1 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 1 * alpha, tmp_1); + tmp_2 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 2 * alpha, tmp_2); + tmp_3 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 3 * alpha, tmp_3); + tmp_4 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 4 * alpha, tmp_4); + tmp_5 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 5 * alpha, tmp_5); + tmp_6 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 6 * alpha, tmp_6); + tmp_7 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta + 7 * alpha, tmp_7); + } + + if constexpr (int16_tag.MaxBlocks() >= 3) { + tmp_0 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 0 * alpha, tmp_0); + tmp_1 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 1 * alpha, tmp_1); + tmp_2 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 2 * alpha, tmp_2); + tmp_3 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 3 * alpha, tmp_3); + tmp_4 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 4 * alpha, tmp_4); + tmp_5 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 5 * alpha, tmp_5); + tmp_6 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 6 * alpha, tmp_6); + tmp_7 = + LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2 + 7 * alpha, tmp_7); + + tmp_0 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 0 * alpha, tmp_0); + tmp_1 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 1 * alpha, tmp_1); + tmp_2 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 2 * alpha, tmp_2); + tmp_3 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 3 * alpha, tmp_3); + tmp_4 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 4 * alpha, tmp_4); + tmp_5 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 5 * alpha, tmp_5); + tmp_6 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 6 * alpha, tmp_6); + tmp_7 = + LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3 + 7 * alpha, tmp_7); + } + + const auto tmp_0_16 = hn::BitCast(int16_tag, tmp_0); + const auto tmp_1_16 = hn::BitCast(int16_tag, tmp_1); + const auto tmp_2_16 = hn::BitCast(int16_tag, tmp_2); + const auto tmp_3_16 = hn::BitCast(int16_tag, tmp_3); + const auto tmp_4_16 = hn::BitCast(int16_tag, tmp_4); + const auto tmp_5_16 = hn::BitCast(int16_tag, tmp_5); + const auto tmp_6_16 = hn::BitCast(int16_tag, tmp_6); + const auto tmp_7_16 = hn::BitCast(int16_tag, tmp_7); + + const auto tmp_12 = hn::ZipLower(int32_tag, tmp_0_16, tmp_2_16); + const auto tmp_13 = hn::ZipLower(int32_tag, tmp_1_16, tmp_3_16); + const auto tmp_14 = hn::ZipLower(int32_tag, tmp_4_16, tmp_6_16); + const auto tmp_15 = hn::ZipLower(int32_tag, tmp_5_16, tmp_7_16); + + const auto res_0 = hn::ZipLower(int64_tag, tmp_12, tmp_14); + const auto res_1 = hn::ZipUpper(int64_tag, tmp_12, tmp_14); + const auto res_2 = hn::ZipLower(int64_tag, tmp_13, tmp_15); + const auto res_3 = hn::ZipUpper(int64_tag, tmp_13, tmp_15); + + hn::Store(hn::BitCast(int8_tag, hn::InterleaveLower(int64_tag, res_0, res_2)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 0); + hn::Store(hn::BitCast(int8_tag, hn::InterleaveUpper(int64_tag, res_0, res_2)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 1); + hn::Store(hn::BitCast(int8_tag, hn::InterleaveLower(int64_tag, res_1, res_3)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 2); + hn::Store(hn::BitCast(int8_tag, hn::InterleaveUpper(int64_tag, res_1, res_3)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 3); +} + +template <typename D> +HWY_ATTR inline void PrepareHorizontalFilterCoefficientsBeta0( + D int16_tag, int alpha, int beta, int sx, int8_t *HWY_RESTRICT coeff) { + (void)int16_tag; + (void)beta; + const auto tmp_0 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 0 * alpha)); + const auto tmp_1 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 1 * alpha)); + const auto tmp_2 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 2 * alpha)); + const auto tmp_3 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 3 * alpha)); + const auto tmp_4 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 4 * alpha)); + const auto tmp_5 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 5 * alpha)); + const auto tmp_6 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 6 * alpha)); + const auto tmp_7 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 7 * alpha)); + + const auto tmp_02 = hn::ZipLower(int32x4_tag, tmp_0, tmp_2); + const auto tmp_13 = hn::ZipLower(int32x4_tag, tmp_1, tmp_3); + const auto tmp_46 = hn::ZipLower(int32x4_tag, tmp_4, tmp_6); + const auto tmp_57 = hn::ZipLower(int32x4_tag, tmp_5, tmp_7); + + constexpr auto int8_tag = hn::Repartition<int8_t, D>(); + constexpr auto int32_tag = hn::Repartition<int32_t, D>(); + constexpr auto int64_tag = hn::Repartition<int64_t, D>(); + + const auto broadcast_12 = + hn::BroadcastBlock<0>(hn::ResizeBitCast(int32_tag, tmp_02)); + const auto broadcast_13 = + hn::BroadcastBlock<0>(hn::ResizeBitCast(int32_tag, tmp_13)); + const auto broadcast_14 = + hn::BroadcastBlock<0>(hn::ResizeBitCast(int32_tag, tmp_46)); + const auto broadcast_15 = + hn::BroadcastBlock<0>(hn::ResizeBitCast(int32_tag, tmp_57)); + + const auto res_0 = hn::ZipLower(int64_tag, broadcast_12, broadcast_14); + const auto res_1 = hn::ZipUpper(int64_tag, broadcast_12, broadcast_14); + const auto res_2 = hn::ZipLower(int64_tag, broadcast_13, broadcast_15); + const auto res_3 = hn::ZipUpper(int64_tag, broadcast_13, broadcast_15); + + hn::Store(hn::BitCast(int8_tag, hn::InterleaveLower(int64_tag, res_0, res_2)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 0); + hn::Store(hn::BitCast(int8_tag, hn::InterleaveUpper(int64_tag, res_0, res_2)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 1); + hn::Store(hn::BitCast(int8_tag, hn::InterleaveLower(int64_tag, res_1, res_3)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 2); + hn::Store(hn::BitCast(int8_tag, hn::InterleaveUpper(int64_tag, res_1, res_3)), + int8_tag, coeff + hn::MaxLanes(coeff_tag) * 3); +} + +template <typename D> +HWY_ATTR inline void PrepareHorizontalFilterCoefficientsAlpha0( + D int16_tag, int alpha, int beta, int sx, int8_t *HWY_RESTRICT coeff) { + (void)alpha; + constexpr auto int8_tag = hn::Repartition<int8_t, D>(); + auto tmp_0 = LoadAV1Filter8BitLower(int8_tag, sx); + if constexpr (int16_tag.MaxBlocks() >= 2) { + tmp_0 = LoadAV1Filter8BitUpper<1>(int8_tag, sx + beta, tmp_0); + } + if constexpr (int16_tag.MaxBlocks() >= 3) { + tmp_0 = LoadAV1Filter8BitUpper<2>(int8_tag, sx + beta * 2, tmp_0); + tmp_0 = LoadAV1Filter8BitUpper<3>(int8_tag, sx + beta * 3, tmp_0); + } + const auto res_0 = hn::BitCast(int16_tag, tmp_0); + + hn::Store(hn::BitCast(int8_tag, hn::Broadcast<0>(res_0)), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 0); + hn::Store(hn::BitCast(int8_tag, hn::Broadcast<1>(res_0)), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 1); + hn::Store(hn::BitCast(int8_tag, hn::Broadcast<2>(res_0)), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 2); + hn::Store(hn::BitCast(int8_tag, hn::Broadcast<3>(res_0)), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 3); +} + +template <typename D> +HWY_ATTR inline void HorizontalFilter(D tag, const hn::VFromD<D> src, + int16_t *HWY_RESTRICT horz_out, int sx, + int alpha, int beta, int row, + const IVec16 round_const, + const int reduce_bits_horiz) { + HWY_ALIGN int8_t coeff[4 * hn::MaxLanes(coeff_tag)]; + PrepareHorizontalFilterCoefficients(hn::Repartition<int16_t, D>(), alpha, + beta, sx, coeff); + FilterPixelsHorizontal(tag, src, horz_out, coeff, round_const, + reduce_bits_horiz, row); +} + +template <typename D> +HWY_ATTR inline void PrepareLastHorizontalFilterCoefficients( + D int16_tag, int alpha, int beta, int sx, int8_t *HWY_RESTRICT coeff) { + (void)int16_tag; + (void)beta; + const auto tmp_0 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 0 * alpha)); + const auto tmp_1 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 1 * alpha)); + const auto tmp_2 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 2 * alpha)); + const auto tmp_3 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 3 * alpha)); + const auto tmp_4 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 4 * alpha)); + const auto tmp_5 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 5 * alpha)); + const auto tmp_6 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 6 * alpha)); + const auto tmp_7 = + hn::BitCast(int16x8_tag, LoadAV1Filter8Bit(sx + 7 * alpha)); + + const auto tmp_8 = hn::ZipLower(int32x4_tag, tmp_0, tmp_2); + const auto tmp_9 = hn::ZipLower(int32x4_tag, tmp_1, tmp_3); + const auto tmp_10 = hn::ZipLower(int32x4_tag, tmp_4, tmp_6); + const auto tmp_11 = hn::ZipLower(int32x4_tag, tmp_5, tmp_7); + + const auto tmp_12 = hn::ZipLower(int64x2_tag, tmp_8, tmp_10); + const auto tmp_13 = hn::ZipUpper(int64x2_tag, tmp_8, tmp_10); + const auto tmp_14 = hn::ZipLower(int64x2_tag, tmp_9, tmp_11); + const auto tmp_15 = hn::ZipUpper(int64x2_tag, tmp_9, tmp_11); + + const auto tmp_16 = hn::InterleaveLower(int64x2_tag, tmp_12, tmp_14); + const auto tmp_17 = hn::InterleaveUpper(int64x2_tag, tmp_12, tmp_14); + const auto tmp_18 = hn::InterleaveLower(int64x2_tag, tmp_13, tmp_15); + const auto tmp_19 = hn::InterleaveUpper(int64x2_tag, tmp_13, tmp_15); + + constexpr auto int8_tag = hn::Repartition<int8_t, D>(); + + const auto tmp_20 = hn::ResizeBitCast(int8_tag, tmp_16); + const auto tmp_21 = hn::ResizeBitCast(int8_tag, tmp_17); + const auto tmp_22 = hn::ResizeBitCast(int8_tag, tmp_18); + const auto tmp_23 = hn::ResizeBitCast(int8_tag, tmp_19); + + hn::Store(hn::BroadcastBlock<0>(tmp_20), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 0); + hn::Store(hn::BroadcastBlock<0>(tmp_21), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 1); + hn::Store(hn::BroadcastBlock<0>(tmp_22), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 2); + hn::Store(hn::BroadcastBlock<0>(tmp_23), int8_tag, + coeff + hn::MaxLanes(coeff_tag) * 3); +} + +template <typename D> +HWY_ATTR HWY_INLINE hn::VFromD<D> LoadRowsClamped( + D tag, const uint8_t *HWY_RESTRICT ref, const int stride, const int iy, + const int height) { + constexpr hn::BlockDFromD<D> block_tag; + const int iy0 = clamp(iy + 0, 0, height - 1); + auto src = hn::ResizeBitCast(tag, hn::LoadU(block_tag, ref + iy0 * stride)); + if constexpr (tag.MaxBlocks() >= 2) { + const int iy1 = clamp(iy + 1, 0, height - 1); + const auto src_1 = hn::LoadU(block_tag, ref + iy1 * stride); + src = hn::InsertBlock<1>(src, src_1); + } + if constexpr (tag.MaxBlocks() >= 3) { + const int iy2 = clamp(iy + 2, 0, height - 1); + const auto src_2 = hn::LoadU(block_tag, ref + iy2 * stride); + const int iy3 = clamp(iy + 3, 0, height - 1); + const auto src_3 = hn::LoadU(block_tag, ref + iy3 * stride); + src = hn::InsertBlock<2>(src, src_2); + src = hn::InsertBlock<3>(src, src_3); + } + return src; +} + +template <void (*PrepareCoeffs)(int alpha, int beta, int sx, + int8_t *HWY_RESTRICT coeffs), + typename D> +HWY_ATTR int WarpHorizontalFilterLoop( + D tag, const uint8_t *HWY_RESTRICT ref, int16_t *HWY_RESTRICT horz_out, + int stride, int32_t ix4, int32_t iy4, int32_t sx4, int alpha, int beta, + int p_height, int height, int i, const IVec16 round_const, + const int reduce_bits_horiz, int k, int8_t *HWY_RESTRICT coeff) { + constexpr int kNumRows = tag.MaxBlocks(); + for (; k < AOMMIN(8, p_height - i) - kNumRows; k += kNumRows) { + const auto src = + LoadRowsClamped(tag, ref + ix4 - 7, stride, iy4 + k, height); + if constexpr (PrepareCoeffs != nullptr) { + int sx = sx4 + beta * (k + 4); + PrepareCoeffs(alpha, beta, sx, coeff); + } + FilterPixelsHorizontal(tag, src, horz_out, coeff, round_const, + reduce_bits_horiz, k + 7); + } + return k; +} + +enum class HorizontalFilterCoeffs { + kAlpha0, + kBeta0, + kDefault, +}; + +template <bool IsLast, HorizontalFilterCoeffs Filter, typename D> +HWY_ATTR void WarpHorizontalPrepareCoeffs(int alpha, int beta, int sx, + int8_t *HWY_RESTRICT coeffs) { + D int16_tag; + switch (Filter) { + case HorizontalFilterCoeffs::kAlpha0: + PrepareHorizontalFilterCoefficientsAlpha0(int16_tag, alpha, beta, sx, + coeffs); + return; + case HorizontalFilterCoeffs::kBeta0: + PrepareHorizontalFilterCoefficientsBeta0(int16_tag, alpha, beta, sx, + coeffs); + return; + case HorizontalFilterCoeffs::kDefault: + default: + if (IsLast) { + PrepareLastHorizontalFilterCoefficients(int16_tag, alpha, beta, sx, + coeffs); + } else { + PrepareHorizontalFilterCoefficients(int16_tag, alpha, beta, sx, coeffs); + } + return; + } +} + +template <bool InnerCoeffUpdate, HorizontalFilterCoeffs Filter> +HWY_ATTR inline void WarpHorizontalFilterTemplate( + const uint8_t *HWY_RESTRICT ref, int16_t *HWY_RESTRICT horz_out, int stride, + int32_t ix4, int32_t iy4, int32_t sx4, int alpha, int beta, int p_height, + int height, int i, const IVec16 round_const, const int reduce_bits_horiz) { + int k = -7, iy; + HWY_ALIGN int8_t coeff[4 * hn::MaxLanes(coeff_tag)]; + if constexpr (!InnerCoeffUpdate) { + WarpHorizontalPrepareCoeffs<false, Filter, decltype(int16xN_tag)>( + alpha, beta, sx4, coeff); + } + if constexpr (uint8xN_tag.MaxBlocks() >= 3) { + k = WarpHorizontalFilterLoop<( + InnerCoeffUpdate + ? WarpHorizontalPrepareCoeffs<false, Filter, decltype(int16xN_tag)> + : nullptr)>(uint8xN_tag, ref, horz_out, stride, ix4, iy4, sx4, + alpha, beta, p_height, height, i, round_const, + reduce_bits_horiz, k, coeff); + } + if constexpr (uint8xN_tag.MaxBlocks() >= 2) { + k = WarpHorizontalFilterLoop<( + InnerCoeffUpdate + ? WarpHorizontalPrepareCoeffs<false, Filter, decltype(int16x16_tag)> + : nullptr)>(uint8x32_tag, ref, horz_out, stride, ix4, iy4, sx4, + alpha, beta, p_height, height, i, round_const, + reduce_bits_horiz, k, coeff); + } + if constexpr (uint8xN_tag.MaxBlocks() == 1) { + k = WarpHorizontalFilterLoop<( + InnerCoeffUpdate + ? WarpHorizontalPrepareCoeffs<true, Filter, decltype(int16x8_tag)> + : nullptr)>(uint8x16_tag, ref, horz_out, stride, ix4, iy4, sx4, + alpha, beta, p_height, height, i, round_const, + reduce_bits_horiz, k, coeff); + } + iy = iy4 + k; + iy = clamp(iy, 0, height - 1); + const auto src = hn::LoadU(uint8x16_tag, ref + iy * stride + ix4 - 7); + if constexpr (InnerCoeffUpdate) { + int sx = sx4 + beta * (k + 4); + WarpHorizontalPrepareCoeffs<true, Filter, decltype(int16x8_tag)>( + alpha, beta, sx, coeff); + } + FilterPixelsHorizontal(uint8x16_tag, src, horz_out, coeff, round_const, + reduce_bits_horiz, k + 7); +} + +HWY_ATTR inline void UnpackWeightsAndSetRoundConst( + ConvolveParams *HWY_RESTRICT conv_params, const int round_bits, + const int offset_bits, IVec16 &HWY_RESTRICT res_sub_const, + IVec16 &HWY_RESTRICT round_bits_const, IVec16 &HWY_RESTRICT wt) { + res_sub_const = + hn::Set(int16xN_tag, -(1 << (offset_bits - conv_params->round_1)) - + (1 << (offset_bits - conv_params->round_1 - 1))); + round_bits_const = hn::Set(int16xN_tag, ((1 << round_bits) >> 1)); + + const auto w0 = static_cast<int16_t>(conv_params->fwd_offset); + const auto w1 = static_cast<int16_t>(conv_params->bck_offset); + const auto wt0 = hn::Set(int16xN_tag, w0); + const auto wt1 = hn::Set(int16xN_tag, w1); + wt = hn::InterleaveLower(wt0, wt1); +} + +HWY_ATTR HWY_INLINE IVec16 LoadAV1WarpedFilter(size_t offset) { + return hn::LoadN(int16xN_tag, + av1_warped_filter[offset >> WARPEDDIFF_PREC_BITS], 8); +} + +HWY_ATTR HWY_INLINE IVec16 LoadAV1WarpedFilterLower(size_t offset) { + return hn::ResizeBitCast( + int16xN_tag, + hn::Load(int16x8_tag, av1_warped_filter[offset >> WARPEDDIFF_PREC_BITS])); +} + +template <int Block> +HWY_ATTR HWY_INLINE IVec16 LoadAV1WarpedFilterUpper(size_t offset, IVec16 src) { + return hn::InsertBlock<Block>( + src, + hn::Load(int16x8_tag, av1_warped_filter[offset >> WARPEDDIFF_PREC_BITS])); +} + +HWY_ATTR inline void PrepareVerticalFilterCoeffs(int gamma, int delta, int sy, + int16_t *HWY_RESTRICT coeffs) { + auto filt_00 = LoadAV1WarpedFilterLower(sy + 0 * gamma); + auto filt_01 = LoadAV1WarpedFilterLower(sy + 2 * gamma); + auto filt_02 = LoadAV1WarpedFilterLower(sy + 4 * gamma); + auto filt_03 = LoadAV1WarpedFilterLower(sy + 6 * gamma); + + if constexpr (int16xN_tag.MaxBlocks() >= 2) { + filt_00 = LoadAV1WarpedFilterUpper<1>(sy + delta + 0 * gamma, filt_00); + filt_01 = LoadAV1WarpedFilterUpper<1>(sy + delta + 2 * gamma, filt_01); + filt_02 = LoadAV1WarpedFilterUpper<1>(sy + delta + 4 * gamma, filt_02); + filt_03 = LoadAV1WarpedFilterUpper<1>(sy + delta + 6 * gamma, filt_03); + } + + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + filt_00 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 0 * gamma, filt_00); + filt_01 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 2 * gamma, filt_01); + filt_02 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 4 * gamma, filt_02); + filt_03 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 6 * gamma, filt_03); + + filt_00 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 0 * gamma, filt_00); + filt_01 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 2 * gamma, filt_01); + filt_02 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 4 * gamma, filt_02); + filt_03 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 6 * gamma, filt_03); + } + + auto filt_0 = hn::BitCast(int32xN_tag, filt_00); + auto filt_1 = hn::BitCast(int32xN_tag, filt_01); + auto filt_2 = hn::BitCast(int32xN_tag, filt_02); + auto filt_3 = hn::BitCast(int32xN_tag, filt_03); + + auto res_0 = hn::ZipLower(int64xN_tag, filt_0, filt_1); + auto res_1 = hn::ZipLower(int64xN_tag, filt_2, filt_3); + auto res_2 = hn::ZipUpper(int64xN_tag, filt_0, filt_1); + auto res_3 = hn::ZipUpper(int64xN_tag, filt_2, filt_3); + + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 3 * hn::MaxLanes(int16xN_tag)); + + filt_00 = LoadAV1WarpedFilterLower(sy + 1 * gamma); + filt_01 = LoadAV1WarpedFilterLower(sy + 3 * gamma); + filt_02 = LoadAV1WarpedFilterLower(sy + 5 * gamma); + filt_03 = LoadAV1WarpedFilterLower(sy + 7 * gamma); + + if constexpr (int16xN_tag.MaxBlocks() >= 2) { + filt_00 = LoadAV1WarpedFilterUpper<1>(sy + delta + 1 * gamma, filt_00); + filt_01 = LoadAV1WarpedFilterUpper<1>(sy + delta + 3 * gamma, filt_01); + filt_02 = LoadAV1WarpedFilterUpper<1>(sy + delta + 5 * gamma, filt_02); + filt_03 = LoadAV1WarpedFilterUpper<1>(sy + delta + 7 * gamma, filt_03); + } + + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + filt_00 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 1 * gamma, filt_00); + filt_01 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 3 * gamma, filt_01); + filt_02 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 5 * gamma, filt_02); + filt_03 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta + 7 * gamma, filt_03); + + filt_00 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 1 * gamma, filt_00); + filt_01 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 3 * gamma, filt_01); + filt_02 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 5 * gamma, filt_02); + filt_03 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta + 7 * gamma, filt_03); + } + + filt_0 = hn::BitCast(int32xN_tag, filt_00); + filt_1 = hn::BitCast(int32xN_tag, filt_01); + filt_2 = hn::BitCast(int32xN_tag, filt_02); + filt_3 = hn::BitCast(int32xN_tag, filt_03); + + res_0 = hn::ZipLower(int64xN_tag, filt_0, filt_1); + res_1 = hn::ZipLower(int64xN_tag, filt_2, filt_3); + res_2 = hn::ZipUpper(int64xN_tag, filt_0, filt_1); + res_3 = hn::ZipUpper(int64xN_tag, filt_2, filt_3); + + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 4 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 5 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 6 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 7 * hn::MaxLanes(int16xN_tag)); +} + +HWY_ATTR inline void PrepareVerticalFilterCoeffsDelta0( + int gamma, int delta, int sy, int16_t *HWY_RESTRICT coeffs) { + (void)delta; + auto filt_00 = LoadAV1WarpedFilter(sy + 0 * gamma); + auto filt_01 = LoadAV1WarpedFilter(sy + 2 * gamma); + auto filt_02 = LoadAV1WarpedFilter(sy + 4 * gamma); + auto filt_03 = LoadAV1WarpedFilter(sy + 6 * gamma); + + auto filt_10 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_00)); + auto filt_11 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_01)); + auto filt_12 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_02)); + auto filt_13 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_03)); + + auto res_0 = hn::ZipLower(int64xN_tag, filt_10, filt_11); + auto res_1 = hn::ZipLower(int64xN_tag, filt_12, filt_13); + auto res_2 = hn::ZipUpper(int64xN_tag, filt_10, filt_11); + auto res_3 = hn::ZipUpper(int64xN_tag, filt_12, filt_13); + + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 3 * hn::MaxLanes(int16xN_tag)); + + filt_00 = LoadAV1WarpedFilter(sy + 1 * gamma); + filt_01 = LoadAV1WarpedFilter(sy + 3 * gamma); + filt_02 = LoadAV1WarpedFilter(sy + 5 * gamma); + filt_03 = LoadAV1WarpedFilter(sy + 7 * gamma); + + filt_10 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_00)); + filt_11 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_01)); + filt_12 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_02)); + filt_13 = hn::BitCast(int32xN_tag, hn::BroadcastBlock<0>(filt_03)); + + res_0 = hn::ZipLower(int64xN_tag, filt_10, filt_11); + res_1 = hn::ZipLower(int64xN_tag, filt_12, filt_13); + res_2 = hn::ZipUpper(int64xN_tag, filt_10, filt_11); + res_3 = hn::ZipUpper(int64xN_tag, filt_12, filt_13); + + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 4 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_0, res_1)), + int16xN_tag, coeffs + 5 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveLower(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 6 * hn::MaxLanes(int16xN_tag)); + hn::Store( + hn::BitCast(int16xN_tag, hn::InterleaveUpper(int64xN_tag, res_2, res_3)), + int16xN_tag, coeffs + 7 * hn::MaxLanes(int16xN_tag)); +} + +HWY_ATTR inline void PrepareVerticalFilterCoeffsGamma0( + int gamma, int delta, int sy, int16_t *HWY_RESTRICT coeffs) { + (void)gamma; + auto filt_0 = LoadAV1WarpedFilterLower(sy); + if constexpr (int16xN_tag.MaxBlocks() >= 2) { + filt_0 = LoadAV1WarpedFilterUpper<1>(sy + delta, filt_0); + } + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + filt_0 = LoadAV1WarpedFilterUpper<2>(sy + 2 * delta, filt_0); + filt_0 = LoadAV1WarpedFilterUpper<3>(sy + 3 * delta, filt_0); + } + auto res_0 = hn::BitCast(int32xN_tag, filt_0); + + auto broadcast_0 = hn::BitCast(int16xN_tag, hn::Broadcast<0>(res_0)); + auto broadcast_1 = hn::BitCast(int16xN_tag, hn::Broadcast<1>(res_0)); + auto broadcast_2 = hn::BitCast(int16xN_tag, hn::Broadcast<2>(res_0)); + auto broadcast_3 = hn::BitCast(int16xN_tag, hn::Broadcast<3>(res_0)); + + hn::Store(broadcast_0, int16xN_tag, coeffs + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_1, int16xN_tag, coeffs + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_2, int16xN_tag, coeffs + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_3, int16xN_tag, coeffs + 3 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_0, int16xN_tag, coeffs + 4 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_1, int16xN_tag, coeffs + 5 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_2, int16xN_tag, coeffs + 6 * hn::MaxLanes(int16xN_tag)); + hn::Store(broadcast_3, int16xN_tag, coeffs + 7 * hn::MaxLanes(int16xN_tag)); +} + +HWY_ATTR inline void FilterPixelsVertical( + int16_t *HWY_RESTRICT horz_out, int16_t *HWY_RESTRICT src_lo, + int16_t *HWY_RESTRICT src_hi, int16_t *HWY_RESTRICT coeffs, + IVec32 &HWY_RESTRICT res_lo, IVec32 &HWY_RESTRICT res_hi, int row) { + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + const auto horz_out_4 = + hn::Load(int16xN_tag, horz_out + (row + 4) * hn::MaxLanes(int16x8_tag)); + const auto horz_out_5 = hn::LoadU( + int16xN_tag, horz_out + (row + 5) * hn::MaxLanes(int16x8_tag)); + const auto horz_out_6 = hn::LoadU( + int16xN_tag, horz_out + (row + 6) * hn::MaxLanes(int16x8_tag)); + const auto horz_out_7 = hn::LoadU( + int16xN_tag, horz_out + (row + 7) * hn::MaxLanes(int16x8_tag)); + const auto src_lo_2 = + hn::InterleaveLower(int16xN_tag, horz_out_4, horz_out_5); + const auto src_hi_2 = + hn::InterleaveUpper(int16xN_tag, horz_out_4, horz_out_5); + const auto src_lo_3 = + hn::InterleaveLower(int16xN_tag, horz_out_6, horz_out_7); + const auto src_hi_3 = + hn::InterleaveUpper(int16xN_tag, horz_out_6, horz_out_7); + hn::Store(src_lo_2, int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_hi_2, int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_lo_3, int16xN_tag, src_lo + 3 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_hi_3, int16xN_tag, src_hi + 3 * hn::MaxLanes(int16xN_tag)); + } else if constexpr (int16xN_tag.MaxBlocks() == 2) { + const auto horz_out_6 = + hn::Load(int16xN_tag, horz_out + (row + 6) * hn::MaxLanes(int16x8_tag)); + const auto horz_out_8 = + hn::Load(int16xN_tag, horz_out + (row + 8) * hn::MaxLanes(int16x8_tag)); + const auto horz_out_7 = + hn::ConcatLowerUpper(int16xN_tag, horz_out_8, horz_out_6); + const auto src_lo_3 = + hn::InterleaveLower(int16xN_tag, horz_out_6, horz_out_7); + const auto src_hi_3 = + hn::InterleaveUpper(int16xN_tag, horz_out_6, horz_out_7); + hn::Store(src_lo_3, int16xN_tag, src_lo + 3 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_hi_3, int16xN_tag, src_hi + 3 * hn::MaxLanes(int16xN_tag)); + } else if constexpr (int16xN_tag.MaxBlocks() == 1) { + const auto horz_out_6 = + hn::Load(int16x8_tag, horz_out + (row + 6) * hn::MaxLanes(int16x8_tag)); + const auto horz_out_7 = + hn::Load(int16x8_tag, horz_out + (row + 7) * hn::MaxLanes(int16x8_tag)); + const auto src_lo_3 = + hn::InterleaveLower(int16x8_tag, horz_out_6, horz_out_7); + const auto src_hi_3 = + hn::InterleaveUpper(int16x8_tag, horz_out_6, horz_out_7); + hn::Store(src_lo_3, int16x8_tag, src_lo + 3 * hn::MaxLanes(int16x8_tag)); + hn::Store(src_hi_3, int16x8_tag, src_hi + 3 * hn::MaxLanes(int16x8_tag)); + } + + const auto coeff_0 = + hn::Load(int16xN_tag, coeffs + 0 * hn::MaxLanes(int16xN_tag)); + const auto coeff_1 = + hn::Load(int16xN_tag, coeffs + 1 * hn::MaxLanes(int16xN_tag)); + const auto coeff_2 = + hn::Load(int16xN_tag, coeffs + 2 * hn::MaxLanes(int16xN_tag)); + const auto coeff_3 = + hn::Load(int16xN_tag, coeffs + 3 * hn::MaxLanes(int16xN_tag)); + const auto coeff_4 = + hn::Load(int16xN_tag, coeffs + 4 * hn::MaxLanes(int16xN_tag)); + const auto coeff_5 = + hn::Load(int16xN_tag, coeffs + 5 * hn::MaxLanes(int16xN_tag)); + const auto coeff_6 = + hn::Load(int16xN_tag, coeffs + 6 * hn::MaxLanes(int16xN_tag)); + const auto coeff_7 = + hn::Load(int16xN_tag, coeffs + 7 * hn::MaxLanes(int16xN_tag)); + + const auto src_lo_0 = + hn::Load(int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_1 = + hn::Load(int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_2 = + hn::Load(int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_3 = + hn::Load(int16xN_tag, src_lo + 3 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_0 = + hn::Load(int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_1 = + hn::Load(int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_2 = + hn::Load(int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_3 = + hn::Load(int16xN_tag, src_hi + 3 * hn::MaxLanes(int16xN_tag)); + + auto even_sum0 = hn::Zero(int32xN_tag); + auto even_sum1 = hn::Zero(int32xN_tag); + even_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_lo_0, coeff_0, + even_sum0, even_sum1); + even_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_lo_1, coeff_1, + even_sum0, even_sum1); + even_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_lo_2, coeff_2, + even_sum0, even_sum1); + even_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_lo_3, coeff_3, + even_sum0, even_sum1); + auto res_even = hn::RearrangeToOddPlusEven(even_sum0, even_sum1); + + auto odd_sum0 = hn::Zero(int32xN_tag); + auto odd_sum1 = hn::Zero(int32xN_tag); + odd_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_hi_0, coeff_4, + odd_sum0, odd_sum1); + odd_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_hi_1, coeff_5, + odd_sum0, odd_sum1); + odd_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_hi_2, coeff_6, + odd_sum0, odd_sum1); + odd_sum0 = hn::ReorderWidenMulAccumulate(int32xN_tag, src_hi_3, coeff_7, + odd_sum0, odd_sum1); + auto res_odd = hn::RearrangeToOddPlusEven(odd_sum0, odd_sum1); + + // Rearrange pixels back into the order 0 ... 7 + res_lo = hn::InterleaveLower(int32xN_tag, res_even, res_odd); + res_hi = hn::InterleaveUpper(int32xN_tag, res_even, res_odd); +} + +template <typename DS, typename DR, typename A, typename B, typename C> +HWY_ATTR HWY_INLINE void StoreRows(DS store_tag, DR row_tag, hn::VFromD<DR> vec, + A stride, B y, C x, + hn::TFromD<DS> *HWY_RESTRICT out) { + hn::TFromD<DS> *HWY_RESTRICT pointers[row_tag.MaxBlocks()]; + for (int i = 0; i < static_cast<int>(row_tag.MaxBlocks()); ++i) { + pointers[i] = &out[(y + i) * stride + x]; + } + hn::Store(hn::ResizeBitCast(store_tag, hn::ExtractBlock<0>(vec)), store_tag, + pointers[0]); + if constexpr (row_tag.MaxBlocks() >= 2) { + hn::Store(hn::ResizeBitCast(store_tag, hn::ExtractBlock<1>(vec)), store_tag, + pointers[1]); + } + if constexpr (row_tag.MaxBlocks() >= 3) { + hn::Store(hn::ResizeBitCast(store_tag, hn::ExtractBlock<2>(vec)), store_tag, + pointers[2]); + hn::Store(hn::ResizeBitCast(store_tag, hn::ExtractBlock<3>(vec)), store_tag, + pointers[3]); + } +} + +HWY_ATTR HWY_INLINE void StoreVerticalFilterOutput( + IVec32 res_lo, IVec32 res_hi, const IVec32 res_add_const, const IVec16 wt, + const IVec16 res_sub_const, const IVec16 round_bits_const, + uint8_t *HWY_RESTRICT pred, ConvolveParams *HWY_RESTRICT conv_params, int i, + int j, int k, const int reduce_bits_vert, int p_stride, int p_width, + const int round_bits) { + constexpr int kNumRows = uint16xN_tag.MaxBlocks(); + if (conv_params->is_compound) { + uint16_t *HWY_RESTRICT pointers[kNumRows]; + for (int row = 0; row < kNumRows; ++row) { + pointers[row] = + &conv_params->dst[(i + k + row) * conv_params->dst_stride + j]; + } + + res_lo = + hn::ShiftRightSame(hn::Add(res_lo, res_add_const), reduce_bits_vert); + + const auto temp_lo_16 = hn::ReorderDemote2To(uint16xN_tag, res_lo, res_lo); + if (conv_params->do_average) { + auto p_16 = + hn::ResizeBitCast(uint16xN_tag, hn::Load(uint16x4_tag, pointers[0])); + if constexpr (kNumRows >= 2) { + p_16 = hn::InsertBlock<1>( + p_16, hn::ResizeBitCast(uint16x8_tag, + hn::Load(uint16x4_tag, pointers[1]))); + } + if constexpr (kNumRows >= 3) { + p_16 = hn::InsertBlock<2>( + p_16, hn::ResizeBitCast(uint16x8_tag, + hn::Load(uint16x4_tag, pointers[2]))); + p_16 = hn::InsertBlock<3>( + p_16, hn::ResizeBitCast(uint16x8_tag, + hn::Load(uint16x4_tag, pointers[3]))); + } + auto res_lo_16 = hn::Undefined(int16xN_tag); + if (conv_params->use_dist_wtd_comp_avg) { + const auto p_16_lo = + hn::BitCast(int16xN_tag, hn::InterleaveLower(p_16, temp_lo_16)); + const auto wt_res_lo = + hn::WidenMulPairwiseAdd(int32xN_tag, p_16_lo, wt); + const auto shifted_32 = hn::ShiftRight<DIST_PRECISION_BITS>(wt_res_lo); + res_lo_16 = hn::BitCast( + int16xN_tag, + hn::ReorderDemote2To(uint16xN_tag, shifted_32, shifted_32)); + } else { + res_lo_16 = hn::ShiftRight<1>( + hn::BitCast(int16xN_tag, hn::Add(p_16, temp_lo_16))); + } + res_lo_16 = hn::Add(res_lo_16, res_sub_const); + res_lo_16 = + hn::ShiftRightSame(hn::Add(res_lo_16, round_bits_const), round_bits); + const auto res_8_lo = + hn::ReorderDemote2To(uint8xN_tag, res_lo_16, res_lo_16); + StoreRows(uint8x4_tag, uint8xN_tag, res_8_lo, p_stride, i + k, j, pred); + } else { + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<0>(temp_lo_16)), + uint16x4_tag, pointers[0]); + if constexpr (kNumRows >= 2) { + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<1>(temp_lo_16)), + uint16x4_tag, pointers[1]); + } + if constexpr (kNumRows >= 3) { + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<2>(temp_lo_16)), + uint16x4_tag, pointers[2]); + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<3>(temp_lo_16)), + uint16x4_tag, pointers[3]); + } + } + if (p_width > 4) { + uint16_t *HWY_RESTRICT pointers4[kNumRows]; + for (int row = 0; row < kNumRows; ++row) { + pointers4[row] = + &conv_params->dst[(i + k + row) * conv_params->dst_stride + j + 4]; + } + res_hi = + hn::ShiftRightSame(hn::Add(res_hi, res_add_const), reduce_bits_vert); + const auto temp_hi_16 = + hn::ReorderDemote2To(uint16xN_tag, res_hi, res_hi); + if (conv_params->do_average) { + auto p4_16 = hn::ResizeBitCast(uint16xN_tag, + hn::Load(uint16x4_tag, pointers4[0])); + if constexpr (kNumRows >= 2) { + p4_16 = hn::InsertBlock<1>( + p4_16, hn::ResizeBitCast(uint16x8_tag, + hn::Load(uint16x4_tag, pointers4[1]))); + } + if constexpr (kNumRows >= 3) { + p4_16 = hn::InsertBlock<2>( + p4_16, hn::ResizeBitCast(uint16x8_tag, + hn::Load(uint16x4_tag, pointers4[2]))); + p4_16 = hn::InsertBlock<3>( + p4_16, hn::ResizeBitCast(uint16x8_tag, + hn::Load(uint16x4_tag, pointers4[3]))); + } + + auto res_hi_16 = hn::Undefined(int16xN_tag); + if (conv_params->use_dist_wtd_comp_avg) { + const auto p_16_hi = + hn::BitCast(int16xN_tag, hn::InterleaveLower(p4_16, temp_hi_16)); + const auto wt_res_hi = + hn::WidenMulPairwiseAdd(int32xN_tag, p_16_hi, wt); + const auto shifted_32 = + hn::ShiftRight<DIST_PRECISION_BITS>(wt_res_hi); + res_hi_16 = hn::BitCast( + int16xN_tag, + hn::ReorderDemote2To(uint16xN_tag, shifted_32, shifted_32)); + } else { + res_hi_16 = hn::ShiftRight<1>( + hn::BitCast(int16xN_tag, hn::Add(p4_16, temp_hi_16))); + } + res_hi_16 = hn::Add(res_hi_16, res_sub_const); + res_hi_16 = hn::ShiftRightSame(hn::Add(res_hi_16, round_bits_const), + round_bits); + const auto res_8_hi = + hn::ReorderDemote2To(uint8xN_tag, res_hi_16, res_hi_16); + StoreRows(uint8x4_tag, uint8xN_tag, res_8_hi, p_stride, i + k, j + 4, + pred); + } else { + hn::Store(hn::ResizeBitCast(uint16x4_tag, temp_hi_16), uint16x4_tag, + pointers4[0]); + if constexpr (kNumRows >= 2) { + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<1>(temp_hi_16)), + uint16x4_tag, pointers4[1]); + } + if constexpr (kNumRows >= 3) { + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<2>(temp_hi_16)), + uint16x4_tag, pointers4[2]); + hn::Store( + hn::ResizeBitCast(uint16x4_tag, hn::ExtractBlock<3>(temp_hi_16)), + uint16x4_tag, pointers4[3]); + } + } + } + } else { + const auto res_lo_round = + hn::ShiftRightSame(hn::Add(res_lo, res_add_const), reduce_bits_vert); + const auto res_hi_round = + hn::ShiftRightSame(hn::Add(res_hi, res_add_const), reduce_bits_vert); + + const auto res_16bit = + hn::ReorderDemote2To(int16xN_tag, res_lo_round, res_hi_round); + const auto res_8bit = + hn::ReorderDemote2To(uint8xN_tag, res_16bit, res_16bit); + // Store, blending with 'pred' if needed + if (p_width == 4) { + StoreRows(uint8x4_tag, uint8xN_tag, res_8bit, p_stride, i + k, j, pred); + } else { + StoreRows(uint8x8_tag, uint8xN_tag, res_8bit, p_stride, i + k, j, pred); + } + } +} + +template <bool InnerCoeffUpdate, + void (*PrepareCoeffs)(int gamma, int delta, int sy, + int16_t *HWY_RESTRICT coeffs)> +HWY_ATTR inline void WarpVerticalFilterTemplate( + uint8_t *HWY_RESTRICT pred, int16_t *HWY_RESTRICT horz_out, + ConvolveParams *HWY_RESTRICT conv_params, int16_t gamma, int16_t delta, + int p_height, int p_stride, int p_width, int i, int j, int sy4, + const int reduce_bits_vert, const IVec32 res_add_const, + const int round_bits, const IVec16 res_sub_const, + const IVec16 round_bits_const, const IVec16 wt) { + HWY_ALIGN int16_t src_lo[4 * hn::MaxLanes(int16xN_tag)]; + HWY_ALIGN int16_t src_hi[4 * hn::MaxLanes(int16xN_tag)]; + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + const auto horz_out_0 = + hn::Load(int16xN_tag, horz_out + 0 * hn::MaxLanes(int16x8_tag)); + const auto horz_out_1 = + hn::LoadU(int16xN_tag, horz_out + 1 * hn::MaxLanes(int16x8_tag)); + const auto horz_out_2 = + hn::LoadU(int16xN_tag, horz_out + 2 * hn::MaxLanes(int16x8_tag)); + const auto horz_out_3 = + hn::LoadU(int16xN_tag, horz_out + 3 * hn::MaxLanes(int16x8_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_0, horz_out_1), + int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_0, horz_out_1), + int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_2, horz_out_3), + int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_2, horz_out_3), + int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + } else if constexpr (int16xN_tag.MaxBlocks() == 2) { + const auto horz_out_0 = + hn::Load(int16xN_tag, horz_out + 0 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_2 = + hn::Load(int16xN_tag, horz_out + 1 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_4 = + hn::Load(int16xN_tag, horz_out + 2 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_6 = + hn::Load(int16xN_tag, horz_out + 3 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_1 = + hn::ConcatLowerUpper(int16xN_tag, horz_out_2, horz_out_0); + const auto horz_out_3 = + hn::ConcatLowerUpper(int16xN_tag, horz_out_4, horz_out_2); + const auto horz_out_5 = + hn::ConcatLowerUpper(int16xN_tag, horz_out_6, horz_out_4); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_0, horz_out_1), + int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_0, horz_out_1), + int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_2, horz_out_3), + int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_2, horz_out_3), + int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_4, horz_out_5), + int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_4, horz_out_5), + int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)); + } else { + const auto horz_out_0 = + hn::Load(int16xN_tag, horz_out + 0 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_1 = + hn::Load(int16xN_tag, horz_out + 1 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_2 = + hn::Load(int16xN_tag, horz_out + 2 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_3 = + hn::Load(int16xN_tag, horz_out + 3 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_4 = + hn::Load(int16xN_tag, horz_out + 4 * hn::MaxLanes(int16xN_tag)); + const auto horz_out_5 = + hn::Load(int16xN_tag, horz_out + 5 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_0, horz_out_1), + int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_0, horz_out_1), + int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_2, horz_out_3), + int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_2, horz_out_3), + int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveLower(int16xN_tag, horz_out_4, horz_out_5), + int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::InterleaveUpper(int16xN_tag, horz_out_4, horz_out_5), + int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)); + } + + HWY_ALIGN int16_t coeffs[8 * hn::MaxLanes(int16xN_tag)]; + if constexpr (!InnerCoeffUpdate) { + PrepareCoeffs(gamma, delta, sy4, coeffs); + } + + for (int k = -4; k < AOMMIN(4, p_height - i - 4); + k += static_cast<int>(int16xN_tag.MaxBlocks())) { + if constexpr (InnerCoeffUpdate) { + int sy = sy4 + delta * (k + 4); + PrepareCoeffs(gamma, delta, sy, coeffs); + } + + IVec32 res_lo, res_hi; + FilterPixelsVertical(horz_out, src_lo, src_hi, coeffs, res_lo, res_hi, + k + 4); + StoreVerticalFilterOutput(res_lo, res_hi, res_add_const, wt, res_sub_const, + round_bits_const, pred, conv_params, i, j, k + 4, + reduce_bits_vert, p_stride, p_width, round_bits); + + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + hn::Store(hn::Load(int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_lo + 3 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_hi + 3 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + } else if constexpr (int16xN_tag.MaxBlocks() == 2) { + hn::Store(hn::Load(int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_lo + 3 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(hn::Load(int16xN_tag, src_hi + 3 * hn::MaxLanes(int16xN_tag)), + int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)); + } else if constexpr (int16xN_tag.MaxBlocks() == 1) { + const auto src_lo_0 = + hn::Load(int16xN_tag, src_lo + 0 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_1 = + hn::Load(int16xN_tag, src_lo + 1 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_2 = + hn::Load(int16xN_tag, src_lo + 2 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_3 = + hn::Load(int16xN_tag, src_lo + 3 * hn::MaxLanes(int16xN_tag)); + const auto src_lo_0_new = hn::InterleaveEven( + hn::ShiftRightLanes<1>(int16xN_tag, src_lo_0), src_lo_1); + const auto src_lo_1_new = hn::InterleaveEven( + hn::ShiftRightLanes<1>(int16xN_tag, src_lo_1), src_lo_2); + const auto src_lo_2_new = hn::InterleaveEven( + hn::ShiftRightLanes<1>(int16xN_tag, src_lo_2), src_lo_3); + hn::Store(src_lo_0_new, int16xN_tag, + src_lo + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_lo_1_new, int16xN_tag, + src_lo + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_lo_2_new, int16xN_tag, + src_lo + 2 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_0 = + hn::Load(int16xN_tag, src_hi + 0 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_1 = + hn::Load(int16xN_tag, src_hi + 1 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_2 = + hn::Load(int16xN_tag, src_hi + 2 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_3 = + hn::Load(int16xN_tag, src_hi + 3 * hn::MaxLanes(int16xN_tag)); + const auto src_hi_0_new = hn::InterleaveEven( + hn::ShiftRightLanes<1>(int16xN_tag, src_hi_0), src_hi_1); + const auto src_hi_1_new = hn::InterleaveEven( + hn::ShiftRightLanes<1>(int16xN_tag, src_hi_1), src_hi_2); + const auto src_hi_2_new = hn::InterleaveEven( + hn::ShiftRightLanes<1>(int16xN_tag, src_hi_2), src_hi_3); + hn::Store(src_hi_0_new, int16xN_tag, + src_hi + 0 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_hi_1_new, int16xN_tag, + src_hi + 1 * hn::MaxLanes(int16xN_tag)); + hn::Store(src_hi_2_new, int16xN_tag, + src_hi + 2 * hn::MaxLanes(int16xN_tag)); + } + } +} + +HWY_ATTR inline void PrepareWarpVerticalFilter( + uint8_t *HWY_RESTRICT pred, int16_t *HWY_RESTRICT horz_out, + ConvolveParams *HWY_RESTRICT conv_params, int16_t gamma, int16_t delta, + int p_height, int p_stride, int p_width, int i, int j, int sy4, + const int reduce_bits_vert, const IVec32 res_add_const, + const int round_bits, const IVec16 res_sub_const, + const IVec16 round_bits_const, const IVec16 wt) { + if (gamma == 0 && delta == 0) + WarpVerticalFilterTemplate<false, PrepareVerticalFilterCoeffsGamma0>( + pred, horz_out, conv_params, gamma, delta, p_height, p_stride, p_width, + i, j, sy4, reduce_bits_vert, res_add_const, round_bits, res_sub_const, + round_bits_const, wt); + else if (gamma == 0 && delta != 0) + WarpVerticalFilterTemplate<true, PrepareVerticalFilterCoeffsGamma0>( + pred, horz_out, conv_params, gamma, delta, p_height, p_stride, p_width, + i, j, sy4, reduce_bits_vert, res_add_const, round_bits, res_sub_const, + round_bits_const, wt); + else if (gamma != 0 && delta == 0) + WarpVerticalFilterTemplate<false, PrepareVerticalFilterCoeffsDelta0>( + pred, horz_out, conv_params, gamma, delta, p_height, p_stride, p_width, + i, j, sy4, reduce_bits_vert, res_add_const, round_bits, res_sub_const, + round_bits_const, wt); + else + WarpVerticalFilterTemplate<true, PrepareVerticalFilterCoeffs>( + pred, horz_out, conv_params, gamma, delta, p_height, p_stride, p_width, + i, j, sy4, reduce_bits_vert, res_add_const, round_bits, res_sub_const, + round_bits_const, wt); +} + +HWY_ATTR inline void PrepareWarpHorizontalFilter( + const uint8_t *HWY_RESTRICT ref, int16_t *HWY_RESTRICT horz_out, int stride, + int32_t ix4, int32_t iy4, int32_t sx4, int alpha, int beta, int p_height, + int height, int i, const IVec16 round_const, const int reduce_bits_horiz) { + if (alpha == 0 && beta == 0) + WarpHorizontalFilterTemplate<false, HorizontalFilterCoeffs::kAlpha0>( + ref, horz_out, stride, ix4, iy4, sx4, alpha, beta, p_height, height, i, + round_const, reduce_bits_horiz); + else if (alpha == 0 && beta != 0) + WarpHorizontalFilterTemplate<true, HorizontalFilterCoeffs::kAlpha0>( + ref, horz_out, stride, ix4, iy4, sx4, alpha, beta, p_height, height, i, + round_const, reduce_bits_horiz); + else if (alpha != 0 && beta == 0) + WarpHorizontalFilterTemplate<false, HorizontalFilterCoeffs::kBeta0>( + ref, horz_out, stride, ix4, iy4, sx4, alpha, beta, p_height, height, i, + round_const, reduce_bits_horiz); + else + WarpHorizontalFilterTemplate<true, HorizontalFilterCoeffs::kDefault>( + ref, horz_out, stride, ix4, iy4, sx4, alpha, beta, p_height, height, i, + round_const, reduce_bits_horiz); +} + +template <typename D> +HWY_ATTR HWY_INLINE int WarpHorizontalFilterOutOfBoundsSetLoop( + D tag, const uint8_t *HWY_RESTRICT ref, int height, int stride, + int p_height, int i, int iy4, int16_t const4, int16_t const5, int offset, + int k, int16_t *HWY_RESTRICT horz_out) { + constexpr int kNumRows = tag.MaxBlocks(); + for (; k < AOMMIN(8, p_height - i) - kNumRows; k += kNumRows) { + int iy = clamp(iy4 + k + 0, 0, height - 1); + auto src = hn::ResizeBitCast( + tag, hn::Set(int16x8_tag, const4 + ref[iy * stride + offset] * const5)); + if constexpr (kNumRows >= 2) { + iy = clamp(iy4 + k + 1, 0, height - 1); + src = hn::InsertBlock<1>( + src, + hn::Set(int16x8_tag, const4 + ref[iy * stride + offset] * const5)); + } + if constexpr (kNumRows >= 3) { + iy = clamp(iy4 + k + 2, 0, height - 1); + src = hn::InsertBlock<2>( + src, + hn::Set(int16x8_tag, const4 + ref[iy * stride + offset] * const5)); + iy = clamp(iy4 + k + 3, 0, height - 1); + src = hn::InsertBlock<3>( + src, + hn::Set(int16x8_tag, const4 + ref[iy * stride + offset] * const5)); + } + hn::Store(src, tag, horz_out + (k + 7) * hn::MaxLanes(int16x8_tag)); + } + return k; +} + +HWY_ATTR void WarpHorizontalFilterOutOfBoundsSet( + const uint8_t *HWY_RESTRICT ref, int height, int stride, int p_height, + int i, int iy4, int16_t const4, int16_t const5, int offset, + int16_t *HWY_RESTRICT horz_out) { + int k = -7, iy; + if constexpr (int16xN_tag.MaxBlocks() >= 3) { + k = WarpHorizontalFilterOutOfBoundsSetLoop(int16xN_tag, ref, height, stride, + p_height, i, iy4, const4, const5, + offset, k, horz_out); + } + if constexpr (int16xN_tag.MaxBlocks() >= 2) { + k = WarpHorizontalFilterOutOfBoundsSetLoop(int16x16_tag, ref, height, + stride, p_height, i, iy4, const4, + const5, offset, k, horz_out); + } + if constexpr (int16xN_tag.MaxBlocks() == 1) { + k = WarpHorizontalFilterOutOfBoundsSetLoop(int16x8_tag, ref, height, stride, + p_height, i, iy4, const4, const5, + offset, k, horz_out); + } + iy = iy4 + k; + iy = clamp(iy4 + k, 0, height - 1); + hn::Store(hn::Set(int16x8_tag, const4 + ref[iy * stride + offset] * const5), + int16x8_tag, horz_out + (k + 7) * hn::MaxLanes(int16x8_tag)); +} + +template <typename D> +HWY_ATTR int WarpHorizontalFilterOutOfBoundsPadLoop( + D tag, const uint8_t *HWY_RESTRICT ref, int stride, int32_t ix4, + int32_t iy4, int32_t sx4, int alpha, int beta, int p_height, int height, + int i, const IVec16 round_const, const int reduce_bits_horiz, + int out_of_boundary_left, int out_of_boundary_right, int k, + int16_t *HWY_RESTRICT horz_out) { + constexpr int kNumRows = tag.MaxBlocks(); + for (; k < (AOMMIN(8, p_height - i) - kNumRows); k += kNumRows) { + auto src = LoadRowsClamped(tag, ref + ix4 - 7, stride, iy4 + k, height); + if (out_of_boundary_left >= 0) { + const auto shuffle_reg_left = + hn::LoadDup128(tag, warp_pad_left[out_of_boundary_left]); + src = hn::TableLookupBytes(src, shuffle_reg_left); + } + if (out_of_boundary_right >= 0) { + const auto shuffle_reg_right = + hn::LoadDup128(tag, warp_pad_right[out_of_boundary_right]); + src = hn::TableLookupBytes(src, shuffle_reg_right); + } + int sx = sx4 + beta * (k + 4); + HorizontalFilter(tag, src, horz_out, sx, alpha, beta, k + 7, round_const, + reduce_bits_horiz); + } + return k; +} + +HWY_ATTR void WarpHorizontalFilterOutOfBoundsPad( + const uint8_t *HWY_RESTRICT ref, int stride, int32_t ix4, int32_t iy4, + int32_t sx4, int alpha, int beta, int p_height, int width, int height, + int i, const IVec16 round_const, const int reduce_bits_horiz, + int16_t *HWY_RESTRICT horz_out) { + const int out_of_boundary_left = -(ix4 - 6); + const int out_of_boundary_right = (ix4 + 8) - width; + int k = -7, iy, sx; + if constexpr (uint8xN_tag.MaxBlocks() >= 3) { + k = WarpHorizontalFilterOutOfBoundsPadLoop( + uint8xN_tag, ref, stride, ix4, iy4, sx4, alpha, beta, p_height, height, + i, round_const, reduce_bits_horiz, out_of_boundary_left, + out_of_boundary_right, k, horz_out); + } + if constexpr (uint8xN_tag.MaxBlocks() >= 2) { + k = WarpHorizontalFilterOutOfBoundsPadLoop( + uint8x32_tag, ref, stride, ix4, iy4, sx4, alpha, beta, p_height, height, + i, round_const, reduce_bits_horiz, out_of_boundary_left, + out_of_boundary_right, k, horz_out); + } + if constexpr (uint8xN_tag.MaxBlocks() == 1) { + k = WarpHorizontalFilterOutOfBoundsPadLoop( + uint8xN_tag, ref, stride, ix4, iy4, sx4, alpha, beta, p_height, height, + i, round_const, reduce_bits_horiz, out_of_boundary_left, + out_of_boundary_right, k, horz_out); + } + iy = iy4 + k; + iy = clamp(iy, 0, height - 1); + auto src = hn::LoadU(uint8x16_tag, ref + iy * stride + ix4 - 7); + if (out_of_boundary_left >= 0) { + const auto shuffle_reg_left = + hn::LoadU(uint8x16_tag, warp_pad_left[out_of_boundary_left]); + src = hn::TableLookupBytes(src, shuffle_reg_left); + } + if (out_of_boundary_right >= 0) { + const auto shuffle_reg_right = + hn::LoadU(uint8x16_tag, warp_pad_right[out_of_boundary_right]); + src = hn::TableLookupBytes(src, shuffle_reg_right); + } + sx = sx4 + beta * (k + 4); + HWY_ALIGN int8_t coeff[4 * hn::MaxLanes(coeff_tag)]; + PrepareLastHorizontalFilterCoefficients(int16xN_tag, alpha, beta, sx, coeff); + FilterPixelsHorizontal(uint8x16_tag, src, horz_out, coeff, round_const, + reduce_bits_horiz, k + 7); +} + +HWY_ATTR void WarpAffine(const int32_t *HWY_RESTRICT mat, + const uint8_t *HWY_RESTRICT ref, int width, int height, + int stride, uint8_t *HWY_RESTRICT pred, int p_col, + int p_row, int p_width, int p_height, int p_stride, + int subsampling_x, int subsampling_y, + ConvolveParams *HWY_RESTRICT conv_params, + int16_t alpha, int16_t beta, int16_t gamma, + int16_t delta) { + int i, j; + const int bd = 8; + const int reduce_bits_horiz = conv_params->round_0; + const int reduce_bits_vert = conv_params->is_compound + ? conv_params->round_1 + : 2 * FILTER_BITS - reduce_bits_horiz; + const int offset_bits_horiz = bd + FILTER_BITS - 1; + assert(IMPLIES(conv_params->is_compound, conv_params->dst != NULL)); + + const int offset_bits_vert = bd + 2 * FILTER_BITS - reduce_bits_horiz; + const auto reduce_bits_vert_const = + hn::Set(int32xN_tag, ((1 << reduce_bits_vert) >> 1)); + const auto res_add_const = hn::Set(int32xN_tag, 1 << offset_bits_vert); + const int round_bits = + 2 * FILTER_BITS - conv_params->round_0 - conv_params->round_1; + const int offset_bits = bd + 2 * FILTER_BITS - conv_params->round_0; + assert(IMPLIES(conv_params->do_average, conv_params->is_compound)); + + const auto round_const = hn::Set( + int16xN_tag, (1 << offset_bits_horiz) + ((1 << reduce_bits_horiz) >> 1)); + + IVec16 res_sub_const, round_bits_const, wt; + UnpackWeightsAndSetRoundConst(conv_params, round_bits, offset_bits, + res_sub_const, round_bits_const, wt); + + IVec32 res_add_const_1; + if (conv_params->is_compound == 1) { + res_add_const_1 = hn::Add(reduce_bits_vert_const, res_add_const); + } else { + res_add_const_1 = hn::Set(int32xN_tag, -(1 << (bd + reduce_bits_vert - 1)) + + ((1 << reduce_bits_vert) >> 1)); + } + const int32_t const1 = alpha * (-4) + beta * (-4) + + (1 << (WARPEDDIFF_PREC_BITS - 1)) + + (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS); + const int32_t const2 = gamma * (-4) + delta * (-4) + + (1 << (WARPEDDIFF_PREC_BITS - 1)) + + (WARPEDPIXEL_PREC_SHIFTS << WARPEDDIFF_PREC_BITS); + const int32_t const3 = ((1 << WARP_PARAM_REDUCE_BITS) - 1); + const int16_t const4 = (1 << (bd + FILTER_BITS - reduce_bits_horiz - 1)); + const int16_t const5 = (1 << (FILTER_BITS - reduce_bits_horiz)); + + for (i = 0; i < p_height; i += 8) { + for (j = 0; j < p_width; j += 8) { + HWY_ALIGN int16_t horz_out[8 * 16 + hn::MaxLanes(int16xN_tag)]; + const int32_t src_x = (p_col + j + 4) << subsampling_x; + const int32_t src_y = (p_row + i + 4) << subsampling_y; + const int64_t dst_x = + (int64_t)mat[2] * src_x + (int64_t)mat[3] * src_y + (int64_t)mat[0]; + const int64_t dst_y = + (int64_t)mat[4] * src_x + (int64_t)mat[5] * src_y + (int64_t)mat[1]; + const int64_t x4 = dst_x >> subsampling_x; + const int64_t y4 = dst_y >> subsampling_y; + + int32_t ix4 = (int32_t)(x4 >> WARPEDMODEL_PREC_BITS); + int32_t sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + int32_t iy4 = (int32_t)(y4 >> WARPEDMODEL_PREC_BITS); + int32_t sy4 = y4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + + // Add in all the constant terms, including rounding and offset + sx4 += const1; + sy4 += const2; + + sx4 &= ~const3; + sy4 &= ~const3; + + // Horizontal filter + // If the block is aligned such that, after clamping, every sample + // would be taken from the leftmost/rightmost column, then we can + // skip the expensive horizontal filter. + + if (ix4 <= -7) { + WarpHorizontalFilterOutOfBoundsSet(ref, height, stride, p_height, i, + iy4, const4, const5, 0, horz_out); + } else if (ix4 >= width + 6) { + WarpHorizontalFilterOutOfBoundsSet(ref, height, stride, p_height, i, + iy4, const4, const5, width - 1, + horz_out); + } else if (((ix4 - 7) < 0) || ((ix4 + 9) > width)) { + WarpHorizontalFilterOutOfBoundsPad( + ref, stride, ix4, iy4, sx4, alpha, beta, p_height, width, height, i, + round_const, reduce_bits_horiz, horz_out); + } else { + PrepareWarpHorizontalFilter(ref, horz_out, stride, ix4, iy4, sx4, alpha, + beta, p_height, height, i, round_const, + reduce_bits_horiz); + } + + // Vertical filter + PrepareWarpVerticalFilter(pred, horz_out, conv_params, gamma, delta, + p_height, p_stride, p_width, i, j, sy4, + reduce_bits_vert, res_add_const_1, round_bits, + res_sub_const, round_bits_const, wt); + } + } +} + +} // namespace HWY_NAMESPACE +} // namespace + +#define MAKE_WARP_AFFINE(suffix) \ + extern "C" void av1_warp_affine_##suffix( \ + const int32_t *HWY_RESTRICT mat, const uint8_t *HWY_RESTRICT ref, \ + int width, int height, int stride, uint8_t *HWY_RESTRICT pred, \ + int p_col, int p_row, int p_width, int p_height, int p_stride, \ + int subsampling_x, int subsampling_y, \ + ConvolveParams *HWY_RESTRICT conv_params, int16_t alpha, int16_t beta, \ + int16_t gamma, int16_t delta); \ + HWY_ATTR void av1_warp_affine_##suffix( \ + const int32_t *HWY_RESTRICT mat, const uint8_t *HWY_RESTRICT ref, \ + int width, int height, int stride, uint8_t *HWY_RESTRICT pred, \ + int p_col, int p_row, int p_width, int p_height, int p_stride, \ + int subsampling_x, int subsampling_y, \ + ConvolveParams *HWY_RESTRICT conv_params, int16_t alpha, int16_t beta, \ + int16_t gamma, int16_t delta) { \ + HWY_NAMESPACE::WarpAffine(mat, ref, width, height, stride, pred, p_col, \ + p_row, p_width, p_height, p_stride, \ + subsampling_x, subsampling_y, conv_params, \ + alpha, beta, gamma, delta); \ + } + +HWY_AFTER_NAMESPACE(); + +#endif // AOM_AV1_COMMON_WARP_PLANE_HWY_H_
diff --git a/av1/common/x86/av1_inv_txfm_avx2.c b/av1/common/x86/av1_inv_txfm_avx2.c index edee095..78ea98a 100644 --- a/av1/common/x86/av1_inv_txfm_avx2.c +++ b/av1/common/x86/av1_inv_txfm_avx2.c
@@ -9,6 +9,8 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <assert.h> + #include "config/aom_config.h" #include "config/av1_rtcd.h" @@ -2226,16 +2228,8 @@ lowbd_inv_txfm2d_add_8x8_avx2(input, output, stride, tx_type, tx_size, eob); break; - case TX_16X16: - case TX_32X32: - case TX_64X64: - case TX_16X32: - case TX_32X16: - case TX_32X64: - case TX_64X32: - case TX_16X64: - case TX_64X16: default: + assert(tx_size < TX_SIZES_ALL); lowbd_inv_txfm2d_add_universe_avx2(input, output, stride, tx_type, tx_size, eob); break;
diff --git a/av1/common/x86/convolve_2d_avx2.c b/av1/common/x86/convolve_2d_avx2.c index 521326c..be53424 100644 --- a/av1/common/x86/convolve_2d_avx2.c +++ b/av1/common/x86/convolve_2d_avx2.c
@@ -13,21 +13,81 @@ #include "config/av1_rtcd.h" -#if CONFIG_SVT_AV1 -#include "third_party/SVT-AV1/convolve_2d_avx2.h" -#endif - #include "aom_dsp/x86/convolve_avx2.h" #include "aom_dsp/aom_filter.h" #include "aom_dsp/x86/synonyms.h" #include "av1/common/convolve.h" -static void convolve_2d_sr_general_avx2( - const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int w, - int h, const InterpFilterParams *filter_params_x, - const InterpFilterParams *filter_params_y, const int subpel_x_qn, - const int subpel_y_qn, ConvolveParams *conv_params) { +static void convolve_2d_sr_w4_avx2( + const uint8_t *src, int32_t src_stride, uint8_t *dst, int32_t dst_stride, + int32_t w, int32_t h, const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, const int32_t subpel_x_qn, + const int32_t subpel_y_qn, ConvolveParams *conv_params) { + int i; + DECLARE_ALIGNED(32, int16_t, im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * 4]); + uint8_t *dst_ptr = dst; + assert(conv_params->round_0 == 3); + assert(conv_params->round_1 == 11); + + const __m128i round_const_h = _mm_set1_epi16(1 << (conv_params->round_0 - 2)); + const __m256i round_const_v = + _mm256_set1_epi32(1 << (conv_params->round_1 - 1)); + + __m128i filt[2], coeffs_h[2] = { 0 }; + __m256i coeffs_v[4] = { 0 }; + + const int horiz_tap = get_filter_tap(filter_params_x, subpel_x_qn); + const int vert_tap = get_filter_tap(filter_params_y, subpel_y_qn); + + assert(horiz_tap == 2 || horiz_tap == 4); + assert(vert_tap == 2 || vert_tap == 4 || vert_tap == 6 || vert_tap == 8); + + if (horiz_tap == 2) + prepare_coeffs_2t_ssse3(filter_params_x, subpel_x_qn, coeffs_h); + else + prepare_coeffs_4t_ssse3(filter_params_x, subpel_x_qn, coeffs_h); + + if (vert_tap == 2) + prepare_coeffs_2t(filter_params_y, subpel_y_qn, coeffs_v); + else if (vert_tap == 4) + prepare_coeffs_4t(filter_params_y, subpel_y_qn, coeffs_v); + else if (vert_tap == 6) + prepare_coeffs_6t(filter_params_y, subpel_y_qn, coeffs_v); + else + prepare_coeffs(filter_params_y, subpel_y_qn, coeffs_v); + + int im_h = h + vert_tap - 1; + const int fo_vert = vert_tap / 2 - 1; + const int fo_horiz = horiz_tap / 2 - 1; + const uint8_t *const src_ptr = src - fo_vert * src_stride - fo_horiz; + + filt[0] = _mm_load_si128((__m128i const *)filt1_global_sse2); + filt[1] = _mm_load_si128((__m128i const *)filt2_global_sse2); + + if (horiz_tap == 2) { + CONVOLVE_SR_HOR_FILTER_2TAP_W4 + } else { + CONVOLVE_SR_HOR_FILTER_4TAP_W4 + } + + if (vert_tap == 2) { + CONVOLVE_SR_VER_FILTER_2TAP_W4 + } else if (vert_tap == 4) { + CONVOLVE_SR_VER_FILTER_4TAP_W4 + } else if (vert_tap == 6) { + CONVOLVE_SR_VER_FILTER_6TAP_W4 + } else { + CONVOLVE_SR_VER_FILTER_8TAP_W4 + } +} + +static void convolve_2d_sr_avx2(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, + const int subpel_x_qn, const int subpel_y_qn, + ConvolveParams *conv_params) { if (filter_params_x->taps > 8) { const int bd = 8; int im_stride = 8, i; @@ -69,43 +129,40 @@ CONVOLVE_SR_VERTICAL_FILTER_12TAP } } else { - const int bd = 8; int im_stride = 8, i; DECLARE_ALIGNED(32, int16_t, im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * 8]); - const int bits = - FILTER_BITS * 2 - conv_params->round_0 - conv_params->round_1; - const int offset_bits = bd + 2 * FILTER_BITS - conv_params->round_0; - assert(conv_params->round_0 > 0); + assert(conv_params->round_0 == 3); + assert(conv_params->round_1 == 11); const __m256i round_const_h = - _mm256_set1_epi16(((1 << (conv_params->round_0 - 1)) >> 1) + - (1 << (bd + FILTER_BITS - 2))); - const __m128i round_shift_h = _mm_cvtsi32_si128(conv_params->round_0 - 1); + _mm256_set1_epi16(1 << (conv_params->round_0 - 2)); + const __m256i round_const_v = + _mm256_set1_epi32(1 << (conv_params->round_1 - 1)); - const __m256i sum_round_v = _mm256_set1_epi32( - (1 << offset_bits) + ((1 << conv_params->round_1) >> 1)); - const __m128i sum_shift_v = _mm_cvtsi32_si128(conv_params->round_1); - - const __m256i round_const_v = _mm256_set1_epi32( - ((1 << bits) >> 1) - (1 << (offset_bits - conv_params->round_1)) - - ((1 << (offset_bits - conv_params->round_1)) >> 1)); - const __m128i round_shift_v = _mm_cvtsi32_si128(bits); - - __m256i filt[4], coeffs_h[4], coeffs_v[4]; - - prepare_coeffs_lowbd(filter_params_x, subpel_x_qn, coeffs_h); - prepare_coeffs(filter_params_y, subpel_y_qn, coeffs_v); + __m256i filt[4], coeffs_h[4] = { 0 }, coeffs_v[4] = { 0 }; int horiz_tap = get_filter_tap(filter_params_x, subpel_x_qn); int vert_tap = get_filter_tap(filter_params_y, subpel_y_qn); - if (horiz_tap == 6) + assert(horiz_tap == 2 || horiz_tap == 4 || horiz_tap == 6 || + horiz_tap == 8); + assert(vert_tap == 2 || vert_tap == 4 || vert_tap == 6 || vert_tap == 8); + + if (horiz_tap == 2) + prepare_coeffs_2t_lowbd(filter_params_x, subpel_x_qn, coeffs_h); + else if (horiz_tap == 4) + prepare_coeffs_4t_lowbd(filter_params_x, subpel_x_qn, coeffs_h); + else if (horiz_tap == 6) prepare_coeffs_6t_lowbd(filter_params_x, subpel_x_qn, coeffs_h); else prepare_coeffs_lowbd(filter_params_x, subpel_x_qn, coeffs_h); - if (vert_tap == 6) + if (vert_tap == 2) + prepare_coeffs_2t(filter_params_y, subpel_y_qn, coeffs_v); + else if (vert_tap == 4) + prepare_coeffs_4t(filter_params_y, subpel_y_qn, coeffs_v); + else if (vert_tap == 6) prepare_coeffs_6t(filter_params_y, subpel_y_qn, coeffs_v); else prepare_coeffs(filter_params_y, subpel_y_qn, coeffs_v); @@ -121,7 +178,9 @@ filt[3] = _mm256_load_si256((__m256i const *)filt4_global_avx2); for (int j = 0; j < w; j += 8) { - if (horiz_tap == 4) { + if (horiz_tap == 2) { + CONVOLVE_SR_HORIZONTAL_FILTER_2TAP + } else if (horiz_tap == 4) { CONVOLVE_SR_HORIZONTAL_FILTER_4TAP } else if (horiz_tap == 6) { CONVOLVE_SR_HORIZONTAL_FILTER_6TAP @@ -129,7 +188,10 @@ CONVOLVE_SR_HORIZONTAL_FILTER_8TAP } - if (vert_tap == 4) { + uint8_t *dst_ptr = dst + j; + if (vert_tap == 2) { + CONVOLVE_SR_VERTICAL_FILTER_2TAP + } else if (vert_tap == 4) { CONVOLVE_SR_VERTICAL_FILTER_4TAP } else if (vert_tap == 6) { CONVOLVE_SR_VERTICAL_FILTER_6TAP @@ -145,23 +207,16 @@ int32_t w, int32_t h, const InterpFilterParams *filter_params_x, const InterpFilterParams *filter_params_y, const int32_t subpel_x_qn, const int32_t subpel_y_qn, ConvolveParams *conv_params) { -#if CONFIG_SVT_AV1 const int32_t tap_x = get_filter_tap(filter_params_x, subpel_x_qn); const int32_t tap_y = get_filter_tap(filter_params_y, subpel_y_qn); - const bool use_general = (tap_x == 12 || tap_y == 12); - if (use_general) { - convolve_2d_sr_general_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_x, filter_params_y, subpel_x_qn, - subpel_y_qn, conv_params); + const bool use_12tap = (tap_x == 12 || tap_y == 12); + if (w <= 4 && !use_12tap) { + convolve_2d_sr_w4_avx2(src, src_stride, dst, dst_stride, w, h, + filter_params_x, filter_params_y, subpel_x_qn, + subpel_y_qn, conv_params); } else { - av1_convolve_2d_sr_specialized_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_x, filter_params_y, - subpel_x_qn, subpel_y_qn, conv_params); + convolve_2d_sr_avx2(src, src_stride, dst, dst_stride, w, h, filter_params_x, + filter_params_y, subpel_x_qn, subpel_y_qn, conv_params); } -#else - convolve_2d_sr_general_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_x, filter_params_y, subpel_x_qn, - subpel_y_qn, conv_params); -#endif }
diff --git a/av1/common/x86/convolve_avx2.c b/av1/common/x86/convolve_avx2.c index d250d88..6b82b5a 100644 --- a/av1/common/x86/convolve_avx2.c +++ b/av1/common/x86/convolve_avx2.c
@@ -13,243 +13,511 @@ #include "config/av1_rtcd.h" -#if CONFIG_SVT_AV1 -#include "third_party/SVT-AV1/convolve_avx2.h" -#endif - #include "aom_dsp/aom_dsp_common.h" #include "aom_dsp/x86/convolve_avx2.h" #include "aom_dsp/x86/convolve_common_intrin.h" #include "aom_dsp/x86/synonyms.h" -static inline void av1_convolve_y_sr_general_avx2( - const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int w, - int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn) { - // right shift is F-1 because we are already dividing - // filter co-efficients by 2 - const int right_shift_bits = (FILTER_BITS - 1); - __m128i right_shift = _mm_cvtsi32_si128(right_shift_bits); - __m256i right_shift_const = _mm256_set1_epi16((1 << right_shift_bits) >> 1); - - __m256i coeffs[6], s[12]; - __m128i d[10]; +void av1_convolve_y_sr_avx2(const uint8_t *src, int32_t src_stride, + uint8_t *dst, int32_t dst_stride, int32_t w, + int32_t h, + const InterpFilterParams *filter_params_y, + const int32_t subpel_y_qn) { + __m128i coeffs_128[4]; + __m256i coeffs[6]; + int x = 0, y = h; int i, vert_tap = get_filter_tap(filter_params_y, subpel_y_qn); + assert(vert_tap == 2 || vert_tap == 4 || vert_tap == 6 || vert_tap == 8 || + vert_tap == 12); + assert(!(w % 2)); + assert(!(h % 2)); - if (vert_tap == 6) - prepare_coeffs_6t_lowbd(filter_params_y, subpel_y_qn, coeffs); - else if (vert_tap == 12) { - prepare_coeffs_12taps(filter_params_y, subpel_y_qn, coeffs); - } else { - prepare_coeffs_lowbd(filter_params_y, subpel_y_qn, coeffs); - } + const int fo_vert = vert_tap / 2 - 1; + const uint8_t *const src_ptr = src - fo_vert * src_stride; + const uint8_t *data = src_ptr; + uint8_t *dst_ptr = dst; - // vert_filt as 4 tap - if (vert_tap == 4) { - const int fo_vert = 1; - const uint8_t *const src_ptr = src - fo_vert * src_stride; - for (int j = 0; j < w; j += 16) { - const uint8_t *data = &src_ptr[j]; - d[0] = _mm_loadu_si128((__m128i *)(data + 0 * src_stride)); - d[1] = _mm_loadu_si128((__m128i *)(data + 1 * src_stride)); - d[2] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); - d[3] = _mm_loadu_si128((__m128i *)(data + 3 * src_stride)); - d[4] = _mm_loadu_si128((__m128i *)(data + 4 * src_stride)); + if (vert_tap == 2) { + if (subpel_y_qn != 8) { + if (w <= 4) { + prepare_coeffs_2t_ssse3(filter_params_y, subpel_y_qn, coeffs_128); + __m128i d[2], res; + if (w == 2) { + d[0] = _mm_cvtsi32_si128(loadu_int16(data)); - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src_01a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[0]), _mm256_castsi128_si256(d[1]), 0x20); + do { + convolve_y_2tap_2x2_ssse3(data, src_stride, coeffs_128, d, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_2x2_sse2(res, dst_ptr, dst_stride); - const __m256i src_12a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[1]), _mm256_castsi128_si256(d[2]), 0x20); - - const __m256i src_23a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[2]), _mm256_castsi128_si256(d[3]), 0x20); - - const __m256i src_34a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[3]), _mm256_castsi128_si256(d[4]), 0x20); - - s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); - s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); - - s[3] = _mm256_unpackhi_epi8(src_01a, src_12a); - s[4] = _mm256_unpackhi_epi8(src_23a, src_34a); - - for (i = 0; i < h; i += 2) { - data = &src_ptr[i * src_stride + j]; - d[5] = _mm_loadu_si128((__m128i *)(data + 5 * src_stride)); - const __m256i src_45a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[4]), _mm256_castsi128_si256(d[5]), 0x20); - - d[4] = _mm_loadu_si128((__m128i *)(data + 6 * src_stride)); - const __m256i src_56a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[5]), _mm256_castsi128_si256(d[4]), 0x20); - - s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); - s[5] = _mm256_unpackhi_epi8(src_45a, src_56a); - - const __m256i res_lo = convolve_lowbd_4tap(s, coeffs + 1); - /* rounding code */ - // shift by F - 1 - const __m256i res_16b_lo = _mm256_sra_epi16( - _mm256_add_epi16(res_lo, right_shift_const), right_shift); - // 8 bit conversion and saturation to uint8 - __m256i res_8b_lo = _mm256_packus_epi16(res_16b_lo, res_16b_lo); - - if (w - j > 8) { - const __m256i res_hi = convolve_lowbd_4tap(s + 3, coeffs + 1); - - /* rounding code */ - // shift by F - 1 - const __m256i res_16b_hi = _mm256_sra_epi16( - _mm256_add_epi16(res_hi, right_shift_const), right_shift); - // 8 bit conversion and saturation to uint8 - __m256i res_8b_hi = _mm256_packus_epi16(res_16b_hi, res_16b_hi); - - __m256i res_a = _mm256_unpacklo_epi64(res_8b_lo, res_8b_hi); - - const __m128i res_0 = _mm256_castsi256_si128(res_a); - const __m128i res_1 = _mm256_extracti128_si256(res_a, 1); - - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_0); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j + dst_stride], - res_1); + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + } while (y > 0); } else { - const __m128i res_0 = _mm256_castsi256_si128(res_8b_lo); - const __m128i res_1 = _mm256_extracti128_si256(res_8b_lo, 1); - if (w - j > 4) { - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j + dst_stride], - res_1); - } else if (w - j > 2) { - xx_storel_32(&dst[i * dst_stride + j], res_0); - xx_storel_32(&dst[i * dst_stride + j + dst_stride], res_1); - } else { - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride + j]; - __m128i *const p_1 = - (__m128i *)&dst[i * dst_stride + j + dst_stride]; - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); - } - } - s[0] = s[1]; - s[1] = s[2]; + assert(w == 4); + d[0] = _mm_cvtsi32_si128(loadu_int32(data)); - s[3] = s[4]; - s[4] = s[5]; + do { + convolve_y_2tap_4x2_ssse3(data, src_stride, coeffs_128, d, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_4x2_sse2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + } while (y > 0); + } + } else { + prepare_coeffs_2t_lowbd(filter_params_y, subpel_y_qn, coeffs); + + if (w == 8) { + __m128i d[2]; + d[0] = _mm_loadl_epi64((__m128i *)data); + + do { + __m256i res; + convolve_y_2tap_8x2_avx2(data, src_stride, coeffs, d, &res); + round_pack_store_y_8x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + } while (y > 0); + + } else if (w == 16) { + __m128i d[2]; + d[0] = _mm_loadu_si128((__m128i *)data); + + do { + __m256i res[2]; + convolve_y_2tap_16x2_avx2(data, src_stride, coeffs, d, res); + round_pack_store_y_16x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + } while (y > 0); + + } else { + assert(!(w % 32)); + + __m256i d[2]; + do { + data = src_ptr + x; + dst_ptr = dst + x; + y = h; + + d[0] = _mm256_loadu_si256((__m256i *)data); + + do { + __m256i res[4]; + convolve_y_2tap_32x2_avx2(data, src_stride, coeffs, d, res); + round_pack_store_y_32x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + } while (y > 0); + + x += 32; + } while (x < w); + } + } + } else { + if (w <= 16) { + __m128i s[2], res; + + if (w == 2) { + s[0] = _mm_cvtsi32_si128(loadu_int16(data)); + + do { + s[1] = _mm_cvtsi32_si128(loadu_int16(data + src_stride)); + res = _mm_avg_epu8(s[0], s[1]); + xx_storel_16(dst_ptr, res); + s[0] = _mm_cvtsi32_si128(loadu_int16(data + 2 * src_stride)); + res = _mm_avg_epu8(s[1], s[0]); + xx_storel_16(dst_ptr + dst_stride, res); + + data += 2 * src_stride; + dst_ptr += 2 * dst_stride; + y -= 2; + } while (y > 0); + } else if (w == 4) { + s[0] = _mm_cvtsi32_si128(loadu_int32(data)); + + do { + s[1] = _mm_cvtsi32_si128(loadu_int32(data + src_stride)); + res = _mm_avg_epu8(s[0], s[1]); + xx_storel_32(dst_ptr, res); + s[0] = _mm_cvtsi32_si128(loadu_int32(data + 2 * src_stride)); + res = _mm_avg_epu8(s[1], s[0]); + xx_storel_32(dst_ptr + dst_stride, res); + + data += 2 * src_stride; + dst_ptr += 2 * dst_stride; + y -= 2; + } while (y > 0); + } else if (w == 8) { + s[0] = _mm_loadl_epi64((__m128i *)data); + + do { + s[1] = _mm_loadl_epi64((__m128i *)(data + src_stride)); + res = _mm_avg_epu8(s[0], s[1]); + _mm_storel_epi64((__m128i *)dst_ptr, res); + s[0] = _mm_loadl_epi64((__m128i *)(data + 2 * src_stride)); + res = _mm_avg_epu8(s[1], s[0]); + _mm_storel_epi64((__m128i *)(dst_ptr + dst_stride), res); + + data += 2 * src_stride; + dst_ptr += 2 * dst_stride; + y -= 2; + } while (y > 0); + } else { + assert(w == 16); + + s[0] = _mm_loadu_si128((__m128i *)data); + + do { + s[1] = _mm_loadu_si128((__m128i *)(data + src_stride)); + res = _mm_avg_epu8(s[0], s[1]); + _mm_storeu_si128((__m128i *)dst_ptr, res); + s[0] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); + res = _mm_avg_epu8(s[1], s[0]); + _mm_storeu_si128((__m128i *)(dst_ptr + dst_stride), res); + + data += 2 * src_stride; + dst_ptr += 2 * dst_stride; + y -= 2; + } while (y > 0); + } + } else { + assert(!(w % 32)); + + __m256i s[2], res; + do { + data = src_ptr + x; + dst_ptr = dst + x; + y = h; + + s[0] = _mm256_loadu_si256((__m256i *)data); + + do { + s[1] = _mm256_loadu_si256((__m256i *)(data + src_stride)); + res = _mm256_avg_epu8(s[0], s[1]); + _mm256_storeu_si256((__m256i *)dst_ptr, res); + s[0] = _mm256_loadu_si256((__m256i *)(data + 2 * src_stride)); + res = _mm256_avg_epu8(s[1], s[0]); + _mm256_storeu_si256((__m256i *)(dst_ptr + dst_stride), res); + + data += 2 * src_stride; + dst_ptr += 2 * dst_stride; + y -= 2; + } while (y > 0); + + x += 32; + } while (x < w); + } + } + } else if (vert_tap == 4) { + if (w <= 4) { + prepare_coeffs_4t_ssse3(filter_params_y, subpel_y_qn, coeffs_128); + __m128i d[4], s[2]; + + if (w == 2) { + d[0] = _mm_cvtsi32_si128(loadu_int16(data + 0 * src_stride)); + d[1] = _mm_cvtsi32_si128(loadu_int16(data + 1 * src_stride)); + d[2] = _mm_cvtsi32_si128(loadu_int16(data + 2 * src_stride)); + + const __m128i src_01a = _mm_unpacklo_epi16(d[0], d[1]); + const __m128i src_12a = _mm_unpacklo_epi16(d[1], d[2]); + + s[0] = _mm_unpacklo_epi8(src_01a, src_12a); + do { + __m128i res; + convolve_y_4tap_2x2_ssse3(data, src_stride, coeffs_128, d, s, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_2x2_sse2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + } while (y > 0); + + } else { + assert(w == 4); + + d[0] = _mm_cvtsi32_si128(loadu_int32(data + 0 * src_stride)); + d[1] = _mm_cvtsi32_si128(loadu_int32(data + 1 * src_stride)); + d[2] = _mm_cvtsi32_si128(loadu_int32(data + 2 * src_stride)); + + const __m128i src_01a = _mm_unpacklo_epi32(d[0], d[1]); + const __m128i src_12a = _mm_unpacklo_epi32(d[1], d[2]); + + s[0] = _mm_unpacklo_epi8(src_01a, src_12a); + do { + __m128i res; + convolve_y_4tap_4x2_ssse3(data, src_stride, coeffs_128, d, s, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_4x2_sse2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + } while (y > 0); + } + } else { + prepare_coeffs_4t_lowbd(filter_params_y, subpel_y_qn, coeffs); + + if (w == 8) { + __m128i d[4]; + __m256i s[2]; + + d[0] = _mm_loadl_epi64((__m128i *)(data + 0 * src_stride)); + d[1] = _mm_loadl_epi64((__m128i *)(data + 1 * src_stride)); + d[2] = _mm_loadl_epi64((__m128i *)(data + 2 * src_stride)); + + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[2]); + + s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); + do { + __m256i res; + convolve_y_4tap_8x2_avx2(data, src_stride, coeffs, d, s, &res); + round_pack_store_y_8x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + } while (y > 0); + } else if (w == 16) { + __m128i d[4]; + __m256i s[4]; + + d[0] = _mm_loadu_si128((__m128i *)(data + 0 * src_stride)); + d[1] = _mm_loadu_si128((__m128i *)(data + 1 * src_stride)); + d[2] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); + + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[2]); + + s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); + s[2] = _mm256_unpackhi_epi8(src_01a, src_12a); + + do { + __m256i res[2]; + convolve_y_4tap_16x2_avx2(data, src_stride, coeffs, d, s, res); + round_pack_store_y_16x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + s[2] = s[3]; + } while (y > 0); + } else { + assert(!(w % 32)); + + __m256i d[4], s1[4], s2[4]; + do { + data = src_ptr + x; + dst_ptr = dst + x; + y = h; + + d[0] = _mm256_loadu_si256((__m256i *)(data + 0 * src_stride)); + d[1] = _mm256_loadu_si256((__m256i *)(data + 1 * src_stride)); + d[2] = _mm256_loadu_si256((__m256i *)(data + 2 * src_stride)); + + s1[0] = _mm256_unpacklo_epi8(d[0], d[1]); + s1[2] = _mm256_unpackhi_epi8(d[0], d[1]); + + s2[0] = _mm256_unpacklo_epi8(d[1], d[2]); + s2[2] = _mm256_unpackhi_epi8(d[1], d[2]); + + do { + __m256i res[4]; + convolve_y_4tap_32x2_avx2(data, src_stride, coeffs, d, s1, s2, res); + round_pack_store_y_32x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s1[0] = s1[1]; + s1[2] = s1[3]; + + s2[0] = s2[1]; + s2[2] = s2[3]; + } while (y > 0); + + x += 32; + } while (x < w); } } } else if (vert_tap == 6) { - const int fo_vert = vert_tap / 2 - 1; - const uint8_t *const src_ptr = src - fo_vert * src_stride; + if (w <= 4) { + prepare_coeffs_6t_ssse3(filter_params_y, subpel_y_qn, coeffs_128); - for (int j = 0; j < w; j += 16) { - const uint8_t *data = &src_ptr[j]; - __m256i src6; + __m128i d[6], s[3]; + if (w == 2) { + d[0] = _mm_cvtsi32_si128(loadu_int16(data + 0 * src_stride)); + d[1] = _mm_cvtsi32_si128(loadu_int16(data + 1 * src_stride)); + d[2] = _mm_cvtsi32_si128(loadu_int16(data + 2 * src_stride)); + d[3] = _mm_cvtsi32_si128(loadu_int16(data + 3 * src_stride)); + d[4] = _mm_cvtsi32_si128(loadu_int16(data + 4 * src_stride)); - d[0] = _mm_loadu_si128((__m128i *)(data + 0 * src_stride)); - d[1] = _mm_loadu_si128((__m128i *)(data + 1 * src_stride)); - d[2] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); - d[3] = _mm_loadu_si128((__m128i *)(data + 3 * src_stride)); - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src_01a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[0]), _mm256_castsi128_si256(d[1]), 0x20); + const __m128i src_01a = _mm_unpacklo_epi16(d[0], d[1]); + const __m128i src_12a = _mm_unpacklo_epi16(d[1], d[2]); + const __m128i src_23a = _mm_unpacklo_epi16(d[2], d[3]); + const __m128i src_34a = _mm_unpacklo_epi16(d[3], d[4]); - const __m256i src_12a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[1]), _mm256_castsi128_si256(d[2]), 0x20); + s[0] = _mm_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm_unpacklo_epi8(src_23a, src_34a); - const __m256i src_23a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[2]), _mm256_castsi128_si256(d[3]), 0x20); + do { + __m128i res; + convolve_y_6tap_2x2_ssse3(data, src_stride, coeffs_128, d, s, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_2x2_sse2(res, dst_ptr, dst_stride); - src6 = _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 4 * src_stride))); - const __m256i src_34a = - _mm256_permute2x128_si256(_mm256_castsi128_si256(d[3]), src6, 0x20); + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; - s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); - s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); + s[0] = s[1]; + s[1] = s[2]; + } while (y > 0); - s[3] = _mm256_unpackhi_epi8(src_01a, src_12a); - s[4] = _mm256_unpackhi_epi8(src_23a, src_34a); + } else { + assert(w == 4); + d[0] = _mm_cvtsi32_si128(loadu_int32(data + 0 * src_stride)); + d[1] = _mm_cvtsi32_si128(loadu_int32(data + 1 * src_stride)); + d[2] = _mm_cvtsi32_si128(loadu_int32(data + 2 * src_stride)); + d[3] = _mm_cvtsi32_si128(loadu_int32(data + 3 * src_stride)); + d[4] = _mm_cvtsi32_si128(loadu_int32(data + 4 * src_stride)); - for (i = 0; i < h; i += 2) { - data = &src_ptr[i * src_stride + j]; - const __m256i src_45a = _mm256_permute2x128_si256( - src6, - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 5 * src_stride))), - 0x20); + const __m128i src_01a = _mm_unpacklo_epi32(d[0], d[1]); + const __m128i src_12a = _mm_unpacklo_epi32(d[1], d[2]); + const __m128i src_23a = _mm_unpacklo_epi32(d[2], d[3]); + const __m128i src_34a = _mm_unpacklo_epi32(d[3], d[4]); - src6 = _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 6 * src_stride))); - const __m256i src_56a = _mm256_permute2x128_si256( - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 5 * src_stride))), - src6, 0x20); + s[0] = _mm_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm_unpacklo_epi8(src_23a, src_34a); - s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); - s[5] = _mm256_unpackhi_epi8(src_45a, src_56a); + do { + __m128i res; + convolve_y_6tap_4x2_ssse3(data, src_stride, coeffs_128, d, s, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_4x2_sse2(res, dst_ptr, dst_stride); - const __m256i res_lo = convolve_lowbd_6tap(s, coeffs); + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; - /* rounding code */ - // shift by F - 1 - const __m256i res_16b_lo = _mm256_sra_epi16( - _mm256_add_epi16(res_lo, right_shift_const), right_shift); - // 8 bit conversion and saturation to uint8 - __m256i res_8b_lo = _mm256_packus_epi16(res_16b_lo, res_16b_lo); + s[0] = s[1]; + s[1] = s[2]; + } while (y > 0); + } + } else { + prepare_coeffs_6t_lowbd(filter_params_y, subpel_y_qn, coeffs); - if (w - j > 8) { - const __m256i res_hi = convolve_lowbd_6tap(s + 3, coeffs); + if (w == 8) { + __m128i d[6]; + __m256i s[3]; - /* rounding code */ - // shift by F - 1 - const __m256i res_16b_hi = _mm256_sra_epi16( - _mm256_add_epi16(res_hi, right_shift_const), right_shift); - // 8 bit conversion and saturation to uint8 - __m256i res_8b_hi = _mm256_packus_epi16(res_16b_hi, res_16b_hi); + d[0] = _mm_loadl_epi64((__m128i *)(data + 0 * src_stride)); + d[1] = _mm_loadl_epi64((__m128i *)(data + 1 * src_stride)); + d[2] = _mm_loadl_epi64((__m128i *)(data + 2 * src_stride)); + d[3] = _mm_loadl_epi64((__m128i *)(data + 3 * src_stride)); + d[4] = _mm_loadl_epi64((__m128i *)(data + 4 * src_stride)); - __m256i res_a = _mm256_unpacklo_epi64(res_8b_lo, res_8b_hi); + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[2]); + const __m256i src_23a = _mm256_setr_m128i(d[2], d[3]); + const __m256i src_34a = _mm256_setr_m128i(d[3], d[4]); - const __m128i res_0 = _mm256_castsi256_si128(res_a); - const __m128i res_1 = _mm256_extracti128_si256(res_a, 1); + s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_0); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j + dst_stride], - res_1); - } else { - const __m128i res_0 = _mm256_castsi256_si128(res_8b_lo); - const __m128i res_1 = _mm256_extracti128_si256(res_8b_lo, 1); - if (w - j > 4) { - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j + dst_stride], - res_1); - } else if (w - j > 2) { - xx_storel_32(&dst[i * dst_stride + j], res_0); - xx_storel_32(&dst[i * dst_stride + j + dst_stride], res_1); - } else { - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride + j]; - __m128i *const p_1 = - (__m128i *)&dst[i * dst_stride + j + dst_stride]; - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); - } - } - s[0] = s[1]; - s[1] = s[2]; - s[3] = s[4]; - s[4] = s[5]; + do { + __m256i res; + convolve_y_6tap_8x2_avx2(data, src_stride, coeffs, d, s, &res); + round_pack_store_y_8x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + s[1] = s[2]; + } while (y > 0); + + } else { + assert(!(w % 16)); + + __m128i d[6]; + __m256i s[6]; + do { + data = src_ptr + x; + dst_ptr = dst + x; + y = h; + + d[0] = _mm_loadu_si128((__m128i *)(data + 0 * src_stride)); + d[1] = _mm_loadu_si128((__m128i *)(data + 1 * src_stride)); + d[2] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); + d[3] = _mm_loadu_si128((__m128i *)(data + 3 * src_stride)); + d[4] = _mm_loadu_si128((__m128i *)(data + 4 * src_stride)); + + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[2]); + const __m256i src_23a = _mm256_setr_m128i(d[2], d[3]); + const __m256i src_34a = _mm256_setr_m128i(d[3], d[4]); + + s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); + + s[3] = _mm256_unpackhi_epi8(src_01a, src_12a); + s[4] = _mm256_unpackhi_epi8(src_23a, src_34a); + + do { + __m256i res[2]; + convolve_y_6tap_16x2_avx2(data, src_stride, coeffs, d, s, res); + round_pack_store_y_16x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + s[1] = s[2]; + + s[3] = s[4]; + s[4] = s[5]; + } while (y > 0); + + x += 16; + } while (x < w); } } } else if (vert_tap == 12) { // vert_tap == 12 - const int fo_vert = filter_params_y->taps / 2 - 1; - const uint8_t *const src_ptr = src - fo_vert * src_stride; + __m128i d[12]; + __m256i s[12]; + prepare_coeffs_12taps(filter_params_y, subpel_y_qn, coeffs); const __m256i v_zero = _mm256_setzero_si256(); - right_shift = _mm_cvtsi32_si128(FILTER_BITS); - right_shift_const = _mm256_set1_epi32((1 << FILTER_BITS) >> 1); + __m128i right_shift = _mm_cvtsi32_si128(FILTER_BITS); + __m256i right_shift_const = _mm256_set1_epi32((1 << FILTER_BITS) >> 1); for (int j = 0; j < w; j += 8) { - const uint8_t *data = &src_ptr[j]; + data = &src_ptr[j]; __m256i src10; d[0] = _mm_loadl_epi64((__m128i *)(data + 0 * src_stride)); @@ -369,14 +637,11 @@ const __m128i res_0 = _mm256_extracti128_si256(res_8b_lo, 0); const __m128i res_1 = _mm256_extracti128_si256(res_8b_lo, 1); if (w - j > 2) { - *(int *)&dst[i * dst_stride + j] = _mm_cvtsi128_si32(res_0); - *(int *)&dst[i * dst_stride + j + dst_stride] = - _mm_cvtsi128_si32(res_1); + xx_storel_32(&dst[i * dst_stride + j], res_0); + xx_storel_32(&dst[i * dst_stride + j + dst_stride], res_1); } else { - *(uint16_t *)&dst[i * dst_stride + j] = - (uint16_t)_mm_cvtsi128_si32(res_0); - *(uint16_t *)&dst[i * dst_stride + j + dst_stride] = - (uint16_t)_mm_cvtsi128_si32(res_1); + xx_storel_16(&dst[i * dst_stride + j], res_0); + xx_storel_16(&dst[i * dst_stride + j + dst_stride], res_1); } } s[0] = s[1]; @@ -393,193 +658,242 @@ } } } else { - const int fo_vert = filter_params_y->taps / 2 - 1; - const uint8_t *const src_ptr = src - fo_vert * src_stride; + assert(vert_tap == 8); - for (int j = 0; j < w; j += 16) { - const uint8_t *data = &src_ptr[j]; - __m256i src6; + if (w <= 4) { + prepare_coeffs_ssse3(filter_params_y, subpel_y_qn, coeffs_128); - d[0] = _mm_loadu_si128((__m128i *)(data + 0 * src_stride)); - d[1] = _mm_loadu_si128((__m128i *)(data + 1 * src_stride)); - d[2] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); - d[3] = _mm_loadu_si128((__m128i *)(data + 3 * src_stride)); - d[4] = _mm_loadu_si128((__m128i *)(data + 4 * src_stride)); - d[5] = _mm_loadu_si128((__m128i *)(data + 5 * src_stride)); - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src_01a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[0]), _mm256_castsi128_si256(d[1]), 0x20); + __m128i d[8], s[4], res; + if (w == 2) { + d[0] = _mm_cvtsi32_si128(loadu_int16(data + 0 * src_stride)); + d[1] = _mm_cvtsi32_si128(loadu_int16(data + 1 * src_stride)); + d[2] = _mm_cvtsi32_si128(loadu_int16(data + 2 * src_stride)); + d[3] = _mm_cvtsi32_si128(loadu_int16(data + 3 * src_stride)); + d[4] = _mm_cvtsi32_si128(loadu_int16(data + 4 * src_stride)); + d[5] = _mm_cvtsi32_si128(loadu_int16(data + 5 * src_stride)); + d[6] = _mm_cvtsi32_si128(loadu_int16(data + 6 * src_stride)); - const __m256i src_12a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[1]), _mm256_castsi128_si256(d[2]), 0x20); + const __m128i src_01a = _mm_unpacklo_epi16(d[0], d[1]); + const __m128i src_12a = _mm_unpacklo_epi16(d[1], d[2]); + const __m128i src_23a = _mm_unpacklo_epi16(d[2], d[3]); + const __m128i src_34a = _mm_unpacklo_epi16(d[3], d[4]); + const __m128i src_45a = _mm_unpacklo_epi16(d[4], d[5]); + const __m128i src_56a = _mm_unpacklo_epi16(d[5], d[6]); - const __m256i src_23a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[2]), _mm256_castsi128_si256(d[3]), 0x20); + s[0] = _mm_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm_unpacklo_epi8(src_23a, src_34a); + s[2] = _mm_unpacklo_epi8(src_45a, src_56a); - const __m256i src_34a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[3]), _mm256_castsi128_si256(d[4]), 0x20); + do { + convolve_y_8tap_2x2_ssse3(data, src_stride, coeffs_128, d, s, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_2x2_sse2(res, dst_ptr, dst_stride); - const __m256i src_45a = _mm256_permute2x128_si256( - _mm256_castsi128_si256(d[4]), _mm256_castsi128_si256(d[5]), 0x20); + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; - src6 = _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 6 * src_stride))); - const __m256i src_56a = - _mm256_permute2x128_si256(_mm256_castsi128_si256(d[5]), src6, 0x20); + s[0] = s[1]; + s[1] = s[2]; + s[2] = s[3]; + } while (y > 0); - s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); - s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); - s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); + } else { + assert(w == 4); - s[4] = _mm256_unpackhi_epi8(src_01a, src_12a); - s[5] = _mm256_unpackhi_epi8(src_23a, src_34a); - s[6] = _mm256_unpackhi_epi8(src_45a, src_56a); + d[0] = _mm_cvtsi32_si128(loadu_int32(data + 0 * src_stride)); + d[1] = _mm_cvtsi32_si128(loadu_int32(data + 1 * src_stride)); + d[2] = _mm_cvtsi32_si128(loadu_int32(data + 2 * src_stride)); + d[3] = _mm_cvtsi32_si128(loadu_int32(data + 3 * src_stride)); + d[4] = _mm_cvtsi32_si128(loadu_int32(data + 4 * src_stride)); + d[5] = _mm_cvtsi32_si128(loadu_int32(data + 5 * src_stride)); + d[6] = _mm_cvtsi32_si128(loadu_int32(data + 6 * src_stride)); - for (i = 0; i < h; i += 2) { - data = &src_ptr[i * src_stride + j]; - const __m256i src_67a = _mm256_permute2x128_si256( - src6, - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 7 * src_stride))), - 0x20); + const __m128i src_01a = _mm_unpacklo_epi32(d[0], d[1]); + const __m128i src_12a = _mm_unpacklo_epi32(d[1], d[2]); + const __m128i src_23a = _mm_unpacklo_epi32(d[2], d[3]); + const __m128i src_34a = _mm_unpacklo_epi32(d[3], d[4]); + const __m128i src_45a = _mm_unpacklo_epi32(d[4], d[5]); + const __m128i src_56a = _mm_unpacklo_epi32(d[5], d[6]); - src6 = _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 8 * src_stride))); - const __m256i src_78a = _mm256_permute2x128_si256( - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(data + 7 * src_stride))), - src6, 0x20); + s[0] = _mm_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm_unpacklo_epi8(src_23a, src_34a); + s[2] = _mm_unpacklo_epi8(src_45a, src_56a); - s[3] = _mm256_unpacklo_epi8(src_67a, src_78a); - s[7] = _mm256_unpackhi_epi8(src_67a, src_78a); + do { + convolve_y_8tap_4x2_ssse3(data, src_stride, coeffs_128, d, s, &res); + res = round_sr_y_ssse3(res); + pack_store_u8_4x2_sse2(res, dst_ptr, dst_stride); - const __m256i res_lo = convolve_lowbd(s, coeffs); + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; - /* rounding code */ - // shift by F - 1 - const __m256i res_16b_lo = _mm256_sra_epi16( - _mm256_add_epi16(res_lo, right_shift_const), right_shift); - // 8 bit conversion and saturation to uint8 - __m256i res_8b_lo = _mm256_packus_epi16(res_16b_lo, res_16b_lo); + s[0] = s[1]; + s[1] = s[2]; + s[2] = s[3]; + } while (y > 0); + } + } else { + prepare_coeffs_lowbd(filter_params_y, subpel_y_qn, coeffs); - if (w - j > 8) { - const __m256i res_hi = convolve_lowbd(s + 4, coeffs); + if (w == 8) { + __m128i d[8]; + __m256i s[4]; - /* rounding code */ - // shift by F - 1 - const __m256i res_16b_hi = _mm256_sra_epi16( - _mm256_add_epi16(res_hi, right_shift_const), right_shift); - // 8 bit conversion and saturation to uint8 - __m256i res_8b_hi = _mm256_packus_epi16(res_16b_hi, res_16b_hi); + d[0] = _mm_loadl_epi64((__m128i *)(data + 0 * src_stride)); + d[1] = _mm_loadl_epi64((__m128i *)(data + 1 * src_stride)); + d[2] = _mm_loadl_epi64((__m128i *)(data + 2 * src_stride)); + d[3] = _mm_loadl_epi64((__m128i *)(data + 3 * src_stride)); + d[4] = _mm_loadl_epi64((__m128i *)(data + 4 * src_stride)); + d[5] = _mm_loadl_epi64((__m128i *)(data + 5 * src_stride)); + d[6] = _mm_loadl_epi64((__m128i *)(data + 6 * src_stride)); - __m256i res_a = _mm256_unpacklo_epi64(res_8b_lo, res_8b_hi); + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[2]); + const __m256i src_23a = _mm256_setr_m128i(d[2], d[3]); + const __m256i src_34a = _mm256_setr_m128i(d[3], d[4]); + const __m256i src_45a = _mm256_setr_m128i(d[4], d[5]); + const __m256i src_56a = _mm256_setr_m128i(d[5], d[6]); - const __m128i res_0 = _mm256_castsi256_si128(res_a); - const __m128i res_1 = _mm256_extracti128_si256(res_a, 1); + s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); + s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_0); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j + dst_stride], - res_1); - } else { - const __m128i res_0 = _mm256_castsi256_si128(res_8b_lo); - const __m128i res_1 = _mm256_extracti128_si256(res_8b_lo, 1); - if (w - j > 4) { - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j + dst_stride], - res_1); - } else if (w - j > 2) { - xx_storel_32(&dst[i * dst_stride + j], res_0); - xx_storel_32(&dst[i * dst_stride + j + dst_stride], res_1); - } else { - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride + j]; - __m128i *const p_1 = - (__m128i *)&dst[i * dst_stride + j + dst_stride]; - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); - } - } - s[0] = s[1]; - s[1] = s[2]; - s[2] = s[3]; + do { + __m256i res; + convolve_y_8tap_8x2_avx2(data, src_stride, coeffs, d, s, &res); + round_pack_store_y_8x2_avx2(res, dst_ptr, dst_stride); - s[4] = s[5]; - s[5] = s[6]; - s[6] = s[7]; + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + s[1] = s[2]; + s[2] = s[3]; + } while (y > 0); + + } else { + assert(!(w % 16)); + + __m128i d[8]; + __m256i s[8]; + do { + data = src_ptr + x; + dst_ptr = dst + x; + y = h; + + d[0] = _mm_loadu_si128((__m128i *)(data + 0 * src_stride)); + d[1] = _mm_loadu_si128((__m128i *)(data + 1 * src_stride)); + d[2] = _mm_loadu_si128((__m128i *)(data + 2 * src_stride)); + d[3] = _mm_loadu_si128((__m128i *)(data + 3 * src_stride)); + d[4] = _mm_loadu_si128((__m128i *)(data + 4 * src_stride)); + d[5] = _mm_loadu_si128((__m128i *)(data + 5 * src_stride)); + d[6] = _mm_loadu_si128((__m128i *)(data + 6 * src_stride)); + + const __m256i src_01a = _mm256_setr_m128i(d[0], d[1]); + const __m256i src_12a = _mm256_setr_m128i(d[1], d[2]); + const __m256i src_23a = _mm256_setr_m128i(d[2], d[3]); + const __m256i src_34a = _mm256_setr_m128i(d[3], d[4]); + const __m256i src_45a = _mm256_setr_m128i(d[4], d[5]); + const __m256i src_56a = _mm256_setr_m128i(d[5], d[6]); + + s[0] = _mm256_unpacklo_epi8(src_01a, src_12a); + s[1] = _mm256_unpacklo_epi8(src_23a, src_34a); + s[2] = _mm256_unpacklo_epi8(src_45a, src_56a); + + s[4] = _mm256_unpackhi_epi8(src_01a, src_12a); + s[5] = _mm256_unpackhi_epi8(src_23a, src_34a); + s[6] = _mm256_unpackhi_epi8(src_45a, src_56a); + + do { + __m256i res[2]; + convolve_y_8tap_16x2_avx2(data, src_stride, coeffs, d, s, res); + round_pack_store_y_16x2_avx2(res, dst_ptr, dst_stride); + + dst_ptr += 2 * dst_stride; + data += 2 * src_stride; + y -= 2; + + s[0] = s[1]; + s[1] = s[2]; + s[2] = s[3]; + + s[4] = s[5]; + s[5] = s[6]; + s[6] = s[7]; + } while (y > 0); + + x += 16; + } while (x < w); } } } } -void av1_convolve_y_sr_avx2(const uint8_t *src, int32_t src_stride, +void av1_convolve_x_sr_avx2(const uint8_t *src, int32_t src_stride, uint8_t *dst, int32_t dst_stride, int32_t w, int32_t h, - const InterpFilterParams *filter_params_y, - const int32_t subpel_y_qn) { -#if CONFIG_SVT_AV1 - const int vert_tap = get_filter_tap(filter_params_y, subpel_y_qn); - - if (vert_tap == 12) { - av1_convolve_y_sr_general_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_y, subpel_y_qn); - } else { - av1_convolve_y_sr_specialized_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_y, subpel_y_qn); - } -#else - av1_convolve_y_sr_general_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_y, subpel_y_qn); -#endif -} - -static inline void av1_convolve_x_sr_general_avx2( - const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int w, - int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn, - ConvolveParams *conv_params) { + const InterpFilterParams *filter_params_x, + const int32_t subpel_x_qn, + ConvolveParams *conv_params) { const int bits = FILTER_BITS - conv_params->round_0; - const __m128i round_shift = _mm_cvtsi32_si128(bits); - __m256i round_0_const = - _mm256_set1_epi16((1 << (conv_params->round_0 - 1)) >> 1); - __m128i round_0_shift = _mm_cvtsi32_si128(conv_params->round_0 - 1); - __m256i round_const = _mm256_set1_epi16((1 << bits) >> 1); - int i, horiz_tap = get_filter_tap(filter_params_x, subpel_x_qn); + int i, j, horiz_tap = get_filter_tap(filter_params_x, subpel_x_qn); assert(bits >= 0); assert((FILTER_BITS - conv_params->round_1) >= 0 || ((conv_params->round_0 + conv_params->round_1) == 2 * FILTER_BITS)); assert(conv_params->round_0 > 0); - __m256i coeffs[6], filt[4]; - filt[0] = _mm256_load_si256((__m256i const *)(filt_global_avx2)); - filt[1] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32)); + assert(horiz_tap == 2 || horiz_tap == 4 || horiz_tap == 6 || horiz_tap == 8 || + horiz_tap == 12); + assert((!(w % 2)) || (w <= 128)); + assert((h % 2) == 0); - if (horiz_tap == 6) - prepare_coeffs_6t_lowbd(filter_params_x, subpel_x_qn, coeffs); - else if (horiz_tap == 12) { - prepare_coeffs_12taps(filter_params_x, subpel_x_qn, coeffs); - } else { - prepare_coeffs_lowbd(filter_params_x, subpel_x_qn, coeffs); - } + __m256i coeffs[6] = { 0 }, filt[4] = { 0 }; + __m128i coeffs_128[4] = { 0 }; + i = 0; // horz_filt as 4 tap if (horiz_tap == 4) { - const int fo_horiz = 1; - const uint8_t *const src_ptr = src - fo_horiz; - if (w <= 8) { - for (i = 0; i < h; i += 2) { - const __m256i data = _mm256_permute2x128_si256( - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride]))), - _mm256_castsi128_si256(_mm_loadu_si128( - (__m128i *)(&src_ptr[i * src_stride + src_stride]))), - 0x20); + // since fo_horiz = 1 + const uint8_t *src_ptr = src - 1; + if (w == 2) { + prepare_coeffs_4t_ssse3(filter_params_x, subpel_x_qn, coeffs_128); + do { + const __m128i res = + convolve_x_4tap_2x2_ssse3(src_ptr, src_stride, coeffs_128); + const __m128i reg = round_sr_x_ssse3(res); + pack_store_u8_2x2_sse2(reg, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 4) { + prepare_coeffs_4t_ssse3(filter_params_x, subpel_x_qn, coeffs_128); + do { + const __m128i reg = + convolve_x_4tap_4x2_ssse3(src_ptr, src_stride, coeffs_128); + const __m128i res = round_sr_x_ssse3(reg); + pack_store_u8_4x2_sse2(res, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 8) { + prepare_coeffs_lowbd(filter_params_x, subpel_x_qn, coeffs); + filt[0] = _mm256_load_si256((__m256i const *)(filt_global_avx2)); + filt[1] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32)); + do { + const __m256i data = _mm256_setr_m128i( + _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride])), + _mm_loadu_si128( + (__m128i *)(&src_ptr[i * src_stride + src_stride]))); __m256i res_16b = convolve_lowbd_x_4tap(data, coeffs + 1, filt); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_0_const), - round_0_shift); - - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_const), - round_shift); + res_16b = round_sr_x_avx2(res_16b); /* rounding code */ // 8 bit conversion and saturation to uint8 @@ -588,22 +902,18 @@ const __m128i res_0 = _mm256_castsi256_si128(res_8b); const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); - if (w > 4) { - _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); - } else if (w > 2) { - xx_storel_32(&dst[i * dst_stride], res_0); - xx_storel_32(&dst[i * dst_stride + dst_stride], res_1); - } else { - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride]; - __m128i *const p_1 = (__m128i *)&dst[i * dst_stride + dst_stride]; - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); - } - } + _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); + _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); + i += 2; + } while (i < h); } else { - for (i = 0; i < h; ++i) { - for (int j = 0; j < w; j += 16) { + assert(!(w % 16)); + prepare_coeffs_lowbd(filter_params_x, subpel_x_qn, coeffs); + filt[0] = _mm256_load_si256((__m256i const *)(filt_global_avx2)); + filt[1] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32)); + do { + j = 0; + do { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15 16 17 // 18 19 20 21 22 23 const __m256i data = _mm256_inserti128_si256( @@ -613,11 +923,7 @@ __m256i res_16b = convolve_lowbd_x_4tap(data, coeffs + 1, filt); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_0_const), - round_0_shift); - - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_const), - round_shift); + res_16b = round_sr_x_avx2(res_16b); /* rounding code */ // 8 bit conversion and saturation to uint8 @@ -628,31 +934,28 @@ res_8b = _mm256_permute4x64_epi64(res_8b, 216); __m128i res = _mm256_castsi256_si128(res_8b); _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res); - } - } + j += 16; + } while (j < w); + i++; + } while (i < h); } } else if (horiz_tap == 6) { - const int fo_horiz = horiz_tap / 2 - 1; - const uint8_t *const src_ptr = src - fo_horiz; + // since (horiz_tap/2 - 1 == 2) + const uint8_t *src_ptr = src - 2; + prepare_coeffs_6t_lowbd(filter_params_x, subpel_x_qn, coeffs); + filt[0] = _mm256_load_si256((__m256i const *)(filt_global_avx2)); + filt[1] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32)); filt[2] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32 * 2)); - filt[3] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32 * 3)); - - if (w <= 8) { - for (i = 0; i < h; i += 2) { - const __m256i data = _mm256_permute2x128_si256( - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride]))), - _mm256_castsi128_si256(_mm_loadu_si128( - (__m128i *)(&src_ptr[i * src_stride + src_stride]))), - 0x20); + if (w == 8) { + do { + const __m256i data = _mm256_setr_m128i( + _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride])), + _mm_loadu_si128( + (__m128i *)(&src_ptr[i * src_stride + src_stride]))); __m256i res_16b = convolve_lowbd_x_6tap(data, coeffs, filt); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_0_const), - round_0_shift); - - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_const), - round_shift); + res_16b = round_sr_x_avx2(res_16b); /* rounding code */ // 8 bit conversion and saturation to uint8 @@ -660,60 +963,132 @@ const __m128i res_0 = _mm256_castsi256_si128(res_8b); const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); - if (w > 4) { - _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); - } else if (w > 2) { - xx_storel_32(&dst[i * dst_stride], res_0); - xx_storel_32(&dst[i * dst_stride + dst_stride], res_1); - } else { - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride]; - __m128i *const p_1 = (__m128i *)&dst[i * dst_stride + dst_stride]; - *(uint16_t *)p_0 = _mm_cvtsi128_si32(res_0); - *(uint16_t *)p_1 = _mm_cvtsi128_si32(res_1); - } - } + _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); + _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); + i += 2; + } while (i < h); + } else if (w == 16) { + do { + __m256i data[2] = { 0 }; + + load_convolve_6tap_16x2_avx2(src_ptr, src_stride, coeffs, filt, data); + round_pack_store_16x2_avx2(data, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 32) { + do { + convolve_sr_store_6tap_32_avx2(src_ptr, coeffs, filt, dst); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else if (w == 64) { + do { + convolve_sr_store_6tap_32_avx2(src_ptr, coeffs, filt, dst); + convolve_sr_store_6tap_32_avx2(src_ptr + 32, coeffs, filt, dst + 32); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); } else { - for (i = 0; i < h; ++i) { - for (int j = 0; j < w; j += 16) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15 16 17 - // 18 19 20 21 22 23 - const __m256i data = _mm256_inserti128_si256( - _mm256_loadu_si256((__m256i *)&src_ptr[(i * src_stride) + j]), - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + (j + 8)]), - 1); + assert(w == 128); - __m256i res_16b = convolve_lowbd_x_6tap(data, coeffs, filt); + do { + convolve_sr_store_6tap_32_avx2(src_ptr, coeffs, filt, dst); + convolve_sr_store_6tap_32_avx2(src_ptr + SECOND_32_BLK, coeffs, filt, + dst + SECOND_32_BLK); + convolve_sr_store_6tap_32_avx2(src_ptr + THIRD_32_BLK, coeffs, filt, + dst + THIRD_32_BLK); + convolve_sr_store_6tap_32_avx2(src_ptr + FOURTH_32_BLK, coeffs, filt, + dst + FOURTH_32_BLK); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } + } else if (horiz_tap == 8) { + // since (horiz_tap / 2 - 1) == 3 + const uint8_t *src_ptr = src - 3; + prepare_coeffs_lowbd(filter_params_x, subpel_x_qn, coeffs); + filt[0] = _mm256_load_si256((__m256i const *)(filt_global_avx2)); + filt[1] = + _mm256_load_si256((__m256i const *)(filt_global_avx2 + SECOND_32_BLK)); + filt[2] = + _mm256_load_si256((__m256i const *)(filt_global_avx2 + THIRD_32_BLK)); + filt[3] = + _mm256_load_si256((__m256i const *)(filt_global_avx2 + FOURTH_32_BLK)); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_0_const), - round_0_shift); + if (w == 8) { + do { + const __m256i data = _mm256_setr_m128i( + _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride])), + _mm_loadu_si128( + (__m128i *)(&src_ptr[i * src_stride + src_stride]))); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_const), - round_shift); + __m256i res_16b = convolve_lowbd_x(data, coeffs, filt); - /* rounding code */ - // 8 bit conversion and saturation to uint8 - __m256i res_8b = _mm256_packus_epi16(res_16b, res_16b); + res_16b = round_sr_x_avx2(res_16b); - // Store values into the destination buffer - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - res_8b = _mm256_permute4x64_epi64(res_8b, 216); - __m128i res = _mm256_castsi256_si128(res_8b); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res); - } - } + /* rounding code */ + // 8 bit conversion and saturation to uint8 + __m256i res_8b = _mm256_packus_epi16(res_16b, res_16b); + + const __m128i res_0 = _mm256_castsi256_si128(res_8b); + const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); + _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); + _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); + i += 2; + } while (i < h); + } else if (w == 16) { + do { + __m256i data[2] = { 0 }; + + load_convolve_8tap_16x2_avx2(src_ptr, src_stride, coeffs, filt, data); + round_pack_store_16x2_avx2(data, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 32) { + do { + load_convolve_round_8tap_32_avx2(src_ptr, coeffs, filt, dst); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else if (w == 64) { + do { + load_convolve_round_8tap_32_avx2(src_ptr, coeffs, filt, dst); + load_convolve_round_8tap_32_avx2(src_ptr + 32, coeffs, filt, dst + 32); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else { + assert(w == 128); + do { + load_convolve_round_8tap_32_avx2(src_ptr, coeffs, filt, dst); + load_convolve_round_8tap_32_avx2(src_ptr + SECOND_32_BLK, coeffs, filt, + dst + SECOND_32_BLK); + load_convolve_round_8tap_32_avx2(src_ptr + THIRD_32_BLK, coeffs, filt, + dst + THIRD_32_BLK); + load_convolve_round_8tap_32_avx2(src_ptr + FOURTH_32_BLK, coeffs, filt, + dst + FOURTH_32_BLK); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); } } else if (horiz_tap == 12) { // horiz_tap == 12 const int fo_horiz = filter_params_x->taps / 2 - 1; + prepare_coeffs_12taps(filter_params_x, subpel_x_qn, coeffs); + const __m128i round_shift = _mm_cvtsi32_si128(bits); const uint8_t *const src_ptr = src - fo_horiz; const __m256i v_zero = _mm256_setzero_si256(); - round_0_const = _mm256_set1_epi32((1 << (conv_params->round_0)) >> 1); - round_const = _mm256_set1_epi32((1 << bits) >> 1); - round_0_shift = _mm_cvtsi32_si128(conv_params->round_0); - __m256i s[6]; + __m256i round_0_const = + _mm256_set1_epi32((1 << (conv_params->round_0)) >> 1); + __m256i round_const = _mm256_set1_epi32((1 << bits) >> 1); + __m128i round_0_shift = _mm_cvtsi32_si128(conv_params->round_0); + __m256i s[6] = { 0 }; if (w <= 4) { - for (i = 0; i < h; i += 2) { + do { const __m256i data = _mm256_permute2x128_si256( _mm256_castsi128_si256( _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride]))), @@ -769,21 +1144,22 @@ const __m128i res_1 = _mm256_extracti128_si256(res_8b_lo, 1); if (w > 2) { // 00 01 02 03 - *(int *)&dst[i * dst_stride] = _mm_cvtsi128_si32(res_0); + xx_storel_32(&dst[i * dst_stride], res_0); // 10 11 12 13 - *(int *)&dst[i * dst_stride + dst_stride] = _mm_cvtsi128_si32(res_1); + xx_storel_32(&dst[i * dst_stride + dst_stride], res_1); } else { // 00 01 - *(uint16_t *)&dst[i * dst_stride] = - (uint16_t)_mm_cvtsi128_si32(res_0); + xx_storel_16(&dst[i * dst_stride], res_0); // 10 11 - *(uint16_t *)&dst[i * dst_stride + dst_stride] = - (uint16_t)_mm_cvtsi128_si32(res_1); + xx_storel_16(&dst[i * dst_stride + dst_stride], res_1); } - } + i += 2; + } while (i < h); } else { - for (i = 0; i < h; i++) { - for (int j = 0; j < w; j += 8) { + assert(!(w % 8)); + do { + j = 0; + do { const __m256i data = _mm256_permute2x128_si256( _mm256_castsi128_si256( _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride + j]))), @@ -824,105 +1200,196 @@ __m256i res_8b_lo = _mm256_packus_epi16(res_16b_lo, res_16b_lo); const __m128i res_0 = _mm256_extracti128_si256(res_8b_lo, 0); const __m128i res_1 = _mm256_extracti128_si256(res_8b_lo, 1); - *(int *)&dst[i * dst_stride + j] = _mm_cvtsi128_si32(res_0); - *(int *)&dst[i * dst_stride + j + 4] = _mm_cvtsi128_si32(res_1); - } - } + xx_storel_32(&dst[i * dst_stride + j], res_0); + xx_storel_32(&dst[i * dst_stride + j + 4], res_1); + + j += 8; + } while (j < w); + i++; + } while (i < h); } } else { - const int fo_horiz = filter_params_x->taps / 2 - 1; - const uint8_t *const src_ptr = src - fo_horiz; - filt[2] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32 * 2)); - filt[3] = _mm256_load_si256((__m256i const *)(filt_global_avx2 + 32 * 3)); + assert(horiz_tap == 2); + // since (filter_params_x->taps / 2 - 1) == 0 + const uint8_t *src_ptr = src; + if (subpel_x_qn != 8) { + if (w <= 8) { + prepare_coeffs_2t_ssse3(filter_params_x, subpel_x_qn, coeffs_128); - if (w <= 8) { - for (i = 0; i < h; i += 2) { - const __m256i data = _mm256_permute2x128_si256( - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride]))), - _mm256_castsi128_si256(_mm_loadu_si128( - (__m128i *)(&src_ptr[i * src_stride + src_stride]))), - 0x20); - - __m256i res_16b = convolve_lowbd_x(data, coeffs, filt); - - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_0_const), - round_0_shift); - - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_const), - round_shift); - - /* rounding code */ - // 8 bit conversion and saturation to uint8 - __m256i res_8b = _mm256_packus_epi16(res_16b, res_16b); - - const __m128i res_0 = _mm256_castsi256_si128(res_8b); - const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); - if (w > 4) { - _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); - } else if (w > 2) { - xx_storel_32(&dst[i * dst_stride], res_0); - xx_storel_32(&dst[i * dst_stride + dst_stride], res_1); + if (w == 2) { + do { + const __m128i data = + convolve_x_2tap_2x2_ssse3(src_ptr, src_stride, coeffs_128); + const __m128i reg = round_sr_x_ssse3(data); + pack_store_u8_2x2_sse2(reg, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 4) { + do { + const __m128i data = + convolve_x_2tap_4x2_ssse3(src_ptr, src_stride, coeffs_128); + const __m128i reg = round_sr_x_ssse3(data); + pack_store_u8_4x2_sse2(reg, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); } else { - __m128i *const p_0 = (__m128i *)&dst[i * dst_stride]; - __m128i *const p_1 = (__m128i *)&dst[i * dst_stride + dst_stride]; - *(uint16_t *)p_0 = (uint16_t)_mm_cvtsi128_si32(res_0); - *(uint16_t *)p_1 = (uint16_t)_mm_cvtsi128_si32(res_1); + assert(w == 8); + + do { + __m128i data[2] = { 0 }; + + convolve_x_2tap_8x2_ssse3(src_ptr, src_stride, coeffs_128, data); + data[0] = round_sr_x_ssse3(data[0]); + data[1] = round_sr_x_ssse3(data[1]); + const __m128i reg = _mm_packus_epi16(data[0], data[1]); + _mm_storel_epi64((__m128i *)dst, reg); + _mm_storeh_epi64((__m128i *)(dst + dst_stride), reg); + + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } + } else { + prepare_coeffs_2t_lowbd(filter_params_x, subpel_x_qn, coeffs); + + if (w == 16) { + do { + __m256i data[2] = { 0 }; + + convolve_x_2tap_16x2_avx2(src_ptr, src_stride, coeffs, data); + round_pack_store_16x2_avx2(data, dst, dst_stride); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 32) { + do { + convolve_round_2tap_32_avx2(src_ptr, coeffs, dst); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else if (w == 64) { + do { + convolve_round_2tap_32_avx2(src_ptr, coeffs, dst); + convolve_round_2tap_32_avx2(src_ptr + SECOND_32_BLK, coeffs, + dst + SECOND_32_BLK); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else { + assert(w == 128); + + do { + convolve_round_2tap_32_avx2(src_ptr, coeffs, dst); + convolve_round_2tap_32_avx2(src_ptr + (SECOND_32_BLK), coeffs, + dst + (SECOND_32_BLK)); + convolve_round_2tap_32_avx2(src_ptr + (THIRD_32_BLK), coeffs, + dst + (THIRD_32_BLK)); + convolve_round_2tap_32_avx2(src_ptr + (FOURTH_32_BLK), coeffs, + dst + (FOURTH_32_BLK)); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); } } } else { - for (i = 0; i < h; ++i) { - for (int j = 0; j < w; j += 16) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15 16 17 - // 18 19 20 21 22 23 - const __m256i data = _mm256_inserti128_si256( - _mm256_loadu_si256((__m256i *)&src_ptr[(i * src_stride) + j]), - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + (j + 8)]), - 1); + if (w == 2) { + do { + __m128i data = load_x_u8_4x2_sse4(src_ptr, src_stride); + const __m128i reg1 = _mm_srli_si128(data, 1); + const __m128i reg2 = _mm_avg_epu8(data, reg1); + xx_storel_16(dst, reg2); + { + uint16_t val = (uint16_t)_mm_extract_epi16(reg2, 2); + memcpy(dst + dst_stride, &val, sizeof(val)); + } + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 4) { + do { + __m128i data = load_8bit_8x2_to_1_reg_sse2( + src_ptr, (int)(sizeof(*src_ptr) * src_stride)); + const __m128i reg1 = _mm_srli_si128(data, 1); + const __m128i reg2 = _mm_avg_epu8(data, reg1); + xx_storel_32(dst, reg2); + { + int32_t val = _mm_extract_epi32(reg2, 2); + memcpy(dst + dst_stride, &val, sizeof(val)); + } - __m256i res_16b = convolve_lowbd_x(data, coeffs, filt); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 8) { + do { + const __m128i data00 = _mm_loadu_si128((__m128i *)src_ptr); + const __m128i data10 = + _mm_loadu_si128((__m128i *)(src_ptr + src_stride)); + const __m128i data01 = _mm_srli_si128(data00, 1); + const __m128i data11 = _mm_srli_si128(data10, 1); + const __m128i reg0 = _mm_avg_epu8(data00, data01); + const __m128i reg1 = _mm_avg_epu8(data10, data11); + _mm_storel_epi64((__m128i *)dst, reg0); + _mm_storel_epi64((__m128i *)(dst + dst_stride), reg1); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_0_const), - round_0_shift); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 16) { + do { + const __m128i data00 = _mm_loadu_si128((__m128i *)src_ptr); + const __m128i data01 = _mm_loadu_si128((__m128i *)(src_ptr + 1)); + const __m128i data10 = + _mm_loadu_si128((__m128i *)(src_ptr + src_stride)); + const __m128i data11 = + _mm_loadu_si128((__m128i *)(src_ptr + src_stride + 1)); + const __m128i reg0 = _mm_avg_epu8(data00, data01); + const __m128i reg1 = _mm_avg_epu8(data10, data11); + _mm_storeu_si128((__m128i *)dst, reg0); + _mm_storeu_si128((__m128i *)(dst + dst_stride), reg1); - res_16b = _mm256_sra_epi16(_mm256_add_epi16(res_16b, round_const), - round_shift); + src_ptr += 2 * src_stride; + dst += 2 * dst_stride; + h -= 2; + } while (h); + } else if (w == 32) { + do { + load_avg_store_2tap_32_avx2(src_ptr, dst); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else if (w == 64) { + do { + load_avg_store_2tap_32_avx2(src_ptr, dst); + load_avg_store_2tap_32_avx2(src_ptr + (SECOND_32_BLK), + dst + (SECOND_32_BLK)); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); + } else { + assert(w == 128); - /* rounding code */ - // 8 bit conversion and saturation to uint8 - __m256i res_8b = _mm256_packus_epi16(res_16b, res_16b); - - // Store values into the destination buffer - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - res_8b = _mm256_permute4x64_epi64(res_8b, 216); - __m128i res = _mm256_castsi256_si128(res_8b); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res); - } + do { + load_avg_store_2tap_32_avx2(src_ptr, dst); + load_avg_store_2tap_32_avx2(src_ptr + (SECOND_32_BLK), + dst + (SECOND_32_BLK)); + load_avg_store_2tap_32_avx2(src_ptr + (THIRD_32_BLK), + dst + (THIRD_32_BLK)); + load_avg_store_2tap_32_avx2(src_ptr + (FOURTH_32_BLK), + dst + (FOURTH_32_BLK)); + src_ptr += src_stride; + dst += dst_stride; + } while ((--h) > 0); } } } } - -void av1_convolve_x_sr_avx2(const uint8_t *src, int32_t src_stride, - uint8_t *dst, int32_t dst_stride, int32_t w, - int32_t h, - const InterpFilterParams *filter_params_x, - const int32_t subpel_x_qn, - ConvolveParams *conv_params) { -#if CONFIG_SVT_AV1 - const int horz_tap = get_filter_tap(filter_params_x, subpel_x_qn); - - if (horz_tap == 12) { - av1_convolve_x_sr_general_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_x, subpel_x_qn, conv_params); - } else { - av1_convolve_x_sr_specialized_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_x, subpel_x_qn, - conv_params); - } -#else - av1_convolve_x_sr_general_avx2(src, src_stride, dst, dst_stride, w, h, - filter_params_x, subpel_x_qn, conv_params); -#endif -}
diff --git a/av1/common/x86/warp_plane_avx2.c b/av1/common/x86/warp_plane_avx2.c index a780939..4f6f910 100644 --- a/av1/common/x86/warp_plane_avx2.c +++ b/av1/common/x86/warp_plane_avx2.c
@@ -14,6 +14,8 @@ #include "av1/common/warped_motion.h" #include "aom_dsp/x86/synonyms.h" +#if !CONFIG_HIGHWAY + DECLARE_ALIGNED(32, static const uint8_t, shuffle_alpha0_mask01_avx2[32]) = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 @@ -1208,3 +1210,5 @@ } } } + +#endif // !CONFIG_HIGHWAY
diff --git a/av1/common/x86/warp_plane_hwy_avx2.cc b/av1/common/x86/warp_plane_hwy_avx2.cc new file mode 100644 index 0000000..a721383 --- /dev/null +++ b/av1/common/x86/warp_plane_hwy_avx2.cc
@@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025, 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. + */ + +#define HWY_BASELINE_TARGETS HWY_AVX2 +#define HWY_BROKEN_32BIT 0 + +#include "av1/common/warp_plane_hwy.h" + +MAKE_WARP_AFFINE(avx2)
diff --git a/av1/common/x86/warp_plane_hwy_avx512.cc b/av1/common/x86/warp_plane_hwy_avx512.cc new file mode 100644 index 0000000..a0e2a87 --- /dev/null +++ b/av1/common/x86/warp_plane_hwy_avx512.cc
@@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025, 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. + */ + +#define HWY_BASELINE_TARGETS HWY_AVX3_DL +#define HWY_BROKEN_32BIT 0 + +#include "av1/common/warp_plane_hwy.h" + +MAKE_WARP_AFFINE(avx512)
diff --git a/av1/common/x86/warp_plane_hwy_sse4.cc b/av1/common/x86/warp_plane_hwy_sse4.cc new file mode 100644 index 0000000..f7c74fb7 --- /dev/null +++ b/av1/common/x86/warp_plane_hwy_sse4.cc
@@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025, 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. + */ + +#define HWY_BASELINE_TARGETS HWY_SSE4 +#define HWY_BROKEN_32BIT 0 + +#include "av1/common/warp_plane_hwy.h" + +MAKE_WARP_AFFINE(sse4_1)
diff --git a/av1/common/x86/warp_plane_sse4.c b/av1/common/x86/warp_plane_sse4.c index eb02683..11de2b0 100644 --- a/av1/common/x86/warp_plane_sse4.c +++ b/av1/common/x86/warp_plane_sse4.c
@@ -137,6 +137,8 @@ }; /* clang-format on */ +#if !CONFIG_HIGHWAY + // Shuffle masks: we want to convert a sequence of bytes 0, 1, 2, ..., 15 // in an SSE register into two sequences: // 0, 2, 2, 4, ..., 12, 12, 14, <don't care> @@ -906,3 +908,5 @@ } } } + +#endif // !CONFIG_HIGHWAY
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c index ca9f0a4..b6ee3ac 100644 --- a/av1/decoder/decodeframe.c +++ b/av1/decoder/decodeframe.c
@@ -373,6 +373,9 @@ int mi_col, aom_reader *r, PARTITION_TYPE partition, BLOCK_SIZE bsize) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_mbmi_block_time); +#endif AV1_COMMON *const cm = &pbi->common; const SequenceHeader *const seq_params = cm->seq_params; const int bw = mi_size_wide[bsize]; @@ -396,6 +399,9 @@ aom_internal_error(xd->error_info, AOM_CODEC_CORRUPT_FRAME, "Invalid block size."); } +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_mbmi_block_time); +#endif } typedef struct PadBlock { @@ -902,6 +908,9 @@ static inline void decode_token_recon_block(AV1Decoder *const pbi, ThreadData *const td, aom_reader *r, BLOCK_SIZE bsize) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_token_recon_block_time); +#endif AV1_COMMON *const cm = &pbi->common; DecoderCodingBlock *const dcb = &td->dcb; MACROBLOCKD *const xd = &dcb->xd; @@ -909,6 +918,9 @@ MB_MODE_INFO *mbmi = xd->mi[0]; if (!is_inter_block(mbmi)) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_token_recon_block_intra_time); +#endif int row, col; assert(bsize == get_plane_block_size(bsize, xd->plane[0].subsampling_x, xd->plane[0].subsampling_y)); @@ -948,8 +960,20 @@ } } } +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_token_recon_block_intra_time); +#endif } else { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_token_recon_block_inter_time); +#endif +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, predict_inter_block_time); +#endif td->predict_inter_block_visit(cm, dcb, bsize); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, predict_inter_block_time); +#endif // Reconstruction if (!mbmi->skip_txfm) { int eobtotal = 0; @@ -994,9 +1018,15 @@ blk_row += bh_var_tx) { for (blk_col = col >> ss_x; blk_col < unit_width; blk_col += bw_var_tx) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_reconstruct_tx_inter_time); +#endif decode_reconstruct_tx(cm, td, r, mbmi, plane, plane_bsize, blk_row, blk_col, block, max_tx_size, &eobtotal); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_reconstruct_tx_inter_time); +#endif block += step; } } @@ -1005,9 +1035,15 @@ } } td->cfl_store_inter_block_visit(cm, xd); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_token_recon_block_inter_time); +#endif } av1_visit_palette(pbi, xd, r, set_color_index_map_offset); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_token_recon_block_time); +#endif } static inline void set_inter_tx_size(MB_MODE_INFO *mbmi, int stride_log2, @@ -1128,6 +1164,9 @@ int mi_col, aom_reader *r, PARTITION_TYPE partition, BLOCK_SIZE bsize) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, parse_decode_block_time); +#endif DecoderCodingBlock *const dcb = &td->dcb; MACROBLOCKD *const xd = &dcb->xd; decode_mbmi_block(pbi, dcb, mi_row, mi_col, r, partition, bsize); @@ -1180,6 +1219,9 @@ if (mbmi->skip_txfm) av1_reset_entropy_context(xd, bsize, num_planes); decode_token_recon_block(pbi, td, r, bsize); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, parse_decode_block_time); +#endif } static inline void set_offsets_for_pred_and_recon(AV1Decoder *const pbi, @@ -1907,14 +1949,19 @@ } } -static inline void resize_context_buffers(AV1_COMMON *cm, int width, - int height) { +static inline void resize_context_buffers(AV1_COMMON *cm, int width, int height, + unsigned int frame_size_limit) { #if CONFIG_SIZE_LIMIT if (width > DECODE_WIDTH_LIMIT || height > DECODE_HEIGHT_LIMIT) aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, "Dimensions of %dx%d beyond allowed size of %dx%d.", width, height, DECODE_WIDTH_LIMIT, DECODE_HEIGHT_LIMIT); #endif + if (frame_size_limit && (uint64_t)width * height > frame_size_limit) { + aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME, + "Dimensions of %dx%d beyond allowed size of %u.", width, + height, frame_size_limit); + } if (cm->width != width || cm->height != height) { const int new_mi_rows = CEIL_POWER_OF_TWO(height, MI_SIZE_LOG2); const int new_mi_cols = CEIL_POWER_OF_TWO(width, MI_SIZE_LOG2); @@ -1977,6 +2024,7 @@ static inline void setup_frame_size(AV1_COMMON *cm, int frame_size_override_flag, + unsigned int frame_size_limit, struct aom_read_bit_buffer *rb) { const SequenceHeader *const seq_params = cm->seq_params; int width, height; @@ -1996,7 +2044,7 @@ } setup_superres(cm, rb, &width, &height); - resize_context_buffers(cm, width, height); + resize_context_buffers(cm, width, height, frame_size_limit); setup_render_size(cm, rb); setup_buffer_pool(cm); } @@ -2015,6 +2063,7 @@ } static inline void setup_frame_size_with_refs(AV1_COMMON *cm, + unsigned int frame_size_limit, struct aom_read_bit_buffer *rb) { int width, height; int found = 0; @@ -2037,7 +2086,7 @@ cm->render_width = buf->render_width; cm->render_height = buf->render_height; setup_superres(cm, rb, &width, &height); - resize_context_buffers(cm, width, height); + resize_context_buffers(cm, width, height, frame_size_limit); found = 1; break; } @@ -2051,7 +2100,7 @@ read_frame_size(rb, num_bits_width, num_bits_height, &width, &height); setup_superres(cm, rb, &width, &height); - resize_context_buffers(cm, width, height); + resize_context_buffers(cm, width, height, frame_size_limit); setup_render_size(cm, rb); } @@ -2718,6 +2767,9 @@ static inline void decode_tile(AV1Decoder *pbi, ThreadData *const td, int tile_row, int tile_col) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_tile_time); +#endif TileInfo tile_info; AV1_COMMON *const cm = &pbi->common; @@ -2741,9 +2793,25 @@ mi_col += cm->seq_params->mib_size) { set_cb_buffer(pbi, dcb, &td->cb_buffer_base, num_planes, 0, 0); +#if CONFIG_INSPECTION + const int start_bits = aom_reader_tell(td->bit_reader); +#endif // Bit-stream parsing and decoding of the superblock decode_partition(pbi, td, mi_row, mi_col, td->bit_reader, cm->seq_params->sb_size, 0x3); +#if CONFIG_INSPECTION + const int end_bits = aom_reader_tell(td->bit_reader); +#endif + +#if CONFIG_INSPECTION + if (pbi->sb_bits) { + const int mib_size = cm->seq_params->mib_size; + const int sb_cols = (cm->mi_params.mi_cols + mib_size - 1) / mib_size; + const int sb_row = mi_row / mib_size; + const int sb_col = mi_col / mib_size; + pbi->sb_bits[sb_row * sb_cols + sb_col] = end_bits - start_bits; + } +#endif if (aom_reader_has_overflowed(td->bit_reader)) { aom_merge_corrupted_flag(&dcb->corrupted, 1); @@ -2755,12 +2823,27 @@ int corrupted = (check_trailing_bits_after_symbol_coder(td->bit_reader)) ? 1 : 0; aom_merge_corrupted_flag(&dcb->corrupted, corrupted); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_tile_time); +#endif } static const uint8_t *decode_tiles(AV1Decoder *pbi, const uint8_t *data, const uint8_t *data_end, int start_tile, int end_tile) { AV1_COMMON *const cm = &pbi->common; +#if CONFIG_INSPECTION + const int mib_size = cm->seq_params->mib_size; + const int sb_cols = (cm->mi_params.mi_cols + mib_size - 1) / mib_size; + const int sb_rows = (cm->mi_params.mi_rows + mib_size - 1) / mib_size; + const int required_size = sb_cols * sb_rows; + if (pbi->sb_bits_alloc_size < required_size) { + aom_free(pbi->sb_bits); + CHECK_MEM_ERROR(cm, pbi->sb_bits, + (int *)aom_malloc(sizeof(*pbi->sb_bits) * required_size)); + pbi->sb_bits_alloc_size = required_size; + } +#endif ThreadData *const td = &pbi->td; CommonTileParams *const tiles = &cm->tiles; const int tile_cols = tiles->cols; @@ -4845,7 +4928,7 @@ } if (current_frame->frame_type == KEY_FRAME) { - setup_frame_size(cm, frame_size_override_flag, rb); + setup_frame_size(cm, frame_size_override_flag, pbi->frame_size_limit, rb); if (features->allow_screen_content_tools && !av1_superres_scaled(cm)) features->allow_intrabc = aom_rb_read_bit(rb); @@ -4857,7 +4940,7 @@ if (current_frame->frame_type == INTRA_ONLY_FRAME) { cm->cur_frame->film_grain_params_present = seq_params->film_grain_params_present; - setup_frame_size(cm, frame_size_override_flag, rb); + setup_frame_size(cm, frame_size_override_flag, pbi->frame_size_limit, rb); if (features->allow_screen_content_tools && !av1_superres_scaled(cm)) features->allow_intrabc = aom_rb_read_bit(rb); @@ -4934,9 +5017,10 @@ } if (!features->error_resilient_mode && frame_size_override_flag) { - setup_frame_size_with_refs(cm, rb); + setup_frame_size_with_refs(cm, pbi->frame_size_limit, rb); } else { - setup_frame_size(cm, frame_size_override_flag, rb); + setup_frame_size(cm, frame_size_override_flag, pbi->frame_size_limit, + rb); } if (features->cur_frame_force_integer_mv) { @@ -5276,6 +5360,9 @@ const uint8_t *data_end, const uint8_t **p_data_end, int start_tile, int end_tile, int initialize_flag) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, av1_decode_tg_tiles_and_wrapup_time); +#endif AV1_COMMON *const cm = &pbi->common; CommonTileParams *const tiles = &cm->tiles; MACROBLOCKD *const xd = &pbi->dcb.xd; @@ -5285,6 +5372,9 @@ if (initialize_flag) setup_frame_info(pbi); const int num_planes = av1_num_planes(cm); +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, decode_tiles_time); +#endif if (pbi->max_threads > 1 && !(tiles->large_scale && !pbi->ext_tile_debug) && pbi->row_mt) *p_data_end = @@ -5294,6 +5384,9 @@ *p_data_end = decode_tiles_mt(pbi, data, data_end, start_tile, end_tile); else *p_data_end = decode_tiles(pbi, data, data_end, start_tile, end_tile); +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, decode_tiles_time); +#endif // If the bit stream is monochrome, set the U and V buffers to a constant. if (num_planes < 3) { @@ -5309,11 +5402,17 @@ av1_alloc_cdef_sync(cm, &pbi->cdef_sync, pbi->num_workers); if (!cm->features.allow_intrabc && !tiles->single_tile_decoding) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, av1_loop_filter_frame_time); +#endif if (cm->lf.filter_level[0] || cm->lf.filter_level[1]) { av1_loop_filter_frame_mt(&cm->cur_frame->buf, cm, &pbi->dcb.xd, 0, num_planes, 0, pbi->tile_workers, pbi->num_workers, &pbi->lf_row_sync, 0); } +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, av1_loop_filter_frame_time); +#endif const int do_cdef = !pbi->skip_loop_filter && !cm->features.coded_lossless && @@ -5328,6 +5427,10 @@ // Frame border extension is not required in the decoder // as it happens in extend_mc_border(). int do_extend_border_mt = 0; + +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, cdef_and_lr_time); +#endif if (!optimized_loop_restoration) { if (do_loop_restoration) av1_loop_restoration_save_boundary_lines(&pbi->common.cur_frame->buf, @@ -5377,6 +5480,9 @@ } } } +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, cdef_and_lr_time); +#endif } if (!pbi->dcb.corrupted) { @@ -5404,4 +5510,8 @@ if (cm->show_frame && !cm->seq_params->order_hint_info.enable_order_hint) { ++cm->current_frame.frame_number; } + +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, av1_decode_tg_tiles_and_wrapup_time); +#endif }
diff --git a/av1/decoder/decoder.c b/av1/decoder/decoder.c index 0ae0b86..81a88b4 100644 --- a/av1/decoder/decoder.c +++ b/av1/decoder/decoder.c
@@ -148,6 +148,11 @@ aom_get_worker_interface()->init(&pbi->lf_worker); pbi->lf_worker.thread_name = "aom lf worker"; +#if CONFIG_INSPECTION + pbi->sb_bits = NULL; + pbi->sb_bits_alloc_size = 0; +#endif + return pbi; } @@ -226,6 +231,9 @@ #if CONFIG_ACCOUNTING aom_accounting_clear(&pbi->accounting); #endif +#if CONFIG_INSPECTION + aom_free(pbi->sb_bits); +#endif av1_free_mc_tmp_buf(&pbi->td); aom_img_metadata_array_free(pbi->metadata); av1_remove_common(&pbi->common); @@ -260,16 +268,17 @@ const YV12_BUFFER_CONFIG *const cfg = get_ref_frame(cm, idx); if (cfg == NULL) { - aom_internal_error(&pbi->error, AOM_CODEC_ERROR, "No reference frame"); - return AOM_CODEC_ERROR; + aom_set_error(&pbi->error, AOM_CODEC_ERROR, "No reference frame"); + return pbi->error.error_code; } - if (!equal_dimensions(cfg, sd)) - aom_internal_error(&pbi->error, AOM_CODEC_ERROR, - "Incorrect buffer dimensions"); - else - aom_yv12_copy_frame(cfg, sd, num_planes); + if (!equal_dimensions(cfg, sd)) { + aom_set_error(&pbi->error, AOM_CODEC_ERROR, "Incorrect buffer dimensions"); + return pbi->error.error_code; + } - return pbi->error.error_code; + aom_yv12_copy_frame(cfg, sd, num_planes); + + return AOM_CODEC_OK; } static int equal_dimensions_and_border(const YV12_BUFFER_CONFIG *a, @@ -286,43 +295,40 @@ int use_external_ref, YV12_BUFFER_CONFIG *sd) { const int num_planes = av1_num_planes(cm); - YV12_BUFFER_CONFIG *ref_buf = NULL; - + // Ensure that aom_internal_error() calls longjmp(). + assert(cm->error->setjmp); // Get the destination reference buffer. - ref_buf = get_ref_frame(cm, idx); + YV12_BUFFER_CONFIG *ref_buf = get_ref_frame(cm, idx); if (ref_buf == NULL) { aom_internal_error(cm->error, AOM_CODEC_ERROR, "No reference frame"); - return AOM_CODEC_ERROR; } if (!use_external_ref) { if (!equal_dimensions(ref_buf, sd)) { aom_internal_error(cm->error, AOM_CODEC_ERROR, "Incorrect buffer dimensions"); - } else { - // Overwrite the reference frame buffer. - aom_yv12_copy_frame(sd, ref_buf, num_planes); } + // Overwrite the reference frame buffer. + aom_yv12_copy_frame(sd, ref_buf, num_planes); } else { if (!equal_dimensions_and_border(ref_buf, sd)) { aom_internal_error(cm->error, AOM_CODEC_ERROR, "Incorrect buffer dimensions"); - } else { - // Overwrite the reference frame buffer pointers. - // Once we no longer need the external reference buffer, these pointers - // are restored. - ref_buf->store_buf_adr[0] = ref_buf->y_buffer; - ref_buf->store_buf_adr[1] = ref_buf->u_buffer; - ref_buf->store_buf_adr[2] = ref_buf->v_buffer; - ref_buf->y_buffer = sd->y_buffer; - ref_buf->u_buffer = sd->u_buffer; - ref_buf->v_buffer = sd->v_buffer; - ref_buf->use_external_reference_buffers = 1; } + // Overwrite the reference frame buffer pointers. + // Once we no longer need the external reference buffer, these pointers + // are restored. + ref_buf->store_buf_adr[0] = ref_buf->y_buffer; + ref_buf->store_buf_adr[1] = ref_buf->u_buffer; + ref_buf->store_buf_adr[2] = ref_buf->v_buffer; + ref_buf->y_buffer = sd->y_buffer; + ref_buf->u_buffer = sd->u_buffer; + ref_buf->v_buffer = sd->v_buffer; + ref_buf->use_external_reference_buffers = 1; } - return cm->error->error_code; + return AOM_CODEC_OK; } aom_codec_err_t av1_copy_new_frame_dec(AV1_COMMON *cm, @@ -330,13 +336,14 @@ YV12_BUFFER_CONFIG *sd) { const int num_planes = av1_num_planes(cm); - if (!equal_dimensions_and_border(new_frame, sd)) - aom_internal_error(cm->error, AOM_CODEC_ERROR, - "Incorrect buffer dimensions"); - else - aom_yv12_copy_frame(new_frame, sd, num_planes); + if (!equal_dimensions_and_border(new_frame, sd)) { + aom_set_error(cm->error, AOM_CODEC_ERROR, "Incorrect buffer dimensions"); + return cm->error->error_code; + } - return cm->error->error_code; + aom_yv12_copy_frame(new_frame, sd, num_planes); + + return AOM_CODEC_OK; } static void release_current_frame(AV1Decoder *pbi) {
diff --git a/av1/decoder/decoder.h b/av1/decoder/decoder.h index d6610cd..02ad429 100644 --- a/av1/decoder/decoder.h +++ b/av1/decoder/decoder.h
@@ -229,6 +229,53 @@ int alloc_tile_cols; } AV1DecTileMT; +#if CONFIG_COLLECT_COMPONENT_TIMING +#include "aom_ports/aom_timer.h" +// Adjust the following to add new components. +enum { + decode_mbmi_block_time, + decode_token_recon_block_intra_time, + predict_inter_block_time, + decode_reconstruct_tx_inter_time, + decode_token_recon_block_inter_time, + decode_token_recon_block_time, + parse_decode_block_time, + decode_tile_time, + decode_tiles_time, + av1_loop_filter_frame_time, + cdef_and_lr_time, + av1_decode_tg_tiles_and_wrapup_time, + aom_decode_frame_from_obus_time, + kTimingComponents, +} UENUM1BYTE(TIMING_COMPONENT); + +static inline char const *get_component_name(int index) { + switch (index) { + case decode_mbmi_block_time: return "decode_mbmi_block_time"; + case decode_token_recon_block_intra_time: + return "decode_token_recon_block_intra_time"; + case predict_inter_block_time: return "predict_inter_block_time"; + case decode_reconstruct_tx_inter_time: + return "decode_reconstruct_tx_inter_time"; + case decode_token_recon_block_inter_time: + return "decode_token_recon_block_inter_time"; + case decode_token_recon_block_time: return "decode_token_recon_block_time"; + case parse_decode_block_time: return "parse_decode_block_time"; + case decode_tile_time: return "decode_tile_time"; + case decode_tiles_time: return "decode_tiles_time"; + case av1_loop_filter_frame_time: return "av1_loop_filter_frame_time"; + case cdef_and_lr_time: return "cdef_and_lr_time"; + case av1_decode_tg_tiles_and_wrapup_time: + return "av1_decode_tg_tiles_and_wrapup_time"; + case aom_decode_frame_from_obus_time: + return "aom_decode_frame_from_obus_time"; + + default: assert(0); + } + return "error"; +} +#endif + typedef struct AV1Decoder { DecoderCodingBlock dcb; @@ -287,9 +334,13 @@ #if CONFIG_INSPECTION aom_inspect_cb inspect_cb; void *inspect_ctx; + int *sb_bits; + int sb_bits_alloc_size; #endif int operating_point; int current_operating_point; + // If nonzero, the maximum frame size (width * height). 0 means unlimited. + unsigned int frame_size_limit; int seen_frame_header; // The expected start_tile (tg_start syntax element) of the next tile group. int next_start_tile; @@ -369,6 +420,18 @@ * Number of spatial layers: may be > 1 for SVC (scalable vector coding). */ unsigned int number_spatial_layers; + +#if CONFIG_COLLECT_COMPONENT_TIMING + /*! + * component_time[] are initialized to zero while decoder starts. + */ + uint64_t component_time[kTimingComponents]; + struct aom_usec_timer component_timer[kTimingComponents]; + /*! + * frame_component_time[] are initialized to zero at beginning of each frame. + */ + uint64_t frame_component_time[kTimingComponents]; +#endif } AV1Decoder; // Returns 0 on success. Sets pbi->common.error.error_code to a nonzero error @@ -445,6 +508,28 @@ /*!\endcond */ +#if CONFIG_COLLECT_COMPONENT_TIMING +static inline void start_timing(AV1Decoder *pbi, int component) { + aom_usec_timer_start(&pbi->component_timer[component]); +} +static inline void end_timing(AV1Decoder *pbi, int component) { + aom_usec_timer_mark(&pbi->component_timer[component]); + pbi->frame_component_time[component] += + aom_usec_timer_elapsed(&pbi->component_timer[component]); +} + +static inline char const *get_frame_type_enum(int type) { + switch (type) { + case 0: return "KEY_FRAME"; + case 1: return "INTER_FRAME"; + case 2: return "INTRA_ONLY_FRAME"; + case 3: return "S_FRAME"; + default: assert(0); + } + return "error"; +} +#endif + #ifdef __cplusplus } // extern "C" #endif
diff --git a/av1/decoder/decodetxb.c b/av1/decoder/decodetxb.c index 17d67cb..e37f239 100644 --- a/av1/decoder/decodetxb.c +++ b/av1/decoder/decodetxb.c
@@ -30,7 +30,6 @@ if (length > 20) { aom_internal_error(xd->error_info, AOM_CODEC_CORRUPT_FRAME, "Invalid length in read_golomb"); - break; } } @@ -341,6 +340,8 @@ TXB_CTX txb_ctx; get_txb_ctx(plane_bsize, tx_size, plane, pd->above_entropy_context + col, pd->left_entropy_context + row, &txb_ctx); + + assert(dcb->xd.error_info->setjmp); const uint8_t cul_level = read_coeffs_txb(cm, dcb, r, row, col, plane, &txb_ctx, tx_size); av1_set_entropy_contexts(xd, pd, plane, plane_bsize, tx_size, cul_level, col,
diff --git a/av1/decoder/grain_synthesis.c b/av1/decoder/grain_synthesis.c index 0655b68..bbf5895 100644 --- a/av1/decoder/grain_synthesis.c +++ b/av1/decoder/grain_synthesis.c
@@ -1379,7 +1379,6 @@ int mc_identity = src->mc == AOM_CICP_MC_IDENTITY ? 1 : 0; switch (src->fmt) { - case AOM_IMG_FMT_AOMI420: case AOM_IMG_FMT_I420: use_high_bit_depth = 0; chroma_subsamp_x = 1;
diff --git a/av1/decoder/inspection.c b/av1/decoder/inspection.c index f84ed17..0eb6f38 100644 --- a/av1/decoder/inspection.c +++ b/av1/decoder/inspection.c
@@ -156,6 +156,16 @@ } // delta_q mi->current_qindex = mbmi->current_qindex; + + if (pbi->sb_bits) { + const int mib_size = cm->seq_params->mib_size; + const int sb_cols = (cm->mi_params.mi_cols + mib_size - 1) / mib_size; + const int sb_row = j / mib_size; + const int sb_col = i / mib_size; + mi->sb_bits = (int16_t)pbi->sb_bits[sb_row * sb_cols + sb_col]; + } else { + mi->sb_bits = 0; + } } } return 1;
diff --git a/av1/decoder/inspection.h b/av1/decoder/inspection.h index 7d1c14d..132147b 100644 --- a/av1/decoder/inspection.h +++ b/av1/decoder/inspection.h
@@ -55,6 +55,7 @@ int16_t intrabc; int16_t palette; int16_t uv_palette; + int16_t sb_bits; }; typedef struct insp_frame_data insp_frame_data;
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c index ea4b1b6..9df0e88 100644 --- a/av1/decoder/obu.c +++ b/av1/decoder/obu.c
@@ -66,15 +66,14 @@ return 0; } -static int byte_alignment(AV1_COMMON *const cm, - struct aom_read_bit_buffer *const rb) { +static void byte_alignment(struct aom_internal_error_info *error_info, + struct aom_read_bit_buffer *const rb) { while (rb->bit_offset & 7) { if (aom_rb_read_bit(rb)) { - cm->error->error_code = AOM_CODEC_CORRUPT_FRAME; - return -1; + aom_internal_error(error_info, AOM_CODEC_CORRUPT_FRAME, + "byte_alignment() is not all 0 bits"); } } - return 0; } static uint32_t read_temporal_delimiter_obu(void) { return 0; } @@ -100,7 +99,8 @@ // On success, sets pbi->sequence_header_ready to 1 and returns the number of // bytes read from 'rb'. -// On failure, sets pbi->common.error.error_code and returns 0. +// On failure, calls aom_internal_error() and does not return, or sets +// pbi->error.error_code and returns 0. static uint32_t read_sequence_header_obu(AV1Decoder *pbi, struct aom_read_bit_buffer *rb) { AV1_COMMON *const cm = &pbi->common; @@ -137,8 +137,9 @@ seq_params->operating_point_idc[0] = 0; seq_params->has_nonzero_operating_point_idc = false; if (!read_bitstream_level(&seq_params->seq_level_idx[0], rb)) { - pbi->error.error_code = AOM_CODEC_UNSUP_BITSTREAM; - return 0; + aom_internal_error(&pbi->error, AOM_CODEC_UNSUP_BITSTREAM, + "Value %d of seq_level_idx[%d] is not yet defined", + seq_params->seq_level_idx[0], 0); } seq_params->tier[0] = 0; seq_params->op_params[0].decoder_model_param_present_flag = 0; @@ -164,8 +165,9 @@ if (seq_params->operating_point_idc[i] != 0) seq_params->has_nonzero_operating_point_idc = true; if (!read_bitstream_level(&seq_params->seq_level_idx[i], rb)) { - pbi->error.error_code = AOM_CODEC_UNSUP_BITSTREAM; - return 0; + aom_internal_error(&pbi->error, AOM_CODEC_UNSUP_BITSTREAM, + "Value %d of seq_level_idx[%d] is not yet defined", + seq_params->seq_level_idx[i], i); } // This is the seq_level_idx[i] > 7 check in the spec. seq_level_idx 7 // is equivalent to level 3.3. @@ -196,7 +198,7 @@ aom_internal_error(&pbi->error, AOM_CODEC_UNSUP_BITSTREAM, "AV1 does not support this combination of " "profile, level, and tier."); - // Buffer size in bits/s is bitrate in bits/s * 1 s + // Buffer size in bits is bitrate in bits/s * 1 s seq_params->op_params[i].buffer_size = seq_params->op_params[i].bitrate; } if (seq_params->timing_info_present && @@ -236,10 +238,11 @@ operating_point = 0; pbi->current_operating_point = seq_params->operating_point_idc[operating_point]; - if (aom_get_num_layers_from_operating_point_idc( - pbi->current_operating_point, &pbi->number_spatial_layers, - &pbi->number_temporal_layers) != AOM_CODEC_OK) { - pbi->error.error_code = AOM_CODEC_ERROR; + aom_codec_err_t status = aom_get_num_layers_from_operating_point_idc( + pbi->current_operating_point, &pbi->number_spatial_layers, + &pbi->number_temporal_layers); + if (status != AOM_CODEC_OK) { + pbi->error.error_code = status; return 0; } @@ -293,11 +296,11 @@ } // On success, returns the tile group header size. On failure, calls -// aom_internal_error() and returns -1. -static int32_t read_tile_group_header(AV1Decoder *pbi, - struct aom_read_bit_buffer *rb, - int *start_tile, int *end_tile, - int tile_start_implicit) { +// aom_internal_error() and does not return. +static uint32_t read_tile_group_header(AV1Decoder *pbi, + struct aom_read_bit_buffer *rb, + int *start_tile, int *end_tile, + int tile_start_implicit) { AV1_COMMON *const cm = &pbi->common; CommonTileParams *const tiles = &cm->tiles; uint32_t saved_bit_offset = rb->bit_offset; @@ -310,7 +313,6 @@ aom_internal_error( &pbi->error, AOM_CODEC_UNSUP_BITSTREAM, "For OBU_FRAME type obu tile_start_and_end_present_flag must be 0"); - return -1; } } if (tiles->large_scale || num_tiles == 1 || @@ -326,47 +328,43 @@ aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "tg_start (%d) must be equal to %d", *start_tile, pbi->next_start_tile); - return -1; } if (*start_tile > *end_tile) { aom_internal_error( &pbi->error, AOM_CODEC_CORRUPT_FRAME, "tg_end (%d) must be greater than or equal to tg_start (%d)", *end_tile, *start_tile); - return -1; } if (*end_tile >= num_tiles) { aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "tg_end (%d) must be less than NumTiles (%d)", *end_tile, num_tiles); - return -1; } pbi->next_start_tile = (*end_tile == num_tiles - 1) ? 0 : *end_tile + 1; return ((rb->bit_offset - saved_bit_offset + 7) >> 3); } -// On success, returns the tile group OBU size. On failure, sets -// pbi->common.error.error_code and returns 0. +// On success, returns the tile group OBU size. On failure, calls +// aom_internal_error() and does not return. static uint32_t read_one_tile_group_obu( AV1Decoder *pbi, struct aom_read_bit_buffer *rb, int is_first_tg, const uint8_t *data, const uint8_t *data_end, const uint8_t **p_data_end, int *is_last_tg, int tile_start_implicit) { AV1_COMMON *const cm = &pbi->common; int start_tile, end_tile; - int32_t header_size, tg_payload_size; assert((rb->bit_offset & 7) == 0); assert(rb->bit_buffer + aom_rb_bytes_read(rb) == data); - header_size = read_tile_group_header(pbi, rb, &start_tile, &end_tile, - tile_start_implicit); - if (header_size == -1 || byte_alignment(cm, rb)) return 0; + uint32_t header_size = read_tile_group_header(pbi, rb, &start_tile, &end_tile, + tile_start_implicit); + byte_alignment(&pbi->error, rb); data += header_size; av1_decode_tg_tiles_and_wrapup(pbi, data, data_end, p_data_end, start_tile, end_tile, is_first_tg); - tg_payload_size = (uint32_t)(*p_data_end - data); + uint32_t tg_payload_size = (uint32_t)(*p_data_end - data); *is_last_tg = end_tile == cm->tiles.rows * cm->tiles.cols - 1; return header_size + tg_payload_size; @@ -482,7 +480,7 @@ // Only called while large_scale_tile = 1. // // On success, returns the tile list OBU size. On failure, sets -// pbi->common.error.error_code and returns 0. +// pbi->error.error_code and returns 0. static uint32_t read_and_decode_one_tile_list(AV1Decoder *pbi, struct aom_read_bit_buffer *rb, const uint8_t *data, @@ -628,7 +626,7 @@ // On failure, calls aom_internal_error() and does not return. static void read_metadata_itut_t35(AV1Decoder *const pbi, const uint8_t *data, - size_t sz) { + size_t sz, bool has_obu_extension_header) { if (sz == 0) { aom_internal_error(&pbi->error, AOM_CODEC_CORRUPT_FRAME, "itu_t_t35_country_code is missing"); @@ -657,7 +655,9 @@ data[end_index]); } alloc_read_metadata(pbi, OBU_METADATA_TYPE_ITUT_T35, data, end_index, - AOM_MIF_ANY_FRAME); + has_obu_extension_header + ? AOM_MIF_ANY_FRAME_LAYER_SPECIFIC + : AOM_MIF_ANY_FRAME); } // On success, returns the number of bytes read from 'data'. On failure, calls @@ -782,9 +782,10 @@ // Checks the metadata for correct syntax but ignores the parsed metadata. // // On success, returns the number of bytes read from 'data'. On failure, sets -// pbi->common.error.error_code and returns 0, or calls aom_internal_error() -// and does not return. -static size_t read_metadata(AV1Decoder *pbi, const uint8_t *data, size_t sz) { +// pbi->error.error_code and returns 0, or calls aom_internal_error() and does +// not return. +static size_t read_metadata(AV1Decoder *pbi, const uint8_t *data, size_t sz, + bool has_obu_extension_header) { size_t type_length; uint64_t type_value; if (aom_uleb_decode(data, sz, &type_value, &type_length) < 0) { @@ -803,7 +804,8 @@ } if (metadata_type == OBU_METADATA_TYPE_ITUT_T35) { // read_metadata_itut_t35() checks trailing bits. - read_metadata_itut_t35(pbi, data + type_length, sz - type_length); + read_metadata_itut_t35(pbi, data + type_length, sz - type_length, + has_obu_extension_header); return sz; } else if (metadata_type == OBU_METADATA_TYPE_HDR_CLL) { size_t bytes_read = @@ -841,10 +843,10 @@ return type_length + (rb.bit_offset >> 3); } -// On success, returns 'sz'. On failure, sets pbi->common.error.error_code and -// returns 0. -static size_t read_padding(AV1_COMMON *const cm, const uint8_t *data, - size_t sz) { +// On success, returns 'sz'. On failure, sets error_info->error_code and returns +// 0. +static size_t read_padding(struct aom_internal_error_info *error_info, + const uint8_t *data, size_t sz) { // The spec allows a padding OBU to be header-only (i.e., obu_size = 0). So // check trailing bits only if sz > 0. if (sz > 0) { @@ -852,7 +854,7 @@ // trailing byte should be 0x80. See https://crbug.com/aomedia/2393. const uint8_t last_nonzero_byte = get_last_nonzero_byte(data, sz); if (last_nonzero_byte != 0x80) { - cm->error->error_code = AOM_CODEC_CORRUPT_FRAME; + error_info->error_code = AOM_CODEC_CORRUPT_FRAME; return 0; } } @@ -865,6 +867,9 @@ int aom_decode_frame_from_obus(struct AV1Decoder *pbi, const uint8_t *data, const uint8_t *data_end, const uint8_t **p_data_end) { +#if CONFIG_COLLECT_COMPONENT_TIMING + start_timing(pbi, aom_decode_frame_from_obus_time); +#endif AV1_COMMON *const cm = &pbi->common; int frame_decoding_finished = 0; int is_first_tg_obu_received = 1; @@ -880,6 +885,9 @@ const uint8_t *frame_header = data; uint32_t frame_header_size = 0; ObuHeader obu_header; + + assert(pbi->error.setjmp); + memset(&obu_header, 0, sizeof(obu_header)); pbi->seen_frame_header = 0; pbi->next_start_tile = 0; @@ -1033,8 +1041,7 @@ if (obu_header.type != OBU_FRAME) break; obu_payload_offset = frame_header_size; // Byte align the reader before reading the tile group. - // byte_alignment() has set pbi->error.error_code if it returns -1. - if (byte_alignment(cm, &rb)) return -1; + byte_alignment(&pbi->error, &rb); AOM_FALLTHROUGH_INTENDED; // fall through to read tile group. case OBU_TILE_GROUP: if (!pbi->seen_frame_header) { @@ -1049,7 +1056,6 @@ pbi, &rb, is_first_tg_obu_received, data + obu_payload_offset, data + payload_size, p_data_end, &frame_decoding_finished, obu_header.type == OBU_FRAME); - if (pbi->error.error_code != AOM_CODEC_OK) return -1; is_first_tg_obu_received = 0; if (frame_decoding_finished) { pbi->seen_frame_header = 0; @@ -1057,10 +1063,12 @@ } pbi->num_tile_groups++; break; - case OBU_METADATA: - decoded_payload_size = read_metadata(pbi, data, payload_size); + case OBU_METADATA: { + decoded_payload_size = + read_metadata(pbi, data, payload_size, obu_header.has_extension); if (pbi->error.error_code != AOM_CODEC_OK) return -1; break; + } case OBU_TILE_LIST: if (CONFIG_NORMAL_TILE_MODE) { pbi->error.error_code = AOM_CODEC_UNSUP_BITSTREAM; @@ -1082,7 +1090,7 @@ if (pbi->error.error_code != AOM_CODEC_OK) return -1; break; case OBU_PADDING: - decoded_payload_size = read_padding(cm, data, payload_size); + decoded_payload_size = read_padding(&pbi->error, data, payload_size); if (pbi->error.error_code != AOM_CODEC_OK) return -1; break; default: @@ -1115,5 +1123,29 @@ } if (pbi->error.error_code != AOM_CODEC_OK) return -1; + +#if CONFIG_COLLECT_COMPONENT_TIMING + end_timing(pbi, aom_decode_frame_from_obus_time); + + // Print out timing information. + int i; + fprintf(stderr, + "\n Frame number: %d, Frame type: %s, Show Frame: %d, Show existing " + "frame: %d\n", + cm->current_frame.frame_number, + get_frame_type_enum(cm->current_frame.frame_type), cm->show_frame, + cm->show_existing_frame); + // Exclude show_existing_frame since it doesn't take much time. + if (!cm->show_existing_frame) { + for (i = 0; i < kTimingComponents; i++) { + pbi->component_time[i] += pbi->frame_component_time[i]; + fprintf(stderr, " %s: %" PRId64 " us (total: %" PRId64 " us)\n", + get_component_name(i), pbi->frame_component_time[i], + pbi->component_time[i]); + pbi->frame_component_time[i] = 0; + } + } +#endif + return frame_decoding_finished; }
diff --git a/av1/encoder/aq_cyclicrefresh.c b/av1/encoder/aq_cyclicrefresh.c index d0135fd..99f90fe 100644 --- a/av1/encoder/aq_cyclicrefresh.c +++ b/av1/encoder/aq_cyclicrefresh.c
@@ -439,6 +439,8 @@ // should we enable cyclic refresh on this frame. cr->apply_cyclic_refresh = 1; if (frame_is_intra_only(cm) || is_lossless_requested(&cpi->oxcf.rc_cfg) || + (is_one_pass_rt_lag_params(cpi) && + (cpi->refresh_frame.alt_ref_frame || cpi->rc.is_src_frame_alt_ref)) || cpi->roi.enabled || cpi->rc.high_motion_content_screen_rtc || scene_change_detected || svc->temporal_layer_id > 0 || svc->prev_number_spatial_layers != svc->number_spatial_layers || @@ -645,7 +647,8 @@ qindex2, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); av1_set_segdata(seg, CR_SEGMENT_ID_BOOST1, SEG_LVL_ALT_Q, qindex_delta);
diff --git a/av1/encoder/arm/av1_highbd_quantize_neon.c b/av1/encoder/arm/av1_highbd_quantize_neon.c index c1016db..fd4749d 100644 --- a/av1/encoder/arm/av1_highbd_quantize_neon.c +++ b/av1/encoder/arm/av1_highbd_quantize_neon.c
@@ -60,15 +60,15 @@ static inline int16x8_t get_max_lane_eob(const int16_t *iscan, int16x8_t v_eobmax, uint16x8_t v_mask) { - const int16x8_t v_iscan = vld1q_s16(&iscan[0]); - const int16x8_t v_iscan_plus1 = vaddq_s16(v_iscan, vdupq_n_s16(1)); - const int16x8_t v_nz_iscan = vbslq_s16(v_mask, v_iscan_plus1, vdupq_n_s16(0)); + const int16x8_t v_iscan = vld1q_s16(iscan); + const int16x8_t v_nz_iscan = vbslq_s16(v_mask, v_iscan, vdupq_n_s16(-1)); return vmaxq_s16(v_eobmax, v_nz_iscan); } static inline uint16_t get_max_eob(int16x8_t v_eobmax) { #if AOM_ARCH_AARCH64 - return (uint16_t)vmaxvq_s16(v_eobmax); + int16_t max_val = vmaxvq_s16(v_eobmax); + return (uint16_t)max_val + 1; #else const int16x4_t v_eobmax_3210 = vmax_s16(vget_low_s16(v_eobmax), vget_high_s16(v_eobmax)); @@ -80,7 +80,7 @@ vshr_n_s64(vreinterpret_s64_s16(v_eobmax_tmp), 16); const int16x4_t v_eobmax_final = vmax_s16(v_eobmax_tmp, vreinterpret_s16_s64(v_eobmax_xxx3)); - return (uint16_t)vget_lane_s16(v_eobmax_final, 0); + return (uint16_t)vget_lane_s16(v_eobmax_final, 0) + 1; #endif }
diff --git a/av1/encoder/arm/highbd_pickrst_neon.c b/av1/encoder/arm/highbd_pickrst_neon.c index bb85a4b..499eb58 100644 --- a/av1/encoder/arm/highbd_pickrst_neon.c +++ b/av1/encoder/arm/highbd_pickrst_neon.c
@@ -463,64 +463,34 @@ { const int16_t *d_t = d; - if (height % 2) { - int32x4_t deltas[(WIENER_WIN + 1) * 2] = { vdupq_n_s32(0) }; - int32x4_t deltas_tr[(WIENER_WIN + 1) * 2] = { vdupq_n_s32(0) }; - int16x8_t ds[WIENER_WIN * 2]; + int32x4_t deltas[WIENER_WIN_CHROMA] = { vdupq_n_s32(0) }; + int16x8_t ds[WIENER_WIN_CHROMA + 1]; - load_s16_8x4(d_t, d_stride, &ds[0], &ds[2], &ds[4], &ds[6]); - load_s16_8x4(d_t + width, d_stride, &ds[1], &ds[3], &ds[5], &ds[7]); - d_t += 4 * d_stride; + ds[0] = load_unaligned_s16_4x2(d_t + 0 * d_stride, width); + ds[1] = load_unaligned_s16_4x2(d_t + 1 * d_stride, width); + ds[2] = load_unaligned_s16_4x2(d_t + 2 * d_stride, width); + ds[3] = load_unaligned_s16_4x2(d_t + 3 * d_stride, width); - step3_win5_oneline_neon(&d_t, d_stride, width, height, ds, deltas); - transpose_arrays_s32_8x8(deltas, deltas_tr); + step3_win5_neon(d_t + 4 * d_stride, d_stride, width, height, ds, deltas); - update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, - deltas_tr[0], vgetq_lane_s32(deltas_tr[4], 0), - H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); + transpose_elems_inplace_s32_4x4(&deltas[0], &deltas[1], &deltas[2], + &deltas[3]); - update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, - deltas_tr[1], vgetq_lane_s32(deltas_tr[5], 0), - H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); + update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, + deltas[0], vgetq_lane_s32(deltas[4], 0), + H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); - update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, - deltas_tr[2], vgetq_lane_s32(deltas_tr[6], 0), - H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); + update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, + deltas[1], vgetq_lane_s32(deltas[4], 1), + H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); - update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, - deltas_tr[3], vgetq_lane_s32(deltas_tr[7], 0), - H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); + update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, + deltas[2], vgetq_lane_s32(deltas[4], 2), + H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); - } else { - int32x4_t deltas[WIENER_WIN_CHROMA * 2] = { vdupq_n_s32(0) }; - int16x8_t ds[WIENER_WIN_CHROMA * 2]; - - ds[0] = load_unaligned_s16_4x2(d_t + 0 * d_stride, width); - ds[1] = load_unaligned_s16_4x2(d_t + 1 * d_stride, width); - ds[2] = load_unaligned_s16_4x2(d_t + 2 * d_stride, width); - ds[3] = load_unaligned_s16_4x2(d_t + 3 * d_stride, width); - - step3_win5_neon(d_t + 4 * d_stride, d_stride, width, height, ds, deltas); - - transpose_elems_inplace_s32_4x4(&deltas[0], &deltas[1], &deltas[2], - &deltas[3]); - - update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, - deltas[0], vgetq_lane_s32(deltas[4], 0), - H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); - - update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, - deltas[1], vgetq_lane_s32(deltas[4], 1), - H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); - - update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, - deltas[2], vgetq_lane_s32(deltas[4], 2), - H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); - - update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, - deltas[3], vgetq_lane_s32(deltas[4], 3), - H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); - } + update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, + deltas[3], vgetq_lane_s32(deltas[4], 3), + H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); } // Step 4: Derive the top and left edge of each square. No square in top and
diff --git a/av1/encoder/arm/highbd_temporal_filter_neon.c b/av1/encoder/arm/highbd_temporal_filter_neon.c index 59cd3fc..dddac6a 100644 --- a/av1/encoder/arm/highbd_temporal_filter_neon.c +++ b/av1/encoder/arm/highbd_temporal_filter_neon.c
@@ -289,6 +289,13 @@ const int *subblock_mses, const int q_factor, const int filter_strength, int tf_wgt_calc_lvl, const uint8_t *pred8, uint32_t *accum, uint16_t *count) { + if (block_size == BLOCK_64X64) { + av1_apply_temporal_filter_c(frame_to_filter, mbd, block_size, mb_row, + mb_col, num_planes, noise_levels, subblock_mvs, + subblock_mses, q_factor, filter_strength, + tf_wgt_calc_lvl, pred8, accum, count); + return; + } const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with Neon!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE);
diff --git a/av1/encoder/arm/pickrst_neon.c b/av1/encoder/arm/pickrst_neon.c index f83e693..70ee3b6 100644 --- a/av1/encoder/arm/pickrst_neon.c +++ b/av1/encoder/arm/pickrst_neon.c
@@ -1027,64 +1027,34 @@ { const int16_t *d_t = d; - if (height % 2) { - int32x4_t deltas[(WIENER_WIN + 1) * 2] = { vdupq_n_s32(0) }; - int32x4_t deltas_tr[(WIENER_WIN + 1) * 2] = { vdupq_n_s32(0) }; - int16x8_t ds[WIENER_WIN * 2]; + int32x4_t deltas[WIENER_WIN_CHROMA] = { vdupq_n_s32(0) }; + int16x8_t ds[WIENER_WIN_CHROMA + 1]; - load_s16_8x4(d_t, d_stride, &ds[0], &ds[2], &ds[4], &ds[6]); - load_s16_8x4(d_t + width, d_stride, &ds[1], &ds[3], &ds[5], &ds[7]); - d_t += 4 * d_stride; + ds[0] = load_unaligned_s16_4x2(d_t + 0 * d_stride, width); + ds[1] = load_unaligned_s16_4x2(d_t + 1 * d_stride, width); + ds[2] = load_unaligned_s16_4x2(d_t + 2 * d_stride, width); + ds[3] = load_unaligned_s16_4x2(d_t + 3 * d_stride, width); - step3_win5_oneline_neon(&d_t, d_stride, width, height, ds, deltas); - transpose_arrays_s32_8x8(deltas, deltas_tr); + step3_win5_neon(d_t + 4 * d_stride, d_stride, width, height, ds, deltas); - update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, - deltas_tr[0], vgetq_lane_s32(deltas_tr[4], 0), - H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); + transpose_elems_inplace_s32_4x4(&deltas[0], &deltas[1], &deltas[2], + &deltas[3]); - update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, - deltas_tr[1], vgetq_lane_s32(deltas_tr[5], 0), - H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); + update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, + deltas[0], vgetq_lane_s32(deltas[4], 0), + H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); - update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, - deltas_tr[2], vgetq_lane_s32(deltas_tr[6], 0), - H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); + update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, + deltas[1], vgetq_lane_s32(deltas[4], 1), + H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); - update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, - deltas_tr[3], vgetq_lane_s32(deltas_tr[7], 0), - H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); + update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, + deltas[2], vgetq_lane_s32(deltas[4], 2), + H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); - } else { - int32x4_t deltas[WIENER_WIN_CHROMA * 2] = { vdupq_n_s32(0) }; - int16x8_t ds[WIENER_WIN_CHROMA * 2]; - - ds[0] = load_unaligned_s16_4x2(d_t + 0 * d_stride, width); - ds[1] = load_unaligned_s16_4x2(d_t + 1 * d_stride, width); - ds[2] = load_unaligned_s16_4x2(d_t + 2 * d_stride, width); - ds[3] = load_unaligned_s16_4x2(d_t + 3 * d_stride, width); - - step3_win5_neon(d_t + 4 * d_stride, d_stride, width, height, ds, deltas); - - transpose_elems_inplace_s32_4x4(&deltas[0], &deltas[1], &deltas[2], - &deltas[3]); - - update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, - deltas[0], vgetq_lane_s32(deltas[4], 0), - H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); - - update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, - deltas[1], vgetq_lane_s32(deltas[4], 1), - H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); - - update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, - deltas[2], vgetq_lane_s32(deltas[4], 2), - H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); - - update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, - deltas[3], vgetq_lane_s32(deltas[4], 3), - H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); - } + update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, + deltas[3], vgetq_lane_s32(deltas[4], 3), + H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); } // Step 4: Derive the top and left edge of each square. No square in top and
diff --git a/av1/encoder/arm/pickrst_neon.h b/av1/encoder/arm/pickrst_neon.h index 356c42d..2eeece8 100644 --- a/av1/encoder/arm/pickrst_neon.h +++ b/av1/encoder/arm/pickrst_neon.h
@@ -422,9 +422,8 @@ dst[4] = src[4] + delta4; } -static inline void compute_delta_step3_two_lines(int32x4_t *sum, - const int16x8_t src, - const int16x8_t dgd) { +static inline void compute_delta_step3_win5(int32x4_t *sum, const int16x8_t src, + const int16x8_t dgd) { *sum = vmlsl_s16(*sum, vget_low_s16(src), vget_low_s16(dgd)); *sum = vmlal_s16(*sum, vget_high_s16(src), vget_high_s16(dgd)); } @@ -437,16 +436,16 @@ ds[4] = load_unaligned_s16_4x2(d + 0 * d_stride, width); ds[5] = load_unaligned_s16_4x2(d + 1 * d_stride, width); - compute_delta_step3_two_lines(&deltas[0], ds[0], ds[0]); - compute_delta_step3_two_lines(&deltas[1], ds[0], ds[1]); - compute_delta_step3_two_lines(&deltas[2], ds[0], ds[2]); - compute_delta_step3_two_lines(&deltas[3], ds[0], ds[3]); - compute_delta_step3_two_lines(&deltas[4], ds[0], ds[4]); - compute_delta_step3_two_lines(&deltas[0], ds[1], ds[1]); - compute_delta_step3_two_lines(&deltas[1], ds[1], ds[2]); - compute_delta_step3_two_lines(&deltas[2], ds[1], ds[3]); - compute_delta_step3_two_lines(&deltas[3], ds[1], ds[4]); - compute_delta_step3_two_lines(&deltas[4], ds[1], ds[5]); + compute_delta_step3_win5(&deltas[0], ds[0], ds[0]); + compute_delta_step3_win5(&deltas[1], ds[0], ds[1]); + compute_delta_step3_win5(&deltas[2], ds[0], ds[2]); + compute_delta_step3_win5(&deltas[3], ds[0], ds[3]); + compute_delta_step3_win5(&deltas[4], ds[0], ds[4]); + compute_delta_step3_win5(&deltas[0], ds[1], ds[1]); + compute_delta_step3_win5(&deltas[1], ds[1], ds[2]); + compute_delta_step3_win5(&deltas[2], ds[1], ds[3]); + compute_delta_step3_win5(&deltas[3], ds[1], ds[4]); + compute_delta_step3_win5(&deltas[4], ds[1], ds[5]); ds[0] = ds[2]; ds[1] = ds[3]; @@ -455,36 +454,17 @@ d += 2 * d_stride; y -= 2; - } while (y); -} + } while (y > 1); -static inline void step3_win5_oneline_neon(const int16_t **const d, - const int32_t d_stride, - const int32_t width, - const int32_t height, int16x8_t *ds, - int32x4_t *deltas) { - int32_t y = height; - do { - ds[8] = vld1q_s16(*d); - ds[9] = vld1q_s16(*d + width); + if (y) { + ds[4] = load_unaligned_s16_4x2(d + 0 * d_stride, width); - compute_delta_step3(&deltas[0], &deltas[4], ds[0], ds[1], ds[0], ds[1]); - compute_delta_step3(&deltas[1], &deltas[5], ds[0], ds[1], ds[2], ds[3]); - compute_delta_step3(&deltas[2], &deltas[6], ds[0], ds[1], ds[4], ds[5]); - compute_delta_step3(&deltas[3], &deltas[7], ds[0], ds[1], ds[6], ds[7]); - compute_delta_step3(&deltas[8], &deltas[12], ds[0], ds[1], ds[8], ds[9]); - - ds[0] = ds[2]; - ds[1] = ds[3]; - ds[2] = ds[4]; - ds[3] = ds[5]; - ds[4] = ds[6]; - ds[5] = ds[7]; - ds[6] = ds[8]; - ds[7] = ds[9]; - - *d += d_stride; - } while (--y); + compute_delta_step3_win5(&deltas[0], ds[0], ds[0]); + compute_delta_step3_win5(&deltas[1], ds[0], ds[1]); + compute_delta_step3_win5(&deltas[2], ds[0], ds[2]); + compute_delta_step3_win5(&deltas[3], ds[0], ds[3]); + compute_delta_step3_win5(&deltas[4], ds[0], ds[4]); + } } static inline void derive_triangle_win5_neon(const int16x8_t *d_is,
diff --git a/av1/encoder/arm/pickrst_sve.h b/av1/encoder/arm/pickrst_sve.h index 94c0375..ad03680 100644 --- a/av1/encoder/arm/pickrst_sve.h +++ b/av1/encoder/arm/pickrst_sve.h
@@ -21,12 +21,12 @@ // Swap each half of the dgd vectors so that we can accumulate the result of // the dot-products directly in the destination matrix. static inline int16x8x2_t transpose_dgd(int16x8_t dgd0, int16x8_t dgd1) { - int16x8_t dgd_trn0 = vreinterpretq_s16_s64( + int16x8x2_t dgd_trn; + dgd_trn.val[0] = vreinterpretq_s16_s64( vzip1q_s64(vreinterpretq_s64_s16(dgd0), vreinterpretq_s64_s16(dgd1))); - int16x8_t dgd_trn1 = vreinterpretq_s16_s64( + dgd_trn.val[1] = vreinterpretq_s16_s64( vzip2q_s64(vreinterpretq_s64_s16(dgd0), vreinterpretq_s64_s16(dgd1))); - - return (struct int16x8x2_t){ dgd_trn0, dgd_trn1 }; + return dgd_trn; } static inline void compute_M_one_row_win5(int16x8_t src, int16x8_t dgd[5], @@ -527,64 +527,34 @@ { const int16_t *d_t = d; - if (height % 2) { - int32x4_t deltas[(WIENER_WIN + 1) * 2] = { vdupq_n_s32(0) }; - int32x4_t deltas_tr[(WIENER_WIN + 1) * 2] = { vdupq_n_s32(0) }; - int16x8_t ds[WIENER_WIN * 2]; + int32x4_t deltas[WIENER_WIN_CHROMA] = { vdupq_n_s32(0) }; + int16x8_t ds[WIENER_WIN_CHROMA + 1]; - load_s16_8x4(d_t, d_stride, &ds[0], &ds[2], &ds[4], &ds[6]); - load_s16_8x4(d_t + width, d_stride, &ds[1], &ds[3], &ds[5], &ds[7]); - d_t += 4 * d_stride; + ds[0] = load_unaligned_s16_4x2(d_t + 0 * d_stride, width); + ds[1] = load_unaligned_s16_4x2(d_t + 1 * d_stride, width); + ds[2] = load_unaligned_s16_4x2(d_t + 2 * d_stride, width); + ds[3] = load_unaligned_s16_4x2(d_t + 3 * d_stride, width); - step3_win5_oneline_neon(&d_t, d_stride, width, height, ds, deltas); - transpose_arrays_s32_8x8(deltas, deltas_tr); + step3_win5_neon(d_t + 4 * d_stride, d_stride, width, height, ds, deltas); - update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, - deltas_tr[0], vgetq_lane_s32(deltas_tr[4], 0), - H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); + transpose_elems_inplace_s32_4x4(&deltas[0], &deltas[1], &deltas[2], + &deltas[3]); - update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, - deltas_tr[1], vgetq_lane_s32(deltas_tr[5], 0), - H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); + update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, + deltas[0], vgetq_lane_s32(deltas[4], 0), + H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); - update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, - deltas_tr[2], vgetq_lane_s32(deltas_tr[6], 0), - H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); + update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, + deltas[1], vgetq_lane_s32(deltas[4], 1), + H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); - update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, - deltas_tr[3], vgetq_lane_s32(deltas_tr[7], 0), - H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); + update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, + deltas[2], vgetq_lane_s32(deltas[4], 2), + H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); - } else { - int32x4_t deltas[WIENER_WIN_CHROMA * 2] = { vdupq_n_s32(0) }; - int16x8_t ds[WIENER_WIN_CHROMA * 2]; - - ds[0] = load_unaligned_s16_4x2(d_t + 0 * d_stride, width); - ds[1] = load_unaligned_s16_4x2(d_t + 1 * d_stride, width); - ds[2] = load_unaligned_s16_4x2(d_t + 2 * d_stride, width); - ds[3] = load_unaligned_s16_4x2(d_t + 3 * d_stride, width); - - step3_win5_neon(d_t + 4 * d_stride, d_stride, width, height, ds, deltas); - - transpose_elems_inplace_s32_4x4(&deltas[0], &deltas[1], &deltas[2], - &deltas[3]); - - update_5_stats_neon(H + 0 * wiener_win * wiener_win2 + 0 * wiener_win, - deltas[0], vgetq_lane_s32(deltas[4], 0), - H + 1 * wiener_win * wiener_win2 + 1 * wiener_win); - - update_5_stats_neon(H + 1 * wiener_win * wiener_win2 + 1 * wiener_win, - deltas[1], vgetq_lane_s32(deltas[4], 1), - H + 2 * wiener_win * wiener_win2 + 2 * wiener_win); - - update_5_stats_neon(H + 2 * wiener_win * wiener_win2 + 2 * wiener_win, - deltas[2], vgetq_lane_s32(deltas[4], 2), - H + 3 * wiener_win * wiener_win2 + 3 * wiener_win); - - update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, - deltas[3], vgetq_lane_s32(deltas[4], 3), - H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); - } + update_5_stats_neon(H + 3 * wiener_win * wiener_win2 + 3 * wiener_win, + deltas[3], vgetq_lane_s32(deltas[4], 3), + H + 4 * wiener_win * wiener_win2 + 4 * wiener_win); } // Step 4: Derive the top and left edge of each square. No square in top and
diff --git a/av1/encoder/arm/quantize_neon.c b/av1/encoder/arm/quantize_neon.c index cbeafc7..e9c0ca7 100644 --- a/av1/encoder/arm/quantize_neon.c +++ b/av1/encoder/arm/quantize_neon.c
@@ -29,7 +29,8 @@ static inline uint16_t get_max_eob(int16x8_t v_eobmax) { #if AOM_ARCH_AARCH64 - return (uint16_t)vmaxvq_s16(v_eobmax); + int16_t max_val = vmaxvq_s16(v_eobmax); + return (uint16_t)max_val + 1; #else const int16x4_t v_eobmax_3210 = vmax_s16(vget_low_s16(v_eobmax), vget_high_s16(v_eobmax)); @@ -41,16 +42,15 @@ vshr_n_s64(vreinterpret_s64_s16(v_eobmax_tmp), 16); const int16x4_t v_eobmax_final = vmax_s16(v_eobmax_tmp, vreinterpret_s16_s64(v_eobmax_xxx3)); - return (uint16_t)vget_lane_s16(v_eobmax_final, 0); + return (uint16_t)vget_lane_s16(v_eobmax_final, 0) + 1; #endif } static inline int16x8_t get_max_lane_eob(const int16_t *iscan, int16x8_t v_eobmax, uint16x8_t v_mask) { - const int16x8_t v_iscan = vld1q_s16(&iscan[0]); - const int16x8_t v_iscan_plus1 = vaddq_s16(v_iscan, vdupq_n_s16(1)); - const int16x8_t v_nz_iscan = vbslq_s16(v_mask, v_iscan_plus1, vdupq_n_s16(0)); + const int16x8_t v_iscan = vld1q_s16(iscan); + const int16x8_t v_nz_iscan = vbslq_s16(v_mask, v_iscan, vdupq_n_s16(-1)); return vmaxq_s16(v_eobmax, v_nz_iscan); } @@ -351,6 +351,33 @@ iscan, 2); } +static inline uint16x8_t quantize_b_logscale0_8( + int16x8_t coeff, int16x8_t abs, uint16x8_t cond, int16x8_t round, + int16x8_t dequant, int16x8_t quant, int16x8_t quant_shift, + tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr) { + const int16x8_t zero = vdupq_n_s16(0); + + int16x8_t coeff_sign = vreinterpretq_s16_u16(vcltq_s16(coeff, zero)); + + int16x8_t tmp = vqaddq_s16(abs, round); + tmp = vsraq_n_s16(tmp, vqdmulhq_s16(tmp, quant), 1); + tmp = vqdmulhq_s16(tmp, quant_shift); + + int16x8_t qcoeff = vsubq_s16(veorq_s16(tmp, coeff_sign), coeff_sign); + qcoeff = vbslq_s16(cond, qcoeff, zero); + store_s16q_to_tran_low(qcoeff_ptr, qcoeff); + + int16x8_t dqcoeff = vmulq_s16(tmp, dequant); + dqcoeff = vsubq_s16(veorq_s16(dqcoeff, coeff_sign), coeff_sign); + dqcoeff = vbslq_s16(cond, dqcoeff, zero); + store_s16q_to_tran_low(dqcoeff_ptr, dqcoeff); + + uint16x8_t tmp_mask = vcgtq_s16(tmp, zero); + uint16x8_t nz_mask = vandq_u16(tmp_mask, cond); + + return nz_mask; +} + void aom_quantize_b_neon(const tran_low_t *coeff_ptr, intptr_t n_coeffs, const int16_t *zbin_ptr, const int16_t *round_ptr, const int16_t *quant_ptr, @@ -361,91 +388,61 @@ (void)quant_shift_ptr; (void)scan; - const int zbins[2] = { zbin_ptr[0], zbin_ptr[1] }; + int16x8_t v_eobmax_76543210 = vdupq_n_s16(-1); - memset(qcoeff_ptr, 0, n_coeffs * sizeof(*qcoeff_ptr)); - memset(dqcoeff_ptr, 0, n_coeffs * sizeof(*dqcoeff_ptr)); + int16x8_t v_zbins = vdupq_n_s16(zbin_ptr[1]); + int16x8_t v_round = vdupq_n_s16(round_ptr[1]); + int16x8_t v_dequant = vdupq_n_s16(dequant_ptr[1]); + int16x8_t v_quant = vdupq_n_s16(quant_ptr[1]); + // Shift by 1 in order to save one shift in the kernel function. + int16x8_t v_quant_shift = vdupq_n_s16(quant_shift_ptr[1] >> 1); - const int16x8_t zero = vdupq_n_s16(0); - int16x8_t v_eobmax_76543210 = vreinterpretq_s16_u16(vceqq_s16(zero, zero)); - - int16x8_t vzbins = vdupq_n_s16(zbins[1]), vround = vdupq_n_s16(round_ptr[1]); - int16x8_t vdequant = vdupq_n_s16(dequant_ptr[1]); - int16x8_t vquant = vdupq_n_s16(quant_ptr[1]); - int16x8_t vquant_shift = vdupq_n_s16(quant_shift_ptr[1]); - - int16x8_t v_coeff = load_tran_low_to_s16q(&coeff_ptr[0]); - int16x8_t v_coeff_sign = vshrq_n_s16(v_coeff, 15); + int16x8_t v_zbins0 = vsetq_lane_s16(zbin_ptr[0], v_zbins, 0); + int16x8_t v_coeff = load_tran_low_to_s16q(coeff_ptr); int16x8_t v_abs = vabsq_s16(v_coeff); + uint16x8_t v_cond = vcgeq_s16(v_abs, v_zbins0); - vzbins = vsetq_lane_s16(zbins[0], vzbins, 0); - - uint16x8_t vcond = vcgeq_s16(v_abs, vzbins); - uint64_t nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(vcond)), 0); + uint64_t nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(v_cond)), 0); if (nz_check) { - vround = vsetq_lane_s16(round_ptr[0], vround, 0); - vquant = vsetq_lane_s16(quant_ptr[0], vquant, 0); - vdequant = vsetq_lane_s16(dequant_ptr[0], vdequant, 0); - vquant_shift = vsetq_lane_s16(quant_shift_ptr[0], vquant_shift, 0); + int16x8_t v_round0 = vsetq_lane_s16(round_ptr[0], v_round, 0); + int16x8_t v_quant0 = vsetq_lane_s16(quant_ptr[0], v_quant, 0); + int16x8_t v_dequant0 = vsetq_lane_s16(dequant_ptr[0], v_dequant, 0); + // Shift by 1 in order to save one shift in the kernel function. + int16x8_t v_quant_shift0 = + vsetq_lane_s16(quant_shift_ptr[0] >> 1, v_quant_shift, 0); - int16x8_t vtmp = vqaddq_s16(v_abs, vround); - int16x8_t vtmp2 = vsraq_n_s16(vtmp, vqdmulhq_s16(vtmp, vquant), 1); - vtmp2 = vshrq_n_s16(vqdmulhq_s16(vtmp2, vquant_shift), 1); + const uint16x8_t v_nz_mask = quantize_b_logscale0_8( + v_coeff, v_abs, v_cond, v_round0, v_dequant0, v_quant0, v_quant_shift0, + qcoeff_ptr, dqcoeff_ptr); - int16x8_t vdest = vsubq_s16(veorq_s16(vtmp2, v_coeff_sign), v_coeff_sign); - int16x8_t coeff_nz_mask = - vbslq_s16(vcond, vdest, load_tran_low_to_s16q(&qcoeff_ptr[0])); - store_s16q_to_tran_low(&qcoeff_ptr[0], coeff_nz_mask); - int16x8_t v_deq_abs = vmulq_s16(vtmp2, vdequant); - - vdest = vsubq_s16(veorq_s16(v_deq_abs, v_coeff_sign), v_coeff_sign); - coeff_nz_mask = - vbslq_s16(vcond, vdest, load_tran_low_to_s16q(&dqcoeff_ptr[0])); - store_s16q_to_tran_low(&dqcoeff_ptr[0], coeff_nz_mask); - - vround = vsetq_lane_s16(round_ptr[1], vround, 0); - vquant = vsetq_lane_s16(quant_ptr[1], vquant, 0); - vdequant = vsetq_lane_s16(dequant_ptr[1], vdequant, 0); - vquant_shift = vsetq_lane_s16(quant_shift_ptr[1], vquant_shift, 0); - - uint16x8_t vtmp_mask = vcgtq_s16(vtmp2, zero); - const uint16x8_t v_nz_mask = vandq_u16(vtmp_mask, vcond); - int16x8_t v_iscan = vld1q_s16(&iscan[0]); - vcond = vandq_u16(v_nz_mask, vcgtq_s16(v_iscan, v_eobmax_76543210)); - v_eobmax_76543210 = vbslq_s16(vcond, v_iscan, v_eobmax_76543210); + int16x8_t v_iscan = vld1q_s16(iscan); + int16x8_t v_eobmax = vmaxq_s16(v_iscan, v_eobmax_76543210); + v_eobmax_76543210 = vbslq_s16(v_nz_mask, v_eobmax, v_eobmax_76543210); + } else { + store_s16q_to_tran_low(qcoeff_ptr, vdupq_n_s16(0)); + store_s16q_to_tran_low(dqcoeff_ptr, vdupq_n_s16(0)); } - vzbins = vsetq_lane_s16(zbins[1], vzbins, 0); for (int i = 8; i < n_coeffs; i += 8) { - v_coeff = load_tran_low_to_s16q(&coeff_ptr[i]); - v_coeff_sign = vshrq_n_s16(v_coeff, 15); + v_coeff = load_tran_low_to_s16q(coeff_ptr + i); v_abs = vabsq_s16(v_coeff); - vcond = vcgeq_s16(v_abs, vzbins); + v_cond = vcgeq_s16(v_abs, v_zbins); - nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(vcond)), 0); + nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(v_cond)), 0); if (nz_check) { - int16x8_t vtmp = vqaddq_s16(v_abs, vround); - int16x8_t vtmp2 = vsraq_n_s16(vtmp, vqdmulhq_s16(vtmp, vquant), 1); + const uint16x8_t v_nz_mask = quantize_b_logscale0_8( + v_coeff, v_abs, v_cond, v_round, v_dequant, v_quant, v_quant_shift, + qcoeff_ptr + i, dqcoeff_ptr + i); - vtmp2 = vshrq_n_s16(vqdmulhq_s16(vtmp2, vquant_shift), 1); - int16x8_t vdest = vsubq_s16(veorq_s16(vtmp2, v_coeff_sign), v_coeff_sign); - int16x8_t coeff_nz_mask = - vbslq_s16(vcond, vdest, load_tran_low_to_s16q(&qcoeff_ptr[i])); - store_s16q_to_tran_low(&qcoeff_ptr[i], coeff_nz_mask); - int16x8_t v_deq_abs = vmulq_s16(vtmp2, vdequant); - vdest = vsubq_s16(veorq_s16(v_deq_abs, v_coeff_sign), v_coeff_sign); - coeff_nz_mask = - vbslq_s16(vcond, vdest, load_tran_low_to_s16q(&dqcoeff_ptr[i])); - store_s16q_to_tran_low(&dqcoeff_ptr[i], coeff_nz_mask); - - uint16x8_t vtmp_mask = vcgtq_s16(vtmp2, zero); - const uint16x8_t v_nz_mask = vandq_u16(vtmp_mask, vcond); - int16x8_t v_iscan = vld1q_s16(&iscan[i]); - vcond = vandq_u16(v_nz_mask, vcgtq_s16(v_iscan, v_eobmax_76543210)); - v_eobmax_76543210 = vbslq_s16(vcond, v_iscan, v_eobmax_76543210); + int16x8_t v_iscan = vld1q_s16(iscan + i); + int16x8_t v_eobmax = vmaxq_s16(v_iscan, v_eobmax_76543210); + v_eobmax_76543210 = vbslq_s16(v_nz_mask, v_eobmax, v_eobmax_76543210); + } else { + store_s16q_to_tran_low(qcoeff_ptr + i, vdupq_n_s16(0)); + store_s16q_to_tran_low(dqcoeff_ptr + i, vdupq_n_s16(0)); } } - *eob_ptr = get_max_eob(v_eobmax_76543210) + 1; + *eob_ptr = get_max_eob(v_eobmax_76543210); } #define QM_MULL_SHIFT(x0, x1) \ @@ -580,7 +577,7 @@ v_eobmax_76543210 = vbslq_s16(vcond, v_iscan, v_eobmax_76543210); } } - *eob_ptr = get_max_eob(v_eobmax_76543210) + 1; + *eob_ptr = get_max_eob(v_eobmax_76543210); } static void aom_quantize_b_helper_32x32_neon( @@ -718,7 +715,7 @@ v_eobmax_76543210 = vbslq_s16(vcond, v_iscan, v_eobmax_76543210); } } - *eob_ptr = get_max_eob(v_eobmax_76543210) + 1; + *eob_ptr = get_max_eob(v_eobmax_76543210); } static void aom_quantize_b_helper_64x64_neon( @@ -867,7 +864,7 @@ v_eobmax_76543210 = vbslq_s16(vcond, v_iscan, v_eobmax_76543210); } } - *eob_ptr = get_max_eob(v_eobmax_76543210) + 1; + *eob_ptr = get_max_eob(v_eobmax_76543210); } void aom_quantize_b_helper_neon( @@ -899,6 +896,35 @@ } } +static inline uint16x8_t quantize_b_logscale1_8( + int16x8_t coeff, int16x8_t abs, uint16x8_t cond, int16x8_t round, + int16x8_t dequant, int16x8_t quant, int16x8_t quant_shift, + tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr) { + const int16x8_t zero = vdupq_n_s16(0); + + int16x8_t coeff_sign = vreinterpretq_s16_u16(vcltq_s16(coeff, zero)); + + int16x8_t tmp = vqaddq_s16(abs, round); + tmp = vsraq_n_s16(tmp, vqdmulhq_s16(tmp, quant), 1); + tmp = vqdmulhq_s16(tmp, quant_shift); + + int16x8_t qcoeff = vsubq_s16(veorq_s16(tmp, coeff_sign), coeff_sign); + qcoeff = vbslq_s16(cond, qcoeff, zero); + store_s16q_to_tran_low(qcoeff_ptr, qcoeff); + + // Shift by log_scale = 1. + int16x8_t dqcoeff = vreinterpretq_s16_u16(vhaddq_u16( + vreinterpretq_u16_s16(vmulq_s16(tmp, dequant)), vdupq_n_u16(0))); + dqcoeff = vsubq_s16(veorq_s16(dqcoeff, coeff_sign), coeff_sign); + dqcoeff = vbslq_s16(cond, dqcoeff, zero); + store_s16q_to_tran_low(dqcoeff_ptr, dqcoeff); + + uint16x8_t tmp_mask = vcgtq_s16(tmp, zero); + const uint16x8_t nz_mask = vandq_u16(tmp_mask, cond); + + return nz_mask; +} + void aom_quantize_b_32x32_neon(const tran_low_t *coeff_ptr, intptr_t n_coeffs, const int16_t *zbin_ptr, const int16_t *round_ptr, @@ -907,10 +933,100 @@ tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, const int16_t *iscan) { - aom_quantize_b_helper_neon(coeff_ptr, n_coeffs, zbin_ptr, round_ptr, - quant_ptr, quant_shift_ptr, qcoeff_ptr, - dqcoeff_ptr, dequant_ptr, eob_ptr, scan, iscan, - NULL, NULL, 1); + (void)scan; + + const int log_scale = 1; + const int zbins[2] = { ROUND_POWER_OF_TWO(zbin_ptr[0], log_scale), + ROUND_POWER_OF_TWO(zbin_ptr[1], log_scale) }; + const int rounds[2] = { ROUND_POWER_OF_TWO(round_ptr[0], log_scale), + ROUND_POWER_OF_TWO(round_ptr[1], log_scale) }; + + int16x8_t v_eobmax_76543210 = vdupq_n_s16(-1); + + int16x8_t v_zbins = vdupq_n_s16(zbins[1]); + int16x8_t v_round = vdupq_n_s16(rounds[1]); + int16x8_t v_dequant = vdupq_n_s16(dequant_ptr[1]); + int16x8_t v_quant = vdupq_n_s16(quant_ptr[1]); + int16x8_t v_quant_shift = vdupq_n_s16(quant_shift_ptr[1]); + + int16x8_t v_zbins0 = vsetq_lane_s16(zbins[0], v_zbins, 0); + int16x8_t v_coeff = load_tran_low_to_s16q(coeff_ptr); + int16x8_t v_abs = vabsq_s16(v_coeff); + uint16x8_t v_cond = vcgeq_s16(v_abs, v_zbins0); + + uint64_t nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(v_cond)), 0); + if (nz_check) { + int16x8_t v_round0 = vsetq_lane_s16(rounds[0], v_round, 0); + int16x8_t v_quant0 = vsetq_lane_s16(quant_ptr[0], v_quant, 0); + int16x8_t v_dequant0 = vsetq_lane_s16(dequant_ptr[0], v_dequant, 0); + int16x8_t v_quant_shift0 = + vsetq_lane_s16(quant_shift_ptr[0], v_quant_shift, 0); + + const uint16x8_t v_nz_mask = quantize_b_logscale1_8( + v_coeff, v_abs, v_cond, v_round0, v_dequant0, v_quant0, v_quant_shift0, + qcoeff_ptr, dqcoeff_ptr); + + int16x8_t v_iscan = vld1q_s16(iscan); + int16x8_t v_eobmax = vmaxq_s16(v_iscan, v_eobmax_76543210); + v_eobmax_76543210 = vbslq_s16(v_nz_mask, v_eobmax, v_eobmax_76543210); + } else { + store_s16q_to_tran_low(qcoeff_ptr, vdupq_n_s16(0)); + store_s16q_to_tran_low(dqcoeff_ptr, vdupq_n_s16(0)); + } + + for (int i = 8; i < n_coeffs; i += 8) { + v_coeff = load_tran_low_to_s16q(coeff_ptr + i); + v_abs = vabsq_s16(v_coeff); + v_cond = vcgeq_s16(v_abs, v_zbins); + + nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(v_cond)), 0); + if (nz_check) { + const uint16x8_t v_nz_mask = quantize_b_logscale1_8( + v_coeff, v_abs, v_cond, v_round, v_dequant, v_quant, v_quant_shift, + qcoeff_ptr + i, dqcoeff_ptr + i); + + int16x8_t v_iscan = vld1q_s16(iscan + i); + int16x8_t v_eobmax = vmaxq_s16(v_iscan, v_eobmax_76543210); + v_eobmax_76543210 = vbslq_s16(v_nz_mask, v_eobmax, v_eobmax_76543210); + } else { + store_s16q_to_tran_low(qcoeff_ptr + i, vdupq_n_s16(0)); + store_s16q_to_tran_low(dqcoeff_ptr + i, vdupq_n_s16(0)); + } + } + *eob_ptr = get_max_eob(v_eobmax_76543210); +} + +static inline uint16x8_t quantize_b_logscale2_8( + int16x8_t coeff, int16x8_t abs, uint16x8_t cond, int16x8_t round, + int16x8_t dequant, int16x8_t quant, int16x8_t quant_shift, + tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr) { + const int16x8_t zero = vdupq_n_s16(0); + const int16x8_t one = vdupq_n_s16(1); + + int16x8_t coeff_sign = vreinterpretq_s16_u16(vcltq_s16(coeff, zero)); + + int16x8_t tmp = vqaddq_s16(abs, round); + tmp = vsraq_n_s16(tmp, vqdmulhq_s16(tmp, quant), 1); + int16x8_t ones = vandq_s16(vshrq_n_s16(vmulq_s16(tmp, quant_shift), 14), one); + tmp = vqdmulhq_s16(tmp, quant_shift); + tmp = vaddq_s16(vshlq_s16(tmp, one), ones); + + int16x8_t qcoeff = vsubq_s16(veorq_s16(tmp, coeff_sign), coeff_sign); + qcoeff = vbslq_s16(cond, qcoeff, zero); + store_s16q_to_tran_low(qcoeff_ptr, qcoeff); + + // Shift right by log_scale = 2. + int16x8_t dqcoeff = vreinterpretq_s16_u16( + vshrq_n_u16(vreinterpretq_u16_s16(vmulq_s16(tmp, dequant)), 2)); + dqcoeff = vorrq_s16(vshlq_n_s16(vqdmulhq_s16(tmp, dequant), 13), dqcoeff); + dqcoeff = vsubq_s16(veorq_s16(dqcoeff, coeff_sign), coeff_sign); + dqcoeff = vbslq_s16(cond, dqcoeff, zero); + store_s16q_to_tran_low(dqcoeff_ptr, dqcoeff); + + uint16x8_t tmp_mask = vcgtq_s16(tmp, zero); + const uint16x8_t nz_mask = vandq_u16(tmp_mask, cond); + + return nz_mask; } void aom_quantize_b_64x64_neon(const tran_low_t *coeff_ptr, intptr_t n_coeffs, @@ -921,8 +1037,65 @@ tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, const int16_t *iscan) { - aom_quantize_b_helper_neon(coeff_ptr, n_coeffs, zbin_ptr, round_ptr, - quant_ptr, quant_shift_ptr, qcoeff_ptr, - dqcoeff_ptr, dequant_ptr, eob_ptr, scan, iscan, - NULL, NULL, 2); + (void)scan; + + const int log_scale = 2; + const int zbins[2] = { ROUND_POWER_OF_TWO(zbin_ptr[0], log_scale), + ROUND_POWER_OF_TWO(zbin_ptr[1], log_scale) }; + const int rounds[2] = { ROUND_POWER_OF_TWO(round_ptr[0], log_scale), + ROUND_POWER_OF_TWO(round_ptr[1], log_scale) }; + + int16x8_t v_eobmax_76543210 = vdupq_n_s16(-1); + + int16x8_t v_zbins = vdupq_n_s16(zbins[1]); + int16x8_t v_round = vdupq_n_s16(rounds[1]); + int16x8_t v_dequant = vdupq_n_s16(dequant_ptr[1]); + int16x8_t v_quant = vdupq_n_s16(quant_ptr[1]); + int16x8_t v_quant_shift = vdupq_n_s16(quant_shift_ptr[1]); + + int16x8_t v_zbins0 = vsetq_lane_s16(zbins[0], v_zbins, 0); + int16x8_t v_coeff = load_tran_low_to_s16q(coeff_ptr); + int16x8_t v_abs = vabsq_s16(v_coeff); + uint16x8_t v_cond = vcgeq_s16(v_abs, v_zbins0); + + uint64_t nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(v_cond)), 0); + if (nz_check) { + int16x8_t v_round0 = vsetq_lane_s16(rounds[0], v_round, 0); + int16x8_t v_quant0 = vsetq_lane_s16(quant_ptr[0], v_quant, 0); + int16x8_t v_dequant0 = vsetq_lane_s16(dequant_ptr[0], v_dequant, 0); + int16x8_t v_quant_shift0 = + vsetq_lane_s16(quant_shift_ptr[0], v_quant_shift, 0); + + const uint16x8_t v_nz_mask = quantize_b_logscale2_8( + v_coeff, v_abs, v_cond, v_round0, v_dequant0, v_quant0, v_quant_shift0, + qcoeff_ptr, dqcoeff_ptr); + + int16x8_t v_iscan = vld1q_s16(iscan); + int16x8_t v_eobmax = vmaxq_s16(v_iscan, v_eobmax_76543210); + v_eobmax_76543210 = vbslq_s16(v_nz_mask, v_eobmax, v_eobmax_76543210); + } else { + store_s16q_to_tran_low(qcoeff_ptr, vdupq_n_s16(0)); + store_s16q_to_tran_low(dqcoeff_ptr, vdupq_n_s16(0)); + } + + for (int i = 8; i < n_coeffs; i += 8) { + v_coeff = load_tran_low_to_s16q(coeff_ptr + i); + v_abs = vabsq_s16(v_coeff); + v_cond = vcgeq_s16(v_abs, v_zbins); + + nz_check = vget_lane_u64(vreinterpret_u64_u8(vmovn_u16(v_cond)), 0); + if (nz_check) { + const uint16x8_t v_nz_mask = quantize_b_logscale2_8( + v_coeff, v_abs, v_cond, v_round, v_dequant, v_quant, v_quant_shift, + qcoeff_ptr + i, dqcoeff_ptr + i); + + int16x8_t v_iscan = vld1q_s16(iscan + i); + int16x8_t v_eobmax = vmaxq_s16(v_iscan, v_eobmax_76543210); + v_eobmax_76543210 = vbslq_s16(v_nz_mask, v_eobmax, v_eobmax_76543210); + } else { + store_s16q_to_tran_low(qcoeff_ptr + i, vdupq_n_s16(0)); + store_s16q_to_tran_low(dqcoeff_ptr + i, vdupq_n_s16(0)); + } + } + *eob_ptr = get_max_eob(v_eobmax_76543210); }
diff --git a/av1/encoder/arm/temporal_filter_neon.c b/av1/encoder/arm/temporal_filter_neon.c index 132045f..1c68b57 100644 --- a/av1/encoder/arm/temporal_filter_neon.c +++ b/av1/encoder/arm/temporal_filter_neon.c
@@ -83,8 +83,8 @@ const uint32_t *luma_sse_sum, const double inv_num_ref_pixels, const double decay_factor, const double inv_factor, const double weight_factor, const double *d_factor, int tf_wgt_calc_lvl) { - assert(((block_width == 16) || (block_width == 32)) && - ((block_height == 16) || (block_height == 32))); + assert(((block_width == 64) || (block_width == 32)) && + ((block_height == 64) || (block_height == 32))); uint32_t diff_sse[BH][BW]; const uint16x8x4_t vmask = vld1q_u16_x4(kSlidingWindowMask); @@ -149,12 +149,16 @@ // Perform filtering. if (tf_wgt_calc_lvl == 0) { for (unsigned int i = 0, k = 0; i < block_height; i++) { + const int y32 = i / (block_height / 2); + const int y16 = (i % (block_height / 2)) / (block_height / 4); + for (unsigned int j = 0; j < block_width; j++, k++) { const int pixel_value = frame[i * stride + j]; const double window_error = diff_sse[i][j] * inv_num_ref_pixels; - const int subblock_idx = - (i >= block_height / 2) * 2 + (j >= block_width / 2); + const int x32 = j / (block_width / 2); + const int x16 = (j % (block_width / 2)) / (block_width / 4); + const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); const double block_error = (double)subblock_mses[subblock_idx]; const double combined_error = weight_factor * window_error + block_error * inv_factor; @@ -169,12 +173,16 @@ } } else { for (unsigned int i = 0, k = 0; i < block_height; i++) { + const int y32 = i / (block_height / 2); + const int y16 = (i % (block_height / 2)) / (block_height / 4); + for (unsigned int j = 0; j < block_width; j++, k++) { const int pixel_value = frame[i * stride + j]; const double window_error = diff_sse[i][j] * inv_num_ref_pixels; - const int subblock_idx = - (i >= block_height / 2) * 2 + (j >= block_width / 2); + const int x32 = j / (block_width / 2); + const int x16 = (j % (block_width / 2)) / (block_width / 4); + const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); const double block_error = (double)subblock_mses[subblock_idx]; const double combined_error = weight_factor * window_error + block_error * inv_factor; @@ -200,7 +208,7 @@ int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count) { const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; - assert(block_size == BLOCK_32X32 && "Only support 32x32 block with Neon!"); + assert(block_size == BLOCK_64X64 && "Only support 64x64 block with Neon!"); assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with Neon!"); assert(!is_high_bitdepth && "Only support low bit-depth with Neon!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE); @@ -231,16 +239,16 @@ // Smaller strength -> smaller filtering weight. double s_decay = pow((double)filter_strength / TF_STRENGTH_THRESHOLD, 2); s_decay = CLIP(s_decay, 1e-5, 1); - double d_factor[4] = { 0 }; + double d_factor[NUM_16X16] = { 0 }; uint16_t frame_sse[SSE_STRIDE * BH] = { 0 }; uint32_t luma_sse_sum[BW * BH] = { 0 }; - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; + distance_threshold = AOMMAX(distance_threshold, 1); + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); - double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; - distance_threshold = AOMMAX(distance_threshold, 1); d_factor[subblock_idx] = distance / distance_threshold; d_factor[subblock_idx] = AOMMAX(d_factor[subblock_idx], 1); } @@ -272,13 +280,30 @@ // will be more accurate. The luma sse sum is reused in both chroma // planes. if (plane == AOM_PLANE_U) { - for (unsigned int i = 0; i < plane_h; i++) { - for (unsigned int j = 0; j < plane_w; j++) { - for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { - for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { - const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. - const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. - luma_sse_sum[i * BW + j] += frame_sse[yy * SSE_STRIDE + xx + 2]; + if (ss_x_shift == 1 && ss_y_shift == 1) { + for (unsigned int i = 0; i < plane_h; ++i) { + const uint16_t *src = &frame_sse[2 * i * SSE_STRIDE + 2]; + uint32_t *dst = luma_sse_sum + i * BW; + + for (unsigned int j = 0; j < plane_w; j += 4) { + const uint16x8_t s0 = vld1q_u16(src + j * 2); + const uint16x8_t s1 = vld1q_u16(src + SSE_STRIDE + j * 2); + + uint32x4_t sum = vpaddlq_u16(s0); + sum = vpadalq_u16(sum, s1); + + vst1q_u32(dst + j, sum); + } + } + } else { + for (unsigned int i = 0; i < plane_h; i++) { + for (unsigned int j = 0; j < plane_w; j++) { + for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { + for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { + const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. + const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. + luma_sse_sum[i * BW + j] += frame_sse[yy * SSE_STRIDE + xx + 2]; + } } } }
diff --git a/av1/encoder/arm/temporal_filter_neon_dotprod.c b/av1/encoder/arm/temporal_filter_neon_dotprod.c index c8fd699..b16c871 100644 --- a/av1/encoder/arm/temporal_filter_neon_dotprod.c +++ b/av1/encoder/arm/temporal_filter_neon_dotprod.c
@@ -71,8 +71,8 @@ const uint32_t *luma_sse_sum, const double inv_num_ref_pixels, const double decay_factor, const double inv_factor, const double weight_factor, const double *d_factor, int tf_wgt_calc_lvl) { - assert(((block_width == 16) || (block_width == 32)) && - ((block_height == 16) || (block_height == 32))); + assert(((block_width == 64) || (block_width == 32)) && + ((block_height == 64) || (block_height == 32))); uint32_t diff_sse[BH][BW]; const uint8x16x2_t vmask = vld1q_u8_x2(kSlidingWindowMask); @@ -171,12 +171,16 @@ // Perform filtering. if (tf_wgt_calc_lvl == 0) { for (unsigned int i = 0, k = 0; i < block_height; i++) { + const int y32 = i / (block_height / 2); + const int y16 = (i % (block_height / 2)) / (block_height / 4); + for (unsigned int j = 0; j < block_width; j++, k++) { const int pixel_value = frame[i * stride + j]; const double window_error = diff_sse[i][j] * inv_num_ref_pixels; - const int subblock_idx = - (i >= block_height / 2) * 2 + (j >= block_width / 2); + const int x32 = j / (block_width / 2); + const int x16 = (j % (block_width / 2)) / (block_width / 4); + const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); const double block_error = (double)subblock_mses[subblock_idx]; const double combined_error = weight_factor * window_error + block_error * inv_factor; @@ -191,12 +195,16 @@ } } else { for (unsigned int i = 0, k = 0; i < block_height; i++) { + const int y32 = i / (block_height / 2); + const int y16 = (i % (block_height / 2)) / (block_height / 4); + for (unsigned int j = 0; j < block_width; j++, k++) { const int pixel_value = frame[i * stride + j]; const double window_error = diff_sse[i][j] * inv_num_ref_pixels; - const int subblock_idx = - (i >= block_height / 2) * 2 + (j >= block_width / 2); + const int x32 = j / (block_width / 2); + const int x16 = (j % (block_width / 2)) / (block_width / 4); + const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); const double block_error = (double)subblock_mses[subblock_idx]; const double combined_error = weight_factor * window_error + block_error * inv_factor; @@ -222,7 +230,7 @@ int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count) { const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; - assert(block_size == BLOCK_32X32 && "Only support 32x32 block with Neon!"); + assert(block_size == BLOCK_64X64 && "Only support 64x64 block with Neon!"); assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with Neon!"); assert(!is_high_bitdepth && "Only support low bit-depth with Neon!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE); @@ -253,16 +261,16 @@ // Smaller strength -> smaller filtering weight. double s_decay = pow((double)filter_strength / TF_STRENGTH_THRESHOLD, 2); s_decay = CLIP(s_decay, 1e-5, 1); - double d_factor[4] = { 0 }; + double d_factor[NUM_16X16] = { 0 }; uint8_t frame_abs_diff[SSE_STRIDE * BH] = { 0 }; uint32_t luma_sse_sum[BW * BH] = { 0 }; - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; + distance_threshold = AOMMAX(distance_threshold, 1); + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); - double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; - distance_threshold = AOMMAX(distance_threshold, 1); d_factor[subblock_idx] = distance / distance_threshold; d_factor[subblock_idx] = AOMMAX(d_factor[subblock_idx], 1); } @@ -294,15 +302,37 @@ // will be more accurate. The luma sse sum is reused in both chroma // planes. if (plane == AOM_PLANE_U) { - for (unsigned int i = 0; i < plane_h; i++) { - for (unsigned int j = 0; j < plane_w; j++) { - for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { - for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { - const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. - const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. - luma_sse_sum[i * BW + j] += - (frame_abs_diff[yy * SSE_STRIDE + xx + 2] * - frame_abs_diff[yy * SSE_STRIDE + xx + 2]); + if (ss_x_shift == 1 && ss_y_shift == 1) { + for (unsigned int i = 0; i < plane_h; ++i) { + const uint8_t *src = &frame_abs_diff[2 * i * SSE_STRIDE + 2]; + uint32_t *dst = luma_sse_sum + i * BW; + + for (unsigned int j = 0; j < plane_w; j += 8) { + const uint8x16_t s0 = vld1q_u8(src + j * 2); + const uint8x16_t s1 = vld1q_u8(src + SSE_STRIDE + j * 2); + + uint8x16x2_t tmp = vzipq_u8(s0, s1); + + const uint32x4_t sum0 = + vdotq_u32(vdupq_n_u32(0), tmp.val[0], tmp.val[0]); + const uint32x4_t sum1 = + vdotq_u32(vdupq_n_u32(0), tmp.val[1], tmp.val[1]); + + vst1q_u32(dst + j, sum0); + vst1q_u32(dst + j + 4, sum1); + } + } + } else { + for (unsigned int i = 0; i < plane_h; i++) { + for (unsigned int j = 0; j < plane_w; j++) { + for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { + for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { + const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. + const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. + luma_sse_sum[i * BW + j] += + (frame_abs_diff[yy * SSE_STRIDE + xx + 2] * + frame_abs_diff[yy * SSE_STRIDE + xx + 2]); + } } } }
diff --git a/av1/encoder/av1_ext_ratectrl.c b/av1/encoder/av1_ext_ratectrl.c new file mode 100644 index 0000000..90ef4b2 --- /dev/null +++ b/av1/encoder/av1_ext_ratectrl.c
@@ -0,0 +1,196 @@ +/* + * Copyright (c) 2025, 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 "aom/aom_ext_ratectrl.h" +#include "av1/encoder/av1_ext_ratectrl.h" + +aom_codec_err_t av1_extrc_init(AOM_EXT_RATECTRL *ext_ratectrl) { + if (ext_ratectrl == NULL) { + return AOM_CODEC_INVALID_PARAM; + } + av1_zero(*ext_ratectrl); + return AOM_CODEC_OK; +} + +aom_codec_err_t av1_extrc_create(aom_rc_funcs_t funcs, + aom_rc_config_t ratectrl_config, + AOM_EXT_RATECTRL *ext_ratectrl) { + aom_rc_status_t rc_status; + aom_rc_firstpass_stats_t *rc_firstpass_stats; + if (ext_ratectrl == NULL) { + return AOM_CODEC_INVALID_PARAM; + } + av1_extrc_delete(ext_ratectrl); + ext_ratectrl->funcs = funcs; + ext_ratectrl->ratectrl_config = ratectrl_config; + rc_status = ext_ratectrl->funcs.create_model(ext_ratectrl->funcs.priv, + &ext_ratectrl->ratectrl_config, + &ext_ratectrl->model); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + rc_firstpass_stats = &ext_ratectrl->rc_firstpass_stats; + rc_firstpass_stats->num_frames = ratectrl_config.show_frame_count; + rc_firstpass_stats->frame_stats = + aom_malloc(sizeof(*rc_firstpass_stats->frame_stats) * + rc_firstpass_stats->num_frames); + if (rc_firstpass_stats->frame_stats == NULL) { + return AOM_CODEC_MEM_ERROR; + } + + ext_ratectrl->ready = 1; + return AOM_CODEC_OK; +} + +static void gen_rc_firstpass_stats(const FIRSTPASS_STATS *stats, + aom_rc_frame_stats_t *rc_frame_stats) { + rc_frame_stats->frame = stats->frame; + rc_frame_stats->weight = stats->weight; + rc_frame_stats->intra_error = stats->intra_error; + rc_frame_stats->coded_error = stats->coded_error; + rc_frame_stats->sr_coded_error = stats->sr_coded_error; + rc_frame_stats->frame_avg_wavelet_energy = stats->frame_avg_wavelet_energy; + rc_frame_stats->pcnt_inter = stats->pcnt_inter; + rc_frame_stats->pcnt_motion = stats->pcnt_motion; + rc_frame_stats->pcnt_second_ref = stats->pcnt_second_ref; + rc_frame_stats->pcnt_neutral = stats->pcnt_neutral; + rc_frame_stats->intra_skip_pct = stats->intra_skip_pct; + rc_frame_stats->inactive_zone_rows = stats->inactive_zone_rows; + rc_frame_stats->inactive_zone_cols = stats->inactive_zone_cols; + rc_frame_stats->MVr = stats->MVr; + rc_frame_stats->mvr_abs = stats->mvr_abs; + rc_frame_stats->MVc = stats->MVc; + rc_frame_stats->mvc_abs = stats->mvc_abs; + rc_frame_stats->MVrv = stats->MVrv; + rc_frame_stats->MVcv = stats->MVcv; + rc_frame_stats->mv_in_out_count = stats->mv_in_out_count; + rc_frame_stats->duration = stats->duration; + rc_frame_stats->count = stats->count; + rc_frame_stats->new_mv_count = stats->new_mv_count; + rc_frame_stats->raw_error_stdev = stats->raw_error_stdev; + rc_frame_stats->is_flash = stats->is_flash; + rc_frame_stats->noise_var = stats->noise_var; + rc_frame_stats->cor_coeff = stats->cor_coeff; + rc_frame_stats->log_intra_error = stats->log_intra_error; + rc_frame_stats->log_coded_error = stats->log_coded_error; +} + +aom_codec_err_t av1_extrc_send_firstpass_stats( + AOM_EXT_RATECTRL *ext_ratectrl, const FIRSTPASS_INFO *first_pass_info) { + assert(first_pass_info != NULL); + assert(ext_ratectrl != NULL); + if (ext_ratectrl->ready) { + aom_rc_status_t rc_status; + aom_rc_firstpass_stats_t *rc_firstpass_stats = + &ext_ratectrl->rc_firstpass_stats; + assert(rc_firstpass_stats->num_frames == first_pass_info->stats_buf_size); + for (int i = 0; i < rc_firstpass_stats->num_frames; ++i) { + gen_rc_firstpass_stats(&first_pass_info->stats_buf[i], + &rc_firstpass_stats->frame_stats[i]); + } + rc_status = ext_ratectrl->funcs.send_firstpass_stats(ext_ratectrl->model, + rc_firstpass_stats); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + } + return AOM_CODEC_OK; +} + +aom_codec_err_t av1_extrc_send_tpl_stats( + AOM_EXT_RATECTRL *ext_ratectrl, const AomTplGopStats *extrc_tpl_gop_stats) { + assert(ext_ratectrl != NULL); + assert(extrc_tpl_gop_stats != NULL); + if (ext_ratectrl->ready && ext_ratectrl->funcs.send_tpl_gop_stats != NULL) { + aom_rc_status_t rc_status = ext_ratectrl->funcs.send_tpl_gop_stats( + ext_ratectrl->model, extrc_tpl_gop_stats); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + } + return AOM_CODEC_OK; +} + +aom_codec_err_t av1_extrc_get_gop_decision( + AOM_EXT_RATECTRL *ext_ratectrl, aom_rc_gop_decision_t *gop_decision) { + aom_rc_status_t rc_status; + assert(ext_ratectrl != NULL); + assert(gop_decision != NULL); + if ((ext_ratectrl->funcs.rc_type & AOM_RC_GOP) == 0) { + return AOM_CODEC_INVALID_PARAM; + } + rc_status = + ext_ratectrl->funcs.get_gop_decision(ext_ratectrl->model, gop_decision); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + return AOM_CODEC_OK; +} + +aom_codec_err_t av1_extrc_get_encodeframe_decision( + AOM_EXT_RATECTRL *ext_ratectrl, int gop_index, + aom_rc_encodeframe_decision_t *encode_frame_decision) { + assert(ext_ratectrl != NULL); + assert(ext_ratectrl->ready && (ext_ratectrl->funcs.rc_type & AOM_RC_QP) != 0); + assert(encode_frame_decision != NULL); + aom_rc_status_t rc_status = ext_ratectrl->funcs.get_encodeframe_decision( + ext_ratectrl->model, gop_index, encode_frame_decision); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + return AOM_CODEC_OK; +} + +aom_codec_err_t av1_extrc_update_encodeframe_result( + AOM_EXT_RATECTRL *ext_ratectrl, int64_t bit_count, + int actual_encoding_qindex) { + assert(ext_ratectrl != NULL); + if (ext_ratectrl->ready) { + aom_rc_status_t rc_status; + aom_rc_encodeframe_result_t encode_frame_result; + encode_frame_result.bit_count = bit_count; + encode_frame_result.actual_encoding_qindex = actual_encoding_qindex; + rc_status = ext_ratectrl->funcs.update_encodeframe_result( + ext_ratectrl->model, &encode_frame_result); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + } + return AOM_CODEC_OK; +} + +aom_codec_err_t av1_extrc_get_key_frame_decision( + AOM_EXT_RATECTRL *ext_ratectrl, + aom_rc_key_frame_decision_t *key_frame_decision) { + assert(ext_ratectrl != NULL); + if (!ext_ratectrl->ready || (ext_ratectrl->funcs.rc_type & AOM_RC_GOP) == 0) { + return AOM_CODEC_INVALID_PARAM; + } + aom_rc_status_t rc_status = ext_ratectrl->funcs.get_key_frame_decision( + ext_ratectrl->model, key_frame_decision); + return rc_status == AOM_RC_OK ? AOM_CODEC_OK : AOM_CODEC_ERROR; +} + +aom_codec_err_t av1_extrc_delete(AOM_EXT_RATECTRL *ext_ratectrl) { + if (ext_ratectrl == NULL) { + return AOM_CODEC_INVALID_PARAM; + } + if (ext_ratectrl->ready) { + aom_rc_status_t rc_status = + ext_ratectrl->funcs.delete_model(ext_ratectrl->model); + if (rc_status == AOM_RC_ERROR) { + return AOM_CODEC_ERROR; + } + aom_free(ext_ratectrl->rc_firstpass_stats.frame_stats); + aom_free(ext_ratectrl->sb_params_list); + } + return av1_extrc_init(ext_ratectrl); +}
diff --git a/av1/encoder/av1_ext_ratectrl.h b/av1/encoder/av1_ext_ratectrl.h new file mode 100644 index 0000000..bf7c4a9 --- /dev/null +++ b/av1/encoder/av1_ext_ratectrl.h
@@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025, 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. + */ + +#ifndef AOM_AV1_ENCODER_AV1_EXT_RATECTRL_H_ +#define AOM_AV1_ENCODER_AV1_EXT_RATECTRL_H_ + +#include "aom/aom_codec.h" +#include "aom/aom_ext_ratectrl.h" +#include "aom/aom_tpl.h" +#include "av1/encoder/firstpass.h" + +typedef struct AOM_EXT_RATECTRL { + int ready; + int ext_rdmult; + // Whether per-superblock delta Q should be used. This is set on frame basis. + // Key, golden and regular ARF frames use delta Q. All other frames do not. + // This flag is added so the sb_params_list array doesn't have to be freed + // and allocated repeatedly. + int use_delta_q; + aom_rc_model_t model; + aom_rc_funcs_t funcs; + aom_rc_config_t ratectrl_config; + aom_rc_firstpass_stats_t rc_firstpass_stats; + aom_sb_params *sb_params_list; +} AOM_EXT_RATECTRL; + +aom_codec_err_t av1_extrc_init(AOM_EXT_RATECTRL *ext_ratectrl); + +aom_codec_err_t av1_extrc_create(aom_rc_funcs_t funcs, + aom_rc_config_t ratectrl_config, + AOM_EXT_RATECTRL *ext_ratectrl); + +aom_codec_err_t av1_extrc_delete(AOM_EXT_RATECTRL *ext_ratectrl); + +aom_codec_err_t av1_extrc_send_firstpass_stats( + AOM_EXT_RATECTRL *ext_ratectrl, const FIRSTPASS_INFO *first_pass_info); + +aom_codec_err_t av1_extrc_send_tpl_stats( + AOM_EXT_RATECTRL *ext_ratectrl, const AomTplGopStats *extrc_tpl_gop_stats); + +aom_codec_err_t av1_extrc_get_encodeframe_decision( + AOM_EXT_RATECTRL *ext_ratectrl, int gop_index, + aom_rc_encodeframe_decision_t *encode_frame_decision); + +aom_codec_err_t av1_extrc_update_encodeframe_result( + AOM_EXT_RATECTRL *ext_ratectrl, int64_t bit_count, + int actual_encoding_qindex); + +aom_codec_err_t av1_extrc_get_key_frame_decision( + AOM_EXT_RATECTRL *ext_ratectrl, + aom_rc_key_frame_decision_t *key_frame_decision); + +aom_codec_err_t av1_extrc_get_gop_decision(AOM_EXT_RATECTRL *ext_ratectrl, + aom_rc_gop_decision_t *gop_decision); + +aom_codec_err_t av1_extrc_get_frame_rdmult( + AOM_EXT_RATECTRL *ext_ratectrl, int show_index, int coding_index, + int gop_index, FRAME_UPDATE_TYPE update_type, int gop_size, int use_alt_ref, + RefCntBuffer *ref_frame_bufs[AOM_RC_MAX_REF_FRAMES], int ref_frame_flags, + int *rdmult); + +#endif // AOM_AV1_ENCODER_AV1_EXT_RATECTRL_H_
diff --git a/av1/encoder/av1_quantize.c b/av1/encoder/av1_quantize.c index 1673d53..aeda245 100644 --- a/av1/encoder/av1_quantize.c +++ b/av1/encoder/av1_quantize.c
@@ -826,7 +826,8 @@ qindex_rdmult, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); const int qindex_change = x->qindex != qindex; if (qindex_change || do_update) { @@ -885,8 +886,7 @@ // Disable deltaq in lossless mode. if (enable_chroma_deltaq && q) { - if (is_allintra && - (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2)) { + if (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2) { int chroma_dc_delta_q = 0; int chroma_ac_delta_q = 0; @@ -902,7 +902,13 @@ // The ramp-down of chroma increase was determined by generating the // convex hull of SSIMULACRA 2 scores (for all boosts from 0-16), and // finding a linear equation that fits the convex hull. - chroma_dc_delta_q = -clamp((quant_params->base_qindex / 2) - 14, 0, 16); + // For the SSIMULACRA 2 tune only: a convex hull of SSIMULACRA 2 scores + // (mean, 10th percentile) was generated by decreasing chroma delta_q + // by 3 different values (16, 20 and 24). 20 was selected as it had the + // best 10th percentile performance. + int offset = (tuning == AOM_TUNE_SSIMULACRA2) ? 20 : 16; + chroma_dc_delta_q = + -clamp((quant_params->base_qindex / 2) - 14, 0, offset); chroma_ac_delta_q = chroma_dc_delta_q; } else if (cm->seq_params->subsampling_x == 1 && cm->seq_params->subsampling_y == 0) { @@ -980,30 +986,28 @@ int (*get_luma_qmlevel)(int, int, int); int (*get_chroma_qmlevel)(int, int, int); - if (is_allintra) { - if (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2) { - if (tuning == AOM_TUNE_SSIMULACRA2) { - // Use luma QM formula specifically tailored for tune SSIMULACRA2 - get_luma_qmlevel = aom_get_qmlevel_luma_ssimulacra2; - } else { - get_luma_qmlevel = aom_get_qmlevel_allintra; - } - - if (cm->seq_params->subsampling_x == 0 && - cm->seq_params->subsampling_y == 0) { - // 4:4:4 subsampling mode has 4x the number of chroma coefficients - // compared to 4:2:0 (2x on each dimension). This means the encoder - // should use lower chroma QM levels that more closely match the scaling - // of an equivalent 4:2:0 chroma QM. - get_chroma_qmlevel = aom_get_qmlevel_444_chroma; - } else { - // For all other chroma subsampling modes, use the all intra QM formula - get_chroma_qmlevel = aom_get_qmlevel_allintra; - } + if (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2) { + if (tuning == AOM_TUNE_SSIMULACRA2) { + // Use luma QM formula specifically tailored for tune SSIMULACRA2 + get_luma_qmlevel = aom_get_qmlevel_luma_ssimulacra2; } else { get_luma_qmlevel = aom_get_qmlevel_allintra; + } + + if (cm->seq_params->subsampling_x == 0 && + cm->seq_params->subsampling_y == 0) { + // 4:4:4 subsampling mode has 4x the number of chroma coefficients + // compared to 4:2:0 (2x on each dimension). This means the encoder + // should use lower chroma QM levels that more closely match the scaling + // of an equivalent 4:2:0 chroma QM. + get_chroma_qmlevel = aom_get_qmlevel_444_chroma; + } else { + // For all other chroma subsampling modes, use the all intra QM formula get_chroma_qmlevel = aom_get_qmlevel_allintra; } + } else if (is_allintra) { + get_luma_qmlevel = aom_get_qmlevel_allintra; + get_chroma_qmlevel = aom_get_qmlevel_allintra; } else { get_luma_qmlevel = aom_get_qmlevel; get_chroma_qmlevel = aom_get_qmlevel;
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c index a6a92bf..178a84a 100644 --- a/av1/encoder/bitstream.c +++ b/av1/encoder/bitstream.c
@@ -4222,23 +4222,24 @@ for (size_t i = 0; i < arr->sz; i++) { aom_metadata_t *current_metadata = arr->metadata_array[i]; if (current_metadata && current_metadata->payload) { + const int metadata_insert_location = + current_metadata->insert_flag & AOM_MIF_INSERT_LOCATION_MASK; if ((cm->current_frame.frame_type == KEY_FRAME && - current_metadata->insert_flag == AOM_MIF_KEY_FRAME) || + metadata_insert_location == AOM_MIF_KEY_FRAME) || (cm->current_frame.frame_type != KEY_FRAME && - current_metadata->insert_flag == AOM_MIF_NON_KEY_FRAME) || - current_metadata->insert_flag == AOM_MIF_ANY_FRAME) { + metadata_insert_location == AOM_MIF_NON_KEY_FRAME) || + metadata_insert_location == AOM_MIF_ANY_FRAME) { // OBU header is either one or two bytes. if (dst_size < 2) { aom_internal_error(cm->error, AOM_CODEC_ERROR, "av1_write_metadata_array: output buffer full"); } - // According to the AV1 spec draft version (as of git commit 5e04f) - // Section 6.7.1, some metadata types can be layer specific, but we - // currently only support non-layer specific metadata. + const bool is_layer_specific_obu = + (current_metadata->insert_flag & AOM_MIF_LAYER_SPECIFIC) != 0; obu_header_size = av1_write_obu_header( &cpi->ppi->level_params, &cpi->frame_header_count, OBU_METADATA, cm->seq_params->has_nonzero_operating_point_idc, - /*is_layer_specific_obu=*/false, 0, dst); + is_layer_specific_obu, 0, dst); assert(obu_header_size <= 2); obu_payload_size = av1_write_metadata_obu(current_metadata, dst + obu_header_size,
diff --git a/av1/encoder/block.h b/av1/encoder/block.h index ecbc1aa..e811fcf 100644 --- a/av1/encoder/block.h +++ b/av1/encoder/block.h
@@ -43,6 +43,9 @@ /*! Maximum value taken by transform type probabilities */ #define MAX_TX_TYPE_PROB 1024 +/*! Maximum value of inter transform RD records. */ +#define TOP_INTER_TX_NO_SPLIT_COUNT 4 + //! Compute color sensitivity index for given plane #define COLOR_SENS_IDX(plane) ((plane) - 1) @@ -441,6 +444,11 @@ */ int use_default_intra_tx_type; + /*! Whether to limit the intra transform search type to the ones in the table + * av1_derived_intra_tx_used_flag[INTRA_MODES]. + */ + int use_derived_intra_tx_type_set; + /*! Probability threshold used for conditionally forcing tx type*/ int default_inter_tx_type_prob_thresh; @@ -868,6 +876,9 @@ /*!\endcond */ +//! Maximum number of estimated RD Cost records for compound average. +#define TOP_COMP_AVG_EST_RD_COUNT 5 + /*! \brief Encoder's parameters related to the current coding block. * * This struct contains most of the information the encoder needs to encode the @@ -952,6 +963,11 @@ * prediction. */ uint8_t *tmp_pred_bufs[2]; + + /*! + * Buffer used for upsampled prediction. + */ + uint8_t *upsample_pred; /**@}*/ /***************************************************************************** @@ -1352,9 +1368,9 @@ //! Flag to indicate to test the superblock MV for the coding block in the // nonrd_pickmode. int sb_me_block; - //! Flag to indicate superblock selected column scroll. + //! Counter for superblock selected column scroll. int sb_col_scroll; - //! Flag to indicate superblock selected row scroll. + //! Counter for superblock selected row scroll. int sb_row_scroll; //! Motion vector from superblock MV derived from int_pro_motion() in // the variance_partitioning. @@ -1399,6 +1415,10 @@ */ int palette_pixels; + /*! \brief Keep records of top no-split RD Costs of transform size search. */ + int64_t top_inter_tx_no_split_rd[MAX_TX_BLOCKS_IN_MAX_SB] + [TOP_INTER_TX_NO_SPLIT_COUNT]; + /*!\brief Pointer to the structure which stores the statistics used by * sb-level multi-pass encoding. */ @@ -1409,6 +1429,9 @@ */ struct SB_FIRST_PASS_STATS *sb_fp_stats; + /*!\brief Array of best estimated RD Costs of compound average. */ + int64_t top_comp_avg_est_rd[TOP_COMP_AVG_EST_RD_COUNT]; + #if CONFIG_PARTITION_SEARCH_ORDER /*!\brief Pointer to RD_STATS structure to be used in * av1_rd_partition_search().
diff --git a/av1/encoder/compound_type.c b/av1/encoder/compound_type.c index d5dcf74..7c462e2 100644 --- a/av1/encoder/compound_type.c +++ b/av1/encoder/compound_type.c
@@ -24,6 +24,11 @@ const int16_t *const residual1, const int16_t *const diff10, uint64_t *best_sse); +// look-up table for number of top compound average estimated RD Costs that +// should be considered based on skip_cmp_using_top_cmp_avg_est_rd_lvl +// speed feature. +static const int num_comp_mode_skip_cand[3] = { 5, 4, 2 }; + // Checks if characteristics of search match static inline int is_comp_rd_match(const AV1_COMP *const cpi, const MACROBLOCK *const x, @@ -730,6 +735,51 @@ return 0; } +// Store the estimated RD Cost of compound average. +static inline void push_comp_avg_est_rd( + int64_t *top_comp_avg_est_rd, int64_t tmp_rd, + int skip_cmp_using_top_cmp_avg_est_rd_lvl) { + if (!skip_cmp_using_top_cmp_avg_est_rd_lvl) return; + assert(skip_cmp_using_top_cmp_avg_est_rd_lvl <= 3); + + const int num_top_cand = + num_comp_mode_skip_cand[skip_cmp_using_top_cmp_avg_est_rd_lvl - 1]; + assert(num_top_cand <= TOP_COMP_AVG_EST_RD_COUNT); + + // Insert the RD Cost in sorted order + for (int i = 0; i < num_top_cand; i++) { + if (tmp_rd < top_comp_avg_est_rd[i]) { + for (int j = num_top_cand - 1; j > i; j--) { + top_comp_avg_est_rd[j] = top_comp_avg_est_rd[j - 1]; + } + top_comp_avg_est_rd[i] = tmp_rd; + break; + } + } +} + +// Avoid further evaluation of compound modes based on top estimated RD Costs of +// compound average. +static inline bool prune_comp_eval_using_comp_avg_est_rd( + const int64_t *top_comp_avg_est_rd, int64_t tmp_rd, int64_t ref_best_rd, + int skip_cmp_using_top_cmp_avg_est_rd_lvl) { + if (!skip_cmp_using_top_cmp_avg_est_rd_lvl) return false; + assert(skip_cmp_using_top_cmp_avg_est_rd_lvl <= 3); + + const int num_top_cand = + num_comp_mode_skip_cand[skip_cmp_using_top_cmp_avg_est_rd_lvl - 1]; + assert(num_top_cand <= TOP_COMP_AVG_EST_RD_COUNT); + + // Do not prune if there is no valid top RD Cost for comparison + if (top_comp_avg_est_rd[num_top_cand - 1] == INT64_MAX || + ref_best_rd == INT64_MAX) + return false; + + if (tmp_rd > top_comp_avg_est_rd[num_top_cand - 1]) return true; + + return false; +} + int av1_handle_inter_intra_mode(const AV1_COMP *const cpi, MACROBLOCK *const x, BLOCK_SIZE bsize, MB_MODE_INFO *mbmi, HandleInterModeArgs *args, int64_t ref_best_rd, @@ -1307,8 +1357,6 @@ const bool skip_mv_refinement_for_avg_distwtd = enable_fast_compound_mode_search == 3 || (enable_fast_compound_mode_search == 2 && (this_mode != NEW_NEWMV)); - const bool skip_mv_refinement_for_diffwtd = - (!enable_fast_compound_mode_search && cur_type == COMPOUND_DIFFWTD); // Case COMPOUND_AVERAGE and COMPOUND_DISTWTD if (cur_type < COMPOUND_WEDGE) { @@ -1376,12 +1424,26 @@ int eval_txfm = prune_mode_by_skip_rd(cpi, x, xd, bsize, ref_skip_rd, rs2 + *rate_mv); + int64_t est_rd = INT64_MAX; + RD_STATS est_rd_stats; if (eval_txfm) { - RD_STATS est_rd_stats; - estimate_yrd_for_sb(cpi, bsize, x, INT64_MAX, &est_rd_stats); - + est_rd = estimate_yrd_for_sb(cpi, bsize, x, INT64_MAX, &est_rd_stats); + } + if (est_rd != INT64_MAX) { best_rd_cur = RDCOST(x->rdmult, rs2 + tmp_rate_mv + est_rd_stats.rate, est_rd_stats.dist); + int rate_sum; + uint8_t tmp_skip_txfm_sb; + int64_t dist_sum, tmp_skip_sse_sb; + model_rd_sb_fn[MODELRD_TYPE_MASKED_COMPOUND]( + cpi, bsize, x, xd, 0, 0, &rate_sum, &dist_sum, &tmp_skip_txfm_sb, + &tmp_skip_sse_sb, NULL, NULL, NULL); + comp_model_rd_cur = + RDCOST(x->rdmult, rs2 + tmp_rate_mv + rate_sum, dist_sum); + // Backup rate and distortion for future reuse + backup_stats(cur_type, comp_rate, comp_dist, comp_model_rate, + comp_model_dist, rate_sum, dist_sum, &est_rd_stats, + comp_rs2, rs2); } } @@ -1524,7 +1586,8 @@ mbmi->mv[1] = tmp_mv[1]; tmp_rate_mv = best_rate_mv; rs2 = best_rs2; - } else if (skip_mv_refinement_for_diffwtd) { + } else if (!enable_fast_compound_mode_search && + cur_type == COMPOUND_DIFFWTD) { int_mv tmp_mv[2]; int best_mask_index = 0; rs2 += get_interinter_compound_mask_rate(&x->mode_costs, mbmi); @@ -1642,6 +1705,19 @@ if (have_newmv_in_inter_mode(this_mode)) update_mask_best_mv(mbmi, best_mv, &best_tmp_rate_mv, tmp_rate_mv); } + if (cur_type == COMPOUND_AVERAGE) { + const int skip_cmp_using_top_cmp_avg_est_rd_lvl = + cpi->sf.inter_sf.skip_cmp_using_top_cmp_avg_est_rd_lvl; + push_comp_avg_est_rd(x->top_comp_avg_est_rd, best_rd_cur, + skip_cmp_using_top_cmp_avg_est_rd_lvl); + if (prune_comp_eval_using_comp_avg_est_rd( + x->top_comp_avg_est_rd, best_rd_cur, ref_best_rd, + skip_cmp_using_top_cmp_avg_est_rd_lvl)) { + *rd = INT64_MAX; + restore_dst_buf(xd, *orig_dst, 1); + return 0; + } + } // reset to original mvs for next iteration mbmi->mv[0].as_int = cur_mv[0].as_int; mbmi->mv[1].as_int = cur_mv[1].as_int;
diff --git a/av1/encoder/context_tree.c b/av1/encoder/context_tree.c index 4273fae..74b82e5 100644 --- a/av1/encoder/context_tree.c +++ b/av1/encoder/context_tree.c
@@ -25,8 +25,6 @@ dst_ctx->best_mode_index = src_ctx->best_mode_index; #endif // CONFIG_INTERNAL_STATS - memcpy(dst_ctx->blk_skip, src_ctx->blk_skip, - sizeof(uint8_t) * src_ctx->num_4x4_blk); av1_copy_array(dst_ctx->tx_type_map, src_ctx->tx_type_map, src_ctx->num_4x4_blk); @@ -84,8 +82,6 @@ const int num_pix = block_size_wide[bsize] * block_size_high[bsize]; const int num_blk = num_pix / 16; - AOM_CHECK_MEM_ERROR(&error, ctx->blk_skip, - aom_calloc(num_blk, sizeof(*ctx->blk_skip))); AOM_CHECK_MEM_ERROR(&error, ctx->tx_type_map, aom_calloc(num_blk, sizeof(*ctx->tx_type_map))); ctx->num_4x4_blk = num_blk; @@ -119,7 +115,6 @@ } void av1_reset_pmc(PICK_MODE_CONTEXT *ctx) { - av1_zero_array(ctx->blk_skip, ctx->num_4x4_blk); av1_zero_array(ctx->tx_type_map, ctx->num_4x4_blk); av1_invalid_rd_stats(&ctx->rd_stats); } @@ -127,8 +122,6 @@ void av1_free_pmc(PICK_MODE_CONTEXT *ctx, int num_planes) { if (ctx == NULL) return; - aom_free(ctx->blk_skip); - ctx->blk_skip = NULL; aom_free(ctx->tx_type_map); for (int i = 0; i < num_planes; ++i) { ctx->coeff[i] = NULL;
diff --git a/av1/encoder/context_tree.h b/av1/encoder/context_tree.h index 6b6d851..1578793 100644 --- a/av1/encoder/context_tree.h +++ b/av1/encoder/context_tree.h
@@ -38,7 +38,6 @@ MB_MODE_INFO mic; MB_MODE_INFO_EXT_FRAME mbmi_ext_best; uint8_t *color_index_map[2]; - uint8_t *blk_skip; tran_low_t *coeff[MAX_MB_PLANE]; tran_low_t *qcoeff[MAX_MB_PLANE];
diff --git a/av1/encoder/cost.c b/av1/encoder/cost.c index e4d15e2..4bf8fce 100644 --- a/av1/encoder/cost.c +++ b/av1/encoder/cost.c
@@ -10,6 +10,7 @@ */ #include <assert.h> +#include "aom_dsp/entcode.h" #include "av1/encoder/cost.h" #include "av1/common/entropy.h"
diff --git a/av1/encoder/enc_enums.h b/av1/encoder/enc_enums.h index 98072b3..142fce6 100644 --- a/av1/encoder/enc_enums.h +++ b/av1/encoder/enc_enums.h
@@ -265,6 +265,17 @@ USE_LARGESTALL, } UENUM1BYTE(TX_SIZE_SEARCH_METHOD); +enum { + // Good Quality Fast Encoding. The encoder balances quality with the amount of + // time it takes to encode the output. Speed setting controls how fast. + GOOD, + // Realtime Fast Encoding. Will force some restrictions on bitrate + // constraints. + REALTIME, + // All intra mode. All the frames are coded as intra frames. + ALLINTRA +} UENUM1BYTE(MODE); + #ifdef __cplusplus } // extern "C" #endif
diff --git a/av1/encoder/encode_strategy.c b/av1/encoder/encode_strategy.c index b18b21f..816ae20 100644 --- a/av1/encoder/encode_strategy.c +++ b/av1/encoder/encode_strategy.c
@@ -611,6 +611,11 @@ } } +static int get_new_fb_map_idx_rc(int new_fb_map_idx) { + if (new_fb_map_idx == INVALID_IDX) return 0; + return 1 << new_fb_map_idx; +} + int av1_get_refresh_frame_flags( const AV1_COMP *const cpi, const EncodeFrameParams *const frame_params, FRAME_UPDATE_TYPE frame_update_type, int gf_index, int cur_disp_order, @@ -637,12 +642,16 @@ #if !CONFIG_REALTIME_ONLY if (cpi->use_ducky_encode && cpi->ducky_encode_info.frame_info.gop_mode == DUCKY_ENCODE_GOP_MODE_RCL) { - int new_fb_map_idx = cpi->ppi->gf_group.update_ref_idx[gf_index]; - if (new_fb_map_idx == INVALID_IDX) return 0; - return 1 << new_fb_map_idx; + return get_new_fb_map_idx_rc(gf_group->update_ref_idx[gf_index]); } #endif // !CONFIG_REALTIME_ONLY + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_GOP) != 0 && + cpi->ext_ratectrl.funcs.get_gop_decision != NULL) { + return get_new_fb_map_idx_rc(gf_group->update_ref_idx[gf_index]); + } + int refresh_mask = 0; if (ext_refresh_frame_flags->update_pending) { if (rtc_ref->set_ref_frame_config || @@ -787,7 +796,8 @@ if (tf_buf != NULL) { frame_input->source = tf_buf; show_existing_alt_ref = av1_check_show_filtered_frame( - tf_buf, &frame_diff, q_index, cm->seq_params->bit_depth); + tf_buf, &frame_diff, q_index, cm->seq_params->bit_depth, + cpi->oxcf.algo_cfg.enable_overlay, 0); if (show_existing_alt_ref) { cpi->common.showable_frame |= 1; } else { @@ -822,8 +832,9 @@ // TODO(angiebird): Reuse tf_info->tf_buf here. av1_temporal_filter(cpi, arf_src_index, cpi->gf_frame_index, &frame_diff, tf_buf_second_arf); - show_existing_alt_ref = av1_check_show_filtered_frame( - tf_buf_second_arf, &frame_diff, q_index, cm->seq_params->bit_depth); + show_existing_alt_ref = + av1_check_show_filtered_frame(tf_buf_second_arf, &frame_diff, q_index, + cm->seq_params->bit_depth, 1, 1); if (show_existing_alt_ref) { aom_extend_frame_borders(tf_buf_second_arf, av1_num_planes(cm)); frame_input->source = tf_buf_second_arf; @@ -869,11 +880,18 @@ if (frame_params->frame_type != KEY_FRAME) { // In rare case, it's possible to have non ARF/GF update_type here. // We should set allow_tpl to zero in the situation - allow_tpl = - allow_tpl && (update_type == ARF_UPDATE || update_type == GF_UPDATE || - (cpi->use_ducky_encode && - cpi->ducky_encode_info.frame_info.gop_mode == - DUCKY_ENCODE_GOP_MODE_RCL)); + bool frame_type_allow_tpl = + update_type == ARF_UPDATE || update_type == GF_UPDATE; + // When external rate control is used, sometimes the sequence ends with + // a GOP with only 1 frame which is a leaf frame. TPL stats needs to be + // calculated for it as well. + if (av1_use_tpl_for_extrc(&cpi->ext_ratectrl)) { + frame_type_allow_tpl |= update_type == LF_UPDATE; + } + allow_tpl = allow_tpl && (frame_type_allow_tpl || + (cpi->use_ducky_encode && + cpi->ducky_encode_info.frame_info.gop_mode == + DUCKY_ENCODE_GOP_MODE_RCL)); } if (allow_tpl) { @@ -995,6 +1013,21 @@ // Initialize reference frame mappings. for (int i = 0; i < REF_FRAMES; ++i) remapped_ref_idx[i] = INVALID_IDX; + if (cpi->ppi->gf_group.use_ext_ref_frame_map[gf_index]) { + for (int rf = LAST_FRAME; rf < REF_FRAMES; ++rf) { + if (cpi->ppi->gf_group.ref_frame_list[gf_index][rf] != INVALID_IDX) { + remapped_ref_idx[rf - LAST_FRAME] = + (int)cpi->ppi->gf_group.ref_frame_list[gf_index][rf]; + } + } + for (int i = 0; i < REF_FRAMES; ++i) { + if (remapped_ref_idx[i] == INVALID_IDX) { + remapped_ref_idx[i] = 0; + } + } + return; + } + #if !CONFIG_REALTIME_ONLY if (cpi->use_ducky_encode && cpi->ducky_encode_info.frame_info.gop_mode == DUCKY_ENCODE_GOP_MODE_RCL) { @@ -1123,7 +1156,7 @@ // Do not map GOLDEN and ALTREF based on their pyramid level if all reference // frames have the same level. - if (n_min_level_refs <= n_bufs) { + if (n_min_level_refs < n_bufs) { // Map the GOLDEN_FRAME. if (golden_idx > -1) add_ref_to_slot(&buffer_map[golden_idx], remapped_ref_idx, GOLDEN_FRAME); @@ -1142,8 +1175,8 @@ // Continue if the current ref slot is already full. if (remapped_ref_idx[frame - LAST_FRAME] != INVALID_IDX) continue; // Find the next unmapped reference buffer - // in decreasing ouptut order relative to current picture. - int next_buf_max = 0; + // in decreasing output order relative to current picture. + int next_buf_max = -1; int next_disp_order = INT_MIN; for (buf_map_idx = n_bufs - 1; buf_map_idx >= 0; buf_map_idx--) { if (!buffer_map[buf_map_idx].used && @@ -1164,8 +1197,8 @@ // Continue if the current ref slot is already full. if (remapped_ref_idx[frame - LAST_FRAME] != INVALID_IDX) continue; // Find the next unmapped reference buffer - // in increasing ouptut order relative to current picture. - int next_buf_max = 0; + // in increasing output order relative to current picture. + int next_buf_max = -1; int next_disp_order = INT_MAX; for (buf_map_idx = n_bufs - 1; buf_map_idx >= 0; buf_map_idx--) { if (!buffer_map[buf_map_idx].used && @@ -1302,8 +1335,7 @@ return -1; } - // TODO(sarahparker) finish bit allocation for one pass pyramid - if (has_no_stats_stage(cpi)) { + if (has_no_stats_stage(cpi) && !is_one_pass_rt_lag_params(cpi)) { gf_cfg->gf_max_pyr_height = AOMMIN(gf_cfg->gf_max_pyr_height, USE_ALTREF_FOR_ONE_PASS); gf_cfg->gf_min_pyr_height = @@ -1494,6 +1526,21 @@ *frame_flags); if (use_rtc_reference_structure_one_layer(cpi)) av1_set_rtc_reference_structure_one_layer(cpi, cpi->gf_frame_index == 0); + } else if (is_one_pass_rt_lag_params(cpi) && oxcf->rc_cfg.mode == AOM_CBR) { + // For realtime mode with lookahead in CBR: the current frame buffer_level + // is used to update the frame target_bandwidth, so we need to call + // av1_calc_i/pframe_target_size_one_pass_cbr() here for every frame. + int target; + const FRAME_UPDATE_TYPE cur_update_type = + gf_group->update_type[cpi->gf_frame_index]; + if (cur_update_type == KF_UPDATE) { + target = av1_calc_iframe_target_size_one_pass_cbr(cpi); + } else { + target = av1_calc_pframe_target_size_one_pass_cbr(cpi, cur_update_type); + } + gf_group->bit_allocation[cpi->gf_frame_index] = target; + av1_rc_set_frame_target(cpi, target, cm->width, cm->height); + cpi->rc.base_frame_target = target; } #endif #if CONFIG_COLLECT_COMPONENT_TIMING
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c index 423f2f9..7f824a9 100644 --- a/av1/encoder/encodeframe.c +++ b/av1/encoder/encodeframe.c
@@ -175,6 +175,8 @@ void av1_init_rtc_counters(MACROBLOCK *const x) { av1_init_cyclic_refresh_counters(x); x->cnt_zeromv = 0; + x->sb_col_scroll = 0; + x->sb_row_scroll = 0; } void av1_accumulate_rtc_counters(AV1_COMP *cpi, const MACROBLOCK *const x) { @@ -306,15 +308,25 @@ const int delta_q_res = delta_q_info->delta_q_res; int current_qindex = cm->quant_params.base_qindex; + const int sb_row = mi_row >> cm->seq_params->mib_size_log2; + const int sb_col = mi_col >> cm->seq_params->mib_size_log2; + const int sb_cols = + CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, cm->seq_params->mib_size_log2); + const int sb_index = sb_row * sb_cols + sb_col; if (cpi->use_ducky_encode && cpi->ducky_encode_info.frame_info.qp_mode == DUCKY_ENCODE_FRAME_MODE_QINDEX) { - const int sb_row = mi_row >> cm->seq_params->mib_size_log2; - const int sb_col = mi_col >> cm->seq_params->mib_size_log2; - const int sb_cols = - CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, cm->seq_params->mib_size_log2); - const int sb_index = sb_row * sb_cols + sb_col; current_qindex = cpi->ducky_encode_info.frame_info.superblock_encode_qindex[sb_index]; + } else if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_QP) != 0 && + cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL && + cpi->ext_ratectrl.sb_params_list != NULL) { + if (cpi->ext_ratectrl.use_delta_q) { + const int q_index = cpi->ext_ratectrl.sb_params_list[sb_index].q_index; + if (q_index != AOM_DEFAULT_Q) { + current_qindex = q_index; + } + } } else if (cpi->oxcf.q_cfg.deltaq_mode == DELTA_Q_PERCEPTUAL) { if (DELTA_Q_PERCEPTUAL_MODULATION == 1) { const int block_wavelet_energy_level = @@ -1281,8 +1293,6 @@ x->sb_me_block = 0; x->sb_me_partition = 0; x->sb_me_mv.as_int = 0; - x->sb_col_scroll = 0; - x->sb_row_scroll = 0; x->sb_force_fixed_part = 1; x->color_palette_thresh = 64; x->force_color_check_block_level = 0; @@ -1688,6 +1698,75 @@ : INTERP_SKIP_LUMA_SKIP_CHROMA; } +/*!\cond */ +typedef struct { + // Scoring function for usefulness of references (the lower score, the more + // useful) + int score; + // Index in the reference buffer + int index; +} RefScoreData; +/*!\endcond */ + +// Comparison function to sort reference frames in ascending score order. +static int compare_score_data_asc(const void *a, const void *b) { + const RefScoreData *ra = (const RefScoreData *)a; + const RefScoreData *rb = (const RefScoreData *)b; + + const int score_diff = ra->score - rb->score; + if (score_diff != 0) return score_diff; + + return ra->index - rb->index; +} + +// Determines whether a given reference frame is "good" based on temporal +// distance and base_qindex. The "good" reference frames are not allowed to be +// pruned by the speed feature "prune_single_ref" frame at block level. +static inline void setup_keep_single_ref_frame_mask(AV1_COMP *cpi) { + const int prune_single_ref = cpi->sf.inter_sf.prune_single_ref; + const AV1_COMMON *const cm = &cpi->common; + cpi->keep_single_ref_frame_mask = 0; + if (frame_is_intra_only(cm)) return; + + RefScoreData ref_score_data[INTER_REFS_PER_FRAME]; + for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) { + ref_score_data[i].score = INT_MAX; + ref_score_data[i].index = i; + } + + // Calculate score for each reference frame based on relative distance to + // the current frame and its base_qindex. A lower score means that the + // reference is potentially more useful. + for (MV_REFERENCE_FRAME ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; + ++ref_frame) { + if (cpi->ref_frame_flags & av1_ref_frame_flag_list[ref_frame]) { + const RefFrameDistanceInfo *const ref_frame_dist_info = + &cpi->ref_frame_dist_info; + const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame); + ref_score_data[ref_frame - LAST_FRAME].score = + abs(ref_frame_dist_info->ref_relative_dist[ref_frame - LAST_FRAME]) + + buf->base_qindex; + } + } + + qsort(ref_score_data, INTER_REFS_PER_FRAME, sizeof(ref_score_data[0]), + compare_score_data_asc); + + // Decide the number of reference frames for which pruning via the speed + // feature prune_single_ref is disallowed. + // prune_single_ref = 0 => None of the 7 reference frames are pruned. + // prune_single_ref = 1 => The best 5 reference frames are not pruned. + // prune_single_ref = 2 => The best 3 reference frames are not pruned. + // prune_single_ref = 3, 4 => All the 7 references are allowed to be pruned. + static const int num_frames_to_keep_lookup[5] = { INTER_REFS_PER_FRAME, 5, 3, + 0, 0 }; + const int num_frames_to_keep = num_frames_to_keep_lookup[prune_single_ref]; + for (int i = 0; i < num_frames_to_keep; ++i) { + const int idx = ref_score_data[i].index; + cpi->keep_single_ref_frame_mask |= 1 << idx; + } +} + static inline void setup_prune_ref_frame_mask(AV1_COMP *cpi) { if ((!cpi->oxcf.ref_frm_cfg.enable_onesided_comp || cpi->sf.inter_sf.disable_onesided_comp) && @@ -2255,6 +2334,9 @@ cpi->prune_ref_frame_mask = 0; // Figure out which ref frames can be skipped at frame level. setup_prune_ref_frame_mask(cpi); + // Disable certain reference frame pruning based on temporal distance and + // quality of that reference frame. + setup_keep_single_ref_frame_mask(cpi); x->txfm_search_info.txb_split_count = 0; #if CONFIG_SPEED_STATS
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c index 79da14f..0dc7ef7 100644 --- a/av1/encoder/encodeframe_utils.c +++ b/av1/encoder/encodeframe_utils.c
@@ -9,6 +9,9 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include "aom/aom_codec.h" +#include "aom/internal/aom_codec_internal.h" + #include "av1/common/common_data.h" #include "av1/common/quant_common.h" #include "av1/common/reconintra.h" @@ -182,25 +185,33 @@ struct macroblock_plane *const p = x->plane; struct macroblockd_plane *const pd = xd->plane; const MB_MODE_INFO *const mi = &ctx->mic; + if (ctx == NULL) { + aom_internal_error(cm->error, AOM_CODEC_ERROR, + "ctx is NULL in av1_update_state: %d %d %d %d %d %d \n", + cm->current_frame.frame_type, cm->width, cm->height, + mi_col, mi_row, bsize); + } + if (mi == NULL) { + aom_internal_error(cm->error, AOM_CODEC_ERROR, + "mi is NULL in av1_update_state: %d %d %d %d %d %d \n", + cm->current_frame.frame_type, cm->width, cm->height, + mi_col, mi_row, bsize); + } MB_MODE_INFO *const mi_addr = xd->mi[0]; const struct segmentation *const seg = &cm->seg; assert(bsize < BLOCK_SIZES_ALL); - const int bw = mi_size_wide[mi->bsize]; - const int bh = mi_size_high[mi->bsize]; + assert(mi->bsize == bsize); + const int bw = mi_size_wide[bsize]; + const int bh = mi_size_high[bsize]; const int mis = mi_params->mi_stride; const int mi_width = mi_size_wide[bsize]; const int mi_height = mi_size_high[bsize]; TxfmSearchInfo *txfm_info = &x->txfm_search_info; - assert(mi->bsize == bsize); - *mi_addr = *mi; copy_mbmi_ext_frame_to_mbmi_ext(&x->mbmi_ext, &ctx->mbmi_ext_best, av1_ref_frame_type(ctx->mic.ref_frame)); - memcpy(txfm_info->blk_skip, ctx->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); - txfm_info->skip_txfm = ctx->rd_stats.skip_txfm; xd->tx_type_map = ctx->tx_type_map;
diff --git a/av1/encoder/encodeframe_utils.h b/av1/encoder/encodeframe_utils.h index 46d36c8..8cd163a 100644 --- a/av1/encoder/encodeframe_utils.h +++ b/av1/encoder/encodeframe_utils.h
@@ -318,7 +318,7 @@ return av1_compute_rd_mult( qindex, bit_depth, update_type, layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, is_stat_consumption_stage(cpi), - cpi->oxcf.tune_cfg.tuning); + cpi->oxcf.tune_cfg.tuning, cpi->oxcf.mode); } static inline int do_split_check(BLOCK_SIZE bsize) {
diff --git a/av1/encoder/encodemb.c b/av1/encoder/encodemb.c index 447cae4..bf5c697 100644 --- a/av1/encoder/encodemb.c +++ b/av1/encoder/encodemb.c
@@ -248,18 +248,6 @@ } } -// Settings for optimization type. NOTE: To set optimization type for all intra -// frames, both `KEY_BLOCK_OPT_TYPE` and `INTRA_BLOCK_OPT_TYPE` should be set. -// TODO(yjshen): These settings are hard-coded and look okay for now. They -// should be made configurable later. -// Blocks of key frames ONLY. -static const OPT_TYPE KEY_BLOCK_OPT_TYPE = TRELLIS_DROPOUT_OPT; -// Blocks of intra frames (key frames EXCLUSIVE). -static const OPT_TYPE INTRA_BLOCK_OPT_TYPE = TRELLIS_DROPOUT_OPT; -// Blocks of inter frames. (NOTE: Dropout optimization is DISABLED by default -// if trellis optimization is on for inter frames.) -static const OPT_TYPE INTER_BLOCK_OPT_TYPE = TRELLIS_DROPOUT_OPT; - enum { QUANT_FUNC_LOWBD = 0, QUANT_FUNC_HIGHBD = 1, @@ -404,16 +392,13 @@ ENTROPY_CONTEXT *a, *l; int dummy_rate_cost = 0; - const int bw = mi_size_wide[plane_bsize]; dst = &pd->dst.buf[(blk_row * pd->dst.stride + blk_col) << MI_SIZE_LOG2]; a = &args->ta[blk_col]; l = &args->tl[blk_row]; TX_TYPE tx_type = DCT_DCT; - const int blk_skip_idx = blk_row * bw + blk_col; - if (!is_blk_skip(x->txfm_search_info.blk_skip, plane, blk_skip_idx) && - !mbmi->skip_mode) { + if (!mbmi->skip_mode) { tx_type = av1_get_tx_type(xd, pd->plane_type, blk_row, blk_col, tx_size, cm->features.reduced_tx_set_used); TxfmParam txfm_param; @@ -432,23 +417,12 @@ &quant_param); av1_xform_quant(x, plane, block, blk_row, blk_col, plane_bsize, &txfm_param, &quant_param); - - // Whether trellis or dropout optimization is required for inter frames. - const bool do_trellis = INTER_BLOCK_OPT_TYPE == TRELLIS_OPT || - INTER_BLOCK_OPT_TYPE == TRELLIS_DROPOUT_OPT; - const bool do_dropout = INTER_BLOCK_OPT_TYPE == DROPOUT_OPT || - INTER_BLOCK_OPT_TYPE == TRELLIS_DROPOUT_OPT; - - if (quant_param.use_optimize_b && do_trellis) { + if (use_trellis) { TXB_CTX txb_ctx; get_txb_ctx(plane_bsize, tx_size, plane, a, l, &txb_ctx); av1_optimize_b(args->cpi, x, plane, block, tx_size, tx_type, &txb_ctx, &dummy_rate_cost); } - if (!quant_param.use_optimize_b && do_dropout) { - av1_dropout_qcoeff(x, plane, block, tx_size, tx_type, - cm->quant_params.base_qindex); - } } else { p->eobs[block] = 0; p->txb_entropy_ctx[block] = 0; @@ -733,7 +707,6 @@ const AV1_COMMON *const cm = &cpi->common; MACROBLOCK *const x = args->x; MACROBLOCKD *const xd = &x->e_mbd; - MB_MODE_INFO *mbmi = xd->mi[0]; struct macroblock_plane *const p = &x->plane[plane]; struct macroblockd_plane *const pd = &xd->plane[plane]; tran_low_t *dqcoeff = p->dqcoeff + BLOCK_OFFSET(block); @@ -746,9 +719,7 @@ av1_predict_intra_block_facade(cm, xd, plane, blk_col, blk_row, tx_size); TX_TYPE tx_type = DCT_DCT; - const int bw = mi_size_wide[plane_bsize]; - if (plane == 0 && is_blk_skip(x->txfm_search_info.blk_skip, plane, - blk_row * bw + blk_col)) { + if (xd->mi[0]->skip_txfm) { *eob = 0; p->txb_entropy_ctx[block] = 0; } else { @@ -777,32 +748,12 @@ av1_xform_quant(x, plane, block, blk_row, blk_col, plane_bsize, &txfm_param, &quant_param); - - // Whether trellis or dropout optimization is required for key frames and - // intra frames. - const bool do_trellis = (frame_is_intra_only(cm) && - (KEY_BLOCK_OPT_TYPE == TRELLIS_OPT || - KEY_BLOCK_OPT_TYPE == TRELLIS_DROPOUT_OPT)) || - (!frame_is_intra_only(cm) && - (INTRA_BLOCK_OPT_TYPE == TRELLIS_OPT || - INTRA_BLOCK_OPT_TYPE == TRELLIS_DROPOUT_OPT)); - const bool do_dropout = (frame_is_intra_only(cm) && - (KEY_BLOCK_OPT_TYPE == DROPOUT_OPT || - KEY_BLOCK_OPT_TYPE == TRELLIS_DROPOUT_OPT)) || - (!frame_is_intra_only(cm) && - (INTRA_BLOCK_OPT_TYPE == DROPOUT_OPT || - INTRA_BLOCK_OPT_TYPE == TRELLIS_DROPOUT_OPT)); - - if (quant_param.use_optimize_b && do_trellis) { + if (use_trellis) { TXB_CTX txb_ctx; get_txb_ctx(plane_bsize, tx_size, plane, a, l, &txb_ctx); av1_optimize_b(args->cpi, x, plane, block, tx_size, tx_type, &txb_ctx, &dummy_rate_cost); } - if (do_dropout) { - av1_dropout_qcoeff(x, plane, block, tx_size, tx_type, - cm->quant_params.base_qindex); - } } if (*eob) { @@ -827,10 +778,6 @@ update_txk_array(xd, blk_row, blk_col, tx_size, DCT_DCT); } - // For intra mode, skipped blocks are so rare that transmitting - // skip_txfm = 1 is very expensive. - mbmi->skip_txfm = 0; - #if !CONFIG_REALTIME_ONLY if (plane == AOM_PLANE_Y && xd->cfl.store_y) { cfl_store_tx(xd, blk_row, blk_col, tx_size, plane_bsize);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index df01bae..a149da7 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -553,7 +553,7 @@ aom_internal_error( &ppi->error, AOM_CODEC_UNSUP_BITSTREAM, "AV1 does not support this combination of profile, level, and tier."); - // Buffer size in bits/s is bitrate in bits/s * 1 s + // Buffer size in bits is bitrate in bits/s * 1 s seq_params->op_params[i].buffer_size = seq_params->op_params[i].bitrate; } } @@ -879,6 +879,7 @@ const FrameDimensionCfg *const frm_dim_cfg = &cpi->oxcf.frm_dim_cfg; const RateControlCfg *const rc_cfg = &oxcf->rc_cfg; FeatureFlags *const features = &cm->features; + const int is_highbitdepth = seq_params->use_highbitdepth; // in case of LAP, lag in frames is set according to number of lap buffers // calculated at init time. This stores and restores LAP's lag in frames to @@ -976,6 +977,14 @@ } } + if (x->upsample_pred == NULL) { + CHECK_MEM_ERROR( + cm, x->upsample_pred, + aom_memalign(16, (1 + is_highbitdepth) * ((MAX_SB_SIZE + 16) + 16) * + MAX_SB_SIZE * sizeof(*x->upsample_pred))); + x->e_mbd.tmp_upsample_pred = x->upsample_pred; + } + av1_reset_segment_features(cm); av1_set_high_precision_mv(cpi, 1, 0); @@ -1750,6 +1759,8 @@ for (int frame = 0; frame < MAX_LAG_BUFFERS; ++frame) { aom_free(tpl_data->tpl_stats_pool[frame]); aom_free_frame_buffer(&tpl_data->tpl_rec_pool[frame]); + aom_free_frame_buffer(&tpl_data->prev_gop_arf_src); + tpl_data->prev_gop_arf_disp_order = -1; tpl_data->tpl_stats_pool[frame] = NULL; } @@ -3146,20 +3157,24 @@ // This is for rtc temporal filtering case. if (is_psnr_calc_enabled(cpi) && cpi->sf.rt_sf.use_rtc_tf) { - const SequenceHeader *seq_params = cm->seq_params; - if (cpi->orig_source.buffer_alloc_sz == 0 || - cpi->rc.prev_coded_width != cpi->oxcf.frm_dim_cfg.width || - cpi->rc.prev_coded_height != cpi->oxcf.frm_dim_cfg.height) { - // Allocate a source buffer to store the true source for psnr calculation. - if (aom_alloc_frame_buffer( - &cpi->orig_source, cpi->oxcf.frm_dim_cfg.width, - cpi->oxcf.frm_dim_cfg.height, seq_params->subsampling_x, - seq_params->subsampling_y, seq_params->use_highbitdepth, - cpi->oxcf.border_in_pixels, cm->features.byte_alignment, false, - 0)) + cpi->orig_source.y_crop_width != cpi->source->y_crop_width || + cpi->orig_source.y_crop_height != cpi->source->y_crop_height || + cpi->orig_source.subsampling_x != cpi->source->subsampling_x || + cpi->orig_source.subsampling_y != cpi->source->subsampling_y || + cpi->orig_source.flags != cpi->source->flags) { + // Allocate a source buffer to store the original source for psnr + // calculation. + const int use_highbitdepth = + (cpi->source->flags & YV12_FLAG_HIGHBITDEPTH) != 0; + if (aom_alloc_frame_buffer(&cpi->orig_source, cpi->source->y_crop_width, + cpi->source->y_crop_height, + cpi->source->subsampling_x, + cpi->source->subsampling_y, use_highbitdepth, + cpi->oxcf.border_in_pixels, + cm->features.byte_alignment, false, 0)) aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, - "Failed to allocate scaled buffer"); + "Failed to allocate cpi->orig_source buffer"); } aom_yv12_copy_y(cpi->source, &cpi->orig_source, 1); @@ -3175,6 +3190,11 @@ // frame. if (!frame_is_intra_only(cm)) av1_pick_and_set_high_precision_mv(cpi, q); + if (svc->temporal_layer_id == 0) { + cpi->rc.num_col_blscroll_last_tl0 = 0; + cpi->rc.num_row_blscroll_last_tl0 = 0; + } + // transform / motion compensation build reconstruction frame av1_encode_frame(cpi); @@ -3422,6 +3442,36 @@ } } + if (av1_encode_for_extrc(&cpi->ext_ratectrl)) { + aom_codec_err_t codec_status; + aom_rc_encodeframe_decision_t encode_frame_decision; + const int sb_rows = CEIL_POWER_OF_TWO(cm->mi_params.mi_rows, + cm->seq_params->mib_size_log2); + const int sb_cols = CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, + cm->seq_params->mib_size_log2); + // This assumes frame sizes don't change when used with external RC. + // cpi->ext_ratectrl is zero'ed at init. + if (cpi->ext_ratectrl.sb_params_list == NULL) { + CHECK_MEM_ERROR( + cm, cpi->ext_ratectrl.sb_params_list, + (aom_sb_params *)aom_calloc( + sb_rows * sb_cols, sizeof(*cpi->ext_ratectrl.sb_params_list))); + } + encode_frame_decision.sb_params_list = cpi->ext_ratectrl.sb_params_list; + encode_frame_decision.use_delta_q = &cpi->ext_ratectrl.use_delta_q; + codec_status = av1_extrc_get_encodeframe_decision( + &cpi->ext_ratectrl, cpi->gf_frame_index, &encode_frame_decision); + if (codec_status != AOM_CODEC_OK) { + aom_internal_error(cm->error, codec_status, + "av1_extrc_get_encodeframe_decision() failed"); + } + // If the external model recommends a reserved value, we use the default + // q. + if (encode_frame_decision.q_index != AOM_DEFAULT_Q) { + q = encode_frame_decision.q_index; + } + } + av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, oxcf->mode == ALLINTRA, oxcf->tune_cfg.tuning); @@ -3563,6 +3613,11 @@ // Ducky encode currently does not support recode loop. loop = 0; } + + // Do not recode if external rate control is used. + if (av1_encode_for_extrc(&cpi->ext_ratectrl)) { + loop = 0; + } #if CONFIG_BITRATE_ACCURACY || CONFIG_RD_COMMAND loop = 0; // turn off recode loop when CONFIG_BITRATE_ACCURACY is on #endif // CONFIG_BITRATE_ACCURACY || CONFIG_RD_COMMAND @@ -3851,7 +3906,7 @@ // Note: Both use common rdmult based on base qindex of fullres. const int64_t rdmult = av1_compute_rd_mult_based_on_qindex( bit_depth, update_type, cm->quant_params.base_qindex, - cpi->oxcf.tune_cfg.tuning); + cpi->oxcf.tune_cfg.tuning, cpi->oxcf.mode); // Find the best rdcost among all superres denoms. int best_denom = -1; @@ -3916,7 +3971,7 @@ // Note: Both use common rdmult based on base qindex of fullres. const int64_t rdmult = av1_compute_rd_mult_based_on_qindex( bit_depth, update_type, cm->quant_params.base_qindex, - cpi->oxcf.tune_cfg.tuning); + cpi->oxcf.tune_cfg.tuning, cpi->oxcf.mode); proj_rdcost1 = RDCOST_DBL_WITH_NATIVE_BD_DIST(rdmult, rate1, sse1, bit_depth); const double proj_rdcost2 = @@ -3981,6 +4036,7 @@ section->frame_avg_wavelet_energy -= frame->frame_avg_wavelet_energy; section->coded_error -= frame->coded_error; section->sr_coded_error -= frame->sr_coded_error; + section->lt_coded_error -= frame->lt_coded_error; section->pcnt_inter -= frame->pcnt_inter; section->pcnt_motion -= frame->pcnt_motion; section->pcnt_second_ref -= frame->pcnt_second_ref; @@ -4512,9 +4568,50 @@ cpi->ppi->gf_group.layer_depth[cpi->gf_frame_index], current_frame->display_order_hint, cpi->ppi->gf_group.max_layer_depth); + const GF_GROUP *gf_group = &cpi->ppi->gf_group; + // Check if this is the last frame in the gop. If so, make a copy of the + // source for TPL. + if (cpi->oxcf.algo_cfg.enable_tpl_model && + gf_group->update_type[cpi->gf_frame_index] != OVERLAY_UPDATE && + gf_group->update_type[cpi->gf_frame_index] != INTNL_OVERLAY_UPDATE) { + int is_last = 1; + for (int i = 0; i < gf_group->size; ++i) { + if (gf_group->display_idx[i] > + (int64_t)current_frame->display_order_hint) { + is_last = 0; + break; + } + } + if (is_last) { + cpi->ppi->tpl_data.prev_gop_arf_disp_order = -1; + const AV1EncoderConfig *const oxcf = &cpi->oxcf; + int ret = aom_realloc_frame_buffer( + &cpi->ppi->tpl_data.prev_gop_arf_src, oxcf->frm_dim_cfg.width, + oxcf->frm_dim_cfg.height, cm->seq_params->subsampling_x, + cm->seq_params->subsampling_y, cm->seq_params->use_highbitdepth, + cpi->oxcf.border_in_pixels, cm->features.byte_alignment, NULL, NULL, + NULL, cpi->alloc_pyramid, 0); + if (ret) + aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, + "Failed to allocate tpl prev_gop_arf_src buf."); + + // Currently it is not supported if source/refernece is resized. + if (cpi->source->y_width == cpi->ppi->tpl_data.prev_gop_arf_src.y_width && + cpi->source->y_height == + cpi->ppi->tpl_data.prev_gop_arf_src.y_height) { + // Copy the content from source to this buffer for next gop. + aom_yv12_copy_frame(cpi->source, &cpi->ppi->tpl_data.prev_gop_arf_src, + av1_num_planes(cm)); + + cpi->ppi->tpl_data.prev_gop_arf_disp_order = + current_frame->display_order_hint; + } + } + } + if (is_stat_generation_stage(cpi)) { #if !CONFIG_REALTIME_ONLY - if (cpi->oxcf.q_cfg.use_fixed_qp_offsets) + if (cpi->oxcf.q_cfg.use_fixed_qp_offsets != 0) av1_noop_first_pass_frame(cpi, frame_input->ts_duration); else av1_first_pass(cpi, frame_input->ts_duration); @@ -4529,6 +4626,16 @@ return AOM_CODEC_ERROR; } + if (cpi->ext_ratectrl.ready && + cpi->ext_ratectrl.funcs.update_encodeframe_result != NULL) { + aom_codec_err_t codec_status = av1_extrc_update_encodeframe_result( + &cpi->ext_ratectrl, (*frame_size) << 3, cm->quant_params.base_qindex); + if (codec_status != AOM_CODEC_OK) { + aom_internal_error(cm->error, codec_status, + "av1_extrc_update_encodeframe_result() failed"); + } + } + return AOM_CODEC_OK; } @@ -4577,6 +4684,33 @@ const int subsampling_y = sd->subsampling_y; const int use_highbitdepth = (sd->flags & YV12_FLAG_HIGHBITDEPTH) != 0; + // Note: Regarding profile setting, the following checks are added to help + // choose a proper profile for the input video. The criterion is that all + // bitstreams must be designated as the lowest profile that match its content. + // E.G. A bitstream that contains 4:4:4 video must be designated as High + // Profile in the seq header, and likewise a bitstream that contains 4:2:2 + // bitstream must be designated as Professional Profile in the sequence + // header. + if ((seq_params->profile == PROFILE_0) && !seq_params->monochrome && + (subsampling_x != 1 || subsampling_y != 1)) { + aom_set_error(cm->error, AOM_CODEC_INVALID_PARAM, + "Non-4:2:0 color format requires profile 1 or 2"); + return -1; + } + if ((seq_params->profile == PROFILE_1) && + !(subsampling_x == 0 && subsampling_y == 0)) { + aom_set_error(cm->error, AOM_CODEC_INVALID_PARAM, + "Profile 1 requires 4:4:4 color format"); + return -1; + } + if ((seq_params->profile == PROFILE_2) && + (seq_params->bit_depth <= AOM_BITS_10) && + !(subsampling_x == 1 && subsampling_y == 0)) { + aom_set_error(cm->error, AOM_CODEC_INVALID_PARAM, + "Profile 2 bit-depth <= 10 requires 4:2:2 color format"); + return -1; + } + #if CONFIG_TUNE_VMAF if (!is_stat_generation_stage(cpi) && cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_WITH_PREPROCESSING) { @@ -4639,33 +4773,6 @@ cpi->ppi->total_time_receive_data += aom_usec_timer_elapsed(&timer); #endif - // Note: Regarding profile setting, the following checks are added to help - // choose a proper profile for the input video. The criterion is that all - // bitstreams must be designated as the lowest profile that match its content. - // E.G. A bitstream that contains 4:4:4 video must be designated as High - // Profile in the seq header, and likewise a bitstream that contains 4:2:2 - // bitstream must be designated as Professional Profile in the sequence - // header. - if ((seq_params->profile == PROFILE_0) && !seq_params->monochrome && - (subsampling_x != 1 || subsampling_y != 1)) { - aom_set_error(cm->error, AOM_CODEC_INVALID_PARAM, - "Non-4:2:0 color format requires profile 1 or 2"); - res = -1; - } - if ((seq_params->profile == PROFILE_1) && - !(subsampling_x == 0 && subsampling_y == 0)) { - aom_set_error(cm->error, AOM_CODEC_INVALID_PARAM, - "Profile 1 requires 4:4:4 color format"); - res = -1; - } - if ((seq_params->profile == PROFILE_2) && - (seq_params->bit_depth <= AOM_BITS_10) && - !(subsampling_x == 1 && subsampling_y == 0)) { - aom_set_error(cm->error, AOM_CODEC_INVALID_PARAM, - "Profile 2 bit-depth <= 10 requires 4:2:2 color format"); - res = -1; - } - return res; } @@ -4955,16 +5062,10 @@ static inline void update_gf_group_index(AV1_COMP *cpi) { // Increment the gf group index ready for the next frame. - if (is_one_pass_rt_params(cpi) && - cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1) { - ++cpi->gf_frame_index; - // Reset gf_frame_index in case it reaches MAX_STATIC_GF_GROUP_LENGTH - // for real time encoding. - if (cpi->gf_frame_index == MAX_STATIC_GF_GROUP_LENGTH) - cpi->gf_frame_index = 0; - } else { - ++cpi->gf_frame_index; - } + ++cpi->gf_frame_index; + // Reset gf_frame_index in case it reaches MAX_STATIC_GF_GROUP_LENGTH. + if (cpi->gf_frame_index == MAX_STATIC_GF_GROUP_LENGTH) + cpi->gf_frame_index = 0; } static void update_fb_of_context_type(const AV1_COMP *const cpi, @@ -5074,6 +5175,25 @@ AV1_PRIMARY *const ppi = cpi->ppi; AV1_COMMON *const cm = &cpi->common; + if (ppi->b_freeze_internal_state) { + // Should not update encoder state, just necessary work to get the + // expected output and then return early. + + // Note *size = 0 indicates a dropped frame for which psnr is not calculated + if (ppi->b_calculate_psnr && cpi_data->frame_size > 0) { + if (cm->show_existing_frame || + (!is_stat_generation_stage(cpi) && cm->show_frame)) { + generate_psnr_packet(cpi); + } + } + + if (cpi_data->pop_lookahead == 1) { + av1_lookahead_pop(cpi->ppi->lookahead, cpi_data->flush, + cpi->compressor_stage); + } + return; + } + update_gm_stats(cpi); #if !CONFIG_REALTIME_ONLY
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h index ab19b1c..b4e25d4 100644 --- a/av1/encoder/encoder.h +++ b/av1/encoder/encoder.h
@@ -34,6 +34,7 @@ #include "av1/common/timing.h" #include "av1/encoder/aq_cyclicrefresh.h" +#include "av1/encoder/av1_ext_ratectrl.h" #include "av1/encoder/av1_quantize.h" #include "av1/encoder/block.h" #include "av1/encoder/context_tree.h" @@ -107,17 +108,6 @@ } aom_rational64_t; // alias for struct aom_rational enum { - // Good Quality Fast Encoding. The encoder balances quality with the amount of - // time it takes to encode the output. Speed setting controls how fast. - GOOD, - // Realtime Fast Encoding. Will force some restrictions on bitrate - // constraints. - REALTIME, - // All intra mode. All the frames are coded as intra frames. - ALLINTRA -} UENUM1BYTE(MODE); - -enum { FRAMEFLAGS_KEY = 1 << 0, FRAMEFLAGS_GOLDEN = 1 << 1, FRAMEFLAGS_BWDREF = 1 << 2, @@ -166,9 +156,8 @@ DELTA_Q_PERCEPTUAL = 2, // Modulation to improve video perceptual quality DELTA_Q_PERCEPTUAL_AI = 3, // Perceptual quality opt for all intra mode DELTA_Q_USER_RATING_BASED = 4, // User rating based delta q mode - DELTA_Q_HDR = 5, // QP adjustment based on HDR block pixel average - DELTA_Q_VARIANCE_BOOST = - 6, // Variance Boost style modulation for all intra mode + DELTA_Q_HDR = 5, // QP adjustment based on HDR block pixel average + DELTA_Q_VARIANCE_BOOST = 6, // Variance Boost style modulation DELTA_Q_MODE_COUNT // This should always be the last member of the enum } UENUM1BYTE(DELTAQ_MODE); @@ -790,9 +779,10 @@ } InputCfg; typedef struct { - // If true, encoder will use fixed QP offsets, that are either: - // - Given by the user, and stored in 'fixed_qp_offsets' array, OR - // - Picked automatically from cq_level. + // Controls how the encoder applies fixed QP offsets. + // If the value is 0, QP offsets are chosen adaptively. + // If the value is 1, fixed QP offsets are picked automatically from cq_level. + // If the value is 2, no QP offsets will be applied. int use_fixed_qp_offsets; // Indicates the minimum flatness of the quantization matrix. int qm_minlevel; @@ -827,9 +817,10 @@ * For values 1-7, eob and skip block optimization are * avoided and rdmult is adjusted in favor of block sharpness. * - * In all-intra mode: it also sets the `loop_filter_sharpness` syntax element - * in the bitstream. Larger values increasingly reduce how much the filtering - * can change the sample values on block edges to favor perceived sharpness. + * In all-intra mode or tune IQ or SSIMULACRA2: it also sets the + * `loop_filter_sharpness` syntax element in the bitstream. Larger values + * increasingly reduce how much the filtering can change the sample values on + * block edges to favor perceived sharpness. */ int sharpness; @@ -1488,6 +1479,7 @@ CONV_BUF_TYPE *tmp_conv_dst; uint64_t abs_sum_level; uint8_t *tmp_pred_bufs[2]; + uint8_t *upsample_pred; uint8_t *wiener_tmp_pred_buf; int intrabc_used; int deltaq_used; @@ -2379,12 +2371,21 @@ double max_scale; } WeberStats; +/*! + * \brief This structure stores different types of frame indices. + */ +typedef struct { + int show_frame_count; +} FRAME_INDEX_SET; + typedef struct { struct loopfilter lf; CdefInfo cdef_info; YV12_BUFFER_CONFIG copy_buffer; RATE_CONTROL rc; MV_STATS mv_stats; + unsigned int frame_number; + FRAME_INDEX_SET frame_index_set; } CODING_CONTEXT; typedef struct { @@ -2400,13 +2401,6 @@ int subsampling_y; } FRAME_INFO; -/*! - * \brief This structure stores different types of frame indices. - */ -typedef struct { - int show_frame_count; -} FRAME_INDEX_SET; - /*!\endcond */ /*! @@ -2888,6 +2882,13 @@ * when --deltaq-mode=3. */ AV1EncRowMultiThreadSync intra_row_mt_sync; + + /*! + * If set to 1, the encoder should not update any internal state after + * completing the encode. E.g. no updates to reference buffers, CDF + * tables or RC state. + */ + int b_freeze_internal_state; } AV1_PRIMARY; /*! @@ -3328,6 +3329,13 @@ int prune_ref_frame_mask; /*! + * Mark the reference frames which are important (based on the temporal + * distance and quality) to prevent pruning the reference frame at block + * level. + */ + int keep_single_ref_frame_mask; + + /*! * Loop Restoration context. */ AV1LrStruct lr_ctxt; @@ -3697,6 +3705,16 @@ * ROI map. */ aom_roi_map_t roi; + + /*! + * External rate control. + */ + AOM_EXT_RATECTRL ext_ratectrl; + + /*! + * Store TPL stats before propagation + */ + AomTplGopStats extrc_tpl_gop_stats; } AV1_COMP; /*! @@ -3767,9 +3785,9 @@ void av1_initialize_enc(unsigned int usage, enum aom_rc_mode end_usage); -struct AV1_COMP *av1_create_compressor(AV1_PRIMARY *ppi, +struct AV1_COMP *av1_create_compressor(struct AV1_PRIMARY *ppi, const AV1EncoderConfig *oxcf, - BufferPool *const pool, + struct BufferPool *const pool, COMPRESSOR_STAGE stage, int lap_lag_in_frames); @@ -4107,7 +4125,8 @@ static inline int is_stat_consumption_stage(const AV1_COMP *const cpi) { return (is_stat_consumption_stage_twopass(cpi) || (cpi->oxcf.pass == AOM_RC_ONE_PASS && - (cpi->compressor_stage == ENCODE_STAGE) && cpi->ppi->lap_enabled)); + (cpi->compressor_stage == ENCODE_STAGE) && cpi->ppi->lap_enabled && + cpi->oxcf.mode != REALTIME)); } // Decide whether 'dv_costs' need to be allocated/stored during the encoding. @@ -4128,11 +4147,17 @@ static inline int has_no_stats_stage(const AV1_COMP *const cpi) { assert( IMPLIES(!cpi->ppi->lap_enabled, cpi->compressor_stage == ENCODE_STAGE)); - return (cpi->oxcf.pass == AOM_RC_ONE_PASS && !cpi->ppi->lap_enabled); + return (cpi->oxcf.pass == AOM_RC_ONE_PASS && + (!cpi->ppi->lap_enabled || cpi->oxcf.mode == REALTIME)); } /*!\cond */ +static inline int is_one_pass_rt_lag_params(const AV1_COMP *cpi) { + return cpi->oxcf.pass == AOM_RC_ONE_PASS && + cpi->oxcf.gf_cfg.lag_in_frames > 0 && cpi->oxcf.mode == REALTIME; +} + static inline int is_one_pass_rt_params(const AV1_COMP *cpi) { return has_no_stats_stage(cpi) && cpi->oxcf.gf_cfg.lag_in_frames == 0 && (cpi->oxcf.mode == REALTIME || cpi->svc.number_spatial_layers > 1); @@ -4208,6 +4233,11 @@ return av1_superres_scaled(cm) || av1_resize_scaled(cm); } +static inline bool av1_encode_for_extrc(AOM_EXT_RATECTRL const *ext_rc) { + return ext_rc->ready && (ext_rc->funcs.rc_type & AOM_RC_QP) != 0 && + ext_rc->funcs.get_encodeframe_decision != NULL; +} + // Don't allow a show_existing_frame to coincide with an error resilient // frame. An exception can be made for a forward keyframe since it has no // previous dependencies.
diff --git a/av1/encoder/encoder_alloc.h b/av1/encoder/encoder_alloc.h index 13c3241..97b5273 100644 --- a/av1/encoder/encoder_alloc.h +++ b/av1/encoder/encoder_alloc.h
@@ -333,6 +333,7 @@ aom_free(cpi->td.mb.palette_buffer); release_compound_type_rd_buffers(&cpi->td.mb.comp_rd_buffer); aom_free(cpi->td.mb.tmp_conv_dst); + aom_free(cpi->td.mb.upsample_pred); for (int j = 0; j < 2; ++j) { aom_free(cpi->td.mb.tmp_pred_bufs[j]); } @@ -375,6 +376,10 @@ aom_free(cpi->mb_delta_q); cpi->mb_delta_q = NULL; + +#if !CONFIG_REALTIME_ONLY + av1_free_tpl_gop_stats(&cpi->extrc_tpl_gop_stats); +#endif } static inline void allocate_gradient_info_for_hog(AV1_COMP *cpi) { @@ -471,6 +476,7 @@ aom_free(td->tctx); aom_free(td->palette_buffer); aom_free(td->tmp_conv_dst); + aom_free(td->upsample_pred); release_compound_type_rd_buffers(&td->comp_rd_buffer); for (int j = 0; j < 2; ++j) { aom_free(td->tmp_pred_bufs[j]);
diff --git a/av1/encoder/encoder_utils.c b/av1/encoder/encoder_utils.c index 89ba731..9655c03 100644 --- a/av1/encoder/encoder_utils.c +++ b/av1/encoder/encoder_utils.c
@@ -9,6 +9,7 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <assert.h> #include <string.h> #include "aom/aomcx.h" @@ -525,7 +526,8 @@ qindex, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], 0, 15, INTER_FRAME, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); } } else { av1_disable_segmentation(seg); @@ -681,55 +683,65 @@ } #endif - // Decide q and q bounds. - *q = av1_rc_pick_q_and_bounds(cpi, cm->width, cm->height, cpi->gf_frame_index, - bottom_index, top_index); + if (cpi->oxcf.q_cfg.use_fixed_qp_offsets == 2 && + cpi->oxcf.rc_cfg.mode == AOM_Q) { + // Disable scaling, and use the same q for all frames of the pyramid + *q = cpi->oxcf.rc_cfg.cq_level; + *top_index = *bottom_index = *q; + cpi->ppi->p_rc.arf_q = *q; + } else { + // Decide q and q bounds. + *q = av1_rc_pick_q_and_bounds(cpi, cm->width, cm->height, + cpi->gf_frame_index, bottom_index, top_index); - if (cpi->oxcf.rc_cfg.mode == AOM_CBR && cpi->rc.force_max_q) { - *q = cpi->rc.worst_quality; - cpi->rc.force_max_q = 0; - } + if (cpi->oxcf.rc_cfg.mode == AOM_CBR && cpi->rc.force_max_q) { + *q = cpi->rc.worst_quality; + cpi->rc.force_max_q = 0; + } #if !CONFIG_REALTIME_ONLY - if (cpi->oxcf.rc_cfg.mode == AOM_Q && - cpi->ppi->tpl_data.tpl_frame[cpi->gf_frame_index].is_valid && - !is_lossless_requested(&cpi->oxcf.rc_cfg)) { - const RateControlCfg *const rc_cfg = &cpi->oxcf.rc_cfg; - const int tpl_q = av1_tpl_get_q_index( - &cpi->ppi->tpl_data, cpi->gf_frame_index, cpi->rc.active_worst_quality, - cm->seq_params->bit_depth); - *q = clamp(tpl_q, rc_cfg->best_allowed_q, rc_cfg->worst_allowed_q); - *top_index = *bottom_index = *q; - if (gf_group->update_type[cpi->gf_frame_index] == ARF_UPDATE) - cpi->ppi->p_rc.arf_q = *q; - } - - if (cpi->oxcf.q_cfg.use_fixed_qp_offsets && cpi->oxcf.rc_cfg.mode == AOM_Q) { - if (is_frame_tpl_eligible(gf_group, cpi->gf_frame_index)) { - const double qratio_grad = - cpi->ppi->p_rc.baseline_gf_interval > 20 ? 0.2 : 0.3; - const double qstep_ratio = - 0.2 + - (1.0 - (double)cpi->rc.active_worst_quality / MAXQ) * qratio_grad; - *q = av1_get_q_index_from_qstep_ratio( - cpi->rc.active_worst_quality, qstep_ratio, cm->seq_params->bit_depth); + if (cpi->oxcf.rc_cfg.mode == AOM_Q && + cpi->ppi->tpl_data.tpl_frame[cpi->gf_frame_index].is_valid && + !is_lossless_requested(&cpi->oxcf.rc_cfg)) { + const RateControlCfg *const rc_cfg = &cpi->oxcf.rc_cfg; + const int tpl_q = av1_tpl_get_q_index( + &cpi->ppi->tpl_data, cpi->gf_frame_index, + cpi->rc.active_worst_quality, cm->seq_params->bit_depth); + *q = clamp(tpl_q, rc_cfg->best_allowed_q, rc_cfg->worst_allowed_q); *top_index = *bottom_index = *q; - if (gf_group->update_type[cpi->gf_frame_index] == ARF_UPDATE || - gf_group->update_type[cpi->gf_frame_index] == KF_UPDATE || - gf_group->update_type[cpi->gf_frame_index] == GF_UPDATE) + if (gf_group->update_type[cpi->gf_frame_index] == ARF_UPDATE) cpi->ppi->p_rc.arf_q = *q; - } else if (gf_group->layer_depth[cpi->gf_frame_index] < - gf_group->max_layer_depth) { - int this_height = gf_group->layer_depth[cpi->gf_frame_index]; - int arf_q = cpi->ppi->p_rc.arf_q; - while (this_height > 1) { - arf_q = (arf_q + cpi->oxcf.rc_cfg.cq_level + 1) / 2; - --this_height; - } - *top_index = *bottom_index = *q = arf_q; } - } + + if (cpi->oxcf.q_cfg.use_fixed_qp_offsets == 1 && + cpi->oxcf.rc_cfg.mode == AOM_Q) { + if (is_frame_tpl_eligible(gf_group, cpi->gf_frame_index)) { + const double qratio_grad = + cpi->ppi->p_rc.baseline_gf_interval > 20 ? 0.2 : 0.3; + const double qstep_ratio = + 0.2 + + (1.0 - (double)cpi->rc.active_worst_quality / MAXQ) * qratio_grad; + *q = av1_get_q_index_from_qstep_ratio(cpi->rc.active_worst_quality, + qstep_ratio, + cm->seq_params->bit_depth); + *top_index = *bottom_index = *q; + if (gf_group->update_type[cpi->gf_frame_index] == ARF_UPDATE || + gf_group->update_type[cpi->gf_frame_index] == KF_UPDATE || + gf_group->update_type[cpi->gf_frame_index] == GF_UPDATE) + cpi->ppi->p_rc.arf_q = *q; + } else if (gf_group->layer_depth[cpi->gf_frame_index] < + gf_group->max_layer_depth) { + int this_height = gf_group->layer_depth[cpi->gf_frame_index]; + int arf_q = cpi->ppi->p_rc.arf_q; + while (this_height > 1) { + arf_q = (arf_q + cpi->oxcf.rc_cfg.cq_level + 1) / 2; + --this_height; + } + *top_index = *bottom_index = *q = arf_q; + } + } #endif + } // Configure experimental use of segmentation for enhanced coding of // static regions if indicated. @@ -738,6 +750,9 @@ if (is_stat_consumption_stage_twopass(cpi) && cpi->sf.hl_sf.static_segmentation) configure_static_seg_features(cpi); + + if (cpi->oxcf.rc_cfg.over_shoot_pct == 0) *top_index = MAXQ; + if (cpi->oxcf.rc_cfg.under_shoot_pct == 0) *bottom_index = MINQ; } #if !CONFIG_REALTIME_ONLY @@ -1314,6 +1329,8 @@ if (!cm->seq_params->reduced_still_picture_hdr && encode_show_existing_frame(cm)) { + assert(cpi->existing_fb_idx_to_show >= 0 && + cpi->existing_fb_idx_to_show < REF_FRAMES); RefCntBuffer *const frame_to_show = cm->ref_frame_map[cpi->existing_fb_idx_to_show]; @@ -1544,6 +1561,8 @@ cc->cdef_info = cm->cdef_info; cc->rc = cpi->rc; cc->mv_stats = cpi->ppi->mv_stats; + cc->frame_number = cpi->common.current_frame.frame_number; + cc->frame_index_set = cpi->frame_index_set; } void av1_save_all_coding_context(AV1_COMP *cpi) {
diff --git a/av1/encoder/encoder_utils.h b/av1/encoder/encoder_utils.h index ca33eec..8691e48 100644 --- a/av1/encoder/encoder_utils.h +++ b/av1/encoder/encoder_utils.h
@@ -889,6 +889,8 @@ restore_cdef_coding_context(&cm->cdef_info, &cc->cdef_info); cpi->rc = cc->rc; cpi->ppi->mv_stats = cc->mv_stats; + cpi->common.current_frame.frame_number = cc->frame_number; + cpi->frame_index_set = cc->frame_index_set; } static inline int equal_dimensions_and_border(const YV12_BUFFER_CONFIG *a,
diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c index 2ffd6ee..636a418 100644 --- a/av1/encoder/ethread.c +++ b/av1/encoder/ethread.c
@@ -963,6 +963,7 @@ assert(p_mt_info->workers != NULL); assert(p_mt_info->tile_thr_data != NULL); + const int is_highbitdepth = ppi->seq_params.use_highbitdepth; int num_workers = p_mt_info->num_workers; int num_enc_workers = av1_get_num_mod_workers_for_alloc(p_mt_info, MOD_ENC); assert(num_enc_workers <= num_workers); @@ -992,6 +993,11 @@ "Failed to allocate PICK_MODE_CONTEXT"); } + AOM_CHECK_MEM_ERROR( + &ppi->error, td->upsample_pred, + aom_memalign(16, (1 + is_highbitdepth) * ((MAX_SB_SIZE + 16) + 16) * + MAX_SB_SIZE * sizeof(*td->upsample_pred))); + if (!is_first_pass && i < num_enc_workers) { // Set up sms_tree. if (av1_setup_sms_tree(ppi->cpi, td)) { @@ -1633,6 +1639,7 @@ thread_data->td->mb.palette_buffer = thread_data->td->palette_buffer; thread_data->td->mb.comp_rd_buffer = thread_data->td->comp_rd_buffer; thread_data->td->mb.tmp_conv_dst = thread_data->td->tmp_conv_dst; + thread_data->td->mb.upsample_pred = thread_data->td->upsample_pred; for (int j = 0; j < 2; ++j) { thread_data->td->mb.tmp_pred_bufs[j] = thread_data->td->tmp_pred_bufs[j]; @@ -1644,6 +1651,8 @@ thread_data->td->src_var_info_of_4x4_sub_blocks; thread_data->td->mb.e_mbd.tmp_conv_dst = thread_data->td->mb.tmp_conv_dst; + thread_data->td->mb.e_mbd.tmp_upsample_pred = + thread_data->td->mb.upsample_pred; for (int j = 0; j < 2; ++j) { thread_data->td->mb.e_mbd.tmp_obmc_bufs[j] = thread_data->td->mb.tmp_pred_bufs[j]; @@ -2294,6 +2303,9 @@ } thread_data->td->mb.tmp_conv_dst = thread_data->td->tmp_conv_dst; thread_data->td->mb.e_mbd.tmp_conv_dst = thread_data->td->mb.tmp_conv_dst; + thread_data->td->mb.upsample_pred = thread_data->td->upsample_pred; + thread_data->td->mb.e_mbd.tmp_upsample_pred = + thread_data->td->mb.upsample_pred; } } } @@ -2470,6 +2482,9 @@ aom_internal_error(cpi->common.error, AOM_CODEC_MEM_ERROR, "Error allocating temporal filter data"); } + thread_data->td->mb.upsample_pred = thread_data->td->upsample_pred; + thread_data->td->mb.e_mbd.tmp_upsample_pred = + thread_data->td->mb.upsample_pred; } } }
diff --git a/av1/encoder/firstpass.c b/av1/encoder/firstpass.c index 4436f00..e8c15cb 100644 --- a/av1/encoder/firstpass.c +++ b/av1/encoder/firstpass.c
@@ -97,6 +97,7 @@ section->log_intra_error = 0.0; section->log_coded_error = 0.0; section->sr_coded_error = 0.0; + section->lt_coded_error = 0.0; section->pcnt_inter = 0.0; section->pcnt_motion = 0.0; section->pcnt_second_ref = 0.0; @@ -129,6 +130,7 @@ section->frame_avg_wavelet_energy += frame->frame_avg_wavelet_energy; section->coded_error += frame->coded_error; section->sr_coded_error += frame->sr_coded_error; + section->lt_coded_error += frame->lt_coded_error; section->pcnt_inter += frame->pcnt_inter; section->pcnt_motion += frame->pcnt_motion; section->pcnt_second_ref += frame->pcnt_second_ref; @@ -254,7 +256,8 @@ // for first pass test. static int get_search_range(int width, int height) { int sr = 0; - const int dim = AOMMIN(width, height); + int dim = AOMMIN(width, height); + dim = AOMMAX(dim, MI_SIZE); while ((dim << sr) < MAX_FULL_PEL_VAL) ++sr; return sr; @@ -520,6 +523,7 @@ xd->lossless[xd->mi[0]->segment_id] = (qindex == 0); xd->mi[0]->mode = DC_PRED; xd->mi[0]->tx_size = TX_4X4; + xd->mi[0]->skip_txfm = 0; if (cpi->sf.fp_sf.disable_recon) first_pass_predict_intra_block_for_luma_plane(seq_params, x, bsize); @@ -679,10 +683,10 @@ // golden_frame: the frame buffer of the golden frame. // unit_row: row index in the unit of first pass block size. // unit_col: column index in the unit of first pass block size. -// recon_yoffset: the y offset of the reconstructed frame buffer, +// recon_yoffset: the y offset of the reconstructed frame buffer, // indicating the starting point of the current block. -// recont_uvoffset: the u/v offset of the reconstructed frame buffer, -// indicating the starting point of the current block. +// recon_uvoffset: the u/v offset of the reconstructed frame buffer, +// indicating the starting point of the current block. // src_yoffset: the y offset of the source frame buffer. // fp_block_size: first pass block size. // this_intra_error: the intra prediction error of this block. @@ -701,7 +705,8 @@ // this_inter_error static int firstpass_inter_prediction( AV1_COMP *cpi, ThreadData *td, const YV12_BUFFER_CONFIG *const last_frame, - const YV12_BUFFER_CONFIG *const golden_frame, const int unit_row, + const YV12_BUFFER_CONFIG *const golden_frame, + const YV12_BUFFER_CONFIG *const last2_frame, const int unit_row, const int unit_col, const int recon_yoffset, const int recon_uvoffset, const int src_yoffset, const BLOCK_SIZE fp_block_size, const int this_intra_error, const int raw_motion_err_counts, @@ -769,6 +774,19 @@ } } + int last2_motion_error = motion_error; + if ((current_frame->frame_number > 2) && last2_frame != NULL) { + FULLPEL_MV tmp_mv = kZeroFullMv; + // Assume 0,0 motion with no mv overhead. + av1_setup_pre_planes(xd, 0, last2_frame, 0, 0, NULL, 1); + xd->plane[0].pre[0].buf += recon_yoffset; + last2_motion_error = + get_prediction_error_bitdepth(is_high_bitdepth, bitdepth, bsize, + &x->plane[0].src, &xd->plane[0].pre[0]); + first_pass_motion_search(cpi, x, &kZeroMv, &tmp_mv, &last2_motion_error); + stats->lt_coded_error += AOMMIN(last2_motion_error, this_intra_error); + } + // Motion search in 2nd reference frame. int gf_motion_error = motion_error; if ((current_frame->frame_number > 1) && golden_frame != NULL) { @@ -858,6 +876,7 @@ double f_h) { fps->coded_error /= num_mbs_16x16; fps->sr_coded_error /= num_mbs_16x16; + fps->lt_coded_error /= num_mbs_16x16; fps->intra_error /= num_mbs_16x16; fps->frame_avg_wavelet_energy /= num_mbs_16x16; fps->log_coded_error = log1p(fps->coded_error); @@ -913,6 +932,7 @@ fps.frame = frame_number; fps.coded_error = (double)(stats->coded_error >> 8) + min_err; fps.sr_coded_error = (double)(stats->sr_coded_error >> 8) + min_err; + fps.lt_coded_error = (double)(stats->lt_coded_error >> 8) + min_err; fps.intra_error = (double)(stats->intra_error >> 8) + min_err; fps.frame_avg_wavelet_energy = (double)stats->frame_avg_wavelet_energy; fps.count = 1.0; @@ -1032,6 +1052,7 @@ stats.new_mv_count += mb_stat.new_mv_count; stats.second_ref_count += mb_stat.second_ref_count; stats.sr_coded_error += mb_stat.sr_coded_error; + stats.lt_coded_error += mb_stat.lt_coded_error; stats.sum_in_vectors += mb_stat.sum_in_vectors; stats.sum_mvc += mb_stat.sum_mvc; stats.sum_mvc_abs += mb_stat.sum_mvc_abs; @@ -1125,8 +1146,8 @@ MACROBLOCKD *const xd = &x->e_mbd; TileInfo *tile = &tile_data->tile_info; const int qindex = find_fp_qindex(seq_params->bit_depth); - const int fp_block_size_width = block_size_high[fp_block_size]; - const int fp_block_size_height = block_size_wide[fp_block_size]; + const int fp_block_size_width = block_size_wide[fp_block_size]; + const int fp_block_size_height = block_size_high[fp_block_size]; const int unit_width = mi_size_wide[fp_block_size]; const int unit_width_log2 = mi_size_wide_log2[fp_block_size]; const int unit_height_log2 = mi_size_high_log2[fp_block_size]; @@ -1149,6 +1170,10 @@ if (!golden_frame) { golden_frame = get_ref_frame_yv12_buf(cm, GOLDEN_FRAME); } + const YV12_BUFFER_CONFIG *last2_frame = + av1_get_scaled_ref_frame(cpi, LAST2_FRAME); + if (!last2_frame) last2_frame = get_ref_frame_yv12_buf(cm, LAST2_FRAME); + YV12_BUFFER_CONFIG *const this_frame = &cm->cur_frame->buf; PICK_MODE_CONTEXT *ctx = td->firstpass_ctx; @@ -1169,6 +1194,8 @@ const int src_y_stride = cpi->source->y_stride; const int recon_y_stride = this_frame->y_stride; const int recon_uv_stride = this_frame->uv_stride; + const int uv_mb_width = + fp_block_size_width >> (this_frame->y_width > this_frame->uv_width); const int uv_mb_height = fp_block_size_height >> (this_frame->y_height > this_frame->uv_height); @@ -1182,7 +1209,7 @@ int src_yoffset = (unit_row * src_y_stride * fp_block_size_height) + (unit_col_start * fp_block_size_width); int recon_uvoffset = (unit_row * recon_uv_stride * uv_mb_height) + - (unit_col_start * uv_mb_height); + (unit_col_start * uv_mb_width); // Set up limit values for motion vectors to prevent them extending // outside the UMV borders. @@ -1222,10 +1249,10 @@ if (!frame_is_intra_only(cm)) { const int this_inter_error = firstpass_inter_prediction( - cpi, td, last_frame, golden_frame, unit_row, unit_col, recon_yoffset, - recon_uvoffset, src_yoffset, fp_block_size, this_intra_error, - raw_motion_err_counts, raw_motion_err_list, best_ref_mv, &best_ref_mv, - &last_mv, mb_stats); + cpi, td, last_frame, golden_frame, last2_frame, unit_row, unit_col, + recon_yoffset, recon_uvoffset, src_yoffset, fp_block_size, + this_intra_error, raw_motion_err_counts, raw_motion_err_list, + best_ref_mv, &best_ref_mv, &last_mv, mb_stats); if (unit_col_in_tile == 0) { *first_top_mv = last_mv; } @@ -1234,18 +1261,19 @@ } else { mb_stats->sr_coded_error += this_intra_error; mb_stats->coded_error += this_intra_error; + mb_stats->lt_coded_error += this_intra_error; } // Adjust to the next column of MBs. x->plane[0].src.buf += fp_block_size_width; if (num_planes > 1) { - x->plane[1].src.buf += uv_mb_height; - x->plane[2].src.buf += uv_mb_height; + x->plane[1].src.buf += uv_mb_width; + x->plane[2].src.buf += uv_mb_width; } recon_yoffset += fp_block_size_width; src_yoffset += fp_block_size_width; - recon_uvoffset += uv_mb_height; + recon_uvoffset += uv_mb_width; mb_stats++; enc_row_mt->sync_write_ptr(row_mt_sync, unit_row_in_tile, unit_col_in_tile, @@ -1288,7 +1316,8 @@ const int qindex = find_fp_qindex(seq_params->bit_depth); const int ref_frame_flags_backup = cpi->ref_frame_flags; cpi->ref_frame_flags = av1_ref_frame_flag_list[LAST_FRAME] | - av1_ref_frame_flag_list[GOLDEN_FRAME]; + av1_ref_frame_flag_list[GOLDEN_FRAME] | + av1_ref_frame_flag_list[LAST2_FRAME]; // Detect if the key frame is screen content type. if (frame_is_intra_only(cm)) { @@ -1342,6 +1371,8 @@ const YV12_BUFFER_CONFIG *last_frame = NULL; const YV12_BUFFER_CONFIG *golden_frame = NULL; + const YV12_BUFFER_CONFIG *last2_frame = NULL; + if (!frame_is_intra_only(cm)) { av1_scale_references(cpi, EIGHTTAP_REGULAR, 0, 0); last_frame = av1_is_scaled(get_ref_scale_factors_const(cm, LAST_FRAME)) @@ -1350,6 +1381,9 @@ golden_frame = av1_is_scaled(get_ref_scale_factors_const(cm, GOLDEN_FRAME)) ? av1_get_scaled_ref_frame(cpi, GOLDEN_FRAME) : get_ref_frame_yv12_buf(cm, GOLDEN_FRAME); + last2_frame = av1_is_scaled(get_ref_scale_factors_const(cm, LAST2_FRAME)) + ? av1_get_scaled_ref_frame(cpi, LAST2_FRAME) + : get_ref_frame_yv12_buf(cm, LAST2_FRAME); } YV12_BUFFER_CONFIG *const this_frame = &cm->cur_frame->buf; @@ -1439,6 +1473,12 @@ current_frame->frame_number, ts_duration, fp_block_size); + if (this_frame_stats->pcnt_inter < 0.2 && last2_frame != NULL) { + assign_frame_buffer_p( + &cm->ref_frame_map[get_ref_frame_map_idx(cm, LAST2_FRAME)], + cm->ref_frame_map[get_ref_frame_map_idx(cm, GOLDEN_FRAME)]); + } + // Copy the previous Last Frame back into gf buffer if the prediction is good // enough... but also don't allow it to lag too far. if ((twopass->sr_update_lag > 3) || @@ -1471,6 +1511,13 @@ cm->ref_frame_map[get_ref_frame_map_idx(cm, LAST_FRAME)]); } + if (current_frame->frame_number == 0 && + get_ref_frame_map_idx(cm, LAST2_FRAME) != INVALID_IDX) { + assign_frame_buffer_p( + &cm->ref_frame_map[get_ref_frame_map_idx(cm, LAST2_FRAME)], + cm->ref_frame_map[get_ref_frame_map_idx(cm, LAST_FRAME)]); + } + print_reconstruction_frame(last_frame, current_frame->frame_number, /*do_print=*/0);
diff --git a/av1/encoder/firstpass.h b/av1/encoder/firstpass.h index a1b7e7d..639eda7 100644 --- a/av1/encoder/firstpass.h +++ b/av1/encoder/firstpass.h
@@ -69,6 +69,10 @@ */ double sr_coded_error; /*! + * Best of intra pred error and inter pred error using long term frame as ref. + */ + double lt_coded_error; + /*! * Percentage of blocks with inter pred error < intra pred error. */ double pcnt_inter; @@ -393,6 +397,9 @@ int skip_frame_as_ref[MAX_STATIC_GF_GROUP_LENGTH]; // Indicates whether a switch frame is due. bool is_sframe_due; + // Indicates whether the ref frame map is overridden by the external rate + // control. + int use_ext_ref_frame_map[MAX_STATIC_GF_GROUP_LENGTH]; /*!\endcond */ } GF_GROUP; /*!\cond */ @@ -482,6 +489,9 @@ int64_t coded_error; // Best of intra pred error and inter pred error using golden frame as ref. int64_t sr_coded_error; + // Best of coded error using long term reference. + int64_t lt_coded_error; + // Count of motion vector. int mv_count; // Count of blocks that pick inter prediction (inter pred error is smaller
diff --git a/av1/encoder/gop_structure.c b/av1/encoder/gop_structure.c index 15ec0eb..01c3e76 100644 --- a/av1/encoder/gop_structure.c +++ b/av1/encoder/gop_structure.c
@@ -17,10 +17,12 @@ #include "aom/aom_codec.h" #include "aom/aom_encoder.h" +#include "aom/aom_ext_ratectrl.h" #include "av1/common/av1_common_int.h" #include "av1/encoder/encoder.h" +#include "av1/encoder/av1_ext_ratectrl.h" #include "av1/encoder/firstpass.h" #include "av1/encoder/gop_structure.h" #include "av1/encoder/pass2_strategy.h" @@ -65,7 +67,7 @@ GF_GROUP *const gf_group, int *cur_frame_idx, int *frame_ind, int *parallel_frame_count, int max_parallel_frames, int do_frame_parallel_encode, int *first_frame_index, int *cur_disp_index, - int layer_depth, int start, int end) { + int layer_depth, int start, int end, const bool scale_max_boost) { gf_group->update_type[*frame_ind] = LF_UPDATE; gf_group->arf_src_offset[*frame_ind] = 0; gf_group->cur_frame_idx[*frame_ind] = *cur_frame_idx; @@ -76,7 +78,7 @@ gf_group->display_idx[*frame_ind] = (*cur_disp_index); gf_group->arf_boost[*frame_ind] = av1_calc_arf_boost(twopass, twopass_frame, p_rc, frame_info, start, - end - start, 0, NULL, NULL, 0); + end - start, 0, NULL, NULL, 0, scale_max_boost); ++(*cur_disp_index); // Set the level of parallelism for the LF_UPDATE frame. @@ -118,7 +120,7 @@ int *parallel_frame_count, int max_parallel_frames, int do_frame_parallel_encode, int *first_frame_index, int depth_thr, int *cur_disp_idx, int layer_depth, int arf_src_offset, int offset, - int f_frames, int b_frames) { + int f_frames, int b_frames, const bool scale_max_boost) { gf_group->update_type[*frame_ind] = INTNL_ARF_UPDATE; gf_group->arf_src_offset[*frame_ind] = arf_src_offset; gf_group->cur_frame_idx[*frame_ind] = *cur_frame_idx; @@ -129,7 +131,7 @@ (*cur_disp_idx) + gf_group->arf_src_offset[*frame_ind]; gf_group->arf_boost[*frame_ind] = av1_calc_arf_boost(twopass, twopass_frame, p_rc, frame_info, offset, - f_frames, b_frames, NULL, NULL, 0); + f_frames, b_frames, NULL, NULL, 0, scale_max_boost); if (do_frame_parallel_encode) { if (depth_thr != INT_MAX) { @@ -177,7 +179,8 @@ RATE_CONTROL *rc, FRAME_INFO *frame_info, int start, int end, int *cur_frame_idx, int *frame_ind, int *parallel_frame_count, int max_parallel_frames, int do_frame_parallel_encode, - int *first_frame_index, int depth_thr, int *cur_disp_idx, int layer_depth) { + int *first_frame_index, int depth_thr, int *cur_disp_idx, int layer_depth, + const bool scale_max_boost) { const int num_frames_to_process = end - start; // Either we are at the last level of the pyramid, or we don't have enough @@ -186,11 +189,11 @@ num_frames_to_process < 3) { // Leaf nodes. while (start < end) { - set_params_for_leaf_frames(twopass, twopass_frame, p_rc, frame_info, - gf_group, cur_frame_idx, frame_ind, - parallel_frame_count, max_parallel_frames, - do_frame_parallel_encode, first_frame_index, - cur_disp_idx, layer_depth, start, end); + set_params_for_leaf_frames( + twopass, twopass_frame, p_rc, frame_info, gf_group, cur_frame_idx, + frame_ind, parallel_frame_count, max_parallel_frames, + do_frame_parallel_encode, first_frame_index, cur_disp_idx, + layer_depth, start, end, scale_max_boost); ++start; } } else { @@ -202,7 +205,7 @@ twopass, twopass_frame, p_rc, frame_info, gf_group, cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, first_frame_index, INT_MAX, cur_disp_idx, - layer_depth, arf_src_offset, m, end - m, m - start); + layer_depth, arf_src_offset, m, end - m, m - start, scale_max_boost); // If encode reordering is enabled, configure the multi-layers accordingly // and return. For e.g., the encode order for gf-interval 16 after @@ -224,7 +227,7 @@ frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, first_frame_index, depth_thr, cur_disp_idx, layer_depth + 1, arf_src_offsets[i], offset[i], - f_frames[i], b_frames[i]); + f_frames[i], b_frames[i], scale_max_boost); } // Initialize the start and end indices to configure LF_UPDATE frames. @@ -240,7 +243,8 @@ twopass, twopass_frame, gf_group, p_rc, rc, frame_info, start_idx[i], end_idx[i], cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, - first_frame_index, depth_thr, cur_disp_idx, layer_depth + 2); + first_frame_index, depth_thr, cur_disp_idx, layer_depth + 2, + scale_max_boost); if (layer_depth_for_intnl_overlay[i] != INVALID_IDX) set_params_for_intnl_overlay_frames( gf_group, cur_frame_idx, frame_ind, first_frame_index, @@ -254,7 +258,7 @@ twopass, twopass_frame, gf_group, p_rc, rc, frame_info, start, m, cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, first_frame_index, depth_thr, cur_disp_idx, - layer_depth + 1); + layer_depth + 1, scale_max_boost); // Overlay for internal ARF. set_params_for_intnl_overlay_frames(gf_group, cur_frame_idx, frame_ind, @@ -266,7 +270,7 @@ twopass, twopass_frame, gf_group, p_rc, rc, frame_info, m + 1, end, cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, first_frame_index, depth_thr, cur_disp_idx, - layer_depth + 1); + layer_depth + 1, scale_max_boost); } } @@ -379,7 +383,7 @@ int *cur_frame_idx, int *frame_ind, int *count_arf_frames, int *doh_gf_index_map, int *parallel_frame_count, int *first_frame_index, int *cur_disp_index, int gf_interval, int layer_depth, - int max_parallel_frames) { + int max_parallel_frames, const bool scale_max_boost) { assert(layer_depth == 2); assert(gf_group->max_layer_depth_allowed >= 4); int layer, node_start, node_end = 0; @@ -416,7 +420,7 @@ set_params_for_leaf_frames( twopass, twopass_frame, p_rc, frame_info, gf_group, cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, 1, - first_frame_index, cur_disp_index, layer, 0, 0); + first_frame_index, cur_disp_index, layer, 0, 0, scale_max_boost); } else { // In order to obtain the layer depths of INTNL_OVERLAY_UPDATE frames, get // the gf index of corresponding INTNL_ARF_UPDATE frames. @@ -436,7 +440,8 @@ RATE_CONTROL *rc, FRAME_INFO *frame_info, int start, int end, int *cur_frame_idx, int *frame_ind, int *parallel_frame_count, int max_parallel_frames, int do_frame_parallel_encode, - int *first_frame_index, int *cur_disp_idx, int layer_depth) { + int *first_frame_index, int *cur_disp_idx, int layer_depth, + const bool scale_max_boost) { const int num_frames_to_process = end - start; // Either we are at the last level of the pyramid, or we don't have enough @@ -452,7 +457,7 @@ gf_group->layer_depth[*frame_ind] = MAX_ARF_LAYERS; gf_group->arf_boost[*frame_ind] = av1_calc_arf_boost(twopass, twopass_frame, p_rc, frame_info, start, - end - start, 0, NULL, NULL, 0); + end - start, 0, NULL, NULL, 0, scale_max_boost); gf_group->frame_type[*frame_ind] = INTER_FRAME; gf_group->refbuf_state[*frame_ind] = REFBUF_UPDATE; gf_group->max_layer_depth = @@ -498,7 +503,7 @@ // Get the boost factor for intermediate ARF frames. gf_group->arf_boost[*frame_ind] = av1_calc_arf_boost(twopass, twopass_frame, p_rc, frame_info, m, end - m, - m - start, NULL, NULL, 0); + m - start, NULL, NULL, 0, scale_max_boost); ++(*frame_ind); // Frames displayed before this internal ARF. @@ -506,7 +511,7 @@ frame_info, start, m, cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, first_frame_index, - cur_disp_idx, layer_depth + 1); + cur_disp_idx, layer_depth + 1, scale_max_boost); // Overlay for internal ARF. gf_group->update_type[*frame_ind] = INTNL_OVERLAY_UPDATE; @@ -528,7 +533,7 @@ frame_info, m + 1, end, cur_frame_idx, frame_ind, parallel_frame_count, max_parallel_frames, do_frame_parallel_encode, first_frame_index, - cur_disp_idx, layer_depth + 1); + cur_disp_idx, layer_depth + 1, scale_max_boost); } } @@ -665,6 +670,8 @@ gf_group->max_layer_depth_allowed >= 4); int first_frame_index = cur_frame_index; + const bool scale_max_boost = (cpi->oxcf.mode != REALTIME); + if (do_frame_parallel_encode) { // construct_multi_layer_gf_structure() takes the input parameter // 'gf_interval' as p_rc->baseline_gf_interval - 1 . Below code computes the @@ -708,7 +715,7 @@ arf_frame_stats, &cur_frame_index, &frame_index, &count_arf_frames, doh_gf_index_map, ¶llel_frame_count, &first_frame_index, &cur_disp_index, actual_gf_length, use_altref + 1, - cpi->ppi->num_fp_contexts); + cpi->ppi->num_fp_contexts, scale_max_boost); // Set gf_group->skip_frame_refresh. for (int i = 0; i < actual_gf_length; i++) { @@ -736,7 +743,7 @@ cur_frame_index, gf_interval, &cur_frame_index, &frame_index, ¶llel_frame_count, cpi->ppi->num_fp_contexts, do_frame_parallel_encode, &first_frame_index, depth_thr, - &cur_disp_index, use_altref + 1); + &cur_disp_index, use_altref + 1, scale_max_boost); } is_multi_layer_configured = 1; } @@ -748,7 +755,7 @@ &cur_frame_index, &frame_index, ¶llel_frame_count, cpi->ppi->num_fp_contexts, do_frame_parallel_encode, &first_frame_index, - &cur_disp_index, use_altref + 1); + &cur_disp_index, use_altref + 1, scale_max_boost); if (use_altref) { gf_group->update_type[frame_index] = OVERLAY_UPDATE; @@ -839,7 +846,54 @@ gf_group->max_layer_depth = AOMMIN(log_gop_length, MAX_ARF_LAYERS); } -void av1_gop_setup_structure(AV1_COMP *cpi) { +static void construct_gop_structure_from_rc( + GF_GROUP *gf_group, aom_rc_gop_decision_t *rc_gop_decision) { + gf_group->size = rc_gop_decision->gop_frame_count; + for (int frame_index = 0; frame_index < gf_group->size; ++frame_index) { + aom_rc_gop_frame_t *gop_frame_rc = + &rc_gop_decision->gop_frame_list[frame_index]; + gf_group->update_type[frame_index] = gop_frame_rc->update_type; + gf_group->layer_depth[frame_index] = gop_frame_rc->layer_depth; + gf_group->update_ref_idx[frame_index] = gop_frame_rc->update_ref_idx; + // `display_idx` means differently in libaom and RC. + // - in libaom: it is display order index in the GOP, equivalent to + // `order_idx` in RC + // - in RC: it is the number of display frames precedeing this frame, which + // is equivalent to `cur_frame_idx` in libaom. + gf_group->display_idx[frame_index] = gop_frame_rc->order_idx; + gf_group->cur_frame_idx[frame_index] = gop_frame_rc->display_idx; + switch (gf_group->update_type[frame_index]) { + case LF_UPDATE: + case INTNL_OVERLAY_UPDATE: + gf_group->arf_src_offset[frame_index] = 0; + break; + case ARF_UPDATE: + case INTNL_ARF_UPDATE: + gf_group->arf_src_offset[frame_index] = + gop_frame_rc->order_idx - gop_frame_rc->display_idx; + break; + default: gf_group->arf_src_offset[frame_index] = 0; + } + gf_group->frame_type[frame_index] = + gop_frame_rc->is_key_frame ? KEY_FRAME : INTER_FRAME; + gf_group->refbuf_state[frame_index] = + gop_frame_rc->is_key_frame ? REFBUF_RESET : REFBUF_UPDATE; + // Always override the ref frame map from external RC. + gf_group->use_ext_ref_frame_map[frame_index] = 1; + for (int i = 0; i < REF_FRAMES; ++i) { + gf_group->ref_frame_list[frame_index][i] = INVALID_IDX; + } + for (int i = 0; i < AOM_RC_MAX_REF_FRAMES; ++i) { + int ref_name = gop_frame_rc->ref_frame_list.name[i]; + int buf_idx = gop_frame_rc->ref_frame_list.index[i]; + if (ref_name >= LAST_FRAME && ref_name <= ALTREF_FRAME) { + gf_group->ref_frame_list[frame_index][ref_name] = (int8_t)buf_idx; + } + } + } +} + +void av1_gop_setup_structure(AV1_COMP *cpi, const int is_final_pass) { RATE_CONTROL *const rc = &cpi->rc; PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; GF_GROUP *const gf_group = &cpi->ppi->gf_group; @@ -848,26 +902,45 @@ const int key_frame = rc->frames_since_key == 0; FRAME_UPDATE_TYPE first_frame_update_type = ARF_UPDATE; - if (key_frame) { - first_frame_update_type = KF_UPDATE; - if (cpi->oxcf.kf_max_pyr_height != -1) { - gf_group->max_layer_depth_allowed = AOMMIN( - cpi->oxcf.kf_max_pyr_height, gf_group->max_layer_depth_allowed); + // define_gf_group() is called twice in av1_set_second_pass_params() with + // `is_final_pass` being 0 and 1 separately. But only one GOP can be advanced + // with the external RC. That is only done when `is_final_pass` is true. + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_GOP) != 0 && + cpi->ext_ratectrl.funcs.get_gop_decision != NULL && is_final_pass) { + aom_rc_gop_decision_t gop_decision; + aom_codec_err_t codec_status = + av1_extrc_get_gop_decision(&cpi->ext_ratectrl, &gop_decision); + if (codec_status != AOM_CODEC_OK) { + aom_internal_error(cpi->common.error, codec_status, + "av1_extrc_get_gop_decision() failed"); } - } else if (!cpi->ppi->gf_state.arf_gf_boost_lst) { - first_frame_update_type = GF_UPDATE; + construct_gop_structure_from_rc(gf_group, &gop_decision); + if (gop_decision.gop_frame_list[0].is_key_frame) { + rc->frames_since_key = 0; + } + } else { + if (key_frame) { + first_frame_update_type = KF_UPDATE; + if (cpi->oxcf.kf_max_pyr_height != -1) { + gf_group->max_layer_depth_allowed = AOMMIN( + cpi->oxcf.kf_max_pyr_height, gf_group->max_layer_depth_allowed); + } + } else if (!cpi->ppi->gf_state.arf_gf_boost_lst) { + first_frame_update_type = GF_UPDATE; + } + + if (cpi->oxcf.algo_cfg.sharpness == 3) + gf_group->max_layer_depth_allowed = + AOMMIN(gf_group->max_layer_depth_allowed, 2); + + gf_group->size = construct_multi_layer_gf_structure( + cpi, twopass, gf_group, rc, frame_info, p_rc->baseline_gf_interval, + first_frame_update_type); + + if (gf_group->max_layer_depth_allowed == 0) + set_ld_layer_depth(gf_group, p_rc->baseline_gf_interval); } - - if (cpi->oxcf.algo_cfg.sharpness == 3) - gf_group->max_layer_depth_allowed = - AOMMIN(gf_group->max_layer_depth_allowed, 2); - - gf_group->size = construct_multi_layer_gf_structure( - cpi, twopass, gf_group, rc, frame_info, p_rc->baseline_gf_interval, - first_frame_update_type); - - if (gf_group->max_layer_depth_allowed == 0) - set_ld_layer_depth(gf_group, p_rc->baseline_gf_interval); } int av1_gop_check_forward_keyframe(const GF_GROUP *gf_group,
diff --git a/av1/encoder/gop_structure.h b/av1/encoder/gop_structure.h index 92380ab..14ef5bf 100644 --- a/av1/encoder/gop_structure.h +++ b/av1/encoder/gop_structure.h
@@ -36,11 +36,13 @@ * the group. It does this primarily by updateing entries in * cpi->twopass.gf_group.update_type[]. * - * \param[in] cpi Top - level encoder instance structure + * \param[in] cpi Top - level encoder instance structure + * \param[in] is_final_pass Whether it is the second call to + * define_gf_group(). * * \remark No return value but this function updates group data structures. */ -void av1_gop_setup_structure(struct AV1_COMP *cpi); +void av1_gop_setup_structure(struct AV1_COMP *cpi, const int is_final_pass); /*!\brief Check whether a frame in the GOP is a forward key frame *
diff --git a/av1/encoder/interp_search.h b/av1/encoder/interp_search.h index 28b036d..428d1e4 100644 --- a/av1/encoder/interp_search.h +++ b/av1/encoder/interp_search.h
@@ -128,12 +128,12 @@ /*! * Stack to store full pixel search start mv of NEWMV mode. */ - FULLPEL_MV start_mv_stack[(MAX_REF_MV_SEARCH - 1) * 2]; + FULLPEL_MV start_mv_stack[MAX_REF_MV_SEARCH * 2]; /*! * Stack to store ref_mv_idx of NEWMV mode. */ - uint8_t ref_mv_idx_stack[(MAX_REF_MV_SEARCH - 1) * 2]; + uint8_t ref_mv_idx_stack[MAX_REF_MV_SEARCH * 2]; /*! * Count of mvs in start mv stack.
diff --git a/av1/encoder/intra_mode_search.c b/av1/encoder/intra_mode_search.c index e8904c7..4cefbab 100644 --- a/av1/encoder/intra_mode_search.c +++ b/av1/encoder/intra_mode_search.c
@@ -298,8 +298,6 @@ best_tx_size = mbmi->tx_size; filter_intra_mode_info = mbmi->filter_intra_mode_info; av1_copy_array(best_tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); - memcpy(ctx->blk_skip, x->txfm_search_info.blk_skip, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); *rate = this_rate; *rate_tokenonly = tokenonly_rd_stats.rate; *distortion = tokenonly_rd_stats.dist; @@ -626,7 +624,7 @@ } else { av1_init_rd_stats(rd_stats); av1_txfm_rd_in_plane(x, cpi, rd_stats, INT64_MAX, 0, plane, plane_bsize, - tx_size, FTXS_NONE, 0); + tx_size, FTXS_NONE); av1_rd_cost_update(x->rdmult, rd_stats); cfl_cost = rd_stats->rdcost; } @@ -1048,7 +1046,6 @@ x->palette_buffer->best_palette_color_map; uint8_t *const color_map = xd->plane[0].color_index_map; MB_MODE_INFO best_mbmi_palette = *mbmi; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; const ModeCosts *mode_costs = &x->mode_costs; const int *const intra_mode_cost = @@ -1064,18 +1061,15 @@ RD_STATS rd_stats_y; av1_invalid_rd_stats(&rd_stats_y); - av1_rd_pick_palette_intra_sby(cpi, x, bsize, intra_mode_cost[DC_PRED], - &best_mbmi_palette, best_palette_color_map, - &best_rd_palette, &rd_stats_y.rate, NULL, - &rd_stats_y.dist, &rd_stats_y.skip_txfm, NULL, - ctx, best_blk_skip, best_tx_type_map); + av1_rd_pick_palette_intra_sby( + cpi, x, bsize, intra_mode_cost[DC_PRED], &best_mbmi_palette, + best_palette_color_map, &best_rd_palette, &rd_stats_y.rate, NULL, + &rd_stats_y.dist, &rd_stats_y.skip_txfm, NULL, ctx, best_tx_type_map); if (rd_stats_y.rate == INT_MAX || pmi->palette_size[0] == 0) { this_rd_cost->rdcost = INT64_MAX; return skippable; } - memcpy(x->txfm_search_info.blk_skip, best_blk_skip, - sizeof(best_blk_skip[0]) * bsize_to_num_blk(bsize)); av1_copy_array(xd->tx_type_map, best_tx_type_map, ctx->num_4x4_blk); memcpy(color_map, best_palette_color_map, rows * cols * sizeof(best_palette_color_map[0])); @@ -1137,7 +1131,6 @@ x->palette_buffer->best_palette_color_map; uint8_t *const color_map = xd->plane[0].color_index_map; MB_MODE_INFO best_mbmi_palette = *mbmi; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; const ModeCosts *mode_costs = &x->mode_costs; const int *const intra_mode_cost = @@ -1153,18 +1146,15 @@ RD_STATS rd_stats_y; av1_invalid_rd_stats(&rd_stats_y); - av1_rd_pick_palette_intra_sby(cpi, x, bsize, intra_mode_cost[DC_PRED], - &best_mbmi_palette, best_palette_color_map, - &best_rd_palette, &rd_stats_y.rate, NULL, - &rd_stats_y.dist, &rd_stats_y.skip_txfm, NULL, - ctx, best_blk_skip, best_tx_type_map); + av1_rd_pick_palette_intra_sby( + cpi, x, bsize, intra_mode_cost[DC_PRED], &best_mbmi_palette, + best_palette_color_map, &best_rd_palette, &rd_stats_y.rate, NULL, + &rd_stats_y.dist, &rd_stats_y.skip_txfm, NULL, ctx, best_tx_type_map); if (rd_stats_y.rate == INT_MAX || pmi->palette_size[0] == 0) { this_rd_cost->rdcost = INT64_MAX; return; } - memcpy(x->txfm_search_info.blk_skip, best_blk_skip, - sizeof(best_blk_skip[0]) * bsize_to_num_blk(bsize)); av1_copy_array(xd->tx_type_map, best_tx_type_map, ctx->num_4x4_blk); memcpy(color_map, best_palette_color_map, rows * cols * sizeof(best_palette_color_map[0])); @@ -1231,8 +1221,6 @@ *rate_tokenonly = this_rate_tokenonly; *distortion = rd_stats.dist; *skippable = rd_stats.skip_txfm; - av1_copy_array(ctx->blk_skip, x->txfm_search_info.blk_skip, - ctx->num_4x4_blk); av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); return 1; } @@ -1262,9 +1250,6 @@ int filter_intra_selected_flag = 0; TX_SIZE best_tx_size = mbmi->tx_size; FILTER_INTRA_MODE best_fi_mode = FILTER_DC_PRED; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; - memcpy(best_blk_skip, x->txfm_search_info.blk_skip, - sizeof(best_blk_skip[0]) * ctx->num_4x4_blk); uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; av1_copy_array(best_tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); mbmi->filter_intra_mode_info.use_filter_intra = 1; @@ -1285,8 +1270,6 @@ if (this_rd_tmp < best_rd_so_far) { best_tx_size = mbmi->tx_size; av1_copy_array(best_tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); - memcpy(best_blk_skip, x->txfm_search_info.blk_skip, - sizeof(best_blk_skip[0]) * ctx->num_4x4_blk); best_fi_mode = fi_mode; *rd_stats_y = rd_stats_y_fi; filter_intra_selected_flag = 1; @@ -1296,8 +1279,6 @@ mbmi->tx_size = best_tx_size; av1_copy_array(xd->tx_type_map, best_tx_type_map, ctx->num_4x4_blk); - memcpy(x->txfm_search_info.blk_skip, best_blk_skip, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); if (filter_intra_selected_flag) { mbmi->filter_intra_mode_info.use_filter_intra = 1; @@ -1325,15 +1306,7 @@ const int mode_cost = mode_costs->mbmode_cost[size_group_lookup[bsize]][mode] + ref_frame_cost; const int skip_ctx = av1_get_skip_txfm_context(xd); - - int known_rate = mode_cost; - const int intra_cost_penalty = av1_get_intra_cost_penalty( - cm->quant_params.base_qindex, cm->quant_params.y_dc_delta_q, - cm->seq_params->bit_depth); - - if (mode != DC_PRED && mode != PAETH_PRED) known_rate += intra_cost_penalty; - known_rate += AOMMIN(mode_costs->skip_txfm_cost[skip_ctx][0], - mode_costs->skip_txfm_cost[skip_ctx][1]); + int known_rate = mode_cost + mode_costs->skip_txfm_cost[skip_ctx][0]; const int64_t known_rd = RDCOST(x->rdmult, known_rate, 0); if (known_rd > best_rd) { intra_search_state->skip_intra_modes = 1; @@ -1424,28 +1397,25 @@ const int try_palette = cpi->oxcf.tool_cfg.enable_palette && av1_allow_palette(cm->features.allow_screen_content_tools, mbmi->bsize); - assert(intra_search_state->rate_uv_intra == INT_MAX); - if (intra_search_state->rate_uv_intra == INT_MAX) { - // If no good uv-predictor had been found, search for it. - const TX_SIZE uv_tx = av1_get_tx_size(AOM_PLANE_U, xd); - av1_rd_pick_intra_sbuv_mode(cpi, x, &intra_search_state->rate_uv_intra, - &intra_search_state->rate_uv_tokenonly, - &intra_search_state->dist_uvs, - &intra_search_state->skip_uvs, bsize, uv_tx); - intra_search_state->mode_uv = mbmi->uv_mode; - if (try_palette) intra_search_state->pmi_uv = *pmi; - intra_search_state->uv_angle_delta = mbmi->angle_delta[PLANE_TYPE_UV]; + // If no good uv-predictor had been found, search for it. + const TX_SIZE uv_tx = av1_get_tx_size(AOM_PLANE_U, xd); + av1_rd_pick_intra_sbuv_mode(cpi, x, &intra_search_state->rate_uv_intra, + &intra_search_state->rate_uv_tokenonly, + &intra_search_state->dist_uvs, + &intra_search_state->skip_uvs, bsize, uv_tx); + intra_search_state->mode_uv = mbmi->uv_mode; + if (try_palette) intra_search_state->pmi_uv = *pmi; + intra_search_state->uv_angle_delta = mbmi->angle_delta[PLANE_TYPE_UV]; - const int uv_rate = intra_search_state->rate_uv_tokenonly; - const int64_t uv_dist = intra_search_state->dist_uvs; - const int64_t uv_rd = RDCOST(x->rdmult, uv_rate, uv_dist); - if (uv_rd > best_rd) { - // If there is no good intra uv-mode available, we can skip all intra - // modes. - intra_search_state->skip_intra_modes = 1; - return 0; - } + const int uv_rate = intra_search_state->rate_uv_tokenonly; + const int64_t uv_dist = intra_search_state->dist_uvs; + const int64_t uv_rd = RDCOST(x->rdmult, uv_rate, uv_dist); + if (uv_rd > best_rd) { + // If there is no good intra uv-mode available, we can skip all intra + // modes. + intra_search_state->skip_intra_modes = 1; + return 0; } // If we are here, then the encoder has found at least one good intra uv @@ -1686,18 +1656,16 @@ *rate_tokenonly = this_rate_tokenonly; *distortion = this_distortion; *skippable = s; - memcpy(ctx->blk_skip, x->txfm_search_info.blk_skip, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); } } // Searches palette if (try_palette) { - av1_rd_pick_palette_intra_sby( - cpi, x, bsize, bmode_costs[DC_PRED], &best_mbmi, best_palette_color_map, - &best_rd, rate, rate_tokenonly, distortion, skippable, &beat_best_rd, - ctx, ctx->blk_skip, ctx->tx_type_map); + av1_rd_pick_palette_intra_sby(cpi, x, bsize, bmode_costs[DC_PRED], + &best_mbmi, best_palette_color_map, &best_rd, + rate, rate_tokenonly, distortion, skippable, + &beat_best_rd, ctx, ctx->tx_type_map); } // Searches filter_intra
diff --git a/av1/encoder/mcomp.c b/av1/encoder/mcomp.c index 06145ce..1739066 100644 --- a/av1/encoder/mcomp.c +++ b/av1/encoder/mcomp.c
@@ -1366,6 +1366,22 @@ do_init_search, cost_list, best_mv, best_mv_stats); } +static inline void update_best_site(unsigned int sad, const FULLPEL_MV *best_mv, + const search_site *site, int idx, + const MV_COST_PARAMS *mv_cost_params, + unsigned int *bestsad, int *best_site) { + if (sad < *bestsad) { + const FULLPEL_MV this_mv = { best_mv->row + site[idx].mv.row, + best_mv->col + site[idx].mv.col }; + const unsigned int thissad = + sad + mvsad_err_cost_(&this_mv, mv_cost_params); + if (thissad < *bestsad) { + *bestsad = thissad; + *best_site = idx; + } + } +} + static int diamond_search_sad(FULLPEL_MV start_mv, unsigned int start_mv_sad, const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, const int search_step, int *num00, @@ -1467,24 +1483,22 @@ unsigned char const *block_offset[4]; unsigned int sads[4]; - for (int j = 0; j < 4; j++) - block_offset[j] = site[idx + j].offset + best_address; + block_offset[0] = site[idx + 0].offset + best_address; + block_offset[1] = site[idx + 1].offset + best_address; + block_offset[2] = site[idx + 2].offset + best_address; + block_offset[3] = site[idx + 3].offset + best_address; ms_params->sdx4df(src_buf, src_stride, block_offset, ref_stride, sads); - for (int j = 0; j < 4; j++) { - if (sads[j] < bestsad) { - const FULLPEL_MV this_mv = { best_mv->row + site[idx + j].mv.row, - best_mv->col + - site[idx + j].mv.col }; - unsigned int thissad = - sads[j] + mvsad_err_cost_(&this_mv, mv_cost_params); - if (thissad < bestsad) { - bestsad = thissad; - best_site = idx + j; - } - } - } + + update_best_site(sads[0], best_mv, site, idx + 0, mv_cost_params, + &bestsad, &best_site); + update_best_site(sads[1], best_mv, site, idx + 1, mv_cost_params, + &bestsad, &best_site); + update_best_site(sads[2], best_mv, site, idx + 2, mv_cost_params, + &bestsad, &best_site); + update_best_site(sads[3], best_mv, site, idx + 3, mv_cost_params, + &bestsad, &best_site); } } else { for (int idx = 1; idx <= num_searches; idx++) { @@ -2572,7 +2586,7 @@ unsigned int besterr; #if CONFIG_AV1_HIGHBITDEPTH if (is_cur_buf_hbd(xd)) { - DECLARE_ALIGNED(16, uint16_t, pred16[MAX_SB_SQUARE]); + uint16_t *pred16 = (uint16_t *)(xd->tmp_upsample_pred); uint8_t *pred8 = CONVERT_TO_BYTEPTR(pred16); if (second_pred != NULL) { if (mask) { @@ -2593,7 +2607,7 @@ } besterr = vfp->vf(pred8, w, src, src_stride, sse); } else { - DECLARE_ALIGNED(16, uint8_t, pred[MAX_SB_SQUARE]); + uint8_t *pred = xd->tmp_upsample_pred; if (second_pred != NULL) { if (mask) { aom_comp_mask_upsampled_pred( @@ -2614,7 +2628,7 @@ besterr = vfp->vf(pred, w, src, src_stride, sse); } #else - DECLARE_ALIGNED(16, uint8_t, pred[MAX_SB_SQUARE]); + uint8_t *pred = xd->tmp_upsample_pred; if (second_pred != NULL) { if (mask) { aom_comp_mask_upsampled_pred(xd, cm, mi_row, mi_col, this_mv, pred, @@ -2895,7 +2909,7 @@ best_mv->col + diag_step.col }; int has_better_mv = 0; - if (var_params->subpel_search_type != USE_2_TAPS_ORIG) { + if (var_params->subpel_search_type > USE_2_TAPS) { check_better(xd, cm, &row_bias_mv, best_mv, mv_limits, var_params, mv_cost_params, besterr, sse1, distortion, &has_better_mv); check_better(xd, cm, &col_bias_mv, best_mv, mv_limits, var_params, @@ -3326,7 +3340,7 @@ *distortion = start_mv_stats->distortion; *sse1 = start_mv_stats->sse; } else { - if (subpel_search_type != USE_2_TAPS_ORIG) { + if (subpel_search_type > USE_2_TAPS) { besterr = upsampled_setup_center_error(xd, cm, bestmv, var_params, mv_cost_params, sse1, distortion); } else { @@ -3346,7 +3360,7 @@ } MV diag_step; - if (subpel_search_type != USE_2_TAPS_ORIG) { + if (subpel_search_type > USE_2_TAPS) { diag_step = first_level_check(xd, cm, iter_center_mv, bestmv, hstep, mv_limits, var_params, mv_cost_params, &besterr, sse1, distortion); @@ -3682,21 +3696,23 @@ const int mi_col = xd->mi_col; unsigned int besterr; - DECLARE_ALIGNED(16, uint8_t, pred[2 * MAX_SB_SQUARE]); #if CONFIG_AV1_HIGHBITDEPTH if (is_cur_buf_hbd(xd)) { - uint8_t *pred8 = CONVERT_TO_BYTEPTR(pred); + uint16_t *pred16 = (uint16_t *)(xd->tmp_upsample_pred); + uint8_t *pred8 = CONVERT_TO_BYTEPTR(pred16); aom_highbd_upsampled_pred(xd, cm, mi_row, mi_col, this_mv, pred8, w, h, subpel_x_q3, subpel_y_q3, ref, ref_stride, xd->bd, subpel_search_type); besterr = vfp->ovf(pred8, w, wsrc, mask, sse); } else { + uint8_t *pred = xd->tmp_upsample_pred; aom_upsampled_pred(xd, cm, mi_row, mi_col, this_mv, pred, w, h, subpel_x_q3, subpel_y_q3, ref, ref_stride, subpel_search_type); besterr = vfp->ovf(pred, w, wsrc, mask, sse); } #else + uint8_t *pred = xd->tmp_upsample_pred; aom_upsampled_pred(xd, cm, mi_row, mi_col, this_mv, pred, w, h, subpel_x_q3, subpel_y_q3, ref, ref_stride, subpel_search_type);
diff --git a/av1/encoder/motion_search_facade.c b/av1/encoder/motion_search_facade.c index 5169989..0bd7d9d 100644 --- a/av1/encoder/motion_search_facade.c +++ b/av1/encoder/motion_search_facade.c
@@ -169,6 +169,8 @@ else start_mv = get_fullmv_from_mv(&ref_mv); + const FULLPEL_MV fullpel_ref_mv = start_mv; + // cand stores start_mv and all possible MVs in a SB. cand_mv_t cand[MAX_TPL_BLK_IN_SB * MAX_TPL_BLK_IN_SB + 1]; av1_zero(cand); @@ -183,54 +185,74 @@ const int cand_cnt = AOMMIN(2, cnt); // TODO(any): Test the speed feature for OBMC_CAUSAL mode. - if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv && + if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv_refmv && mbmi->motion_mode == SIMPLE_TRANSLATION) { - const int stack_size = args->start_mv_cnt; for (int cand_idx = 0; cand_idx < cand_cnt; cand_idx++) { int_mv *fmv_cand = &cand[cand_idx].fmv; int skip_cand_mv = 0; // Check difference between mvs in the stack and candidate mv. - for (int stack_idx = 0; stack_idx < stack_size; stack_idx++) { - const uint8_t this_ref_mv_idx = args->ref_mv_idx_stack[stack_idx]; - const FULLPEL_MV *fmv_stack = &args->start_mv_stack[stack_idx]; + for (int stack_idx = 0; stack_idx < args->start_mv_cnt; stack_idx++) { + uint8_t this_ref_mv_idx = args->ref_mv_idx_stack[stack_idx]; const int this_newmv_valid = args->single_newmv_valid[this_ref_mv_idx][ref]; - const int row_diff = abs(fmv_stack->row - fmv_cand->as_fullmv.row); - const int col_diff = abs(fmv_stack->col - fmv_cand->as_fullmv.col); - if (!this_newmv_valid) continue; + if (!this_newmv_valid && this_ref_mv_idx != mbmi->ref_mv_idx) continue; - if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv >= 2) { + const FULLPEL_MV *fmv_stack = &args->start_mv_stack[stack_idx]; + const int start_mv_row_diff = + abs(fmv_stack->row - fmv_cand->as_fullmv.row); + const int start_mv_col_diff = + abs(fmv_stack->col - fmv_cand->as_fullmv.col); + + if (mbmi->mode == NEAR_NEWMV || mbmi->mode == NEW_NEARMV) { + assert(has_second_ref(mbmi)); + this_ref_mv_idx += 1; + } + const MV this_ref_mv = + av1_get_ref_mv_from_stack(ref_idx, mbmi->ref_frame, this_ref_mv_idx, + &x->mbmi_ext) + .as_mv; + + assert(IMPLIES(args->ref_mv_idx_stack[stack_idx] == mbmi->ref_mv_idx, + this_ref_mv.row == ref_mv.row)); + assert(IMPLIES(args->ref_mv_idx_stack[stack_idx] == mbmi->ref_mv_idx, + this_ref_mv.col == ref_mv.col)); + + const FULLPEL_MV this_fullpel_ref_mv = get_fullmv_from_mv(&this_ref_mv); + const int ref_mv_row_diff = + abs(this_fullpel_ref_mv.row - fullpel_ref_mv.row); + const int ref_mv_col_diff = + abs(this_fullpel_ref_mv.col - fullpel_ref_mv.col); + + if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv_refmv >= 2) { // Prunes the current start_mv candidate, if the absolute mv // difference of both row and column are <= 1. - if (row_diff <= 1 && col_diff <= 1) { + if (start_mv_row_diff <= 1 && start_mv_col_diff <= 1 && + ref_mv_row_diff <= 1 && ref_mv_col_diff <= 1) { skip_cand_mv = 1; break; } - } else if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv >= 1) { + } else if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv_refmv >= 1) { // Prunes the current start_mv candidate, if the sum of the absolute // mv difference of row and column is <= 1. - if (row_diff + col_diff <= 1) { + if ((start_mv_row_diff + start_mv_col_diff <= 1) && + (ref_mv_row_diff + ref_mv_col_diff <= 1)) { skip_cand_mv = 1; break; } } } if (skip_cand_mv) { - // Ensure atleast one full-pel motion search is not pruned. - assert(mbmi->ref_mv_idx != 0); // Mark the candidate mv as invalid so that motion search gets skipped. cand[cand_idx].fmv.as_int = INVALID_MV; } else { // Store start_mv candidate and corresponding ref_mv_idx of full-pel - // search in the mv stack (except last ref_mv_idx). - if (mbmi->ref_mv_idx != MAX_REF_MV_SEARCH - 1) { - assert(args->start_mv_cnt < (MAX_REF_MV_SEARCH - 1) * 2); - args->start_mv_stack[args->start_mv_cnt] = fmv_cand->as_fullmv; - args->ref_mv_idx_stack[args->start_mv_cnt] = mbmi->ref_mv_idx; - args->start_mv_cnt++; - } + // search in the mv stack. + assert(args->start_mv_cnt < MAX_REF_MV_SEARCH * 2); + args->start_mv_stack[args->start_mv_cnt] = fmv_cand->as_fullmv; + args->ref_mv_idx_stack[args->start_mv_cnt] = mbmi->ref_mv_idx; + args->start_mv_cnt++; } } }
diff --git a/av1/encoder/motion_search_facade.h b/av1/encoder/motion_search_facade.h index a1cbb77..5e7f417 100644 --- a/av1/encoder/motion_search_facade.h +++ b/av1/encoder/motion_search_facade.h
@@ -123,10 +123,17 @@ const int sf_blk_search_method = mv_sf->use_bsize_dependent_search_method; const int min_dim = AOMMIN(block_size_wide[bsize], block_size_high[bsize]); const int qband = x->qindex >> (QINDEX_BITS - 2); - const bool use_faster_search_method = - (sf_blk_search_method == 1 && min_dim >= 32) || - (sf_blk_search_method >= 2 && min_dim >= 16 && - x->content_state_sb.source_sad_nonrd <= kMedSad && qband < 3); + const int min_dim_th[4] = { 128, 64, 32, 16 }; + bool use_faster_search_method = false; + + if (sf_blk_search_method >= 1 && sf_blk_search_method <= 3) { + use_faster_search_method = + (min_dim >= min_dim_th[sf_blk_search_method - 1]); + } else if (sf_blk_search_method == 4) { + use_faster_search_method = + (min_dim >= min_dim_th[sf_blk_search_method - 1] && + x->content_state_sb.source_sad_nonrd <= kMedSad && qband < 3); + } if (use_faster_search_method) { search_method = av1_get_faster_search_method(search_method);
diff --git a/av1/encoder/nonrd_opt.c b/av1/encoder/nonrd_opt.c index 1acf7fb..a196e0c 100644 --- a/av1/encoder/nonrd_opt.c +++ b/av1/encoder/nonrd_opt.c
@@ -708,7 +708,6 @@ PRED_BUFFER *tmp_buffers, PRED_BUFFER **this_mode_pred, RD_STATS *best_rdc, BEST_PICKMODE *best_pickmode, - PICK_MODE_CONTEXT *ctx, unsigned int *best_sad_norm) { AV1_COMMON *const cm = &cpi->common; MACROBLOCKD *const xd = &x->e_mbd; @@ -924,14 +923,8 @@ mi->uv_mode = this_mode; mi->mv[0].as_int = INVALID_MV; mi->mv[1].as_int = INVALID_MV; - if (!this_rdc.skip_txfm) - memset(ctx->blk_skip, 0, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); } } - if (best_pickmode->best_ref_frame == INTRA_FRAME) - memset(ctx->blk_skip, 0, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); mi->tx_size = best_pickmode->best_tx_size; *best_sad_norm = args.best_sad >>
diff --git a/av1/encoder/nonrd_opt.h b/av1/encoder/nonrd_opt.h index 0bdf73e..9f81c0a 100644 --- a/av1/encoder/nonrd_opt.h +++ b/av1/encoder/nonrd_opt.h
@@ -21,7 +21,9 @@ #define RTC_MODES (AOMMAX(RTC_INTER_MODES, RTC_INTRA_MODES)) #define CALC_BIASED_RDCOST(rdcost) (7 * (rdcost) >> 3) #define NUM_COMP_INTER_MODES_RT (6) +#define NUM_COMP_INTER_MODES_RT_FULL (10) #define NUM_INTER_MODES 12 +#define NUM_INTER_MODES_FULL 28 #define CAP_TX_SIZE_FOR_BSIZE_GT32(tx_mode_search_type, bsize) \ (((tx_mode_search_type) != ONLY_4X4 && (bsize) > BLOCK_32X32) ? true : false) #define TX_SIZE_FOR_BSIZE_GT32 (TX_16X16) @@ -144,6 +146,23 @@ { ALTREF_FRAME, GLOBALMV }, { ALTREF_FRAME, NEWMV }, }; +static const REF_MODE ref_mode_set_full[NUM_INTER_MODES_FULL] = { + { LAST_FRAME, NEARESTMV }, { LAST_FRAME, NEARMV }, + { LAST_FRAME, GLOBALMV }, { LAST_FRAME, NEWMV }, + { GOLDEN_FRAME, NEARESTMV }, { GOLDEN_FRAME, NEARMV }, + { GOLDEN_FRAME, GLOBALMV }, { GOLDEN_FRAME, NEWMV }, + { ALTREF_FRAME, NEARESTMV }, { ALTREF_FRAME, NEARMV }, + { ALTREF_FRAME, GLOBALMV }, { ALTREF_FRAME, NEWMV }, + { LAST2_FRAME, NEARESTMV }, { LAST2_FRAME, NEARMV }, + { LAST2_FRAME, GLOBALMV }, { LAST2_FRAME, NEWMV }, + { LAST3_FRAME, NEARESTMV }, { LAST3_FRAME, NEARMV }, + { LAST3_FRAME, GLOBALMV }, { LAST3_FRAME, NEWMV }, + { BWDREF_FRAME, NEARESTMV }, { BWDREF_FRAME, NEARMV }, + { BWDREF_FRAME, GLOBALMV }, { BWDREF_FRAME, NEWMV }, + { ALTREF2_FRAME, NEARESTMV }, { ALTREF2_FRAME, NEARMV }, + { ALTREF2_FRAME, GLOBALMV }, { ALTREF2_FRAME, NEWMV }, +}; + static const COMP_REF_MODE comp_ref_mode_set[NUM_COMP_INTER_MODES_RT] = { { { LAST_FRAME, GOLDEN_FRAME }, GLOBAL_GLOBALMV }, { { LAST_FRAME, GOLDEN_FRAME }, NEAREST_NEARESTMV }, @@ -153,6 +172,20 @@ { { LAST_FRAME, ALTREF_FRAME }, NEAREST_NEARESTMV }, }; +static const COMP_REF_MODE + comp_ref_mode_set_full[NUM_COMP_INTER_MODES_RT_FULL] = { + { { LAST_FRAME, GOLDEN_FRAME }, GLOBAL_GLOBALMV }, + { { LAST_FRAME, GOLDEN_FRAME }, NEAREST_NEARESTMV }, + { { LAST_FRAME, LAST2_FRAME }, GLOBAL_GLOBALMV }, + { { LAST_FRAME, LAST2_FRAME }, NEAREST_NEARESTMV }, + { { LAST_FRAME, ALTREF_FRAME }, GLOBAL_GLOBALMV }, + { { LAST_FRAME, ALTREF_FRAME }, NEAREST_NEARESTMV }, + { { LAST_FRAME, BWDREF_FRAME }, GLOBAL_GLOBALMV }, + { { LAST_FRAME, BWDREF_FRAME }, NEAREST_NEARESTMV }, + { { LAST_FRAME, ALTREF2_FRAME }, GLOBAL_GLOBALMV }, + { { LAST_FRAME, ALTREF2_FRAME }, NEAREST_NEARESTMV }, + }; + static const int_interpfilters filters_ref_set[9] = { [0].as_filters = { EIGHTTAP_REGULAR, EIGHTTAP_REGULAR }, [1].as_filters = { EIGHTTAP_SMOOTH, EIGHTTAP_SMOOTH }, @@ -466,7 +499,8 @@ &frame_mv[NEARESTMV][ref_frame], &frame_mv[NEARMV][ref_frame], 0); frame_mv[GLOBALMV][ref_frame] = mbmi_ext->global_mvs[ref_frame]; // Early exit for non-LAST frame if force_skip_low_temp_var is set. - if (!ref_is_scaled && bsize >= BLOCK_8X8 && !skip_pred_mv && + if (!is_one_pass_rt_lag_params(cpi) && !ref_is_scaled && + bsize >= BLOCK_8X8 && !skip_pred_mv && !(force_skip_low_temp_var && ref_frame != LAST_FRAME)) { av1_mv_pred(cpi, x, yv12_mb[ref_frame][0].buf, yv12->y_stride, ref_frame, bsize); @@ -528,7 +562,7 @@ cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && cpi->oxcf.speed >= 11 && cpi->rc.high_source_sad && ((cpi->sf.rt_sf.prune_palette_search_nonrd > 2) || - (cpi->sf.rt_sf.rc_compute_spatial_var_sc && + (cpi->sf.rt_sf.rc_compute_spatial_var_sc_kf && cpi->rc.frame_spatial_variance < 1200 && cpi->rc.perc_spatial_flat_blocks < 5 && cpi->rc.percent_blocks_with_motion > 98 && source_variance < 4000))); @@ -585,7 +619,6 @@ PRED_BUFFER *tmp_buffers, PRED_BUFFER **this_mode_pred, RD_STATS *best_rdc, BEST_PICKMODE *best_pickmode, - PICK_MODE_CONTEXT *ctx, unsigned int *best_sad_norm); #endif // AOM_AV1_ENCODER_NONRD_OPT_H_
diff --git a/av1/encoder/nonrd_pickmode.c b/av1/encoder/nonrd_pickmode.c index d7d2636..14581d7 100644 --- a/av1/encoder/nonrd_pickmode.c +++ b/av1/encoder/nonrd_pickmode.c
@@ -77,8 +77,9 @@ // Copy best inter mode parameters to best_pickmode static inline void update_search_state_nonrd( InterModeSearchStateNonrd *search_state, MB_MODE_INFO *const mi, - TxfmSearchInfo *txfm_info, RD_STATS *nonskip_rdc, PICK_MODE_CONTEXT *ctx, + TxfmSearchInfo *txfm_info, RD_STATS *nonskip_rdc, PREDICTION_MODE this_best_mode, const int64_t sse_y) { + (void)txfm_info; BEST_PICKMODE *const best_pickmode = &search_state->best_pickmode; best_pickmode->best_sse = sse_y; @@ -93,10 +94,6 @@ best_pickmode->best_mode_skip_txfm = search_state->this_rdc.skip_txfm; best_pickmode->best_mode_initial_skip_flag = (nonskip_rdc->rate == INT_MAX && search_state->this_rdc.skip_txfm); - if (!best_pickmode->best_mode_skip_txfm) { - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); - } } static inline int subpel_select(AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize, @@ -210,6 +207,14 @@ int cost_list[5]; int search_subpel = 1; + if (av1_is_scaled(get_ref_scale_factors_const(cm, ref))) { + const YV12_BUFFER_CONFIG *scaled_ref = av1_get_scaled_ref_frame(cpi, ref); + (void)scaled_ref; + assert(scaled_ref != NULL); + assert(scaled_ref->y_crop_width == cm->width && + scaled_ref->y_crop_height == cm->height); + } + start_mv = get_fullmv_from_mv(&ref_mv); if (!use_base_mv) @@ -314,7 +319,8 @@ int_mv *this_ref_frm_newmv = &frame_mv[NEWMV][ref_frame]; unsigned int y_sad_zero; if (ref_frame > LAST_FRAME && cpi->oxcf.rc_cfg.mode == AOM_CBR && - (cpi->ref_frame_flags & AOM_LAST_FLAG) && gf_temporal_ref) { + (cpi->ref_frame_flags & AOM_LAST_FLAG) && gf_temporal_ref && + !is_one_pass_rt_lag_params(cpi)) { int tmp_sad; int dis; @@ -374,7 +380,8 @@ const MACROBLOCKD *xd, const ModeCosts *mode_costs, int segment_id, BLOCK_SIZE bsize, - unsigned int *ref_costs_single) { + unsigned int *ref_costs_single, + int is_one_pass_rt_lag) { int seg_ref_active = segfeature_active(&cm->seg, segment_id, SEG_LVL_REF_FRAME); if (seg_ref_active) { @@ -398,6 +405,18 @@ ref_costs_single[GOLDEN_FRAME] += mode_costs->single_ref_cost[0][1][0]; ref_costs_single[ALTREF_FRAME] += mode_costs->single_ref_cost[0][0][1]; ref_costs_single[ALTREF_FRAME] += mode_costs->single_ref_cost[0][2][0]; + if (is_one_pass_rt_lag) { + ref_costs_single[ALTREF2_FRAME] = base_cost; + ref_costs_single[BWDREF_FRAME] = base_cost; + ref_costs_single[LAST2_FRAME] = base_cost; + ref_costs_single[LAST3_FRAME] = base_cost; + ref_costs_single[LAST2_FRAME] += mode_costs->single_ref_cost[0][0][0]; + ref_costs_single[LAST3_FRAME] += mode_costs->single_ref_cost[0][0][0]; + ref_costs_single[BWDREF_FRAME] += mode_costs->single_ref_cost[0][0][1]; + ref_costs_single[BWDREF_FRAME] += mode_costs->single_ref_cost[0][1][0]; + ref_costs_single[ALTREF2_FRAME] += mode_costs->single_ref_cost[0][0][1]; + ref_costs_single[ALTREF2_FRAME] += mode_costs->single_ref_cost[0][2][0]; + } } } @@ -1665,15 +1684,6 @@ if (this_rdc.rdcost < best_rdc.rdcost) { best_rdc = this_rdc; best_mode = this_mode; - if (!this_rdc.skip_txfm) { - if (flat_blocks_screen && args.skippable && best_rdc.dist < 20000) { - memcpy(ctx->blk_skip, x->txfm_search_info.blk_skip, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); - } else { - memset(ctx->blk_skip, 0, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); - } - } } if (this_mode == DC_PRED) { if (flat_blocks_screen && args.skippable && this_rdc.dist > 0) @@ -1699,7 +1709,6 @@ try_palette &= prune; } if (try_palette) { - const TxfmSearchInfo *txfm_info = &x->txfm_search_info; const unsigned int intra_ref_frame_cost = 0; x->color_palette_thresh = (best_sad_norm < 500) ? 32 : 64; @@ -1714,10 +1723,6 @@ best_rdc.rate = this_rdc.rate; best_rdc.dist = this_rdc.dist; best_rdc.rdcost = this_rdc.rdcost; - if (!this_rdc.skip_txfm) { - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); - } if (xd->tx_type_map[0] != DCT_DCT) av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); } else { @@ -1735,8 +1740,6 @@ // do it here again in case the above logic changes. if (is_lossless_requested(&cpi->oxcf.rc_cfg)) { x->txfm_search_info.skip_txfm = 0; - memset(ctx->blk_skip, 0, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); } #if CONFIG_INTERNAL_STATS @@ -2232,14 +2235,12 @@ const int *use_ref_frame_mask, int comp_index, bool comp_use_zero_zeromv_only, MV_REFERENCE_FRAME *last_comp_ref_frame, BLOCK_SIZE bsize) { - const MV_REFERENCE_FRAME *rf = comp_ref_mode_set[comp_index].ref_frame; + const MV_REFERENCE_FRAME *rf = + is_one_pass_rt_lag_params(cpi) + ? comp_ref_mode_set_full[comp_index].ref_frame + : comp_ref_mode_set[comp_index].ref_frame; int skip_gf = 0; int skip_alt = 0; - *this_mode = comp_ref_mode_set[comp_index].pred_mode; - *ref_frame = rf[0]; - *ref_frame2 = rf[1]; - assert(*ref_frame == LAST_FRAME); - assert(*this_mode == GLOBAL_GLOBALMV || *this_mode == NEAREST_NEARESTMV); if (x->source_variance < 50 && bsize > BLOCK_16X16) { if (x->color_sensitivity_sb_g[COLOR_SENS_IDX(AOM_PLANE_U)] == 1 || x->color_sensitivity_sb_g[COLOR_SENS_IDX(AOM_PLANE_V)] == 1) @@ -2371,7 +2372,8 @@ // Estimate cost for single reference frames estimate_single_ref_frame_costs(cm, xd, mode_costs, segment_id, bsize, - search_state->ref_costs_single); + search_state->ref_costs_single, + is_one_pass_rt_lag_params(cpi)); // Reset flag to indicate modes evaluated av1_zero(search_state->mode_checked); @@ -2407,9 +2409,17 @@ &search_state->use_scaled_ref_frame[LAST_FRAME]); } // Update mask to use all reference frame - get_ref_frame_use_mask(cpi, x, mi, mi_row, mi_col, bsize, gf_temporal_ref, - search_state->use_ref_frame_mask, - force_skip_low_temp_var); + if (!is_one_pass_rt_lag_params(cpi)) { + get_ref_frame_use_mask(cpi, x, mi, mi_row, mi_col, bsize, gf_temporal_ref, + search_state->use_ref_frame_mask, + force_skip_low_temp_var); + } else { + for (MV_REFERENCE_FRAME ref_frame_iter = LAST_FRAME; + ref_frame_iter <= ALTREF_FRAME; ++ref_frame_iter) { + search_state->use_ref_frame_mask[ref_frame_iter] = + cpi->ref_frame_flags & (1 << (ref_frame_iter - 1)); + } + } skip_pred_mv = x->force_zeromv_skip_for_blk || (x->nonrd_prune_ref_frame_search > 2 && @@ -2450,6 +2460,15 @@ // for allowed compound modes, setup ref mv stack and reference frame. if (idx >= num_inter_modes) { const int comp_index = idx - num_inter_modes; + const COMP_REF_MODE *const comp_modes = is_one_pass_rt_lag_params(cpi) + ? comp_ref_mode_set_full + : comp_ref_mode_set; + const MV_REFERENCE_FRAME *rf = comp_modes[comp_index].ref_frame; + *this_mode = comp_modes[comp_index].pred_mode; + *ref_frame = rf[0]; + *ref_frame2 = rf[1]; + assert(*ref_frame == LAST_FRAME); + assert(*this_mode == GLOBAL_GLOBALMV || *this_mode == NEAREST_NEARESTMV); if (!setup_compound_params_from_comp_idx( cpi, x, search_state->yv12_mb, this_mode, ref_frame, ref_frame2, search_state->frame_mv, search_state->use_ref_frame_mask, @@ -2459,9 +2478,15 @@ } *is_single_pred = 0; } else { - *this_mode = ref_mode_set[idx].pred_mode; - *ref_frame = ref_mode_set[idx].ref_frame; *ref_frame2 = NONE_FRAME; + const REF_MODE *const single_modes = + is_one_pass_rt_lag_params(cpi) ? ref_mode_set_full : ref_mode_set; + *this_mode = single_modes[idx].pred_mode; + *ref_frame = single_modes[idx].ref_frame; + } + + if (is_one_pass_rt_lag_params(cpi) && cpi->rc.is_src_frame_alt_ref) { + if (*this_mode != GLOBALMV || *ref_frame != ALTREF_FRAME) return true; } if (cpi->sf.rt_sf.skip_newmv_mode_sad_screen && cpi->rc.high_source_sad && @@ -2473,6 +2498,26 @@ (*this_mode != GLOBALMV || *ref_frame != LAST_FRAME)) return true; + // Skip the mode if use reference frame mask flag is not set. + if (!search_state->use_ref_frame_mask[*ref_frame]) return true; + + *force_mv_inter_layer = 0; + if (cpi->ppi->use_svc && svc->spatial_layer_id > 0 && + ((*ref_frame == LAST_FRAME && svc->skip_mvsearch_last) || + (*ref_frame == GOLDEN_FRAME && svc->skip_mvsearch_gf) || + (*ref_frame == ALTREF_FRAME && svc->skip_mvsearch_altref))) { + // Only test mode if NEARESTMV/NEARMV is (svc_mv.mv.col, svc_mv.mv.row), + // otherwise set NEWMV to (svc_mv.mv.col, svc_mv.mv.row). + // Skip newmv and filter search. + *force_mv_inter_layer = 1; + if (*this_mode == NEWMV) { + search_state->frame_mv[*this_mode][*ref_frame] = svc_mv; + } else if (search_state->frame_mv[*this_mode][*ref_frame].as_int != + svc_mv.as_int) { + return true; + } + } + // If the segment reference frame feature is enabled then do nothing if the // current ref frame is not allowed. if (segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME)) { @@ -2481,9 +2526,6 @@ return false; } - // Skip the mode if use reference frame mask flag is not set. - if (!search_state->use_ref_frame_mask[*ref_frame]) return true; - // Don't skip non_last references if LAST is not used a reference. if (!(cpi->ref_frame_flags & AOM_LAST_FLAG) && (*ref_frame == GOLDEN_FRAME || *ref_frame == ALTREF_FRAME)) @@ -2540,6 +2582,7 @@ // Skip compound mode based on variance of previously evaluated single // reference modes. if (rt_sf->prune_compoundmode_with_singlemode_var && !*is_single_pred && + !is_one_pass_rt_lag_params(cpi) && prune_compoundmode_with_singlemode_var( *this_mode, *ref_frame, *ref_frame2, search_state->frame_mv, search_state->mode_checked, search_state->vars, @@ -2547,23 +2590,6 @@ return true; } - *force_mv_inter_layer = 0; - if (cpi->ppi->use_svc && svc->spatial_layer_id > 0 && - ((*ref_frame == LAST_FRAME && svc->skip_mvsearch_last) || - (*ref_frame == GOLDEN_FRAME && svc->skip_mvsearch_gf) || - (*ref_frame == ALTREF_FRAME && svc->skip_mvsearch_altref))) { - // Only test mode if NEARESTMV/NEARMV is (svc_mv.mv.col, svc_mv.mv.row), - // otherwise set NEWMV to (svc_mv.mv.col, svc_mv.mv.row). - // Skip newmv and filter search. - *force_mv_inter_layer = 1; - if (*this_mode == NEWMV) { - search_state->frame_mv[*this_mode][*ref_frame] = svc_mv; - } else if (search_state->frame_mv[*this_mode][*ref_frame].as_int != - svc_mv.as_int) { - return true; - } - } - // For screen content: skip mode testing based on source_sad. if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && !x->force_zeromv_skip_for_blk) { @@ -2596,6 +2622,8 @@ return true; } + if (is_one_pass_rt_lag_params(cpi)) return false; + // Skip mode based on block size, reference frame mode and other block // properties. if (skip_mode_by_bsize_and_ref_frame( @@ -2682,6 +2710,8 @@ RD_STATS nonskip_rdc; av1_invalid_rd_stats(&nonskip_rdc); + (void)ctx; + if (x->sb_me_block && this_mode == NEWMV && ref_frame == LAST_FRAME) { // Set the NEWMV_LAST to the sb MV. search_state->frame_mv[NEWMV][LAST_FRAME].as_int = x->sb_me_mv.as_int; @@ -3034,7 +3064,7 @@ if (search_state->this_rdc.rdcost < search_state->best_rdc.rdcost) { search_state->best_rdc = search_state->this_rdc; *best_early_term = this_early_term; - update_search_state_nonrd(search_state, mi, txfm_info, &nonskip_rdc, ctx, + update_search_state_nonrd(search_state, mi, txfm_info, &nonskip_rdc, this_best_mode, sse_y); // This is needed for the compound modes. @@ -3074,7 +3104,6 @@ struct macroblockd_plane *const pd = &xd->plane[0]; const int bw = block_size_wide[bsize]; const int bh = block_size_high[bsize]; - TxfmSearchInfo *txfm_info = &x->txfm_search_info; BEST_PICKMODE *const best_pickmode = &search_state->best_pickmode; // TODO(marpan): Only allow for 8 bit-depth for now, re-enable for 10/12 bit @@ -3129,10 +3158,6 @@ best_pickmode->tx_type = IDTX; search_state->best_rdc.rdcost = idx_rdcost; best_pickmode->best_mode_skip_txfm = idtx_rdc.skip_txfm; - if (!idtx_rdc.skip_txfm) { - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); - } xd->tx_type_map[0] = best_pickmode->tx_type; memset(ctx->tx_type_map, best_pickmode->tx_type, ctx->num_4x4_blk); memset(xd->tx_type_map, best_pickmode->tx_type, ctx->num_4x4_blk); @@ -3176,10 +3201,7 @@ if (x->color_sensitivity[COLOR_SENS_IDX(AOM_PLANE_U)] || x->color_sensitivity[COLOR_SENS_IDX(AOM_PLANE_V)]) search_state->this_rdc.skip_txfm = 0; - if (!search_state->this_rdc.skip_txfm) { - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); - } + if (xd->tx_type_map[0] != DCT_DCT) av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); } @@ -3268,7 +3290,8 @@ int best_early_term = 0; int force_skip_low_temp_var = 0; unsigned int sse_zeromv_norm = UINT_MAX; - const int num_inter_modes = NUM_INTER_MODES; + const int num_inter_modes = + is_one_pass_rt_lag_params(cpi) ? NUM_INTER_MODES_FULL : NUM_INTER_MODES; const REAL_TIME_SPEED_FEATURES *const rt_sf = &cpi->sf.rt_sf; bool check_globalmv = rt_sf->check_globalmv_on_single_ref; PRED_BUFFER tmp_buffer[4]; @@ -3299,7 +3322,9 @@ int_mv svc_mv = { .as_int = 0 }; int force_mv_inter_layer = 0; bool comp_use_zero_zeromv_only = 0; - int tot_num_comp_modes = NUM_COMP_INTER_MODES_RT; + int tot_num_comp_modes = is_one_pass_rt_lag_params(cpi) + ? NUM_COMP_INTER_MODES_RT_FULL + : NUM_COMP_INTER_MODES_RT; #if CONFIG_AV1_TEMPORAL_DENOISING const int denoise_recheck_zeromv = 1; AV1_PICKMODE_CTX_DEN ctx_den; @@ -3396,9 +3421,14 @@ tx_mode_to_biggest_tx_size[txfm_params->tx_mode_search_type]), TX_16X16); - fill_single_inter_mode_costs(search_state.single_inter_mode_costs, - num_inter_modes, ref_mode_set, mode_costs, - mbmi_ext->mode_context); + if (!is_one_pass_rt_lag_params(cpi)) + fill_single_inter_mode_costs(search_state.single_inter_mode_costs, + num_inter_modes, ref_mode_set, mode_costs, + mbmi_ext->mode_context); + else + fill_single_inter_mode_costs(search_state.single_inter_mode_costs, + num_inter_modes, ref_mode_set_full, mode_costs, + mbmi_ext->mode_context); MV_REFERENCE_FRAME last_comp_ref_frame = NONE_FRAME; @@ -3418,6 +3448,7 @@ !x->force_zeromv_skip_for_blk && x->content_state_sb.source_sad_nonrd != kZeroSad && x->source_variance == 0 && bsize < cm->seq_params->sb_size && + search_state.use_ref_frame_mask[LAST_FRAME] && search_state.yv12_mb[LAST_FRAME][0].width == cm->width && search_state.yv12_mb[LAST_FRAME][0].height == cm->height) { set_block_source_sad(cpi, x, bsize, &search_state.yv12_mb[LAST_FRAME][0]); @@ -3584,7 +3615,7 @@ search_state.ref_costs_single[INTRA_FRAME], reuse_inter_pred, &orig_dst, tmp_buffer, &this_mode_pred, &search_state.best_rdc, - best_pickmode, ctx, &best_intra_sad_norm); + best_pickmode, &best_intra_sad_norm); int skip_idtx_palette = (x->color_sensitivity[COLOR_SENS_IDX(AOM_PLANE_U)] || x->color_sensitivity[COLOR_SENS_IDX(AOM_PLANE_V)]) && @@ -3625,7 +3656,6 @@ // For lossless: always force the skip flags off. if (is_lossless_requested(&cpi->oxcf.rc_cfg)) { txfm_info->skip_txfm = 0; - memset(ctx->blk_skip, 0, sizeof(ctx->blk_skip[0]) * ctx->num_4x4_blk); } else { txfm_info->skip_txfm = best_pickmode->best_mode_skip_txfm; }
diff --git a/av1/encoder/palette.c b/av1/encoder/palette.c index 79a2fa5..14c8970 100644 --- a/av1/encoder/palette.c +++ b/av1/encoder/palette.c
@@ -232,9 +232,9 @@ int n, uint16_t *color_cache, int n_cache, bool do_header_rd_based_gating, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable, - int *beat_best_rd, PICK_MODE_CONTEXT *ctx, uint8_t *blk_skip, - uint8_t *tx_type_map, int *beat_best_palette_rd, - bool *do_header_rd_based_breakout, int discount_color_cost) { + int *beat_best_rd, PICK_MODE_CONTEXT *ctx, uint8_t *tx_type_map, + int *beat_best_palette_rd, bool *do_header_rd_based_breakout, + int discount_color_cost) { if (do_header_rd_based_breakout != NULL) *do_header_rd_based_breakout = false; optimize_palette_colors(color_cache, n_cache, n, 1, centroids, cpi->common.seq_params->bit_depth); @@ -314,8 +314,6 @@ memcpy(best_palette_color_map, color_map, block_width * block_height * sizeof(color_map[0])); *best_mbmi = *mbmi; - memcpy(blk_skip, x->txfm_search_info.blk_skip, - sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk); av1_copy_array(tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); if (rate) *rate = this_rate; if (rate_tokenonly) *rate_tokenonly = tokenonly_rd_stats.rate; @@ -342,7 +340,7 @@ int n_cache, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable, int *beat_best_rd, PICK_MODE_CONTEXT *ctx, - uint8_t *best_blk_skip, uint8_t *tx_type_map, int discount_color_cost) { + uint8_t *tx_type_map, int discount_color_cost) { int16_t centroids[PALETTE_MAX_SIZE]; int n = start_n; int top_color_winner = end_n; @@ -357,9 +355,9 @@ palette_rd_y(cpi, x, mbmi, bsize, dc_mode_cost, data, centroids, n, color_cache, n_cache, do_header_rd_based_gating, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, - distortion, skippable, beat_best_rd, ctx, best_blk_skip, - tx_type_map, &beat_best_palette_rd, - &do_header_rd_based_breakout, discount_color_cost); + distortion, skippable, beat_best_rd, ctx, tx_type_map, + &beat_best_palette_rd, &do_header_rd_based_breakout, + discount_color_cost); *last_n_searched = n; if (do_header_rd_based_breakout) { // Terminate palette_size search by setting last_n_searched to end_n. @@ -389,8 +387,8 @@ int n_cache, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable, int *beat_best_rd, PICK_MODE_CONTEXT *ctx, - uint8_t *best_blk_skip, uint8_t *tx_type_map, uint8_t *color_map, - int data_points, int discount_color_cost) { + uint8_t *tx_type_map, uint8_t *color_map, int data_points, + int discount_color_cost) { int16_t centroids[PALETTE_MAX_SIZE]; const int max_itr = 50; int n = start_n; @@ -410,9 +408,9 @@ palette_rd_y(cpi, x, mbmi, bsize, dc_mode_cost, data, centroids, n, color_cache, n_cache, do_header_rd_based_gating, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, - distortion, skippable, beat_best_rd, ctx, best_blk_skip, - tx_type_map, &beat_best_palette_rd, - &do_header_rd_based_breakout, discount_color_cost); + distortion, skippable, beat_best_rd, ctx, tx_type_map, + &beat_best_palette_rd, &do_header_rd_based_breakout, + discount_color_cost); *last_n_searched = n; if (do_header_rd_based_breakout) { // Terminate palette_size search by setting last_n_searched to end_n. @@ -543,8 +541,7 @@ const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize, int dc_mode_cost, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable, - int *beat_best_rd, PICK_MODE_CONTEXT *ctx, uint8_t *best_blk_skip, - uint8_t *tx_type_map) { + int *beat_best_rd, PICK_MODE_CONTEXT *ctx, uint8_t *tx_type_map) { MACROBLOCKD *const xd = &x->e_mbd; MB_MODE_INFO *const mbmi = xd->mi[0]; assert(!is_inter_block(mbmi)); @@ -662,7 +659,7 @@ cpi, x, mbmi, bsize, dc_mode_cost, data, top_colors, min_n, max_n + 1, step_size, do_header_rd_based_gating, &unused, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, - distortion, skippable, beat_best_rd, ctx, best_blk_skip, tx_type_map, + distortion, skippable, beat_best_rd, ctx, tx_type_map, discount_color_cost); // Evaluate neighbors for the winner color (if winner is found) in the // above coarse search for dominant colors @@ -676,8 +673,8 @@ stage2_max_n + 1, stage2_step_size, /*do_header_rd_based_gating=*/false, &unused, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, - distortion, skippable, beat_best_rd, ctx, best_blk_skip, - tx_type_map, discount_color_cost); + distortion, skippable, beat_best_rd, ctx, tx_type_map, + discount_color_cost); } // K-means clustering. // Perform k-means coarse palette search to find the winner candidate @@ -686,8 +683,7 @@ min_n, max_n + 1, step_size, do_header_rd_based_gating, &unused, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx, - best_blk_skip, tx_type_map, color_map, rows * cols, - discount_color_cost); + tx_type_map, color_map, rows * cols, discount_color_cost); // Evaluate neighbors for the winner color (if winner is found) in the // above coarse search for k-means if (k_means_winner <= max_n) { @@ -700,8 +696,8 @@ start_n_stage2, end_n_stage2 + 1, step_size_stage2, /*do_header_rd_based_gating=*/false, &unused, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, - distortion, skippable, beat_best_rd, ctx, best_blk_skip, - tx_type_map, color_map, rows * cols, discount_color_cost); + distortion, skippable, beat_best_rd, ctx, tx_type_map, color_map, + rows * cols, discount_color_cost); } } else { const int max_n = AOMMIN(colors, PALETTE_MAX_SIZE), @@ -712,7 +708,7 @@ cpi, x, mbmi, bsize, dc_mode_cost, data, top_colors, min_n, max_n + 1, 1, do_header_rd_based_gating, &last_n_searched, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, - distortion, skippable, beat_best_rd, ctx, best_blk_skip, tx_type_map, + distortion, skippable, beat_best_rd, ctx, tx_type_map, discount_color_cost); if (last_n_searched < max_n) { // Search in descending order until we get to the previous best @@ -721,7 +717,7 @@ last_n_searched, -1, /*do_header_rd_based_gating=*/false, &unused, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx, - best_blk_skip, tx_type_map, discount_color_cost); + tx_type_map, discount_color_cost); } // K-means clustering. if (colors == PALETTE_MIN_SIZE) { @@ -733,8 +729,7 @@ color_cache, n_cache, /*do_header_rd_based_gating=*/false, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx, - best_blk_skip, tx_type_map, NULL, NULL, - discount_color_cost); + tx_type_map, NULL, NULL, discount_color_cost); } else { // Perform k-means palette search in ascending order last_n_searched = min_n; @@ -743,8 +738,7 @@ min_n, max_n + 1, 1, do_header_rd_based_gating, &last_n_searched, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx, - best_blk_skip, tx_type_map, color_map, rows * cols, - discount_color_cost); + tx_type_map, color_map, rows * cols, discount_color_cost); if (last_n_searched < max_n) { // Search in descending order until we get to the previous best perform_k_means_palette_search( @@ -752,8 +746,8 @@ max_n, last_n_searched, -1, /*do_header_rd_based_gating=*/false, &unused, color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly, distortion, skippable, - beat_best_rd, ctx, best_blk_skip, tx_type_map, color_map, - rows * cols, discount_color_cost); + beat_best_rd, ctx, tx_type_map, color_map, rows * cols, + discount_color_cost); } } }
diff --git a/av1/encoder/palette.h b/av1/encoder/palette.h index ce10949..ac1112f 100644 --- a/av1/encoder/palette.h +++ b/av1/encoder/palette.h
@@ -174,7 +174,7 @@ int dc_mode_cost, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable, int *beat_best_rd, struct PICK_MODE_CONTEXT *ctx, - uint8_t *best_blk_skip, uint8_t *tx_type_map); + uint8_t *tx_type_map); /*!\brief Search for the best palette in the chroma plane. *
diff --git a/av1/encoder/partition_model_weights.h b/av1/encoder/partition_model_weights.h index 28d960d..576b981 100644 --- a/av1/encoder/partition_model_weights.h +++ b/av1/encoder/partition_model_weights.h
@@ -1317,6 +1317,842 @@ #define FEATURE_SIZE 18 #define LABEL_SIZE 4 +#define NEW_LABEL_SIZE 3 + +static const float av1_partition4_search_thresh[6][3][5] = { + // Aggressiveness = 0 + { + // lowres + { 1.0f, 0.7451170578367811f, 0.7001978100387174f, 0.7410514819747771f, + 1.0f }, + // midres + { 1.0f, 0.7494756052316782f, 0.6892602739627772f, 0.8573332311578094f, + 1.0f }, + // hdres + { 1.0f, 0.7494756052316782f, 0.6892602739627772f, 0.8573332311578094f, + 1.0f }, + }, + // Aggressiveness = 1 + { + // lowres + { 1.0f, 0.8885108052540076f, 0.6485475489414385f, 0.8310364661853259f, + 1.0f }, + // midres + { 1.0f, 0.6669407157796841f, 0.7813399048525052f, 0.8876257905832826f, + 1.0f }, + // hdres + { 1.0f, 0.6669407157796841f, 0.7813399048525052f, 0.8876257905832826f, + 1.0f }, + }, + // Aggressiveness = 2 + { + // lowres + { 1.0f, 0.8885108052540076f, 0.6485475489414385f, 0.8310364661853259f, + 1.0f }, + // midres + { 1.0f, 0.6669407157796841f, 0.7813399048525052f, 0.8876257905832826f, + 1.0f }, + // hdres + { 1.0f, 0.6669407157796841f, 0.7813399048525052f, 0.8876257905832826f, + 1.0f }, + }, + // Aggressiveness = 3 + { + // lowres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + // midres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + // hdres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + }, + // Aggressiveness = 4 + { + // lowres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + // midres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + // hdres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + }, + // Aggressiveness = 5 + { + // lowres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + // midres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + // hdres + { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, + }, +}; + +static const float av1_partition4_not_search_thresh[6][3][5] = { + // Aggressiveness = 0 + { + // lowres + { 0.0f, 0.001052842338862181f, 0.03450079872362423f, + 0.002759167859733889f, 0.0f }, + // midres + { 0.0f, 0.07053592384393781f, 0.03838345436035215f, 0.005675936031290841f, + 0.0f }, + // hdres + { 0.0f, 0.07053592384393781f, 0.03838345436035215f, 0.005675936031290841f, + 0.0f }, + }, + // Aggressiveness = 1 + { + // lowres + { 0.0f, 0.003841755695390817f, 0.04822550751756426f, + 0.008487399657253631f, 0.0f }, + // midres + { 0.0f, 0.0011047168521408242f, 0.04736033094945354f, + 0.01638628311248205f, 0.0f }, + // hdres + { 0.0f, 0.0011047168521408242f, 0.04736033094945354f, + 0.01638628311248205f, 0.0f }, + }, + // Aggressiveness = 2 + { + // lowres + { 0.0f, 0.003841755695390817f, 0.04822550751756426f, + 0.008487399657253631f, 0.0f }, + // midres + { 0.0f, 0.0011047168521408242f, 0.04736033094945354f, + 0.01638628311248205f, 0.0f }, + // hdres + { 0.0f, 0.0011047168521408242f, 0.04736033094945354f, + 0.01638628311248205f, 0.0f }, + + }, + // Aggressiveness = 3 + { + // lowres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // midres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // hdres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + }, + // Aggressiveness = 4 + { + // lowres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // midres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // hdres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + }, + // Aggressiveness = 5 + { + // lowres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // midres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // hdres + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + }, +}; + +static const float av1_partition4_nn_mean_64[FEATURE_SIZE] = { + 1.3752231712399923f, 7.8818296620238755f, 0.70302558915341073f, + 0.89354391968945868f, 0.69912435960273867f, 0.894260991860082f, + 0.3926311487269134f, 0.396667092758365f, 0.39248475739497596f, + 0.60199542223376412f, 0.79385711361043609f, 0.71296985320460649f, + 0.70907736145028422f, 0.79726457629807679f, 0.78323554106033988f, + 0.70020724711817484f, 0.703606514382454f, 0.812416459726476f, +}; + +static const float av1_partition4_nn_std_64[FEATURE_SIZE] = { + 1.5210330851172169f, 3.2502345451890569f, 0.31526449400836393f, + 0.28189232462592312f, 0.31774840567547408f, 0.28162114702432761f, + 0.32736466480428811f, 0.32670076454358127f, 0.327642806340881f, + 0.39886024415245425f, 0.58334698402992669f, 0.43936371898261278f, + 0.43195994078504846f, 0.6085133217391806f, 0.58153035818647314f, + 0.43649370363303375f, 0.43352660981855345f, 0.6044169876719907f +}; + +static const float av1_partition4_nn_mean_32[FEATURE_SIZE] = { + 1.2246859239905685f, 8.2993701373465054f, 0.766659262212363f, + 0.94652387284747963f, 0.7600040783826757f, 0.95272747538676006f, + 0.65159866132436739f, 0.65257588048947879f, 0.66423008846285458f, + 0.76469913589630689f, 0.82278748860287532f, 0.74684876377199483f, + 0.747579068836797f, 0.81934887236245424f, 0.83835878037641287f, + 0.75032880096189358f, 0.75488102829056769f, 0.84759980602684326f, +}; + +static const float av1_partition4_nn_std_32[FEATURE_SIZE] = { + 1.6075309663410215f, 2.5730348757929398f, 0.26321073080207291f, + 0.16943329319298692f, 0.2651485345364597f, 0.16189549806595446f, + 0.37708176589960463f, 0.37678428010229964f, 0.376236054947869f, + 0.35251481923629219f, 0.54812998882840647f, 0.40597153804110392f, + 0.4062410958984316f, 0.55966403917740415f, 0.551261263412241f, + 0.39617447478383311f, 0.3975763070782759f, 0.56159171003404f +}; + +static const float av1_partition4_nn_mean_16[FEATURE_SIZE] = { + 1.0045835791325202f, 8.4894105104733484f, 0.662368560626388f, + 0.907764748376923f, 0.6627159222349438f, 0.936766105471581f, + 0.45489736309556683f, 0.46132801974042803f, 0.46931301089726096f, + 0.85059414467649674f, 0.779699826074789f, 0.70609877051124081f, + 0.711834073256916f, 0.79712849358962179f, 0.8351636078095156f, + 0.75367264126747469f, 0.75873775601873783f, 0.8466653756621495f +}; + +static const float av1_partition4_nn_std_16[FEATURE_SIZE] = { + 1.6427502751165233f, 2.3099057132670047f, 0.24533039885709371f, + 0.19978306019327963f, 0.24478191346772621f, 0.17204070228378546f, + 0.33179173716308064f, 0.33283691607603066f, 0.33758170469103288f, + 0.3009885171419176f, 0.550933084988767f, 0.41342659059456571f, + 0.41384314531287553f, 0.56715063304567692f, 0.54874118341614453f, + 0.39768773547851133f, 0.397536461220648f, 0.55655009687449353f +}; + +static const float *const av1_partition4_nn_mean[5] = { + NULL, + av1_partition4_nn_mean_64, + av1_partition4_nn_mean_32, + av1_partition4_nn_mean_16, + NULL, +}; + +static const float *const av1_partition4_nn_std[5] = { + NULL, + av1_partition4_nn_std_64, + av1_partition4_nn_std_32, + av1_partition4_nn_std_16, + NULL, +}; + +static const float + av1_hd_4_partition_nn_weights_64_layer0[FEATURE_SIZE * 24] = { + 0.187540665268898f, 0.008867358788847923f, 1.3245491981506348f, + 0.18199755251407623f, -1.320246934890747f, -0.11215319484472275f, + -0.2073437124490738f, -1.6804399490356445f, 1.0172151327133179f, + -0.04451597109436989f, 0.3141838610172272f, -0.10051298141479492f, + 0.19940334558486938f, 0.3332000970840454f, -0.19904619455337524f, + 0.575351357460022f, -0.3605368435382843f, -0.2180677056312561f, + -0.08732779324054718f, -0.5763136148452759f, -0.4628996253013611f, + -0.19735047221183777f, -0.2669188976287842f, -0.18305927515029907f, + 1.9265873432159424f, 0.44848161935806274f, 0.48354148864746094f, + 0.883162796497345f, -0.2056751400232315f, 0.26105034351348877f, + 0.22955726087093353f, -0.07927738130092621f, 0.22645841538906097f, + -0.07682249695062637f, 0.46167850494384766f, 0.03855176270008087f, + 0.27271342277526855f, -0.032083675265312195f, 1.2821667194366455f, + 0.2666876018047333f, -0.09309065341949463f, -0.15320968627929688f, + -1.6731971502304077f, -1.3742161989212036f, 0.9385388493537903f, + -0.008384025655686855f, 0.24508674442768097f, 0.1386878341436386f, + 0.02423120103776455f, 0.19301238656044006f, 0.13091489672660828f, + -0.6223750710487366f, -0.051762085407972336f, 0.3243873119354248f, + -0.17509253323078156f, -0.30384597182273865f, 2.6790037155151367f, + 0.20672129094600677f, -0.2797843813896179f, 0.4765399992465973f, + -0.45524242520332336f, 0.39230310916900635f, -0.23308643698692322f, + -0.3281370997428894f, -0.33606868982315063f, -0.24563899636268616f, + -0.11075028777122498f, 0.26341673731803894f, -0.1780794858932495f, + -0.3037680685520172f, 0.6263296604156494f, -0.7237133383750916f, + -0.28761041164398193f, 0.09403169900178909f, 0.5554772019386292f, + 0.08419027924537659f, -0.20748449862003326f, 0.8960804343223572f, + 1.731931447982788f, 0.9879814386367798f, -0.7795932292938232f, + -0.08355121314525604f, -1.2876038551330566f, 0.2683749794960022f, + 0.046085603535175323f, 0.6003941297531128f, 0.39504218101501465f, + 0.42627424001693726f, 0.2481756955385208f, 0.2539105713367462f, + 0.3717970848083496f, -1.372495174407959f, 1.779883623123169f, + 0.0023461435921490192f, -0.4034990966320038f, -0.005759629420936108f, + -0.3803994655609131f, -0.3320770561695099f, 2.070441961288452f, + 0.3284848630428314f, 0.35943546891212463f, -0.7431299090385437f, + 0.327751100063324f, 0.42656365036964417f, 0.0988604873418808f, + 0.17223680019378662f, -0.14063750207424164f, 0.01995408907532692f, + -1.0309178829193115f, -0.18265926837921143f, -0.9445133805274963f, + 0.1541268527507782f, 0.7334791421890259f, 1.5515707731246948f, + -0.14698545634746552f, 0.49188539385795593f, -0.6401307582855225f, + -0.24233347177505493f, -0.24929864704608917f, 0.7331327795982361f, + -0.24525558948516846f, -0.6303561925888062f, 0.2914137542247772f, + -0.1501816362142563f, 0.33382299542427063f, 0.20683197677135468f, + 0.6331838965415955f, 0.21398495137691498f, -0.7407512068748474f, + -0.38856998085975647f, 1.4056344032287598f, 0.2766624987125397f, + -0.47957277297973633f, 0.4720071256160736f, -2.237626791000366f, + 0.17220094799995422f, 0.1279810518026352f, -0.170374795794487f, + 0.15627238154411316f, 0.06910572201013565f, 0.1091245785355568f, + 0.13457776606082916f, 0.0723930299282074f, 0.18878978490829468f, + -0.9244682788848877f, 0.49643731117248535f, 0.00938953272998333f, + 0.817326009273529f, -0.872970700263977f, 1.2162615060806274f, + 1.6745234727859497f, 0.7649728655815125f, 0.6812664270401001f, + 0.7555683851242065f, -0.3641526997089386f, -0.5201636552810669f, + 0.33287081122398376f, -0.015199027955532074f, 0.11200721561908722f, + 0.1262284219264984f, -0.30170801281929016f, 0.29431891441345215f, + -0.8517263531684875f, -0.011521201580762863f, -0.2496422827243805f, + 0.4014142155647278f, -1.337280035018921f, -0.4126336872577667f, + 1.4324754476547241f, 0.5764614343643188f, 0.9383725523948669f, + 0.15321384370326996f, 0.16989222168922424f, 0.3319786489009857f, + -0.07834333181381226f, 0.13870780169963837f, 0.06260085105895996f, + 0.1040545254945755f, -0.04136618599295616f, -0.1875564157962799f, + 0.12765266001224518f, 0.119786836206913f, 2.6260948181152344f, + -0.718943178653717f, 2.216468572616577f, 0.2265452891588211f, + 0.3913697898387909f, 1.3077619075775146f, 0.8483156561851501f, + 0.17309871315956116f, -0.032115768641233444f, 0.06523145735263824f, + -0.039812054485082626f, 0.20178377628326416f, 0.09728340059518814f, + 0.06543758511543274f, -0.018573174253106117f, 0.22792796790599823f, + -0.25049784779548645f, -0.26964524388313293f, 0.3470650613307953f, + 0.07785002142190933f, 0.9330359697341919f, 0.13356055319309235f, + 0.5518191456794739f, -0.03740643337368965f, -1.0142114162445068f, + -0.3127419948577881f, 0.6786038279533386f, -0.746333658695221f, + 0.7910341024398804f, -0.5631489753723145f, 0.13812024891376495f, + 0.5231190919876099f, -0.4686489403247833f, 0.09117632359266281f, + 0.7155937552452087f, -0.7463059425354004f, -0.4379642903804779f, + 0.48007652163505554f, 0.0414154939353466f, 0.08547665923833847f, + -0.08740316331386566f, -0.3762202262878418f, 1.650416374206543f, + 0.6305139660835266f, 0.2807236909866333f, 0.3252447545528412f, + -1.2813769578933716f, 0.1806870996952057f, -0.07484257966279984f, + -0.4510236084461212f, -0.32734254002571106f, -0.2525199055671692f, + -0.6823204755783081f, 0.4419010579586029f, -0.13107456266880035f, + 1.5115501880645752f, -1.0415934324264526f, 0.6362841725349426f, + 1.1276127099990845f, 1.6090517044067383f, 1.4753515720367432f, + -0.5725038647651672f, 0.4504983127117157f, 0.0542747899889946f, + 0.23436619341373444f, -0.09478037059307098f, 0.17619547247886658f, + 0.5189757943153381f, 0.3518279492855072f, -0.20417530834674835f, + -0.3984152376651764f, 1.4910639524459839f, 0.896291196346283f, + -0.07476390153169632f, -0.38988080620765686f, -0.36142808198928833f, + -0.5949490070343018f, -0.34817272424697876f, 2.390124559402466f, + 0.036864519119262695f, -0.29277393221855164f, 0.2582416832447052f, + 0.12344545125961304f, -0.19278477132320404f, -0.3593710958957672f, + 0.052842043340206146f, -0.4351940155029297f, 0.39930951595306396f, + -0.05878692492842674f, -0.13607178628444672f, -0.3738308250904083f, + 0.07306762039661407f, 0.14370614290237427f, 0.7443045377731323f, + -0.6571366786956787f, 1.4732838869094849f, 0.07397766411304474f, + -0.026601698249578476f, 0.16516001522541046f, -0.09079989790916443f, + 0.08782806247472763f, -0.02378779835999012f, 1.2652226686477661f, + 1.1744853258132935f, 1.0768793821334839f, 1.2959463596343994f, + 0.18875858187675476f, -0.16353435814380646f, 0.12929506599903107f, + -0.028879646211862564f, 1.7920417785644531f, 0.2096109390258789f, + 0.9538826942443848f, -1.3157790899276733f, 1.025888204574585f, + 0.14866796135902405f, 0.11838590353727341f, -0.1063050851225853f, + 0.10628897696733475f, -0.07982262223958969f, -0.26907843351364136f, + 0.010558247566223145f, 0.13393597304821014f, 0.22190389037132263f, + -0.19494540989398956f, 0.3906410038471222f, -0.9874037504196167f, + 0.651418149471283f, -0.8602163791656494f, 0.32449445128440857f, + 1.2791974544525146f, 1.0395246744155884f, 1.5486201047897339f, + -0.7350376844406128f, -0.6816073060035706f, 0.016160577535629272f, + -0.18916243314743042f, -0.26918724179267883f, 0.4815976321697235f, + 0.2128375917673111f, 0.32033786177635193f, 0.5065921545028687f, + -0.9307171106338501f, 0.007387777790427208f, -0.45773541927337646f, + -0.19515591859817505f, -0.28307247161865234f, 1.3757314682006836f, + 1.5909974575042725f, 1.4594842195510864f, 1.3418850898742676f, + 1.449257493019104f, 0.1290784776210785f, -0.02872903272509575f, + 0.0876379907131195f, 0.0945722684264183f, -0.11620496213436127f, + 0.03221723809838295f, 0.01832464151084423f, -0.0350685641169548f, + 0.2947345972061157f, -0.6470182538032532f, 0.7246863842010498f, + -0.04240961745381355f, -0.8078872561454773f, 0.2905123829841614f, + 0.9782440066337585f, -0.7729941010475159f, 0.9055317044258118f, + 0.037821024656295776f, 0.123793825507164f, 0.17636357247829437f, + 0.022636424750089645f, 0.31781089305877686f, 0.4737413227558136f, + -0.6823905110359192f, 1.179976463317871f, -0.6974776983261108f, + -0.7578544616699219f, 0.4514416754245758f, 0.8697171211242676f, + -0.45408639311790466f, 0.06114231050014496f, 0.4886225461959839f, + -0.902211606502533f, 0.2965964078903198f, 0.0068778120912611485f, + 0.7137743830680847f, 0.3116300702095032f, -0.2853015661239624f, + 0.9911389350891113f, -0.21889007091522217f, 0.2786492705345154f, + -0.1595863401889801f, -0.2824990749359131f, 0.6888416409492493f, + -0.8445804715156555f, -0.07846039533615112f, -0.9218422174453735f, + -0.36275193095207214f, -0.24915170669555664f, -0.022376133129000664f, + 0.9710543751716614f, 1.1314961910247803f, 0.8179596662521362f, + -0.12418682873249054f, 0.1507333666086197f, -0.22915267944335938f, + 0.0621408149600029f, -0.18360091745853424f, 0.045976653695106506f, + -0.039411406964063644f, 0.09902478009462357f, 0.02785186842083931f, + -0.11535168439149857f, -0.021012280136346817f, -0.8498482704162598f, + -0.08284465223550797f, 1.7017556428909302f, 0.09805655479431152f, + -0.819100558757782f, 1.2941229343414307f, -1.4690512418746948f, + -0.14031103253364563f, -0.11753756552934647f, -0.09136147797107697f, + -0.22433844208717346f, 0.553429126739502f, 0.03767363727092743f, + 0.013898416422307491f, 0.25408342480659485f, 0.047054700553417206f, + 0.38260170817375183f, -0.10799504816532135f, 0.7206213474273682f, + 0.40167760848999023f, 0.33209502696990967f, -0.05047299340367317f, + -1.4984959363937378f, -0.7021075487136841f, 0.33913472294807434f, + 0.1524118036031723f, -0.15081769227981567f, -0.27317649126052856f, + -0.15303443372249603f, -0.6619091033935547f, -0.1907443255186081f, + 0.0709405466914177f, 0.2743484377861023f, -0.12694808840751648f + }; + +static const float av1_hd_4_partition_nn_bias_64_layer0[24] = { + -0.017236731946468353f, 1.2984086275100708f, -0.5433139204978943f, + -0.1250726878643036f, 0.1385560780763626f, -0.715609073638916f, + 0.23101328313350677f, -0.5218021273612976f, 0.4044009745121002f, + 1.2596054077148438f, -1.4503605365753174f, -0.09927619248628616f, + -0.692095160484314f, 0.339007705450058f, -0.9172008633613586f, + -0.6032222509384155f, 1.5522794723510742f, -0.2724784016609192f, + 1.1838854551315308f, 0.1453675925731659f, -0.8429049253463745f, + 1.4830207824707031f, 0.4452385902404785f, -0.19120316207408905f +}; + +static const float + av1_hd_4_partition_nn_weights_64_layer1[24 * NEW_LABEL_SIZE] = { + -0.13316302001476288f, 0.3058302104473114f, 0.1070905551314354f, + 0.14361895620822906f, -0.4736453890800476f, -0.33969804644584656f, + 0.030571846291422844f, 0.33224594593048096f, -0.06829124689102173f, + 0.21595115959644318f, 0.8105601668357849f, -0.13124717772006989f, + -0.2749757170677185f, -0.7534167170524597f, 0.006968092638999224f, + 0.04543568938970566f, -0.22467342019081116f, -0.47277456521987915f, + 0.7455835938453674f, -0.17081353068351746f, -0.33045464754104614f, + 0.2652832865715027f, -0.12469493597745895f, 0.010430725291371346f, + -0.6428546905517578f, -0.06037088856101036f, -0.6064530611038208f, + 0.06202490255236626f, 0.0295811016112566f, 0.2661149799823761f, + 0.09020768105983734f, 0.2919042408466339f, -0.5376702547073364f, + -0.6015531420707703f, -1.8029735088348389f, 0.07257948815822601f, + 0.11942317336797714f, -0.28495341539382935f, -0.5178318619728088f, + 0.416162371635437f, -0.34815943241119385f, -0.2532649636268616f, + -0.8336657285690308f, -0.28184619545936584f, -0.07333157956600189f, + -0.0849723070859909f, -0.012794683687388897f, -0.300246000289917f, + 0.30970948934555054f, 0.06398778408765793f, 0.3188731074333191f, + -0.32415783405303955f, -0.23729059100151062f, -1.2901034355163574f, + -0.3617444634437561f, -0.3765851557254791f, -0.6880390644073486f, + -0.08437152206897736f, -3.1945059299468994f, -0.41093024611473083f, + -0.5344216227531433f, 0.11081822216510773f, -0.4143705666065216f, + -0.28478118777275085f, 0.37445685267448425f, 0.3433232307434082f, + -0.5481890439987183f, 0.31730446219444275f, -0.04274527728557587f, + -0.17332714796066284f, -0.6555780172348022f, -0.1539755016565323f + }; + +static const float av1_hd_4_partition_nn_bias_64_layer1[NEW_LABEL_SIZE] = { + 0.9356993436813354f, -0.9818968772888184f, -0.5307729244232178f +}; + +static const float + av1_hd_4_partition_nn_weights_32_layer0[FEATURE_SIZE * 32] = { + -1.0885568857192993f, 0.7276804447174072f, 0.3899334967136383f, + 1.786031723022461f, 0.047051746398210526f, 0.7338393330574036f, + -1.0688824653625488f, 0.7300519347190857f, 0.5556117296218872f, + 0.15455445647239685f, 0.23771341145038605f, 0.17510148882865906f, + 0.07676580548286438f, 0.33183372020721436f, 0.029779063537716866f, + -0.038887616246938705f, 0.12371493875980377f, -0.1936332881450653f, + 0.4791683554649353f, 0.01103244535624981f, 0.22002530097961426f, + 0.1311550885438919f, -0.25968194007873535f, 0.6977970004081726f, + 0.4742209017276764f, -1.7808669805526733f, 0.5283331871032715f, + 0.12193027883768082f, 0.24376845359802246f, -0.05244125798344612f, + -0.012799731455743313f, -0.11021115630865097f, 0.2635785937309265f, + 0.5267615914344788f, -0.6972594261169434f, 0.5042043924331665f, + 0.5771448612213135f, 0.16394701600074768f, 0.6534922122955322f, + 0.44265198707580566f, 0.001729517593048513f, -0.24405747652053833f, + -0.46474769711494446f, -1.2396752834320068f, 0.4316665828227997f, + -0.40889325737953186f, 0.21149589121341705f, 0.059366464614868164f, + -0.23183700442314148f, 0.26841700077056885f, -0.18424294888973236f, + 0.018943576142191887f, 0.1582796424627304f, -0.04466909542679787f, + 0.1756608933210373f, -0.14650224149227142f, 0.6032110452651978f, + 0.23037950694561005f, 0.05099782347679138f, -0.11585348099470139f, + -0.6360398530960083f, -0.504222571849823f, 1.2582234144210815f, + 0.639336109161377f, -0.08372239023447037f, 0.23684656620025635f, + -0.8035249710083008f, 0.6960340738296509f, -0.1946241408586502f, + 0.26888877153396606f, 0.0608806349337101f, -0.0793396532535553f, + -0.21731014549732208f, -0.44480085372924805f, 0.9669836759567261f, + -0.03727146238088608f, -1.048531413078308f, 0.09407489746809006f, + 0.7357499599456787f, -0.1052381843328476f, 0.7807036638259888f, + -1.2890628576278687f, -0.27905717492103577f, -0.2336570918560028f, + 0.24939477443695068f, -0.1286485493183136f, -0.017393384128808975f, + 0.2383008748292923f, -0.2378801703453064f, 0.046414244920015335f, + 0.04329893738031387f, 0.09692811220884323f, -0.23152312636375427f, + 0.26998770236968994f, 0.11675097048282623f, 0.048871126025915146f, + -0.5766774415969849f, 0.5075773000717163f, -0.16599853336811066f, + 0.07355321198701859f, 1.2679802179336548f, -0.470909059047699f, + -0.7353945970535278f, 0.2871803045272827f, 0.04421471804380417f, + 0.028569340705871582f, 0.07299203425645828f, 0.17615129053592682f, + -0.6759434938430786f, 0.3759593665599823f, -0.03345653414726257f, + 0.15250401198863983f, 1.6251002550125122f, -0.32556575536727905f, + -0.4355400800704956f, 0.18039146065711975f, -0.6343501210212708f, + -0.26397016644477844f, 0.1334858238697052f, 0.17045122385025024f, + 0.06101013347506523f, 0.15618449449539185f, -0.05479817092418671f, + 0.19636914134025574f, -0.171217679977417f, -0.2721777856349945f, + -0.03099956177175045f, -0.09452107548713684f, -0.2100360095500946f, + -0.08023128658533096f, 0.5380405783653259f, 0.04074520245194435f, + -0.45956486463546753f, -0.26170605421066284f, -0.036938007920980453f, + 0.3081861436367035f, 0.7264460921287537f, 0.4579879939556122f, + 0.5227369070053101f, 0.7139924764633179f, -1.3371893167495728f, + 0.8577211499214172f, -0.05075681209564209f, 0.2690950036048889f, + -0.3681940734386444f, 0.08500909060239792f, 0.8850646018981934f, + 0.4467816948890686f, 0.28028661012649536f, 0.27217888832092285f, + 0.7745168209075928f, 0.7731392979621887f, 1.2513463497161865f, + -0.35894691944122314f, 0.060745496302843094f, -0.04395193234086037f, + 0.11425885558128357f, -0.0328013151884079f, -0.1833721548318863f, + -0.07130905985832214f, 0.05300029367208481f, -0.10985758155584335f, + 0.006968754343688488f, 0.9198938012123108f, -0.17839254438877106f, + 0.26150068640708923f, 0.3576587736606598f, -0.23587310314178467f, + -0.0849660113453865f, 0.6160711050033569f, -0.18021133542060852f, + -0.30802857875823975f, 0.43131136894226074f, 0.1159980297088623f, + -0.25173503160476685f, 0.1161213293671608f, -0.1237974539399147f, + -0.7531319260597229f, -0.00878852792084217f, 0.2545885741710663f, + 0.007603700738400221f, -0.654176652431488f, 0.44965240359306335f, + 0.7931277751922607f, 0.18094922602176666f, -0.17669828236103058f, + 0.41031819581985474f, 0.12209723144769669f, -0.2814178168773651f, + -0.5220250487327576f, 0.270843505859375f, -1.7025750875473022f, + 0.019062979146838188f, 0.03137283772230148f, -0.17925892770290375f, + -0.42655882239341736f, -0.6994401216506958f, -0.07981792837381363f, + -0.1174769252538681f, -0.17482306063175201f, 0.07917492091655731f, + 0.5423614382743835f, -0.19157607853412628f, -0.05202263593673706f, + 0.04898262768983841f, 0.9668978452682495f, -0.27084100246429443f, + 0.1934838443994522f, 0.20127049088478088f, 0.07396796345710754f, + -0.06063545122742653f, -0.04891745001077652f, -0.5657607316970825f, + 0.5004168152809143f, -0.8361988663673401f, -0.1273525059223175f, + 0.8776155710220337f, -0.10246502608060837f, 0.46023881435394287f, + 0.07886356115341187f, 0.9126405119895935f, 0.17006607353687286f, + -0.513926088809967f, 0.1184217631816864f, 0.4032308757305145f, + -0.6753421425819397f, -0.043491754680871964f, 0.028111552819609642f, + -0.03926699236035347f, -0.1501464545726776f, -0.0014241111930459738f, + -0.004508028272539377f, 0.1367940455675125f, -0.1648765653371811f, + -0.3467996418476105f, 0.8369541168212891f, 0.8667364120483398f, + 0.6326888799667358f, 0.0986432433128357f, 0.20588631927967072f, + 0.03708481788635254f, 0.7360354065895081f, -0.43546396493911743f, + 0.2251778244972229f, 0.5427256226539612f, -0.08095406740903854f, + 0.9812734723091125f, 0.6085113286972046f, 0.853574812412262f, + 0.6173090934753418f, 0.6338220238685608f, 0.5749073624610901f, + 0.16422225534915924f, -0.024263106286525726f, -0.26709797978401184f, + -0.27645328640937805f, -0.18558774888515472f, 0.19723981618881226f, + 0.3469878137111664f, -0.853264331817627f, 0.1911182701587677f, + -0.15484920144081116f, 0.1289137303829193f, 0.16144156455993652f, + 0.28407949209213257f, 0.06683658063411713f, 0.10071670264005661f, + -0.13541074097156525f, 0.8519083857536316f, -1.3628060817718506f, + 0.044920407235622406f, -0.9304912090301514f, -0.0543082021176815f, + -0.5795891880989075f, 0.7444899678230286f, 0.34974098205566406f, + -0.5224471688270569f, 0.7189369797706604f, 0.002385274972766638f, + 0.28222599625587463f, -0.48682257533073425f, -0.24234719574451447f, + -0.04255838692188263f, 0.12397146970033646f, 0.618983805179596f, + 0.20875586569309235f, 0.1820639818906784f, 0.6538670659065247f, + -0.42951807379722595f, -0.9420881271362305f, 0.7871586680412292f, + 0.3056884706020355f, -0.22634023427963257f, -0.4248776435852051f, + 0.7732703685760498f, -0.559977650642395f, 0.08991166949272156f, + -0.5344432592391968f, 0.4384763538837433f, 0.0689520537853241f, + 0.054295241832733154f, 0.4064214527606964f, 0.33730319142341614f, + -0.5907483696937561f, -0.25099942088127136f, 0.4158354103565216f, + -0.49657127261161804f, -0.05017169192433357f, -0.19485296308994293f, + 0.6315739750862122f, 0.026322035118937492f, -0.022450678050518036f, + 0.7323209643363953f, 0.04370767995715141f, -0.24496810138225555f, + 0.11060530692338943f, -0.17143982648849487f, 0.9523182511329651f, + -0.6612598896026611f, -0.37088659405708313f, -0.06733931601047516f, + 0.10264944285154343f, -0.5029839277267456f, 0.05873759835958481f, + -0.9268806576728821f, 0.08673790097236633f, -0.05127667635679245f, + -0.12422613799571991f, 1.112686276435852f, 0.8936125636100769f, + -0.13199946284294128f, -0.7763554453849792f, -0.43546125292778015f, + 0.2081739604473114f, -0.02353234775364399f, 0.12321862578392029f, + 0.21226866543293f, -0.13369852304458618f, -0.07170510292053223f, + 0.022296279668807983f, -0.056023065000772476f, 0.07766319811344147f, + 0.538470983505249f, -0.09681082516908646f, 0.04744873568415642f, + 0.15997207164764404f, 0.1942814141511917f, -0.606421709060669f, + -1.6964457035064697f, 1.8364115953445435f, -1.2303802967071533f, + 0.3570907413959503f, 0.010215475223958492f, -0.08084885030984879f, + 0.07036393880844116f, 0.08225959539413452f, 0.1582154929637909f, + 0.051107257604599f, 0.007791138254106045f, -0.07944256067276001f, + 0.1391010731458664f, -0.13120266795158386f, -0.22520364820957184f, + 0.5478659868240356f, 0.2474595457315445f, 0.5424359440803528f, + 0.2528058588504791f, -0.3962598741054535f, 0.3255935311317444f, + 0.03695685788989067f, -0.07569821178913116f, 0.11970824003219604f, + -0.09008245170116425f, 0.23039402067661285f, 0.22283776104450226f, + -1.016753911972046f, -0.3937026262283325f, 0.5536431670188904f, + -0.0240758266299963f, 0.4818081855773926f, 0.05481531843543053f, + 0.2614268660545349f, -0.0978444516658783f, 0.5413931608200073f, + -0.12983573973178864f, 0.11426673084497452f, -0.8978381156921387f, + 2.279249429702759f, 0.2615402340888977f, 0.21105653047561646f, + 0.5502492785453796f, 0.21621979773044586f, 0.084201879799366f, + 0.3490462899208069f, -0.026039229705929756f, -0.10916426032781601f, + -0.07801277190446854f, 0.6263523101806641f, 0.6516022086143494f, + 0.4567950665950775f, -0.1453220695257187f, 0.2806280255317688f, + 0.1846635937690735f, -0.39070093631744385f, 0.2953556478023529f, + -0.6223417520523071f, 0.7452172040939331f, -0.43376561999320984f, + 0.08578093349933624f, 0.047524344176054f, -0.1837213784456253f, + -0.1024935394525528f, 0.11444173008203506f, -0.6773277521133423f, + 0.27492958307266235f, -0.09823667258024216f, -0.22346174716949463f, + 0.0019273801008239388f, -2.3131091594696045f, 0.21303993463516235f, + 0.24907292425632477f, -0.2713790833950043f, -0.010520088486373425f, + 0.008633764460682869f, 0.09717638790607452f, -0.06675714254379272f, + -0.15131525695323944f, -0.06854774802923203f, 0.18723946809768677f, + 0.18391983211040497f, -0.03460600599646568f, 0.04807600751519203f, + 0.3075238764286041f, -0.5352725386619568f, -0.49505266547203064f, + 0.13530148565769196f, 0.6102568507194519f, 0.07696415483951569f, + 0.7194110155105591f, 0.06245575100183487f, -0.5269941091537476f, + 0.6319996118545532f, 0.19854962825775146f, 0.16299931704998016f, + -0.36398473381996155f, -0.29766127467155457f, -0.8556507229804993f, + 0.19275438785552979f, 0.10368930548429489f, -0.18639041483402252f, + -0.13361744582653046f, 0.20305071771144867f, -0.2384209781885147f, + -0.05955479294061661f, -0.1649128794670105f, 0.406151682138443f, + 0.6018882393836975f, 0.7667891383171082f, -1.7823314666748047f, + -0.6050052642822266f, -0.33709582686424255f, -0.07998203486204147f, + -0.48412758111953735f, -0.05572658032178879f, 0.1572490930557251f, + 0.2134942263364792f, 0.14582860469818115f, 0.18256354331970215f, + -0.377498060464859f, -0.9428679347038269f, 0.7819222211837769f, + 0.40199485421180725f, 0.37610843777656555f, 0.09845725446939468f, + -0.2473520040512085f, 1.216661810874939f, 0.30756333470344543f, + -0.6023632287979126f, -0.43360501527786255f, -0.13598665595054626f, + -0.2213943898677826f, -0.05226043984293938f, -0.4922758936882019f, + -0.0975913479924202f, -0.21766597032546997f, -0.3971554636955261f, + 0.7108594179153442f, -0.7833206057548523f, -0.1663222461938858f, + -0.10129307955503464f, 0.694123387336731f, -0.18871963024139404f, + -0.20957666635513306f, -0.6052191853523254f, -0.4168408513069153f, + 0.5734631419181824f, -0.16406677663326263f, -0.025442425161600113f, + 0.2279360443353653f, -0.39298707246780396f, 0.6339749097824097f, + 0.811789870262146f, 0.5733523964881897f, 0.7220039367675781f, + -0.2420847862958908f, -0.036595702171325684f, 0.16434960067272186f, + 0.1654539257287979f, -0.05197849124670029f, 1.6182829141616821f, + 0.10106345266103745f, -0.4488222897052765f, 1.9005721807479858f, + 1.1414402723312378f, -0.0009835668606683612f, -0.053397852927446365f, + 0.053350090980529785f, -0.01116836816072464f, -0.20531748235225677f, + 0.031078532338142395f, -0.12687280774116516f, 0.04337241128087044f, + -0.19423863291740417f, -0.1944684386253357f, -1.0170292854309082f, + 0.253818154335022f, 0.2104361206293106f, 0.303714781999588f, + 0.24978181719779968f, -0.13854654133319855f, 0.43723681569099426f, + -0.35904571413993835f, 0.3720517158508301f, 0.37751492857933044f, + -0.22321447730064392f, 0.2643660604953766f, -1.2104402780532837f, + -0.1291334182024002f, -0.799103856086731f, -0.6578419804573059f, + 0.1304577738046646f, -0.310467928647995f, -0.5715723633766174f, + -0.2751500606536865f, -1.4782752990722656f, 0.056794919073581696f, + -0.42365702986717224f, 0.29207560420036316f, -0.7994689345359802f, + 0.6121898293495178f, -0.13781405985355377f, -0.17137859761714935f, + -0.06067662686109543f, -0.2110602855682373f, -0.14195752143859863f, + -0.033988095819950104f, -0.290365606546402f, -0.11846823245286942f, + -0.5680235624313354f, -0.11251579970121384f, 0.11803086847066879f, + 0.17645932734012604f, 0.173707515001297f, -0.08128058165311813f, + 1.9146397113800049f, -2.025925636291504f, -0.2421492487192154f, + 0.1311802864074707f, 0.007723645307123661f, 0.03510530665516853f, + 0.01055131945759058f, -0.001576860318891704f, 0.12198811024427414f, + 0.051576755940914154f, 0.29626473784446716f, -0.21319039165973663f + }; + +static const float av1_hd_4_partition_nn_bias_32_layer0[32] = { + 0.2225671112537384f, -1.488280177116394f, -1.25272536277771f, + 0.30195170640945435f, -1.5750389099121094f, -0.15505032241344452f, + 0.4310544729232788f, -0.7423095703125f, 0.3700641989707947f, + -0.6942943930625916f, -1.8595184087753296f, 0.9343048930168152f, + -0.2134159654378891f, -1.0248521566390991f, -0.3916787803173065f, + -0.13198702037334442f, -0.004904405679553747f, -0.02703840285539627f, + 0.6067211031913757f, -0.783298909664154f, -0.43120503425598145f, + -0.5023682117462158f, -0.6118841171264648f, -1.6906251907348633f, + -0.22733944654464722f, -0.1254553198814392f, -1.4340238571166992f, + -0.22106066346168518f, -1.1736090183258057f, -0.19184525310993195f, + 1.6934525966644287f, 0.06706780195236206f +}; + +static const float + av1_hd_4_partition_nn_weights_32_layer1[32 * NEW_LABEL_SIZE] = { + 0.16060389578342438f, -0.21775491535663605f, 0.19317272305488586f, + 0.16914533078670502f, -0.45330461859703064f, 0.2787623107433319f, + 0.31299149990081787f, 0.05981941148638725f, 0.26470449566841125f, + 0.09401649236679077f, 0.009454675950109959f, 0.07412216812372208f, + 0.1894051879644394f, -0.005571697372943163f, -0.29581448435783386f, + 0.1277410089969635f, -0.2395242303609848f, -0.07200955599546432f, + -0.06016874685883522f, -0.052163951098918915f, -0.17527195811271667f, + -0.35842975974082947f, -0.2974209785461426f, -0.28416672348976135f, + -0.13073766231536865f, 0.06440825760364532f, 0.11089038848876953f, + 0.04264971986413002f, -0.047677211463451385f, -0.15897323191165924f, + 0.20627695322036743f, 0.14130337536334991f, -0.34490400552749634f, + -0.5835226774215698f, -0.2576460540294647f, 0.45405450463294983f, + -0.06914465129375458f, 0.47381001710891724f, 0.2275693118572235f, + -0.18174265325069427f, -0.32693126797676086f, -0.18394632637500763f, + 0.32419729232788086f, -0.312508225440979f, -0.03289115056395531f, + -0.0015484002651646733f, -0.411852091550827f, -0.025346053764224052f, + -0.5360790491104126f, 0.0021535682026296854f, -0.1873963326215744f, + -0.6050364375114441f, -0.32903650403022766f, 0.054302141070365906f, + -0.2966037094593048f, 0.09255900233983994f, -0.3126637041568756f, + 0.3107044994831085f, -0.41937246918678284f, 0.42857909202575684f, + 0.413314551115036f, 0.08698277920484543f, -0.2702505588531494f, + -0.3499393165111542f, 0.1393149048089981f, -0.07625073194503784f, + 0.22380144894123077f, 0.07417313754558563f, -0.030424892902374268f, + 0.010089819319546223f, -0.18347591161727905f, 0.2737013101577759f, + -0.002240510890260339f, 0.08045932650566101f, -0.3990366756916046f, + 0.28912147879600525f, -0.25353744626045227f, -0.398074209690094f, + -0.04493967071175575f, -0.26016902923583984f, -0.0123167484998703f, + -0.37009286880493164f, -0.5511913895606995f, 0.4690968990325928f, + 0.04034169763326645f, -0.04233121499419212f, -0.006188324186950922f, + 0.662132740020752f, -0.19957122206687927f, -0.2447078973054886f, + -0.37380027770996094f, -0.019658207893371582f, -0.8110345005989075f, + 0.04405290633440018f, -0.6720242500305176f, 0.28009140491485596f + }; + +static const float av1_hd_4_partition_nn_bias_32_layer1[NEW_LABEL_SIZE] = { + 0.2621873617172241f, -0.3843003511428833f, 0.061671461910009384f +}; + +static const float + av1_hd_4_partition_nn_weights_16_layer0[FEATURE_SIZE * 24] = { + 0.016253290697932243f, -1.297595500946045f, -0.27500706911087036f, + -0.015488669276237488f, 0.13751500844955444f, 0.35479483008384705f, + 0.14572829008102417f, 0.9681192636489868f, 0.1723741590976715f, + 0.6096186637878418f, -0.27040380239486694f, 0.10235186666250229f, + -1.328527569770813f, -0.21975912153720856f, -0.646583616733551f, + -0.055015575140714645f, -0.2533200681209564f, -0.3775864541530609f, + 0.14515726268291473f, 0.7094276547431946f, 0.1637670248746872f, + -0.44492098689079285f, 0.45630398392677307f, -0.09169628471136093f, + -1.2921737432479858f, -0.7724092602729797f, -0.3599521517753601f, + -0.5058088302612305f, 0.05034554377198219f, -0.13767042756080627f, + -0.063873291015625f, -0.22768817842006683f, 0.4867677688598633f, + 0.30664312839508057f, 0.197816863656044f, 0.7504525184631348f, + -1.1621628999710083f, 1.2124617099761963f, 0.25230467319488525f, + -0.05503295734524727f, -1.4942923784255981f, -0.32912740111351013f, + 0.3235040307044983f, -0.2510537803173065f, 0.771889328956604f, + -0.3342115581035614f, 0.5448428988456726f, 0.33309075236320496f, + 0.323270320892334f, 0.7348728775978088f, 0.08185584098100662f, + 0.2510865032672882f, 0.3047282099723816f, -0.1991347074508667f, + -0.12788036465644836f, 0.2775464951992035f, 0.8931537866592407f, + 0.056438326835632324f, -0.318469375371933f, 0.09217683225870132f, + 0.5232695937156677f, -1.103989601135254f, 1.3018217086791992f, + -0.009919252246618271f, -0.2791503667831421f, 0.061792079359292984f, + 0.07982557266950607f, -0.0922408252954483f, 0.6757814884185791f, + -0.7555142641067505f, -0.5889946222305298f, 0.6173131465911865f, + -0.8960505723953247f, -0.5027951002120972f, 0.13630244135856628f, + 0.6446100473403931f, -0.4130464196205139f, -0.4017779231071472f, + 0.20977409183979034f, -0.4052879810333252f, 1.3482177257537842f, + 0.14319704473018646f, 0.10465224087238312f, 0.7072924971580505f, + -0.10870729386806488f, -0.519813597202301f, -0.044540468603372574f, + 0.033170267939567566f, 0.09132528305053711f, -0.06453194469213486f, + -0.21186839044094086f, -0.05110237002372742f, -0.20925769209861755f, + -0.21288996934890747f, 1.6771467924118042f, 0.5502645373344421f, + -1.7048338651657104f, 0.16985715925693512f, -1.4219897985458374f, + -0.15837456285953522f, -0.2376108467578888f, -0.0772041529417038f, + -0.10152464359998703f, -0.12048980593681335f, -0.04174394533038139f, + 0.03342839702963829f, -0.11870015412569046f, 0.045642685145139694f, + -0.17824901640415192f, -0.39495736360549927f, 0.023219818249344826f, + 0.06599095463752747f, -1.3451234102249146f, -0.36058682203292847f, + -0.6323454976081848f, 0.6011573672294617f, 0.5714403986930847f, + -0.12171386182308197f, -0.2662772238254547f, 0.11267754435539246f, + 0.05486325919628143f, -0.005583520047366619f, -0.4404391944408417f, + 0.9215461611747742f, -0.7242451906204224f, 0.35334518551826477f, + 0.10570390522480011f, -0.25813040137290955f, -0.14261184632778168f, + 0.23588822782039642f, 0.8678246140480042f, 0.18859107792377472f, + -1.3121229410171509f, -0.3789387047290802f, -0.12254016101360321f, + 0.32620975375175476f, -0.020516367629170418f, -0.02394155040383339f, + -0.04741834104061127f, 0.6299850940704346f, 0.4153035879135132f, + 0.24157185852527618f, 0.1356470286846161f, 0.23200926184654236f, + 0.1951109915971756f, 0.18018396198749542f, 1.8084784746170044f, + 0.2517470717430115f, -0.73732590675354f, -0.3096546232700348f, + -1.2763310670852661f, -1.914092779159546f, 1.0422425270080566f, + -0.10959721356630325f, 0.004097330383956432f, 0.029040994122624397f, + 0.08122090995311737f, 0.12759143114089966f, -0.10597894340753555f, + -0.08978335559368134f, 0.06638552993535995f, -0.1336660236120224f, + -0.006535878870636225f, -0.41592198610305786f, 0.5584124326705933f, + 0.2367665022611618f, 0.196196049451828f, -0.07954944670200348f, + 0.5095464587211609f, -0.09126847237348557f, -0.21107706427574158f, + 0.029520299285650253f, -1.2078922986984253f, 0.3940129578113556f, + 0.7266209721565247f, -1.5306856632232666f, -0.1339416354894638f, + 0.1284932792186737f, -0.21777178347110748f, 0.07416583597660065f, + -0.09128864854574203f, 0.054931897670030594f, 2.331453323364258f, + 0.3878869414329529f, 3.0721747875213623f, -0.20134149491786957f, + 0.4023342430591583f, 1.0996533632278442f, 0.804786741733551f, + 0.05523429065942764f, 0.3615642488002777f, 0.28358790278434753f, + 1.0130475759506226f, 0.6530840992927551f, 0.5036265850067139f, + 0.07072196900844574f, 1.1965914964675903f, 0.15188759565353394f, + -0.4931938946247101f, -0.5595028400421143f, 0.4211946129798889f, + 0.4326711595058441f, 1.68171226978302f, -0.5289874076843262f, + 0.5924357771873474f, -1.0259782075881958f, 0.8006095886230469f, + -0.011948885396122932f, -0.06665080040693283f, 0.017298908904194832f, + 0.19788117706775665f, 0.030706975609064102f, 0.4442530870437622f, + -0.045532263815402985f, 0.9565532207489014f, -0.13111355900764465f, + -0.17530879378318787f, -1.7634141445159912f, 0.4697858691215515f, + 0.1104392409324646f, -0.2023022472858429f, -0.22376781702041626f, + 0.8732469081878662f, 0.8716534376144409f, 0.5823152661323547f, + -0.18083855509757996f, -1.308184266090393f, -0.654875636100769f, + 0.02435542456805706f, -0.0694618821144104f, -0.8239381909370422f, + -0.3262384235858917f, -0.4603644609451294f, -0.34901463985443115f, + -0.45511960983276367f, 0.09622194617986679f, -0.9455815553665161f, + -0.1763199418783188f, -0.9315241575241089f, -0.15244387090206146f, + 1.9309401512145996f, 1.658923625946045f, 0.6284119486808777f, + 1.4863715171813965f, -0.10375337302684784f, -0.10615994036197662f, + 0.06735323369503021f, -0.21195504069328308f, 0.09150348603725433f, + 0.03171848878264427f, 0.08611147850751877f, -0.186605304479599f, + 0.1885748654603958f, -0.03468483313918114f, 0.45047804713249207f, + 0.28302377462387085f, -0.06926856935024261f, -0.3081843852996826f, + 1.321082353591919f, 1.173419713973999f, -0.6945704221725464f, + -0.0016985656693577766f, 0.491913765668869f, 0.44896093010902405f, + 0.22076819837093353f, -0.8053346276283264f, 0.469636470079422f, + 0.3976723849773407f, 0.3846956193447113f, 0.3820555508136749f, + -0.5353395342826843f, -1.3723238706588745f, -0.6204023957252502f, + 0.013284329324960709f, 1.4644020795822144f, 1.1471366882324219f, + 0.5871153473854065f, 1.382630705833435f, -0.19867826998233795f, + -0.36002323031425476f, -0.5623172521591187f, -0.6787152886390686f, + -0.46966180205345154f, -0.06944172829389572f, -0.37814465165138245f, + -0.2501605749130249f, 0.23046647012233734f, -0.7194182872772217f, + 0.03376816213130951f, -0.5843089818954468f, -0.9881840944290161f, + 0.3359870910644531f, 0.4855765104293823f, -0.014989004470407963f, + -0.506977915763855f, -0.6273276805877686f, 0.6636883020401001f, + 0.3604613244533539f, -0.5910298824310303f, 0.7819033861160278f, + -0.279474139213562f, 0.42845603823661804f, 0.04737255349755287f, + 0.4609934985637665f, 0.295590877532959f, 0.1710357666015625f, + -0.5633265376091003f, -0.3976214528083801f, -0.3271988034248352f, + 1.0001078844070435f, -0.8906325101852417f, 1.7183932065963745f, + 0.657141923904419f, -0.45635029673576355f, 1.7355040311813354f, + -1.08721923828125f, -0.055488500744104385f, 0.060083694756031036f, + 0.04308626428246498f, 0.16541598737239838f, -0.004631219431757927f, + -0.05671098455786705f, -0.04844706505537033f, -0.04004818573594093f, + 0.5423532724380493f, -0.22633561491966248f, -0.27722683548927307f, + -0.14303728938102722f, 0.09665144979953766f, 0.10049419850111008f, + 0.0016488885739818215f, -0.9644765853881836f, -0.37397530674934387f, + -0.16206716001033783f, 0.46272340416908264f, 0.5197669267654419f, + 0.22682012617588043f, -0.14623111486434937f, 0.07362930476665497f, + -0.38951820135116577f, -0.31893840432167053f, -0.06790059059858322f, + -0.9926403760910034f, -0.02509547397494316f, -0.2454613894224167f, + 1.5675048828125f, -0.9967404007911682f, -0.4881826937198639f, + 2.075866937637329f, 0.9609254002571106f, 0.9962529540061951f, + 0.28117066621780396f, -0.049589112401008606f, -0.22676287591457367f, + 0.02461501583456993f, -0.015650441870093346f, 0.062187422066926956f, + 0.03559637814760208f, -0.022159814834594727f, 0.12390958517789841f, + -0.3556448221206665f, -0.509732186794281f, 0.23648309707641602f, + -0.02601865492761135f, -1.9125454425811768f, 0.2455364614725113f, + -0.2373759150505066f, 0.5479041337966919f, -0.09244018793106079f, + 0.4884181618690491f, -0.24114874005317688f, -0.14978568255901337f, + -0.17928700149059296f, -0.16638562083244324f, 0.20396184921264648f, + 0.35705462098121643f, 0.027246948331594467f, -0.03450827673077583f, + -1.0188283920288086f, -0.10113411396741867f, -0.9864289164543152f, + -0.10024290531873703f, -0.1372683346271515f, 1.2718243598937988f, + 1.9245606660842896f, 1.5185524225234985f, 0.8519737720489502f, + 1.2267814874649048f, -0.04248328134417534f, 0.03472750261425972f, + -0.010050246492028236f, 0.011352861300110817f, 0.0025208336301147938f, + -0.08251981437206268f, 0.013549050316214561f, 0.03250717744231224f, + -0.08791140466928482f, -0.6399267315864563f, 0.6154654026031494f, + 0.17678213119506836f, -0.25917088985443115f, 0.4593951106071472f, + 0.9585320353507996f, 0.6765605211257935f, 0.18305635452270508f, + -0.31955450773239136f, 0.18701691925525665f, -1.0847679376602173f, + -1.4102541208267212f, 0.5045204162597656f, 0.15169933438301086f, + 0.24514462053775787f, 0.3000636696815491f, 0.032767392694950104f, + 0.28788086771965027f, 0.17492298781871796f, 1.1169567108154297f, + 0.1887919008731842f, 1.3159228563308716f, 0.5523889064788818f, + -0.3944258689880371f, -1.4844435453414917f, 1.5090211629867554f, + 0.3398161232471466f, -0.006244915537536144f, 0.2332850992679596f, + -0.04527553915977478f, -0.004237487446516752f, -0.3812176585197449f, + 0.3342042565345764f, 0.09058798104524612f, 0.06223088502883911f + }; + +static const float av1_hd_4_partition_nn_bias_16_layer0[24] = { + 0.17428483068943024f, -0.18228434026241302f, -0.22544708847999573f, + 0.9856225252151489f, 0.7987083196640015f, -0.2589545249938965f, + -0.34056714177131653f, 0.518294632434845f, -0.8025332093238831f, + -0.04026109725236893f, -2.161693811416626f, 0.9118471145629883f, + 0.928778350353241f, 1.0625823736190796f, 0.11546365916728973f, + 0.006691100541502237f, -1.3805687427520752f, -0.11042501777410507f, + -0.2545872926712036f, 1.2440357208251953f, -1.6608234643936157f, + 0.9239633083343506f, -0.7040714621543884f, 1.1720298528671265f +}; + +static const float + av1_hd_4_partition_nn_weights_16_layer1[24 * NEW_LABEL_SIZE] = { + -0.0328470803797245f, 0.23792479932308197f, 0.25561416149139404f, + -0.020317574962973595f, 0.11895804107189178f, 0.11310578882694244f, + -0.11336013674736023f, 0.16002647578716278f, 0.18786849081516266f, + 0.10171400010585785f, 2.3350460529327393f, -0.2828865051269531f, + -0.1283670961856842f, 0.3708970248699188f, -0.21812289953231812f, + -0.0782235786318779f, -0.2884467840194702f, -0.36653709411621094f, + 0.28875768184661865f, 0.3709397315979004f, -0.1924125701189041f, + 0.8257001042366028f, -0.3084794580936432f, 0.10378926247358322f, + 0.10832912474870682f, 0.23994174599647522f, -0.09522973001003265f, + -0.20610085129737854f, -0.2905101180076599f, 0.18631568551063538f, + -0.3959210515022278f, 0.056451812386512756f, -1.2908092737197876f, + 0.3916683793067932f, -8.122172355651855f, -0.12142644822597504f, + -0.4428127706050873f, -0.24993056058883667f, 0.2432871013879776f, + -0.4911264181137085f, 0.23390810191631317f, -0.08834259957075119f, + -0.0922209843993187f, -0.791261613368988f, -0.2527330815792084f, + -0.8133859038352966f, 0.14507368206977844f, -0.45458677411079407f, + -0.5710628628730774f, -0.2644200026988983f, 0.2170594483613968f, + 0.40066513419151306f, -0.18364030122756958f, -0.8001738786697388f, + 0.14335019886493683f, -0.2577047646045685f, 0.11850440502166748f, + -0.14166106283664703f, -2.869997024536133f, 0.11104889959096909f, + -0.5155230760574341f, -0.8204565048217773f, -0.2842898666858673f, + -0.4841044247150421f, -0.23635634779930115f, 0.3735388517379761f, + 0.19305679202079773f, -0.5582130551338196f, 0.5497944951057434f, + -0.4491751790046692f, -0.2788706123828888f, 0.5158282518386841f + }; + +static const float av1_hd_4_partition_nn_bias_16_layer1[NEW_LABEL_SIZE] = { + 0.8354771137237549f, -0.7445926666259766f, -0.5160333514213562f +}; static const float av1_4_partition_nn_weights_16_layer0[FEATURE_SIZE * 24] = { -2.032866f, 0.056691f, 0.495960f, 0.778785f, 0.548153f, -0.806942f, @@ -1426,21 +2262,39 @@ 0.401786f, }; -static const NN_CONFIG av1_4_partition_nnconfig_16 = { - FEATURE_SIZE, // num_inputs - LABEL_SIZE, // num_outputs - 1, // num_hidden_layers +static const NN_CONFIG av1_4_partition_nnconfig_16[2] = { { - 24, // num_hidden_nodes + FEATURE_SIZE, // num_inputs + LABEL_SIZE, // num_outputs + 1, // num_hidden_layers + { + 24, // num_hidden_nodes + }, + { + av1_4_partition_nn_weights_16_layer0, + av1_4_partition_nn_weights_16_layer1, + }, + { + av1_4_partition_nn_bias_16_layer0, + av1_4_partition_nn_bias_16_layer1, + }, }, { - av1_4_partition_nn_weights_16_layer0, - av1_4_partition_nn_weights_16_layer1, - }, - { - av1_4_partition_nn_bias_16_layer0, - av1_4_partition_nn_bias_16_layer1, - }, + FEATURE_SIZE, // num_inputs + NEW_LABEL_SIZE, // num_outputs + 1, // num_hidden_layers + { + 24, // num_hidden_nodes + }, + { + av1_hd_4_partition_nn_weights_16_layer0, + av1_hd_4_partition_nn_weights_16_layer1, + }, + { + av1_hd_4_partition_nn_bias_16_layer0, + av1_hd_4_partition_nn_bias_16_layer1, + }, + } }; static const float av1_4_partition_nn_weights_32_layer0[FEATURE_SIZE * 32] = { @@ -1583,21 +2437,39 @@ -0.261961f, }; -static const NN_CONFIG av1_4_partition_nnconfig_32 = { - FEATURE_SIZE, // num_inputs - LABEL_SIZE, // num_outputs - 1, // num_hidden_layers +static const NN_CONFIG av1_4_partition_nnconfig_32[2] = { { - 32, // num_hidden_nodes + FEATURE_SIZE, // num_inputs + LABEL_SIZE, // num_outputs + 1, // num_hidden_layers + { + 32, // num_hidden_nodes + }, + { + av1_4_partition_nn_weights_32_layer0, + av1_4_partition_nn_weights_32_layer1, + }, + { + av1_4_partition_nn_bias_32_layer0, + av1_4_partition_nn_bias_32_layer1, + }, }, { - av1_4_partition_nn_weights_32_layer0, - av1_4_partition_nn_weights_32_layer1, - }, - { - av1_4_partition_nn_bias_32_layer0, - av1_4_partition_nn_bias_32_layer1, - }, + FEATURE_SIZE, // num_inputs + NEW_LABEL_SIZE, // num_outputs + 1, // num_hidden_layers + { + 32, // num_hidden_nodes + }, + { + av1_hd_4_partition_nn_weights_32_layer0, + av1_hd_4_partition_nn_weights_32_layer1, + }, + { + av1_hd_4_partition_nn_bias_32_layer0, + av1_hd_4_partition_nn_bias_32_layer1, + }, + } }; static const float av1_4_partition_nn_weights_64_layer0[FEATURE_SIZE * 24] = { @@ -1708,84 +2580,105 @@ 0.040013f, }; -static const NN_CONFIG av1_4_partition_nnconfig_64 = { - FEATURE_SIZE, // num_inputs - LABEL_SIZE, // num_outputs - 1, // num_hidden_layers +static const NN_CONFIG av1_4_partition_nnconfig_64[2] = { { - 24, // num_hidden_nodes + FEATURE_SIZE, // num_inputs + LABEL_SIZE, // num_outputs + 1, // num_hidden_layers + { + 24, // num_hidden_nodes + }, + { + av1_4_partition_nn_weights_64_layer0, + av1_4_partition_nn_weights_64_layer1, + }, + { + av1_4_partition_nn_bias_64_layer0, + av1_4_partition_nn_bias_64_layer1, + }, }, { - av1_4_partition_nn_weights_64_layer0, - av1_4_partition_nn_weights_64_layer1, - }, - { - av1_4_partition_nn_bias_64_layer0, - av1_4_partition_nn_bias_64_layer1, - }, + FEATURE_SIZE, // num_inputs + NEW_LABEL_SIZE, // num_outputs + 1, // num_hidden_layers + { + 24, // num_hidden_nodes + }, + { + av1_hd_4_partition_nn_weights_64_layer0, + av1_hd_4_partition_nn_weights_64_layer1, + }, + { + av1_hd_4_partition_nn_bias_64_layer0, + av1_hd_4_partition_nn_bias_64_layer1, + }, + } }; #undef FEATURE_SIZE #undef LABEL_SIZE +#undef NEW_LABEL_SIZE #define FEATURE_SIZE 4 // Mean and std static const float av1_hd_partition_breakout_nn_mean_128[FEATURE_SIZE] = { - 10.730792598027795f, - 376.6180794798389f, - 699.9787168227473f, - 2069.426780349039f, + 1.7569518370024242f, + 5.5324198861333107f, + 5.5260479508123526f, + 7.1913091289363038f, }; static const float av1_hd_partition_breakout_nn_std_128[FEATURE_SIZE] = { - 29.023640354060735f, 659.3967891826734f, 940.2882285903872f, - 2566.2070264789245f + 1.3286579037156365f, 1.4302357105525316f, 1.947275803679912f, + 1.4856880036033451f }; static const float av1_hd_partition_breakout_nn_mean_64[FEATURE_SIZE] = { - 22.220171631578605f, - 360.49969866754554f, - 642.6605866566223f, - 1022.2158389850164f, + 2.5083185600034166f, + 5.5459875545856132f, + 5.4419578738009537f, + 6.3575563591302293f, }; static const float av1_hd_partition_breakout_nn_std_64[FEATURE_SIZE] = { - 69.0770174031301f, 758.5544028348198f, 891.8245485291498f, 1901.3873859786233f + 1.5327874427371651f, 1.4413133143150851f, 1.852577618179309f, + 1.7441221691208793f }; static const float av1_hd_partition_breakout_nn_mean_32[FEATURE_SIZE] = { - 56.23900643356807f, - 411.708483646884f, - 552.7719521312714f, - 671.8117635469166f, + 3.2925380941946631f, + 5.6592503897046313f, + 5.2656875189877344f, + 5.88623228906336f, }; static const float av1_hd_partition_breakout_nn_std_32[FEATURE_SIZE] = { - 198.97960359590587f, 959.3829390828911f, 830.9980036115545f, - 1498.4982976375336f + 1.6894083970930263f, 1.4685304613224999f, 1.8598497961630693f, + 1.7648307635114531f }; static const float av1_hd_partition_breakout_nn_mean_16[FEATURE_SIZE] = { - 136.9843430416749f, - 532.9528184025893f, - 504.7980827006724f, - 445.7892729738405f, + 4.334899946924148f, + 5.8802354638427667f, + 5.2098555971571239f, + 5.5808807097219715f, }; static const float av1_hd_partition_breakout_nn_std_16[FEATURE_SIZE] = { - 478.52767648003527f, 1308.8412547684493f, 795.1318845991449f, - 1176.5665198192673f + 1.7269713123782717f, 1.5227226730026808f, 1.9255508380289716f, + 1.6959046024771494f }; static const float av1_hd_partition_breakout_nn_mean_8[FEATURE_SIZE] = { - 275.0553416698255f, - 566.7785552508784f, - 406.48781378761095f, - 247.1067473273997f, + 5.17688933331104f, + 6.0068390263641165f, + 4.9884361306237377f, + 5.0973575838899237f, }; static const float av1_hd_partition_breakout_nn_std_8[FEATURE_SIZE] = { - 935.5925922140688f, 1435.299322400821f, 678.9806893808023f, 791.3637162723987f + 1.693000978888298f, 1.4741850818874491f, 1.8398258859567682f, + 1.6434668808089992f }; static const float *const av1_hd_partition_breakout_nn_mean[5] = { @@ -1802,418 +2695,397 @@ static const float av1_hd_partition_breakout_nn_weights_128_layer0[FEATURE_SIZE * 32] = { - 0.4625445008277893f, 0.046699944883584976f, - 0.01686757430434227f, -0.20717839896678925f, - 3.7204904556274414f, -0.09939002245664597f, - -0.03257349878549576f, -0.02249263785779476f, - 4.27296257019043f, -0.2201482355594635f, - -0.02630791626870632f, -0.8290131092071533f, - 4.106664657592773f, -0.03872422128915787f, - -0.0008374569588340819f, 0.008610394783318043f, - 2.8612468242645264f, 0.029475420713424683f, - 0.01878979615867138f, -0.07678258419036865f, - 2.642153024673462f, -0.7023047208786011f, - -0.16276267170906067f, -1.7577937841415405f, - 3.9672751426696777f, -0.019311338663101196f, - -0.00475256098434329f, 7.173077028710395e-05f, - -0.06531551480293274f, -0.07583651691675186f, - -0.02518392540514469f, 0.005303085781633854f, - 4.6276326179504395f, -0.4294697642326355f, - -0.06581428647041321f, -0.4343421459197998f, - -3.5704429149627686f, -0.3216317892074585f, - 0.2882029414176941f, 0.6410080194473267f, - -3.4585084915161133f, 0.24044956266880035f, - 0.2667864263057709f, 0.35314950346946716f, - -0.9072761535644531f, 0.4799148142337799f, - -1.9291492700576782f, -0.14138458669185638f, - -4.956376075744629f, -0.4101237654685974f, - -0.00801194179803133f, 0.016341974958777428f, - 10.001052856445312f, -0.10566182434558868f, - -0.008026782423257828f, -0.04508136212825775f, - -0.11174708604812622f, -0.1031208261847496f, - 0.004450094420462847f, -0.0714421346783638f, - -3.7611849308013916f, -0.42005446553230286f, - -0.39350515604019165f, 0.47153064608573914f, - -0.36057817935943604f, -2.4768900871276855f, - -0.0051008667796850204f, 0.588866114616394f, - 3.6189825534820557f, -0.018487676978111267f, - 0.033942464739084244f, 0.3527982831001282f, - 2.350560426712036f, -1.840644359588623f, - 0.1856888085603714f, -1.4500601291656494f, - -2.9771881103515625f, 0.15055160224437714f, - -0.7389538884162903f, -0.02373156137764454f, - 3.244478464126587f, 0.09777998179197311f, - 0.03143433853983879f, -0.3365783095359802f, - -3.579131841659546f, -0.3401176333427429f, - 0.3683990240097046f, 0.6233412027359009f, - -5.737433433532715f, -1.0643895864486694f, - -0.0862712413072586f, -0.8468044400215149f, - 4.366209983825684f, -0.03896212950348854f, - -0.005277127958834171f, -0.054806072264909744f, - 4.20292854309082f, -0.33671531081199646f, - -0.04025593772530556f, -0.4954434335231781f, - 1.4843676090240479f, -0.05833450332283974f, - -1.2679489850997925f, 0.526577353477478f, - 4.186404705047607f, -0.019188573583960533f, - 0.03789565712213516f, 0.35230082273483276f, - 3.3375051021575928f, -0.01609140820801258f, - 0.027185490354895592f, 0.25255802273750305f, - 4.186381816864014f, -0.10051476955413818f, - -0.03126750886440277f, -0.02391117438673973f, - 3.763073205947876f, -0.04791776463389397f, - -2.3893446268630214e-05f, 0.010260583832859993f, - -2.701782464981079f, -0.3117542862892151f, - -0.08752211928367615f, 0.4682832360267639f, - 2.9983580112457275f, -0.47988685965538025f, - 0.2418350726366043f, 0.451855331659317f + -0.24421754479408264f, 0.5826895833015442f, -0.468494713306427f, + 0.05104721337556839f, -0.3030756115913391f, -0.47995227575302124f, + 0.30220189690589905f, 0.6468079686164856f, -0.6581971049308777f, + -0.08561690151691437f, -0.25164133310317993f, -0.7251309752464294f, + 0.28622835874557495f, -0.3805813491344452f, -1.2038640975952148f, + -0.03289572894573212f, -0.37501949071884155f, -0.6697373390197754f, + 0.10718680173158646f, 0.39980190992355347f, -0.6406639218330383f, + 0.3059859871864319f, -0.04687154293060303f, -0.0283386018127203f, + 0.2230420708656311f, -0.024931879714131355f, 0.029338961467146873f, + -0.39070868492126465f, 0.12055488675832748f, 0.44709470868110657f, + -0.2240329086780548f, 0.3899381160736084f, 0.7343757748603821f, + 0.3804762363433838f, -0.6964248418807983f, 0.031393926590681076f, + 1.1329405307769775f, 0.2145451456308365f, 0.15080654621124268f, + -0.5583563446998596f, 0.7352061867713928f, 0.03883780539035797f, + -0.003559485776349902f, -0.6891534328460693f, -0.2841552793979645f, + -0.033437542617321014f, -0.05212170630693436f, 0.46065035462379456f, + 0.49060872197151184f, -1.0210745334625244f, -0.20526815950870514f, + 0.3765658140182495f, -0.050877176225185394f, -0.4010245203971863f, + 0.2002274990081787f, -0.45646876096725464f, 0.028925752267241478f, + 0.44919347763061523f, -0.02976205199956894f, -0.14882135391235352f, + -0.23057465255260468f, -0.1957021802663803f, -0.02549680881202221f, + -0.8748094439506531f, -0.48586851358413696f, -0.4607398211956024f, + -0.19540899991989136f, 0.857029139995575f, -0.37086406350135803f, + -0.15118056535720825f, 0.18724219501018524f, 0.9177047610282898f, + -0.04173488914966583f, -0.3510979413986206f, 0.17673315107822418f, + -0.4069298505783081f, 0.020244762301445007f, -0.6126715540885925f, + 0.6862805485725403f, 0.1272497922182083f, -0.4310610890388489f, + -0.5232776403427124f, -0.26814699172973633f, 0.8925990462303162f, + -0.12301289290189743f, -0.5783265829086304f, 0.6845996379852295f, + 0.22650295495986938f, -0.11178003996610641f, -0.2689964771270752f, + -1.2071754932403564f, 0.8008237481117249f, -0.07050541788339615f, + -0.572569727897644f, 0.27917924523353577f, -0.6794210076332092f, + 0.37107107043266296f, 0.04119817912578583f, 0.08820059895515442f, + -0.29868319630622864f, -0.8464934229850769f, -0.6945635080337524f, + -0.6215496063232422f, 0.08221497386693954f, -0.6280669569969177f, + -0.44894012808799744f, 0.5003432631492615f, 0.4358958899974823f, + -0.03372211381793022f, 0.523205041885376f, -0.03965454921126366f, + -0.13546960055828094f, -0.3377343714237213f, -0.3924517333507538f, + -0.1991700977087021f, 0.6752629280090332f, -1.0135334730148315f, + 0.5485906600952148f, -0.020843572914600372f, -0.1461324244737625f, + -0.7540525794029236f, 0.21933406591415405f, -0.5565986633300781f, + 0.1918906718492508f, -0.40834516286849976f, 0.9896414279937744f, + 0.11563636362552643f, -0.017263229936361313f }; static const float av1_hd_partition_breakout_nn_bias_128_layer0[32] = { - -0.09511213004589081f, 0.9938786625862122f, 1.4839892387390137f, - 1.0876423120498657f, 0.8019428253173828f, 0.2738432288169861f, - 1.071292519569397f, -0.09138987958431244f, 1.2080857753753662f, - -0.6339017748832703f, -0.4984428584575653f, -1.2238473892211914f, - -1.3475229740142822f, 2.4443564414978027f, -0.16558663547039032f, - 0.033367302268743515f, -0.7447078227996826f, 1.0401278734207153f, - -0.4657363295555115f, -1.0536733865737915f, 0.8565444946289062f, - -0.5986987948417664f, -0.789206862449646f, 1.1764090061187744f, - 1.3952962160110474f, -0.17746582627296448f, 1.187064290046692f, - 0.9370515942573547f, 1.1067490577697754f, 1.003738522529602f, - -2.1517810821533203f, 0.8461123704910278f + 0.2676561176776886f, -0.29510897397994995f, -1.1071470975875854f, + 0.583503007888794f, -0.472633957862854f, -0.44106626510620117f, + 0.6721175909042358f, -0.42975232005119324f, -0.5145384073257446f, + 0.15512774884700775f, 1.0278737545013428f, -0.6500952839851379f, + 1.0237534046173096f, 0.5816664099693298f, 0.6633380055427551f, + -1.3322787284851074f, -0.4271796643733978f, 0.06402774155139923f, + 0.5194029808044434f, 0.03623528033494949f, -0.34109270572662354f, + -0.15831837058067322f, -0.07824128866195679f, 0.8782597184181213f, + -0.014673121273517609f, -0.20494380593299866f, -0.3248092532157898f, + 0.6707151532173157f, -0.2768913507461548f, -0.31312206387519836f, + -0.6461104154586792f, 0.16326004266738892f }; static const float av1_hd_partition_breakout_nn_weights_128_layer1[32] = { - -0.00859279278665781f, -2.9574432373046875f, -4.146053314208984f, - -3.348489761352539f, -0.05143212527036667f, -2.0400538444519043f, - -4.622243404388428f, -0.05732434615492821f, -5.16105318069458f, - 1.1596481800079346f, 0.8911784291267395f, 8.114648818969727f, - 19.255064010620117f, 11.491721153259277f, -0.2828827202320099f, - 1.5688998699188232f, 0.7430379986763f, -1.0839532613754272f, - -1.3526417016983032f, 5.244595527648926f, -4.02890682220459f, - 0.8462737798690796f, 5.2530364990234375f, -4.688608646392822f, - -4.285271644592285f, 0.3642231523990631f, -2.4636125564575195f, - -0.8168565034866333f, -6.247287273406982f, -1.9640626907348633f, - -2.2665669918060303f, -0.7841953039169312f + -0.31587469577789307f, 0.2444663941860199f, 1.1686184406280518f, + -1.136441707611084f, 0.462795227766037f, 0.16067592799663544f, + -0.08983717858791351f, -0.1526145339012146f, 1.2807552814483643f, + -1.8411308526992798f, -2.5061304569244385f, -0.33663204312324524f, + -1.3827414512634277f, -0.2779307961463928f, -0.47907790541648865f, + 1.9458348751068115f, 0.3252658247947693f, 0.6043151617050171f, + -0.2127833366394043f, 0.3176756501197815f, 0.9920678734779358f, + 0.49299460649490356f, 0.8988507390022278f, -0.7701797485351562f, + 0.29247382283210754f, 0.7251673340797424f, 0.460658997297287f, + -0.3683358430862427f, 0.6006630659103394f, 0.7680628299713135f, + 0.8999216556549072f, -0.529272735118866f }; static const float av1_hd_partition_breakout_nn_bias_128_layer1[1] = { - 0.1112925186753273f + -0.22755585610866547f }; static const float av1_hd_partition_breakout_nn_weights_64_layer0[FEATURE_SIZE * 32] = { - -2.9600589275360107f, 0.042996086180210114f, -0.5422487258911133f, - -0.16088658571243286f, 2.665553331375122f, 0.17293912172317505f, - -0.011884346604347229f, -1.3571131229400635f, -4.151463508605957f, - -0.09992470592260361f, -0.03328946977853775f, -0.19198642671108246f, - -4.485296726226807f, 0.0004126991261728108f, 0.00528523325920105f, - 0.0014443921390920877f, 3.302469253540039f, -0.054684557020664215f, - 0.0016338867135345936f, -2.7690939903259277f, 2.5339231491088867f, - 0.5221245884895325f, -0.17638295888900757f, -0.31949788331985474f, - 2.323598623275757f, 0.2059188336133957f, -0.01752222701907158f, - -1.1489571332931519f, 2.984546422958374f, -0.19020183384418488f, - 0.010538210161030293f, -1.4752967357635498f, 1.8535363674163818f, - 0.8166176080703735f, -0.5628044009208679f, -1.9997116327285767f, - -2.098487377166748f, 0.3857003152370453f, -1.727634310722351f, - -0.15402595698833466f, 3.4408957958221436f, -0.2683435082435608f, - 0.023594189435243607f, 0.31177008152008057f, 3.039790391921997f, - 0.4344753623008728f, 0.00961180217564106f, -0.45155903697013855f, - -2.201700210571289f, -2.646042585372925f, 0.01605065166950226f, - 0.13390818238258362f, -3.6872377395629883f, 0.7149689793586731f, - -0.04385269060730934f, -0.10101474821567535f, 2.27207088470459f, - 0.34321531653404236f, 0.04720620810985565f, -0.7443615198135376f, - -5.059787750244141f, 0.02228701300919056f, -0.0019030789844691753f, - 0.0005637781578116119f, 3.129986524581909f, 0.09729813039302826f, - -0.024177810177206993f, -1.8108325004577637f, 3.3083548545837402f, - -0.7547611594200134f, -0.44618573784828186f, -0.21931181848049164f, - -2.2004897594451904f, -2.6458916664123535f, 0.09130378812551498f, - 0.2665710747241974f, -0.5643004775047302f, -0.33926868438720703f, - -0.6195669174194336f, 0.7023015022277832f, -5.160871982574463f, - -0.0034803124144673347f, -0.011646432802081108f, 0.012036978267133236f, - -3.1459012031555176f, -1.4449714422225952f, -0.0737914890050888f, - 0.6170333623886108f, 2.4652867317199707f, -0.05448657274246216f, - -0.16370250284671783f, -0.6195785999298096f, -4.647854328155518f, - -0.00691827479749918f, 0.038473278284072876f, 0.032320134341716766f, - -4.32373571395874f, 0.004058001562952995f, -0.004842042922973633f, - -0.011608919128775597f, 2.67899489402771f, -0.21639853715896606f, - -0.004101857542991638f, -2.9161124229431152f, 4.8421549797058105f, - 0.04978768154978752f, -0.04711421951651573f, -0.06902165710926056f, - -0.16757740080356598f, 0.021471548825502396f, -0.09624961763620377f, - -0.05160382017493248f, -4.769743919372559f, 0.1121208518743515f, - -0.024247704073786736f, -0.20753318071365356f, 0.47400805354118347f, - 0.46464693546295166f, -2.335646390914917f, -0.00663331151008606f, - -1.8362538814544678f, -1.3952279090881348f, -0.0224999338388443f, - 0.9369595050811768f, -0.32786062359809875f, -1.2076143026351929f, - -0.4175379276275635f, 0.920979917049408f + 0.4476310610771179f, -0.8712111711502075f, 0.1288183629512787f, + 0.792915940284729f, -0.37857282161712646f, -0.5999379754066467f, + -0.09074056148529053f, 0.7428027987480164f, -0.806922197341919f, + -0.06513206660747528f, 0.0015326066641137004f, 0.03463340178132057f, + -0.45006656646728516f, 0.8719941973686218f, -0.3037683367729187f, + -0.4873082637786865f, 0.3746185898780823f, -0.35810524225234985f, + 0.16044893860816956f, -0.5600897073745728f, 0.7878753542900085f, + -0.057914119213819504f, -0.05587376281619072f, -0.32516810297966003f, + -0.054352328181266785f, 0.7050285339355469f, 0.4860455393791199f, + -0.26461395621299744f, 0.4708733856678009f, 0.6615039110183716f, + -0.6717810034751892f, 0.25766628980636597f, -0.20877176523208618f, + 0.4233146607875824f, -0.8261363506317139f, 0.6218927502632141f, + -0.7742594480514526f, -0.09597370028495789f, -0.016565624624490738f, + 0.3243916630744934f, 0.730843722820282f, 0.3434459865093231f, + -0.6115328073501587f, 0.05172838270664215f, -0.8085055947303772f, + -0.23082391917705536f, 0.25912967324256897f, 0.48390132188796997f, + -0.24071218073368073f, -0.21271266043186188f, 0.15473750233650208f, + 1.1897283792495728f, -0.605708122253418f, 0.8830835223197937f, + -0.8744847774505615f, -0.10007809847593307f, 0.14527341723442078f, + -0.3217686712741852f, -0.6968852281570435f, -0.12154795229434967f, + -0.8508146405220032f, -0.23319056630134583f, 0.27581697702407837f, + 0.49674510955810547f, 0.2984752655029297f, -0.24128034710884094f, + -0.6872956156730652f, 0.6757507920265198f, -0.9962611198425293f, + -0.24671325087547302f, 0.04458463191986084f, 0.5092823505401611f, + -0.755521833896637f, 0.1556125432252884f, -0.19932909309864044f, + 0.8233707547187805f, -0.33803892135620117f, 0.1641087830066681f, + -0.1640886515378952f, 0.2807005047798157f, 0.11740092933177948f, + 0.9441748261451721f, -0.19026336073875427f, 0.1905575692653656f, + -0.7156860828399658f, -0.30425164103507996f, -0.6542183756828308f, + -0.048906467854976654f, 0.6393331289291382f, -0.5186219811439514f, + -0.15262047946453094f, 0.729802668094635f, 0.4545252323150635f, + -0.5133960843086243f, 0.26521825790405273f, -0.4991665482521057f, + -0.4927463233470917f, -0.07089114189147949f, 0.33401384949684143f, + 0.3023838400840759f, 0.7638855576515198f, -0.4981137812137604f, + -0.5541877150535583f, -0.11165370792150497f, 0.3648005425930023f, + 0.5878415107727051f, 0.11315485090017319f, -1.0285885334014893f, + -0.45433279871940613f, -0.7583989500999451f, -0.14650657773017883f, + 0.24613532423973083f, -0.2773364782333374f, -0.9591728448867798f, + 0.5280625224113464f, 0.5242233276367188f, -0.39398059248924255f, + -0.8652032613754272f, 0.5243239998817444f, 0.5806688666343689f, + 0.42181527614593506f, -0.09341983497142792f, 0.12693725526332855f, + -0.39904987812042236f, 0.15719516575336456f, 0.16289645433425903f, + 0.20234838128089905f, -0.25414201617240906f }; static const float av1_hd_partition_breakout_nn_bias_64_layer0[32] = { - -0.9125985503196716f, 0.9354565739631653f, -0.9121711850166321f, - -1.1538583040237427f, 0.08968774974346161f, 1.1123112440109253f, - 1.4973655939102173f, 0.608467698097229f, 0.18589675426483154f, - -1.441516399383545f, 0.9556304812431335f, 0.7926673889160156f, - -1.4284138679504395f, -0.7179064750671387f, -0.047664791345596313f, - -1.2288035154342651f, 0.6012410521507263f, 0.18902179598808289f, - -0.89959716796875f, 0.23802867531776428f, -1.300124168395996f, - -0.08421888202428818f, 0.28658464550971985f, -0.27196547389030457f, - -1.0962705612182617f, -0.350778728723526f, 0.8084302544593811f, - -0.11797107756137848f, -0.9147244691848755f, -0.9423892498016357f, - -0.46693262457847595f, -1.179872989654541f + -0.34709370136260986f, -0.009843609295785427f, -1.6221565008163452f, + -0.668915867805481f, 0.9886232018470764f, 0.6522868275642395f, + -0.37716832756996155f, -0.07447462528944016f, 0.3766818642616272f, + -1.1512787342071533f, 0.40794867277145386f, -0.14982998371124268f, + -0.19331441819667816f, -0.9201817512512207f, 0.48975181579589844f, + -0.137226402759552f, -0.5480761528015137f, -0.14310558140277863f, + -0.035678427666425705f, -0.724799633026123f, 0.6203687787055969f, + -0.26913538575172424f, -0.45061108469963074f, 1.1059123277664185f, + -0.9563385844230652f, 0.40938442945480347f, 0.4411890506744385f, + 0.1871682107448578f, 0.004259683191776276f, -0.050544340163469315f, + 0.789713442325592f, 0.26867425441741943f }; static const float av1_hd_partition_breakout_nn_weights_64_layer1[32] = { - 1.888397216796875f, -1.6998655796051025f, 2.5516481399536133f, - 31.333364486694336f, -7.420342922210693f, -1.175126552581787f, - -2.0703518390655518f, -1.0818297863006592f, -1.0498319864273071f, - 12.22414779663086f, -0.6940112113952637f, -0.3731234669685364f, - 11.562948226928711f, 0.6934157013893127f, 1.0499767065048218f, - 15.071033477783203f, -2.1736562252044678f, 0.47170618176460266f, - 1.8899933099746704f, 0.2540336847305298f, 27.493698120117188f, - 0.8192774057388306f, -0.011624461971223354f, 1.5983129739761353f, - 34.282840728759766f, -15.804471969604492f, 1.1542948484420776f, - -0.09561372548341751f, 4.60494327545166f, 1.2926712036132812f, - 0.4446795880794525f, -0.7026389241218567f + 0.7450170516967773f, 0.1674197018146515f, 8.603666305541992f, + -1.4572426080703735f, -0.6806990504264832f, -0.36511117219924927f, + -0.21973447501659393f, -0.33904269337654114f, 0.494196355342865f, + 2.428866386413574f, -0.33053985238075256f, 0.5259426236152649f, + 0.7364671230316162f, 1.8298554420471191f, -0.20149026811122894f, + 0.4657254219055176f, 0.9128966331481934f, 0.41023728251457214f, + 0.4559532403945923f, -0.49007904529571533f, -0.6186080574989319f, + 0.6019283533096313f, 0.41913628578186035f, -0.9652310609817505f, + -1.266809105873108f, -0.6605119109153748f, -1.0554698705673218f, + 0.28501376509666443f, 0.40640202164649963f, 0.4747793972492218f, + -0.3028396964073181f, -0.020081687718629837f }; static const float av1_hd_partition_breakout_nn_bias_64_layer1[1] = { - -0.15468640625476837f + -0.35164690017700195f }; static const float av1_hd_partition_breakout_nn_weights_32_layer0[FEATURE_SIZE * 32] = { - -0.18351112306118011f, -0.15651831030845642f, 0.0013713851803913713f, - 0.01054045557975769f, -3.780832529067993f, -0.15531419217586517f, - 0.03428387641906738f, -0.027534646913409233f, -4.079058647155762f, - 0.05176529288291931f, -0.0065496210008859634f, -0.08574667572975159f, - -1.9617663621902466f, 0.3167760670185089f, -0.33277764916419983f, - 0.7234531044960022f, 2.2539572715759277f, -0.02695995196700096f, - 0.005501582287251949f, -3.854844093322754f, -2.5093464851379395f, - -1.8817286491394043f, -0.13509777188301086f, 0.8389045000076294f, - -0.8123356103897095f, -2.210233449935913f, 0.04169511795043945f, - 1.3030999898910522f, 1.0332109928131104f, 0.15076060593128204f, - -0.02417255938053131f, -3.10256290435791f, -3.5063133239746094f, - 0.1668155938386917f, -0.11349064111709595f, -0.08673816174268723f, - 0.2023429572582245f, 0.2143218219280243f, -2.2571842670440674f, - 0.1890597939491272f, -2.0899853706359863f, -0.5377482771873474f, - -0.24231456220149994f, 0.7975266575813293f, -1.104709267616272f, - 0.42163029313087463f, -1.588227391242981f, -0.5404914021492004f, - 0.39026254415512085f, 0.3967314660549164f, -2.0958690643310547f, - -0.1487407088279724f, -1.2271692752838135f, -2.4342153072357178f, - 0.0073483348824083805f, 0.4332350790500641f, 1.8399561643600464f, - 0.20915934443473816f, 0.014152542687952518f, -2.279658079147339f, - 2.1412830352783203f, -1.2423213720321655f, 0.10377844423055649f, - 0.0026721248868852854f, -1.515641212463379f, 0.011470284312963486f, - 0.433901309967041f, 0.03397373855113983f, 1.9424909353256226f, - 0.007673368323594332f, -0.019963057711720467f, -3.2635231018066406f, - -3.814363956451416f, -0.008298925124108791f, -0.0006046147318556905f, - -0.03078455477952957f, -2.5249812602996826f, -1.5319974422454834f, - 0.03523765504360199f, 0.47840240597724915f, -2.459254503250122f, - -0.060729049146175385f, 0.3428601920604706f, 1.0676825046539307f, - -3.196352481842041f, 0.00556157436221838f, -0.014416366815567017f, - 0.00013636458606924862f, 0.5342910289764404f, 0.08967946469783783f, - -1.079426884651184f, 0.0459836907684803f, 1.169906735420227f, - 0.23663409054279327f, 0.15936784446239471f, 0.12904685735702515f, - -1.941832423210144f, -2.3517515659332275f, -0.4566086530685425f, - 1.001920223236084f, -2.479144334793091f, -1.4872301816940308f, - 0.44298961758613586f, 0.0910615548491478f, 0.34078770875930786f, - -1.0173975229263306f, 0.2069491446018219f, 0.8345462679862976f, - 1.5491070747375488f, 0.20149222016334534f, 0.2451339066028595f, - -1.176571011543274f, -2.3429925441741943f, 0.2398013025522232f, - -0.9587415456771851f, -0.05250968039035797f, -3.1348395347595215f, - -0.55987948179245f, -0.040084246546030045f, 0.29037174582481384f, - 1.663692593574524f, 0.6011162400245667f, 0.014114673249423504f, - -0.9607003331184387f, -1.0632480382919312f, -0.9293855428695679f, - -0.7134267091751099f, 0.9321397542953491f + -0.8952129483222961f, -0.23612700402736664f, -0.022868705913424492f, + 0.504967451095581f, 0.3275434970855713f, 1.1059952974319458f, + -0.34120088815689087f, -0.07344819605350494f, 0.15775921940803528f, + -0.04879460483789444f, 0.1439337134361267f, 1.2064639329910278f, + 0.006475068163126707f, -0.4494408369064331f, -0.10751494765281677f, + 1.1100929975509644f, 0.12578976154327393f, 0.4349801242351532f, + 0.5385650396347046f, -0.4146132171154022f, -0.6666572690010071f, + 0.1782642900943756f, -0.43637481331825256f, -0.522347629070282f, + -0.7048850655555725f, -0.21371017396450043f, 0.1338515281677246f, + 0.9378389716148376f, -0.9088003635406494f, 0.3172213137149811f, + -0.6936320066452026f, 0.6413770318031311f, -0.6204543113708496f, + -0.20075663924217224f, 0.011177961714565754f, 0.3956983685493469f, + -0.23885823786258698f, 0.7718412280082703f, 0.013668450526893139f, + -0.11212749779224396f, -0.3808520436286926f, -0.019013067707419395f, + -0.5678153038024902f, 1.038125991821289f, -0.0636964738368988f, + -0.9305693507194519f, 0.5443329811096191f, 0.5650880932807922f, + -0.2772569954395294f, -0.7757272720336914f, 0.02383701130747795f, + 0.5687315464019775f, -0.4549035429954529f, -0.16180022060871124f, + 0.033420149236917496f, 0.5288522839546204f, -0.3871588110923767f, + 0.712287962436676f, 0.008839298039674759f, -0.4081243574619293f, + -0.44496551156044006f, -0.49737873673439026f, 0.5079053044319153f, + 0.4881437122821808f, 0.6699126362800598f, 0.28043776750564575f, + -0.5355777740478516f, 0.2881544232368469f, 0.33460190892219543f, + -0.26208120584487915f, -0.44822946190834045f, -0.733630895614624f, + -0.35417506098747253f, 0.6161319613456726f, -0.0993569940328598f, + -0.7897841334342957f, -0.551094651222229f, 0.4497978985309601f, + -0.31630903482437134f, 0.46718651056289673f, -0.19858941435813904f, + 1.0053157806396484f, -0.3194795548915863f, 0.31316739320755005f, + -0.035107459872961044f, -0.7919036746025085f, 0.04070507362484932f, + -0.325952410697937f, -0.8687034845352173f, -0.30160313844680786f, + 0.16643138229846954f, 1.1927493810653687f, -0.5958532094955444f, + -0.38102859258651733f, -0.06056114658713341f, 1.063319206237793f, + -0.028595328330993652f, 0.7656055688858032f, 0.5335075855255127f, + -0.6592297554016113f, -0.3134363293647766f, -0.8477844595909119f, + 0.14632871747016907f, 0.5351374745368958f, -0.007659543305635452f, + -0.8012065291404724f, 0.47477757930755615f, 0.4271889626979828f, + -0.6191681623458862f, -0.14256015419960022f, 0.038999997079372406f, + 0.4897593557834625f, -0.713534414768219f, 0.07782091945409775f, + -0.28364527225494385f, -0.17344607412815094f, -0.24871549010276794f, + 0.6229367852210999f, 0.35328975319862366f, -0.48995962738990784f, + 0.9903460741043091f, -0.1367904245853424f, -0.14807336032390594f, + -0.28647252917289734f, 0.6976669430732727f, -0.5536572933197021f, + 0.3724263310432434f, -0.7798140645027161f }; static const float av1_hd_partition_breakout_nn_bias_32_layer0[32] = { - -0.11753689497709274f, -0.7140882015228271f, -0.9722395539283752f, - -0.6984077095985413f, -0.726676881313324f, -0.36886507272720337f, - -0.17676463723182678f, -0.10097962617874146f, -0.29938116669654846f, - -1.727449893951416f, 0.5804333090782166f, -1.2764358520507812f, - -1.108852505683899f, -0.8272318840026855f, 0.6785213947296143f, - 0.11738266795873642f, -2.057903528213501f, -0.37823784351348877f, - -0.9595664143562317f, -1.0248923301696777f, -0.23907040059566498f, - -0.8479264974594116f, -0.13894951343536377f, -1.7720012664794922f, - -1.6527719497680664f, -0.20360442996025085f, -1.7053756713867188f, - 0.8720151782035828f, -1.0570132732391357f, -0.7993026971817017f, - 0.46637022495269775f, -0.8374565243721008f + 0.5906000733375549f, 0.8543934226036072f, 0.43217557668685913f, + -0.643294632434845f, 0.30037060379981995f, -0.7660125494003296f, + 0.1731051206588745f, -0.7480033040046692f, -0.6353603005409241f, + 0.4871520698070526f, 0.7321553230285645f, 0.2810375988483429f, + -0.43400588631629944f, -0.17055644094944f, -0.8143026232719421f, + 0.13951803743839264f, -1.2939090728759766f, -0.5045616626739502f, + -1.295732021331787f, -0.9267586469650269f, -0.13748344779014587f, + -0.8858642578125f, 0.24190591275691986f, 0.34041425585746765f, + 0.05495613068342209f, 0.25397151708602905f, 0.24138814210891724f, + 0.12242670357227325f, -0.5556198358535767f, -0.46377164125442505f, + 0.6218398213386536f, 0.7764495015144348f }; static const float av1_hd_partition_breakout_nn_weights_32_layer1[32] = { - -0.329619824886322f, 4.491082191467285f, 11.859853744506836f, - -0.2909659743309021f, -20.884370803833008f, 1.2395148277282715f, - 0.7091589570045471f, -2.458725690841675f, 1.714850664138794f, - 2.3455934524536133f, 0.7239453792572021f, 11.537474632263184f, - 3.062530755996704f, 2.7946059703826904f, -1.4265711307525635f, - 0.44554367661476135f, -1.534838080406189f, -4.844552993774414f, - 18.15884780883789f, 5.826704978942871f, 0.5580787062644958f, - 11.919114112854004f, 0.24757426977157593f, 0.30521970987319946f, - -1.6404640674591064f, 0.848618745803833f, -0.8315955996513367f, - -0.8414434194564819f, 6.57717227935791f, 2.829108476638794f, - -0.25260433554649353f, -0.4767799377441406f + 0.6254786252975464f, -0.724627673625946f, 0.5428725481033325f, + 0.8609197735786438f, -0.33933258056640625f, 0.6795636415481567f, + 0.5508725643157959f, 1.4960711002349854f, 0.8083735108375549f, + -0.3456595838069916f, 0.5514245629310608f, 0.28833192586898804f, + 0.7953433394432068f, -0.009960951283574104f, -1.7862937450408936f, + 0.1605624407529831f, 1.6885508298873901f, -0.49641919136047363f, + -4.162553310394287f, -2.510878086090088f, 0.3701621890068054f, + -0.5248422622680664f, 0.5657362937927246f, 0.48716917634010315f, + -0.23934587836265564f, 0.45801791548728943f, 0.3940700590610504f, + 0.07297442853450775f, 0.287155419588089f, -0.0904238149523735f, + -0.608720064163208f, -0.7513124942779541f }; static const float av1_hd_partition_breakout_nn_bias_32_layer1[1] = { - 0.04493425786495209f + 0.1117519736289978f }; static const float av1_hd_partition_breakout_nn_weights_16_layer0[FEATURE_SIZE * 32] = { - -0.03770807012915611f, 0.1042923852801323f, 0.3589613735675812f, - 0.8915500044822693f, -0.025765806436538696f, -0.8080071806907654f, - 0.11935761570930481f, 1.081723690032959f, -1.0217446088790894f, - -0.05894668400287628f, -0.0417763888835907f, 1.9492236375808716f, - -1.6240522861480713f, -1.0665417909622192f, -0.4230113625526428f, - 2.6151089668273926f, -1.0250322818756104f, -1.013023853302002f, - -0.3417854607105255f, 1.2021280527114868f, -2.932366132736206f, - -1.5600024461746216f, 0.013506319373846054f, 1.827286958694458f, - 0.5162537097930908f, 0.37939924001693726f, -0.12543700635433197f, - -3.9469857215881348f, -0.5308187007904053f, 0.055751170963048935f, - -2.1356542110443115f, 0.9524611830711365f, -1.2815498113632202f, - -0.2583577334880829f, 0.5526014566421509f, -0.991321861743927f, - 0.4657832682132721f, -0.32342609763145447f, 0.012717160396277905f, - -4.607941150665283f, -0.7786141633987427f, -0.7440438270568848f, - -0.10956363379955292f, 1.9957225322723389f, -3.557837724685669f, - -0.06580297648906708f, -0.019815972074866295f, 0.48607689142227173f, - 0.9143388271331787f, 0.7489029765129089f, 0.23694343864917755f, - -2.3731346130371094f, -2.455751895904541f, 0.02998301573097706f, - -1.352641224861145f, 1.4107884168624878f, -2.7167775630950928f, - -1.3195676803588867f, -0.0024603719357401133f, 1.568859338760376f, - -1.368638277053833f, -0.6318024396896362f, -0.8460041284561157f, - 0.7431659698486328f, -0.17625141143798828f, -0.25151070952415466f, - 0.19983969628810883f, 2.0860755443573f, -3.904813289642334f, - -0.38988929986953735f, -0.002895161509513855f, 0.714531660079956f, - -3.0156004428863525f, -0.6497556567192078f, 0.32678696513175964f, - 0.5911594033241272f, -1.1386737823486328f, -0.6372389793395996f, - -0.029834266752004623f, 2.0517866611480713f, -1.0608230829238892f, - -0.955141544342041f, -0.10072463750839233f, 1.5374855995178223f, - -1.2410826683044434f, -2.169691562652588f, 0.003649030579254031f, - 2.0521841049194336f, -0.9012426733970642f, 0.6359571218490601f, - 0.10054442286491394f, 0.9355012774467468f, -2.433013439178467f, - -1.3059970140457153f, -0.062273263931274414f, 1.2142263650894165f, - 0.07180827856063843f, -0.02799983322620392f, 0.012213055975735188f, - -0.15036505460739136f, -1.5302343368530273f, -0.9248730540275574f, - -1.2439353466033936f, 1.6203926801681519f, -0.26789119839668274f, - 0.29344648122787476f, -0.12080803513526917f, 2.095102548599243f, - -3.6576807498931885f, 0.32156985998153687f, -0.00601181061938405f, - 0.3735950291156769f, -1.2174468040466309f, -0.21403838694095612f, - -2.081902503967285f, 1.0142937898635864f, -1.2942562103271484f, - -2.4739151000976562f, 0.11531462520360947f, 1.7982516288757324f, - -2.203162670135498f, 0.1353112757205963f, -1.742370843887329f, - 0.44314906001091003f, -1.1483821868896484f, -0.6968573331832886f, - -0.059433888643980026f, 0.9399102330207825f + 0.3427221179008484f, 0.06073892489075661f, -0.06882427632808685f, + 1.1336252689361572f, 0.097809799015522f, -0.6385823488235474f, + 0.43692731857299805f, -0.6029260754585266f, -0.13368098437786102f, + -0.6389026045799255f, 0.2193300873041153f, 0.6348177790641785f, + 0.22468054294586182f, 0.13958360254764557f, -0.05150606855750084f, + -0.28101637959480286f, -0.4977607727050781f, -0.3215360641479492f, + -0.16947278380393982f, 1.0890949964523315f, -1.02256441116333f, + 0.34789103269577026f, 0.34546756744384766f, -0.08185210078954697f, + 0.24066677689552307f, -1.2535117864608765f, 0.04398220032453537f, + 0.8025804162025452f, -0.2541569769382477f, 0.3501926362514496f, + -0.3649175763130188f, -0.2356642484664917f, 0.8068639039993286f, + -0.23699556291103363f, -0.2195892035961151f, -0.1547853797674179f, + -0.15287409722805023f, 0.4898677468299866f, -0.3860497772693634f, + 0.32813069224357605f, -0.7604209184646606f, -0.6611303091049194f, + 0.006376428063958883f, 1.0097582340240479f, 0.18538667261600494f, + 0.5587815642356873f, 0.30065974593162537f, -0.8898136615753174f, + 0.63029944896698f, 0.839385986328125f, -0.2562950849533081f, + -0.5478677749633789f, -0.7537129521369934f, 0.019417811185121536f, + 0.23349952697753906f, 0.07770441472530365f, 0.0019170257728546858f, + 0.003552661743015051f, 0.012630019336938858f, 0.03414732217788696f, + -0.04959012567996979f, -0.21843530237674713f, -0.5894723534584045f, + 0.21037738025188446f, -0.4222605526447296f, -0.5836210250854492f, + 0.011957126669585705f, 0.0031051053665578365f, -0.5127609968185425f, + -0.7208832502365112f, 0.05614267289638519f, 1.1000267267227173f, + -0.7031041383743286f, -0.20904500782489777f, 0.2827422320842743f, + 0.5169914960861206f, -0.03972400724887848f, -0.24878446757793427f, + -0.6129308938980103f, 0.3691819906234741f, -0.5902302861213684f, + -0.44936394691467285f, -0.20858828723430634f, -0.1926502138376236f, + 0.015391144901514053f, -0.05831986665725708f, 0.0012774848146364093f, + 0.9318875670433044f, -0.07232701778411865f, -0.07282842695713043f, + -0.3941495716571808f, 0.6986988186836243f, -0.6321771144866943f, + -0.1466921716928482f, -0.18214966356754303f, -0.2929930090904236f, + -0.048453543335199356f, 0.5302963852882385f, 0.3090183138847351f, + -0.7714007496833801f, -0.03153527155518532f, -0.28210026025772095f, + -0.0035391938872635365f, 1.1437654495239258f, 0.3694542348384857f, + -0.8283377885818481f, -0.06223354861140251f, 0.7067912817001343f, + -0.5538484454154968f, -0.3097091019153595f, 0.24007144570350647f, + 0.5142718553543091f, 0.5205507278442383f, -0.7828086018562317f, + 0.02196805737912655f, -0.5540323853492737f, -0.5541141629219055f, + -0.5407395958900452f, 0.83812016248703f, 0.5495181679725647f, + -0.36925947666168213f, -0.5186708569526672f, 0.035871513187885284f, + 0.9290533661842346f, 0.7525129914283752f, 0.16506646573543549f, + 0.015168293379247189f, -0.22260211408138275f }; static const float av1_hd_partition_breakout_nn_bias_16_layer0[32] = { - -2.500817060470581f, -1.1564264297485352f, 0.2977291941642761f, - -0.255332350730896f, -0.44481080770492554f, -0.6241381168365479f, - -0.4780181348323822f, -0.6444894075393677f, -1.2649517059326172f, - -1.3098241090774536f, 0.10575855523347855f, -0.6572186946868896f, - -0.13408327102661133f, -2.2591185569763184f, -0.6009128093719482f, - -0.9144248962402344f, 0.5553908944129944f, -0.40132245421409607f, - 0.3925309181213379f, 0.9891119003295898f, 0.7784745097160339f, - -0.47138360142707825f, -1.2775310277938843f, -0.3515026867389679f, - -0.4055931568145752f, -0.4989534318447113f, 0.7892708778381348f, - -0.5805770754814148f, -1.247983694076538f, -0.46460986137390137f, - -1.3504952192306519f, -0.29709282517433167f + 0.3051907420158386f, -0.7940329313278198f, 0.37281763553619385f, + -0.47349175810813904f, 0.9813013076782227f, -0.6225157976150513f, + -0.3041200339794159f, 0.18893377482891083f, -0.04121162369847298f, + 0.0799027606844902f, -0.06257568299770355f, 0.004709158092737198f, + 0.4165017306804657f, 0.40799248218536377f, -0.1605808585882187f, + 0.4690406322479248f, 0.2635003328323364f, 0.37693503499031067f, + 0.509822428226471f, 0.6932690739631653f, 0.3800438344478607f, + -0.9304105639457703f, 0.688030481338501f, -1.478393316268921f, + -0.08685281872749329f, -0.7200548648834229f, -0.9290581345558167f, + 0.47607457637786865f, -0.2131124883890152f, 0.4063669741153717f, + 0.6758500337600708f, 0.4055664539337158f }; static const float av1_hd_partition_breakout_nn_weights_16_layer1[32] = { - 0.37168681621551514f, 1.6819430589675903f, -0.24184460937976837f, - -0.5973770022392273f, -0.9343476295471191f, 9.948596000671387f, - -2.4088406562805176f, 0.7863813638687134f, -0.4910288453102112f, - -8.380901336669922f, -0.41362130641937256f, 5.82503604888916f, - -0.4502200186252594f, -1.826108694076538f, 4.183648109436035f, - 2.714763879776001f, -0.26116943359375f, 2.803555727005005f, - 0.8531390428543091f, 1.0828324556350708f, 0.8189658522605896f, - -2.4576377868652344f, -0.3627878427505493f, 2.138772487640381f, - -0.016609186306595802f, 0.7119042873382568f, 0.5726228356361389f, - 1.508818507194519f, 7.78598690032959f, 2.531076669692993f, - 6.370460510253906f, -0.04699355363845825f + 0.4050608277320862f, -0.41333451867103577f, 0.17236362397670746f, + 0.024612678214907646f, 0.9329894781112671f, -0.838448166847229f, + -1.4745935201644897f, 0.09989786148071289f, -0.10163675248622894f, + 0.17781522870063782f, 2.2620022296905518f, -0.3684936761856079f, + -0.6526914834976196f, 0.2942826449871063f, -0.35902100801467896f, + 0.10697637498378754f, 0.20386190712451935f, 1.2234491109848022f, + 0.30403849482536316f, 0.35678544640541077f, 0.29488465189933777f, + 1.736344814300537f, 0.5220925807952881f, -1.7380571365356445f, + -0.2642151713371277f, 0.7513654828071594f, -1.9644155502319336f, + 0.1906810700893402f, -0.35255730152130127f, 0.5716033577919006f, + 0.653854250907898f, -0.26972928643226624f }; static const float av1_hd_partition_breakout_nn_bias_16_layer1[1] = { - 0.42323046922683716f + 0.35929590463638306f }; static const float av1_hd_partition_breakout_nn_weights_8_layer0[FEATURE_SIZE * 32] = { - -0.9726589918136597f, -0.5411639213562012f, 0.7087171673774719f, - 1.7313759326934814f, -2.402184009552002f, -0.047024089843034744f, - -0.027457909658551216f, 1.6469149589538574f, 0.20576165616512299f, - -0.7170919179916382f, -0.10603219270706177f, 2.669275999069214f, - -0.2676953077316284f, -0.16688808798789978f, -0.03148927167057991f, - 1.4055346250534058f, -0.7267357110977173f, -1.3847031593322754f, - 0.21190257370471954f, -1.012730598449707f, -1.2875311374664307f, - -0.9335927963256836f, 0.9185367226600647f, -0.262455552816391f, - -1.8908239603042603f, -0.7330380082130432f, 0.580338716506958f, - 1.0240660905838013f, -0.741085946559906f, -0.4254375696182251f, - 1.0939886569976807f, 0.8454716205596924f, 0.028208844363689423f, - -1.108168363571167f, -0.5589184165000916f, 2.6788110733032227f, - -2.6662967205047607f, -0.7947397232055664f, 0.0377255417406559f, - 2.2486352920532227f, -0.6704809069633484f, 0.7823348641395569f, - 0.18206557631492615f, 1.8088916540145874f, -0.2850566804409027f, - -0.2228451520204544f, -0.0077590541914105415f, 0.267416387796402f, - -0.11488392949104309f, -0.3818961977958679f, 0.15274959802627563f, - 1.7191359996795654f, -0.1928352564573288f, 0.1540122777223587f, - -1.7266273498535156f, 0.16492529213428497f, -0.26917898654937744f, - 0.0453917570412159f, 0.006803665775805712f, -3.431412696838379f, - -0.8123732209205627f, 0.20750433206558228f, 0.043349288403987885f, - 1.5046347379684448f, -0.43123146891593933f, 0.496737003326416f, - 0.13186758756637573f, 1.348464012145996f, -1.0381258726119995f, - -0.14341726899147034f, -0.12307563424110413f, -0.47205984592437744f, - -0.26413795351982117f, -0.4825311005115509f, 0.01020186860114336f, - 1.8038893938064575f, 0.2135389894247055f, -0.5266213417053223f, - -0.34630000591278076f, 2.1957969665527344f, -2.6997740268707275f, - 0.22156886756420135f, 0.01723811775445938f, 0.9266343116760254f, - -0.29436543583869934f, -0.21920204162597656f, 0.050585292279720306f, - 1.8021506071090698f, 0.28632524609565735f, -1.0584778785705566f, - -0.004406697116792202f, 1.6804509162902832f, 0.1515306681394577f, - -0.5138083696365356f, -1.493039846420288f, 1.1335597038269043f, - -2.868360757827759f, -0.7500830888748169f, -0.018952427431941032f, - 1.7541661262512207f, -1.2600187063217163f, -0.19970472157001495f, - -0.027286890894174576f, -2.7354300022125244f, -1.349392056465149f, - -2.1341166496276855f, 0.1937991976737976f, -0.12994812428951263f, - -0.8666549921035767f, 0.3629917502403259f, -0.2922564446926117f, - 1.5711613893508911f, -1.5161727666854858f, -0.07508653402328491f, - -0.015387722291052341f, 1.1435129642486572f, 0.08286841958761215f, - -0.4614250361919403f, -0.26704689860343933f, 2.349350929260254f, - -0.16499075293540955f, 0.025024881586432457f, 0.001498897559940815f, - -0.04137025028467178f, -0.8304779529571533f, -0.20447227358818054f, - 0.009074501693248749f, 0.8690717816352844f + -1.1174798011779785f, -0.02540789172053337f, -0.024517325684428215f, + 0.9283695220947266f, 0.514511227607727f, 0.008708865381777287f, + -0.7743955254554749f, 0.03391582891345024f, -0.4907812476158142f, + 0.06405021250247955f, 0.009607836604118347f, -0.06413879245519638f, + -0.3100389838218689f, 0.027276739478111267f, 0.02754838392138481f, + 0.2741911709308624f, -0.33674532175064087f, -0.12841999530792236f, + -0.07583238184452057f, 1.2658021450042725f, -0.07618967443704605f, + 0.018406564369797707f, 0.36800676584243774f, 1.1263890266418457f, + 0.29222336411476135f, -0.1937945932149887f, -0.781943678855896f, + 0.08040471374988556f, 0.06932314485311508f, 0.27592363953590393f, + 0.6615497469902039f, -0.3753996193408966f, 0.04914139211177826f, + -0.18584918975830078f, 0.3851499855518341f, -0.14877784252166748f, + 0.8386304974555969f, -0.9132646322250366f, 0.09750116616487503f, + -0.38142505288124084f, -0.27932387590408325f, -0.3158399760723114f, + 0.3219115734100342f, -0.11211645603179932f, -0.1601802557706833f, + 0.199840247631073f, -0.1438094526529312f, 1.4131516218185425f, + -0.6507886648178101f, -0.35280969738960266f, -0.07980803400278091f, + 0.9108555912971497f, -0.49617353081703186f, -0.2692374289035797f, + 0.019175099208950996f, 0.5736552476882935f, -0.48202967643737793f, + -0.26134830713272095f, 0.018342891708016396f, 0.6154197454452515f, + -0.18230408430099487f, -0.8209198713302612f, 0.6161503195762634f, + 0.4757719039916992f, 0.5258967876434326f, -0.9027984142303467f, + -0.47966063022613525f, 0.6204502582550049f, -1.342071294784546f, + 0.12434060871601105f, 0.14531831443309784f, -0.15101315081119537f, + -0.35774746537208557f, 0.08184701204299927f, -0.04071677848696709f, + 1.2944822311401367f, -0.06492389738559723f, 0.34913739562034607f, + -0.08534345775842667f, 1.3283460140228271f, -0.7555822134017944f, + 0.3319704234600067f, -0.17644117772579193f, 0.5248327255249023f, + -0.46480831503868103f, -0.23956067860126495f, 0.0013285222230479121f, + 0.5432708859443665f, 0.30651840567588806f, 0.37756049633026123f, + -0.03282826766371727f, 0.06274658441543579f, -0.2130001038312912f, + 0.2907213270664215f, 0.09125600755214691f, -0.133547842502594f, + -0.05953352898359299f, 0.032438505440950394f, 0.05902266874909401f, + -0.043947484344244f, -1.100358486175537f, 0.2626442313194275f, + 0.056724611669778824f, -0.19415289163589478f, 0.010464449413120747f, + -0.385423481464386f, -0.29392293095588684f, 0.5806636214256287f, + -0.28265658020973206f, 1.0120196342468262f, 0.17220932245254517f, + -0.9805957078933716f, -0.368004709482193f, -0.42056915163993835f, + -0.1005094051361084f, -0.740735650062561f, 0.8810465335845947f, + -0.4035398066043854f, 0.06286423653364182f, -0.26255688071250916f, + -0.24484986066818237f, -0.1917300969362259f, 0.23190940916538239f, + -0.5081801414489746f, -0.909449577331543f, 0.6952412128448486f, + -0.41929101943969727f, -0.28803330659866333f }; static const float av1_hd_partition_breakout_nn_bias_8_layer0[32] = { - 0.7538588047027588f, -0.0891113132238388f, 0.6207568049430847f, - 0.1754276007413864f, -0.9016708731651306f, -0.5710541009902954f, - 0.3960225582122803f, -0.5215008854866028f, 0.1935194879770279f, - 0.1463070660829544f, -0.5508337020874023f, -0.25621265172958374f, - -0.21759319305419922f, -0.6702320575714111f, -0.510668158531189f, - -0.11305645108222961f, 0.9119726419448853f, 0.932460367679596f, - 0.5708784461021423f, 0.9256153702735901f, 0.4665280282497406f, - 0.2577301561832428f, 0.4606616199016571f, 0.4183802306652069f, - -0.11034739017486572f, -1.2057222127914429f, -1.5402069091796875f, - 0.36954033374786377f, -0.07251504063606262f, 0.4745430052280426f, - -0.4834096133708954f, -0.08527179062366486f + 0.9311190843582153f, -0.04600553959608078f, 0.4736453592777252f, + -0.21461233496665955f, -0.6837071776390076f, 0.41968896985054016f, + 0.5667760372161865f, -0.295064777135849f, 0.6580685973167419f, + -0.12489219009876251f, 0.6441367864608765f, 0.017892301082611084f, + 0.3467213213443756f, 0.24851299822330475f, 0.27420252561569214f, + 0.6503739356994629f, -0.9539271593093872f, -0.7639755606651306f, + 0.3438030183315277f, -0.65008944272995f, 0.1381210833787918f, + 0.17897674441337585f, 0.6611759066581726f, -0.6359773278236389f, + 0.753626823425293f, -0.8921093940734863f, 0.7966955304145813f, + -0.20982062816619873f, 0.2511371076107025f, -0.23097757995128632f, + 1.1271780729293823f, 0.26889708638191223f }; static const float av1_hd_partition_breakout_nn_weights_8_layer1[32] = { - 1.1129406690597534f, -1.5291675329208374f, -0.8876321911811829f, - 0.30224356055259705f, -0.40715256333351135f, -0.5691804885864258f, - 0.8208304643630981f, -0.6657942533493042f, -1.1190624237060547f, - 3.5144896507263184f, -0.4905086159706116f, -0.04449653998017311f, - 0.8738682270050049f, 0.38138872385025024f, -2.4828171730041504f, - -0.31892910599708557f, 0.4230459928512573f, 0.6047843098640442f, - 0.5003713369369507f, 1.1065287590026855f, 0.97183758020401f, - 0.2900541126728058f, 0.5317407846450806f, 0.6475094556808472f, - 4.615662574768066f, -7.686334133148193f, -0.9662672877311707f, - 0.6237889528274536f, -0.5734347701072693f, -0.4399879276752472f, - -0.016914604231715202f, -0.6294510960578918f + 2.6572957038879395f, 0.17708347737789154f, 0.09978588670492172f, + -0.15976227819919586f, 2.394906997680664f, 0.2525644302368164f, + 0.4016534388065338f, -0.18184596300125122f, 0.20857582986354828f, + -0.6041266918182373f, 0.1736220121383667f, 0.5656698346138f, + 1.886559247970581f, 1.0891855955123901f, 0.3225744664669037f, + 0.5683131217956543f, -1.198813796043396f, -1.226926326751709f, + 0.42729490995407104f, -1.0998389720916748f, -1.6383575201034546f, + 0.47224658727645874f, 0.3989439904689789f, 0.003893804969266057f, + 0.2592338025569916f, -0.9896919131278992f, 0.23605197668075562f, + -1.2522234916687012f, 0.44355013966560364f, -0.37252721190452576f, + 0.4963497519493103f, -0.596142053604126f }; static const float av1_hd_partition_breakout_nn_bias_8_layer1[1] = { - 1.0166552066802979f + 0.8022159934043884f }; static const float @@ -3059,19 +3931,19 @@ { // lowres { - 2.312415f, // p = 0.9099 - 1.848067f, // p = 0.8639 - 2.708050f, // p = 0.9375 - 1.567311f, // p = 0.8274 - 3.167687f, // p = 0.9596 + 1.587045f, // p = 0.830200 + 1.982054f, // p = 0.878900 + 2.150334f, // p = 0.895700 + 2.093809f, // p = 0.890300 + 2.428837f, // p = 0.919000 }, // midres { - 2.312415f, // p = 0.9099 - 1.848067f, // p = 0.8639 - 2.708050f, // p = 0.9375 - 1.567311f, // p = 0.8274 - 3.167687f, // p = 0.9596 + 2.044592f, // p = 0.885400 + 1.961544f, // p = 0.876700 + 2.940236f, // p = 0.949800 + 2.301486f, // p = 0.909000 + 2.265745f, // p = 0.906000 }, // hdres { @@ -3086,19 +3958,19 @@ { // lowres { - 2.312415f, // p = 0.9099 - 1.848067f, // p = 0.8639 - 2.708050f, // p = 0.9375 - 1.567311f, // p = 0.8274 - 3.167687f, // p = 0.9596 + 1.390049f, // p = 0.800600 + 1.784893f, // p = 0.856300 + 3.199090f, // p = 0.960800 + 2.119666f, // p = 0.892800 + 2.026000f, // p = 0.883500 }, // midres { - 2.312415f, // p = 0.9099 - 1.848067f, // p = 0.8639 - 2.708050f, // p = 0.9375 - 1.567311f, // p = 0.8274 - 3.167687f, // p = 0.9596 + 1.587045f, // p = 0.830200 + 1.982054f, // p = 0.878900 + 2.150334f, // p = 0.895700 + 2.093809f, // p = 0.890300 + 2.428837f, // p = 0.919000 }, // hdres { @@ -3210,19 +4082,19 @@ { // lowres { - -2.197225f, // p = 0.1 - -5.289093f, // p = 0.005021 - -2.522730f, // p = 0.07428 - -7.174085f, // p = 0.0007656 - -6.316499f, // p = 0.001803 + -2.758650f, // p = 0.059600 + -4.385780f, // p = 0.012300 + -4.230259f, // p = 0.014340 + -6.455747f, // p = 0.001569 + -8.541803f, // p = 0.000195 }, // midres { - -2.197225f, // p = 0.1 - -5.289093f, // p = 0.005021 - -2.522730f, // p = 0.07428 - -7.174085f, // p = 0.0007656 - -6.316499f, // p = 0.001803 + -4.881569f, // p = 0.007528 + -4.656635f, // p = 0.009409 + -3.037505f, // p = 0.045760 + -6.679364f, // p = 0.001255 + -5.944256f, // p = 0.002614 }, // hdres { @@ -3237,19 +4109,19 @@ { // lowres { - -2.197225f, // p = 0.1 - -5.289093f, // p = 0.005021 - -2.522730f, // p = 0.07428 - -7.174085f, // p = 0.0007656 - -6.316499f, // p = 0.001803 + -4.381672f, // p = 0.012350 + -4.034317f, // p = 0.017390 + -3.986288f, // p = 0.018230 + -7.325495f, // p = 0.000658 + -7.908147f, // p = 0.000368 }, // midres { - -2.197225f, // p = 0.1 - -5.289093f, // p = 0.005021 - -2.522730f, // p = 0.07428 - -7.174085f, // p = 0.0007656 - -6.316499f, // p = 0.001803 + -2.758650f, // p = 0.059600 + -4.385780f, // p = 0.012300 + -4.230259f, // p = 0.014340 + -6.455747f, // p = 0.001569 + -8.541803f, // p = 0.000195 }, // hdres {
diff --git a/av1/encoder/partition_search.c b/av1/encoder/partition_search.c index 81ac00b..c3b667d 100644 --- a/av1/encoder/partition_search.c +++ b/av1/encoder/partition_search.c
@@ -10,6 +10,7 @@ */ #include <float.h> +#include <inttypes.h> #include "config/aom_config.h" @@ -41,7 +42,9 @@ #include "av1/encoder/tune_vmaf.h" #endif -#define COLLECT_MOTION_SEARCH_FEATURE_SB 0 +#ifndef COLLECT_MOTION_SEARCH_FEATURE_SB +#define COLLECT_MOTION_SEARCH_FEATURE_SB CONFIG_PARTITION_SEARCH_ORDER +#endif #if CONFIG_PARTITION_SEARCH_ORDER void av1_reset_part_sf(PARTITION_SPEED_FEATURES *part_sf) { @@ -96,6 +99,7 @@ #endif // CONFIG_PARTITION_SEARCH_ORDER #if !CONFIG_REALTIME_ONLY +#if COLLECT_MOTION_SEARCH_FEATURE_SB // If input |features| is NULL, write tpl stats to file for each super block. // Otherwise, store tpl stats to |features|. // The tpl stats is computed in the unit of tpl_bsize_1d (16x16). @@ -204,6 +208,7 @@ } } } +#endif // COLLECT_MOTION_SEARCH_FEATURE_SB #endif // !CONFIG_REALTIME_ONLY static void update_txfm_count(MACROBLOCK *x, MACROBLOCKD *xd, @@ -412,7 +417,6 @@ const int mi_col = xd->mi_col; if (!is_inter) { xd->cfl.store_y = store_cfl_required(cm, xd); - mbmi->skip_txfm = 1; for (int plane = 0; plane < num_planes; ++plane) { av1_encode_intra_block_plane(cpi, x, bsize, plane, dry_run, cpi->optimize_seg_arr[mbmi->segment_id]); @@ -2107,6 +2111,7 @@ (subsampling_x + subsampling_y))); } + if (!is_inter_block(xd->mi[0])) xd->mi[0]->skip_txfm = 0; encode_superblock(cpi, tile_data, td, tp, dry_run, bsize, rate); if (!dry_run) { update_cb_offsets(x, bsize, subsampling_x, subsampling_y); @@ -4560,6 +4565,7 @@ av1_restore_context(x, x_ctx, mi_row, mi_col, bsize, av1_num_planes(cm)); } +#if COLLECT_MOTION_SEARCH_FEATURE_SB // The max number of nodes in the partition tree. // The number of leaf nodes is (128x128) / (4x4) = 1024. // The number of All possible parent nodes is 1 + 2 + ... + 512 = 1023. @@ -4623,6 +4629,7 @@ fclose(pfile); } +#endif // COLLECT_MOTION_SEARCH_FEATURE_SB #if CONFIG_PARTITION_SEARCH_ORDER static void verify_write_partition_tree(const AV1_COMP *const cpi, @@ -4911,18 +4918,77 @@ node->partitioning = partitioning; bsize = node->block_size; } - if (partitioning == PARTITION_SPLIT) { - const BLOCK_SIZE subsize = get_partition_subsize(bsize, PARTITION_SPLIT); - for (int i = 0; i < 4; ++i) { - if (node != NULL) { // Suppress warning - node->split[i] = av1_alloc_pc_tree_node(subsize); - if (!node->split[i]) - aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, - "Failed to allocate PC_TREE"); - node->split[i]->index = i; - tree_node_queue[last_idx] = node->split[i]; - ++last_idx; - } + if (partitioning != PARTITION_NONE) { + const BLOCK_SIZE subsize = get_partition_subsize(bsize, partitioning); + // Smaller block size for AB partition + const BLOCK_SIZE subsize2 = get_partition_subsize(bsize, PARTITION_SPLIT); + + switch (partitioning) { + case PARTITION_SPLIT: + case PARTITION_HORZ_4: + case PARTITION_VERT_4: + for (int i = 0; i < 4; ++i) { + if (node != NULL) { // Suppress warning + node->split[i] = av1_alloc_pc_tree_node(subsize); + if (!node->split[i]) + aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, + "Failed to allocate PC_TREE"); + node->split[i]->index = i; + tree_node_queue[last_idx] = node->split[i]; + ++last_idx; + } + } + break; + case PARTITION_HORZ: + case PARTITION_VERT: + for (int i = 0; i < 2; ++i) { + if (node != NULL) { // Suppress warning + node->split[i] = av1_alloc_pc_tree_node(subsize); + if (!node->split[i]) + aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, + "Failed to allocate PC_TREE"); + node->split[i]->index = i; + tree_node_queue[last_idx] = node->split[i]; + ++last_idx; + } + } + break; + case PARTITION_HORZ_A: + case PARTITION_VERT_A: + if (node != NULL) { // Suppress warning + node->split[0] = av1_alloc_pc_tree_node(subsize2); + node->split[1] = av1_alloc_pc_tree_node(subsize2); + node->split[2] = av1_alloc_pc_tree_node(subsize); + for (int i = 0; i < 3; ++i) { + if (!node->split[i]) + aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, + "Failed to allocate PC_TREE"); + node->split[i]->index = i; + tree_node_queue[last_idx] = node->split[i]; + ++last_idx; + } + } + break; + case PARTITION_HORZ_B: + case PARTITION_VERT_B: + if (node != NULL) { // Suppress warning + node->split[0] = av1_alloc_pc_tree_node(subsize); + node->split[1] = av1_alloc_pc_tree_node(subsize2); + node->split[2] = av1_alloc_pc_tree_node(subsize2); + for (int i = 0; i < 3; ++i) { + if (!node->split[i]) + aom_internal_error(error_info, AOM_CODEC_MEM_ERROR, + "Failed to allocate PC_TREE"); + node->split[i]->index = i; + tree_node_queue[last_idx] = node->split[i]; + ++last_idx; + } + } + break; + case PARTITION_NONE: + default: + // Should not hit this case. + assert(0); } } --num_nodes; @@ -5554,16 +5620,6 @@ part_search_state.partition_rect_allowed[VERT] &= !blk_params.has_cols; } -#ifndef NDEBUG - // Nothing should rely on the default value of this array (which is just - // leftover from encoding the previous block. Setting it to fixed pattern - // when debugging. - // bit 0, 1, 2 are blk_skip of each plane - // bit 4, 5, 6 are initialization checking of each plane - memset(x->txfm_search_info.blk_skip, 0x77, - sizeof(x->txfm_search_info.blk_skip)); -#endif // NDEBUG - assert(mi_size_wide[bsize] == mi_size_high[bsize]); // Set buffers and offsets. @@ -5594,12 +5650,13 @@ // external ML model. // TODO(chengchen): reduce motion search. This function is similar to // av1_get_max_min_partition_features(). - if (COLLECT_MOTION_SEARCH_FEATURE_SB && !frame_is_intra_only(cm) && - bsize == cm->seq_params->sb_size) { +#if COLLECT_MOTION_SEARCH_FEATURE_SB + if (!frame_is_intra_only(cm) && bsize == cm->seq_params->sb_size) { av1_collect_motion_search_features_sb(cpi, td, tile_data, mi_row, mi_col, bsize, /*features=*/NULL); collect_tpl_stats_sb(cpi, bsize, mi_row, mi_col, /*features=*/NULL); } +#endif // !COLLECT_MOTION_SEARCH_FEATURE_SB // Update rd cost of the bound using the current multiplier. av1_rd_cost_update(x->rdmult, &best_rdc); @@ -5861,12 +5918,11 @@ const int emit_output = multi_pass_mode != SB_DRY_PASS; const RUN_TYPE run_type = emit_output ? OUTPUT_ENABLED : DRY_RUN_NORMAL; +#if COLLECT_MOTION_SEARCH_FEATURE_SB // Write partition tree to file. Not used by default. - if (COLLECT_MOTION_SEARCH_FEATURE_SB) { - write_partition_tree(cpi, pc_tree, bsize, mi_row, mi_col); - ++cpi->sb_counter; - } - + write_partition_tree(cpi, pc_tree, bsize, mi_row, mi_col); + ++cpi->sb_counter; +#endif // COLLECT_MOTION_SEARCH_FEATURE_SB set_cb_offsets(x->cb_offset, 0, 0); encode_sb(cpi, td, tile_data, tp, mi_row, mi_col, run_type, bsize, pc_tree, NULL);
diff --git a/av1/encoder/partition_strategy.c b/av1/encoder/partition_strategy.c index 526ccb1e..4b44ba4 100644 --- a/av1/encoder/partition_strategy.c +++ b/av1/encoder/partition_strategy.c
@@ -210,7 +210,6 @@ bit_depth, &output)) { aom_internal_error(xd->error_info, AOM_CODEC_MEM_ERROR, "Error allocating CNN data"); - return; } } else { uint8_t *image[1] = { x->plane[AOM_PLANE_Y].src.buf - stride - 1 }; @@ -219,7 +218,6 @@ cnn_config, &thread_data, &output)) { aom_internal_error(xd->error_info, AOM_CODEC_MEM_ERROR, "Error allocating CNN data"); - return; } } @@ -1323,12 +1321,14 @@ #define FEATURES 18 #define LABELS 4 +#define NEW_LABELS 3 // Use a ML model to predict if horz4 and vert4 should be considered. void av1_ml_prune_4_partition(AV1_COMP *const cpi, MACROBLOCK *const x, int part_ctx, int64_t best_rd, PartitionSearchState *part_state, int *part4_allowed, unsigned int pb_source_variance) { + const AV1_COMMON *const cm = &cpi->common; const PartitionBlkParams blk_params = part_state->part_blk_params; const int mi_row = blk_params.mi_row; const int mi_col = blk_params.mi_col; @@ -1345,15 +1345,34 @@ if (best_rd >= 1000000000) return; int64_t *horz_rd = rect_part_rd[HORZ4]; int64_t *vert_rd = rect_part_rd[VERT4]; + + const int is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; + const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; + // res_idx is 0 for res < 480p, 1 for 480p, 2 for 720p+ + const int res_idx = is_480p_or_larger + is_720p_or_larger; + + const int bsize_idx = convert_bsize_to_idx(bsize); + if (bsize_idx < 0) return; + const float *ml_mean = av1_partition4_nn_mean[bsize_idx]; + const float *ml_std = av1_partition4_nn_std[bsize_idx]; + + int ml_model_index = (cpi->sf.part_sf.ml_4_partition_search_level_index < 3); + const NN_CONFIG *nn_config = NULL; // 4-way partitions are only allowed for these three square block sizes. switch (bsize) { - case BLOCK_16X16: nn_config = &av1_4_partition_nnconfig_16; break; - case BLOCK_32X32: nn_config = &av1_4_partition_nnconfig_32; break; - case BLOCK_64X64: nn_config = &av1_4_partition_nnconfig_64; break; + case BLOCK_16X16: + nn_config = &av1_4_partition_nnconfig_16[ml_model_index]; + break; + case BLOCK_32X32: + nn_config = &av1_4_partition_nnconfig_32[ml_model_index]; + break; + case BLOCK_64X64: + nn_config = &av1_4_partition_nnconfig_64[ml_model_index]; + break; default: assert(0 && "Unexpected bsize."); } - if (!nn_config) return; + if (!nn_config || !ml_mean || !ml_std) return; // Generate features. float features[FEATURES]; @@ -1437,6 +1456,12 @@ } assert(feature_index == FEATURES); + if (ml_model_index) { + for (int idx = 0; idx < FEATURES; idx++) { + features[idx] = (features[idx] - ml_mean[idx]) / ml_std[idx]; + } + } + // Write features to file if (!frame_is_intra_only(&cpi->common)) { write_features_to_file(cpi->oxcf.partition_info_path, @@ -1444,34 +1469,61 @@ FEATURES, 7, bsize, mi_row, mi_col); } - // Calculate scores using the NN model. - float score[LABELS] = { 0.0f }; - av1_nn_predict(features, nn_config, 1, score); - int int_score[LABELS]; - int max_score = -1000; - for (int i = 0; i < LABELS; ++i) { - int_score[i] = (int)(100 * score[i]); - max_score = AOMMAX(int_score[i], max_score); - } + if (ml_model_index == 0) { + // Calculate scores using the NN model. + float score[LABELS] = { 0.0f }; + av1_nn_predict(features, nn_config, 1, score); + int int_score[LABELS]; + int max_score = -1000; + for (int i = 0; i < LABELS; ++i) { + int_score[i] = (int)(100 * score[i]); + max_score = AOMMAX(int_score[i], max_score); + } - // Make decisions based on the model scores. - int thresh = max_score; - switch (bsize) { - case BLOCK_16X16: thresh -= 500; break; - case BLOCK_32X32: thresh -= 500; break; - case BLOCK_64X64: thresh -= 200; break; - default: break; - } - av1_zero_array(part4_allowed, NUM_PART4_TYPES); - for (int i = 0; i < LABELS; ++i) { - if (int_score[i] >= thresh) { - if ((i >> 0) & 1) part4_allowed[HORZ4] = 1; - if ((i >> 1) & 1) part4_allowed[VERT4] = 1; + // Make decisions based on the model scores. + int thresh = max_score; + switch (bsize) { + case BLOCK_16X16: thresh -= 500; break; + case BLOCK_32X32: thresh -= 500; break; + case BLOCK_64X64: thresh -= 200; break; + default: break; + } + av1_zero_array(part4_allowed, NUM_PART4_TYPES); + for (int i = 0; i < LABELS; ++i) { + if (int_score[i] >= thresh) { + if ((i >> 0) & 1) part4_allowed[HORZ4] = 1; + if ((i >> 1) & 1) part4_allowed[VERT4] = 1; + } + } + } else { + // Calculate scores using the NN model. + float score[NEW_LABELS] = { 0.0f }; + float probs[NEW_LABELS] = { 0.0f }; + av1_nn_predict(features, nn_config, 1, score); + + av1_nn_softmax(score, probs, NEW_LABELS); + + // Make decisions based on the model scores. + const float search_thresh = av1_partition4_search_thresh + [cpi->sf.part_sf.ml_4_partition_search_level_index][res_idx][bsize_idx]; + const float not_search_thresh = av1_partition4_not_search_thresh + [cpi->sf.part_sf.ml_4_partition_search_level_index][res_idx][bsize_idx]; + + for (int i = 1; i < NEW_LABELS; ++i) { + if (probs[i] >= search_thresh) { + if (i == 1) part4_allowed[HORZ4] = 1; + if (i == 2) part4_allowed[VERT4] = 1; + } + if (probs[i] < not_search_thresh) { + if (i == 1) part4_allowed[HORZ4] = 0; + if (i == 2) part4_allowed[VERT4] = 0; + } } } } #undef FEATURES #undef LABELS +#undef NEW_LABELS #define FEATURES 4 void av1_ml_predict_breakout(AV1_COMP *const cpi, const MACROBLOCK *const x, @@ -1536,16 +1588,29 @@ float rate_f = (float)AOMMIN(rd_stats->rate, INT_MAX); rate_f = ((float)x->rdmult / 128.0f / 512.0f / (float)(1 << num_pels_log2)) * rate_f; - features[feature_index++] = rate_f; + features[feature_index++] = + cpi->sf.part_sf.ml_partition_search_breakout_model_index + ? log1pf((float)rate_f) + : rate_f; const float dist_f = (float)(AOMMIN(rd_stats->dist, INT_MAX) >> num_pels_log2); - features[feature_index++] = dist_f; + features[feature_index++] = + cpi->sf.part_sf.ml_partition_search_breakout_model_index + ? log1pf((float)dist_f) + : dist_f; - features[feature_index++] = (float)pb_source_variance; + features[feature_index++] = + cpi->sf.part_sf.ml_partition_search_breakout_model_index + ? log1pf((float)pb_source_variance) + : (float)pb_source_variance; const int dc_q = (int)x->plane[0].dequant_QTX[0] >> (bit_depth - 8); - features[feature_index++] = (float)(dc_q * dc_q) / 256.0f; + features[feature_index++] = + cpi->sf.part_sf.ml_partition_search_breakout_model_index + ? log1pf((float)(dc_q * dc_q) / 256.0f) + : (float)(dc_q * dc_q) / 256.0f; + assert(feature_index == FEATURES); if (cpi->sf.part_sf.ml_partition_search_breakout_model_index) { @@ -1719,6 +1784,7 @@ av1_is_whole_blk_in_frame(blk_params, mi_params); if (try_intra_cnn_based_part_prune) { + assert(x->e_mbd.error_info->setjmp); intra_mode_cnn_partition(&cpi->common, x, x->part_search_info.quad_tree_idx, cpi->sf.part_sf.intra_cnn_based_part_prune_level, part_state); @@ -1927,6 +1993,7 @@ // Pruning: pruning out some ab partitions using a DNN taking rd costs of // sub-blocks from previous basic partition types. if (cpi->sf.part_sf.ml_prune_partition && ext_partition_allowed && + part_cfg->enable_ab_partitions && part_state->partition_rect_allowed[HORZ] && part_state->partition_rect_allowed[VERT]) { // TODO(huisu@google.com): x->source_variance may not be the current
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c index 99dcfb8..52b92b2 100644 --- a/av1/encoder/pass2_strategy.c +++ b/av1/encoder/pass2_strategy.c
@@ -164,12 +164,28 @@ return (int)max_bits; } +static const double q_pow_term[(QINDEX_RANGE >> 5) + 1] = { 0.65, 0.70, 0.75, + 0.80, 0.85, 0.90, + 0.95, 0.95, 0.95 }; +#define ERR_DIVISOR 96.0 +static double calc_correction_factor(double err_per_mb, int q) { + const double error_term = err_per_mb / ERR_DIVISOR; + const int index = q >> 5; + // Adjustment to power term based on qindex + const double power_term = + q_pow_term[index] + + (((q_pow_term[index + 1] - q_pow_term[index]) * (q % 32)) / 32.0); + assert(error_term >= 0.0); + return fclamp(pow(error_term, power_term), 0.05, 5.0); +} + // Based on history adjust expectations of bits per macroblock. static void twopass_update_bpm_factor(AV1_COMP *cpi, int rate_err_tol) { TWO_PASS *const twopass = &cpi->ppi->twopass; const PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; // Based on recent history adjust expectations of bits per macroblock. + double damp_fac = AOMMAX(5.0, rate_err_tol / 10.0); double rate_err_factor = 1.0; const double adj_limit = AOMMAX(0.2, (double)(100 - rate_err_tol) / 200.0); const double min_fac = 1.0 - adj_limit; @@ -204,7 +220,9 @@ #endif // CONFIG_THREE_PASS int err_estimate = p_rc->rate_error_estimate; + int64_t bits_left = twopass->bits_left; int64_t total_actual_bits = p_rc->total_actual_bits; + int64_t bits_off_target = p_rc->vbr_bits_off_target; double rolling_arf_group_actual_bits = (double)twopass->rolling_arf_group_actual_bits; double rolling_arf_group_target_bits = @@ -219,6 +237,10 @@ : 0; total_actual_bits = simulate_parallel_frame ? p_rc->temp_total_actual_bits : p_rc->total_actual_bits; + bits_off_target = simulate_parallel_frame ? p_rc->temp_vbr_bits_off_target + : p_rc->vbr_bits_off_target; + bits_left = + simulate_parallel_frame ? p_rc->temp_bits_left : twopass->bits_left; rolling_arf_group_target_bits = (double)(simulate_parallel_frame ? p_rc->temp_rolling_arf_group_target_bits @@ -231,22 +253,22 @@ : p_rc->rate_error_estimate; #endif - if ((p_rc->bits_off_target && total_actual_bits > 0) && - (rolling_arf_group_target_bits >= 1.0)) { - if (rolling_arf_group_actual_bits > rolling_arf_group_target_bits) { - double error_fraction = - (rolling_arf_group_actual_bits - rolling_arf_group_target_bits) / - rolling_arf_group_target_bits; - error_fraction = (error_fraction > 1.0) ? 1.0 : error_fraction; - rate_err_factor = 1.0 + error_fraction; + if (p_rc->bits_off_target && total_actual_bits > 0) { + if (cpi->ppi->lap_enabled) { + rate_err_factor = rolling_arf_group_actual_bits / + DOUBLE_DIVIDE_CHECK(rolling_arf_group_target_bits); } else { - double error_fraction = - (rolling_arf_group_target_bits - rolling_arf_group_actual_bits) / - rolling_arf_group_target_bits; - rate_err_factor = 1.0 - error_fraction; + rate_err_factor = 1.0 - ((double)(bits_off_target) / + AOMMAX(total_actual_bits, bits_left)); } - rate_err_factor = fclamp(rate_err_factor, min_fac, max_fac); + // Adjustment is damped if this is 1 pass with look ahead processing + // (as there are only ever a few frames of data) and for all but the first + // GOP in normal two pass. + if ((twopass->bpm_factor != 1.0) || cpi->ppi->lap_enabled) { + rate_err_factor = 1.0 + ((rate_err_factor - 1.0) / damp_fac); + } + rate_err_factor = AOMMAX(min_fac, AOMMIN(max_fac, rate_err_factor)); } // Is the rate control trending in the right direction. Only make @@ -254,44 +276,36 @@ if ((rate_err_factor < 1.0 && err_estimate >= 0) || (rate_err_factor > 1.0 && err_estimate <= 0)) { twopass->bpm_factor *= rate_err_factor; - twopass->bpm_factor = fclamp(twopass->bpm_factor, min_fac, max_fac); + if (rate_err_tol >= 100) { + twopass->bpm_factor = + AOMMAX(min_fac, AOMMIN(max_fac, twopass->bpm_factor)); + } else { + twopass->bpm_factor = AOMMAX(0.1, AOMMIN(10.0, twopass->bpm_factor)); + } } } -static const double q_div_term[(QINDEX_RANGE >> 4) + 1] = { - 18.0, 30.0, 38.0, 44.0, 47.0, 50.0, 52.0, 54.0, 56.0, - 58.0, 60.0, 62.0, 64.0, 66.0, 68.0, 70.0, 72.0 -}; - -#define EPMB_SCALER 1250000 -static double calc_correction_factor(double err_per_mb, int q) { - double power_term = 0.90; - const int index = q >> 4; - const double divisor = - q_div_term[index] + - (((q_div_term[index + 1] - q_div_term[index]) * (q % 16)) / 16.0); - double error_term = EPMB_SCALER * pow(err_per_mb, power_term); - return error_term / divisor; +static int qbpm_enumerator(int rate_err_tol) { + return 1200000 + ((300000 * AOMMIN(75, AOMMAX(rate_err_tol - 25, 0))) / 75); } // Similar to find_qindex_by_rate() function in ratectrl.c, but includes // calculation of a correction_factor. -static int find_qindex_by_rate_with_correction(uint64_t desired_bits_per_mb, - aom_bit_depth_t bit_depth, - double error_per_mb, - double group_weight_factor, - int best_qindex, - int worst_qindex) { +static int find_qindex_by_rate_with_correction( + uint64_t desired_bits_per_mb, aom_bit_depth_t bit_depth, + double error_per_mb, double group_weight_factor, int rate_err_tol, + int best_qindex, int worst_qindex) { assert(best_qindex <= worst_qindex); int low = best_qindex; int high = worst_qindex; while (low < high) { const int mid = (low + high) >> 1; - const double q_factor = calc_correction_factor(error_per_mb, mid); + const double mid_factor = calc_correction_factor(error_per_mb, mid); const double q = av1_convert_qindex_to_q(mid, bit_depth); + const int enumerator = qbpm_enumerator(rate_err_tol); const uint64_t mid_bits_per_mb = - (uint64_t)((q_factor * group_weight_factor) / q); + (uint64_t)((enumerator * mid_factor * group_weight_factor) / q); if (mid_bits_per_mb > desired_bits_per_mb) { low = mid + 1; @@ -343,10 +357,6 @@ const uint64_t target_norm_bits_per_mb = ((uint64_t)av_target_bandwidth << BPER_MB_NORMBITS) / active_mbs; int rate_err_tol = AOMMIN(rc_cfg->under_shoot_pct, rc_cfg->over_shoot_pct); - const double size_factor = - (active_mbs < 500) ? 0.925 : ((active_mbs > 3000) ? 1.05 : 1.0); - const double speed_factor = - AOMMIN(1.02, (0.975 + (0.005 * cpi->oxcf.speed))); // Update bpm correction factor based on previous GOP rate error. twopass_update_bpm_factor(cpi, rate_err_tol); @@ -355,8 +365,7 @@ // content at the given rate. int q = find_qindex_by_rate_with_correction( target_norm_bits_per_mb, cpi->common.seq_params->bit_depth, - av_err_per_mb, - cpi->ppi->twopass.bpm_factor * speed_factor * size_factor, + av_err_per_mb, cpi->ppi->twopass.bpm_factor, rate_err_tol, rc->best_quality, rc->worst_quality); // Restriction on active max q for constrained quality mode. @@ -574,10 +583,15 @@ } } +// scale_max_boost = (cpi->oxcf.mode != REALTIME) in most cases as it was only +// tuned in non-rtc cases. The only exception is when we derive +// gfu_boost_average, we pass scale_max_boost = false for better coding +// efficiency. static double calc_frame_boost(const PRIMARY_RATE_CONTROL *p_rc, const FRAME_INFO *frame_info, const FIRSTPASS_STATS *this_frame, - double this_frame_mv_in_out, double max_boost) { + double this_frame_mv_in_out, double max_boost, + const bool scale_max_boost) { double frame_boost; const double lq = av1_convert_qindex_to_q(p_rc->avg_frame_qindex[INTER_FRAME], frame_info->bit_depth); @@ -593,11 +607,14 @@ // Increase boost for frames where new data coming into frame (e.g. zoom out). // Slightly reduce boost if there is a net balance of motion out of the frame // (zoom in). The range for this_frame_mv_in_out is -1.0 to +1.0. - if (this_frame_mv_in_out > 0.0) + if (this_frame_mv_in_out > 0.0) { frame_boost += frame_boost * (this_frame_mv_in_out * 2.0); + if (scale_max_boost) max_boost += max_boost * (this_frame_mv_in_out * 2.0); + } // In the extreme case the boost is halved. - else + else { frame_boost += frame_boost * (this_frame_mv_in_out / 2.0); + } return AOMMIN(frame_boost, max_boost * boost_q_correction); } @@ -664,7 +681,7 @@ const PRIMARY_RATE_CONTROL *p_rc, FRAME_INFO *frame_info, int offset, int f_frames, int b_frames, int *num_fpstats_used, int *num_fpstats_required, - int project_gfu_boost) { + int project_gfu_boost, const bool scale_max_boost) { int i; GF_GROUP_STATS gf_stats; init_gf_stats(&gf_stats); @@ -697,10 +714,10 @@ : gf_stats.decay_accumulator; } - boost_score += - gf_stats.decay_accumulator * - calc_frame_boost(p_rc, frame_info, this_frame, - gf_stats.this_frame_mv_in_out, GF_MAX_BOOST); + boost_score += gf_stats.decay_accumulator * + calc_frame_boost(p_rc, frame_info, this_frame, + gf_stats.this_frame_mv_in_out, GF_MAX_BOOST, + scale_max_boost); if (num_fpstats_used) (*num_fpstats_used)++; } @@ -733,10 +750,10 @@ : gf_stats.decay_accumulator; } - boost_score += - gf_stats.decay_accumulator * - calc_frame_boost(p_rc, frame_info, this_frame, - gf_stats.this_frame_mv_in_out, GF_MAX_BOOST); + boost_score += gf_stats.decay_accumulator * + calc_frame_boost(p_rc, frame_info, this_frame, + gf_stats.this_frame_mv_in_out, GF_MAX_BOOST, + scale_max_boost); if (num_fpstats_used) (*num_fpstats_used)++; } arf_boost += (int)boost_score; @@ -1032,9 +1049,10 @@ // If almost totally static, we will not use the the max GF length later, // so we can continue for more frames. - if (((frame_index - cur_start) >= active_max_gf_interval + 1) && - !is_almost_static(gf_stats->zero_motion_accumulator, - twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled)) { + if ((frame_index - cur_start) >= active_max_gf_interval + 1 && + ((cpi->oxcf.mode != REALTIME) || + !is_almost_static(gf_stats->zero_motion_accumulator, + twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled))) { return 1; } return 0; @@ -1042,6 +1060,10 @@ static int is_shorter_gf_interval_better( AV1_COMP *cpi, const EncodeFrameParams *frame_params) { + if (av1_use_tpl_for_extrc(&cpi->ext_ratectrl) || + av1_encode_for_extrc(&cpi->ext_ratectrl)) { + return 0; + } const RATE_CONTROL *const rc = &cpi->rc; PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; int gop_length_decision_method = cpi->sf.tpl_sf.gop_length_decision_method; @@ -2031,11 +2053,20 @@ const int last_frame = regions[num_regions - 1].last - offset; // score of how much the arf helps the whole GOP double base_score = 0.0; + int count_base = 0; + bool static_frames = false; + // Accumulate base_score in for (int j = cur_start + 1; j < cur_start + min_shrink_int; j++) { if (stats + j >= twopass->stats_buf_ctx->stats_in_end) break; base_score = (base_score + 1.0) * stats[j].cor_coeff; + count_base++; } + + if (count_base && base_score / count_base > 0.992) { + static_frames = true; + } + int met_blending = 0; // Whether we have met blending areas before int last_blending = 0; // Whether the previous frame if blending for (int j = cur_start + min_shrink_int; j <= cur_last; j++) { @@ -2086,7 +2117,9 @@ AOMMAX(stats[n].intra_error, 0.001))); } - if (this_score > best_score) { + // Slightly relax the condition for videos starting with frozen + // frames. + if (this_score + (static_frames ? 0.5 : 0) > best_score) { best_score = this_score; best_j = j; } @@ -2170,10 +2203,12 @@ * case of one pass encoding where no lookahead stats are avialable. * * \param[in] cpi Top-level encoder structure + * \param[in] is_final_pass Whether it is the second call to + * define_gf_group(). * * \remark Nothing is returned. Instead, cpi->ppi->gf_group is changed. */ -static void define_gf_group_pass0(AV1_COMP *cpi) { +static void define_gf_group_pass0(AV1_COMP *cpi, const int is_final_pass) { RATE_CONTROL *const rc = &cpi->rc; PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; GF_GROUP *const gf_group = &cpi->ppi->gf_group; @@ -2181,13 +2216,9 @@ const GFConfig *const gf_cfg = &oxcf->gf_cfg; int target; - if (oxcf->q_cfg.aq_mode == CYCLIC_REFRESH_AQ) { - av1_cyclic_refresh_set_golden_update(cpi); - } else { - p_rc->baseline_gf_interval = p_rc->gf_intervals[p_rc->cur_gf_index]; - rc->intervals_till_gf_calculate_due--; - p_rc->cur_gf_index++; - } + p_rc->baseline_gf_interval = p_rc->gf_intervals[p_rc->cur_gf_index]; + rc->intervals_till_gf_calculate_due--; + p_rc->cur_gf_index++; // correct frames_to_key when lookahead queue is flushing correct_frames_to_key(cpi); @@ -2199,17 +2230,28 @@ p_rc->constrained_gf_group = (p_rc->baseline_gf_interval >= rc->frames_to_key) ? 1 : 0; - gf_group->max_layer_depth_allowed = oxcf->gf_cfg.gf_max_pyr_height; + // Default for pyr_height if inputs not set. + if (oxcf->gf_cfg.gf_max_pyr_height == 0 || + oxcf->gf_cfg.gf_min_pyr_height == 0) { + gf_group->max_layer_depth_allowed = 1; + } else { + gf_group->max_layer_depth_allowed = oxcf->gf_cfg.gf_max_pyr_height; + } // Rare case when the look-ahead is less than the target GOP length, can't // generate ARF frame. + // Also disable ARF frame if the motion content (source_sad) is high in + // lookahead buffer. + const uint64_t thresh_sad = 8 * 64 * 64; if (p_rc->baseline_gf_interval > gf_cfg->lag_in_frames || !is_altref_enabled(gf_cfg->lag_in_frames, gf_cfg->enable_auto_arf) || - p_rc->baseline_gf_interval < rc->min_gf_interval) + p_rc->baseline_gf_interval < rc->min_gf_interval || + rc->frame_source_sad_lag[0] > thresh_sad) { gf_group->max_layer_depth_allowed = 0; + } // Set up the structure of this Group-Of-Pictures (same as GF_GROUP) - av1_gop_setup_structure(cpi); + av1_gop_setup_structure(cpi, is_final_pass); // Allocate bits to each of the frames in the GF group. // TODO(sarahparker) Extend this to work with pyramid structure. @@ -2392,7 +2434,58 @@ const AV1EncoderConfig *const oxcf = &cpi->oxcf; const RateControlCfg *const rc_cfg = &oxcf->rc_cfg; + if (cpi->oxcf.mode != REALTIME) { + TWO_PASS_FRAME stats_in_backup = cpi->twopass_frame; + int gfu_boost_sum = 0; + int gfu_count = 0; + int accumulate_i = 0; + if (rc->frames_since_key == 0) { + for (int k = 0; k < MAX_NUM_GF_INTERVALS; k++) { + if (p_rc->gf_intervals[k] == 0) { + break; + } + + int new_i = p_rc->gf_intervals[k]; + int ext_len_new = new_i - (k == 0 ? is_intra_only : 0); + if (use_alt_ref) { + if (accumulate_i >= rc->frames_to_key) { + break; + } + const int forward_frames = + (rc->frames_to_key - accumulate_i - new_i >= ext_len_new) + ? ext_len_new + : AOMMAX(0, rc->frames_to_key - accumulate_i - new_i); + if (k) { + cpi->twopass_frame.stats_in += new_i; + if (cpi->twopass_frame.stats_in >= + twopass->stats_buf_ctx->stats_in_end) { + cpi->twopass_frame.stats_in = + twopass->stats_buf_ctx->stats_in_end; + } + } + reset_fpf_position(&cpi->twopass_frame, cpi->twopass_frame.stats_in); + // Calculate the boost for alt ref. Note that we pass the + // scale_max_boost=false to derive gfu_boost_average, which can help + // the coding efficiency for some clips with global motion. + int gfu_boost_tmp = av1_calc_arf_boost( + twopass, &cpi->twopass_frame, p_rc, frame_info, alt_offset, + forward_frames, ext_len_new, &p_rc->num_stats_used_for_gfu_boost, + &p_rc->num_stats_required_for_gfu_boost, cpi->ppi->lap_enabled, + /*scale_max_boost=*/false); + gfu_boost_sum += gfu_boost_tmp; + } + gfu_count++; + accumulate_i += new_i; + } + assert(gfu_count > 0); + p_rc->gfu_boost_average = gfu_boost_sum / gfu_count; + } + cpi->twopass_frame = stats_in_backup; + } + int ext_len = i - is_intra_only; + const bool scale_max_boost = (cpi->oxcf.mode != REALTIME); + if (use_alt_ref) { const int forward_frames = (rc->frames_to_key - i >= ext_len) ? ext_len @@ -2402,15 +2495,17 @@ p_rc->gfu_boost = av1_calc_arf_boost( twopass, &cpi->twopass_frame, p_rc, frame_info, alt_offset, forward_frames, ext_len, &p_rc->num_stats_used_for_gfu_boost, - &p_rc->num_stats_required_for_gfu_boost, cpi->ppi->lap_enabled); + &p_rc->num_stats_required_for_gfu_boost, cpi->ppi->lap_enabled, + scale_max_boost); } else { reset_fpf_position(&cpi->twopass_frame, start_pos); - p_rc->gfu_boost = AOMMIN( - MAX_GF_BOOST, - av1_calc_arf_boost( - twopass, &cpi->twopass_frame, p_rc, frame_info, alt_offset, ext_len, - 0, &p_rc->num_stats_used_for_gfu_boost, - &p_rc->num_stats_required_for_gfu_boost, cpi->ppi->lap_enabled)); + p_rc->gfu_boost = + AOMMIN(MAX_GF_BOOST, + av1_calc_arf_boost(twopass, &cpi->twopass_frame, p_rc, + frame_info, alt_offset, ext_len, 0, + &p_rc->num_stats_used_for_gfu_boost, + &p_rc->num_stats_required_for_gfu_boost, + cpi->ppi->lap_enabled, scale_max_boost)); } #define LAST_ALR_BOOST_FACTOR 0.2f @@ -2533,7 +2628,7 @@ } if (has_no_stats_stage(cpi)) { - define_gf_group_pass0(cpi); + define_gf_group_pass0(cpi, is_final_pass); return; } @@ -2578,10 +2673,11 @@ int use_alt_ref; if (can_disable_arf) { use_alt_ref = - !is_almost_static(gf_stats.zero_motion_accumulator, - twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled) && p_rc->use_arf_in_this_kf_group && (i < gf_cfg->lag_in_frames) && - (i >= MIN_GF_INTERVAL); + (i >= MIN_GF_INTERVAL) && + ((cpi->oxcf.mode != REALTIME) || + !is_almost_static(gf_stats.zero_motion_accumulator, + twopass->kf_zeromotion_pct, cpi->ppi->lap_enabled)); } else { use_alt_ref = p_rc->use_arf_in_this_kf_group && (i < gf_cfg->lag_in_frames) && (i > 2); @@ -2634,7 +2730,7 @@ update_gop_length(rc, p_rc, i, is_final_pass); // Set up the structure of this Group-Of-Pictures (same as GF_GROUP) - av1_gop_setup_structure(cpi); + av1_gop_setup_structure(cpi, is_final_pass); set_gop_bits_boost(cpi, i, is_intra_only, is_final_pass, use_alt_ref, alt_offset, start_pos, &gf_stats); @@ -2710,7 +2806,7 @@ update_gop_length(rc, p_rc, i, is_final_pass); // Set up the structure of this Group-Of-Pictures (same as GF_GROUP) - av1_gop_setup_structure(cpi); + av1_gop_setup_structure(cpi, is_final_pass); set_gop_bits_boost(cpi, i, is_intra_only, is_final_pass, use_alt_ref, 0, start_pos, &gf_stats); @@ -2850,6 +2946,14 @@ // Get the next frame details const FIRSTPASS_STATS *local_next_frame = av1_firstpass_info_peek(firstpass_info, this_stats_index + i); + + if ((local_next_frame->intra_error - this_stats->intra_error) / + DOUBLE_DIVIDE_CHECK(this_stats->intra_error) > + 0.1 && + this_stats->coded_error > local_next_frame->coded_error * 6) { + break; + } + double next_iiratio = (BOOST_FACTOR * local_next_frame->intra_error / DOUBLE_DIVIDE_CHECK(local_next_frame->coded_error)); @@ -2976,6 +3080,7 @@ : cpi->common.mi_params.MBs; const int future_stats_count = av1_firstpass_info_future_count(firstpass_info, 0); + while (frames_to_key < future_stats_count && frames_to_key < num_frames_to_detect_scenecut) { // Provided that we are not at the end of the file... @@ -2990,7 +3095,21 @@ oxcf->rc_cfg.mode, cpi->ppi->p_rc.enable_scenecut_detection, num_mbs); if (scenecut_detected) { - break; + int test_next_gop = 0; + + for (int idx = 0; idx < 32; ++idx) { + const FIRSTPASS_STATS *next_stats = + av1_firstpass_info_peek(firstpass_info, frames_to_key + idx); + + if (next_stats == NULL) continue; + + if (cpi->common.current_frame.frame_number + frames_to_key + idx > + 2 && + next_stats->lt_coded_error * 2.5 < next_stats->coded_error) + test_next_gop = 1; + } + + if (!test_next_gop) break; } } @@ -3260,15 +3379,30 @@ kf_raw_err = this_frame->intra_error; kf_mod_err = calculate_modified_err(frame_info, twopass, oxcf, this_frame); - // We assume the current frame is a key frame and we are looking for the next - // key frame. Therefore search_start_idx = 1 - frames_to_key = define_kf_interval(cpi, firstpass_info, kf_cfg->key_freq_max, - /*search_start_idx=*/1); - - if (frames_to_key != -1) { - rc->frames_to_key = AOMMIN(kf_cfg->key_freq_max, frames_to_key); + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_GOP) != 0 && + cpi->ext_ratectrl.funcs.get_key_frame_decision != NULL) { + aom_rc_key_frame_decision_t key_frame_decision; + aom_codec_err_t codec_status = av1_extrc_get_key_frame_decision( + &cpi->ext_ratectrl, &key_frame_decision); + if (codec_status == AOM_CODEC_OK) { + rc->frames_to_key = key_frame_decision.key_frame_group_size; + } else { + aom_internal_error(cpi->common.error, codec_status, + "av1_extrc_get_key_frame_decision() failed"); + } } else { - rc->frames_to_key = kf_cfg->key_freq_max; + // We assume the current frame is a key frame and we are looking for the + // next key frame. Therefore search_start_idx = 1 + frames_to_key = + define_kf_interval(cpi, firstpass_info, kf_cfg->key_freq_max, + /*search_start_idx=*/1); + + if (frames_to_key != -1) { + rc->frames_to_key = AOMMIN(kf_cfg->key_freq_max, frames_to_key); + } else { + rc->frames_to_key = kf_cfg->key_freq_max; + } } if (cpi->ppi->lap_enabled) correct_frames_to_key(cpi); @@ -3713,6 +3847,105 @@ first_stats->cor_coeff = 1.0; } +static void get_one_pass_rt_lag_params(AV1_COMP *cpi, unsigned int frame_flags, + EncodeFrameParams *const frame_params) { + RATE_CONTROL *const rc = &cpi->rc; + PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; + GF_GROUP *const gf_group = &cpi->ppi->gf_group; + const AV1EncoderConfig *const oxcf = &cpi->oxcf; + // Check forced key frames. + const int frames_to_next_forced_key = detect_app_forced_key(cpi); + if (frames_to_next_forced_key == 0) { + rc->frames_to_key = 0; + frame_flags &= FRAMEFLAGS_KEY; + } else if (frames_to_next_forced_key > 0 && + frames_to_next_forced_key < rc->frames_to_key) { + rc->frames_to_key = frames_to_next_forced_key; + } + frame_params->frame_type = gf_group->frame_type[cpi->gf_frame_index]; + if (cpi->gf_frame_index < gf_group->size && !(frame_flags & FRAMEFLAGS_KEY)) { + av1_setup_target_rate(cpi); + } + // Keyframe processing. + if (rc->frames_to_key <= 0) { + const KeyFrameCfg *const kf_cfg = &oxcf->kf_cfg; + assert(rc->frames_to_key == 0); + // Define next KF group. + frame_params->frame_type = KEY_FRAME; + rc->frames_since_key = 0; + // Use arfs if possible. + p_rc->use_arf_in_this_kf_group = is_altref_enabled( + oxcf->gf_cfg.lag_in_frames, oxcf->gf_cfg.enable_auto_arf); + // Reset the GF group data structures. + av1_zero(*gf_group); + cpi->gf_frame_index = 0; + // KF is always a GF so clear frames till next gf counter. + rc->frames_till_gf_update_due = 0; + int num_frames_to_app_forced_key = detect_app_forced_key(cpi); + p_rc->this_key_frame_forced = + cpi->common.current_frame.frame_number != 0 && rc->frames_to_key == 0; + if (num_frames_to_app_forced_key != -1) + rc->frames_to_key = num_frames_to_app_forced_key; + else + rc->frames_to_key = AOMMAX(1, kf_cfg->key_freq_max); + correct_frames_to_key(cpi); + p_rc->kf_boost = DEFAULT_KF_BOOST; + gf_group->update_type[0] = KF_UPDATE; + } + // Define a new GF/ARF group. (Should always enter here for key frames). + if (cpi->gf_frame_index == gf_group->size) { + int max_gop_length = + (oxcf->gf_cfg.lag_in_frames >= 32) + ? AOMMIN(MAX_GF_INTERVAL, oxcf->gf_cfg.lag_in_frames - + oxcf->algo_cfg.arnr_max_frames / 2) + : MAX_GF_LENGTH_LAP; + // Limit the max gop length for the last gop in 1 pass setting. + max_gop_length = AOMMIN(max_gop_length, rc->frames_to_key); + // Go through source frames in lookahead buffer and compute source metrics: + // scene change, frame average source sad, etc. + int num_frames = 1; + int scene_change_gop_frame_index = 0; + rc->frame_source_sad_lag[0] = 0; + rc->avg_source_sad = 0; + for (int i = 1; i < max_gop_length; i++) { + EncodeFrameInput frame_input; + memset(&frame_input, 0, sizeof(frame_input)); + struct lookahead_entry *e = + av1_lookahead_peek(cpi->ppi->lookahead, i, cpi->compressor_stage); + struct lookahead_entry *e_prev = + av1_lookahead_peek(cpi->ppi->lookahead, i - 1, cpi->compressor_stage); + if (e != NULL && e_prev != NULL) { + frame_input.source = &e->img; + frame_input.last_source = &e_prev->img; + rc->high_source_sad_lag[i] = -1; + rc->frame_source_sad_lag[i] = 0; + av1_rc_scene_detection_onepass_rt(cpi, &frame_input); + rc->high_source_sad_lag[i] = rc->high_source_sad; + rc->frame_source_sad_lag[i] = rc->frame_source_sad; + if (rc->high_source_sad_lag[i] == 1 && i > 1) { + // Scene change, so exit and constrain the gop to this frame. + scene_change_gop_frame_index = i; + break; + } + num_frames++; + rc->frame_source_sad_lag[0] += rc->frame_source_sad_lag[i]; + } + } + if (scene_change_gop_frame_index > 0) + max_gop_length = AOMMIN(max_gop_length, scene_change_gop_frame_index); + rc->frame_source_sad_lag[0] = rc->frame_source_sad_lag[0] / num_frames; + calculate_gf_length(cpi, max_gop_length, MAX_NUM_GF_INTERVALS); + define_gf_group(cpi, frame_params, 0); + rc->frames_till_gf_update_due = p_rc->baseline_gf_interval; + frame_params->frame_type = gf_group->frame_type[cpi->gf_frame_index]; + av1_setup_target_rate(cpi); + // Reset the source_sad parameters for the encoding. + rc->high_source_sad = 0; + rc->frame_source_sad = UINT64_MAX; + rc->avg_source_sad = 0; + } +} + void av1_get_second_pass_params(AV1_COMP *cpi, EncodeFrameParams *const frame_params, unsigned int frame_flags) { @@ -3722,6 +3955,19 @@ GF_GROUP *const gf_group = &cpi->ppi->gf_group; const AV1EncoderConfig *const oxcf = &cpi->oxcf; + if (is_one_pass_rt_lag_params(cpi)) { + get_one_pass_rt_lag_params(cpi, frame_flags, frame_params); + return; + } + + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & AOM_RC_GOP) != 0 && + cpi->ext_ratectrl.funcs.get_gop_decision != NULL) { + frame_params->show_frame = + !(gf_group->update_type[cpi->gf_frame_index] == ARF_UPDATE || + gf_group->update_type[cpi->gf_frame_index] == INTNL_ARF_UPDATE); + } + if (cpi->use_ducky_encode && cpi->ducky_encode_info.frame_info.gop_mode == DUCKY_ENCODE_GOP_MODE_RCL) { frame_params->frame_type = gf_group->frame_type[cpi->gf_frame_index]; @@ -3735,6 +3981,16 @@ return; } + if (cpi->common.current_frame.frame_number == 0 && + cpi->ext_ratectrl.funcs.send_firstpass_stats != NULL) { + const aom_codec_err_t codec_status = av1_extrc_send_firstpass_stats( + &cpi->ext_ratectrl, &cpi->ppi->twopass.firstpass_info); + if (codec_status != AOM_CODEC_OK) { + aom_internal_error(cpi->common.error, codec_status, + "av1_extrc_send_firstpass_stats() failed"); + } + } + const FIRSTPASS_STATS *const start_pos = cpi->twopass_frame.stats_in; int update_total_stats = 0; @@ -3805,6 +4061,8 @@ frame_params->frame_type = KEY_FRAME; find_next_key_frame(cpi, &this_frame); this_frame = this_frame_copy; + // Mark prev gop arf source as unusable + cpi->ppi->tpl_data.prev_gop_arf_disp_order = -1; } if (rc->frames_to_fwd_kf <= 0) @@ -3924,7 +4182,6 @@ if (rc->intervals_till_gf_calculate_due == 0 || 1) { calculate_gf_length(cpi, max_gop_length, MAX_NUM_GF_INTERVALS); } - if (max_gop_length > 16 && oxcf->algo_cfg.enable_tpl_model && oxcf->gf_cfg.lag_in_frames >= 32 && cpi->sf.tpl_sf.gop_length_decision_method != 3) { @@ -4274,7 +4531,7 @@ int maxq_adj_limit; minq_adj_limit = (rc_cfg->mode == AOM_CQ ? MINQ_ADJ_LIMIT_CQ : MINQ_ADJ_LIMIT); - maxq_adj_limit = (rc->worst_quality - rc->active_worst_quality); + maxq_adj_limit = rc->worst_quality - rc->active_worst_quality; // Undershoot if ((rc_cfg->under_shoot_pct < 100) && @@ -4286,9 +4543,8 @@ if ((pct_error >= rc_cfg->under_shoot_pct) && (p_rc->rate_error_estimate > 0)) { twopass->extend_minq += 1; - twopass->extend_maxq -= 1; } - + twopass->extend_maxq -= 1; // Overshoot } else if ((rc_cfg->over_shoot_pct < 100) && (p_rc->rolling_actual_bits > p_rc->rolling_target_bits)) { @@ -4300,8 +4556,18 @@ if ((pct_error >= rc_cfg->over_shoot_pct) && (p_rc->rate_error_estimate < 0)) { twopass->extend_maxq += 1; - twopass->extend_minq -= 1; } + twopass->extend_minq -= 1; + } else { + // Adjustment for extreme local overshoot. + // Only applies when normal adjustment above is not used (e.g. + // when threshold is set to 100). + if (rc->projected_frame_size > (2 * rc->base_frame_target) && + rc->projected_frame_size > (2 * rc->avg_frame_bandwidth)) + ++twopass->extend_maxq; + // Unwind extreme overshoot adjustment. + else if (p_rc->rolling_target_bits > p_rc->rolling_actual_bits) + --twopass->extend_maxq; } twopass->extend_minq = clamp(twopass->extend_minq, -minq_adj_limit, minq_adj_limit);
diff --git a/av1/encoder/pass2_strategy.h b/av1/encoder/pass2_strategy.h index 1ca39ec..be14d8e 100644 --- a/av1/encoder/pass2_strategy.h +++ b/av1/encoder/pass2_strategy.h
@@ -109,7 +109,7 @@ const PRIMARY_RATE_CONTROL *p_rc, FRAME_INFO *frame_info, int offset, int f_frames, int b_frames, int *num_fpstats_used, int *num_fpstats_required, - int project_gfu_boost); + int project_gfu_boost, const bool scale_max_boost); void av1_gop_bit_allocation(const AV1_COMP *cpi, RATE_CONTROL *const rc, GF_GROUP *gf_group, int is_key_frame, int use_arf,
diff --git a/av1/encoder/pickcdef.c b/av1/encoder/pickcdef.c index 27aea1d..d498c06 100644 --- a/av1/encoder/pickcdef.c +++ b/av1/encoder/pickcdef.c
@@ -730,7 +730,7 @@ } void av1_pick_cdef_from_qp(AV1_COMMON *const cm, int skip_cdef, - int is_screen_content) { + int is_screen_content, bool avoid_uv_cdef) { const int bd = cm->seq_params->bit_depth; const int q = av1_ac_quant_QTX(cm->quant_params.base_qindex, 0, bd) >> (bd - 8); @@ -794,7 +794,8 @@ cdef_info->cdef_strengths[0] = predicted_y_f1 * CDEF_SEC_STRENGTHS + predicted_y_f2; cdef_info->cdef_uv_strengths[0] = - predicted_uv_f1 * CDEF_SEC_STRENGTHS + predicted_uv_f2; + avoid_uv_cdef ? 0 + : predicted_uv_f1 * CDEF_SEC_STRENGTHS + predicted_uv_f2; // mbmi->cdef_strength is already set in the encoding stage. We don't need to // set it again here. @@ -822,15 +823,16 @@ void av1_cdef_search(AV1_COMP *cpi) { AV1_COMMON *cm = &cpi->common; CDEF_CONTROL cdef_control = cpi->oxcf.tool_cfg.cdef_control; + const bool apply_adaptive_cdef = + cdef_control == CDEF_ADAPTIVE && + (cpi->oxcf.rc_cfg.mode == AOM_Q || cpi->oxcf.rc_cfg.mode == AOM_CQ); assert(cdef_control != CDEF_NONE); // For CDEF_ADAPTIVE, turning off CDEF around qindex 32 was best for still // pictures if ((cdef_control == CDEF_REFERENCE && cpi->ppi->rtc_ref.non_reference_frame) || - (cdef_control == CDEF_ADAPTIVE && cpi->oxcf.mode == ALLINTRA && - (cpi->oxcf.rc_cfg.mode == AOM_Q || cpi->oxcf.rc_cfg.mode == AOM_CQ) && - cpi->oxcf.rc_cfg.cq_level <= 32)) { + (apply_adaptive_cdef && cpi->oxcf.rc_cfg.cq_level <= 32)) { CdefInfo *const cdef_info = &cm->cdef_info; cdef_info->nb_cdef_strengths = 1; cdef_info->cdef_bits = 0; @@ -842,7 +844,8 @@ // Indicate if external RC is used for testing const int rtc_ext_rc = cpi->rc.rtc_external_ratectrl; if (rtc_ext_rc) { - av1_pick_cdef_from_qp(cm, 0, 0); + av1_pick_cdef_from_qp(cm, /*skip_cdef=*/0, /*is_screen_content=*/0, + /*avoid_uv_cdef=*/false); return; } CDEF_PICK_METHOD pick_method = cpi->sf.lpf_sf.cdef_pick_method; @@ -852,8 +855,14 @@ AOMMAX(cpi->sf.rt_sf.screen_content_cdef_filter_qindex_thresh, cpi->rc.best_quality + 5) && cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN; - av1_pick_cdef_from_qp(cm, cpi->sf.rt_sf.skip_cdef_sb, - use_screen_content_model); + + // For adaptive CDEF, do not apply CDEF to chroma channels. + // This is done to reduce decode time, as CDEF is a relatively-expensive + // filter to compute. + const bool avoid_uv_cdef = apply_adaptive_cdef; + + av1_pick_cdef_from_qp(cm, cpi->sf.rt_sf.skip_cdef_sb != 0, + use_screen_content_model, avoid_uv_cdef); return; } const CommonModeInfoParams *const mi_params = &cm->mi_params; @@ -896,7 +905,25 @@ const int max_signaling_bits = joint_strengths == 1 ? 0 : get_msb(joint_strengths - 1) + 1; int rdmult = cpi->td.mb.rdmult; - for (int i = 0; i <= 3; i++) { + + // For adaptive CDEF, reduce primary and secondary CDEF strengths for + // qindexes up to 220. + const bool should_reduce_cdef_strengths = + apply_adaptive_cdef && cpi->oxcf.rc_cfg.cq_level <= 220; + // For adaptive CDEF with strength reduction, zero out CDEF strengths with + // low values (luma and/or chroma). This is done to reduce decode time, as + // CDEF is a relatively-expensive filter to compute. + const bool should_zero_cdef_strengths = + should_reduce_cdef_strengths && cpi->sf.lpf_sf.zero_low_cdef_strengths; + // If running adaptive CDEF with strength zeroing, let search derive at least + // two CDEF strengths (i.e. at least 1 CDEF signaling bit), unless search was + // explicitly set to search for 1 strength only (i.e. 0 CDEF signaling bits). + // Doing so will help find opportunities to zero out low strengths to reduce + // overall decode time. + const int min_signaling_bits = + (should_zero_cdef_strengths && max_signaling_bits > 0) ? 1 : 0; + + for (int i = min_signaling_bits; i <= 3; i++) { if (i > max_signaling_bits) break; int best_lev0[CDEF_MAX_STRENGTHS] = { 0 }; int best_lev1[CDEF_MAX_STRENGTHS] = { 0 }; @@ -946,18 +973,19 @@ if (fast) { for (int j = 0; j < cdef_info->nb_cdef_strengths; j++) { const int luma_strength = cdef_info->cdef_strengths[j]; - const int chroma_strength = cdef_info->cdef_uv_strengths[j]; int pri_strength, sec_strength; STORE_CDEF_FILTER_STRENGTH(cdef_info->cdef_strengths[j], pick_method, luma_strength); - STORE_CDEF_FILTER_STRENGTH(cdef_info->cdef_uv_strengths[j], pick_method, - chroma_strength); + if (num_planes > 1) { + const int chroma_strength = cdef_info->cdef_uv_strengths[j]; + STORE_CDEF_FILTER_STRENGTH(cdef_info->cdef_uv_strengths[j], pick_method, + chroma_strength); + } } } - // For CDEF_ADAPTIVE, set primary and secondary CDEF at reduced strength for - // qindexes 33 through 220. + // Perform CDEF strength reduction. // Note 1: for odd strengths, the 0.5 discarded by ">> 1" is a significant // part of the strength when the strength is small, and because there are // few strength levels, odd strengths are reduced significantly more than a @@ -970,27 +998,54 @@ // resulting SSIMULACRA 2 scores were either exactly the same (at cpu-used 6), // or within noise level (at cpu-used 3). Given that there were no discernible // improvements, this special mapping was left out for reduced strength. - if (cdef_control == CDEF_ADAPTIVE && cpi->oxcf.mode == ALLINTRA && - (cpi->oxcf.rc_cfg.mode == AOM_Q || cpi->oxcf.rc_cfg.mode == AOM_CQ) && - cpi->oxcf.rc_cfg.cq_level <= 220) { + if (should_reduce_cdef_strengths) { for (int j = 0; j < cdef_info->nb_cdef_strengths; j++) { const int luma_strength = cdef_info->cdef_strengths[j]; - const int chroma_strength = cdef_info->cdef_uv_strengths[j]; - const int new_pri_luma_strength = (luma_strength / CDEF_SEC_STRENGTHS) >> 1; const int new_sec_luma_strength = (luma_strength % CDEF_SEC_STRENGTHS) >> 1; - const int new_pri_chroma_strength = - (chroma_strength / CDEF_SEC_STRENGTHS) >> 1; - const int new_sec_chroma_strength = - (chroma_strength % CDEF_SEC_STRENGTHS) >> 1; cdef_info->cdef_strengths[j] = new_pri_luma_strength * CDEF_SEC_STRENGTHS + new_sec_luma_strength; - cdef_info->cdef_uv_strengths[j] = - new_pri_chroma_strength * CDEF_SEC_STRENGTHS + - new_sec_chroma_strength; + + int new_pri_chroma_strength; + int new_sec_chroma_strength; + + if (num_planes > 1) { + const int chroma_strength = cdef_info->cdef_uv_strengths[j]; + new_pri_chroma_strength = (chroma_strength / CDEF_SEC_STRENGTHS) >> 1; + new_sec_chroma_strength = (chroma_strength % CDEF_SEC_STRENGTHS) >> 1; + + cdef_info->cdef_uv_strengths[j] = + new_pri_chroma_strength * CDEF_SEC_STRENGTHS + + new_sec_chroma_strength; + } + + // Zero out entries with low CDEF luma (and optional chroma) strengths. + // The low-strength thresholds were empirically derived from subjective + // testing and SSIMULACRA 2 scores. These strike a balance between + // perceptual quality gains and a reasonable single-threaded decode time + // increase (~10%) over --enable-cdef 0. There's an overall 0.18 point + // loss in SSIMULACRA 2 scores over no CDEF strength zeroing at speed 6, + // QP 30 on the CLIC 2020 dataset. + if (should_zero_cdef_strengths) { + const bool is_low_luma_strength = + new_pri_luma_strength <= 4 && new_sec_luma_strength <= 1; + + if (is_low_luma_strength) { + cdef_info->cdef_strengths[j] = 0; + } + if (num_planes > 1) { + const bool is_low_chroma_strength = + new_pri_chroma_strength <= 4 && new_sec_chroma_strength <= 1; + + if (is_low_luma_strength || is_low_chroma_strength) { + // Disable CDEF on chroma if we've disabled it on luma + cdef_info->cdef_uv_strengths[j] = 0; + } + } + } } }
diff --git a/av1/encoder/pickcdef.h b/av1/encoder/pickcdef.h index 6056ad9..70fc0e2 100644 --- a/av1/encoder/pickcdef.h +++ b/av1/encoder/pickcdef.h
@@ -251,10 +251,11 @@ * \param[in,out] cm Pointer to top level common structure * \param[in] skip_cdef Flag to skip CDEF filtering * \param[in] is_screen_content Flag indicating screen content + * \param[in] avoid_uv_cdef Flag to avoid assigning UV CDEF strengths * */ void av1_pick_cdef_from_qp(AV1_COMMON *const cm, int skip_cdef, - int is_screen_content); + int is_screen_content, bool avoid_uv_cdef); #ifdef __cplusplus } // extern "C"
diff --git a/av1/encoder/picklpf.c b/av1/encoder/picklpf.c index bc776d7..d7333aa 100644 --- a/av1/encoder/picklpf.c +++ b/av1/encoder/picklpf.c
@@ -217,10 +217,17 @@ int disable_filter_rt_screen = 0; (void)sd; - // Enable loop filter sharpness only for allintra encoding mode, - // as frames do not have to serve as references to others - lf->sharpness_level = - cpi->oxcf.mode == ALLINTRA ? cpi->oxcf.algo_cfg.sharpness : 0; + // Enable loop filter sharpness only for all-intra encoding mode, + // or tune IQ or SSIMULACRA2. This is because: + // - All-intra: frames do not have to serve as references to others + // - Tune IQ/SSIMULACRA2: enabling loop filter sharpness has been found to + // be beneficial for sharpness perception + if (cpi->oxcf.mode == ALLINTRA || cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { + lf->sharpness_level = cpi->oxcf.algo_cfg.sharpness; + } else { + lf->sharpness_level = 0; + } if (cpi->oxcf.algo_cfg.enable_adaptive_sharpness) { // Loop filter sharpness levels are highly nonlinear. Visually, lf sharpness @@ -228,7 +235,7 @@ // written to pick levels 0, 1 and 7 to keep it simple. int max_lf_sharpness; - if (cm->quant_params.base_qindex <= 120) { + if (cm->quant_params.base_qindex <= 112) { max_lf_sharpness = 7; } else if (cm->quant_params.base_qindex <= 160) { max_lf_sharpness = 1; @@ -409,9 +416,7 @@ unsigned int pyramid_level = cm->current_frame.pyramid_level; if (pyramid_level > 1) { int filter_threshold; - if (pyramid_level >= 5) - filter_threshold = 32; - else if (pyramid_level >= 4) + if (pyramid_level >= 4) filter_threshold = 16; else filter_threshold = 8;
diff --git a/av1/encoder/pickrst.c b/av1/encoder/pickrst.c index 3913d58..cd79ed9 100644 --- a/av1/encoder/pickrst.c +++ b/av1/encoder/pickrst.c
@@ -848,25 +848,27 @@ flt0, flt1, flt_stride, exqd, &err, error_info); get_best_error(&besterr, err, exqd, bestxqd, &bestep, ep); } - // evaluate left and right ep of winner in seed ep - int bestep_ref = bestep; - for (ep = bestep_ref - 1; ep < bestep_ref + 2; ep += 2) { - if (ep < SGRPROJ_EP_GRP1_START_IDX || ep > SGRPROJ_EP_GRP1_END_IDX) - continue; - int64_t err; - compute_sgrproj_err(dat8, width, height, dat_stride, src8, src_stride, - use_highbitdepth, bit_depth, pu_width, pu_height, ep, - flt0, flt1, flt_stride, exqd, &err, error_info); - get_best_error(&besterr, err, exqd, bestxqd, &bestep, ep); - } - // evaluate last two group - for (idx = 0; idx < SGRPROJ_EP_GRP2_3_SEARCH_COUNT; idx++) { - ep = sgproj_ep_grp2_3[idx][bestep]; - int64_t err; - compute_sgrproj_err(dat8, width, height, dat_stride, src8, src_stride, - use_highbitdepth, bit_depth, pu_width, pu_height, ep, - flt0, flt1, flt_stride, exqd, &err, error_info); - get_best_error(&besterr, err, exqd, bestxqd, &bestep, ep); + if (enable_sgr_ep_pruning < 2) { + // evaluate left and right ep of winner in seed ep + int bestep_ref = bestep; + for (ep = bestep_ref - 1; ep < bestep_ref + 2; ep += 2) { + if (ep < SGRPROJ_EP_GRP1_START_IDX || ep > SGRPROJ_EP_GRP1_END_IDX) + continue; + int64_t err; + compute_sgrproj_err(dat8, width, height, dat_stride, src8, src_stride, + use_highbitdepth, bit_depth, pu_width, pu_height, + ep, flt0, flt1, flt_stride, exqd, &err, error_info); + get_best_error(&besterr, err, exqd, bestxqd, &bestep, ep); + } + // evaluate last two group + for (idx = 0; idx < SGRPROJ_EP_GRP2_3_SEARCH_COUNT; idx++) { + ep = sgproj_ep_grp2_3[idx][bestep]; + int64_t err; + compute_sgrproj_err(dat8, width, height, dat_stride, src8, src_stride, + use_highbitdepth, bit_depth, pu_width, pu_height, + ep, flt0, flt1, flt_stride, exqd, &err, error_info); + get_best_error(&besterr, err, exqd, bestxqd, &bestep, ep); + } } }
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c index 1b55fe2..491e7a5 100644 --- a/av1/encoder/ratectrl.c +++ b/av1/encoder/ratectrl.c
@@ -41,6 +41,9 @@ #define USE_UNRESTRICTED_Q_IN_CQ_MODE 0 +#define RES_NUM 2 +#define MODE_NUM 2 + // Max rate target for 1080P and below encodes under normal circumstances // (1920 * 1080 / (16 * 16)) * MAX_MB_RATE bits per MB #define MAX_MB_RATE 250 @@ -68,35 +71,53 @@ } \ } while (0) +#define ASSIGN_MINQ_TABLE_2(bit_depth, name, res_idx, mode_idx) \ + do { \ + switch (bit_depth) { \ + case AOM_BITS_8: name = name##_8[mode_idx][res_idx]; break; \ + case AOM_BITS_10: name = name##_10[mode_idx][res_idx]; break; \ + case AOM_BITS_12: name = name##_12[mode_idx][res_idx]; break; \ + default: \ + assert(0 && \ + "bit_depth should be AOM_BITS_8, AOM_BITS_10" \ + " or AOM_BITS_12"); \ + name = NULL; \ + } \ + } while (0) + // Tables relating active max Q to active min Q -static int kf_low_motion_minq_8[QINDEX_RANGE]; -static int kf_high_motion_minq_8[QINDEX_RANGE]; -static int arfgf_low_motion_minq_8[QINDEX_RANGE]; -static int arfgf_high_motion_minq_8[QINDEX_RANGE]; -static int inter_minq_8[QINDEX_RANGE]; +static int kf_low_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int kf_high_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_low_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_high_motion_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int inter_minq_8[MODE_NUM][RES_NUM][QINDEX_RANGE]; static int rtc_minq_8[QINDEX_RANGE]; -static int kf_low_motion_minq_10[QINDEX_RANGE]; -static int kf_high_motion_minq_10[QINDEX_RANGE]; -static int arfgf_low_motion_minq_10[QINDEX_RANGE]; -static int arfgf_high_motion_minq_10[QINDEX_RANGE]; -static int inter_minq_10[QINDEX_RANGE]; +static int kf_low_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int kf_high_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_low_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_high_motion_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int inter_minq_10[MODE_NUM][RES_NUM][QINDEX_RANGE]; static int rtc_minq_10[QINDEX_RANGE]; -static int kf_low_motion_minq_12[QINDEX_RANGE]; -static int kf_high_motion_minq_12[QINDEX_RANGE]; -static int arfgf_low_motion_minq_12[QINDEX_RANGE]; -static int arfgf_high_motion_minq_12[QINDEX_RANGE]; -static int inter_minq_12[QINDEX_RANGE]; +static int kf_low_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int kf_high_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_low_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int arfgf_high_motion_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; +static int inter_minq_12[MODE_NUM][RES_NUM][QINDEX_RANGE]; static int rtc_minq_12[QINDEX_RANGE]; -static int gf_high = 2400; -static int gf_low = 300; -#ifdef STRICT_RC -static int kf_high = 3200; -#else -static int kf_high = 5000; -#endif -static int kf_low = 400; +static int gf_high_1 = 2875; +static int gf_low_1 = 562; +static int gf_high_2 = 4994; +static int gf_low_2 = 100; + +static int kf_high = 8000; +static int kf_low = 553; + +static int gf_high_rtc = 2400; +static int gf_low_rtc = 300; +static int kf_high_rtc = 5000; +static int kf_low_rtc = 400; // How many times less pixels there are to encode given the current scaling. // Temporary replacement for rcf_mult and rate_thresh_mult. @@ -120,18 +141,41 @@ return av1_find_qindex(minqtarget, bit_depth, 0, QINDEX_RANGE - 1); } -static void init_minq_luts(int *kf_low_m, int *kf_high_m, int *arfgf_low, - int *arfgf_high, int *inter, int *rtc, +static double x1[MODE_NUM][RES_NUM][5] = { + { + { 0.1771, 0.379, 0.3279, 0.6634, 1.385 }, + { 0.1917, 0.3760, 0.34570, 0.6916, 1.14820 }, + }, + { + { 0.15, 0.45, 0.30, 0.55, 0.90 }, + { 0.15, 0.45, 0.30, 0.55, 0.90 }, + }, +}; + +static void init_minq_luts(int kf_low_m[MODE_NUM][RES_NUM][QINDEX_RANGE], + int kf_high_m[MODE_NUM][RES_NUM][QINDEX_RANGE], + int arfgf_low[MODE_NUM][RES_NUM][QINDEX_RANGE], + int arfgf_high[MODE_NUM][RES_NUM][QINDEX_RANGE], + int inter[MODE_NUM][RES_NUM][QINDEX_RANGE], int *rtc, aom_bit_depth_t bit_depth) { int i; - for (i = 0; i < QINDEX_RANGE; i++) { - const double maxq = av1_convert_qindex_to_q(i, bit_depth); - kf_low_m[i] = get_minq_index(maxq, 0.000001, -0.0004, 0.150, bit_depth); - kf_high_m[i] = get_minq_index(maxq, 0.0000021, -0.00125, 0.45, bit_depth); - arfgf_low[i] = get_minq_index(maxq, 0.0000015, -0.0009, 0.30, bit_depth); - arfgf_high[i] = get_minq_index(maxq, 0.0000021, -0.00125, 0.55, bit_depth); - inter[i] = get_minq_index(maxq, 0.00000271, -0.00113, 0.90, bit_depth); - rtc[i] = get_minq_index(maxq, 0.00000271, -0.00113, 0.70, bit_depth); + for (int mode = 0; mode < MODE_NUM; mode++) { + for (int res = 0; res < RES_NUM; res++) { + for (i = 0; i < QINDEX_RANGE; i++) { + const double maxq = av1_convert_qindex_to_q(i, bit_depth); + kf_low_m[mode][res][i] = get_minq_index(maxq, 0.000001, -0.0004, + x1[mode][res][0], bit_depth); + kf_high_m[mode][res][i] = get_minq_index(maxq, 0.0000021, -0.00125, + x1[mode][res][1], bit_depth); + arfgf_low[mode][res][i] = get_minq_index(maxq, 0.0000015, -0.0009, + x1[mode][res][2], bit_depth); + arfgf_high[mode][res][i] = get_minq_index(maxq, 0.0000021, -0.00125, + x1[mode][res][3], bit_depth); + inter[mode][res][i] = get_minq_index(maxq, 0.00000271, -0.00113, + x1[mode][res][4], bit_depth); + rtc[i] = get_minq_index(maxq, 0.00000271, -0.00113, 0.70, bit_depth); + } + } } } @@ -499,6 +543,7 @@ rc->force_max_q = 0; rc->postencode_drop = 0; rc->frames_since_scene_change = 0; + rc->last_frame_low_source_sad = 0; } static bool check_buffer_below_thresh(AV1_COMP *cpi, int64_t buffer_level, @@ -1122,34 +1167,58 @@ } } +static int gfboost_thresh[3] = { 4000, 4000, 3000 }; + static int get_kf_active_quality(const PRIMARY_RATE_CONTROL *const p_rc, int q, - aom_bit_depth_t bit_depth) { + aom_bit_depth_t bit_depth, const int res_idx, + const bool rtc_mode) { int *kf_low_motion_minq; int *kf_high_motion_minq; - ASSIGN_MINQ_TABLE(bit_depth, kf_low_motion_minq); - ASSIGN_MINQ_TABLE(bit_depth, kf_high_motion_minq); - return get_active_quality(q, p_rc->kf_boost, kf_low, kf_high, + ASSIGN_MINQ_TABLE_2(bit_depth, kf_low_motion_minq, res_idx > 1, rtc_mode); + ASSIGN_MINQ_TABLE_2(bit_depth, kf_high_motion_minq, res_idx > 1, rtc_mode); + + int kf_low_local = rtc_mode ? kf_low_rtc : kf_low; + int kf_high_local = rtc_mode ? kf_high_rtc : kf_high; + + return get_active_quality(q, p_rc->kf_boost, kf_low_local, kf_high_local, kf_low_motion_minq, kf_high_motion_minq); } -static int get_gf_active_quality_no_rc(int gfu_boost, int q, - aom_bit_depth_t bit_depth) { +static int get_gf_active_quality_no_rc(const PRIMARY_RATE_CONTROL *const p_rc, + int q, aom_bit_depth_t bit_depth, + const int res_idx, const bool rtc_mode) { int *arfgf_low_motion_minq; int *arfgf_high_motion_minq; - ASSIGN_MINQ_TABLE(bit_depth, arfgf_low_motion_minq); - ASSIGN_MINQ_TABLE(bit_depth, arfgf_high_motion_minq); - return get_active_quality(q, gfu_boost, gf_low, gf_high, + ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_low_motion_minq, res_idx > 1, rtc_mode); + ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_high_motion_minq, res_idx > 1, rtc_mode); + int gf_low_local, gf_high_local; + + if (!rtc_mode) { + gf_low_local = (p_rc->gfu_boost_average < gfboost_thresh[res_idx]) + ? gf_low_1 + : gf_low_2; + gf_high_local = (p_rc->gfu_boost_average < gfboost_thresh[res_idx]) + ? gf_high_1 + : gf_high_2; + } else { + gf_low_local = gf_low_rtc; + gf_high_local = gf_high_rtc; + } + + return get_active_quality(q, p_rc->gfu_boost, gf_low_local, gf_high_local, arfgf_low_motion_minq, arfgf_high_motion_minq); } static int get_gf_active_quality(const PRIMARY_RATE_CONTROL *const p_rc, int q, - aom_bit_depth_t bit_depth) { - return get_gf_active_quality_no_rc(p_rc->gfu_boost, q, bit_depth); + aom_bit_depth_t bit_depth, const int res_idx, + const bool rtc_mode) { + return get_gf_active_quality_no_rc(p_rc, q, bit_depth, res_idx, rtc_mode); } -static int get_gf_high_motion_quality(int q, aom_bit_depth_t bit_depth) { +static int get_gf_high_motion_quality(int q, aom_bit_depth_t bit_depth, + const int res_idx, const bool rtc_mode) { int *arfgf_high_motion_minq; - ASSIGN_MINQ_TABLE(bit_depth, arfgf_high_motion_minq); + ASSIGN_MINQ_TABLE_2(bit_depth, arfgf_high_motion_minq, res_idx > 1, rtc_mode); return arfgf_high_motion_minq[q]; } @@ -1209,7 +1278,26 @@ int adjustment = 0; int active_worst_quality; int ambient_qp; - if (frame_is_intra_only(cm)) return rc->worst_quality; + if (frame_is_intra_only(cm)) { + // Allow for active_worst_quality to go lower than rc->worst_quality (max) + // under certain conditions: that the frame spatial variance is below + // threshold and the buffer is full/stable. Also check the encoded vs target + // size for the last keyframe. + if (cpi->sf.rt_sf.rc_compute_spatial_var_sc_kf && + svc->number_spatial_layers == 1 && rc->frame_spatial_variance < 1000 && + p_rc->buffer_level > p_rc->optimal_buffer_level && + p_rc->optimal_buffer_level > (rc->avg_frame_bandwidth << 3) && + rc->last_encoded_size_keyframe < (rc->last_target_size_keyframe << 3)) { + if (p_rc->buffer_level > ((9 * p_rc->optimal_buffer_level) >> 3)) + ambient_qp = p_rc->avg_frame_qindex[INTER_FRAME]; + else + ambient_qp = + (rc->worst_quality + p_rc->avg_frame_qindex[INTER_FRAME]) >> 1; + return AOMMIN(rc->worst_quality, AOMMAX(ambient_qp, rc->best_quality)); + } else { + return rc->worst_quality; + } + } // For ambient_qp we use minimum of avg_frame_qindex[KEY_FRAME/INTER_FRAME] // for the first few frames following key frame. These are both initialized // to worst_quality and updated with (3/4, 1/4) average in postencode_update. @@ -1290,6 +1378,12 @@ int active_best_quality = rc->best_quality; ASSIGN_MINQ_TABLE(bit_depth, rtc_minq); + const int is_608p_or_larger = AOMMIN(cm->width, cm->height) >= 608; + const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; + // res_idx is 0 for res < 480p, 1 for 480p, 2 for 608p+ + const int res_idx = is_480p_or_larger + is_608p_or_larger; + const bool rtc_mode = (cpi->oxcf.mode == REALTIME); + if (frame_is_intra_only(cm)) { // Handle the special case for key frames forced when we have reached // the maximum key frame interval. Here force the Q to a range @@ -1304,8 +1398,9 @@ // not first frame of one pass and kf_boost is set double q_adj_factor = 1.0; double q_val; - active_best_quality = get_kf_active_quality( - p_rc, p_rc->avg_frame_qindex[KEY_FRAME], bit_depth); + active_best_quality = + get_kf_active_quality(p_rc, p_rc->avg_frame_qindex[KEY_FRAME], + bit_depth, res_idx, rtc_mode); // Allow somewhat lower kf minq with small image formats. if ((width * height) <= (352 * 288)) { q_adj_factor -= 0.25; @@ -1327,7 +1422,8 @@ p_rc->avg_frame_qindex[INTER_FRAME] < active_worst_quality) { q = p_rc->avg_frame_qindex[INTER_FRAME]; } - active_best_quality = get_gf_active_quality(p_rc, q, bit_depth); + active_best_quality = + get_gf_active_quality(p_rc, q, bit_depth, res_idx, rtc_mode); } else { // Use the lower of active_worst_quality and recent/average Q. FRAME_TYPE frame_type = @@ -1499,6 +1595,12 @@ const RefreshFrameInfo *const refresh_frame = &cpi->refresh_frame; const enum aom_rc_mode rc_mode = oxcf->rc_cfg.mode; + const int is_608p_or_larger = AOMMIN(cm->width, cm->height) >= 608; + const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; + // res_idx is 0 for res < 480p, 1 for 480p, 2 for 608p+ + const int res_idx = is_480p_or_larger + is_608p_or_larger; + const bool rtc_mode = (cpi->oxcf.mode == REALTIME); + assert(has_no_stats_stage(cpi)); assert(rc_mode == AOM_VBR || (!USE_UNRESTRICTED_Q_IN_CQ_MODE && rc_mode == AOM_CQ) || @@ -1513,7 +1615,7 @@ int active_worst_quality = calc_active_worst_quality_no_stats_vbr(cpi); int q; int *inter_minq; - ASSIGN_MINQ_TABLE(bit_depth, inter_minq); + ASSIGN_MINQ_TABLE_2(bit_depth, inter_minq, res_idx > 1, rtc_mode); if (frame_is_intra_only(cm)) { if (rc_mode == AOM_Q) { @@ -1539,8 +1641,9 @@ } else { // not first frame of one pass and kf_boost is set double q_adj_factor = 1.0; - active_best_quality = get_kf_active_quality( - p_rc, p_rc->avg_frame_qindex[KEY_FRAME], bit_depth); + active_best_quality = + get_kf_active_quality(p_rc, p_rc->avg_frame_qindex[KEY_FRAME], + bit_depth, res_idx, rtc_mode); // Allow somewhat lower kf minq with small image formats. if ((width * height) <= (352 * 288)) { @@ -1567,7 +1670,8 @@ // For constrained quality don't allow Q less than the cq level if (rc_mode == AOM_CQ) { if (q < cq_level) q = cq_level; - active_best_quality = get_gf_active_quality(p_rc, q, bit_depth); + active_best_quality = + get_gf_active_quality(p_rc, q, bit_depth, res_idx, rtc_mode); // Constrained quality use slightly lower active best. active_best_quality = active_best_quality * 15 / 16; } else if (rc_mode == AOM_Q) { @@ -1579,7 +1683,8 @@ : av1_compute_qdelta(rc, q_val, q_val * 0.50, bit_depth); active_best_quality = AOMMAX(qindex + delta_qindex, rc->best_quality); } else { - active_best_quality = get_gf_active_quality(p_rc, q, bit_depth); + active_best_quality = + get_gf_active_quality(p_rc, q, bit_depth, res_idx, rtc_mode); } } else { if (rc_mode == AOM_Q) { @@ -1717,6 +1822,12 @@ int active_worst_quality = *active_worst; const int bit_depth = cm->seq_params->bit_depth; + const int is_608p_or_larger = AOMMIN(cm->width, cm->height) >= 608; + const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; + // res_idx is 0 for res < 480p, 1 for 480p, 2 for 608p+ + const int res_idx = is_480p_or_larger + is_608p_or_larger; + const bool rtc_mode = (cpi->oxcf.mode == REALTIME); + if (rc->frames_to_key <= 1 && oxcf->rc_cfg.mode == AOM_Q) { // If the next frame is also a key frame or the current frame is the // only frame in the sequence in AOM_Q mode, just use the cq_level @@ -1762,8 +1873,8 @@ double q_val; // Baseline value derived from active_worst_quality and kf boost. - active_best_quality = - get_kf_active_quality(p_rc, active_worst_quality, bit_depth); + active_best_quality = get_kf_active_quality(p_rc, active_worst_quality, + bit_depth, res_idx, rtc_mode); if (cpi->is_screen_content_type) { active_best_quality /= 2; } @@ -1843,8 +1954,8 @@ } #else (void)is_intrl_arf_boost; - active_best_quality -= cpi->ppi->twopass.extend_minq / 8; - active_worst_quality += cpi->ppi->twopass.extend_maxq / 4; + active_best_quality -= cpi->ppi->twopass.extend_minq / 4; + active_worst_quality += cpi->ppi->twopass.extend_maxq; #endif } @@ -1953,14 +2064,20 @@ const RefreshFrameInfo *const refresh_frame = &cpi->refresh_frame; const GF_GROUP *gf_group = &cpi->ppi->gf_group; const enum aom_rc_mode rc_mode = oxcf->rc_cfg.mode; - int *inter_minq; - ASSIGN_MINQ_TABLE(bit_depth, inter_minq); int active_best_quality = 0; - const int is_intrl_arf_boost = - gf_group->update_type[gf_index] == INTNL_ARF_UPDATE; - int is_leaf_frame = - !(gf_group->update_type[gf_index] == ARF_UPDATE || - gf_group->update_type[gf_index] == GF_UPDATE || is_intrl_arf_boost); + FRAME_UPDATE_TYPE update_type = gf_group->update_type[gf_index]; + const int is_intrl_arf_boost = update_type == INTNL_ARF_UPDATE; + int is_leaf_frame = !(update_type == ARF_UPDATE || update_type == GF_UPDATE || + is_intrl_arf_boost); + + const int is_608p_or_larger = AOMMIN(cm->width, cm->height) >= 608; + const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; + // res_idx is 0 for res < 480p, 1 for 480p, 2 for 608p+ + const int res_idx = is_480p_or_larger + is_608p_or_larger; + const bool rtc_mode = (cpi->oxcf.mode == REALTIME); + + int *inter_minq; + ASSIGN_MINQ_TABLE_2(bit_depth, inter_minq, res_idx > 1, rtc_mode); // TODO(jingning): Consider to rework this hack that covers issues incurred // in lightfield setting. @@ -1968,7 +2085,8 @@ is_leaf_frame = !(refresh_frame->golden_frame || refresh_frame->alt_ref_frame || is_intrl_arf_boost); } - const int is_overlay_frame = rc->is_src_frame_alt_ref; + const int is_overlay_frame = + update_type == OVERLAY_UPDATE || update_type == INTNL_OVERLAY_UPDATE; if (is_leaf_frame || is_overlay_frame) { if (rc_mode == AOM_Q) return cq_level; @@ -1992,10 +2110,12 @@ q = p_rc->avg_frame_qindex[INTER_FRAME]; } if (rc_mode == AOM_CQ && q < cq_level) q = cq_level; - active_best_quality = get_gf_active_quality(p_rc, q, bit_depth); + active_best_quality = + get_gf_active_quality(p_rc, q, bit_depth, res_idx, rtc_mode); // Constrained quality use slightly lower active best. if (rc_mode == AOM_CQ) active_best_quality = active_best_quality * 15 / 16; - const int min_boost = get_gf_high_motion_quality(q, bit_depth); + const int min_boost = + get_gf_high_motion_quality(q, bit_depth, res_idx, rtc_mode); const int boost = min_boost - active_best_quality; active_best_quality = min_boost - (int)(boost * p_rc->arf_boost_factor); if (!is_intrl_arf_boost) return active_best_quality; @@ -2461,6 +2581,10 @@ } if (cpi->refresh_frame.golden_frame) rc->frame_num_last_gf_refresh = current_frame->frame_number; + + if (rc->frame_source_sad < 10000) + rc->last_frame_low_source_sad = rc->frame_number_encoded; + rc->prev_coded_width = cm->width; rc->prev_coded_height = cm->height; rc->frame_number_encoded++; @@ -2567,38 +2691,30 @@ static void set_gf_interval_range(const AV1_COMP *const cpi, RATE_CONTROL *const rc) { const AV1EncoderConfig *const oxcf = &cpi->oxcf; + // Set Maximum gf/arf interval + rc->max_gf_interval = oxcf->gf_cfg.max_gf_interval; + rc->min_gf_interval = oxcf->gf_cfg.min_gf_interval; + if (rc->min_gf_interval == 0) + rc->min_gf_interval = av1_rc_get_default_min_gf_interval( + oxcf->frm_dim_cfg.width, oxcf->frm_dim_cfg.height, cpi->framerate); + if (rc->max_gf_interval == 0) + rc->max_gf_interval = + get_default_max_gf_interval(cpi->framerate, rc->min_gf_interval); + /* + * Extended max interval for genuinely static scenes like slide shows. + * The no.of.stats available in the case of LAP is limited, + * hence setting to max_gf_interval. + */ + if (cpi->ppi->lap_enabled) + rc->static_scene_max_gf_interval = rc->max_gf_interval + 1; + else + rc->static_scene_max_gf_interval = MAX_STATIC_GF_GROUP_LENGTH; - // Special case code for 1 pass fixed Q mode tests - if ((has_no_stats_stage(cpi)) && (oxcf->rc_cfg.mode == AOM_Q)) { - rc->max_gf_interval = oxcf->gf_cfg.max_gf_interval; - rc->min_gf_interval = oxcf->gf_cfg.min_gf_interval; - rc->static_scene_max_gf_interval = rc->min_gf_interval + 1; - } else { - // Set Maximum gf/arf interval - rc->max_gf_interval = oxcf->gf_cfg.max_gf_interval; - rc->min_gf_interval = oxcf->gf_cfg.min_gf_interval; - if (rc->min_gf_interval == 0) - rc->min_gf_interval = av1_rc_get_default_min_gf_interval( - oxcf->frm_dim_cfg.width, oxcf->frm_dim_cfg.height, cpi->framerate); - if (rc->max_gf_interval == 0) - rc->max_gf_interval = - get_default_max_gf_interval(cpi->framerate, rc->min_gf_interval); - /* - * Extended max interval for genuinely static scenes like slide shows. - * The no.of.stats available in the case of LAP is limited, - * hence setting to max_gf_interval. - */ - if (cpi->ppi->lap_enabled) - rc->static_scene_max_gf_interval = rc->max_gf_interval + 1; - else - rc->static_scene_max_gf_interval = MAX_STATIC_GF_GROUP_LENGTH; + if (rc->max_gf_interval > rc->static_scene_max_gf_interval) + rc->max_gf_interval = rc->static_scene_max_gf_interval; - if (rc->max_gf_interval > rc->static_scene_max_gf_interval) - rc->max_gf_interval = rc->static_scene_max_gf_interval; - - // Clamp min to max - rc->min_gf_interval = AOMMIN(rc->min_gf_interval, rc->max_gf_interval); - } + // Clamp min to max + rc->min_gf_interval = AOMMIN(rc->min_gf_interval, rc->max_gf_interval); } void av1_rc_update_framerate(AV1_COMP *cpi, int width, int height) { @@ -2723,7 +2839,7 @@ int av1_calc_pframe_target_size_one_pass_vbr( const AV1_COMP *const cpi, FRAME_UPDATE_TYPE frame_update_type) { - static const int af_ratio = 10; + const int af_ratio = is_one_pass_rt_lag_params(cpi) ? 6 : 10; const RATE_CONTROL *const rc = &cpi->rc; const PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc; int64_t target; @@ -2756,7 +2872,14 @@ const RATE_CONTROL *rc = &cpi->rc; const PRIMARY_RATE_CONTROL *p_rc = &cpi->ppi->p_rc; const RateControlCfg *rc_cfg = &oxcf->rc_cfg; - const int64_t diff = p_rc->optimal_buffer_level - p_rc->buffer_level; + const RefreshFrameInfo *const refresh_frame = &cpi->refresh_frame; + int64_t diff = p_rc->optimal_buffer_level - p_rc->buffer_level; + // For refresh alt or golden: keep diff negative to force setting + // higher target bandwidth on these frames. + // Only realtime mode with lookahead. + if (is_one_pass_rt_lag_params(cpi) && + (refresh_frame->alt_ref_frame || refresh_frame->golden_frame)) + diff = AOMMIN(diff, -p_rc->optimal_buffer_level); const int64_t one_pct_bits = 1 + p_rc->optimal_buffer_level / 100; int min_frame_target = AOMMAX(rc->avg_frame_bandwidth >> 4, FRAME_OVERHEAD_BITS); @@ -3143,21 +3266,8 @@ return best_sad; } -/*!\brief Check for scene detection, for 1 pass real-time mode. - * - * Compute average source sad (temporal sad: between current source and - * previous source) over a subset of superblocks. Use this is detect big changes - * in content and set the \c cpi->rc.high_source_sad flag. - * - * \ingroup rate_control - * \param[in] cpi Top level encoder structure - * \param[in] frame_input Current and last input source frames - * - * \remark Nothing is returned. Instead the flag \c cpi->rc.high_source_sad - * is set if scene change is detected, and \c cpi->rc.avg_source_sad is updated. - */ -static void rc_scene_detection_onepass_rt(AV1_COMP *cpi, - const EncodeFrameInput *frame_input) { +void av1_rc_scene_detection_onepass_rt(AV1_COMP *cpi, + const EncodeFrameInput *frame_input) { AV1_COMMON *const cm = &cpi->common; RATE_CONTROL *const rc = &cpi->rc; YV12_BUFFER_CONFIG const *const unscaled_src = frame_input->source; @@ -3176,8 +3286,14 @@ width = cpi->oxcf.frm_dim_cfg.width; height = cpi->oxcf.frm_dim_cfg.height; } + // Set src_sad_blk_64x64 to NULL also for number_spatial layers > 1, as + // it is never allocated for number_spatial_layers > 1 (see the condition + // under which we allocate cpi->src_sad_blk_64x64 later in this function). + // This is guard against the case where the number_spatial_layers + // is changed dynamically without re-alloc of encoder. if (width != cm->render_width || height != cm->render_height || - unscaled_src == NULL || unscaled_last_src == NULL) { + cpi->svc.number_spatial_layers > 1 || unscaled_src == NULL || + unscaled_last_src == NULL) { aom_free(cpi->src_sad_blk_64x64); cpi->src_sad_blk_64x64 = NULL; } @@ -3243,7 +3359,7 @@ rc->prev_frame_is_dropped || cpi->svc.number_temporal_layers > 1; // Store blkwise SAD for later use. Disable for spatial layers for now. if (width == cm->render_width && height == cm->render_height && - cpi->svc.number_spatial_layers == 1) { + cpi->svc.number_spatial_layers == 1 && !is_one_pass_rt_lag_params(cpi)) { if (cpi->src_sad_blk_64x64 == NULL) { CHECK_MEM_ERROR(cm, cpi->src_sad_blk_64x64, (uint64_t *)aom_calloc(sb_cols * sb_rows, @@ -3301,11 +3417,13 @@ // between current and previous frame value(s). Use minimum threshold // for cases where there is small change from content that is completely // static. + const int thresh_zero_sad_samples = + avg_sad > 8 * min_thresh ? 3 * (num_samples >> 2) : num_samples >> 1; if (!light_change && avg_sad > AOMMAX(min_thresh, (unsigned int)(rc->avg_source_sad * thresh)) && rc->frames_since_key > 1 + cpi->svc.number_spatial_layers && - num_zero_temp_sad < 3 * (num_samples >> 2)) + num_zero_temp_sad < thresh_zero_sad_samples) rc->high_source_sad = 1; else rc->high_source_sad = 0; @@ -3321,14 +3439,23 @@ } // Update the high_motion_content_screen_rtc flag on TL0. Avoid the update // if too many consecutive frame drops occurred. - const uint64_t thresh_high_motion = 9 * 64 * 64; + // The threshold_high_motion is kept to a large value, to account for + // mis-detection for scroll for scaled input (where scroll motion can be + // subpel and not detected below). The threshold may be improved when better + // scroll detection (for subpel) is added. + const int scale = + (unscaled_src->y_width * unscaled_src->y_height > 1920 * 1080) ? 30 : 10; + const uint64_t thresh_high_motion = scale * 64 * 64; if (cpi->svc.temporal_layer_id == 0 && rc->drop_count_consec < 3) { cpi->rc.high_motion_content_screen_rtc = 0; - if (cpi->oxcf.speed >= 11 && + if (cpi->oxcf.speed >= 11 && !is_one_pass_rt_lag_params(cpi) && cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && + rc->num_col_blscroll_last_tl0 < 5 && + rc->num_row_blscroll_last_tl0 < 5 && rc->percent_blocks_with_motion > 40 && rc->prev_avg_source_sad > thresh_high_motion && rc->avg_source_sad > thresh_high_motion && + rc->frame_number_encoded - rc->last_frame_low_source_sad > 12 && rc->avg_frame_low_motion < 60 && unscaled_src->y_width >= 1280 && unscaled_src->y_height >= 720) { cpi->rc.high_motion_content_screen_rtc = 1; @@ -3800,15 +3927,15 @@ svc->spatial_layer_id == 0) { if (rc->prev_coded_width == cm->width && rc->prev_coded_height == cm->height) { - rc_scene_detection_onepass_rt(cpi, frame_input); + av1_rc_scene_detection_onepass_rt(cpi, frame_input); } else { aom_free(cpi->src_sad_blk_64x64); cpi->src_sad_blk_64x64 = NULL; } } - if (((*frame_type == KEY_FRAME && cpi->sf.rt_sf.rc_adjust_keyframe) || - (cpi->sf.rt_sf.rc_compute_spatial_var_sc && rc->high_source_sad)) && - svc->spatial_layer_id == 0 && cm->seq_params->bit_depth == 8 && + if (cpi->sf.rt_sf.rc_compute_spatial_var_sc_kf && + (*frame_type == KEY_FRAME || rc->high_source_sad) && + svc->spatial_layer_id == 0 && !cm->seq_params->use_highbitdepth && cpi->oxcf.rc_cfg.max_intra_bitrate_pct > 0) rc_spatial_act_onepass_rt(cpi, frame_input->source->y_buffer, frame_input->source->y_stride); @@ -3832,10 +3959,6 @@ resize_reset_rc(cpi, resize_pending_params->width, resize_pending_params->height, cm->width, cm->height); } - if (svc->temporal_layer_id == 0) { - rc->num_col_blscroll_last_tl0 = 0; - rc->num_row_blscroll_last_tl0 = 0; - } // Set the GF interval and update flag. if (!rc->rtc_external_ratectrl) set_gf_interval_update_onepass_rt(cpi, *frame_type); @@ -3904,7 +4027,7 @@ // For easy scene changes used lower QP, otherwise set max-q. // If rt_sf->compute_spatial_var_sc is enabled relax the max-q // condition based on frame spatial variance. - if (cpi->sf.rt_sf.rc_compute_spatial_var_sc) { + if (cpi->sf.rt_sf.rc_compute_spatial_var_sc_kf) { if (cpi->rc.frame_spatial_variance < 100) { *q = (cpi->rc.worst_quality + *q) >> 1; } else if (cpi->rc.frame_spatial_variance < 400 ||
diff --git a/av1/encoder/ratectrl.h b/av1/encoder/ratectrl.h index 88c048f..deea12d 100644 --- a/av1/encoder/ratectrl.h +++ b/av1/encoder/ratectrl.h
@@ -190,10 +190,21 @@ int sframe_due; int high_source_sad; + /*! + * The high_source_sad flag, which indicates scene change, for each frame [i] + * in the lookahead buffer, used for the encoding mode: is_one_pass_rt_lag. + */ + int high_source_sad_lag[MAX_GF_INTERVAL]; int high_motion_content_screen_rtc; uint64_t avg_source_sad; uint64_t prev_avg_source_sad; uint64_t frame_source_sad; + /*! + * The frame source sad for each frame [i] in the lookahead buffer, + * used for the encoding mode: is_one_pass_rt_lag. + */ + uint64_t frame_source_sad_lag[MAX_GF_INTERVAL]; + unsigned int last_frame_low_source_sad; uint64_t frame_spatial_variance; int static_since_last_scene_change; int last_encoded_size_keyframe; @@ -318,6 +329,11 @@ int gfu_boost; /*! + * Average boost factor of ARFs and GFs within a kf interval + */ + int gfu_boost_average; + + /*! * Stores the determined gf group lengths for a set of gf groups */ int gf_intervals[MAX_NUM_GF_INTERVALS]; @@ -830,6 +846,22 @@ */ int av1_postencode_drop_cbr(struct AV1_COMP *cpi, size_t *size); +/*!\brief Check for scene detection, for 1 pass real-time mode. + * + * Compute average source sad (temporal sad: between current source and + * previous source) over a subset of superblocks. Use this is detect big changes + * in content and set the \c cpi->rc.high_source_sad flag. + * + * \ingroup rate_control + * \param[in] cpi Top level encoder structure + * \param[in] frame_input Current and last input source frames + * + * \remark Nothing is returned. Instead the flag \c cpi->rc.high_source_sad + * is set if scene change is detected, and \c cpi->rc.avg_source_sad is updated. + */ +void av1_rc_scene_detection_onepass_rt( + struct AV1_COMP *cpi, const struct EncodeFrameInput *frame_input); + #ifdef __cplusplus } // extern "C" #endif
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c index 43e6a78..e9f6e54 100644 --- a/av1/encoder/rd.c +++ b/av1/encoder/rd.c
@@ -92,6 +92,8 @@ av1_cost_tokens_from_cdf(mode_costs->skip_mode_cost[i], fc->skip_mode_cdfs[i], NULL); } + } else { + av1_zero(mode_costs->skip_mode_cost); } for (i = 0; i < SKIP_CONTEXTS; ++i) { @@ -386,7 +388,8 @@ int av1_compute_rd_mult_based_on_qindex(aom_bit_depth_t bit_depth, FRAME_UPDATE_TYPE update_type, - int qindex, aom_tune_metric tuning) { + int qindex, aom_tune_metric tuning, + MODE mode) { const int q = av1_dc_quant_QTX(qindex, 0, bit_depth); int64_t rdmult = q * q; if (update_type == KF_UPDATE) { @@ -401,21 +404,32 @@ } if (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2) { - // Further multiply rdmult (by up to 200/128 = 1.5625) to improve image - // quality. The most noticeable effect is a mild bias towards choosing - // larger transform sizes (e.g. one 16x16 transform instead of 4 8x8 - // transforms). - // For very high qindexes, start progressively reducing the weight towards - // unity (128/128), as transforms are large enough and making them even - // larger actually harms subjective quality and SSIMULACRA 2 scores. - // This weight part of the equation was determined by iteratively increasing - // weight on CID22 and Daala's subset1, and observing its effects on visual + int weight; + + // Weight terms were determined by iteratively testing various weights + // on CID22 and Daala's subset1, and observing its effects on visual // quality and SSIMULACRA 2 scores along the usable (0-100) range. - // The ramp-down part of the equation was determined by choosing a fixed - // initial qindex point [qindex 159 = (255 - 159) * 3 / 4] where SSIMULACRA - // 2 scores for encodes with qindexes greater than 159 scored at or above - // their equivalents with no rdmult adjustment. - const int weight = clamp(((255 - qindex) * 3) / 4, 0, 72) + 128; + if (mode == REALTIME) { + // Realtime mode: further multiply rdmult by a fourth (32/128 = 0.25) + // to improve image quality. + // The most noticeable effect is that for inter frames, there's a + // stronger bias towards choosing inter prediction modes with encoded + // coefficient residuals (i.e. no skip mode). + weight = 32; + } else { + // All-intra and good-quality modes: Further multiply rdmult (by up to + // 200/128 = 1.5625) to improve image quality. + // The most noticeable effect is a mild bias towards choosing larger + // transform sizes (e.g. one 16x16 transform instead of 4 8x8 transforms). + // For very high qindexes, start progressively reducing the weight towards + // unity (128/128), as transforms are large enough and making them even + // larger actually harms subjective quality and SSIMULACRA 2 scores. + // The ramp-down part of the equation was determined by choosing a fixed + // initial qindex point [qindex 159 = (255 - 159) * 3 / 4] where + // SSIMULACRA 2 scores for encodes with qindexes greater than 159 scored + // at or above their equivalents with no rdmult adjustment. + weight = clamp(((255 - qindex) * 3) / 4, 0, 72) + 128; + } rdmult = (int64_t)((double)rdmult * weight / 128.0); } @@ -436,10 +450,10 @@ const FRAME_TYPE frame_type, const int use_fixed_qp_offsets, const int is_stat_consumption_stage, - const aom_tune_metric tuning) { + const aom_tune_metric tuning, const MODE mode) { int64_t rdmult = av1_compute_rd_mult_based_on_qindex(bit_depth, update_type, - qindex, tuning); - if (is_stat_consumption_stage && !use_fixed_qp_offsets && + qindex, tuning, mode); + if (is_stat_consumption_stage && (use_fixed_qp_offsets == 0) && (frame_type != KEY_FRAME)) { // Layer depth adjustment rdmult = (rdmult * rd_layer_depth_factor[layer_depth]) >> 7; @@ -506,7 +520,8 @@ cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning) / + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode) / beta); } #endif // !CONFIG_REALTIME_ONLY @@ -790,7 +805,8 @@ qindex_rdmult, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); #if CONFIG_RD_COMMAND if (cpi->oxcf.pass == 2) { const RD_COMMAND *rd_command = &cpi->rd_command;
diff --git a/av1/encoder/rd.h b/av1/encoder/rd.h index 0b79fd9..b97149c 100644 --- a/av1/encoder/rd.h +++ b/av1/encoder/rd.h
@@ -238,7 +238,8 @@ */ int av1_compute_rd_mult_based_on_qindex(aom_bit_depth_t bit_depth, FRAME_UPDATE_TYPE update_type, - int qindex, aom_tune_metric tuning); + int qindex, aom_tune_metric tuning, + MODE mode); int av1_compute_rd_mult(const int qindex, const aom_bit_depth_t bit_depth, const FRAME_UPDATE_TYPE update_type, @@ -246,7 +247,7 @@ const FRAME_TYPE frame_type, const int use_fixed_qp_offsets, const int is_stat_consumption_stage, - const aom_tune_metric tuning); + const aom_tune_metric tuning, const MODE mode); void av1_initialize_rd_consts(struct AV1_COMP *cpi);
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c index 869a23c..efbdba2 100644 --- a/av1/encoder/rdopt.c +++ b/av1/encoder/rdopt.c
@@ -512,6 +512,16 @@ compare_rd_idx_pair); } +// Initialize estimated RD Cost records of compound average. +static inline void init_comp_avg_est_rd( + struct macroblock *x, int skip_cmp_using_top_cmp_avg_est_rd_lvl) { + if (!skip_cmp_using_top_cmp_avg_est_rd_lvl) return; + + for (int j = 0; j < TOP_COMP_AVG_EST_RD_COUNT; j++) { + x->top_comp_avg_est_rd[j] = INT64_MAX; + } +} + // Similar to get_horver_correlation, but also takes into account first // row/column, when computing horizontal/vertical correlation. void av1_get_horver_correlation_full_c(const int16_t *diff, int stride, @@ -784,7 +794,28 @@ } static void adjust_rdcost(const AV1_COMP *cpi, const MACROBLOCK *x, - RD_STATS *rd_cost) { + RD_STATS *rd_cost, bool is_inter_pred) { + if ((cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) && + is_inter_pred) { + // Tune IQ and SSIMULACRA2 can be used to encode layered images, where + // keyframes could be encoded at a lower or similar quality (i.e. higher + // QP) than inter-coded frames. + // In this case, libaom tends to underestimate the true RD cost of inter + // prediction candidates, causing encoded file size to increase without a + // corresponding increase in quality. + // When both intra and inter encoded block candidates are available (with + // rdcosts close to each other), the intra-coded candidate was subjectively + // observed to be a bit less blurry, with a corresponding increase in + // SSIMULACRA 2 scores. + // Apply a 1.125x inter block bias to increase overall perceptual + // compression efficiency, while still allowing the encoder to pick inter + // prediction when it's beneficial. + rd_cost->dist += rd_cost->dist >> 3; + rd_cost->rdcost += rd_cost->rdcost >> 3; + return; + } + if (cpi->oxcf.algo_cfg.sharpness != 3) return; if (frame_is_kf_gf_arf(cpi)) return; @@ -807,7 +838,14 @@ } static void adjust_cost(const AV1_COMP *cpi, const MACROBLOCK *x, - int64_t *rd_cost) { + int64_t *rd_cost, bool is_inter_pred) { + if ((cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) && + is_inter_pred) { + *rd_cost += *rd_cost >> 3; + return; + } + if (cpi->oxcf.algo_cfg.sharpness != 3) return; if (frame_is_kf_gf_arf(cpi)) return; @@ -1401,29 +1439,34 @@ *mode_index_end = SIMPLE_TRANSLATION; } -// Increase rd cost of warp mode for low complexity decoding. -static inline void increase_warp_mode_rd(const MB_MODE_INFO *const best_mbmi, - const MB_MODE_INFO *const this_mbmi, - int64_t *const best_scaled_rd, - int64_t *const this_scaled_rd, - int rd_bias_scale_pct) { - // Check rd bias percentage is non-zero. - if (!rd_bias_scale_pct) return; +// Increase rd cost of warp and obmc motion modes for low complexity decoding. +static inline void increase_motion_mode_rd(const MB_MODE_INFO *const best_mbmi, + const MB_MODE_INFO *const this_mbmi, + int64_t *const best_scaled_rd, + int64_t *const this_scaled_rd, + int rd_warp_bias_scale_pct, + float rd_obmc_bias_scale_pct) { if (*best_scaled_rd == INT64_MAX || *this_scaled_rd == INT64_MAX) return; - // Experiments have been performed with increasing the RD cost of warp mode at - // the below locations of inter mode evaluation. + // Experiments have been performed with increasing the RD cost of warp and + // obmc motion modes at the below locations of inter mode evaluation. // (1). Inter mode evaluation loop in av1_rd_pick_inter_mode(). // (2). Motion mode evaluation during handle_inter_mode() call. // (3). Motion mode evaluation for winner motion modes. // (4). Tx search for best inter candidates. // Based on the speed quality trade-off results of this speed feature, the rd // bias logic is enabled only at (2), (3) and (4). - const double rd_bias_scale = rd_bias_scale_pct / 100.0; + const double rd_warp_bias_scale = rd_warp_bias_scale_pct / 100.0; + const double rd_obmc_bias_scale = rd_obmc_bias_scale_pct / 100.0; if (best_mbmi->motion_mode == WARPED_CAUSAL) - *best_scaled_rd += (int64_t)(rd_bias_scale * *best_scaled_rd); + *best_scaled_rd += (int64_t)(rd_warp_bias_scale * *best_scaled_rd); + else if (best_mbmi->motion_mode == OBMC_CAUSAL) + *best_scaled_rd += (int64_t)(rd_obmc_bias_scale * *best_scaled_rd); + if (this_mbmi->motion_mode == WARPED_CAUSAL) - *this_scaled_rd += (int64_t)(rd_bias_scale * *this_scaled_rd); + *this_scaled_rd += (int64_t)(rd_warp_bias_scale * *this_scaled_rd); + else if (this_mbmi->motion_mode == OBMC_CAUSAL) + *this_scaled_rd += (int64_t)(rd_obmc_bias_scale * *this_scaled_rd); } /*!\brief AV1 motion mode search @@ -1512,7 +1555,6 @@ const int rate2_nocoeff = rd_stats->rate; int best_xskip_txfm = 0; RD_STATS best_rd_stats, best_rd_stats_y, best_rd_stats_uv; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; const int rate_mv0 = *rate_mv; const int interintra_allowed = cm->seq_params->enable_interintra_compound && @@ -1847,9 +1889,15 @@ } } - adjust_cost(cpi, x, &this_yrd); - adjust_rdcost(cpi, x, rd_stats); - adjust_rdcost(cpi, x, rd_stats_y); + if (this_yrd < INT64_MAX) { + adjust_cost(cpi, x, &this_yrd, /*is_inter_pred=*/true); + } + adjust_rdcost(cpi, x, rd_stats, /*is_inter_pred=*/true); + // Bug 494653438: If do_tx_search is 0, rd_stats_y is uninitialized, so + // valgrind will warn if we use rd_stats_y->rdcost in a conditional. + if (!do_tx_search || rd_stats_y->rdcost < INT64_MAX) { + adjust_rdcost(cpi, x, rd_stats_y, /*is_inter_pred=*/true); + } const int64_t tmp_rd = RDCOST(x->rdmult, rd_stats->rate, rd_stats->dist); if (mode_index == 0) { @@ -1858,8 +1906,10 @@ int64_t best_scaled_rd = best_rd; int64_t this_scaled_rd = tmp_rd; if (mode_index != 0) - increase_warp_mode_rd(&best_mbmi, mbmi, &best_scaled_rd, &this_scaled_rd, - cpi->sf.inter_sf.bias_warp_mode_rd_scale_pct); + increase_motion_mode_rd(&best_mbmi, mbmi, &best_scaled_rd, + &this_scaled_rd, + cpi->sf.inter_sf.bias_warp_mode_rd_scale_pct, + cpi->sf.inter_sf.bias_obmc_mode_rd_scale_pct); if (mode_index == 0 || this_scaled_rd < best_scaled_rd) { // Update best_rd data if this is the best motion mode so far @@ -1870,8 +1920,6 @@ best_rate_mv = tmp_rate_mv; *yrd = this_yrd; if (num_planes > 1) best_rd_stats_uv = *rd_stats_uv; - memcpy(best_blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * xd->height * xd->width); av1_copy_array(best_tx_type_map, xd->tx_type_map, xd->height * xd->width); best_xskip_txfm = mbmi->skip_txfm; } @@ -1888,8 +1936,6 @@ *rd_stats = best_rd_stats; *rd_stats_y = best_rd_stats_y; if (num_planes > 1) *rd_stats_uv = best_rd_stats_uv; - memcpy(txfm_info->blk_skip, best_blk_skip, - sizeof(txfm_info->blk_skip[0]) * xd->height * xd->width); av1_copy_array(xd->tx_type_map, best_tx_type_map, xd->height * xd->width); txfm_info->skip_txfm = best_xskip_txfm; @@ -2223,7 +2269,6 @@ // Compute the estimated RD cost for the motion vector with simple translation. static int64_t simple_translation_pred_rd(AV1_COMP *const cpi, MACROBLOCK *x, - RD_STATS *rd_stats, HandleInterModeArgs *args, int ref_mv_idx, int64_t ref_best_rd, BLOCK_SIZE bsize) { @@ -2240,7 +2285,8 @@ { p[0].dst.buf, p[1].dst.buf, p[2].dst.buf }, { p[0].dst.stride, p[1].dst.stride, p[2].dst.stride }, }; - av1_init_rd_stats(rd_stats); + RD_STATS rd_stats; + av1_init_rd_stats(&rd_stats); mbmi->interinter_comp.type = COMPOUND_AVERAGE; mbmi->comp_group_idx = 0; @@ -2255,10 +2301,10 @@ mbmi->motion_mode = SIMPLE_TRANSLATION; mbmi->ref_mv_idx = ref_mv_idx; - rd_stats->rate += args->ref_frame_cost + args->single_comp_cost; + rd_stats.rate += args->ref_frame_cost + args->single_comp_cost; const int drl_cost = get_drl_cost(mbmi, mbmi_ext, mode_costs->drl_mode_cost0, ref_frame_type); - rd_stats->rate += drl_cost; + rd_stats.rate += drl_cost; int_mv cur_mv[2]; if (!build_cur_mv(cur_mv, mbmi->mode, cm, x, 0)) { @@ -2269,9 +2315,9 @@ mbmi->mv[i].as_int = cur_mv[i].as_int; } const int ref_mv_cost = cost_mv_ref(mode_costs, mbmi->mode, mode_ctx); - rd_stats->rate += ref_mv_cost; + rd_stats.rate += ref_mv_cost; - if (RDCOST(x->rdmult, rd_stats->rate, 0) > ref_best_rd) { + if (RDCOST(x->rdmult, rd_stats.rate, 0) > ref_best_rd) { return INT64_MAX; } @@ -2293,7 +2339,7 @@ int64_t est_dist; model_rd_sb_fn[MODELRD_CURVFIT](cpi, bsize, x, xd, 0, 0, &est_rate, &est_dist, NULL, NULL, NULL, NULL, NULL); - return RDCOST(x->rdmult, rd_stats->rate + est_rate, est_dist); + return RDCOST(x->rdmult, rd_stats.rate + est_rate, est_dist); } // Represents a set of integers, from 0 to sizeof(int) * 8, as bits in @@ -2310,7 +2356,6 @@ // Returns an integer where, if the i-th bit is set, it means that the i-th // motion vector should be searched. This is only set for NEAR_MV. static int ref_mv_idx_to_search(AV1_COMP *const cpi, MACROBLOCK *x, - RD_STATS *rd_stats, HandleInterModeArgs *const args, int64_t ref_best_rd, BLOCK_SIZE bsize, const int ref_set) { @@ -2355,7 +2400,7 @@ continue; } idx_rdcost[ref_mv_idx] = simple_translation_pred_rd( - cpi, x, rd_stats, args, ref_mv_idx, ref_best_rd, bsize); + cpi, x, args, ref_mv_idx, ref_best_rd, bsize); } // Find the index with the best RD cost. int best_idx = 0; @@ -2554,28 +2599,17 @@ static inline int prune_modes_based_on_tpl_stats( PruneInfoFromTpl *inter_cost_info_from_tpl, const int *refs, int ref_mv_idx, const PREDICTION_MODE this_mode, int prune_mode_level) { + const int is_ref_last2 = refs[0] == LAST2_FRAME || refs[1] == LAST2_FRAME; + if (prune_mode_level == 1 && !is_ref_last2) return 0; + const int have_newmv = have_newmv_in_inter_mode(this_mode); - if ((prune_mode_level < 2) && have_newmv) return 0; + if ((prune_mode_level == 2) && have_newmv) return 0; const int64_t best_inter_cost = inter_cost_info_from_tpl->best_inter_cost; if (best_inter_cost == INT64_MAX) return 0; - const int prune_level = prune_mode_level - 1; int64_t cur_inter_cost; - const int is_globalmv = - (this_mode == GLOBALMV) || (this_mode == GLOBAL_GLOBALMV); - const int prune_index = is_globalmv ? MAX_REF_MV_SEARCH : ref_mv_idx; - - // Thresholds used for pruning: - // Lower value indicates aggressive pruning and higher value indicates - // conservative pruning which is set based on ref_mv_idx and speed feature. - // 'prune_index' 0, 1, 2 corresponds to ref_mv indices 0, 1 and 2. prune_index - // 3 corresponds to GLOBALMV/GLOBAL_GLOBALMV - static const int tpl_inter_mode_prune_mul_factor[3][MAX_REF_MV_SEARCH + 1] = { - { 6, 6, 6, 4 }, { 6, 4, 4, 4 }, { 5, 4, 4, 4 } - }; - const int is_comp_pred = (refs[1] > INTRA_FRAME); if (!is_comp_pred) { cur_inter_cost = inter_cost_info_from_tpl->ref_inter_cost[refs[0] - 1]; @@ -2589,6 +2623,22 @@ cur_inter_cost = AOMMAX(inter_cost_ref0, inter_cost_ref1); } + if (is_ref_last2) return (cur_inter_cost > best_inter_cost); + + const int is_globalmv = + (this_mode == GLOBALMV) || (this_mode == GLOBAL_GLOBALMV); + const int prune_index = is_globalmv ? MAX_REF_MV_SEARCH : ref_mv_idx; + const int prune_level = prune_mode_level - 2; + + // Thresholds used for pruning: + // Lower value indicates aggressive pruning and higher value indicates + // conservative pruning which is set based on ref_mv_idx and speed feature. + // 'prune_index' 0, 1, 2 corresponds to ref_mv indices 0, 1 and 2. + // prune_index 3 corresponds to GLOBALMV/GLOBAL_GLOBALMV + static const int tpl_inter_mode_prune_mul_factor[3][MAX_REF_MV_SEARCH + 1] = { + { 6, 6, 6, 4 }, { 6, 4, 4, 4 }, { 5, 4, 4, 4 } + }; + // Prune the mode if cur_inter_cost is greater than threshold times // best_inter_cost if (cur_inter_cost > @@ -2963,9 +3013,9 @@ * \param[in] bsize Current block size. * \param[in,out] rd_stats Struct to keep track of the overall RD * information. - * \param[in,out] rd_stats_y Struct to keep track of the RD information + * \param[out] rd_stats_y Struct to keep track of the RD information * for only the Y plane. - * \param[in,out] rd_stats_uv Struct to keep track of the RD information + * \param[out] rd_stats_uv Struct to keep track of the RD information * for only the UV planes. * \param[in] args HandleInterModeArgs struct holding * miscellaneous arguments for inter mode @@ -3009,7 +3059,8 @@ * \param[out] yrd Stores the rdcost corresponding to encoding * the luma plane. * - * \return The RD cost for the mode being searched. + * \return The RD cost for the mode being searched. If the return value is + * INT64_MAX, the output parameters are not set; do not use them. */ static int64_t handle_inter_mode( AV1_COMP *const cpi, TileDataEnc *tile_data, MACROBLOCK *x, @@ -3061,7 +3112,6 @@ const int8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame); RD_STATS best_rd_stats, best_rd_stats_y, best_rd_stats_uv; int64_t best_rd = INT64_MAX; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; int64_t best_yrd = INT64_MAX; MB_MODE_INFO best_mbmi = *mbmi; @@ -3088,7 +3138,7 @@ int_mv save_mv[MAX_REF_MV_SEARCH - 1][2]; int best_ref_mv_idx = -1; const int idx_mask = - ref_mv_idx_to_search(cpi, x, rd_stats, args, ref_best_rd, bsize, ref_set); + ref_mv_idx_to_search(cpi, x, args, ref_best_rd, bsize, ref_set); const int16_t mode_ctx = av1_mode_context_analyzer(mbmi_ext->mode_context, mbmi->ref_frame); const ModeCosts *mode_costs = &x->mode_costs; @@ -3326,8 +3376,6 @@ best_rd = tmp_rd; best_mbmi = *mbmi; best_xskip_txfm = txfm_info->skip_txfm; - memcpy(best_blk_skip, txfm_info->blk_skip, - sizeof(best_blk_skip[0]) * xd->height * xd->width); av1_copy_array(best_tx_type_map, xd->tx_type_map, xd->height * xd->width); motion_mode_cand->rate_mv = rate_mv; @@ -3353,8 +3401,6 @@ txfm_info->skip_txfm = best_xskip_txfm; assert(IMPLIES(mbmi->comp_group_idx == 1, mbmi->interinter_comp.type != COMPOUND_AVERAGE)); - memcpy(txfm_info->blk_skip, best_blk_skip, - sizeof(best_blk_skip[0]) * xd->height * xd->width); av1_copy_array(xd->tx_type_map, best_tx_type_map, xd->height * xd->width); rd_stats->rdcost = RDCOST(x->rdmult, rd_stats->rate, rd_stats->dist); @@ -3388,7 +3434,6 @@ MACROBLOCKD *const xd = &x->e_mbd; const TileInfo *tile = &xd->tile; MB_MODE_INFO *mbmi = xd->mi[0]; - TxfmSearchInfo *txfm_info = &x->txfm_search_info; const int mi_row = xd->mi_row; const int mi_col = xd->mi_col; @@ -3439,7 +3484,6 @@ MB_MODE_INFO best_mbmi = *mbmi; RD_STATS best_rdstats = *rd_stats; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE] = { 0 }; uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; av1_copy_array(best_tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); @@ -3565,15 +3609,11 @@ best_rd = rd_stats_yuv.rdcost; best_mbmi = *mbmi; best_rdstats = rd_stats_yuv; - memcpy(best_blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * xd->height * xd->width); av1_copy_array(best_tx_type_map, xd->tx_type_map, xd->height * xd->width); } } *mbmi = best_mbmi; *rd_stats = best_rdstats; - memcpy(txfm_info->blk_skip, best_blk_skip, - sizeof(txfm_info->blk_skip[0]) * xd->height * xd->width); av1_copy_array(xd->tx_type_map, best_tx_type_map, ctx->num_4x4_blk); #if CONFIG_RD_DEBUG mbmi->rd_stats = *rd_stats; @@ -3593,7 +3633,6 @@ MACROBLOCKD *const xd = &x->e_mbd; MB_MODE_INFO *const mbmi = xd->mi[0]; const int num_planes = av1_num_planes(cm); - TxfmSearchInfo *txfm_info = &x->txfm_search_info; int rate_y = 0, rate_uv = 0, rate_y_tokenonly = 0, rate_uv_tokenonly = 0; uint8_t y_skip_txfm = 0, uv_skip_txfm = 0; int64_t dist_y = 0, dist_uv = 0; @@ -3618,8 +3657,6 @@ // Set up the tx variables for reproducing the y predictions in case we // need it for chroma-from-luma. if (xd->is_chroma_ref && store_cfl_required_rdo(cm, x)) { - memcpy(txfm_info->blk_skip, ctx->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); av1_copy_array(xd->tx_type_map, ctx->tx_type_map, ctx->num_4x4_blk); } const TX_SIZE max_uv_tx_size = av1_get_tx_size(AOM_PLANE_U, xd); @@ -3643,13 +3680,11 @@ best_rd = rd_cost->rdcost; if (rd_pick_intrabc_mode_sb(cpi, x, ctx, rd_cost, bsize, best_rd) < best_rd) { ctx->rd_stats.skip_txfm = mbmi->skip_txfm; - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); assert(rd_cost->rate != INT_MAX); } if (rd_cost->rate == INT_MAX) return; - ctx->mic = *xd->mi[0]; + ctx->mic = *mbmi; av1_copy_mbmi_ext_to_mbmi_ext_frame(&ctx->mbmi_ext_best, &x->mbmi_ext, av1_ref_frame_type(xd->mi[0]->ref_frame)); av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); @@ -3830,7 +3865,6 @@ MACROBLOCKD *const xd = &x->e_mbd; MB_MODE_INFO *const mbmi = xd->mi[0]; TxfmSearchParams *txfm_params = &x->txfm_search_params; - TxfmSearchInfo *txfm_info = &x->txfm_search_info; int64_t best_rd; const int num_planes = av1_num_planes(cm); @@ -3910,8 +3944,6 @@ INT64_MAX); memset(mbmi->inter_tx_size, mbmi->tx_size, sizeof(mbmi->inter_tx_size)); - for (int i = 0; i < xd->height * xd->width; ++i) - set_blk_skip(txfm_info->blk_skip, 0, i, rd_stats_y.skip_txfm); } } else { av1_pick_uniform_tx_size_type_yrd(cpi, x, &rd_stats_y, bsize, @@ -3951,7 +3983,6 @@ if (best_rd > this_rd) { *best_mbmode = *mbmi; *best_mode_index = winner_mode_index; - av1_copy_array(ctx->blk_skip, txfm_info->blk_skip, ctx->num_4x4_blk); av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); rd_cost->rate = this_rate; rd_cost->dist = rd_stats_y.dist + rd_stats_uv.dist; @@ -4149,11 +4180,10 @@ 0) { // Prune inter modes when relative dist of ALTREF2 and ALTREF is close // to the relative dist of LAST_FRAME. - if (inter_sf->alt_ref_search_fp == 1 && - (abs(cpi->ref_frame_dist_info - .ref_relative_dist[ref_frame - LAST_FRAME]) > - 1.5 * abs(cpi->ref_frame_dist_info - .ref_relative_dist[LAST_FRAME - LAST_FRAME]))) { + if (abs(cpi->ref_frame_dist_info + .ref_relative_dist[ref_frame - LAST_FRAME] - + cpi->ref_frame_dist_info + .ref_relative_dist[LAST_FRAME - LAST_FRAME]) > 4) { continue; } if (x->pred_mv_sad[ref_frame] > sad_thresh) @@ -4194,8 +4224,8 @@ // Prune reference frames which are not the closest to the current // frame and with large pred_mv_sad. if (inter_sf->prune_single_ref) { - assert(inter_sf->prune_single_ref > 0 && inter_sf->prune_single_ref < 3); - const double prune_threshes[2] = { 1.20, 1.05 }; + assert(inter_sf->prune_single_ref > 0 && inter_sf->prune_single_ref < 5); + const double prune_thresh = (inter_sf->prune_single_ref <= 3) ? 1.20 : 1.05; for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) { const RefFrameDistanceInfo *const ref_frame_dist_info = @@ -4203,16 +4233,16 @@ const int is_closest_ref = (ref_frame == ref_frame_dist_info->nearest_past_ref) || (ref_frame == ref_frame_dist_info->nearest_future_ref); + const int ref_idx = ref_frame - LAST_FRAME; - if (!is_closest_ref) { + if (!(cpi->keep_single_ref_frame_mask & (1 << ref_idx) || + is_closest_ref)) { const int dir = (ref_frame_dist_info->ref_relative_dist[ref_frame - LAST_FRAME] < 0) ? 0 : 1; if (x->best_pred_mv_sad[dir] < INT_MAX && - x->pred_mv_sad[ref_frame] > - prune_threshes[inter_sf->prune_single_ref - 1] * - x->best_pred_mv_sad[dir]) + x->pred_mv_sad[ref_frame] > prune_thresh * x->best_pred_mv_sad[dir]) mask->pred_modes[ref_frame] |= INTER_SINGLE_ALL; } } @@ -5117,7 +5147,6 @@ const int skip_ctx = av1_get_skip_txfm_context(xd); const int skip_txfm = mbmi->skip_txfm && !is_mode_intra(av1_mode_defs[new_best_mode].mode); - const TxfmSearchInfo *txfm_info = &x->txfm_search_info; search_state->best_rd = new_best_rd_stats->rdcost; search_state->best_mode_index = new_best_mode; @@ -5137,8 +5166,6 @@ search_state->best_rate_uv = new_best_rd_stats_uv->rate; } search_state->best_y_rdcost = *new_best_rd_stats_y; - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); } @@ -5241,9 +5268,10 @@ int64_t best_scaled_rd = search_state->best_rd; int64_t this_scaled_rd = rd_stats.rdcost; if (search_state->best_mode_index != THR_INVALID) - increase_warp_mode_rd(&search_state->best_mbmode, mbmi, &best_scaled_rd, - &this_scaled_rd, - cpi->sf.inter_sf.bias_warp_mode_rd_scale_pct); + increase_motion_mode_rd(&search_state->best_mbmode, mbmi, + &best_scaled_rd, &this_scaled_rd, + cpi->sf.inter_sf.bias_warp_mode_rd_scale_pct, + cpi->sf.inter_sf.bias_obmc_mode_rd_scale_pct); if (this_scaled_rd < best_scaled_rd) { *yrd = this_yrd; @@ -5583,9 +5611,10 @@ int64_t best_scaled_rd = search_state->best_rd; int64_t this_scaled_rd = rd_stats.rdcost; - increase_warp_mode_rd(&search_state->best_mbmode, mbmi, &best_scaled_rd, - &this_scaled_rd, - cpi->sf.inter_sf.bias_warp_mode_rd_scale_pct); + increase_motion_mode_rd(&search_state->best_mbmode, mbmi, &best_scaled_rd, + &this_scaled_rd, + cpi->sf.inter_sf.bias_warp_mode_rd_scale_pct, + cpi->sf.inter_sf.bias_obmc_mode_rd_scale_pct); if (this_scaled_rd < best_rd_in_this_partition) { best_rd_in_this_partition = rd_stats.rdcost; *yrd = this_yrd; @@ -5725,7 +5754,6 @@ int best_mode_cost_y = -1; MB_MODE_INFO best_mbmi = *xd->mi[0]; THR_MODES best_mode_enum = THR_INVALID; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; const int num_4x4 = bsize_to_num_blk(bsize); @@ -5742,6 +5770,8 @@ if (bh > 4 || bw > 4) return; } + mbmi->skip_txfm = 0; + for (int mode_idx = 0; mode_idx < LUMA_MODE_COUNT; ++mode_idx) { if (sf->intra_sf.skip_intra_in_interframe && search_state->intra_search_state.skip_intra_modes) @@ -5799,7 +5829,7 @@ &best_model_rd, top_intra_model_rd); if (intra_rd_y < INT64_MAX) { - adjust_cost(cpi, x, &intra_rd_y); + adjust_cost(cpi, x, &intra_rd_y, /*is_inter_pred=*/false); } if (is_luma_result_valid && intra_rd_y < yrd_threshold) { @@ -5810,8 +5840,6 @@ best_rd_y = intra_rd_y; best_mbmi = *mbmi; best_mode_enum = mode_enum; - memcpy(best_blk_skip, x->txfm_search_info.blk_skip, - sizeof(best_blk_skip[0]) * num_4x4); av1_copy_array(best_tx_type_map, xd->tx_type_map, num_4x4); } } @@ -5825,8 +5853,6 @@ // Restores the best luma mode *mbmi = best_mbmi; - memcpy(x->txfm_search_info.blk_skip, best_blk_skip, - sizeof(best_blk_skip[0]) * num_4x4); av1_copy_array(xd->tx_type_map, best_tx_type_map, num_4x4); // Performs chroma search @@ -5887,7 +5913,7 @@ intra_rd_stats.rdcost = this_rd; - adjust_rdcost(cpi, x, &intra_rd_stats); + adjust_rdcost(cpi, x, &intra_rd_stats, /*is_inter_pred=*/false); // Collect mode stats for multiwinner mode processing const int txfm_search_done = 1; @@ -5902,6 +5928,18 @@ } } +// Initialize the table that stores best RD Costs of transform no-split. +static inline void init_top_tx_no_split_rd_for_inter_modes( + MACROBLOCK *x, int prune_inter_tx_split_rd_eval_lvl) { + if (!prune_inter_tx_split_rd_eval_lvl) return; + + for (int i = 0; i < MAX_TX_BLOCKS_IN_MAX_SB; i++) { + for (int j = 0; j < TOP_INTER_TX_NO_SPLIT_COUNT; j++) { + x->top_inter_tx_no_split_rd[i][j] = INT64_MAX; + } + } +} + #if !CONFIG_REALTIME_ONLY // Prepare inter_cost and intra_cost from TPL stats, which are used as ML // features in intra mode pruning. @@ -6073,6 +6111,10 @@ INTERINTRA_MODES, INTERINTRA_MODES, INTERINTRA_MODES, INTERINTRA_MODES, INTERINTRA_MODES, INTERINTRA_MODES, INTERINTRA_MODES, INTERINTRA_MODES }; + + init_top_tx_no_split_rd_for_inter_modes( + x, sf->tx_sf.prune_inter_tx_split_rd_eval_lvl); + HandleInterModeArgs args = { { NULL }, { MAX_SB_SIZE, MAX_SB_SIZE, MAX_SB_SIZE }, { NULL }, @@ -6259,7 +6301,7 @@ mode_start = SINGLE_REF_MODE_START; mode_end = SINGLE_REF_MODE_END; } - + init_comp_avg_est_rd(x, sf->inter_sf.skip_cmp_using_top_cmp_avg_est_rd_lvl); for (THR_MODES midx = mode_start; midx < mode_end; ++midx) { // Get the actual prediction mode we are trying in this iteration const THR_MODES mode_enum = av1_default_mode_order[midx]; @@ -6273,11 +6315,8 @@ ref_frame > INTRA_FRAME && second_ref_frame == NONE_FRAME; const int comp_pred = second_ref_frame > INTRA_FRAME; - init_mbmi(mbmi, this_mode, ref_frames, cm); - txfm_info->skip_txfm = 0; sf_args.num_single_modes_processed += is_single_pred; - set_ref_ptrs(cm, xd, ref_frame, second_ref_frame); #if CONFIG_COLLECT_COMPONENT_TIMING start_timing(cpi, skip_inter_mode_time); #endif @@ -6289,6 +6328,9 @@ #endif if (is_skip_inter_mode) continue; + init_mbmi(mbmi, this_mode, ref_frames, cm); + set_ref_ptrs(cm, xd, ref_frame, second_ref_frame); + // Select prediction reference frames. for (i = 0; i < num_planes; i++) { xd->plane[i].pre[0] = yv12_mb[ref_frame][i]; @@ -6359,8 +6401,8 @@ ref_frame_rd[ref_frame] = this_rd; } - adjust_cost(cpi, x, &this_rd); - adjust_rdcost(cpi, x, &rd_stats); + adjust_cost(cpi, x, &this_rd, /*is_inter_pred=*/true); + adjust_rdcost(cpi, x, &rd_stats, /*is_inter_pred=*/true); // Did this mode help, i.e., is it the new best mode if (this_rd < search_state.best_rd) { @@ -6479,8 +6521,6 @@ search_state.best_mbmode = *mbmi; search_state.best_skip2 = 0; search_state.best_mode_skippable = this_skippable; - memcpy(ctx->blk_skip, txfm_info->blk_skip, - sizeof(txfm_info->blk_skip[0]) * ctx->num_4x4_blk); av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk); } }
diff --git a/av1/encoder/rdopt_utils.h b/av1/encoder/rdopt_utils.h index 5214800..5063d34 100644 --- a/av1/encoder/rdopt_utils.h +++ b/av1/encoder/rdopt_utils.h
@@ -558,6 +558,7 @@ case DEFAULT_EVAL: txfm_params->default_inter_tx_type_prob_thresh = INT_MAX; txfm_params->use_default_intra_tx_type = 0; + txfm_params->use_derived_intra_tx_type_set = 0; txfm_params->skip_txfm_level = winner_mode_params->skip_txfm_level[DEFAULT_EVAL]; txfm_params->predict_dc_level = @@ -576,8 +577,10 @@ break; case MODE_EVAL: txfm_params->use_default_intra_tx_type = - (cpi->sf.tx_sf.tx_type_search.fast_intra_tx_type_search || + (cpi->sf.tx_sf.tx_type_search.fast_intra_tx_type_search == 2 || cpi->oxcf.txfm_cfg.use_intra_default_tx_only); + txfm_params->use_derived_intra_tx_type_set = + cpi->sf.tx_sf.tx_type_search.fast_intra_tx_type_search == 1; txfm_params->default_inter_tx_type_prob_thresh = cpi->sf.tx_sf.tx_type_search.fast_inter_tx_type_prob_thresh; txfm_params->skip_txfm_level = @@ -607,6 +610,7 @@ case WINNER_MODE_EVAL: txfm_params->default_inter_tx_type_prob_thresh = INT_MAX; txfm_params->use_default_intra_tx_type = 0; + txfm_params->use_derived_intra_tx_type_set = 0; txfm_params->skip_txfm_level = winner_mode_params->skip_txfm_level[WINNER_MODE_EVAL]; txfm_params->predict_dc_level =
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c index a981f08..53983af 100644 --- a/av1/encoder/speed_features.c +++ b/av1/encoder/speed_features.c
@@ -223,6 +223,7 @@ } if (speed >= 1) { + sf->part_sf.ml_4_partition_search_level_index = 1; if (is_720p_or_larger) { sf->part_sf.use_square_partition_only_threshold = BLOCK_128X128; } else if (is_480p_or_larger) { @@ -232,12 +233,11 @@ } if (is_720p_or_larger) { - sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.9999999f; - sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.9999999f; - sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.9618367258814811f; - sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.9990705139233304f; - sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.9648891196441841f; - + sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.5042595622791082f; + sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.8378425823517456f; + sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.8047585616503903f; sf->part_sf.ml_partition_search_breakout_model_index = 1; } else { sf->part_sf.ml_partition_search_breakout_thresh[0] = -1.0f; @@ -250,20 +250,19 @@ } if (speed >= 2) { + sf->part_sf.ml_4_partition_search_level_index = 2; if (is_720p_or_larger) { sf->part_sf.use_square_partition_only_threshold = BLOCK_64X64; - } else if (is_480p_or_larger) { - sf->part_sf.use_square_partition_only_threshold = BLOCK_32X32; } else { sf->part_sf.use_square_partition_only_threshold = BLOCK_32X32; } if (is_720p_or_larger) { - sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.9583713938680828f; - sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.9999999f; - sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.9634239069901543f; - sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.9000000000000001f; - sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.9196596355880025f; + sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.5042595622791082f; + sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.8378425823517456f; + sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.8047585616503903f; sf->part_sf.ml_partition_search_breakout_model_index = 1; } @@ -285,6 +284,7 @@ if (speed >= 3) { sf->part_sf.ml_early_term_after_part_split_level = 0; + sf->part_sf.ml_4_partition_search_level_index = 3; if (is_720p_or_larger) { for (int i = 0; i < PARTITION_BLOCK_SIZES; ++i) { @@ -501,7 +501,7 @@ sf->tpl_sf.search_method = FAST_BIGDIA; sf->tx_sf.tx_type_search.winner_mode_tx_type_pruning = 2; - sf->tx_sf.tx_type_search.fast_intra_tx_type_search = 1; + sf->tx_sf.tx_type_search.fast_intra_tx_type_search = 2; sf->tx_sf.tx_type_search.prune_2d_txfm_mode = TX_TYPE_PRUNE_3; sf->tx_sf.tx_type_search.prune_tx_type_est_rd = 1; @@ -560,7 +560,7 @@ // target image quality is very low. sf->part_sf.default_max_partition_size = BLOCK_32X32; - sf->mv_sf.use_bsize_dependent_search_method = 1; + sf->mv_sf.use_bsize_dependent_search_method = 3; sf->mv_sf.intrabc_search_level = 1; sf->tx_sf.tx_type_search.winner_mode_tx_type_pruning = 3; @@ -636,9 +636,10 @@ if (speed < 1 || speed > 3) return; const AV1_COMMON *const cm = &cpi->common; - const bool is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; - const bool is_between_608p_and_720p = AOMMIN(cm->width, cm->height) >= 608 && - AOMMIN(cm->width, cm->height) <= 720; + const bool is_between_608p_and_1080p = AOMMIN(cm->width, cm->height) >= 608 && + AOMMIN(cm->width, cm->height) <= 1080; + const bool is_between_720p_and_1080p = AOMMIN(cm->width, cm->height) >= 720 && + AOMMIN(cm->width, cm->height) <= 1080; const bool is_vertical_video = cm->width < cm->height; const FRAME_UPDATE_TYPE update_type = @@ -647,7 +648,7 @@ const int is_key_frame = frame_is_intra_only(cm); // Speed features for vertical videos - if (is_vertical_video && is_between_608p_and_720p) { + if (is_vertical_video && is_between_608p_and_1080p) { const int leaf_and_overlay_frames = (update_type == LF_UPDATE || update_type == OVERLAY_UPDATE || update_type == INTNL_OVERLAY_UPDATE); @@ -659,6 +660,7 @@ sf->lpf_sf.switchable_lr_with_bias_level = 1; sf->inter_sf.bias_warp_mode_rd_scale_pct = 4; + sf->inter_sf.bias_obmc_mode_rd_scale_pct = 1.5f; sf->part_sf.split_partition_penalty_level = is_key_frame ? 0 : 2; @@ -668,20 +670,13 @@ } // Speed features for regular videos - if (!is_vertical_video && is_720p_or_larger) { + if (!is_vertical_video && is_between_720p_and_1080p) { sf->gm_sf.gm_erroradv_tr_level = 1; sf->hl_sf.ref_frame_mvs_lvl = 1; - sf->lpf_sf.dual_sgr_penalty_level = boosted ? 1 : 2; - sf->lpf_sf.switchable_lr_with_bias_level = 1; - sf->lpf_sf.skip_loop_filter_using_filt_error = - (update_type != OVERLAY_UPDATE && update_type != INTNL_OVERLAY_UPDATE && - cpi->common.current_frame.pyramid_level > 1) - ? 1 - : 0; - sf->inter_sf.bias_warp_mode_rd_scale_pct = 4; + sf->inter_sf.bias_obmc_mode_rd_scale_pct = 1.5f; sf->part_sf.split_partition_penalty_level = is_key_frame ? 0 : 2; @@ -772,7 +767,14 @@ } if (speed >= 1) { - if (is_480p_or_lesser) sf->inter_sf.skip_newmv_in_drl = 1; + sf->part_sf.ml_4_partition_search_level_index = 1; + sf->inter_sf.skip_newmv_in_drl = 1; + + if (is_480p_or_lesser) { + sf->inter_sf.skip_cmp_using_top_cmp_avg_est_rd_lvl = 1; + } else { + sf->inter_sf.skip_cmp_using_top_cmp_avg_est_rd_lvl = 2; + } if (is_720p_or_larger) { sf->part_sf.use_square_partition_only_threshold = BLOCK_128X128; @@ -783,11 +785,11 @@ } if (is_720p_or_larger) { - sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.9999999f; - sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.9999999f; - sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.9618367258814811f; - sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.9990705139233304f; - sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.9648891196441841f; + sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.5042595622791082f; + sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.8378425823517456f; + sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.8047585616503903f; sf->part_sf.ml_partition_search_breakout_model_index = 1; } else { sf->part_sf.ml_partition_search_breakout_thresh[0] = -1.0f; @@ -802,20 +804,19 @@ } if (speed >= 2) { + sf->part_sf.ml_4_partition_search_level_index = 2; if (is_720p_or_larger) { sf->part_sf.use_square_partition_only_threshold = BLOCK_64X64; - } else if (is_480p_or_larger) { - sf->part_sf.use_square_partition_only_threshold = BLOCK_32X32; } else { sf->part_sf.use_square_partition_only_threshold = BLOCK_32X32; } if (is_720p_or_larger) { - sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.9583713938680828f; - sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.9999999f; - sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.9634239069901543f; - sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.9000000000000001f; - sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.9196596355880025f; + sf->part_sf.ml_partition_search_breakout_thresh[0] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[1] = 0.5042595622791082f; + sf->part_sf.ml_partition_search_breakout_thresh[2] = 0.5f; + sf->part_sf.ml_partition_search_breakout_thresh[3] = 0.8378425823517456f; + sf->part_sf.ml_partition_search_breakout_thresh[4] = 0.8047585616503903f; sf->part_sf.ml_partition_search_breakout_model_index = 1; } @@ -847,6 +848,8 @@ sf->inter_sf.limit_inter_mode_cands = is_lf_frame ? 2 : 0; } + sf->inter_sf.skip_cmp_using_top_cmp_avg_est_rd_lvl = 3; + if (is_480p_or_larger) { sf->tx_sf.tx_type_search.prune_tx_type_using_stats = 1; if (use_hbd) sf->tx_sf.prune_tx_size_level = 2; @@ -892,6 +895,8 @@ sf->part_sf.ml_partition_search_breakout_model_index = 0; } + sf->part_sf.ml_4_partition_search_level_index = 3; + if (is_720p_or_larger) { sf->part_sf.partition_search_breakout_dist_thr = (1 << 25); sf->part_sf.partition_search_breakout_rate_thr = 200; @@ -940,7 +945,7 @@ if (is_480p_or_larger) { sf->tx_sf.tx_type_search.prune_tx_type_using_stats = 2; } else { - sf->mv_sf.skip_fullpel_search_using_startmv = boosted ? 0 : 1; + sf->mv_sf.skip_fullpel_search_using_startmv_refmv = boosted ? 0 : 1; } sf->inter_sf.disable_interinter_wedge_var_thresh = UINT_MAX; @@ -974,7 +979,7 @@ sf->inter_sf.skip_newmv_in_drl = 4; sf->inter_sf.prune_comp_ref_frames = 1; - sf->mv_sf.skip_fullpel_search_using_startmv = boosted ? 0 : 1; + sf->mv_sf.skip_fullpel_search_using_startmv_refmv = boosted ? 0 : 1; if (!is_720p_or_larger) { sf->inter_sf.mv_cost_upd_level = INTERNAL_COST_UPD_SBROW_SET; @@ -993,9 +998,11 @@ sf->inter_sf.prune_nearmv_using_neighbors = PRUNE_NEARMV_LEVEL2; } - if (is_720p_or_larger) + if (is_720p_or_larger) { sf->part_sf.ext_part_eval_based_on_cur_best = (allow_screen_content_tools || frame_is_intra_only(cm)) ? 0 : 1; + sf->part_sf.auto_max_partition_based_on_simple_motion = NOT_IN_USE; + } if (is_480p_or_larger) { sf->tpl_sf.reduce_num_frames = 1; @@ -1008,11 +1015,9 @@ sf->inter_sf.prune_comp_ref_frames = 2; sf->inter_sf.prune_nearest_near_mv_using_refmv_weight = (boosted || allow_screen_content_tools) ? 0 : 1; - sf->mv_sf.skip_fullpel_search_using_startmv = boosted ? 0 : 2; + sf->mv_sf.skip_fullpel_search_using_startmv_refmv = boosted ? 0 : 2; - if (is_720p_or_larger) { - sf->part_sf.auto_max_partition_based_on_simple_motion = NOT_IN_USE; - } else if (is_480p_or_larger) { + if (is_480p_or_larger && !is_720p_or_larger) { sf->part_sf.auto_max_partition_based_on_simple_motion = DIRECT_PRED; } @@ -1047,6 +1052,12 @@ sf->inter_sf.prune_ref_mv_idx_search = 1; } + if (is_720p_or_larger) { + sf->mv_sf.use_bsize_dependent_search_method = 1; + } else { + sf->mv_sf.use_bsize_dependent_search_method = 2; + } + if (!is_720p_or_larger) { sf->tx_sf.tx_type_search.fast_inter_tx_type_prob_thresh = is_boosted_arf2_bwd_type ? 450 : 150; @@ -1059,6 +1070,11 @@ if (cpi->oxcf.enable_low_complexity_decode) set_good_speed_features_lc_dec_framesize_dependent(cpi, sf, speed); + + if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { + sf->intra_sf.skip_intra_in_interframe = 0; + } } static void set_good_speed_features_framesize_independent( @@ -1083,7 +1099,6 @@ sf->gm_sf.prune_ref_frame_for_gm_search = boosted ? 0 : 1; sf->gm_sf.disable_gm_search_based_on_stats = 1; - sf->part_sf.less_rectangular_check_level = 1; sf->part_sf.ml_prune_partition = 1; sf->part_sf.prune_ext_partition_types_search_level = 1; sf->part_sf.prune_part4_search = 2; @@ -1106,8 +1121,10 @@ sf->inter_sf.reduce_inter_modes = boosted ? 1 : 2; sf->inter_sf.selective_ref_frame = 1; sf->inter_sf.use_dist_wtd_comp_flag = DIST_WTD_COMP_SKIP_MV_SEARCH; + sf->inter_sf.enable_fast_compound_mode_search = 1; sf->interp_sf.use_fast_interpolation_filter_search = 1; + sf->interp_sf.disable_dual_filter = 1; sf->intra_sf.intra_pruning_with_hog = 1; @@ -1132,6 +1149,8 @@ sf->rd_sf.perform_coeff_opt = 1; sf->hl_sf.superres_auto_search_type = SUPERRES_AUTO_DUAL; + sf->lpf_sf.reduce_wiener_window_size = 1; + if (speed >= 1) { sf->hl_sf.adjust_num_frames_for_arf_filtering = allow_screen_content_tools ? 0 : 1; @@ -1164,6 +1183,8 @@ sf->inter_sf.reuse_inter_intra_mode = 1; sf->inter_sf.selective_ref_frame = 2; sf->inter_sf.skip_arf_compound = 1; + sf->inter_sf.prune_comp_using_best_single_mode_ref = 2; + sf->inter_sf.use_dist_wtd_comp_flag = DIST_WTD_COMP_DISABLED; sf->interp_sf.use_interp_filter = 1; @@ -1177,6 +1198,7 @@ sf->tx_sf.tx_type_search.ml_tx_split_thresh = 4000; sf->tx_sf.tx_type_search.prune_2d_txfm_mode = TX_TYPE_PRUNE_2; sf->tx_sf.tx_type_search.skip_tx_search = 1; + sf->tx_sf.prune_inter_tx_split_rd_eval_lvl = 1; sf->rd_sf.perform_coeff_opt = boosted ? 2 : 3; sf->rd_sf.tx_domain_dist_level = boosted ? 1 : 2; @@ -1214,21 +1236,17 @@ sf->inter_sf.fast_interintra_wedge_search = 1; sf->inter_sf.prune_comp_search_by_single_result = boosted ? 4 : 1; sf->inter_sf.prune_ext_comp_using_neighbors = 1; - sf->inter_sf.prune_comp_using_best_single_mode_ref = 2; sf->inter_sf.prune_comp_type_by_comp_avg = 2; sf->inter_sf.selective_ref_frame = 3; - sf->inter_sf.use_dist_wtd_comp_flag = DIST_WTD_COMP_DISABLED; - sf->inter_sf.enable_fast_compound_mode_search = 1; sf->inter_sf.reuse_mask_search_results = 1; set_txfm_rd_gate_level(sf->inter_sf.txfm_rd_gate_level, boosted ? 0 : 1); sf->inter_sf.inter_mode_txfm_breakout = boosted ? 0 : 1; sf->inter_sf.alt_ref_search_fp = 1; + sf->inter_sf.prune_inter_modes_based_on_tpl = 1; + sf->inter_sf.prune_single_ref = boosted ? 1 : 2; sf->interp_sf.adaptive_interp_filter_search = 1; - sf->interp_sf.disable_dual_filter = 1; - sf->intra_sf.disable_smooth_intra = - !frame_is_intra_only(&cpi->common) || (cpi->rc.frames_to_key > 1); sf->intra_sf.intra_pruning_with_hog = 2; sf->intra_sf.skip_intra_in_interframe = is_inter_frame ? 2 : 1; sf->intra_sf.skip_filter_intra_in_inter_frames = 1; @@ -1242,21 +1260,22 @@ sf->lpf_sf.prune_wiener_based_on_src_var = 1; sf->lpf_sf.prune_sgr_based_on_wiener = 1; sf->lpf_sf.disable_loop_restoration_chroma = boosted ? 0 : 1; - sf->lpf_sf.reduce_wiener_window_size = boosted ? 0 : 1; // TODO(any): Re-evaluate this feature set to 1 in speed 2. sf->tpl_sf.allow_compound_pred = 0; sf->tpl_sf.prune_ref_frames_in_tpl = 1; + + sf->tx_sf.prune_inter_tx_split_rd_eval_lvl = 2; } if (speed >= 3) { sf->hl_sf.high_precision_mv_usage = CURRENT_Q; + sf->hl_sf.weight_calc_level_in_tf = 1; sf->gm_sf.prune_ref_frame_for_gm_search = 1; sf->gm_sf.prune_zero_mv_with_sse = 1; sf->gm_sf.num_refinement_steps = 0; - sf->part_sf.less_rectangular_check_level = 2; sf->part_sf.simple_motion_search_prune_agg = allow_screen_content_tools ? SIMPLE_AGG_LVL0 @@ -1264,7 +1283,6 @@ sf->part_sf.prune_ext_part_using_split_info = 1; sf->part_sf.simple_motion_search_rect_split = 1; - sf->mv_sf.full_pixel_search_level = 1; sf->mv_sf.subpel_search_method = SUBPEL_TREE_PRUNED; sf->mv_sf.search_method = DIAMOND; sf->mv_sf.disable_second_mv = 2; @@ -1278,13 +1296,14 @@ // TODO(any): Experiment with the early exit mechanism for speeds 0, 1 and 2 // and clean-up the speed feature sf->inter_sf.perform_best_rd_based_gating_for_chroma = 1; - sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 0 : 1; + sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 1 : 2; sf->inter_sf.prune_comp_search_by_single_result = boosted ? 4 : 2; sf->inter_sf.selective_ref_frame = 5; sf->inter_sf.reuse_compound_type_decision = 1; set_txfm_rd_gate_level(sf->inter_sf.txfm_rd_gate_level, boosted ? 0 : (is_boosted_arf2_bwd_type ? 1 : 2)); sf->inter_sf.inter_mode_txfm_breakout = boosted ? 0 : 2; + sf->inter_sf.prune_single_ref = 2; sf->interp_sf.adaptive_interp_filter_search = 2; @@ -1301,7 +1320,6 @@ sf->tpl_sf.prune_intra_modes = 1; sf->tpl_sf.reduce_first_step_size = 6; sf->tpl_sf.subpel_force_stop = QUARTER_PEL; - sf->tpl_sf.gop_length_decision_method = 1; sf->tx_sf.adaptive_txb_search_level = boosted ? 2 : 3; sf->tx_sf.tx_type_search.use_skip_flag_prediction = 2; @@ -1346,7 +1364,7 @@ sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_MOTION_MODE] = boosted ? 0 : 5; sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_COMP_TYPE_MODE] = boosted ? 0 : 3; - sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 0 : 2; + sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 1 : 3; sf->inter_sf.prune_ext_comp_using_neighbors = 2; sf->inter_sf.prune_obmc_prob_thresh = INT_MAX; sf->inter_sf.disable_interinter_wedge_var_thresh = UINT_MAX; @@ -1385,7 +1403,6 @@ } if (speed >= 5) { - sf->hl_sf.weight_calc_level_in_tf = 1; sf->hl_sf.adjust_num_frames_for_arf_filtering = allow_screen_content_tools ? 0 : 2; @@ -1401,7 +1418,7 @@ sf->mv_sf.warp_search_method = WARP_SEARCH_DIAMOND; sf->inter_sf.prune_inter_modes_if_skippable = 1; - sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 1; + sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 3; sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_DEFAULT] = boosted ? 0 : 4; sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_COMP_TYPE_MODE] = boosted ? 0 : 5; sf->inter_sf.enable_fast_compound_mode_search = 2; @@ -1409,6 +1426,7 @@ sf->interp_sf.skip_interp_filter_search = boosted ? 0 : 1; sf->intra_sf.chroma_intra_pruning_with_hog = 3; + sf->intra_sf.disable_smooth_intra = 1; // TODO(any): Extend multi-winner mode processing support for inter frames sf->winner_mode_sf.multi_winner_mode_type = @@ -1416,7 +1434,7 @@ : MULTI_WINNER_MODE_OFF; // Disable Self-guided Loop restoration filter. - sf->lpf_sf.disable_sgr_filter = true; + sf->lpf_sf.enable_sgr_ep_pruning = 2; sf->lpf_sf.disable_wiener_coeff_refine_search = true; sf->tpl_sf.prune_starting_mv = 3; @@ -1436,9 +1454,9 @@ sf->gm_sf.downsample_level = 2; - sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 0 : 3; + sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 1 : 4; sf->inter_sf.selective_ref_frame = 6; - sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 2; + sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 4; sf->inter_sf.prune_ext_comp_using_neighbors = 3; sf->intra_sf.chroma_intra_pruning_with_hog = 4; @@ -1455,7 +1473,6 @@ sf->part_sf.prune_part4_search = 3; sf->mv_sf.simple_motion_subpel_force_stop = FULL_PEL; - sf->mv_sf.use_bsize_dependent_search_method = 1; sf->tpl_sf.gop_length_decision_method = 3; @@ -1474,6 +1491,32 @@ sf->tx_sf.adaptive_txb_search_level = 0; sf->tx_sf.tx_type_search.use_skip_flag_prediction = 0; } + + // Set speed features for the IQ and SSIMULACRA2 tuning modes + // Layered image encoding has different requirements than regular video + // coding. + // Mainly, most of these speed features undo an implicit assumption that + // keyframes are encoded at a better quality than inter-coded frames. + // This means the encoder needs to be more thorough at considering and + // performing RDO on intra block candidates vs. inter block candidates for + // the best compression efficiency. + // Finally, enabling certain coding tools are beneficial for layered image + // encoding in general. + if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { + sf->intra_sf.skip_intra_in_interframe = 0; + sf->inter_sf.inter_mode_rd_model_estimation = 0; + sf->mv_sf.use_intrabc = 1; + + // Don't prune intra candidates too aggressively, as it can cause more + // expensive inter candidates to be chosen instead + if (sf->intra_sf.intra_pruning_with_hog > 3) { + sf->intra_sf.intra_pruning_with_hog = 3; + } + if (sf->intra_sf.chroma_intra_pruning_with_hog > 3) { + sf->intra_sf.chroma_intra_pruning_with_hog = 3; + } + } } static void set_rt_speed_feature_framesize_dependent(const AV1_COMP *const cpi, @@ -1703,7 +1746,7 @@ if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN) { // TODO(marpan): Check settings for speed 7 and 8. if (speed >= 7) { - sf->rt_sf.reduce_mv_pel_precision_highmotion = 1; + sf->rt_sf.reduce_mv_pel_precision_highmotion = 0; sf->mv_sf.use_bsize_dependent_search_method = 0; sf->rt_sf.skip_cdef_sb = 1; sf->rt_sf.increase_color_thresh_palette = 1; @@ -1722,7 +1765,6 @@ sf->rt_sf.nonrd_prune_ref_frame_search = 3; sf->rt_sf.var_part_split_threshold_shift = 10; sf->mv_sf.subpel_search_method = SUBPEL_TREE_PRUNED_MORE; - sf->rt_sf.reduce_mv_pel_precision_highmotion = 3; sf->rt_sf.reduce_mv_pel_precision_lowcomplex = 1; sf->lpf_sf.cdef_pick_method = CDEF_PICK_FROM_Q; sf->rt_sf.nonrd_check_partition_merge_mode = 0; @@ -1740,7 +1782,7 @@ sf->rt_sf.prune_palette_search_nonrd = 1; sf->rt_sf.prune_intra_mode_using_best_sad_so_far = true; sf->rt_sf.rc_faster_convergence_static = 1; - sf->rt_sf.rc_compute_spatial_var_sc = 1; + sf->rt_sf.rc_compute_spatial_var_sc_kf = 1; } if (speed >= 11) { sf->rt_sf.skip_lf_screen = 2; @@ -1826,10 +1868,42 @@ // temporal filtering which may change the input source during encoding: // this causes an issue on resized frames when psnr is calculated, // so disable it here for frames that are resized (encoding width/height - // different from configured width/height). - if (is_psnr_calc_enabled(cpi) && (cpi->oxcf.frm_dim_cfg.width != cm->width || - cpi->oxcf.frm_dim_cfg.height != cm->height)) + // different from configured width/height). Also disable for spatial layers. + // Bug: 491358681 + if ((is_psnr_calc_enabled(cpi) && + (cpi->oxcf.frm_dim_cfg.width != cm->width || + cpi->oxcf.frm_dim_cfg.height != cm->height)) || + cpi->svc.number_spatial_layers > 1) sf->rt_sf.use_rtc_tf = 0; + + // This speed feature is causing artifacts with active_maps enabled, so + // disable for now. + if (cpi->active_map.enabled) + sf->rt_sf.set_zeromv_skip_based_on_source_sad = 0; + + if (is_one_pass_rt_lag_params(cpi)) { + const RefreshFrameInfo *const refresh_frame = &cpi->refresh_frame; + if (refresh_frame->alt_ref_frame) { + sf->rt_sf.source_metrics_sb_nonrd = 0; + sf->rt_sf.var_part_based_on_qidx = 0; + } + sf->rt_sf.use_nonrd_altref_frame = 1; + // For non-zero lag: disable the 3 speed features below for now, + // until further testing. + sf->rt_sf.use_rtc_tf = 0; + sf->rt_sf.nonrd_check_partition_merge_mode = 0; + sf->rt_sf.nonrd_check_partition_split = 0; + // These (nonrd) speed features that force zeromv-LAST early in partition + // are disabled since for src_frame_alt_ref frame the zeromv-ALTREF_FRAME + // mode is forced in the nonrd_pickmode. + if (cpi->rc.is_src_frame_alt_ref) { + sf->rt_sf.increase_source_sad_thresh = 0; + sf->rt_sf.part_early_exit_zeromv = 0; + } + // This feature is for CBR mode, turning if off means the gop interval + // will not be changed after encoding. + sf->rt_sf.gf_refresh_based_on_qp = 0; + } } static void set_rt_speed_features_framesize_independent(AV1_COMP *cpi, @@ -1940,7 +2014,7 @@ sf->tx_sf.inter_tx_size_search_init_depth_sqr = 1; sf->tx_sf.tx_type_search.prune_2d_txfm_mode = TX_TYPE_PRUNE_3; sf->tx_sf.refine_fast_tx_search_results = 0; - sf->tx_sf.tx_type_search.fast_intra_tx_type_search = 1; + sf->tx_sf.tx_type_search.fast_intra_tx_type_search = 2; sf->tx_sf.tx_type_search.use_skip_flag_prediction = 2; sf->tx_sf.tx_type_search.winner_mode_tx_type_pruning = 4; @@ -1968,7 +2042,7 @@ sf->rt_sf.mode_search_skip_flags |= FLAG_SKIP_INTRA_DIRMISMATCH; sf->rt_sf.num_inter_modes_for_tx_search = 5; sf->rt_sf.prune_inter_modes_using_temp_var = 1; - sf->rt_sf.use_real_time_ref_set = 1; + sf->rt_sf.use_real_time_ref_set = is_one_pass_rt_lag_params(cpi) ? 0 : 1; sf->rt_sf.use_simple_rd_model = 1; sf->rt_sf.prune_inter_modes_with_golden_ref = boosted ? 0 : 1; // TODO(any): This sf could be removed. @@ -1997,6 +2071,13 @@ sf->rt_sf.use_fast_fixed_part = 0; sf->rt_sf.increase_source_sad_thresh = 0; + if (is_one_pass_rt_lag_params(cpi) && speed <= 6) { + sf->hl_sf.frame_parameter_update = 1; + sf->inter_sf.use_dist_wtd_comp_flag = 0; + sf->inter_sf.disable_masked_comp = 1; + sf->inter_sf.disable_onesided_comp = 1; + } + if (speed >= 6) { sf->mv_sf.use_fullpel_costlist = 1; @@ -2096,7 +2177,7 @@ sf->rt_sf.var_part_based_on_qidx = 4; sf->rt_sf.partition_direct_merging = 1; sf->rt_sf.prune_compoundmode_with_singlemode_var = false; - sf->mv_sf.use_bsize_dependent_search_method = 2; + sf->mv_sf.use_bsize_dependent_search_method = 4; sf->rt_sf.prune_hv_pred_modes_using_src_sad = true; } if (speed >= 9) { @@ -2124,6 +2205,11 @@ cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN) { sf->winner_mode_sf.dc_blk_pred_level = 3; } + + if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { + sf->intra_sf.skip_intra_in_interframe = 0; + } } static inline void init_hl_sf(HIGH_LEVEL_SPEED_FEATURES *hl_sf) { @@ -2152,7 +2238,7 @@ } static inline void init_tpl_sf(TPL_SPEED_FEATURES *tpl_sf) { - tpl_sf->gop_length_decision_method = 0; + tpl_sf->gop_length_decision_method = 1; tpl_sf->prune_intra_modes = 0; tpl_sf->prune_starting_mv = 0; tpl_sf->reduce_first_step_size = 0; @@ -2200,6 +2286,7 @@ -1; // -1 means not enabled. } part_sf->ml_partition_search_breakout_model_index = 0; + part_sf->ml_4_partition_search_level_index = 0; part_sf->simple_motion_search_prune_agg = SIMPLE_AGG_LVL0; part_sf->simple_motion_search_split = 0; part_sf->simple_motion_search_prune_rect = 0; @@ -2243,7 +2330,7 @@ mv_sf->use_downsampled_sad = 0; mv_sf->disable_extensive_joint_motion_search = 0; mv_sf->disable_second_mv = 0; - mv_sf->skip_fullpel_search_using_startmv = 0; + mv_sf->skip_fullpel_search_using_startmv_refmv = 0; mv_sf->warp_search_method = WARP_SEARCH_SQUARE; mv_sf->warp_search_iters = 8; mv_sf->use_intrabc = 1; @@ -2301,6 +2388,8 @@ inter_sf->limit_txfm_eval_per_mode = 0; inter_sf->skip_arf_compound = 0; inter_sf->bias_warp_mode_rd_scale_pct = 0; + inter_sf->bias_obmc_mode_rd_scale_pct = 0.0f; + inter_sf->skip_cmp_using_top_cmp_avg_est_rd_lvl = 0; set_txfm_rd_gate_level(inter_sf->txfm_rd_gate_level, 0); } @@ -2361,6 +2450,7 @@ tx_sf->prune_tx_size_level = 0; tx_sf->prune_intra_tx_depths_using_nn = false; tx_sf->use_rd_based_breakout_for_intra_tx_search = false; + tx_sf->prune_inter_tx_split_rd_eval_lvl = 0; } static inline void init_rd_sf(RD_CALC_SPEED_FEATURES *rd_sf, @@ -2420,6 +2510,7 @@ lpf_sf->lpf_pick = LPF_PICK_FROM_FULL_IMAGE; lpf_sf->use_coarse_filter_level_search = 0; lpf_sf->cdef_pick_method = CDEF_FULL_SEARCH; + lpf_sf->zero_low_cdef_strengths = 0; // Set decoder side speed feature to use less dual sgr modes lpf_sf->dual_sgr_penalty_level = 0; // Enable Wiener and Self-guided Loop restoration filters by default. @@ -2454,7 +2545,7 @@ rt_sf->overshoot_detection_cbr = NO_DETECTION; rt_sf->check_scene_detection = 0; rt_sf->rc_adjust_keyframe = 0; - rt_sf->rc_compute_spatial_var_sc = 0; + rt_sf->rc_compute_spatial_var_sc_kf = 0; rt_sf->prefer_large_partition_blocks = 0; rt_sf->use_temporal_noise_estimate = 0; rt_sf->fullpel_search_step_param = 0; @@ -2742,31 +2833,15 @@ if (speed < 1 || speed > 3) return; const AV1_COMMON *const cm = &cpi->common; - const bool is_between_608p_and_720p = AOMMIN(cm->width, cm->height) >= 608 && - AOMMIN(cm->width, cm->height) <= 720; - const bool is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; + const bool is_between_608p_and_1080p = AOMMIN(cm->width, cm->height) >= 608 && + AOMMIN(cm->width, cm->height) <= 1080; const bool is_vertical_video = cm->width < cm->height; - const FRAME_UPDATE_TYPE update_type = - get_frame_update_type(&cpi->ppi->gf_group, cpi->gf_frame_index); - const bool leaf_and_overlay_frames = - (update_type == LF_UPDATE || update_type == OVERLAY_UPDATE || - update_type == INTNL_OVERLAY_UPDATE); // Speed features for vertical videos - if (is_vertical_video && is_between_608p_and_720p) { + if (is_vertical_video && is_between_608p_and_1080p) { sf->lpf_sf.min_lr_unit_size = RESTORATION_UNITSIZE_MAX >> 1; sf->lpf_sf.max_lr_unit_size = RESTORATION_UNITSIZE_MAX >> 1; } - - // Speed features for regular videos - if (!is_vertical_video && is_720p_or_larger) { - if (speed <= 2 && leaf_and_overlay_frames) { - // For 720p and above, only enable this feature for leaf and overlay - // frames to avoid quality degradation on ARF frames. - sf->lpf_sf.min_lr_unit_size = RESTORATION_UNITSIZE_MAX >> 1; - sf->lpf_sf.max_lr_unit_size = RESTORATION_UNITSIZE_MAX >> 1; - } - } } // Override some speed features based on qindex @@ -2783,6 +2858,13 @@ const int is_arf2_bwd_type = cpi->ppi->gf_group.update_type[cpi->gf_frame_index] == INTNL_ARF_UPDATE; + if (cpi->oxcf.mode == ALLINTRA || cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { + if (cm->quant_params.base_qindex <= 140) { + sf->lpf_sf.zero_low_cdef_strengths = 1; + } + } + if (cpi->oxcf.mode == REALTIME) { if (speed >= 6) { const int qindex_thresh = boosted ? 190 : (is_720p_or_larger ? 120 : 150); @@ -2865,9 +2947,9 @@ } } - if (speed >= 4) { + if (speed >= 3) { // Disable rectangular partitions for lower quantizers - const int aggr = AOMMIN(1, speed - 4); + const int aggr = (speed <= 4) ? 0 : 1; const int qindex_thresh[2] = { 65, 80 }; int disable_rect_part; disable_rect_part = !boosted; @@ -2894,8 +2976,13 @@ sf->mv_sf.search_method = NSTEP_8PT; } } + sf->part_sf.less_rectangular_check_level = 1; } + if (speed == 3) + sf->part_sf.less_rectangular_check_level = + (cm->quant_params.base_qindex >= 170) ? 1 : 2; + if (speed >= 4) { // Disable LR search at low and high quantizers and enable only for // mid-quantizer range. @@ -2904,9 +2991,11 @@ const int qindex_high[2] = { 180, 160 }; if (cm->quant_params.base_qindex <= qindex_low[is_720p_or_larger] || cm->quant_params.base_qindex > qindex_high[is_720p_or_larger]) { - sf->lpf_sf.disable_loop_restoration_luma = 1; + sf->lpf_sf.disable_sgr_filter = true; + sf->lpf_sf.disable_wiener_coeff_refine_search = true; } } + sf->part_sf.less_rectangular_check_level = 2; } if (speed == 1) {
diff --git a/av1/encoder/speed_features.h b/av1/encoder/speed_features.h index 01f55cd..8bb6a0d 100644 --- a/av1/encoder/speed_features.h +++ b/av1/encoder/speed_features.h
@@ -256,6 +256,11 @@ typedef struct { TX_TYPE_PRUNE_MODE prune_2d_txfm_mode; + + // Limit the intra transform search type. + // 1 : Limit the intra transform search type to the ones in the table + // av1_derived_intra_tx_used_flag[INTRA_MODES]. + // 2 : Limit the intra transform search type to the default transform. int fast_intra_tx_type_search; // INT_MAX: Disable fast search. @@ -673,6 +678,9 @@ // ML based partition search breakout model index int ml_partition_search_breakout_model_index; + // ML based partition search breakout model index + int ml_4_partition_search_level_index; + // Aggressiveness levels for pruning split and rectangular partitions based on // simple_motion_search. SIMPLE_AGG_LVL0 to SIMPLE_AGG_LVL5 correspond to // simple motion search based pruning. QIDX_BASED_AGG_LVL1 corresponds to @@ -858,7 +866,9 @@ SEARCH_METHODS search_method; // Enable the use of faster, less accurate mv search method - // 0: disable, 1: if bsize >= BLOCK_32X32, 2: based on bsize, SAD and qp + // 0: Disable + // 1 - 3: Disable for larger bsize + // 4: Based on bsize, SAD and qp // TODO(chiyotsai@google.com): Take the clip's resolution and mv activity into // account. int use_bsize_dependent_search_method; @@ -953,11 +963,14 @@ // 2: disable second MV int disable_second_mv; - // Skips full pixel search based on start mv of prior ref_mv_idx. + // Skips full pixel search based on closeness of start mv and ref mv + // of previous search. // 0: Disabled - // 1: Skips the full pixel search upto 4 neighbor full-pel MV positions. - // 2: Skips the full pixel search upto 8 neighbor full-pel MV positions. - int skip_fullpel_search_using_startmv; + // 1: Skips the full pixel search upto 4 neighbor full-pel start MV and ref MV + // positions. + // 2: Skips the full pixel search upto 8 neighbor full-pel start MV and ref MV + // positions. + int skip_fullpel_search_using_startmv_refmv; // Method to use for refining WARPED_CAUSAL motion vectors // TODO(rachelbarker): Can this be unified with OBMC in some way? @@ -1010,7 +1023,7 @@ int alt_ref_search_fp; // Prune reference frames for single prediction modes based on temporal - // distance and pred MV SAD. Feasible values are 0, 1, 2. The feature is + // distance and pred MV SAD. Feasible values are 0-4. The feature is // disabled for 0. An increasing value indicates more aggressive pruning // threshold. int prune_single_ref; @@ -1146,7 +1159,9 @@ // Prune inter modes based on tpl stats // 0 : no pruning - // 1 - 3 indicate increasing aggressiveness in order. + // 1 : Allow pruning of LAST2 frame + // 2 - 4: Allow pruning of all reference frames with increased aggressiveness + // of pruning in order int prune_inter_modes_based_on_tpl; // Skip NEARMV and NEAR_NEARMV modes using ref frames of above and left @@ -1219,6 +1234,17 @@ // encoder decisions are biased against local warp, favoring low complexity // modes. int bias_warp_mode_rd_scale_pct; + + // Percentage of scaling used to increase the rd cost of obmc motion mode so + // that encoder decisions are biased against local obmc, favoring low + // complexity modes. + float bias_obmc_mode_rd_scale_pct; + + // Avoid further evaluation of compound modes using top estimate RD Costs of + // compound average. + // Values are 0 (not used),1 - 3 with progressively increasing + // aggressiveness, i.e., decreasing number of top candidates. + int skip_cmp_using_top_cmp_avg_est_rd_lvl; } INTER_MODE_SPEED_FEATURES; typedef struct INTERP_FILTER_SPEED_FEATURES { @@ -1440,6 +1466,12 @@ // for speed 3, 4, 5, 6, 7 and 8 on a typical image dataset with coding // performance change less than 0.004%. bool use_rd_based_breakout_for_intra_tx_search; + + // Prune RD evaluation of transform split using RD Costs of transform no-split + // of inter modes that are evaluated so far. + // Values are 0 (not used), 1 - 2 with progressively increasing + // aggressiveness, i.e., decreasing number of top candidates + int prune_inter_tx_split_rd_eval_lvl; } TX_SPEED_FEATURES; typedef struct RD_CALC_SPEED_FEATURES { @@ -1560,6 +1592,12 @@ // Control how the CDEF strength is determined. CDEF_PICK_METHOD cdef_pick_method; + // Decoder side speed feature for adaptive CDEF with strength reduction. + // Zero out values with low CDEF strengths (luma and/or chroma). This is + // done as CDEF is a relatively-expensive filter to compute during decode. + // This speed feature is only enabled in all intra mode. + bool zero_low_cdef_strengths; + // Decoder side speed feature to add penalty for use of dual-sgr filters. // Takes values 0 - 10, 0 indicating no penalty and each additional level // adding a penalty of 1% @@ -1574,7 +1612,14 @@ // search_switchable() int switchable_lr_with_bias_level; - // prune sgr ep using binary search like mechanism + // Enable fast search in self guided restoration + // 0 : Search over all 16 SGR projection parameters listed + // in av1_sgr_params[SGRPROJ_PARAMS]. + // 1 : Approximate search using binary search like mechanism, + // a total of 8 SGR projection parameters are searched. + // 2 : Search only 'ep' values in + // sgproj_ep_grp1_seed[SGRPROJ_EP_GRP1_SEARCH_COUNT], + // a total of 4 SGR projection parameters are searched. int enable_sgr_ep_pruning; // Disable loop restoration for Chroma plane @@ -1724,8 +1769,8 @@ // For keyframes in rtc: adjust the rc_bits_per_mb, to reduce overshoot. int rc_adjust_keyframe; - // On scene change: compute spatial variance. - int rc_compute_spatial_var_sc; + // On scene change or keyframe: compute spatial variance. + int rc_compute_spatial_var_sc_kf; // For nonrd mode: Prefer larger partition blks in variance based partitioning // 0: disabled, 1-3: increasing aggressiveness
diff --git a/av1/encoder/temporal_filter.c b/av1/encoder/temporal_filter.c index 1fab6bf..183077c 100644 --- a/av1/encoder/temporal_filter.c +++ b/av1/encoder/temporal_filter.c
@@ -48,8 +48,90 @@ // Forward Declaration. static void tf_determine_block_partition(const MV block_mv, const int block_mse, + const MV *midblock_mvs, + const int *midblock_mses, MV *subblock_mvs, int *subblock_mses); +// Do motion search for 1 subblock. The block size can be 32x32 or 16x16. +static void subblock_motion_search( + AV1_COMP *cpi, MACROBLOCK *mb, const YV12_BUFFER_CONFIG *frame_to_filter, + const YV12_BUFFER_CONFIG *ref_frame, BLOCK_SIZE tf_block_size, int mb_row, + int mb_col, BLOCK_SIZE subblock_size, int idx, int subblock_ofst_i, + int subblock_ofst_j, FULLPEL_MOTION_SEARCH_PARAMS *full_ms_params, + SUBPEL_MOTION_SEARCH_PARAMS *ms_params, const MV *ref_mv, + FULLPEL_MV start_mv, const search_site_config *search_site_cfg, + SEARCH_METHODS search_method, MV_COST_TYPE mv_cost_type, int step_param, + SUBPEL_SEARCH_TYPE subpel_ms_type, MV *subblock_mvs, int *subblock_mses, + FULLPEL_MV_STATS *best_mv_stats, int q) { + MACROBLOCKD *const mbd = &mb->e_mbd; + + const int mb_height = block_size_high[tf_block_size]; + const int mb_width = block_size_wide[tf_block_size]; + const int mi_h = mi_size_high_log2[tf_block_size]; + const int mi_w = mi_size_wide_log2[tf_block_size]; + + const int y_stride = frame_to_filter->y_stride; + assert(y_stride == ref_frame->y_stride); + const int y_offset = mb_row * mb_height * y_stride + mb_col * mb_width; + + const int subblock_height = block_size_high[subblock_size]; + const int subblock_width = block_size_wide[subblock_size]; + const int subblock_pels = subblock_height * subblock_width; + + int_mv best_mv; // Searched motion vector. + unsigned int sse, error; + int distortion; + int cost_list[5]; + + av1_set_mv_row_limits(&cpi->common.mi_params, &mb->mv_limits, + (mb_row << mi_h) + (subblock_ofst_i >> MI_SIZE_LOG2), + (subblock_height >> MI_SIZE_LOG2), + cpi->oxcf.border_in_pixels); + av1_set_mv_col_limits(&cpi->common.mi_params, &mb->mv_limits, + (mb_col << mi_w) + (subblock_ofst_j >> MI_SIZE_LOG2), + (subblock_width >> MI_SIZE_LOG2), + cpi->oxcf.border_in_pixels); + const int boffset = subblock_ofst_i * y_stride + subblock_ofst_j; + mb->plane[0].src.buf = frame_to_filter->y_buffer + y_offset + boffset; + mbd->plane[0].pre[0].buf = ref_frame->y_buffer + y_offset + boffset; + av1_make_default_fullpel_ms_params(full_ms_params, cpi, mb, subblock_size, + ref_mv, start_mv, search_site_cfg, + search_method, + /*fine_search_interval=*/0); + full_ms_params->run_mesh_search = 1; + full_ms_params->mv_cost_params.mv_cost_type = mv_cost_type; + + // May need to re-tune prune_mesh_search after increase TF + // block to 64x64. + if (cpi->sf.mv_sf.prune_mesh_search == PRUNE_MESH_SEARCH_LVL_1) { + // Enable prune_mesh_search based on q for PRUNE_MESH_SEARCH_LVL_1. + full_ms_params->prune_mesh_search = (q <= 20) ? 0 : 1; + full_ms_params->mesh_search_mv_diff_threshold = 2; + } + + av1_full_pixel_search(start_mv, full_ms_params, step_param, + cond_cost_list(cpi, cost_list), &best_mv.as_fullmv, + best_mv_stats, NULL); + + av1_make_default_subpel_ms_params(ms_params, cpi, mb, subblock_size, ref_mv, + cost_list); + ms_params->forced_stop = EIGHTH_PEL; + ms_params->var_params.subpel_search_type = subpel_ms_type; + // Since we are merely refining the result from full pixel + // search, we don't need regularization for subpel search + ms_params->mv_cost_params.mv_cost_type = MV_COST_NONE; + best_mv_stats->err_cost = 0; + + MV subpel_start_mv = get_mv_from_fullmv(&best_mv.as_fullmv); + assert(av1_is_subpelmv_in_range(&ms_params->mv_limits, subpel_start_mv)); + error = cpi->mv_search_params.find_fractional_mv_step( + &mb->e_mbd, &cpi->common, ms_params, subpel_start_mv, best_mv_stats, + &best_mv.as_mv, &distortion, &sse, NULL); + + subblock_mses[idx] = DIVIDE_AND_ROUND(error, subblock_pels); + subblock_mvs[idx] = best_mv.as_mv; +} + // This function returns the minimum and maximum log variances for 4x4 sub // blocks in the current block. static inline void get_log_var_4x4sub_blk( @@ -87,6 +169,13 @@ static int get_q(const AV1_COMP *cpi) { const GF_GROUP *gf_group = &cpi->ppi->gf_group; const FRAME_TYPE frame_type = gf_group->frame_type[cpi->gf_frame_index]; + + if (cpi->oxcf.rc_cfg.mode == AOM_Q) { + int cq_level = cpi->oxcf.rc_cfg.cq_level; + return (int)av1_convert_qindex_to_q(cq_level, + cpi->common.seq_params->bit_depth); + } + const int q = (int)av1_convert_qindex_to_q(cpi->ppi->p_rc.avg_frame_qindex[frame_type], cpi->common.seq_params->bit_depth); @@ -229,6 +318,10 @@ FULLPEL_MV_STATS best_mv_stats; int block_mse = INT_MAX; MV block_mv = kZeroMv; + // 32x32 block motion search results. + int midblock_mses[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; + MV midblock_mvs[4] = { kZeroMv, kZeroMv, kZeroMv, kZeroMv }; + const int q = get_q(cpi); av1_make_default_fullpel_ms_params(&full_ms_params, cpi, mb, block_size, @@ -282,54 +375,49 @@ *is_dc_diff_large = 50 * error < sse; if (src_var <= 2 * (int64_t)distortion) *is_low_cntras = 1; + // On 4 mid-blocks in the 64x64 tf block. if (allow_me_for_sub_blks) { - // On 4 sub-blocks. - const BLOCK_SIZE subblock_size = av1_ss_size_lookup[block_size][1][1]; - const int subblock_height = block_size_high[subblock_size]; - const int subblock_width = block_size_wide[subblock_size]; - const int subblock_pels = subblock_height * subblock_width; - start_mv = get_fullmv_from_mv(ref_mv); + // midblock_size is 32x32, which corresponds to each 32x32 block in the + // 64x64 block. + const BLOCK_SIZE midblock_size = av1_ss_size_lookup[block_size][1][1]; + const int midblock_height = block_size_high[midblock_size]; + const int midblock_width = block_size_wide[midblock_size]; + int midblock_idx = 0; + for (int i = 0; i < mb_height; i += midblock_height) { + for (int j = 0; j < mb_width; j += midblock_width) { + start_mv = get_fullmv_from_mv(ref_mv); - int subblock_idx = 0; - for (int i = 0; i < mb_height; i += subblock_height) { - for (int j = 0; j < mb_width; j += subblock_width) { - const int offset = i * y_stride + j; - mb->plane[0].src.buf = frame_to_filter->y_buffer + y_offset + offset; - mbd->plane[0].pre[0].buf = ref_frame->y_buffer + y_offset + offset; - av1_make_default_fullpel_ms_params( - &full_ms_params, cpi, mb, subblock_size, &baseline_mv, start_mv, - search_site_cfg, search_method, - /*fine_search_interval=*/0); - full_ms_params.run_mesh_search = 1; - full_ms_params.mv_cost_params.mv_cost_type = mv_cost_type; + subblock_motion_search( + cpi, mb, frame_to_filter, ref_frame, block_size, mb_row, mb_col, + midblock_size, midblock_idx, i, j, &full_ms_params, &ms_params, + &baseline_mv, start_mv, search_site_cfg, search_method, + mv_cost_type, step_param, subpel_search_type, midblock_mvs, + midblock_mses, &best_mv_stats, q); - if (cpi->sf.mv_sf.prune_mesh_search == PRUNE_MESH_SEARCH_LVL_1) { - // Enable prune_mesh_search based on q for PRUNE_MESH_SEARCH_LVL_1. - full_ms_params.prune_mesh_search = (q <= 20) ? 0 : 1; - full_ms_params.mesh_search_mv_diff_threshold = 2; + // On 4 sub-blocks in 1 mid-block. + { + const BLOCK_SIZE subblock_size = + av1_ss_size_lookup[midblock_size][1][1]; + const int subblock_height = block_size_high[subblock_size]; + const int subblock_width = block_size_wide[subblock_size]; + + start_mv = get_fullmv_from_mv(&midblock_mvs[midblock_idx]); + + int bidx = midblock_idx * 4; + for (int bi = 0; bi < midblock_height; bi += subblock_height) { + for (int bj = 0; bj < midblock_width; bj += subblock_width) { + subblock_motion_search( + cpi, mb, frame_to_filter, ref_frame, block_size, mb_row, + mb_col, subblock_size, bidx, (i + bi), (j + bj), + &full_ms_params, &ms_params, &baseline_mv, start_mv, + search_site_cfg, search_method, mv_cost_type, step_param, + subpel_search_type, subblock_mvs, subblock_mses, + &best_mv_stats, q); + ++bidx; + } + } } - av1_full_pixel_search(start_mv, &full_ms_params, step_param, - cond_cost_list(cpi, cost_list), - &best_mv.as_fullmv, &best_mv_stats, NULL); - - av1_make_default_subpel_ms_params(&ms_params, cpi, mb, subblock_size, - &baseline_mv, cost_list); - ms_params.forced_stop = EIGHTH_PEL; - ms_params.var_params.subpel_search_type = subpel_search_type; - // Since we are merely refining the result from full pixel search, we - // don't need regularization for subpel search - ms_params.mv_cost_params.mv_cost_type = MV_COST_NONE; - best_mv_stats.err_cost = 0; - - subpel_start_mv = get_mv_from_fullmv(&best_mv.as_fullmv); - assert( - av1_is_subpelmv_in_range(&ms_params.mv_limits, subpel_start_mv)); - error = cpi->mv_search_params.find_fractional_mv_step( - &mb->e_mbd, &cpi->common, &ms_params, subpel_start_mv, - &best_mv_stats, &best_mv.as_mv, &distortion, &sse, NULL); - subblock_mses[subblock_idx] = DIVIDE_AND_ROUND(error, subblock_pels); - subblock_mvs[subblock_idx] = best_mv.as_mv; - ++subblock_idx; + ++midblock_idx; } } } @@ -341,11 +429,11 @@ // Make partition decision. if (allow_me_for_sub_blks) { - tf_determine_block_partition(block_mv, block_mse, subblock_mvs, - subblock_mses); + tf_determine_block_partition(block_mv, block_mse, midblock_mvs, + midblock_mses, subblock_mvs, subblock_mses); } else { - // Copy 32X32 block mv and mse values to sub blocks - for (int i = 0; i < 4; ++i) { + // Copy 64X64 block mv and mse values to sub blocks + for (int i = 0; i < NUM_16X16; ++i) { subblock_mvs[i] = block_mv; subblock_mses[i] = block_mse; } @@ -365,6 +453,8 @@ // block_mv: Motion vector for the entire block (ONLY as reference). // block_mse: Motion search error (MSE) for the entire block (ONLY as // reference). +// midblock_mvs: Pointer to the motion vectors for 4 mid-blocks. +// midblock_mses: Pointer to the search errors (MSE) for 4 mid-blocks. // subblock_mvs: Pointer to the motion vectors for 4 sub-blocks (will be // modified based on the partition decision). // subblock_mses: Pointer to the search errors (MSE) for 4 sub-blocks (will @@ -373,23 +463,53 @@ // Nothing will be returned. Results are saved in `subblock_mvs` and // `subblock_mses`. static void tf_determine_block_partition(const MV block_mv, const int block_mse, + const MV *midblock_mvs, + const int *midblock_mses, MV *subblock_mvs, int *subblock_mses) { int min_subblock_mse = INT_MAX; int max_subblock_mse = INT_MIN; int64_t sum_subblock_mse = 0; - for (int i = 0; i < 4; ++i) { + int i; + + // Go through 4 32x32 blocks. + for (int idx = 0; idx < 4; ++idx) { + min_subblock_mse = INT_MAX; + max_subblock_mse = INT_MIN; + sum_subblock_mse = 0; + + const int sub_idx = idx * 4; + for (i = sub_idx; i < sub_idx + 4; ++i) { + sum_subblock_mse += subblock_mses[i]; + min_subblock_mse = AOMMIN(min_subblock_mse, subblock_mses[i]); + max_subblock_mse = AOMMAX(max_subblock_mse, subblock_mses[i]); + } + + if (((midblock_mses[idx] * 15 <= sum_subblock_mse * 4) && + max_subblock_mse - min_subblock_mse < 48) || + ((midblock_mses[idx] * 14 <= sum_subblock_mse * 4) && + max_subblock_mse - min_subblock_mse < 24)) { // No split. + for (i = sub_idx; i < sub_idx + 4; ++i) { + subblock_mvs[i] = midblock_mvs[idx]; + subblock_mses[i] = midblock_mses[idx]; + } + } + } + + min_subblock_mse = INT_MAX; + max_subblock_mse = INT_MIN; + sum_subblock_mse = 0; + for (i = 0; i < NUM_16X16; ++i) { sum_subblock_mse += subblock_mses[i]; min_subblock_mse = AOMMIN(min_subblock_mse, subblock_mses[i]); max_subblock_mse = AOMMAX(max_subblock_mse, subblock_mses[i]); } - // TODO(any): The following magic numbers may be tuned to improve the - // performance OR find a way to get rid of these magic numbers. - if (((block_mse * 15 < sum_subblock_mse * 4) && - max_subblock_mse - min_subblock_mse < 48) || - ((block_mse * 14 < sum_subblock_mse * 4) && - max_subblock_mse - min_subblock_mse < 24)) { // No split. - for (int i = 0; i < 4; ++i) { + if (((block_mse * 15 <= sum_subblock_mse) && + (max_subblock_mse - min_subblock_mse) * 16 < sum_subblock_mse * 3) || + ((block_mse * 14 <= sum_subblock_mse) && + (max_subblock_mse - min_subblock_mse) * 8 < + sum_subblock_mse)) { // No split. + for (i = 0; i < NUM_16X16; ++i) { subblock_mvs[i] = block_mv; subblock_mses[i] = block_mse; } @@ -459,35 +579,45 @@ const int plane_w = mb_width >> subsampling_x; // Plane width. const int plane_y = mb_y >> subsampling_y; // Y-coord (Top-left). const int plane_x = mb_x >> subsampling_x; // X-coord (Top-left). - const int h = plane_h >> 1; // Sub-block height. - const int w = plane_w >> 1; // Sub-block width. - const int is_y_plane = (plane == 0); // Is Y-plane? + + const int h32 = plane_h >> 1; // 32x32 sub-block height. + const int w32 = plane_w >> 1; // 32x32 sub-block width. + const int h16 = plane_h >> 2; // 16x16 sub-block height. + const int w16 = plane_w >> 2; // 16x16 sub-block width. + + const int is_y_plane = (plane == 0); // Is Y-plane? const struct buf_2d ref_buf = { NULL, ref_frame->buffers[plane], ref_frame->widths[is_y_plane ? 0 : 1], ref_frame->heights[is_y_plane ? 0 : 1], ref_frame->strides[is_y_plane ? 0 : 1] }; - // Handle each subblock. - int subblock_idx = 0; - for (int i = 0; i < plane_h; i += h) { - for (int j = 0; j < plane_w; j += w) { - // Choose proper motion vector. - const MV mv = subblock_mvs[subblock_idx++]; - assert(mv.row >= INT16_MIN && mv.row <= INT16_MAX && - mv.col >= INT16_MIN && mv.col <= INT16_MAX); + const int sub_y[4] = { 0, 0, h32, h32 }; + const int sub_x[4] = { 0, w32, 0, w32 }; + // Handle each 16x16 subblock. + for (int idx = 0; idx < 4; ++idx) { + int subblock_idx = idx * 4; + for (int i = 0; i < h32; i += h16) { + for (int j = 0; j < w32; j += w16) { + // Choose proper motion vector. + const MV mv = subblock_mvs[subblock_idx++]; + assert(mv.row >= INT16_MIN && mv.row <= INT16_MAX && + mv.col >= INT16_MIN && mv.col <= INT16_MAX); - const int y = plane_y + i; - const int x = plane_x + j; + const int y = plane_y + sub_y[idx] + i; + const int x = plane_x + sub_x[idx] + j; - // Build predictior for each sub-block on current plane. - InterPredParams inter_pred_params; - av1_init_inter_params(&inter_pred_params, w, h, y, x, subsampling_x, - subsampling_y, bit_depth, is_high_bitdepth, - is_intrabc, scale, &ref_buf, interp_filters); - inter_pred_params.conv_params = get_conv_params(0, plane, bit_depth); - av1_enc_build_one_inter_predictor(&pred[plane_offset + i * plane_w + j], - plane_w, &mv, &inter_pred_params); + // Build predictior for each sub-block on current plane. + InterPredParams inter_pred_params; + av1_init_inter_params(&inter_pred_params, w16, h16, y, x, + subsampling_x, subsampling_y, bit_depth, + is_high_bitdepth, is_intrabc, scale, &ref_buf, + interp_filters); + inter_pred_params.conv_params = get_conv_params(0, plane, bit_depth); + av1_enc_build_one_inter_predictor( + &pred[plane_offset + (sub_y[idx] + i) * plane_w + sub_x[idx] + j], + plane_w, &mv, &inter_pred_params); + } } } plane_offset += plane_h * plane_w; @@ -704,8 +834,10 @@ const double n_decay = 0.5 + log(2 * noise_levels[plane] + 5.0); decay_factor[plane] = 1 / (n_decay * q_decay * s_decay); } - double d_factor[4] = { 0 }; - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + + // Figure out each 16x16 block's d_factor beforehand. + double d_factor[NUM_16X16] = { 0 }; + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); @@ -789,7 +921,13 @@ // Combine window error and block error, and normalize it. const double window_error = sum_square_diff * inv_num_ref_pixels; - const int subblock_idx = (i >= h / 2) * 2 + (j >= w / 2); + + // 16x16 block index + const int y32 = i / (h / 2); + const int x32 = j / (w / 2); + const int y16 = (i % (h / 2)) / (h / 4); + const int x16 = (j % (w / 2)) / (w / 4); + const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); const double block_error = (double)subblock_mses[subblock_idx]; const double combined_error = weight_factor * window_error + block_error * inv_factor; @@ -923,6 +1061,19 @@ const GF_GROUP *gf_group = &cpi->ppi->gf_group; const FRAME_TYPE frame_type = gf_group->frame_type[cpi->gf_frame_index]; +#if CONFIG_AV1_HIGHBITDEPTH + // Determine whether the video is with `YUV 4:2:2` format, since the avx2/sse2 + // function only supports square block size. We will use C function instead + // for videos with `YUV 4:2:2` format. + int is_yuv422_format = 0; + for (int plane = 1; plane < num_planes; ++plane) { + if (mbd->plane[plane].subsampling_x != mbd->plane[plane].subsampling_y) { + is_yuv422_format = 1; + break; + } + } +#endif + // Do filtering. FRAME_DIFF *diff = &td->tf_data.diff; av1_set_mv_row_limits(&cpi->common.mi_params, &mb->mv_limits, @@ -960,8 +1111,16 @@ if (frames[frame] == NULL) continue; // Motion search. - MV subblock_mvs[4] = { kZeroMv, kZeroMv, kZeroMv, kZeroMv }; - int subblock_mses[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; + // block size is 64x64. 16 16x16 in 1 64x64. + // Store motion search results in 16x16 units. + MV subblock_mvs[NUM_16X16] = { kZeroMv, kZeroMv, kZeroMv, kZeroMv, + kZeroMv, kZeroMv, kZeroMv, kZeroMv, + kZeroMv, kZeroMv, kZeroMv, kZeroMv, + kZeroMv, kZeroMv, kZeroMv, kZeroMv }; + int subblock_mses[NUM_16X16] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX, + INT_MAX, INT_MAX, INT_MAX, INT_MAX, + INT_MAX, INT_MAX, INT_MAX, INT_MAX, + INT_MAX, INT_MAX, INT_MAX, INT_MAX }; int is_dc_diff_large = 0; int is_low_cntras = 0; @@ -999,7 +1158,8 @@ // only supports 32x32 block size and 5x5 filtering window. if (is_frame_high_bitdepth(frame_to_filter)) { // for high bit-depth #if CONFIG_AV1_HIGHBITDEPTH - if (TF_BLOCK_SIZE == BLOCK_32X32 && TF_WINDOW_LENGTH == 5) { + if (!is_yuv422_format && TF_BLOCK_SIZE == BLOCK_64X64 && + TF_WINDOW_LENGTH == 5) { av1_highbd_apply_temporal_filter( frame_to_filter, mbd, block_size, mb_row, mb_col, num_planes, noise_levels, subblock_mvs, subblock_mses, q_factor, @@ -1015,7 +1175,7 @@ #endif // CONFIG_AV1_HIGHBITDEPTH } else { // for 8-bit - if (TF_BLOCK_SIZE == BLOCK_32X32 && TF_WINDOW_LENGTH == 5) { + if (TF_BLOCK_SIZE == BLOCK_64X64 && TF_WINDOW_LENGTH == 5) { av1_apply_temporal_filter( frame_to_filter, mbd, block_size, mb_row, mb_col, num_planes, noise_levels, subblock_mvs, subblock_mses, q_factor, @@ -1206,10 +1366,10 @@ num_before = AOMMIN(is_forward_keyframe ? num_frames / 2 : 0, max_before); num_after = AOMMIN(num_frames - 1, max_after); } else { - int gfu_boost = av1_calc_arf_boost(&cpi->ppi->twopass, &cpi->twopass_frame, - &cpi->ppi->p_rc, &cpi->frame_info, - filter_frame_lookahead_idx, max_before, - max_after, NULL, NULL, 0); + int gfu_boost = av1_calc_arf_boost( + &cpi->ppi->twopass, &cpi->twopass_frame, &cpi->ppi->p_rc, + &cpi->frame_info, filter_frame_lookahead_idx, max_before, max_after, + NULL, NULL, 0, cpi->oxcf.mode != REALTIME); num_frames = AOMMIN(num_frames, gfu_boost / 150); num_frames += !(num_frames & 1); // Make the number odd. @@ -1430,7 +1590,10 @@ int av1_check_show_filtered_frame(const YV12_BUFFER_CONFIG *frame, const FRAME_DIFF *frame_diff, int q_index, - aom_bit_depth_t bit_depth) { + aom_bit_depth_t bit_depth, int enable_overlay, + int is_second_arf) { + if (!enable_overlay || is_second_arf) return 1; + const int frame_height = frame->y_crop_height; const int frame_width = frame->y_crop_width; const int block_height = block_size_high[TF_BLOCK_SIZE];
diff --git a/av1/encoder/temporal_filter.h b/av1/encoder/temporal_filter.h index 9585942..bef61f5 100644 --- a/av1/encoder/temporal_filter.h +++ b/av1/encoder/temporal_filter.h
@@ -26,15 +26,18 @@ // TODO(wtc): These two variables are only used in avx2, sse2, neon // implementations, where the block size is still hard coded to TF_BLOCK_SIZE. // This should be fixed to align with the c implementation. -#define BH 32 -#define BW 32 +#define BH 64 +#define BW 64 // Block size used in temporal filtering. -#define TF_BLOCK_SIZE BLOCK_32X32 +#define TF_BLOCK_SIZE BLOCK_64X64 // Window size for temporal filtering. #define TF_WINDOW_LENGTH 5 +// Number of 16x16 blocks within one 64x64 TF block. +#define NUM_16X16 16 + // A constant number, sqrt(pi / 2), used for noise estimation. static const double SQRT_PI_BY_2 = 1.25331413732; @@ -338,17 +341,20 @@ * to make decision. * * \ingroup src_frame_proc - * \param[in] frame filtered frame's buffer - * \param[in] frame_diff structure of sse and sum of the - * filtered frame. - * \param[in] q_index q_index used for this frame - * \param[in] bit_depth bit depth + * \param[in] frame filtered frame's buffer + * \param[in] frame_diff structure of sse and sum of the + * filtered frame. + * \param[in] q_index q_index used for this frame + * \param[in] bit_depth bit depth + * \param[in] enable_overlay arf overlay is enabled or disabled + * \param[in] is_second_arf whether or not this is a second ARF frame * \return return 1 if this frame can be shown directly, otherwise * return 0 */ int av1_check_show_filtered_frame(const YV12_BUFFER_CONFIG *frame, const FRAME_DIFF *frame_diff, int q_index, - aom_bit_depth_t bit_depth); + aom_bit_depth_t bit_depth, int enable_overlay, + int is_second_arf); /*!\cond */ // Allocates memory for members of TemporalFilterData.
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c index a8c2f83..4266fca 100644 --- a/av1/encoder/tpl_model.c +++ b/av1/encoder/tpl_model.c
@@ -200,6 +200,16 @@ aom_internal_error(&ppi->error, AOM_CODEC_MEM_ERROR, "Failed to allocate frame buffer"); } + + if (aom_alloc_frame_buffer( + &tpl_data->prev_gop_arf_src, width, height, seq_params->subsampling_x, + seq_params->subsampling_y, seq_params->use_highbitdepth, + tpl_data->border_in_pixels, byte_alignment, false, + alloc_y_plane_only)) + aom_internal_error(&ppi->error, AOM_CODEC_MEM_ERROR, + "Failed to allocate prev gop arf buffer"); + + tpl_data->prev_gop_arf_disp_order = -1; } static inline int32_t tpl_get_satd_cost(BitDepthInfo bd_info, int16_t *src_diff, @@ -1014,9 +1024,58 @@ tpl_stats->srcrf_dist = recon_error << TPL_DEP_COST_SCALE_LOG2; tpl_stats->srcrf_sse = pred_error << TPL_DEP_COST_SCALE_LOG2; + const YV12_BUFFER_CONFIG *ref_frame_ptr[2]; + + if (best_mode == NEW_NEWMV) { + ref_frame_ptr[0] = tpl_data->ref_frame[comp_ref_frames[best_cmp_rf_idx][0]]; + ref_frame_ptr[1] = + tpl_data->src_ref_frame[comp_ref_frames[best_cmp_rf_idx][1]]; + get_rate_distortion(&rate_cost, &recon_error, &pred_error, src_diff, coeff, + qcoeff, dqcoeff, cm, x, ref_frame_ptr, rec_buffer_pool, + rec_stride_pool, tx_size, best_mode, mi_row, mi_col, + use_y_only_rate_distortion, 0 /*do_recon*/, NULL); + tpl_stats->cmp_recrf_dist[0] = recon_error << TPL_DEP_COST_SCALE_LOG2; + tpl_stats->cmp_recrf_rate[0] = rate_cost; + + rate_cost = 0; + ref_frame_ptr[0] = + tpl_data->src_ref_frame[comp_ref_frames[best_cmp_rf_idx][0]]; + ref_frame_ptr[1] = tpl_data->ref_frame[comp_ref_frames[best_cmp_rf_idx][1]]; + get_rate_distortion(&rate_cost, &recon_error, &pred_error, src_diff, coeff, + qcoeff, dqcoeff, cm, x, ref_frame_ptr, rec_buffer_pool, + rec_stride_pool, tx_size, best_mode, mi_row, mi_col, + use_y_only_rate_distortion, 0 /*do_recon*/, NULL); + tpl_stats->cmp_recrf_dist[1] = recon_error << TPL_DEP_COST_SCALE_LOG2; + tpl_stats->cmp_recrf_rate[1] = rate_cost; + } + // Final encode rate_cost = 0; - const YV12_BUFFER_CONFIG *ref_frame_ptr[2]; + // Add uv bound if needed + if (best_mode == D203_PRED && xd->left_available && + mi_row + tx_size_high_unit[tx_size] < xd->tile.mi_row_end) { + const int num_planes = use_y_only_rate_distortion ? 1 : av1_num_planes(cm); + for (int plane = 1; plane < num_planes; ++plane) { + struct macroblockd_plane *pd = &xd->plane[plane]; + int dst_mb_offset_uv = + ((mi_row * MI_SIZE) >> pd->subsampling_y) * rec_stride_pool[plane] + + ((mi_col * MI_SIZE) >> pd->subsampling_x); + uint8_t *dst_uv_buffer = rec_buffer_pool[plane] + dst_mb_offset_uv; + int dst_uv_buffer_stride = rec_stride_pool[plane]; + int bh_uv = (bh >> pd->subsampling_y); + + if (is_cur_buf_hbd(xd)) { + uint16_t *dst_uv = CONVERT_TO_SHORTPTR(dst_uv_buffer); + for (int i = 0; i < bh_uv; ++i) + dst_uv[(bh_uv + i) * dst_uv_buffer_stride - 1] = + dst_uv[(bh_uv - 1) * dst_uv_buffer_stride - 1]; + } else { + for (int i = 0; i < bh_uv; ++i) + dst_uv_buffer[(bh_uv + i) * dst_uv_buffer_stride - 1] = + dst_uv_buffer[(bh_uv - 1) * dst_uv_buffer_stride - 1]; + } + } + } ref_frame_ptr[0] = best_mode == NEW_NEWMV @@ -1046,17 +1105,11 @@ tpl_stats->recrf_dist = AOMMAX(tpl_stats->srcrf_dist, tpl_stats->recrf_dist); tpl_stats->recrf_rate = AOMMAX(tpl_stats->srcrf_rate, tpl_stats->recrf_rate); - if (best_mode == NEW_NEWMV) { - ref_frame_ptr[0] = tpl_data->ref_frame[comp_ref_frames[best_cmp_rf_idx][0]]; - ref_frame_ptr[1] = - tpl_data->src_ref_frame[comp_ref_frames[best_cmp_rf_idx][1]]; - get_rate_distortion(&rate_cost, &recon_error, &pred_error, src_diff, coeff, - qcoeff, dqcoeff, cm, x, ref_frame_ptr, rec_buffer_pool, - rec_stride_pool, tx_size, best_mode, mi_row, mi_col, - use_y_only_rate_distortion, 1 /*do_recon*/, NULL); - tpl_stats->cmp_recrf_dist[0] = recon_error << TPL_DEP_COST_SCALE_LOG2; - tpl_stats->cmp_recrf_rate[0] = rate_cost; - + if (best_mode == NEWMV) { + tpl_stats->mv[best_rf_idx] = best_mv[0]; + tpl_stats->ref_frame_index[0] = best_rf_idx; + tpl_stats->ref_frame_index[1] = NONE_FRAME; + } else if (best_mode == NEW_NEWMV) { tpl_stats->cmp_recrf_dist[0] = AOMMAX(tpl_stats->srcrf_dist, tpl_stats->cmp_recrf_dist[0]); tpl_stats->cmp_recrf_rate[0] = @@ -1067,17 +1120,6 @@ tpl_stats->cmp_recrf_rate[0] = AOMMIN(tpl_stats->recrf_rate, tpl_stats->cmp_recrf_rate[0]); - rate_cost = 0; - ref_frame_ptr[0] = - tpl_data->src_ref_frame[comp_ref_frames[best_cmp_rf_idx][0]]; - ref_frame_ptr[1] = tpl_data->ref_frame[comp_ref_frames[best_cmp_rf_idx][1]]; - get_rate_distortion(&rate_cost, &recon_error, &pred_error, src_diff, coeff, - qcoeff, dqcoeff, cm, x, ref_frame_ptr, rec_buffer_pool, - rec_stride_pool, tx_size, best_mode, mi_row, mi_col, - use_y_only_rate_distortion, 1 /*do_recon*/, NULL); - tpl_stats->cmp_recrf_dist[1] = recon_error << TPL_DEP_COST_SCALE_LOG2; - tpl_stats->cmp_recrf_rate[1] = rate_cost; - tpl_stats->cmp_recrf_dist[1] = AOMMAX(tpl_stats->srcrf_dist, tpl_stats->cmp_recrf_dist[1]); tpl_stats->cmp_recrf_rate[1] = @@ -1087,13 +1129,7 @@ AOMMIN(tpl_stats->recrf_dist, tpl_stats->cmp_recrf_dist[1]); tpl_stats->cmp_recrf_rate[1] = AOMMIN(tpl_stats->recrf_rate, tpl_stats->cmp_recrf_rate[1]); - } - if (best_mode == NEWMV) { - tpl_stats->mv[best_rf_idx] = best_mv[0]; - tpl_stats->ref_frame_index[0] = best_rf_idx; - tpl_stats->ref_frame_index[1] = NONE_FRAME; - } else if (best_mode == NEW_NEWMV) { tpl_stats->ref_frame_index[0] = comp_ref_frames[best_cmp_rf_idx][0]; tpl_stats->ref_frame_index[1] = comp_ref_frames[best_cmp_rf_idx][1]; tpl_stats->mv[tpl_stats->ref_frame_index[0]] = best_mv[0]; @@ -1373,20 +1409,30 @@ xd->block_ref_scale_factors[0] = &tpl_data->sf; xd->block_ref_scale_factors[1] = &tpl_data->sf; - const int base_qindex = + int base_qindex = cpi->use_ducky_encode ? gf_group->q_val[frame_idx] : pframe_qindex; - // The TPL model is only meant to be run in inter mode, so ensure that we are - // not running in all intra mode, which implies we are not tuning for image - // quality (IQ) or SSIMULACRA2. - assert(cpi->oxcf.tune_cfg.tuning != AOM_TUNE_IQ && - cpi->oxcf.tune_cfg.tuning != AOM_TUNE_SSIMULACRA2 && - cpi->oxcf.mode != ALLINTRA); + + // Override QP decision with RC + if (av1_use_tpl_for_extrc(&cpi->ext_ratectrl) && + cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL) { + aom_rc_encodeframe_decision_t encode_frame_decision; + encode_frame_decision.sb_params_list = NULL; + // Even though delta Q is not used for TPL, this pointer still needs to be + // set to avoid segfault. + encode_frame_decision.use_delta_q = &cpi->ext_ratectrl.use_delta_q; + if (av1_extrc_get_encodeframe_decision(&cpi->ext_ratectrl, frame_idx, + &encode_frame_decision) == + AOM_CODEC_OK) { + base_qindex = encode_frame_decision.q_index; + } + } // Get rd multiplier set up. rdmult = av1_compute_rd_mult( base_qindex, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); if (rdmult < 1) rdmult = 1; av1_set_error_per_bit(&x->errorperbit, rdmult); @@ -1402,7 +1448,7 @@ gf_group->update_type[cpi->gf_frame_index]; tpl_frame->base_rdmult = av1_compute_rd_mult_based_on_qindex( bd_info.bit_depth, update_type, base_qindex, - cpi->oxcf.tune_cfg.tuning) / + cpi->oxcf.tune_cfg.tuning, cpi->oxcf.mode) / 6; if (cpi->use_ducky_encode) @@ -1427,6 +1473,53 @@ gf_group->layer_depth[frame_idx] >= layer_depth_th; } +static void tpl_store_before_propagation(AV1_COMP *cpi, + AomTplBlockStats *tpl_block_stats, + TplDepStats *src_stats, int mi_row, + int mi_col) { + TplParams *const tpl_data = &cpi->ppi->tpl_data; + GF_GROUP *gf_group = &cpi->ppi->gf_group; + + tpl_block_stats->row = mi_row * MI_SIZE; + tpl_block_stats->col = mi_col * MI_SIZE; + tpl_block_stats->srcrf_sse = src_stats->srcrf_sse; + // These need to be scaled down for external RC as libaom scales them up + // first. See b/274644689. + tpl_block_stats->srcrf_dist = + src_stats->srcrf_dist >> TPL_DEP_COST_SCALE_LOG2; + tpl_block_stats->recrf_sse = src_stats->recrf_sse >> TPL_DEP_COST_SCALE_LOG2; + tpl_block_stats->recrf_dist = + src_stats->recrf_dist >> TPL_DEP_COST_SCALE_LOG2; + tpl_block_stats->intra_sse = src_stats->intra_sse >> TPL_DEP_COST_SCALE_LOG2; + tpl_block_stats->intra_dist = + src_stats->intra_dist >> TPL_DEP_COST_SCALE_LOG2; + tpl_block_stats->cmp_recrf_dist[0] = src_stats->cmp_recrf_dist[0]; + tpl_block_stats->cmp_recrf_dist[1] = src_stats->cmp_recrf_dist[1]; + tpl_block_stats->mc_dep_rate = src_stats->mc_dep_rate; + tpl_block_stats->mc_dep_dist = src_stats->mc_dep_dist; + tpl_block_stats->inter_cost = src_stats->inter_cost; + tpl_block_stats->intra_cost = src_stats->intra_cost; + tpl_block_stats->srcrf_rate = src_stats->srcrf_rate; + tpl_block_stats->recrf_rate = src_stats->recrf_rate; + tpl_block_stats->intra_rate = src_stats->intra_rate; + tpl_block_stats->cmp_recrf_rate[0] = src_stats->cmp_recrf_rate[0]; + tpl_block_stats->cmp_recrf_rate[1] = src_stats->cmp_recrf_rate[1]; + tpl_block_stats->ref_frame_index[0] = + gf_group->ref_frame_list[tpl_data->frame_idx] + [LAST_FRAME + src_stats->ref_frame_index[0]]; + tpl_block_stats->ref_frame_index[1] = + gf_group->ref_frame_list[tpl_data->frame_idx] + [LAST_FRAME + src_stats->ref_frame_index[1]]; + for (int ref = 0; ref < AOM_RC_INTER_REFS_PER_FRAME; ++ref) { + tpl_block_stats->mv[ref].as_mv.col = src_stats->mv[ref].as_mv.col; + tpl_block_stats->mv[ref].as_mv.row = src_stats->mv[ref].as_mv.row; + tpl_block_stats->mv[ref].as_fullmv.col = src_stats->mv[ref].as_fullmv.col; + tpl_block_stats->mv[ref].as_fullmv.row = src_stats->mv[ref].as_fullmv.row; + tpl_block_stats->mv[ref].as_int = src_stats->mv[ref].as_int; + tpl_block_stats->pred_error[ref] = src_stats->pred_error[ref]; + } +} + // This function stores the motion estimation dependencies of all the blocks in // a row void av1_mc_flow_dispenser_row(AV1_COMP *cpi, TplTxfmStats *tpl_txfm_stats, @@ -1440,7 +1533,6 @@ TplParams *const tpl_data = &cpi->ppi->tpl_data; TplDepFrame *tpl_frame = &tpl_data->tpl_frame[tpl_data->frame_idx]; MACROBLOCKD *xd = &x->e_mbd; - const int tplb_cols_in_tile = ROUND_POWER_OF_TWO(mi_params->mi_cols, mi_size_wide_log2[bsize]); const int tplb_row = ROUND_POWER_OF_TWO(mi_row, mi_size_high_log2[bsize]); @@ -1476,6 +1568,17 @@ // Motion flow dependency dispenser. tpl_model_store(tpl_frame->tpl_stats_ptr, mi_row, mi_col, tpl_frame->stride, &tpl_stats, tpl_data->tpl_stats_block_mis_log2); + + if (av1_use_tpl_for_extrc(&cpi->ext_ratectrl)) { + AomTplFrameStats *tpl_frame_stats_before_propagation = + &cpi->extrc_tpl_gop_stats.frame_stats_list[tpl_data->frame_idx]; + AomTplBlockStats *block_stats = + &tpl_frame_stats_before_propagation + ->block_stats_list[mi_row * tpl_frame->mi_cols + mi_col]; + tpl_store_before_propagation(cpi, block_stats, &tpl_stats, mi_row, + mi_col); + } + (*tpl_row_mt->sync_write_ptr)(&tpl_data->tpl_mt_sync, tplb_row, tplb_col_in_tile, tplb_cols_in_tile); } @@ -1521,7 +1624,7 @@ } } -static inline void init_gop_frames_for_tpl( +static inline int init_gop_frames_for_tpl( AV1_COMP *cpi, const EncodeFrameParams *const init_frame_params, GF_GROUP *gf_group, int *tpl_group_frames, int *pframe_qindex) { AV1_COMMON *cm = &cpi->common; @@ -1544,7 +1647,12 @@ tpl_data->tpl_frame[-i - 1].rec_picture = NULL; tpl_data->tpl_frame[-i - 1].frame_display_index = 0; } else { - tpl_data->tpl_frame[-i - 1].gf_picture = &cm->ref_frame_map[i]->buf; + if (cm->ref_frame_map[i]->display_order_hint == + tpl_data->prev_gop_arf_disp_order) { + tpl_data->tpl_frame[-i - 1].gf_picture = &tpl_data->prev_gop_arf_src; + } else { + tpl_data->tpl_frame[-i - 1].gf_picture = &cm->ref_frame_map[i]->buf; + } tpl_data->tpl_frame[-i - 1].rec_picture = &cm->ref_frame_map[i]->buf; tpl_data->tpl_frame[-i - 1].frame_display_index = cm->ref_frame_map[i]->display_order_hint; @@ -1722,6 +1830,8 @@ ++extend_frame_count; ++frame_display_index; } + + return extend_frame_count; } void av1_init_tpl_stats(TplParams *const tpl_data) { @@ -1856,6 +1966,78 @@ return exp((mc_dep_cost_base - intra_cost_base) / cbcmp_base); } +void av1_free_tpl_gop_stats(AomTplGopStats *extrc_tpl_gop_stats) { + if (extrc_tpl_gop_stats == NULL || + extrc_tpl_gop_stats->frame_stats_list == NULL) { + return; + } + for (int frame_index = 0; frame_index < extrc_tpl_gop_stats->size; + ++frame_index) { + AomTplFrameStats *this_frame_stats = + &extrc_tpl_gop_stats->frame_stats_list[frame_index]; + aom_free(this_frame_stats->block_stats_list); + this_frame_stats->block_stats_list = NULL; + } + aom_free(extrc_tpl_gop_stats->frame_stats_list); + extrc_tpl_gop_stats->frame_stats_list = NULL; + extrc_tpl_gop_stats->size = 0; +} + +static void init_tpl_stats_before_propagation( + struct aom_internal_error_info *error_info, + AomTplGopStats *extrc_tpl_gop_stats, TplParams *tpl_stats, + int tpl_gop_frames, int frame_width, int frame_height) { + av1_free_tpl_gop_stats(extrc_tpl_gop_stats); + AOM_CHECK_MEM_ERROR( + error_info, extrc_tpl_gop_stats->frame_stats_list, + aom_calloc(tpl_gop_frames, + sizeof(*extrc_tpl_gop_stats->frame_stats_list))); + extrc_tpl_gop_stats->size = tpl_gop_frames; + for (int frame_index = 0; frame_index < tpl_gop_frames; ++frame_index) { + const int mi_rows = tpl_stats->tpl_frame[frame_index].mi_rows; + const int mi_cols = tpl_stats->tpl_frame[frame_index].mi_cols; + AomTplFrameStats *this_frame_stats = + &extrc_tpl_gop_stats->frame_stats_list[frame_index]; + AOM_CHECK_MEM_ERROR( + error_info, this_frame_stats->block_stats_list, + aom_calloc(mi_rows * mi_cols, + sizeof(*this_frame_stats->block_stats_list))); + this_frame_stats->num_blocks = mi_rows * mi_cols; + this_frame_stats->frame_width = frame_width; + this_frame_stats->frame_height = frame_height; + } +} + +static void trim_tpl_stats(struct aom_internal_error_info *error_info, + AomTplGopStats *extrc_tpl_gop_stats, + int extra_frames) { + int i; + AomTplFrameStats *new_frame_stats; + const int new_size = extrc_tpl_gop_stats->size - extra_frames; + if (extrc_tpl_gop_stats->size <= extra_frames) + aom_internal_error( + error_info, AOM_CODEC_ERROR, + "The number of frames in AomTplGopStats is fewer than expected."); + AOM_CHECK_MEM_ERROR(error_info, new_frame_stats, + aom_calloc(new_size, sizeof(*new_frame_stats))); + for (i = 0; i < new_size; i++) { + AomTplFrameStats *frame_stats = &extrc_tpl_gop_stats->frame_stats_list[i]; + const int num_blocks = frame_stats->num_blocks; + new_frame_stats[i].num_blocks = frame_stats->num_blocks; + new_frame_stats[i].frame_width = frame_stats->frame_width; + new_frame_stats[i].frame_height = frame_stats->frame_height; + new_frame_stats[i].num_blocks = num_blocks; + AOM_CHECK_MEM_ERROR( + error_info, new_frame_stats[i].block_stats_list, + aom_calloc(num_blocks, sizeof(*new_frame_stats[i].block_stats_list))); + memcpy(new_frame_stats[i].block_stats_list, frame_stats->block_stats_list, + num_blocks * sizeof(*new_frame_stats[i].block_stats_list)); + } + av1_free_tpl_gop_stats(extrc_tpl_gop_stats); + extrc_tpl_gop_stats->size = new_size; + extrc_tpl_gop_stats->frame_stats_list = new_frame_stats; +} + int av1_tpl_setup_stats(AV1_COMP *cpi, int gop_eval, const EncodeFrameParams *const frame_params) { #if CONFIG_COLLECT_COMPONENT_TIMING @@ -1890,13 +2072,19 @@ int pframe_qindex; int tpl_gf_group_frames; - init_gop_frames_for_tpl(cpi, frame_params, gf_group, &tpl_gf_group_frames, - &pframe_qindex); + int extended_frame_count = init_gop_frames_for_tpl( + cpi, frame_params, gf_group, &tpl_gf_group_frames, &pframe_qindex); cpi->ppi->p_rc.base_layer_qp = pframe_qindex; av1_init_tpl_stats(tpl_data); + if (av1_use_tpl_for_extrc(&cpi->ext_ratectrl)) { + init_tpl_stats_before_propagation( + cpi->common.error, &cpi->extrc_tpl_gop_stats, tpl_data, + tpl_gf_group_frames, cpi->common.width, cpi->common.height); + } + TplBuffers *tpl_tmp_buffers = &cpi->td.tpl_tmp_buffers; if (!tpl_alloc_temp_buffers(tpl_tmp_buffers, tpl_data->tpl_bsize_1d)) { aom_internal_error(cpi->common.error, AOM_CODEC_MEM_ERROR, @@ -1962,6 +2150,19 @@ num_planes); } + if (av1_use_tpl_for_extrc(&cpi->ext_ratectrl)) { + // TPL stats has extra frames from next GOP. Trim those extra frames for + // external RC. + trim_tpl_stats(cpi->common.error, &cpi->extrc_tpl_gop_stats, + extended_frame_count); + const aom_codec_err_t codec_status = + av1_extrc_send_tpl_stats(&cpi->ext_ratectrl, &cpi->extrc_tpl_gop_stats); + if (codec_status != AOM_CODEC_OK) { + aom_internal_error(cpi->common.error, codec_status, + "av1_extrc_send_tpl_stats() failed"); + } + } + for (int frame_idx = tpl_gf_group_frames - 1; frame_idx >= cpi->gf_frame_index; --frame_idx) { if (skip_tpl_for_frame(gf_group, frame_idx, gop_eval, approx_gop_eval, @@ -2112,7 +2313,8 @@ orig_qindex_rdmult, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); const int new_qindex_rdmult = quant_params->base_qindex + x->rdmult_delta_qindex + @@ -2121,7 +2323,8 @@ new_qindex_rdmult, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, boost_index, frame_type, cpi->oxcf.q_cfg.use_fixed_qp_offsets, - is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning); + is_stat_consumption_stage(cpi), cpi->oxcf.tune_cfg.tuning, + cpi->oxcf.mode); const double scaling_factor = (double)new_rdmult / (double)orig_rdmult; @@ -2230,7 +2433,7 @@ if (qstep <= target_qstep) break; } } else { - for (qindex = leaf_qindex; qindex <= MAXQ; ++qindex) { + for (qindex = leaf_qindex; qindex < MAXQ; ++qindex) { const double qstep = av1_dc_quant_QTX(qindex, 0, bit_depth); if (qstep >= target_qstep) break; }
diff --git a/av1/encoder/tpl_model.h b/av1/encoder/tpl_model.h index 3690671..ba6fd9c 100644 --- a/av1/encoder/tpl_model.h +++ b/av1/encoder/tpl_model.h
@@ -29,11 +29,13 @@ #include "config/aom_config.h" +#include "aom/aom_tpl.h" #include "aom_scale/yv12config.h" #include "aom_util/aom_pthread.h" #include "av1/common/mv.h" #include "av1/common/scale.h" +#include "av1/encoder/av1_ext_ratectrl.h" #include "av1/encoder/block.h" #include "av1/encoder/lookahead.h" #include "av1/encoder/ratectrl.h" @@ -236,6 +238,16 @@ const YV12_BUFFER_CONFIG *ref_frame[INTER_REFS_PER_FRAME]; /*! + * The buffer for the past gop's last frame's src. + */ + YV12_BUFFER_CONFIG prev_gop_arf_src; + + /*! + * Display order of the past gop's last frame. + */ + int64_t prev_gop_arf_disp_order; + + /*! * Parameters related to synchronization for top-right dependency in row based * multi-threading of tpl */ @@ -404,6 +416,10 @@ void av1_read_rd_command(const char *filepath, RD_COMMAND *rd_command); #endif // CONFIG_RD_COMMAND +static inline bool av1_use_tpl_for_extrc(AOM_EXT_RATECTRL const *ext_rc) { + return ext_rc->ready && ext_rc->funcs.send_tpl_gop_stats != NULL; +} + /*!\brief Allocate buffers used by tpl model * * \param[in] Top-level encode/decode structure @@ -686,6 +702,12 @@ double av1_tpl_compute_frame_mv_entropy(const TplDepFrame *tpl_frame, uint8_t right_shift); +/*!\brief Free the memory allocated for cpi->extrc_tpl_gop_stats. + * + * \param[in] extrc_tpl_gop_stats TPL stats for the GOP used for external RC. + */ +void av1_free_tpl_gop_stats(AomTplGopStats *extrc_tpl_gop_stats); + #if CONFIG_RATECTRL_LOG typedef struct { int coding_frame_count;
diff --git a/av1/encoder/tx_search.c b/av1/encoder/tx_search.c index 334bce1..72ac440 100644 --- a/av1/encoder/tx_search.c +++ b/av1/encoder/tx_search.c
@@ -9,6 +9,8 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <inttypes.h> + #include "av1/common/cfl.h" #include "av1/common/reconintra.h" #include "av1/encoder/block.h" @@ -77,6 +79,10 @@ 12, 12, 23, 23, 32, 32, 8, 8, 16, 16, 23, 23 }; +// look-up table for number of top no-split RD Costs that should be considered +// based on prune_inter_tx_split_rd_eval_lvl speed feature. +static const int num_inter_tx_no_split_cand[2] = { 4, 3 }; + static inline uint32_t get_block_residue_hash(MACROBLOCK *x, BLOCK_SIZE bsize) { const int rows = block_size_high[bsize]; const int cols = block_size_wide[bsize]; @@ -111,8 +117,6 @@ MACROBLOCKD *const xd = &x->e_mbd; MB_MODE_INFO *const mbmi = xd->mi[0]; mbmi->tx_size = mb_rd_info->tx_size; - memcpy(x->txfm_search_info.blk_skip, mb_rd_info->blk_skip, - sizeof(mb_rd_info->blk_skip[0]) * n4); av1_copy(mbmi->inter_tx_size, mb_rd_info->inter_tx_size); av1_copy_array(xd->tx_type_map, mb_rd_info->tx_type_map, n4); *rd_stats = mb_rd_info->rd_stats; @@ -247,8 +251,6 @@ memset(xd->tx_type_map, DCT_DCT, sizeof(xd->tx_type_map[0]) * n4); memset(mbmi->inter_tx_size, tx_size, sizeof(mbmi->inter_tx_size)); mbmi->tx_size = tx_size; - for (int i = 0; i < n4; ++i) - set_blk_skip(x->txfm_search_info.blk_skip, 0, i, 1); rd_stats->skip_txfm = 1; if (is_cur_buf_hbd(xd)) dist = ROUND_POWER_OF_TWO(dist, (xd->bd - 8) * 2); rd_stats->dist = rd_stats->sse = (dist << 4); @@ -297,13 +299,67 @@ const MB_MODE_INFO *const mbmi = xd->mi[0]; mb_rd_info->hash_value = hash; mb_rd_info->tx_size = mbmi->tx_size; - memcpy(mb_rd_info->blk_skip, x->txfm_search_info.blk_skip, - sizeof(mb_rd_info->blk_skip[0]) * n4); av1_copy(mb_rd_info->inter_tx_size, mbmi->inter_tx_size); av1_copy_array(mb_rd_info->tx_type_map, xd->tx_type_map, n4); mb_rd_info->rd_stats = *rd_stats; } +// Store the RD Cost of transform no-split. +static inline void push_inter_block_tx_no_split_rd( + MACROBLOCK *x, const MB_MODE_INFO *mbmi, int64_t tmp_rd, int blk_idx, + int prune_inter_tx_split_rd_eval_lvl) { + assert(blk_idx < MAX_TX_BLOCKS_IN_MAX_SB); + if (!prune_inter_tx_split_rd_eval_lvl) return; + + if (blk_idx == -1 || tmp_rd == INT64_MAX) return; + + // Do not store for skip and intraBC modes + if (mbmi->skip_mode != 0 || is_intrabc_block(mbmi)) return; + + assert(prune_inter_tx_split_rd_eval_lvl <= 2); + const int num_top_cand = + num_inter_tx_no_split_cand[prune_inter_tx_split_rd_eval_lvl - 1]; + assert(num_top_cand <= TOP_INTER_TX_NO_SPLIT_COUNT); + + // Insert the RD Cost in sorted order + for (int i = 0; i < num_top_cand; i++) { + if (tmp_rd < x->top_inter_tx_no_split_rd[blk_idx][i]) { + for (int j = num_top_cand - 1; j > i; j--) { + x->top_inter_tx_no_split_rd[blk_idx][j] = + x->top_inter_tx_no_split_rd[blk_idx][j - 1]; + } + x->top_inter_tx_no_split_rd[blk_idx][i] = tmp_rd; + break; + } + } +} + +// Prune the evaluation of transform split. +static inline bool prune_tx_split_eval_using_no_split_rd( + const MACROBLOCK *x, const MB_MODE_INFO *mbmi, int64_t tmp_rd, int blk_idx, + int prune_inter_tx_split_rd_eval_lvl) { + if (!prune_inter_tx_split_rd_eval_lvl) return false; + + if (blk_idx == -1 || tmp_rd == INT64_MAX) return false; + + // Do not prune for skip and intraBC modes + if (mbmi->skip_mode != 0 || is_intrabc_block(mbmi)) return false; + + assert(prune_inter_tx_split_rd_eval_lvl <= 2); + const int num_top_cand = + num_inter_tx_no_split_cand[prune_inter_tx_split_rd_eval_lvl - 1]; + assert(num_top_cand <= TOP_INTER_TX_NO_SPLIT_COUNT); + + // Do not prune if there is no valid top RD Cost for comparison + if (x->top_inter_tx_no_split_rd[blk_idx][num_top_cand - 1] == INT64_MAX) + return false; + + if (tmp_rd > x->top_inter_tx_no_split_rd[blk_idx][num_top_cand - 1]) + return true; + + return false; +} + static int get_search_init_depth(int mi_width, int mi_height, int is_inter, const SPEED_FEATURES *sf, int tx_size_search_method) { @@ -331,7 +387,7 @@ TX_SIZE tx_size, int depth, BLOCK_SIZE plane_bsize, ENTROPY_CONTEXT *ta, ENTROPY_CONTEXT *tl, TXFM_CONTEXT *tx_above, TXFM_CONTEXT *tx_left, RD_STATS *rd_stats, int64_t prev_level_rd, int64_t ref_best_rd, - int *is_cost_valid, FAST_TX_SEARCH_MODE ftxs_mode); + int *is_cost_valid, FAST_TX_SEARCH_MODE ftxs_mode, int blk_idx); // NOTE: CONFIG_COLLECT_RD_STATS has 3 possible values // 0: Do not collect any RD stats @@ -1819,6 +1875,9 @@ } else if (fast_tx_search) { allowed_tx_mask = 0x0c01; // V_DCT, H_DCT, DCT_DCT allowed_tx_mask &= ext_tx_used_flag; + } else if (!is_inter && txfm_params->use_derived_intra_tx_type_set) { + allowed_tx_mask = av1_derived_intra_tx_used_flag[intra_dir]; + allowed_tx_mask &= ext_tx_used_flag; } else { assert(plane == 0); allowed_tx_mask = ext_tx_used_flag; @@ -2021,8 +2080,8 @@ int block, int blk_row, int blk_col, BLOCK_SIZE plane_bsize, TX_SIZE tx_size, const TXB_CTX *const txb_ctx, - FAST_TX_SEARCH_MODE ftxs_mode, int skip_trellis, - int64_t ref_best_rd, RD_STATS *best_rd_stats) { + FAST_TX_SEARCH_MODE ftxs_mode, int64_t ref_best_rd, + RD_STATS *best_rd_stats) { const AV1_COMMON *cm = &cpi->common; MACROBLOCKD *xd = &x->e_mbd; MB_MODE_INFO *mbmi = xd->mi[0]; @@ -2038,8 +2097,8 @@ plane ? 0 : blk_row * xd->tx_type_map_stride + blk_col; av1_invalid_rd_stats(best_rd_stats); - skip_trellis |= !is_trellis_used(cpi->optimize_seg_arr[xd->mi[0]->segment_id], - DRY_RUN_NORMAL); + int skip_trellis = !is_trellis_used( + cpi->optimize_seg_arr[xd->mi[0]->segment_id], DRY_RUN_NORMAL); uint8_t best_txb_ctx = 0; // txk_allowed = TX_TYPES: >1 tx types are allowed @@ -2338,9 +2397,8 @@ int64_t ref_rdcost) { assert(is_inter_block(x->e_mbd.mi[0])); RD_STATS this_rd_stats; - const int skip_trellis = 0; search_tx_type(cpi, x, 0, block, blk_row, blk_col, plane_bsize, tx_size, - txb_ctx, ftxs_mode, skip_trellis, ref_rdcost, &this_rd_stats); + txb_ctx, ftxs_mode, ref_rdcost, &this_rd_stats); av1_merge_rd_stats(rd_stats, &this_rd_stats); } @@ -2354,7 +2412,6 @@ MACROBLOCKD *const xd = &x->e_mbd; MB_MODE_INFO *const mbmi = xd->mi[0]; struct macroblock_plane *const p = &x->plane[0]; - const int bw = mi_size_wide[plane_bsize]; const ENTROPY_CONTEXT *const pta = ta + blk_col; const ENTROPY_CONTEXT *const ptl = tl + blk_row; const TX_SIZE txs_ctx = get_txsize_entropy_ctx(tx_size); @@ -2384,8 +2441,6 @@ update_txk_array(xd, blk_row, blk_col, tx_size, DCT_DCT); } rd_stats->skip_txfm = pick_skip_txfm; - set_blk_skip(x->txfm_search_info.blk_skip, 0, blk_row * bw + blk_col, - pick_skip_txfm); if (tx_size > TX_4X4 && depth < MAX_VARTX_DEPTH) rd_stats->rate += x->mode_costs.txfm_partition_cost[txfm_partition_ctx][0]; @@ -2432,7 +2487,7 @@ select_tx_block(cpi, x, offsetr, offsetc, block, sub_txs, depth + 1, plane_bsize, ta, tl, tx_above, tx_left, &this_rd_stats, no_split_rd / nblks, ref_best_rd - split_rd_stats->rdcost, - &this_cost_valid, ftxs_mode); + &this_cost_valid, ftxs_mode, -1); if (!this_cost_valid) { split_rd_stats->rdcost = INT64_MAX; return; @@ -2547,7 +2602,7 @@ TX_SIZE tx_size, int depth, BLOCK_SIZE plane_bsize, ENTROPY_CONTEXT *ta, ENTROPY_CONTEXT *tl, TXFM_CONTEXT *tx_above, TXFM_CONTEXT *tx_left, RD_STATS *rd_stats, int64_t prev_level_rd, int64_t ref_best_rd, - int *is_cost_valid, FAST_TX_SEARCH_MODE ftxs_mode) { + int *is_cost_valid, FAST_TX_SEARCH_MODE ftxs_mode, int blk_idx) { assert(tx_size < TX_SIZES_ALL); av1_init_rd_stats(rd_stats); if (ref_best_rd < 0) { @@ -2588,6 +2643,10 @@ plane_bsize, ta, tl, ctx, rd_stats, ref_best_rd, ftxs_mode, &no_split); + push_inter_block_tx_no_split_rd( + x, mbmi, no_split.rd, blk_idx, + cpi->sf.tx_sf.prune_inter_tx_split_rd_eval_lvl); + // Speed features for early termination. const int search_level = cpi->sf.tx_sf.adaptive_txb_search_level; if (search_level) { @@ -2602,6 +2661,11 @@ if (cpi->sf.tx_sf.txb_split_cap) { if (p->eobs[block] == 0) try_split = 0; } + if (prune_tx_split_eval_using_no_split_rd( + x, mbmi, no_split.rd, blk_idx, + cpi->sf.tx_sf.prune_inter_tx_split_rd_eval_lvl)) { + try_split = 0; + } } // ML based speed feature to skip searching for split transform blocks. @@ -2641,9 +2705,6 @@ } mbmi->tx_size = tx_size; update_txk_array(xd, blk_row, blk_col, tx_size, no_split.tx_type); - const int bw = mi_size_wide[plane_bsize]; - set_blk_skip(x->txfm_search_info.blk_skip, 0, blk_row * bw + blk_col, - rd_stats->skip_txfm); } else { *rd_stats = split_rd_stats; if (split_rd_stats.rdcost == INT64_MAX) *is_cost_valid = 0; @@ -2740,10 +2801,9 @@ const int64_t skip_txfm_rd = is_inter_block(mbmi) ? RDCOST(x->rdmult, skip_txfm_rate, 0) : INT64_MAX; const int64_t no_skip_txfm_rd = RDCOST(x->rdmult, no_skip_txfm_rate, 0); - const int skip_trellis = 0; av1_txfm_rd_in_plane(x, cpi, rd_stats, ref_best_rd, AOMMIN(no_skip_txfm_rd, skip_txfm_rd), AOM_PLANE_Y, bs, - mbmi->tx_size, FTXS_NONE, skip_trellis); + mbmi->tx_size, FTXS_NONE); } static inline void choose_smallest_tx_size(const AV1_COMP *const cpi, @@ -2754,9 +2814,8 @@ mbmi->tx_size = TX_4X4; // TODO(any) : Pass this_rd based on skip/non-skip cost - const int skip_trellis = 0; av1_txfm_rd_in_plane(x, cpi, rd_stats, ref_best_rd, 0, 0, bs, mbmi->tx_size, - FTXS_NONE, skip_trellis); + FTXS_NONE); } #if !CONFIG_REALTIME_ONLY @@ -2840,15 +2899,12 @@ * \param[in] tx_size The given transform size * \param[in] ftxs_mode Transform search mode specifying desired speed and quality tradeoff - * \param[in] skip_trellis Binary flag indicating if trellis optimization - should be skipped * \return An int64_t value that is the best RD cost found. */ static int64_t uniform_txfm_yrd(const AV1_COMP *const cpi, MACROBLOCK *x, RD_STATS *rd_stats, int64_t ref_best_rd, BLOCK_SIZE bs, TX_SIZE tx_size, - FAST_TX_SEARCH_MODE ftxs_mode, - int skip_trellis) { + FAST_TX_SEARCH_MODE ftxs_mode) { assert(IMPLIES(is_rect_tx(tx_size), is_rect_tx_allowed_bsize(bs))); MACROBLOCKD *const xd = &x->e_mbd; MB_MODE_INFO *const mbmi = xd->mi[0]; @@ -2875,7 +2931,7 @@ mbmi->tx_size = tx_size; av1_txfm_rd_in_plane(x, cpi, rd_stats, ref_best_rd, AOMMIN(no_this_rd, skip_txfm_rd), AOM_PLANE_Y, bs, - tx_size, ftxs_mode, skip_trellis); + tx_size, ftxs_mode); if (rd_stats->rate == INT_MAX) return INT64_MAX; int64_t rd; @@ -2940,15 +2996,12 @@ init_depth = MAX_TX_DEPTH; } - const int skip_trellis = 0; uint8_t best_txk_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE]; - uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE]; TX_SIZE best_tx_size = max_rect_tx_size; int64_t best_rd = INT64_MAX; const int num_blks = bsize_to_num_blk(bs); x->rd_model = FULL_TXFM_RD; int64_t rd[MAX_TX_DEPTH + 1] = { INT64_MAX, INT64_MAX, INT64_MAX }; - TxfmSearchInfo *txfm_info = &x->txfm_search_info; for (int tx_size = start_tx, depth = init_depth; depth <= MAX_TX_DEPTH; depth++, tx_size = sub_tx_size_map[tx_size]) { if ((!cpi->oxcf.txfm_cfg.enable_tx64 && @@ -2977,9 +3030,8 @@ ? AOMMIN(ref_best_rd, best_rd) : ref_best_rd; rd[depth] = uniform_txfm_yrd(cpi, x, &this_rd_stats, rd_thresh, bs, tx_size, - FTXS_NONE, skip_trellis); + FTXS_NONE); if (rd[depth] < best_rd) { - av1_copy_array(best_blk_skip, txfm_info->blk_skip, num_blks); av1_copy_array(best_txk_type_map, xd->tx_type_map, num_blks); best_tx_size = tx_size; best_rd = rd[depth]; @@ -2997,7 +3049,6 @@ if (rd_stats->rate != INT_MAX) { mbmi->tx_size = best_tx_size; av1_copy_array(xd->tx_type_map, best_txk_type_map, num_blks); - av1_copy_array(txfm_info->blk_skip, best_blk_skip, num_blks); } #if !CONFIG_REALTIME_ONLY @@ -3049,8 +3100,8 @@ TXB_CTX txb_ctx; get_txb_ctx(plane_bsize, tx_size, plane, a, l, &txb_ctx); search_tx_type(cpi, x, plane, block, blk_row, blk_col, plane_bsize, tx_size, - &txb_ctx, args->ftxs_mode, args->skip_trellis, - args->best_rd - args->current_rd, &this_rd_stats); + &txb_ctx, args->ftxs_mode, args->best_rd - args->current_rd, + &this_rd_stats); #if !CONFIG_REALTIME_ONLY if (plane == AOM_PLANE_Y && xd->cfl.store_y) { @@ -3064,16 +3115,6 @@ #endif // CONFIG_RD_DEBUG av1_set_txb_context(x, plane, block, tx_size, a, l); - const int blk_idx = - blk_row * (block_size_wide[plane_bsize] >> MI_SIZE_LOG2) + blk_col; - - TxfmSearchInfo *txfm_info = &x->txfm_search_info; - if (plane == 0) - set_blk_skip(txfm_info->blk_skip, plane, blk_idx, - x->plane[plane].eobs[block] == 0); - else - set_blk_skip(txfm_info->blk_skip, plane, blk_idx, 0); - int64_t rd; if (is_inter) { const int64_t no_skip_txfm_rd = @@ -3256,21 +3297,17 @@ rd_stats->zero_rate = zero_blk_rate; tx_type_rd(cpi, x, tx_size, blk_row, blk_col, block, plane_bsize, &txb_ctx, rd_stats, ftxs_mode, ref_best_rd); - const int mi_width = mi_size_wide[plane_bsize]; - TxfmSearchInfo *txfm_info = &x->txfm_search_info; if (RDCOST(x->rdmult, rd_stats->rate, rd_stats->dist) >= RDCOST(x->rdmult, zero_blk_rate, rd_stats->sse) || rd_stats->skip_txfm == 1) { rd_stats->rate = zero_blk_rate; rd_stats->dist = rd_stats->sse; rd_stats->skip_txfm = 1; - set_blk_skip(txfm_info->blk_skip, 0, blk_row * mi_width + blk_col, 1); x->plane[0].eobs[block] = 0; x->plane[0].txb_entropy_ctx[block] = 0; update_txk_array(xd, blk_row, blk_col, tx_size, DCT_DCT); } else { rd_stats->skip_txfm = 0; - set_blk_skip(txfm_info->blk_skip, 0, blk_row * mi_width + blk_col, 0); } if (tx_size > TX_4X4 && depth < MAX_VARTX_DEPTH) rd_stats->rate += x->mode_costs.txfm_partition_cost[ctx][0]; @@ -3434,6 +3471,7 @@ int64_t skip_txfm_rd = RDCOST(x->rdmult, skip_txfm_cost, 0); int64_t no_skip_txfm_rd = RDCOST(x->rdmult, no_skip_txfm_cost, 0); int block = 0; + int blk_idx = 0; av1_init_rd_stats(rd_stats); for (int idy = 0; idy < max_block_high(xd, bsize, 0); idy += bh) { @@ -3447,7 +3485,8 @@ // Search for the best transform block size and type for the sub-block. select_tx_block(cpi, x, idy, idx, block, max_tx_size, init_depth, bsize, ctxa, ctxl, tx_above, tx_left, &pn_rd_stats, INT64_MAX, - best_rd_sofar, &is_cost_valid, ftxs_mode); + best_rd_sofar, &is_cost_valid, ftxs_mode, blk_idx); + blk_idx++; if (!is_cost_valid || pn_rd_stats.rate == INT_MAX) { av1_invalid_rd_stats(rd_stats); return INT64_MAX; @@ -3672,7 +3711,6 @@ av1_subtract_plane(x, plane_bsize, plane); } - const int skip_trellis = 0; const TX_SIZE uv_tx_size = av1_get_tx_size(AOM_PLANE_U, xd); int is_cost_valid = 1; for (int plane = 1; plane < MAX_MB_PLANE; ++plane) { @@ -3687,7 +3725,7 @@ chroma_ref_best_rd != INT64_MAX) chroma_ref_best_rd = ref_best_rd - AOMMIN(this_rd, skip_txfm_rd); av1_txfm_rd_in_plane(x, cpi, &this_rd_stats, chroma_ref_best_rd, 0, plane, - plane_bsize, uv_tx_size, FTXS_NONE, skip_trellis); + plane_bsize, uv_tx_size, FTXS_NONE); if (this_rd_stats.rate == INT_MAX) { is_cost_valid = 0; break; @@ -3712,8 +3750,7 @@ void av1_txfm_rd_in_plane(MACROBLOCK *x, const AV1_COMP *cpi, RD_STATS *rd_stats, int64_t ref_best_rd, int64_t current_rd, int plane, BLOCK_SIZE plane_bsize, - TX_SIZE tx_size, FAST_TX_SEARCH_MODE ftxs_mode, - int skip_trellis) { + TX_SIZE tx_size, FAST_TX_SEARCH_MODE ftxs_mode) { assert(IMPLIES(plane == 0, x->e_mbd.mi[0]->tx_size == tx_size)); if (!cpi->oxcf.txfm_cfg.enable_tx64 && @@ -3736,7 +3773,7 @@ args.best_rd = ref_best_rd; args.current_rd = current_rd; args.ftxs_mode = ftxs_mode; - args.skip_trellis = skip_trellis; + args.skip_trellis = 0; av1_init_rd_stats(&args.rd_stats); av1_get_entropy_contexts(plane_bsize, pd, args.t_above, args.t_left); @@ -3792,8 +3829,6 @@ } else { av1_pick_uniform_tx_size_type_yrd(cpi, x, rd_stats_y, bsize, rd_thresh); memset(mbmi->inter_tx_size, mbmi->tx_size, sizeof(mbmi->inter_tx_size)); - for (int i = 0; i < xd->height * xd->width; ++i) - set_blk_skip(x->txfm_search_info.blk_skip, 0, i, rd_stats_y->skip_txfm); } if (rd_stats_y->rate == INT_MAX) return 0;
diff --git a/av1/encoder/tx_search.h b/av1/encoder/tx_search.h index 9181304..a16d549 100644 --- a/av1/encoder/tx_search.h +++ b/av1/encoder/tx_search.h
@@ -152,16 +152,13 @@ * \param[in] tx_size The given transform size * \param[in] ftxs_mode Transform search mode specifying desired speed and quality tradeoff - * \param[in] skip_trellis Binary flag indicating if trellis optimization - should be skipped * * \remark Nothing is returned. The RD results will be saved in rd_stats. */ void av1_txfm_rd_in_plane(MACROBLOCK *x, const AV1_COMP *cpi, RD_STATS *rd_stats, int64_t ref_best_rd, int64_t current_rd, int plane, BLOCK_SIZE plane_bsize, - TX_SIZE tx_size, FAST_TX_SEARCH_MODE ftxs_mode, - int skip_trellis); + TX_SIZE tx_size, FAST_TX_SEARCH_MODE ftxs_mode); /*!\brief Recursive transform size and type search. *
diff --git a/av1/encoder/txb_rdopt.c b/av1/encoder/txb_rdopt.c index 6f0fdff..cf76ffd 100644 --- a/av1/encoder/txb_rdopt.c +++ b/av1/encoder/txb_rdopt.c
@@ -80,7 +80,6 @@ const tran_low_t *tcoeff, tran_low_t *qcoeff, tran_low_t *dqcoeff, uint8_t *levels, int sharpness, const qm_val_t *iqmatrix, const qm_val_t *qmatrix) { - const int dqv = get_dqv(dequant, scan[si], iqmatrix); (void)eob; // this simple version assumes the coeff's scan_idx is not DC (scan_idx != 0) // and not the last (scan_idx != eob - 1) @@ -104,6 +103,7 @@ return; } + const int dqv = get_dqv(dequant, scan[si], iqmatrix); const int64_t dist = get_coeff_dist(abs_tqc, abs_dqc, shift, qmatrix, ci); const int64_t rd = RDCOST(rdmult, rate, dist); @@ -135,7 +135,6 @@ const LV_MAP_COEFF_COST *txb_costs, const tran_low_t *tcoeff, tran_low_t *qcoeff, tran_low_t *dqcoeff, uint8_t *levels, int sharpness, const qm_val_t *iqmatrix, const qm_val_t *qmatrix) { - const int dqv = get_dqv(dequant, scan[si], iqmatrix); assert(si != *eob - 1); const int ci = scan[si]; const tran_low_t qc = qcoeff[ci]; @@ -144,6 +143,7 @@ if (qc == 0) { *accu_rate += txb_costs->base_cost[coeff_ctx][0]; } else { + const int dqv = get_dqv(dequant, scan[si], iqmatrix); int lower_level = 0; const tran_low_t abs_qc = abs(qc); const tran_low_t tqc = tcoeff[ci]; @@ -299,6 +299,36 @@ return 0; } +static AOM_FORCE_INLINE void update_coeff_eob_facade( + int *accu_rate, int64_t *accu_dist, int *eob, int *nz_num, int *nz_ci, + int *si, TX_SIZE tx_size, TX_CLASS tx_class, int bhl, int width, + int dc_sign_ctx, int64_t rdmult, int shift, const int16_t *dequant, + const int16_t *scan, const LV_MAP_EOB_COST *txb_eob_costs, + const LV_MAP_COEFF_COST *txb_costs, const tran_low_t *tcoeff, + tran_low_t *qcoeff, tran_low_t *dqcoeff, uint8_t *levels, int sharpness, + const qm_val_t *iqmatrix, const qm_val_t *qmatrix, int max_nz_num) { + for (; *si >= 0 && *nz_num <= max_nz_num; --*si) { + update_coeff_eob(accu_rate, accu_dist, eob, nz_num, nz_ci, *si, tx_size, + tx_class, bhl, width, dc_sign_ctx, rdmult, shift, dequant, + scan, txb_eob_costs, txb_costs, tcoeff, qcoeff, dqcoeff, + levels, sharpness, iqmatrix, qmatrix); + } +} + +static AOM_FORCE_INLINE void update_coeff_simple_facade( + int *accu_rate, int *si, int eob, TX_SIZE tx_size, TX_CLASS tx_class, + int bhl, int64_t rdmult, int shift, const int16_t *dequant, + const int16_t *scan, const LV_MAP_COEFF_COST *txb_costs, + const tran_low_t *tcoeff, tran_low_t *qcoeff, tran_low_t *dqcoeff, + uint8_t *levels, int sharpness, const qm_val_t *iqmatrix, + const qm_val_t *qmatrix) { + for (; *si >= 1; --*si) { + update_coeff_simple(accu_rate, *si, eob, tx_size, tx_class, bhl, rdmult, + shift, dequant, scan, txb_costs, tcoeff, qcoeff, + dqcoeff, levels, sharpness, iqmatrix, qmatrix); + } +} + int av1_optimize_txb(const struct AV1_COMP *cpi, MACROBLOCK *x, int plane, int block, TX_SIZE tx_size, TX_TYPE tx_type, const TXB_CTX *const txb_ctx, int *rate_cost, @@ -400,13 +430,11 @@ #define UPDATE_COEFF_EOB_CASE(tx_class_literal) \ case tx_class_literal: \ - for (; si >= 0 && nz_num <= max_nz_num; --si) { \ - update_coeff_eob(&accu_rate, &accu_dist, &eob, &nz_num, nz_ci, si, \ - tx_size, tx_class_literal, bhl, width, \ - txb_ctx->dc_sign_ctx, rdmult, shift, dequant, scan, \ - txb_eob_costs, txb_costs, tcoeff, qcoeff, dqcoeff, \ - levels, sharpness, iqmatrix, qmatrix); \ - } \ + update_coeff_eob_facade( \ + &accu_rate, &accu_dist, &eob, &nz_num, nz_ci, &si, tx_size, \ + tx_class_literal, bhl, width, txb_ctx->dc_sign_ctx, rdmult, shift, \ + dequant, scan, txb_eob_costs, txb_costs, tcoeff, qcoeff, dqcoeff, \ + levels, sharpness, iqmatrix, qmatrix, max_nz_num); \ break switch (tx_class) { UPDATE_COEFF_EOB_CASE(TX_CLASS_2D); @@ -421,14 +449,12 @@ non_skip_cost, qcoeff, dqcoeff); } -#define UPDATE_COEFF_SIMPLE_CASE(tx_class_literal) \ - case tx_class_literal: \ - for (; si >= 1; --si) { \ - update_coeff_simple(&accu_rate, si, eob, tx_size, tx_class_literal, bhl, \ - rdmult, shift, dequant, scan, txb_costs, tcoeff, \ - qcoeff, dqcoeff, levels, sharpness, iqmatrix, \ - qmatrix); \ - } \ +#define UPDATE_COEFF_SIMPLE_CASE(tx_class_literal) \ + case tx_class_literal: \ + update_coeff_simple_facade(&accu_rate, &si, eob, tx_size, \ + tx_class_literal, bhl, rdmult, shift, dequant, \ + scan, txb_costs, tcoeff, qcoeff, dqcoeff, \ + levels, sharpness, iqmatrix, qmatrix); \ break switch (tx_class) { UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_2D);
diff --git a/av1/encoder/var_based_part.c b/av1/encoder/var_based_part.c index 3027726..d09b0ca 100644 --- a/av1/encoder/var_based_part.c +++ b/av1/encoder/var_based_part.c
@@ -1027,6 +1027,10 @@ const YV12_BUFFER_CONFIG *yv12_alt = get_ref_frame_yv12_buf(cm, ALTREF_FRAME); const struct scale_factors *const sf = get_ref_scale_factors_const(cm, LAST_FRAME); + const struct scale_factors *const sf_g = + get_ref_scale_factors_const(cm, GOLDEN_FRAME); + const struct scale_factors *const sf_alt = + get_ref_scale_factors_const(cm, ALTREF_FRAME); struct buf_2d dst; unsigned int uv_sad_g = 0; unsigned int uv_sad_alt = 0; @@ -1063,7 +1067,7 @@ uint8_t *src = (plane == 1) ? yv12_g->u_buffer : yv12_g->v_buffer; setup_pred_plane(&dst, xd->mi[0]->bsize, src, yv12_g->uv_crop_width, yv12_g->uv_crop_height, yv12_g->uv_stride, xd->mi_row, - xd->mi_col, sf, xd->plane[plane].subsampling_x, + xd->mi_col, sf_g, xd->plane[plane].subsampling_x, xd->plane[plane].subsampling_y); uv_sad_g = cpi->ppi->fn_ptr[bs].sdf(p->src.buf, p->src.stride, dst.buf, dst.stride); @@ -1074,7 +1078,7 @@ uint8_t *src = (plane == 1) ? yv12_alt->u_buffer : yv12_alt->v_buffer; setup_pred_plane(&dst, xd->mi[0]->bsize, src, yv12_alt->uv_crop_width, yv12_alt->uv_crop_height, yv12_alt->uv_stride, - xd->mi_row, xd->mi_col, sf, + xd->mi_row, xd->mi_col, sf_alt, xd->plane[plane].subsampling_x, xd->plane[plane].subsampling_y); uv_sad_alt = cpi->ppi->fn_ptr[bs].sdf(p->src.buf, p->src.stride, @@ -1368,9 +1372,9 @@ x->sb_me_mv.as_int = mi->mv[0].as_int; if (cpi->svc.temporal_layer_id == 0) { if (abs(mi->mv[0].as_mv.col) > 16 && abs(mi->mv[0].as_mv.row) == 0) - x->sb_col_scroll = 1; + x->sb_col_scroll++; else if (abs(mi->mv[0].as_mv.row) > 16 && abs(mi->mv[0].as_mv.col) == 0) - x->sb_row_scroll = 1; + x->sb_row_scroll++; } } else { x->sb_me_partition = 0;
diff --git a/av1/encoder/x86/highbd_temporal_filter_avx2.c b/av1/encoder/x86/highbd_temporal_filter_avx2.c index 6232e9e..acaf767 100644 --- a/av1/encoder/x86/highbd_temporal_filter_avx2.c +++ b/av1/encoder/x86/highbd_temporal_filter_avx2.c
@@ -26,78 +26,48 @@ { 0, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF } }; -static AOM_FORCE_INLINE void get_squared_error_16x16_avx2( +static AOM_FORCE_INLINE void get_squared_error_avx2( const uint16_t *frame1, const unsigned int stride, const uint16_t *frame2, const unsigned int stride2, const int block_width, const int block_height, uint32_t *frame_sse, const unsigned int sse_stride) { - (void)block_width; const uint16_t *src1 = frame1; const uint16_t *src2 = frame2; + assert(block_width >= 32); uint32_t *dst = frame_sse + 2; for (int i = 0; i < block_height; i++) { - __m256i v_src1 = _mm256_loadu_si256((__m256i *)src1); - __m256i v_src2 = _mm256_loadu_si256((__m256i *)src2); - __m256i v_diff = _mm256_sub_epi16(v_src1, v_src2); - __m256i v_mullo = _mm256_mullo_epi16(v_diff, v_diff); - __m256i v_mulhi = _mm256_mulhi_epi16(v_diff, v_diff); + for (int j = 0; j < block_width; j += 32) { + __m256i v_src1 = _mm256_loadu_si256((__m256i *)(src1 + j)); + __m256i v_src2 = _mm256_loadu_si256((__m256i *)(src2 + j)); + __m256i v_diff = _mm256_sub_epi16(v_src1, v_src2); + __m256i v_mullo = _mm256_mullo_epi16(v_diff, v_diff); + __m256i v_mulhi = _mm256_mulhi_epi16(v_diff, v_diff); - __m256i v_lo = _mm256_unpacklo_epi16(v_mullo, v_mulhi); - __m256i v_hi = _mm256_unpackhi_epi16(v_mullo, v_mulhi); - __m256i diff_lo = - _mm256_inserti128_si256(v_lo, _mm256_extracti128_si256(v_hi, 0), 1); - __m256i diff_hi = - _mm256_inserti128_si256(v_hi, _mm256_extracti128_si256(v_lo, 1), 0); + __m256i v_lo = _mm256_unpacklo_epi16(v_mullo, v_mulhi); + __m256i v_hi = _mm256_unpackhi_epi16(v_mullo, v_mulhi); + __m256i diff_lo = + _mm256_inserti128_si256(v_lo, _mm256_extracti128_si256(v_hi, 0), 1); + __m256i diff_hi = + _mm256_inserti128_si256(v_hi, _mm256_extracti128_si256(v_lo, 1), 0); - _mm256_storeu_si256((__m256i *)dst, diff_lo); - dst += 8; - _mm256_storeu_si256((__m256i *)dst, diff_hi); + _mm256_storeu_si256((__m256i *)(dst + j), diff_lo); + _mm256_storeu_si256((__m256i *)(dst + j + 8), diff_hi); - src1 += stride, src2 += stride2; - dst += sse_stride - 8; - } -} + v_src1 = _mm256_loadu_si256((__m256i *)(src1 + j + 16)); + v_src2 = _mm256_loadu_si256((__m256i *)(src2 + j + 16)); + v_diff = _mm256_sub_epi16(v_src1, v_src2); + v_mullo = _mm256_mullo_epi16(v_diff, v_diff); + v_mulhi = _mm256_mulhi_epi16(v_diff, v_diff); -static AOM_FORCE_INLINE void get_squared_error_32x32_avx2( - const uint16_t *frame1, const unsigned int stride, const uint16_t *frame2, - const unsigned int stride2, const int block_width, const int block_height, - uint32_t *frame_sse, const unsigned int sse_stride) { - (void)block_width; - const uint16_t *src1 = frame1; - const uint16_t *src2 = frame2; - uint32_t *dst = frame_sse + 2; - for (int i = 0; i < block_height; i++) { - __m256i v_src1 = _mm256_loadu_si256((__m256i *)src1); - __m256i v_src2 = _mm256_loadu_si256((__m256i *)src2); - __m256i v_diff = _mm256_sub_epi16(v_src1, v_src2); - __m256i v_mullo = _mm256_mullo_epi16(v_diff, v_diff); - __m256i v_mulhi = _mm256_mulhi_epi16(v_diff, v_diff); + v_lo = _mm256_unpacklo_epi16(v_mullo, v_mulhi); + v_hi = _mm256_unpackhi_epi16(v_mullo, v_mulhi); + diff_lo = + _mm256_inserti128_si256(v_lo, _mm256_extracti128_si256(v_hi, 0), 1); + diff_hi = + _mm256_inserti128_si256(v_hi, _mm256_extracti128_si256(v_lo, 1), 0); - __m256i v_lo = _mm256_unpacklo_epi16(v_mullo, v_mulhi); - __m256i v_hi = _mm256_unpackhi_epi16(v_mullo, v_mulhi); - __m256i diff_lo = - _mm256_inserti128_si256(v_lo, _mm256_extracti128_si256(v_hi, 0), 1); - __m256i diff_hi = - _mm256_inserti128_si256(v_hi, _mm256_extracti128_si256(v_lo, 1), 0); - - _mm256_storeu_si256((__m256i *)dst, diff_lo); - _mm256_storeu_si256((__m256i *)(dst + 8), diff_hi); - - v_src1 = _mm256_loadu_si256((__m256i *)(src1 + 16)); - v_src2 = _mm256_loadu_si256((__m256i *)(src2 + 16)); - v_diff = _mm256_sub_epi16(v_src1, v_src2); - v_mullo = _mm256_mullo_epi16(v_diff, v_diff); - v_mulhi = _mm256_mulhi_epi16(v_diff, v_diff); - - v_lo = _mm256_unpacklo_epi16(v_mullo, v_mulhi); - v_hi = _mm256_unpackhi_epi16(v_mullo, v_mulhi); - diff_lo = - _mm256_inserti128_si256(v_lo, _mm256_extracti128_si256(v_hi, 0), 1); - diff_hi = - _mm256_inserti128_si256(v_hi, _mm256_extracti128_si256(v_lo, 1), 0); - - _mm256_storeu_si256((__m256i *)(dst + 16), diff_lo); - _mm256_storeu_si256((__m256i *)(dst + 24), diff_hi); - + _mm256_storeu_si256((__m256i *)(dst + j + 16), diff_lo); + _mm256_storeu_si256((__m256i *)(dst + j + 24), diff_hi); + } src1 += stride; src2 += stride2; dst += sse_stride; @@ -150,18 +120,13 @@ const double inv_num_ref_pixels, const double decay_factor, const double inv_factor, const double weight_factor, double *d_factor, int tf_wgt_calc_lvl) { - assert(((block_width == 16) || (block_width == 32)) && - ((block_height == 16) || (block_height == 32))); + assert(((block_width == 64) || (block_width == 32)) && + ((block_height == 64) || (block_height == 32))); uint32_t acc_5x5_sse[BH][BW]; - if (block_width == 32) { - get_squared_error_32x32_avx2(frame1, stride, frame2, stride2, block_width, - block_height, frame_sse, SSE_STRIDE); - } else { - get_squared_error_16x16_avx2(frame1, stride, frame2, stride2, block_width, - block_height, frame_sse, SSE_STRIDE); - } + get_squared_error_avx2(frame1, stride, frame2, stride2, block_width, + block_height, frame_sse, SSE_STRIDE); __m256i vsrc[5]; @@ -306,15 +271,17 @@ acc_5x5_sse[row][col + 3] = xx_mask_and_hadd(vsum, 3); } - double subblock_mses_scaled[4]; - double d_factor_decayed[4]; - for (int idx = 0; idx < 4; idx++) { + double subblock_mses_scaled[NUM_16X16]; + double d_factor_decayed[NUM_16X16]; + for (int idx = 0; idx < NUM_16X16; idx++) { subblock_mses_scaled[idx] = subblock_mses[idx] * inv_factor; d_factor_decayed[idx] = d_factor[idx] * decay_factor; } if (tf_wgt_calc_lvl == 0) { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; @@ -323,7 +290,12 @@ diff_sse >>= ((bd - 8) * 2); const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -338,7 +310,9 @@ } } else { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; @@ -347,7 +321,13 @@ diff_sse >>= ((bd - 8) * 2); const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + // const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -373,8 +353,8 @@ int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count) { const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; - assert(block_size == BLOCK_32X32 && "Only support 32x32 block with sse2!"); - assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with sse2!"); + assert(block_size == BLOCK_64X64 && "Only support 64x64 block with avx2!"); + assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with avx2!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE); (void)is_high_bitdepth; @@ -401,17 +381,21 @@ // Smaller strength -> smaller filtering weight. double s_decay = pow((double)filter_strength / TF_STRENGTH_THRESHOLD, 2); s_decay = CLIP(s_decay, 1e-5, 1); - double d_factor[4] = { 0 }; - uint32_t frame_sse[SSE_STRIDE * BH] = { 0 }; - uint32_t luma_sse_sum[BW * BH] = { 0 }; + double d_factor[NUM_16X16] = { 0 }; + uint32_t *frame_sse = + (uint32_t *)aom_memalign(32, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + uint32_t *luma_sse_sum = + (uint32_t *)aom_memalign(32, sizeof(luma_sse_sum[0]) * BW * BH); + memset(frame_sse, 0, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + memset(luma_sse_sum, 0, sizeof(luma_sse_sum[0]) * BW * BH); uint16_t *pred1 = CONVERT_TO_SHORTPTR(pred); - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; + distance_threshold = AOMMAX(distance_threshold, 1); + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); - double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; - distance_threshold = AOMMAX(distance_threshold, 1); d_factor[subblock_idx] = distance / distance_threshold; d_factor[subblock_idx] = AOMMAX(d_factor[subblock_idx], 1); } @@ -443,13 +427,36 @@ // will be more accurate. The luma sse sum is reused in both chroma // planes. if (plane == AOM_PLANE_U) { - for (unsigned int i = 0, k = 0; i < plane_h; i++) { - for (unsigned int j = 0; j < plane_w; j++, k++) { - for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { - for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { - const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. - const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. - luma_sse_sum[i * BW + j] += frame_sse[yy * SSE_STRIDE + xx + 2]; + if (ss_x_shift == 1 && ss_y_shift == 1) { + for (unsigned int i = 0; i < plane_h; i++) { + const uint32_t *src_0 = &frame_sse[2 * i * SSE_STRIDE + 2]; + const uint32_t *src_1 = &frame_sse[(2 * i + 1) * SSE_STRIDE + 2]; + for (unsigned int j = 0; j < plane_w; j += 8) { + const __m256i reg0_lo = + _mm256_loadu_si256((__m256i *)(src_0 + 2 * j)); + const __m256i reg1_lo = + _mm256_loadu_si256((__m256i *)(src_1 + 2 * j)); + const __m256i reg0_hi = + _mm256_loadu_si256((__m256i *)(src_0 + 2 * j + 8)); + const __m256i reg1_hi = + _mm256_loadu_si256((__m256i *)(src_1 + 2 * j + 8)); + + const __m256i reg_0 = _mm256_add_epi32(reg0_lo, reg1_lo); + const __m256i reg_1 = _mm256_add_epi32(reg0_hi, reg1_hi); + __m256i res = _mm256_hadd_epi32(reg_0, reg_1); + res = _mm256_permute4x64_epi64(res, 0xD8); + _mm256_storeu_si256((__m256i *)&luma_sse_sum[i * BW + j], res); + } + } + } else { + for (unsigned int i = 0, k = 0; i < plane_h; i++) { + for (unsigned int j = 0; j < plane_w; j++, k++) { + for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { + for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { + const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. + const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. + luma_sse_sum[i * BW + j] += frame_sse[yy * SSE_STRIDE + xx + 2]; + } } } } @@ -463,4 +470,6 @@ weight_factor, d_factor, tf_wgt_calc_lvl); plane_offset += plane_h * plane_w; } + aom_free(frame_sse); + aom_free(luma_sse_sum); }
diff --git a/av1/encoder/x86/highbd_temporal_filter_sse2.c b/av1/encoder/x86/highbd_temporal_filter_sse2.c index b328dca..5cf18ab 100644 --- a/av1/encoder/x86/highbd_temporal_filter_sse2.c +++ b/av1/encoder/x86/highbd_temporal_filter_sse2.c
@@ -98,8 +98,8 @@ const double inv_num_ref_pixels, const double decay_factor, const double inv_factor, const double weight_factor, double *d_factor, int tf_wgt_calc_lvl) { - assert(((block_width == 16) || (block_width == 32)) && - ((block_height == 16) || (block_height == 32))); + assert(((block_width == 64) || (block_width == 32)) && + ((block_height == 64) || (block_height == 32))); uint32_t acc_5x5_sse[BH][BW]; @@ -181,15 +181,17 @@ } } - double subblock_mses_scaled[4]; - double d_factor_decayed[4]; - for (int idx = 0; idx < 4; idx++) { + double subblock_mses_scaled[NUM_16X16]; + double d_factor_decayed[NUM_16X16]; + for (int idx = 0; idx < NUM_16X16; idx++) { subblock_mses_scaled[idx] = subblock_mses[idx] * inv_factor; d_factor_decayed[idx] = d_factor[idx] * decay_factor; } if (tf_wgt_calc_lvl == 0) { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; @@ -198,7 +200,12 @@ diff_sse >>= ((bd - 8) * 2); const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -213,7 +220,9 @@ } } else { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; @@ -222,7 +231,12 @@ diff_sse >>= ((bd - 8) * 2); const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -248,7 +262,7 @@ int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count) { const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; - assert(block_size == BLOCK_32X32 && "Only support 32x32 block with sse2!"); + assert(block_size == BLOCK_64X64 && "Only support 64x64 block with sse2!"); assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with sse2!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE); (void)is_high_bitdepth; @@ -276,17 +290,22 @@ // Smaller strength -> smaller filtering weight. double s_decay = pow((double)filter_strength / TF_STRENGTH_THRESHOLD, 2); s_decay = CLIP(s_decay, 1e-5, 1); - double d_factor[4] = { 0 }; - uint32_t frame_sse[SSE_STRIDE * BH] = { 0 }; - uint32_t luma_sse_sum[BW * BH] = { 0 }; + + double d_factor[NUM_16X16] = { 0 }; + uint32_t *frame_sse = + (uint32_t *)aom_memalign(32, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + uint32_t *luma_sse_sum = + (uint32_t *)aom_memalign(32, sizeof(luma_sse_sum[0]) * BW * BH); + memset(frame_sse, 0, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + memset(luma_sse_sum, 0, sizeof(luma_sse_sum[0]) * BW * BH); uint16_t *pred1 = CONVERT_TO_SHORTPTR(pred); - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; + distance_threshold = AOMMAX(distance_threshold, 1); + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); - double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; - distance_threshold = AOMMAX(distance_threshold, 1); d_factor[subblock_idx] = distance / distance_threshold; d_factor[subblock_idx] = AOMMAX(d_factor[subblock_idx], 1); } @@ -338,4 +357,6 @@ weight_factor, d_factor, tf_wgt_calc_lvl); plane_offset += plane_h * plane_w; } + aom_free(frame_sse); + aom_free(luma_sse_sum); }
diff --git a/av1/encoder/x86/reconinter_enc_sse2.c b/av1/encoder/x86/reconinter_enc_sse2.c index a18e172..8ab566a 100644 --- a/av1/encoder/x86/reconinter_enc_sse2.c +++ b/av1/encoder/x86/reconinter_enc_sse2.c
@@ -126,8 +126,7 @@ aom_convolve8_vert(ref, ref_stride, comp_pred, width, NULL, -1, kernel, 16, width, height); } else { - DECLARE_ALIGNED(16, uint8_t, - temp[((MAX_SB_SIZE * 2 + 16) + 16) * MAX_SB_SIZE]); + uint8_t *temp = comp_pred; const int16_t *const kernel_x = av1_get_interp_filter_subpel_kernel(filter, subpel_x_q3 << 1); const int16_t *const kernel_y = @@ -230,8 +229,7 @@ aom_highbd_convolve8_vert(ref8, ref_stride, comp_pred8, width, NULL, -1, kernel, 16, width, height, bd); } else { - DECLARE_ALIGNED(16, uint16_t, - temp[((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE]); + uint16_t *temp = CONVERT_TO_SHORTPTR(comp_pred8); const int16_t *const kernel_x = av1_get_interp_filter_subpel_kernel(filter, subpel_x_q3 << 1); const int16_t *const kernel_y =
diff --git a/av1/encoder/x86/temporal_filter_avx2.c b/av1/encoder/x86/temporal_filter_avx2.c index f3d85e1..45d563b 100644 --- a/av1/encoder/x86/temporal_filter_avx2.c +++ b/av1/encoder/x86/temporal_filter_avx2.c
@@ -229,62 +229,35 @@ return (count < 16) ? -1.0 : (double)accum / (6 * count) * SQRT_PI_BY_2; } -static AOM_FORCE_INLINE void get_squared_error_16x16_avx2( +static AOM_FORCE_INLINE void get_squared_error_avx2( const uint8_t *frame1, const unsigned int stride, const uint8_t *frame2, const unsigned int stride2, const int block_width, const int block_height, uint16_t *frame_sse, const unsigned int sse_stride) { - (void)block_width; const uint8_t *src1 = frame1; const uint8_t *src2 = frame2; uint16_t *dst = frame_sse; for (int i = 0; i < block_height; i++) { - __m128i vf1_128, vf2_128; - __m256i vf1, vf2, vdiff1, vsqdiff1; + for (int j = 0; j < block_width; j += 32) { + __m256i vsrc1, vsrc2, vmin, vmax, vdiff, vdiff1, vdiff2, vres1, vres2; - vf1_128 = _mm_loadu_si128((__m128i *)(src1)); - vf2_128 = _mm_loadu_si128((__m128i *)(src2)); - vf1 = _mm256_cvtepu8_epi16(vf1_128); - vf2 = _mm256_cvtepu8_epi16(vf2_128); - vdiff1 = _mm256_sub_epi16(vf1, vf2); - vsqdiff1 = _mm256_mullo_epi16(vdiff1, vdiff1); + vsrc1 = _mm256_loadu_si256((__m256i *)(src1 + j)); + vsrc2 = _mm256_loadu_si256((__m256i *)(src2 + j)); + vmax = _mm256_max_epu8(vsrc1, vsrc2); + vmin = _mm256_min_epu8(vsrc1, vsrc2); + vdiff = _mm256_subs_epu8(vmax, vmin); - _mm256_storeu_si256((__m256i *)(dst), vsqdiff1); + __m128i vtmp1 = _mm256_castsi256_si128(vdiff); + __m128i vtmp2 = _mm256_extracti128_si256(vdiff, 1); + vdiff1 = _mm256_cvtepu8_epi16(vtmp1); + vdiff2 = _mm256_cvtepu8_epi16(vtmp2); + + vres1 = _mm256_mullo_epi16(vdiff1, vdiff1); + vres2 = _mm256_mullo_epi16(vdiff2, vdiff2); + _mm256_storeu_si256((__m256i *)(dst + j), vres1); + _mm256_storeu_si256((__m256i *)(dst + 16 + j), vres2); + } // Set zero to uninitialized memory to avoid uninitialized loads later - *(int *)(dst + 16) = _mm_cvtsi128_si32(_mm_setzero_si128()); - - src1 += stride, src2 += stride2; - dst += sse_stride; - } -} - -static AOM_FORCE_INLINE void get_squared_error_32x32_avx2( - const uint8_t *frame1, const unsigned int stride, const uint8_t *frame2, - const unsigned int stride2, const int block_width, const int block_height, - uint16_t *frame_sse, const unsigned int sse_stride) { - (void)block_width; - const uint8_t *src1 = frame1; - const uint8_t *src2 = frame2; - uint16_t *dst = frame_sse; - for (int i = 0; i < block_height; i++) { - __m256i vsrc1, vsrc2, vmin, vmax, vdiff, vdiff1, vdiff2, vres1, vres2; - - vsrc1 = _mm256_loadu_si256((__m256i *)src1); - vsrc2 = _mm256_loadu_si256((__m256i *)src2); - vmax = _mm256_max_epu8(vsrc1, vsrc2); - vmin = _mm256_min_epu8(vsrc1, vsrc2); - vdiff = _mm256_subs_epu8(vmax, vmin); - - __m128i vtmp1 = _mm256_castsi256_si128(vdiff); - __m128i vtmp2 = _mm256_extracti128_si256(vdiff, 1); - vdiff1 = _mm256_cvtepu8_epi16(vtmp1); - vdiff2 = _mm256_cvtepu8_epi16(vtmp2); - - vres1 = _mm256_mullo_epi16(vdiff1, vdiff1); - vres2 = _mm256_mullo_epi16(vdiff2, vdiff2); - _mm256_storeu_si256((__m256i *)(dst), vres1); - _mm256_storeu_si256((__m256i *)(dst + 16), vres2); - // Set zero to uninitialized memory to avoid uninitialized loads later - *(int *)(dst + 32) = _mm_cvtsi128_si32(_mm_setzero_si128()); + *(int *)(dst + block_width) = _mm_cvtsi128_si32(_mm_setzero_si128()); src1 += stride; src2 += stride2; @@ -351,18 +324,13 @@ const double inv_num_ref_pixels, const double decay_factor, const double inv_factor, const double weight_factor, double *d_factor, int tf_wgt_calc_lvl) { - assert(((block_width == 16) || (block_width == 32)) && - ((block_height == 16) || (block_height == 32))); + assert(((block_width == 64) || (block_width == 32)) && + ((block_height == 64) || (block_height == 32))); uint32_t acc_5x5_sse[BH][BW]; - if (block_width == 32) { - get_squared_error_32x32_avx2(frame1, stride, frame2, stride2, block_width, - block_height, frame_sse, SSE_STRIDE); - } else { - get_squared_error_16x16_avx2(frame1, stride, frame2, stride2, block_width, - block_height, frame_sse, SSE_STRIDE); - } + get_squared_error_avx2(frame1, stride, frame2, stride2, block_width, + block_height, frame_sse, SSE_STRIDE); __m256i vsrc[5]; @@ -409,21 +377,28 @@ } } - double subblock_mses_scaled[4]; - double d_factor_decayed[4]; - for (int idx = 0; idx < 4; idx++) { + double subblock_mses_scaled[NUM_16X16]; + double d_factor_decayed[NUM_16X16]; + for (int idx = 0; idx < NUM_16X16; idx++) { subblock_mses_scaled[idx] = subblock_mses[idx] * inv_factor; d_factor_decayed[idx] = d_factor[idx] * decay_factor; } if (tf_wgt_calc_lvl == 0) { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -436,8 +411,8 @@ } } } else { - __m256d subblock_mses_reg[4]; - __m256d d_factor_mul_n_decay_qr_invs[4]; + __m256d subblock_mses_reg[NUM_16X16]; + __m256d d_factor_mul_n_decay_qr_invs[NUM_16X16]; const __m256 zero = _mm256_set1_ps(0.0f); const __m256 point_five = _mm256_set1_ps(0.5f); const __m256 seven = _mm256_set1_ps(7.0f); @@ -445,17 +420,15 @@ const __m256d weight_factor_256bit = _mm256_set1_pd(weight_factor); const __m256 tf_weight_scale = _mm256_set1_ps((float)TF_WEIGHT_SCALE); // Maintain registers to hold mse and d_factor at subblock level. - subblock_mses_reg[0] = _mm256_set1_pd(subblock_mses_scaled[0]); - subblock_mses_reg[1] = _mm256_set1_pd(subblock_mses_scaled[1]); - subblock_mses_reg[2] = _mm256_set1_pd(subblock_mses_scaled[2]); - subblock_mses_reg[3] = _mm256_set1_pd(subblock_mses_scaled[3]); - d_factor_mul_n_decay_qr_invs[0] = _mm256_set1_pd(d_factor_decayed[0]); - d_factor_mul_n_decay_qr_invs[1] = _mm256_set1_pd(d_factor_decayed[1]); - d_factor_mul_n_decay_qr_invs[2] = _mm256_set1_pd(d_factor_decayed[2]); - d_factor_mul_n_decay_qr_invs[3] = _mm256_set1_pd(d_factor_decayed[3]); + for (int i = 0; i < NUM_16X16; i++) { + subblock_mses_reg[i] = _mm256_set1_pd(subblock_mses_scaled[i]); + d_factor_mul_n_decay_qr_invs[i] = _mm256_set1_pd(d_factor_decayed[i]); + } for (int i = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; uint32_t *luma_sse_sum_temp = luma_sse_sum + i * BW; for (int j = 0; j < block_width; j += 8) { const __m256i acc_sse = @@ -477,9 +450,13 @@ const __m256d window_error_2 = _mm256_mul_pd(diff_sse_pd_2, inv_num_ref_pixel_256bit); - // const int subblock_idx = y_blk_raster_offset + (j >= block_width / - // 2); - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + // const int subblock_idx = (y32 * 2 + x32) * 4 + (y16 * 2 + x16); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const __m256d blk_error = subblock_mses_reg[subblock_idx]; // const double combined_error = @@ -555,7 +532,7 @@ int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count) { const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; - assert(block_size == BLOCK_32X32 && "Only support 32x32 block with avx2!"); + assert(block_size == BLOCK_64X64 && "Only support 64x64 block with avx2!"); assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with avx2!"); assert(!is_high_bitdepth && "Only support low bit-depth with avx2!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE); @@ -584,16 +561,20 @@ // Smaller strength -> smaller filtering weight. double s_decay = pow((double)filter_strength / TF_STRENGTH_THRESHOLD, 2); s_decay = CLIP(s_decay, 1e-5, 1); - double d_factor[4] = { 0 }; - uint16_t frame_sse[SSE_STRIDE * BH] = { 0 }; - uint32_t luma_sse_sum[BW * BH] = { 0 }; + double d_factor[NUM_16X16] = { 0 }; + uint16_t *frame_sse = + (uint16_t *)aom_memalign(32, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + uint32_t *luma_sse_sum = + (uint32_t *)aom_memalign(32, sizeof(luma_sse_sum[0]) * BW * BH); + memset(frame_sse, 0, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + memset(luma_sse_sum, 0, sizeof(luma_sse_sum[0]) * BW * BH); - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; + distance_threshold = AOMMAX(distance_threshold, 1); + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); - double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; - distance_threshold = AOMMAX(distance_threshold, 1); d_factor[subblock_idx] = distance / distance_threshold; d_factor[subblock_idx] = AOMMAX(d_factor[subblock_idx], 1); } @@ -624,13 +605,35 @@ // will be more accurate. The luma sse sum is reused in both chroma // planes. if (plane == AOM_PLANE_U) { - for (unsigned int i = 0, k = 0; i < plane_h; i++) { - for (unsigned int j = 0; j < plane_w; j++, k++) { - for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { - for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { - const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. - const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. - luma_sse_sum[i * BW + j] += frame_sse[yy * SSE_STRIDE + xx]; + if (ss_x_shift == 1 && ss_y_shift == 1) { + const __m256i zero_reg = _mm256_setzero_si256(); + for (unsigned int i = 0; i < plane_h; i++) { + const uint16_t *src_0 = &frame_sse[2 * i * SSE_STRIDE]; + const uint16_t *src_1 = &frame_sse[(2 * i + 1) * SSE_STRIDE]; + for (unsigned int j = 0; j < plane_w; j += 8) { + const __m256i reg0 = _mm256_loadu_si256((__m256i *)(src_0 + j * 2)); + const __m256i reg1 = _mm256_loadu_si256((__m256i *)(src_1 + j * 2)); + + const __m256i reg0_lo = _mm256_unpacklo_epi16(reg0, zero_reg); + const __m256i reg0_hi = _mm256_unpackhi_epi16(reg0, zero_reg); + const __m256i reg1_lo = _mm256_unpacklo_epi16(reg1, zero_reg); + const __m256i reg1_hi = _mm256_unpackhi_epi16(reg1, zero_reg); + + const __m256i reg_0 = _mm256_add_epi32(reg0_lo, reg1_lo); + const __m256i reg_1 = _mm256_add_epi32(reg0_hi, reg1_hi); + const __m256i res = _mm256_hadd_epi32(reg_0, reg_1); + _mm256_storeu_si256((__m256i *)&luma_sse_sum[i * BW + j], res); + } + } + } else { + for (unsigned int i = 0, k = 0; i < plane_h; i++) { + for (unsigned int j = 0; j < plane_w; j++, k++) { + for (int ii = 0; ii < (1 << ss_y_shift); ++ii) { + for (int jj = 0; jj < (1 << ss_x_shift); ++jj) { + const int yy = (i << ss_y_shift) + ii; // Y-coord on Y-plane. + const int xx = (j << ss_x_shift) + jj; // X-coord on Y-plane. + luma_sse_sum[i * BW + j] += frame_sse[yy * SSE_STRIDE + xx]; + } } } } @@ -644,4 +647,6 @@ weight_factor, d_factor, tf_wgt_calc_lvl); plane_offset += plane_h * plane_w; } + aom_free(frame_sse); + aom_free(luma_sse_sum); }
diff --git a/av1/encoder/x86/temporal_filter_sse2.c b/av1/encoder/x86/temporal_filter_sse2.c index 8a8c947..7fcea7e 100644 --- a/av1/encoder/x86/temporal_filter_sse2.c +++ b/av1/encoder/x86/temporal_filter_sse2.c
@@ -110,8 +110,8 @@ const double inv_num_ref_pixels, const double decay_factor, const double inv_factor, const double weight_factor, double *d_factor, int tf_wgt_calc_lvl) { - assert(((block_width == 16) || (block_width == 32)) && - ((block_height == 16) || (block_height == 32))); + assert(((block_width == 64) || (block_width == 32)) && + ((block_height == 64) || (block_height == 32))); uint32_t acc_5x5_sse[BH][BW]; @@ -170,21 +170,28 @@ } } - double subblock_mses_scaled[4]; - double d_factor_decayed[4]; - for (int idx = 0; idx < 4; idx++) { + double subblock_mses_scaled[NUM_16X16]; + double d_factor_decayed[NUM_16X16]; + for (int idx = 0; idx < NUM_16X16; idx++) { subblock_mses_scaled[idx] = subblock_mses[idx] * inv_factor; d_factor_decayed[idx] = d_factor[idx] * decay_factor; } if (tf_wgt_calc_lvl == 0) { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -198,13 +205,20 @@ } } else { for (int i = 0, k = 0; i < block_height; i++) { - const int y_blk_raster_offset = (i >= block_height / 2) * 2; + const int y32_blk_raster_offset = (i >= (block_height >> 1)) << 1; + const int y16_blk_raster_offset = + ((i % (block_height >> 1)) >= (block_height >> 2)) << 1; for (int j = 0; j < block_width; j++, k++) { const int pixel_value = frame2[i * stride2 + j]; uint32_t diff_sse = acc_5x5_sse[i][j] + luma_sse_sum[i * BW + j]; const double window_error = diff_sse * inv_num_ref_pixels; - const int subblock_idx = y_blk_raster_offset + (j >= block_width / 2); + const int x32_blk_raster_offset = (j >= (block_width >> 1)); + const int x16_blk_raster_offset = + ((j % (block_width >> 1)) >= (block_width >> 2)); + const int subblock_idx = + ((y32_blk_raster_offset + x32_blk_raster_offset) << 2) + + (y16_blk_raster_offset + x16_blk_raster_offset); const double combined_error = weight_factor * window_error + subblock_mses_scaled[subblock_idx]; @@ -228,7 +242,7 @@ int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count) { const int is_high_bitdepth = frame_to_filter->flags & YV12_FLAG_HIGHBITDEPTH; - assert(block_size == BLOCK_32X32 && "Only support 32x32 block with sse2!"); + assert(block_size == BLOCK_64X64 && "Only support 64x64 block with sse2!"); assert(TF_WINDOW_LENGTH == 5 && "Only support window length 5 with sse2!"); assert(!is_high_bitdepth && "Only support low bit-depth with sse2!"); assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE); @@ -257,16 +271,20 @@ // Smaller strength -> smaller filtering weight. double s_decay = pow((double)filter_strength / TF_STRENGTH_THRESHOLD, 2); s_decay = CLIP(s_decay, 1e-5, 1); - double d_factor[4] = { 0 }; - uint16_t frame_sse[SSE_STRIDE * BH] = { 0 }; - uint32_t luma_sse_sum[BW * BH] = { 0 }; + double d_factor[NUM_16X16] = { 0 }; + uint16_t *frame_sse = + (uint16_t *)aom_memalign(32, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + uint32_t *luma_sse_sum = + (uint32_t *)aom_memalign(32, sizeof(luma_sse_sum[0]) * BW * BH); + memset(frame_sse, 0, sizeof(frame_sse[0]) * SSE_STRIDE * BH); + memset(luma_sse_sum, 0, sizeof(luma_sse_sum[0]) * BW * BH); - for (int subblock_idx = 0; subblock_idx < 4; subblock_idx++) { + double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; + distance_threshold = AOMMAX(distance_threshold, 1); + for (int subblock_idx = 0; subblock_idx < NUM_16X16; subblock_idx++) { // Larger motion vector -> smaller filtering weight. const MV mv = subblock_mvs[subblock_idx]; const double distance = sqrt(pow(mv.row, 2) + pow(mv.col, 2)); - double distance_threshold = min_frame_size * TF_SEARCH_DISTANCE_THRESHOLD; - distance_threshold = AOMMAX(distance_threshold, 1); d_factor[subblock_idx] = distance / distance_threshold; d_factor[subblock_idx] = AOMMAX(d_factor[subblock_idx], 1); } @@ -317,4 +335,6 @@ weight_factor, d_factor, tf_wgt_calc_lvl); plane_offset += plane_h * plane_w; } + aom_free(frame_sse); + aom_free(luma_sse_sum); }
diff --git a/av1/ratectrl_rtc.cc b/av1/ratectrl_rtc.cc index 7ee58e9..9d176e5 100644 --- a/av1/ratectrl_rtc.cc +++ b/av1/ratectrl_rtc.cc
@@ -16,6 +16,7 @@ #include "aom/aom_encoder.h" #include "aom/aomcx.h" +#include "aom/internal/aom_codec_internal.h" #include "aom_dsp/aom_dsp_common.h" #include "aom_mem/aom_mem.h" #include "av1/common/common.h" @@ -72,6 +73,29 @@ namespace aom { +// Creates a setjmp target using `CPI->common.error->jmp` and sets +// `CPI->common.error->setjmp = 1`. Returns false on longjmp. This should be +// accompanied by a call to DISABLE_SETJMP using the same CPI before going out +// of scope. +#define ENABLE_SETJMP(CPI) \ + do { \ + struct aom_internal_error_info *const enable_setjmp_error = \ + (CPI)->common.error; \ + if (setjmp(enable_setjmp_error->jmp)) { \ + enable_setjmp_error->setjmp = 0; \ + return false; \ + } \ + enable_setjmp_error->setjmp = 1; \ + } while (0) + +// Sets CPI->common.error->setjmp = 0. +#define DISABLE_SETJMP(CPI) \ + do { \ + struct aom_internal_error_info *const enable_setjmp_error = \ + (CPI)->common.error; \ + enable_setjmp_error->setjmp = 0; \ + } while (0) + std::unique_ptr<AV1RateControlRTC> AV1RateControlRTC::Create( const AV1RateControlRtcConfig &cfg) { std::unique_ptr<AV1RateControlRTC> rc_api(new (std::nothrow) @@ -84,6 +108,7 @@ static_cast<AV1_PRIMARY *>(aom_memalign(32, sizeof(AV1_PRIMARY))); if (!rc_api->cpi_->ppi) return nullptr; av1_zero(*rc_api->cpi_->ppi); + rc_api->cpi_->common.error = &rc_api->cpi_->ppi->error; rc_api->cpi_->common.seq_params = &rc_api->cpi_->ppi->seq_params; av1_zero(*rc_api->cpi_->common.seq_params); if (!rc_api->InitRateControl(cfg)) return nullptr; @@ -224,7 +249,7 @@ av1_new_framerate(cpi_, cpi_->framerate); if (cpi_->svc.number_temporal_layers > 1 || cpi_->svc.number_spatial_layers > 1) { - int64_t target_bandwidth_svc = 0; + volatile int64_t target_bandwidth_svc = 0; for (int sl = 0; sl < cpi_->svc.number_spatial_layers; ++sl) { for (int tl = 0; tl < cpi_->svc.number_temporal_layers; ++tl) { const int layer = @@ -246,10 +271,14 @@ } } - if (cm->current_frame.frame_number == 0) av1_init_layer_context(cpi_); + ENABLE_SETJMP(cpi_); + if (cm->current_frame.frame_number == 0) { + av1_init_layer_context(cpi_); + } // This is needed to initialize external RC flag in layer context structure. cpi_->rc.rtc_external_ratectrl = 1; av1_update_layer_context_change_config(cpi_, target_bandwidth_svc); + DISABLE_SETJMP(cpi_); } check_reset_rc_flag(cpi_); return true; @@ -362,7 +391,8 @@ } AV1CdefInfo AV1RateControlRTC::GetCdefInfo() const { - av1_pick_cdef_from_qp(&cpi_->common, 0, 0); + av1_pick_cdef_from_qp(&cpi_->common, /*skip_cdef=*/0, /*is_screen_content=*/0, + /*avoid_uv_cdef=*/false); AV1CdefInfo cdef_level; cdef_level.cdef_strength_y = cpi_->common.cdef_info.cdef_strengths[0]; cdef_level.cdef_strength_uv = cpi_->common.cdef_info.cdef_uv_strengths[0];
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake index a8efb4a..05113fb 100644 --- a/build/cmake/aom_config_defaults.cmake +++ b/build/cmake/aom_config_defaults.cmake
@@ -60,6 +60,8 @@ set_aom_detect_var(HAVE_FEXCEPT 0 "Internal flag, GNU fenv.h present for target.") set_aom_detect_var(HAVE_PTHREAD_H 0 "Internal flag, target pthread support.") +set_aom_detect_var(HAVE_PTHREAD_SETNAME_NP 0 + "Internal flag, target has pthread_setname_np API support.") set_aom_detect_var(HAVE_UNISTD_H 0 "Internal flag, unistd.h present for target.") set_aom_detect_var(HAVE_WXWIDGETS 0 "WxWidgets present.") @@ -80,9 +82,6 @@ set_aom_config_var(CONFIG_GCOV 0 "Enable gcov support.") set_aom_config_var(CONFIG_GPROF 0 "Enable gprof support.") set_aom_config_var(CONFIG_LIBYUV 1 "Enables libyuv scaling/conversion support.") -# Set CONFIG_SVT_AV1 to 0 to avoid the BSD 3-Clause Clear License used by the -# code in third_party/SVT-AV1/. -set_aom_config_var(CONFIG_SVT_AV1 1 "Enables SVT-AV1 AVX2 convolution support.") set_aom_config_var(CONFIG_AV1_HIGHBITDEPTH 1 "Build with high bitdepth support.")
diff --git a/build/cmake/aom_configure.cmake b/build/cmake/aom_configure.cmake index 07e354a..9b8a0dc 100644 --- a/build/cmake/aom_configure.cmake +++ b/build/cmake/aom_configure.cmake
@@ -14,6 +14,7 @@ set(AOM_BUILD_CMAKE_AOM_CONFIGURE_CMAKE_ 1) include(FindThreads) +include(CheckSymbolExists) include("${AOM_ROOT}/build/cmake/aom_config_defaults.cmake") include("${AOM_ROOT}/build/cmake/aom_experiment_deps.cmake") @@ -258,6 +259,15 @@ set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) aom_check_source_compiles("unistd_check" "#include <unistd.h>" HAVE_UNISTD_H) +if(HAVE_PTHREAD_H) + # Check for pthread setname_np API + aom_push_var(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") + check_symbol_exists(pthread_setname_np pthread.h _HAS_PTHREAD_SETNAME_NP) + aom_pop_var(CMAKE_REQUIRED_DEFINITIONS) + + set(HAVE_PTHREAD_SETNAME_NP ${_HAS_PTHREAD_SETNAME_NP}) +endif() + if(NOT WIN32) aom_push_var(CMAKE_REQUIRED_LIBRARIES "m") aom_check_c_compiles("fenv_check" "#define _GNU_SOURCE @@ -301,7 +311,8 @@ # Test compiler flags. if(MSVC) - # It isn't possible to specify C99 conformance for MSVC. + # C11 conformance is available starting in Visual Studio 2019 version 16.8. + require_c_flag("/std:c11" YES) add_cxx_flag_if_supported("/std:c++17") add_compiler_flag_if_supported("/W3") @@ -314,7 +325,7 @@ # Compile source files in parallel add_compiler_flag_if_supported("/MP") else() - require_c_flag("-std=c99" YES) + require_c_flag("-std=c11" YES) if(CYGWIN AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # The GNU C++ compiler in Cygwin needs the -std=gnu++* flag to make the # POSIX function declarations visible in the Standard C Library headers. @@ -358,6 +369,7 @@ add_compiler_flag_if_supported("-Wunreachable-code-aggressive") add_compiler_flag_if_supported("-Wunused") add_compiler_flag_if_supported("-Wvla") + add_c_flag_if_supported("-Wc23-extensions") add_cxx_flag_if_supported("-Wc++20-extensions") add_cxx_flag_if_supported("-Wc++23-extensions") @@ -432,6 +444,20 @@ unset(AOM_LIB_LINK_TYPE) endif() +# Sanitize boolean variables to ensure they are 0 or 1. +foreach(aom_config_var ${AOM_CONFIG_VARS}) + if( + NOT aom_config_var MATCHES + "AOM_RTCD_FLAGS|CONFIG_MAX_DECODE_PROFILE|DECODE_HEIGHT_LIMIT|DECODE_WIDTH_LIMIT" + ) + if(${aom_config_var}) + set(${aom_config_var} 1) + else() + set(${aom_config_var} 0) + endif() + endif() +endforeach() + # Generate aom_config templates. set(aom_config_asm_template "${AOM_CONFIG_DIR}/config/aom_config.asm.cmake") set(aom_config_h_template "${AOM_CONFIG_DIR}/config/aom_config.h.cmake")
diff --git a/build/cmake/aom_install.cmake b/build/cmake/aom_install.cmake index 4291782..1ac795e 100644 --- a/build/cmake/aom_install.cmake +++ b/build/cmake/aom_install.cmake
@@ -20,7 +20,9 @@ if(CONFIG_AV1_ENCODER) list(APPEND AOM_INSTALL_INCS "${AOM_ROOT}/aom/aomcx.h" "${AOM_ROOT}/aom/aom_encoder.h" - "${AOM_ROOT}/aom/aom_external_partition.h") + "${AOM_ROOT}/aom/aom_ext_ratectrl.h" + "${AOM_ROOT}/aom/aom_external_partition.h" + "${AOM_ROOT}/aom/aom_tpl.h") endif() # Generate aom.pc and setup dependencies to ensure it is created when necessary.
diff --git a/build/cmake/rtcd.pl b/build/cmake/rtcd.pl index ba19761..10f6597 100755 --- a/build/cmake/rtcd.pl +++ b/build/cmake/rtcd.pl
@@ -221,11 +221,9 @@ # sub common_top() { my $include_guard = uc($opts{sym})."_H_"; - my @time = localtime; - my $year = $time[5] + 1900; print <<EOF; /* - * Copyright (c) ${year}, Alliance for Open Media. All rights reserved. + * Copyright (c) 2017, 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
diff --git a/docs.cmake b/docs.cmake index 154cb0d..1b76161 100644 --- a/docs.cmake +++ b/docs.cmake
@@ -199,6 +199,16 @@ "Lightfield bitstream parsing example.") endif() +if(CONFIG_AV1_DECODER AND CONFIG_AV1_ENCODER) + set(AOM_DOXYGEN_EXAMPLE_SOURCES + ${AOM_DOXYGEN_EXAMPLE_SOURCES} + "${AOM_ROOT}/examples/low_complexity_mode_encoder.c") + + set(AOM_DOXYGEN_EXAMPLE_DESCRIPTIONS + ${AOM_DOXYGEN_EXAMPLE_DESCRIPTIONS} + "Low complexity decode mode encoder example.") +endif() + # Iterates over list named by $list_name and appends each item to $AOM_DOXYFILE # as values assigned to $var_name with no line breaks between list items. # Appends a new line after the entire config variable is expanded.
diff --git a/examples/inspect.c b/examples/inspect.c index 9042eb8..43fb049 100644 --- a/examples/inspect.c +++ b/examples/inspect.c
@@ -65,7 +65,8 @@ INTRABC_LAYER = 1 << 17, PALETTE_LAYER = 1 << 18, UV_PALETTE_LAYER = 1 << 19, - ALL_LAYERS = (1 << 20) - 1 + BITS_LAYER = 1 << 20, + ALL_LAYERS = (1 << 21) - 1 } LayerType; static LayerType layers = 0; @@ -115,6 +116,8 @@ ARG_DEF("plt", "palette", 0, "Dump Palette Size"); static const arg_def_t dump_uv_palette_arg = ARG_DEF("uvp", "uv_palette", 0, "Dump UV Palette Size"); +static const arg_def_t dump_bits_arg = + ARG_DEF("sb", "sb_bits", 0, "Dump Bits of each superblock"); static const arg_def_t usage_arg = ARG_DEF("h", "help", 0, "Help"); static const arg_def_t skip_non_transform_arg = ARG_DEF( "snt", "skip_non_transform", 1, "Skip is counted as a non transform."); @@ -149,6 +152,7 @@ &dump_intrabc_arg, &dump_palette_arg, &dump_uv_palette_arg, + &dump_bits_arg, &usage_arg, &skip_non_transform_arg, &combined_arg, @@ -275,6 +279,7 @@ { "compound_type", offsetof(insp_mi_data, compound_type) }, { "referenceFrame", offsetof(insp_mi_data, ref_frame) }, { "skip", offsetof(insp_mi_data, skip) }, + { "sb_bits", offsetof(insp_mi_data, sb_bits) }, }; int parm_count = sizeof(parm_offsets) / sizeof(parm_offsets[0]); @@ -704,6 +709,10 @@ buf += put_block_info(buf, palette_map, "uv_palette", offsetof(insp_mi_data, uv_palette), 0); } + if (layers & BITS_LAYER) { + buf += put_block_info(buf, NULL, "sb_bits", offsetof(insp_mi_data, sb_bits), + 0); + } if (combined_parm_count > 0) buf += put_combined(buf); if (layers & REFERENCE_FRAME_LAYER) { buf += put_block_info(buf, refs_map, "referenceFrame", @@ -928,6 +937,8 @@ layers |= PALETTE_LAYER; else if (arg_match(&arg, &dump_uv_palette_arg, argi)) layers |= UV_PALETTE_LAYER; + else if (arg_match(&arg, &dump_bits_arg, argi)) + layers |= BITS_LAYER; else if (arg_match(&arg, &dump_all_arg, argi)) layers |= ALL_LAYERS; else if (arg_match(&arg, &compress_arg, argi))
diff --git a/examples/lightfield_encoder.c b/examples/lightfield_encoder.c index 63d72d4..dc8e890 100644 --- a/examples/lightfield_encoder.c +++ b/examples/lightfield_encoder.c
@@ -434,7 +434,6 @@ // lf_blocksize X lf_blocksize images will all use the reference image // in the middle of the block of images. int lf_blocksize; - aom_codec_ctx_t codec; aom_codec_enc_cfg_t cfg; aom_image_t raw; aom_image_t raw_shift; @@ -485,7 +484,8 @@ // Configuration res = aom_codec_enc_config_default(encoder, &cfg, 0); - if (res) die_codec(&codec, "Failed to get default codec config."); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); cfg.g_w = w; cfg.g_h = h;
diff --git a/examples/lossless_encoder.c b/examples/lossless_encoder.c index 194c717..c3d3b93 100644 --- a/examples/lossless_encoder.c +++ b/examples/lossless_encoder.c
@@ -96,7 +96,8 @@ aom_codec_ctx_t codec; res = aom_codec_enc_config_default(encoder, &cfg, 0); - if (res) die_codec(&codec, "Failed to get default codec config."); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); cfg.g_w = info.frame_width; cfg.g_h = info.frame_height;
diff --git a/examples/low_complexity_mode_encoder.c b/examples/low_complexity_mode_encoder.c new file mode 100644 index 0000000..31d8f06 --- /dev/null +++ b/examples/low_complexity_mode_encoder.c
@@ -0,0 +1,177 @@ +/* + * Copyright (c) 2026, 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. + */ + +// Low Complexity (LC) Mode Encoder +// ================================ +// +// This is an example of a low complexity mode encoder. It takes an input +// file in Y4M format, passes it through the encoder with LC mode on, and +// writes the compressed frames to disk in IVF format. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "aom/aom_encoder.h" +#include "aom/aomcx.h" +#include "common/tools_common.h" +#include "common/video_writer.h" + +static const char *exec_name; + +void usage_exit(void) { + fprintf(stderr, + "Usage: %s <codec> <width> <height> <infile> <outfile> " + "<keyframe-interval> <frames to encode>\n", + exec_name); + exit(EXIT_FAILURE); +} + +static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img, + int frame_index, int flags, AvxVideoWriter *writer) { + int got_pkts = 0; + aom_codec_iter_t iter = NULL; + const aom_codec_cx_pkt_t *pkt = NULL; + const aom_codec_err_t res = + aom_codec_encode(codec, img, frame_index, 1, flags); + if (res != AOM_CODEC_OK) die_codec(codec, "Failed to encode frame"); + + while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) { + got_pkts = 1; + + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0; + if (!aom_video_writer_write_frame(writer, pkt->data.frame.buf, + pkt->data.frame.sz, + pkt->data.frame.pts)) { + die_codec(codec, "Failed to write compressed frame"); + } + printf(keyframe ? "K" : "."); + fflush(stdout); + } + } + + return got_pkts; +} + +int main(int argc, char **argv) { + FILE *infile = NULL; + aom_codec_ctx_t codec; + aom_codec_enc_cfg_t cfg; + int frame_count = 0; + aom_image_t raw; + aom_codec_err_t res; + AvxVideoInfo info; + AvxVideoWriter *writer = NULL; + const int fps = 30; + const int bitrate = 400; + int keyframe_interval = 0; + int max_frames = 0; + int frames_encoded = 0; + const char *codec_arg = NULL; + const char *width_arg = NULL; + const char *height_arg = NULL; + const char *infile_arg = NULL; + const char *outfile_arg = NULL; + const char *keyframe_interval_arg = NULL; + const int usage = 0; // AOM_USAGE_GOOD_QUALITY + const int speed = 2; + + exec_name = argv[0]; + + // Clear explicitly, as simply assigning "{ 0 }" generates + // "missing-field-initializers" warning in some compilers. + memset(&info, 0, sizeof(info)); + + if (argc != 8) die("Invalid number of arguments"); + + codec_arg = argv[1]; + width_arg = argv[2]; + height_arg = argv[3]; + infile_arg = argv[4]; + outfile_arg = argv[5]; + keyframe_interval_arg = argv[6]; + max_frames = (int)strtol(argv[7], NULL, 0); + + aom_codec_iface_t *encoder = get_aom_encoder_by_short_name(codec_arg); + if (!encoder) die("Unsupported codec."); + + info.codec_fourcc = get_fourcc_by_aom_encoder(encoder); + info.frame_width = (int)strtol(width_arg, NULL, 0); + info.frame_height = (int)strtol(height_arg, NULL, 0); + info.time_base.numerator = 1; + info.time_base.denominator = fps; + + if (info.frame_width <= 0 || info.frame_height <= 0 || + (info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) { + die("Invalid frame size: %dx%d", info.frame_width, info.frame_height); + } + + if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, info.frame_width, + info.frame_height, 1)) { + die("Failed to allocate image."); + } + + keyframe_interval = (int)strtol(keyframe_interval_arg, NULL, 0); + if (keyframe_interval < 0) die("Invalid keyframe interval value."); + + printf("Using %s\n", aom_codec_iface_name(encoder)); + + res = aom_codec_enc_config_default(encoder, &cfg, usage); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); + + cfg.g_w = info.frame_width; + cfg.g_h = info.frame_height; + cfg.g_timebase.num = info.time_base.numerator; + cfg.g_timebase.den = info.time_base.denominator; + cfg.rc_target_bitrate = bitrate; + cfg.g_lag_in_frames = 35; + + writer = aom_video_writer_open(outfile_arg, kContainerIVF, &info); + if (!writer) die("Failed to open %s for writing.", outfile_arg); + + if (!(infile = fopen(infile_arg, "rb"))) + die("Failed to open %s for reading.", infile_arg); + + if (aom_codec_enc_init(&codec, encoder, &cfg, 0)) + die("Failed to initialize encoder"); + + if (aom_codec_control(&codec, AOME_SET_CPUUSED, speed)) + die_codec(&codec, "Failed to set cpu-used"); + + if (aom_codec_control(&codec, AV1E_SET_ENABLE_LOW_COMPLEXITY_DECODE, 1)) + die_codec(&codec, "Failed to set low-complexity mode"); + + // Encode frames. + while (aom_img_read(&raw, infile)) { + int flags = 0; + if (keyframe_interval > 0 && frame_count % keyframe_interval == 0) + flags |= AOM_EFLAG_FORCE_KF; + encode_frame(&codec, &raw, frame_count++, flags, writer); + frames_encoded++; + if (max_frames > 0 && frames_encoded >= max_frames) break; + } + + // Flush encoder. + while (encode_frame(&codec, NULL, -1, 0, writer)) continue; + + printf("\n"); + fclose(infile); + printf("Processed %d frames.\n", frame_count); + + aom_img_free(&raw); + if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec."); + + aom_video_writer_close(writer); + + return EXIT_SUCCESS; +}
diff --git a/examples/multilayer_metadata.cc b/examples/multilayer_metadata.cc index 9427029..7c7149a 100644 --- a/examples/multilayer_metadata.cc +++ b/examples/multilayer_metadata.cc
@@ -12,12 +12,12 @@ #include "examples/multilayer_metadata.h" #include <assert.h> -#include <inttypes.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <cinttypes> #include <cmath> #include <fstream> #include <iostream> @@ -430,6 +430,49 @@ return true; } +bool parse_multilayer_layer_local_metadata( + std::ifstream &file, int min_indent, int *line_idx, + std::vector<FrameLocalMetadata> &frames) { + bool has_list_prefix; + int indent = -1; + std::string field_name; + ParsedValue value; + bool syntax_error; + while (parse_line(file, min_indent, /*is_list=*/true, &indent, + &has_list_prefix, line_idx, &field_name, &value, + &syntax_error)) { + if (has_list_prefix) { + frames.push_back({}); + } + if (frames.empty()) { + fprintf(stderr, "Error: Missing list prefix '-' at line %d\n", *line_idx); + return false; + } + + if (field_name == "frame_idx") { + RETURN_IF_FALSE( + value.IntegerValueInRange(0, std::numeric_limits<long>::max(), + *line_idx, &frames.back().frame_idx)); + } else if (field_name == "alpha") { + RETURN_IF_FALSE(parse_multilayer_layer_alpha( + file, + /*min_indent=*/indent + 1, line_idx, &frames.back().alpha)); + + } else if (field_name == "depth") { + RETURN_IF_FALSE(parse_multilayer_layer_depth( + file, + /*min_indent=*/indent + 1, line_idx, &frames.back().depth)); + + } else { + fprintf(stderr, "Error: Unknown field %s at line %d\n", + field_name.c_str(), *line_idx); + return false; + } + } + if (syntax_error) return false; + return true; +} + bool validate_layer(const LayerMetadata &layer, bool layer_has_alpha, bool layer_has_depth) { if (layer_has_alpha != (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA && @@ -475,7 +518,8 @@ // Validate the previous layer. if (!layers.empty()) { - validate_layer(layers.back(), layer_has_alpha, layer_has_depth); + RETURN_IF_FALSE( + validate_layer(layers.back(), layer_has_alpha, layer_has_depth)); } if (layers.size() == 1 && layers.back().layer_color_description.second) { fprintf(stderr, @@ -520,24 +564,25 @@ layer->layer_color_description = value_present(color_properties); } else if ((field_name == "alpha")) { layer_has_alpha = true; - RETURN_IF_FALSE(parse_multilayer_layer_alpha( - file, - /*min_indent=*/indent + 1, line_idx, &layer->global_alpha_info)); + RETURN_IF_FALSE(parse_multilayer_layer_alpha(file, + /*min_indent=*/indent + 1, + line_idx, &layer->alpha)); } else if (field_name == "depth") { layer_has_depth = true; - RETURN_IF_FALSE(parse_multilayer_layer_depth( - file, - /*min_indent=*/indent + 1, line_idx, &layer->global_depth_info)); - if ((layer->global_depth_info.d_min.second || - layer->global_depth_info.d_max.second) && - layer->global_depth_info.disparity_ref_view_id == - (layers.size() - 1)) { + RETURN_IF_FALSE(parse_multilayer_layer_depth(file, + /*min_indent=*/indent + 1, + line_idx, &layer->depth)); + if ((layer->depth.d_min.second || layer->depth.d_max.second) && + layer->depth.disparity_ref_view_id == (layers.size() - 1)) { fprintf(stderr, "disparity_ref_view_id must be different from the layer's id " "for layer %d (zero-based index)\n", static_cast<int>(layers.size()) - 1); return false; } + } else if (field_name == "local_metadata") { + RETURN_IF_FALSE(parse_multilayer_layer_local_metadata( + file, /*min_indent=*/indent + 1, line_idx, layer->local_metadata)); } else { fprintf(stderr, "Error: Unknown field %s at line %d\n", field_name.c_str(), *line_idx); @@ -545,7 +590,8 @@ } } if (syntax_error) return false; - validate_layer(layers.back(), layer_has_alpha, layer_has_depth); + RETURN_IF_FALSE( + validate_layer(layers.back(), layer_has_alpha, layer_has_depth)); return true; } @@ -786,6 +832,33 @@ return true; } +void print_alpha_information(const AlphaInformation &alpha) { + printf(" alpha_simple_flag: %d\n", alpha.alpha_simple_flag); + if (!alpha.alpha_simple_flag) { + printf(" alpha_bit_depth: %d\n", alpha.alpha_bit_depth); + printf(" alpha_clip_idc: %d\n", alpha.alpha_clip_idc); + printf(" alpha_incr_flag: %d\n", alpha.alpha_incr_flag); + printf(" alpha_transparent_value: %hu\n", alpha.alpha_transparent_value); + printf(" alpha_opaque_value: %hu\n", alpha.alpha_opaque_value); + printf(" alpha_color_description: %s\n", + format_color_properties(alpha.alpha_color_description).c_str()); + } +} + +void print_depth_information(const DepthInformation &depth) { + printf(" z_near: %s\n", + format_depth_representation_element(depth.z_near).c_str()); + printf(" z_far: %s\n", + format_depth_representation_element(depth.z_far).c_str()); + printf(" d_min: %s\n", + format_depth_representation_element(depth.d_min).c_str()); + printf(" d_max: %s\n", + format_depth_representation_element(depth.d_max).c_str()); + printf(" depth_representation_type: %d\n", + depth.depth_representation_type); + printf(" disparity_ref_view_id: %d\n", depth.disparity_ref_view_id); +} + } // namespace double depth_representation_element_to_double( @@ -881,45 +954,21 @@ printf(" layer_color_description: %s\n", format_color_properties(layer.layer_color_description).c_str()); if (layer.layer_type == MULTILAYER_LAYER_TYPE_ALPHA) { - printf(" alpha:\n"); - printf(" alpha_use_idc: %d\n", layer.global_alpha_info.alpha_use_idc); - printf(" alpha_simple_flag: %d\n", - layer.global_alpha_info.alpha_simple_flag); - if (!layer.global_alpha_info.alpha_simple_flag) { - printf(" alpha_bit_depth: %d\n", - layer.global_alpha_info.alpha_bit_depth); - printf(" alpha_clip_idc: %d\n", - layer.global_alpha_info.alpha_clip_idc); - printf(" alpha_incr_flag: %d\n", - layer.global_alpha_info.alpha_incr_flag); - printf(" alpha_transparent_value: %hu\n", - layer.global_alpha_info.alpha_transparent_value); - printf(" alpha_opaque_value: %hu\n", - layer.global_alpha_info.alpha_opaque_value); - printf(" alpha_color_description: %s\n", - format_color_properties( - layer.global_alpha_info.alpha_color_description) - .c_str()); + if (layer.layer_metadata_scope >= SCOPE_GLOBAL) { + printf(" global alpha:\n"); + print_alpha_information(layer.alpha); + } + for (const FrameLocalMetadata &local_metadata : layer.local_metadata) { + printf(" local alpha for frame %ld:\n", local_metadata.frame_idx); + print_alpha_information(local_metadata.alpha); } } else if (layer.layer_type == MULTILAYER_LAYER_TYPE_DEPTH) { - printf(" depth:\n"); - printf(" z_near: %s\n", - format_depth_representation_element(layer.global_depth_info.z_near) - .c_str()); - printf(" z_far: %s\n", - format_depth_representation_element(layer.global_depth_info.z_far) - .c_str()); - printf(" d_min: %s\n", - format_depth_representation_element(layer.global_depth_info.d_min) - .c_str()); - printf(" d_max: %s\n", - format_depth_representation_element(layer.global_depth_info.d_max) - .c_str()); - printf(" depth_representation_type: %d\n", - layer.global_depth_info.depth_representation_type); - printf(" disparity_ref_view_id: %d\n", - layer.global_depth_info.disparity_ref_view_id); - printf("\n"); + printf(" global depth:\n"); + print_depth_information(layer.depth); + for (const FrameLocalMetadata &local_metadata : layer.local_metadata) { + printf(" local depth for frame %ld:\n", local_metadata.frame_idx); + print_depth_information(local_metadata.depth); + } } } printf("\n");
diff --git a/examples/multilayer_metadata.h b/examples/multilayer_metadata.h index 392f1c5..1ac1c3c 100644 --- a/examples/multilayer_metadata.h +++ b/examples/multilayer_metadata.h
@@ -61,7 +61,7 @@ std::pair<DepthRepresentationElement, bool> z_far; std::pair<DepthRepresentationElement, bool> d_min; std::pair<DepthRepresentationElement, bool> d_max; - uint8_t depth_representation_type; // [0, 15] + uint8_t depth_representation_type; // [0, 2]. Values 3 to 15 are reserved. // Only relevant if d_min or d_max are present. uint8_t disparity_ref_view_id; // [0, 3] }; @@ -110,6 +110,14 @@ // 4 to 7 are reserved. }; +struct FrameLocalMetadata { + long frame_idx; + // Relevant for MULTILAYER_LAYER_TYPE_ALPHA with scope != SCOPE_GLOBAL. + AlphaInformation alpha; + // Relevant for MULTILAYER_LAYER_TYPE_DEPTH with scope != SCOPE_GLOBAL. + DepthInformation depth; +}; + struct LayerMetadata { LayerType layer_type; // [0, 31] bool luma_plane_only_flag; @@ -121,9 +129,12 @@ std::pair<ColorProperties, bool> layer_color_description; // Relevant for MULTILAYER_LAYER_TYPE_ALPHA with scope >= SCOPE_GLOBAL. - AlphaInformation global_alpha_info; + AlphaInformation alpha; // Relevant for MULTILAYER_LAYER_TYPE_DEPTH with scope >= SCOPE_GLOBAL. - DepthInformation global_depth_info; + DepthInformation depth; + + // Relevant when scope != SCOPE_GLOBAL. + std::vector<FrameLocalMetadata> local_metadata; }; struct MultilayerMetadata {
diff --git a/examples/noise_model.c b/examples/noise_model.c index 1816e33..e23185e 100644 --- a/examples/noise_model.c +++ b/examples/noise_model.c
@@ -38,6 +38,7 @@ * --auto-alt-ref=2 --bit-depth=8 --film-grain-table=film_grain.tbl \ * -o denoised_with_grain_params.ivf denoised.854_480.yuv */ +#include <inttypes.h> #include <math.h> #include <stdio.h> #include <stdlib.h>
diff --git a/examples/scalable_encoder.c b/examples/scalable_encoder.c index 91d35a2..963b4c3 100644 --- a/examples/scalable_encoder.c +++ b/examples/scalable_encoder.c
@@ -186,7 +186,8 @@ aom_codec_ctx_t codec; res = aom_codec_enc_config_default(encoder, &cfg, 0); - if (res) die_codec(&codec, "Failed to get default codec config."); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); cfg.g_w = info.frame_width; cfg.g_h = info.frame_height;
diff --git a/examples/set_maps.c b/examples/set_maps.c index 4f41b47..0d03983 100644 --- a/examples/set_maps.c +++ b/examples/set_maps.c
@@ -167,7 +167,8 @@ printf("Using %s\n", aom_codec_iface_name(encoder)); res = aom_codec_enc_config_default(encoder, &cfg, usage); - if (res) die_codec(&codec, "Failed to get default codec config."); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); cfg.g_w = info.frame_width; cfg.g_h = info.frame_height;
diff --git a/examples/simple_encoder.c b/examples/simple_encoder.c index b08f97b..f5ddb3b 100644 --- a/examples/simple_encoder.c +++ b/examples/simple_encoder.c
@@ -212,7 +212,8 @@ printf("Using %s\n", aom_codec_iface_name(encoder)); res = aom_codec_enc_config_default(encoder, &cfg, usage); - if (res) die_codec(&codec, "Failed to get default codec config."); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); cfg.g_w = info.frame_width; cfg.g_h = info.frame_height;
diff --git a/examples/svc_encoder_rtc.cc b/examples/svc_encoder_rtc.cc index 10c2102..c7956bc 100644 --- a/examples/svc_encoder_rtc.cc +++ b/examples/svc_encoder_rtc.cc
@@ -13,13 +13,13 @@ // encoding scheme for RTC video applications. #include <assert.h> -#include <inttypes.h> #include <limits.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <cinttypes> #include <memory> #include "config/aom_config.h" @@ -1463,8 +1463,55 @@ } } +static void write_alpha_information( + struct aom_write_bit_buffer *buffer, + const libaom_examples::AlphaInformation &alpha_info) { + write_literal(buffer, alpha_info.alpha_use_idc, 2); + write_literal(buffer, alpha_info.alpha_simple_flag, 1); + if (!alpha_info.alpha_simple_flag) { + write_literal(buffer, alpha_info.alpha_bit_depth, 3, /*offset=*/8); + write_literal(buffer, alpha_info.alpha_clip_idc, 2); + write_literal(buffer, alpha_info.alpha_incr_flag, 1); + write_literal(buffer, alpha_info.alpha_transparent_value, + alpha_info.alpha_bit_depth + 1); + write_literal(buffer, alpha_info.alpha_opaque_value, + alpha_info.alpha_bit_depth + 1); + if (buffer->bit_offset % 8 != 0) { + // ai_byte_alignment_bits + write_literal(buffer, 0, 8 - (buffer->bit_offset % 8)); + } + assert(buffer->bit_offset % 8 == 0); + + write_literal(buffer, 0, 6); // ai_reserved_6bits + write_color_properties(buffer, alpha_info.alpha_color_description); + } else { + write_literal(buffer, 0, 5); // ai_reserved_5bits + } +} + +static void write_depth_information( + struct aom_write_bit_buffer *buffer, + const libaom_examples::DepthInformation &depth_info) { + write_literal(buffer, depth_info.z_near.second, 1); + write_literal(buffer, depth_info.z_far.second, 1); + write_literal(buffer, depth_info.d_min.second, 1); + write_literal(buffer, depth_info.d_max.second, 1); + write_literal(buffer, depth_info.depth_representation_type, 4); + if (depth_info.d_min.second || depth_info.d_max.second) { + write_literal(buffer, depth_info.disparity_ref_view_id, 2); + } + write_depth_representation_element(buffer, depth_info.z_near); + write_depth_representation_element(buffer, depth_info.z_far); + write_depth_representation_element(buffer, depth_info.d_min); + write_depth_representation_element(buffer, depth_info.d_max); + if (buffer->bit_offset % 8 != 0) { + write_literal(buffer, 0, 8 - (buffer->bit_offset % 8)); + } +} + static void add_multilayer_metadata( - aom_image_t *frame, const libaom_examples::MultilayerMetadata &multilayer) { + aom_image_t *frame, const libaom_examples::MultilayerMetadata &multilayer, + int frame_idx, int spatial_id) { // Large enough buffer for the multilayer metadata. // Each layer's metadata is less than 100 bytes and there are at most 4 // layers. @@ -1506,51 +1553,12 @@ if (layer.layer_type == libaom_examples::MULTILAYER_LAYER_TYPE_ALPHA && layer.layer_metadata_scope >= libaom_examples::SCOPE_GLOBAL) { - const libaom_examples::AlphaInformation &alpha_info = - layer.global_alpha_info; - write_literal(&buffer, alpha_info.alpha_use_idc, 2); - write_literal(&buffer, alpha_info.alpha_simple_flag, 1); - if (!alpha_info.alpha_simple_flag) { - write_literal(&buffer, alpha_info.alpha_bit_depth, 3, /*offset=*/8); - write_literal(&buffer, alpha_info.alpha_clip_idc, 2); - write_literal(&buffer, alpha_info.alpha_incr_flag, 1); - write_literal(&buffer, alpha_info.alpha_transparent_value, - alpha_info.alpha_bit_depth + 1); - write_literal(&buffer, alpha_info.alpha_opaque_value, - alpha_info.alpha_bit_depth + 1); - if (buffer.bit_offset % 8 != 0) { - // ai_byte_alignment_bits - write_literal(&buffer, 0, 8 - (buffer.bit_offset % 8)); - } - assert(buffer.bit_offset % 8 == 0); - - write_literal(&buffer, 0, 6); // ai_reserved_6bits - write_color_properties(&buffer, alpha_info.alpha_color_description); - } else { - write_literal(&buffer, 0, 5); // ai_reserved_5bits - } - + write_alpha_information(&buffer, layer.alpha); assert(buffer.bit_offset % 8 == 0); } else if (layer.layer_type == libaom_examples::MULTILAYER_LAYER_TYPE_DEPTH && layer.layer_metadata_scope >= libaom_examples::SCOPE_GLOBAL) { - const libaom_examples::DepthInformation &depth_info = - layer.global_depth_info; - write_literal(&buffer, depth_info.z_near.second, 1); - write_literal(&buffer, depth_info.z_far.second, 1); - write_literal(&buffer, depth_info.d_min.second, 1); - write_literal(&buffer, depth_info.d_max.second, 1); - write_literal(&buffer, depth_info.depth_representation_type, 4); - if (depth_info.d_min.second || depth_info.d_max.second) { - write_literal(&buffer, depth_info.disparity_ref_view_id, 2); - } - write_depth_representation_element(&buffer, depth_info.z_near); - write_depth_representation_element(&buffer, depth_info.z_far); - write_depth_representation_element(&buffer, depth_info.d_min); - write_depth_representation_element(&buffer, depth_info.d_max); - if (buffer.bit_offset % 8 != 0) { - write_literal(&buffer, 0, 8 - (buffer.bit_offset % 8)); - } + write_depth_information(&buffer, layer.depth); assert(buffer.bit_offset % 8 == 0); } @@ -1572,6 +1580,36 @@ AOM_MIF_KEY_FRAME)) { die("Error: Failed to add metadata\n"); } + + if ((int)multilayer.layers.size() > spatial_id) { + const libaom_examples::LayerMetadata &layer = multilayer.layers[spatial_id]; + for (const libaom_examples::FrameLocalMetadata &local_metadata : + layer.local_metadata) { + if (local_metadata.frame_idx == frame_idx) { + if (layer.layer_type == libaom_examples::MULTILAYER_LAYER_TYPE_ALPHA) { + buffer = { data.data(), 0 }; + write_alpha_information(&buffer, local_metadata.alpha); + if (aom_img_add_metadata(frame, + 34 /*METADATA_TYPE_ALPHA_INFORMATION*/, + buffer.bit_buffer, buffer.bit_offset / 8, + AOM_MIF_ANY_FRAME_LAYER_SPECIFIC)) { + die("Error: Failed to add metadata\n"); + } + } else if (layer.layer_type == + libaom_examples::MULTILAYER_LAYER_TYPE_DEPTH) { + buffer = { data.data(), 0 }; + write_depth_information(&buffer, local_metadata.depth); + if (aom_img_add_metadata(frame, + 35 /*METADATA_TYPE_DEPTH_INFORMATION*/, + buffer.bit_buffer, buffer.bit_offset / 8, + AOM_MIF_ANY_FRAME_LAYER_SPECIFIC)) { + die("Error: Failed to add metadata\n"); + } + } + break; + } + } + } } #if CONFIG_AV1_DECODER @@ -1637,7 +1675,7 @@ } #endif // CONFIG_AV1_DECODER -struct psnr_stats { +struct PsnrStats { // The second element of these arrays is reserved for high bitdepth. uint64_t psnr_sse_total[2]; uint64_t psnr_samples_total[2]; @@ -1645,19 +1683,22 @@ int psnr_count[2]; }; -static void show_psnr(struct psnr_stats *psnr_stream, double peak) { - double ovpsnr; +static void show_psnr(struct PsnrStats *psnr_stream, double peak, + int num_layers) { + for (int sl = 0; sl < num_layers; ++sl) { + if (!psnr_stream[sl].psnr_count[0]) continue; - if (!psnr_stream->psnr_count[0]) return; + fprintf(stderr, "\nPSNR (Layer %d, Overall/Avg/Y/U/V)", sl); + const double ovpsnr = + sse_to_psnr((double)psnr_stream[sl].psnr_samples_total[0], peak, + (double)psnr_stream[sl].psnr_sse_total[0]); + fprintf(stderr, " %.3f", ovpsnr); - fprintf(stderr, "\nPSNR (Overall/Avg/Y/U/V)"); - ovpsnr = sse_to_psnr((double)psnr_stream->psnr_samples_total[0], peak, - (double)psnr_stream->psnr_sse_total[0]); - fprintf(stderr, " %.3f", ovpsnr); - - for (int i = 0; i < 4; i++) { - fprintf(stderr, " %.3f", - psnr_stream->psnr_totals[0][i] / psnr_stream->psnr_count[0]); + for (int i = 0; i < 4; i++) { + fprintf( + stderr, " %.3f", + psnr_stream[sl].psnr_totals[0][i] / psnr_stream[sl].psnr_count[0]); + } } fprintf(stderr, "\n"); } @@ -2095,7 +2136,7 @@ } frame_avail = 1; - struct psnr_stats psnr_stream; + struct PsnrStats psnr_stream[MAX_NUM_SPATIAL_LAYERS]; memset(&psnr_stream, 0, sizeof(psnr_stream)); while (frame_avail || got_data) { struct aom_usec_timer timer; @@ -2133,7 +2174,7 @@ &ref_frame_comp_pred); } if (app_input.multilayer_metadata_file != NULL) { - add_multilayer_metadata(&raw, multilayer_metadata); + add_multilayer_metadata(&raw, multilayer_metadata, frame_cnt, slx); } // Set the speed per layer. if (test_speed_per_layer) { @@ -2376,12 +2417,17 @@ break; case AOM_CODEC_PSNR_PKT: if (app_input.show_psnr) { - psnr_stream.psnr_sse_total[0] += pkt->data.psnr.sse[0]; - psnr_stream.psnr_samples_total[0] += pkt->data.psnr.samples[0]; - for (int plane = 0; plane < 4; plane++) { - psnr_stream.psnr_totals[0][plane] += pkt->data.psnr.psnr[plane]; + const int sl = layer_id.spatial_layer_id; + const int show_psnr_hbd = + (cfg.g_input_bit_depth > 8 || cfg.g_bit_depth > AOM_BITS_8); + const int hbd = show_psnr_hbd; + psnr_stream[sl].psnr_sse_total[hbd] += pkt->data.psnr.sse[0]; + psnr_stream[sl].psnr_samples_total[hbd] += + pkt->data.psnr.samples[0]; + for (i = 0; i < 4; i++) { + psnr_stream[sl].psnr_totals[hbd][i] += pkt->data.psnr.psnr[i]; } - psnr_stream.psnr_count[0]++; + psnr_stream[sl].psnr_count[hbd]++; } break; default: break; @@ -2432,7 +2478,10 @@ 1000000 * (double)frame_cnt / (double)cx_time); if (app_input.show_psnr) { - show_psnr(&psnr_stream, 255.0); + const int show_psnr_hbd = + (cfg.g_input_bit_depth > 8 || cfg.g_bit_depth > AOM_BITS_8); + show_psnr(psnr_stream, (double)((1 << (show_psnr_hbd ? 12 : 8)) - 1), + ss_number_layers); } if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy encoder");
diff --git a/examples/twopass_encoder.c b/examples/twopass_encoder.c index 4d7c653..285bef2 100644 --- a/examples/twopass_encoder.c +++ b/examples/twopass_encoder.c
@@ -187,7 +187,6 @@ int main(int argc, char **argv) { FILE *infile = NULL; int w, h; - aom_codec_ctx_t codec; aom_codec_enc_cfg_t cfg; aom_image_t raw; aom_codec_err_t res; @@ -225,7 +224,8 @@ // Configuration res = aom_codec_enc_config_default(encoder, &cfg, 0); - if (res) die_codec(&codec, "Failed to get default codec config."); + if (res) + die("Failed to get default codec config: %s", aom_codec_err_to_string(res)); cfg.g_w = w; cfg.g_h = h;
diff --git a/test/allintra_end_to_end_test.cc b/test/allintra_end_to_end_test.cc index ba0f0a9..a33aaa2 100644 --- a/test/allintra_end_to_end_test.cc +++ b/test/allintra_end_to_end_test.cc
@@ -24,13 +24,13 @@ const unsigned int kFrames = 20; const int kBitrate = 500; -typedef struct { +struct TestVideoParam { const char *filename; unsigned int input_bit_depth; aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename
diff --git a/test/altref_test.cc b/test/altref_test.cc index 354a5a8..ee1a347 100644 --- a/test/altref_test.cc +++ b/test/altref_test.cc
@@ -15,14 +15,14 @@ #include "test/i420_video_source.h" #include "test/util.h" namespace { -typedef struct { +struct AltRefTestParams { const unsigned int min_kf_dist; const unsigned int max_kf_dist; const unsigned int min_gf_interval; const unsigned int max_gf_interval; const unsigned int lag_in_frames; libaom_test::TestMode encoding_mode; -} AltRefTestParams; +}; static const AltRefTestParams TestParams[] = { { 0, 10, 4, 8, 10, ::libaom_test::kOnePassGood }, @@ -113,11 +113,11 @@ ::testing::ValuesIn(TestParams), ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ)); -typedef struct { +struct gfIntervalParam { const ::libaom_test::TestMode encoding_mode; const unsigned int min_gf_interval; const unsigned int max_gf_interval; -} gfIntervalParam; +}; const gfIntervalParam gfTestParams[] = { // single pass
diff --git a/test/aom_image_test.cc b/test/aom_image_test.cc index 5976061..6c0059b 100644 --- a/test/aom_image_test.cc +++ b/test/aom_image_test.cc
@@ -9,6 +9,7 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <array> #include <climits> #include "aom/aom_image.h" @@ -49,14 +50,19 @@ 0); } -TEST(AomImageTest, AomImgAllocNone) { - const int kWidth = 128; - const int kHeight = 128; +TEST(AomImageTest, AomImgAllocInvalidImageFormats) { + static constexpr std::array<int, 7> kImageFormats = { + AOM_IMG_FMT_NONE, AOM_IMG_FMT_NONE - 1, AOM_IMG_FMT_NV12 + 1, + AOM_IMG_FMT_I42016 - 1, AOM_IMG_FMT_I44416 + 1, AOM_IMG_FMT_AOMYV12, + AOM_IMG_FMT_AOMI420 + }; - aom_image_t img; - aom_img_fmt_t format = AOM_IMG_FMT_NONE; - unsigned int align = 32; - ASSERT_EQ(aom_img_alloc(&img, format, kWidth, kHeight, align), nullptr); + for (const auto img_fmt : kImageFormats) { + EXPECT_EQ( + aom_img_alloc(/*img=*/nullptr, static_cast<aom_img_fmt_t>(img_fmt), + /*d_w=*/32, /*d_h=*/32, /*align=*/1), + nullptr); + } } TEST(AomImageTest, AomImgAllocNv12) {
diff --git a/test/arf_freq_test.cc b/test/arf_freq_test.cc index 1236ce2..d01eaed 100644 --- a/test/arf_freq_test.cc +++ b/test/arf_freq_test.cc
@@ -28,7 +28,7 @@ #define ARF_NOT_SEEN 1000001 #define ARF_SEEN_ONCE 1000000 -typedef struct { +struct TestVideoParam { const char *filename; unsigned int width; unsigned int height; @@ -38,12 +38,12 @@ aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; -typedef struct { +struct TestEncodeParam { libaom_test::TestMode mode; int cpu_used; -} TestEncodeParam; +}; const TestVideoParam kTestVectors[] = { // artificially increase framerate to trigger default check
diff --git a/test/av1_convolve_scale_test.cc b/test/av1_convolve_scale_test.cc index a8344fe..8be4b87 100644 --- a/test/av1_convolve_scale_test.cc +++ b/test/av1_convolve_scale_test.cc
@@ -175,7 +175,7 @@ } } -typedef tuple<int, int> BlockDimension; +using BlockDimension = tuple<int, int>; struct BaseParams { BaseParams(BlockDimension dimensions) : dims(dimensions) {} @@ -321,19 +321,19 @@ ConvolveParams convolve_params_; }; -typedef tuple<int, int> BlockDimension; +using BlockDimension = tuple<int, int>; -typedef void (*LowbdConvolveFunc)(const uint8_t *src, int src_stride, - uint8_t *dst, int dst_stride, int w, int h, - const InterpFilterParams *filter_params_x, - const InterpFilterParams *filter_params_y, - const int subpel_x_qn, const int x_step_qn, - const int subpel_y_qn, const int y_step_qn, - ConvolveParams *conv_params); +using LowbdConvolveFunc = void (*)(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, + const int subpel_x_qn, const int x_step_qn, + const int subpel_y_qn, const int y_step_qn, + ConvolveParams *conv_params); // Test parameter list: // <tst_fun, dims, avg> -typedef tuple<LowbdConvolveFunc, BlockDimension> LowBDParams; +using LowBDParams = tuple<LowbdConvolveFunc, BlockDimension>; class LowBDConvolveScaleTest : public ConvolveScaleTestBase<uint8_t>, @@ -417,17 +417,17 @@ #endif // HAVE_SSE4_1 #if CONFIG_AV1_HIGHBITDEPTH -typedef void (*HighbdConvolveFunc)(const uint16_t *src, int src_stride, - uint16_t *dst, int dst_stride, int w, int h, - const InterpFilterParams *filter_params_x, - const InterpFilterParams *filter_params_y, - const int subpel_x_qn, const int x_step_qn, - const int subpel_y_qn, const int y_step_qn, - ConvolveParams *conv_params, int bd); +using HighbdConvolveFunc = void (*)(const uint16_t *src, int src_stride, + uint16_t *dst, int dst_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, + const int subpel_x_qn, const int x_step_qn, + const int subpel_y_qn, const int y_step_qn, + ConvolveParams *conv_params, int bd); // Test parameter list: // <tst_fun, dims, avg, bd> -typedef tuple<HighbdConvolveFunc, BlockDimension, int> HighBDParams; +using HighBDParams = tuple<HighbdConvolveFunc, BlockDimension, int>; class HighBDConvolveScaleTest : public ConvolveScaleTestBase<uint16_t>,
diff --git a/test/av1_convolve_test.cc b/test/av1_convolve_test.cc index b14c6b7..9dee488 100644 --- a/test/av1_convolve_test.cc +++ b/test/av1_convolve_test.cc
@@ -318,11 +318,11 @@ //////////////////////////////////////////////////////// // Single reference convolve-x functions (low bit-depth) //////////////////////////////////////////////////////// -typedef void (*convolve_x_func)(const uint8_t *src, int src_stride, - uint8_t *dst, int dst_stride, int w, int h, - const InterpFilterParams *filter_params_x, - const int subpel_x_qn, - ConvolveParams *conv_params); +using convolve_x_func = void (*)(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const int subpel_x_qn, + ConvolveParams *conv_params); class AV1ConvolveXTest : public AV1ConvolveTest<convolve_x_func> { public: @@ -401,7 +401,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -509,7 +509,7 @@ aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -535,10 +535,10 @@ ///////////////////////////////////////////////////////// // Single reference convolve-x functions (high bit-depth) ///////////////////////////////////////////////////////// -typedef void (*highbd_convolve_x_func)( - const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w, - int h, const InterpFilterParams *filter_params_x, const int subpel_x_qn, - ConvolveParams *conv_params, int bd); +using highbd_convolve_x_func = + void (*)(const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, + int w, int h, const InterpFilterParams *filter_params_x, + const int subpel_x_qn, ConvolveParams *conv_params, int bd); class AV1ConvolveXHighbdTest : public AV1ConvolveTest<highbd_convolve_x_func> { public: @@ -618,7 +618,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -725,7 +725,7 @@ aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -754,10 +754,10 @@ //////////////////////////////////////////////////////// // Single reference convolve-y functions (low bit-depth) //////////////////////////////////////////////////////// -typedef void (*convolve_y_func)(const uint8_t *src, int src_stride, - uint8_t *dst, int dst_stride, int w, int h, - const InterpFilterParams *filter_params_y, - const int subpel_y_qn); +using convolve_y_func = void (*)(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, int h, + const InterpFilterParams *filter_params_y, + const int subpel_y_qn); class AV1ConvolveYTest : public AV1ConvolveTest<convolve_y_func> { public: @@ -826,7 +826,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -925,7 +925,7 @@ aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -951,10 +951,10 @@ ///////////////////////////////////////////////////////// // Single reference convolve-y functions (high bit-depth) ///////////////////////////////////////////////////////// -typedef void (*highbd_convolve_y_func)( - const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w, - int h, const InterpFilterParams *filter_params_y, const int subpel_y_qn, - int bd); +using highbd_convolve_y_func = + void (*)(const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, + int w, int h, const InterpFilterParams *filter_params_y, + const int subpel_y_qn, int bd); class AV1ConvolveYHighbdTest : public AV1ConvolveTest<highbd_convolve_y_func> { public: @@ -1023,7 +1023,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -1122,7 +1122,7 @@ aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", filter, width, height, time1, + printf("%d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", filter, width, height, time1, time2, time1 / time2); } }; @@ -1151,9 +1151,9 @@ ////////////////////////////////////////////////////////////// // Single reference convolve-copy functions (low bit-depth) ////////////////////////////////////////////////////////////// -typedef void (*convolve_copy_func)(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, int w, - int h); +using convolve_copy_func = void (*)(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, int w, + int h); class AV1ConvolveCopyTest : public AV1ConvolveTest<convolve_copy_func> { public: @@ -1195,9 +1195,9 @@ /////////////////////////////////////////////////////////////// // Single reference convolve-copy functions (high bit-depth) /////////////////////////////////////////////////////////////// -typedef void (*highbd_convolve_copy_func)(const uint16_t *src, - ptrdiff_t src_stride, uint16_t *dst, - ptrdiff_t dst_stride, int w, int h); +using highbd_convolve_copy_func = void (*)(const uint16_t *src, + ptrdiff_t src_stride, uint16_t *dst, + ptrdiff_t dst_stride, int w, int h); class AV1ConvolveCopyHighbdTest : public AV1ConvolveTest<highbd_convolve_copy_func> { @@ -1241,12 +1241,12 @@ ///////////////////////////////////////////////////////// // Single reference convolve-2D functions (low bit-depth) ///////////////////////////////////////////////////////// -typedef void (*convolve_2d_func)(const uint8_t *src, int src_stride, - uint8_t *dst, int dst_stride, int w, int h, - const InterpFilterParams *filter_params_x, - const InterpFilterParams *filter_params_y, - const int subpel_x_qn, const int subpel_y_qn, - ConvolveParams *conv_params); +using convolve_2d_func = void (*)(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, int h, + const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, + const int subpel_x_qn, const int subpel_y_qn, + ConvolveParams *conv_params); class AV1Convolve2DTest : public AV1ConvolveTest<convolve_2d_func> { public: @@ -1338,7 +1338,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height, + printf("%d - %d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", h_f, v_f, width, height, time1, time2, time1 / time2); } }; @@ -1456,7 +1456,7 @@ aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height, + printf("%d - %d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", h_f, v_f, width, height, time1, time2, time1 / time2); } }; @@ -1483,11 +1483,11 @@ // Single reference convolve-2d functions (high bit-depth) ////////////////////////////////////////////////////////// -typedef void (*highbd_convolve_2d_func)( - const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w, - int h, const InterpFilterParams *filter_params_x, - const InterpFilterParams *filter_params_y, const int subpel_x_qn, - const int subpel_y_qn, ConvolveParams *conv_params, int bd); +using highbd_convolve_2d_func = + void (*)(const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, + int w, int h, const InterpFilterParams *filter_params_x, + const InterpFilterParams *filter_params_y, const int subpel_x_qn, + const int subpel_y_qn, ConvolveParams *conv_params, int bd); class AV1Convolve2DHighbdTest : public AV1ConvolveTest<highbd_convolve_2d_func> { @@ -1581,7 +1581,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height, + printf("%d - %d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", h_f, v_f, width, height, time1, time2, time1 / time2); } }; @@ -1696,7 +1696,7 @@ aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height, + printf("%d - %d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", h_f, v_f, width, height, time1, time2, time1 / time2); } }; @@ -1951,6 +1951,11 @@ BuildLowbdLumaParams(av1_dist_wtd_convolve_x_neon_i8mm)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P(RVV, AV1ConvolveXCompoundTest, + BuildLowbdLumaParams(av1_dist_wtd_convolve_x_rvv)); +#endif + #if CONFIG_AV1_HIGHBITDEPTH ///////////////////////////////////////////////// // Compound convolve-x functions (high bit-depth) @@ -2046,6 +2051,12 @@ BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_x_neon)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P( + RVV, AV1ConvolveXHighbdCompoundTest, + BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_x_rvv)); +#endif + #if HAVE_SVE2 INSTANTIATE_TEST_SUITE_P( SVE2, AV1ConvolveXHighbdCompoundTest, @@ -2092,6 +2103,23 @@ BuildLowbdLumaParams(av1_dist_wtd_convolve_y_neon)); #endif +#if HAVE_NEON_DOTPROD +INSTANTIATE_TEST_SUITE_P( + NEON_DOTPROD, AV1ConvolveYCompoundTest, + BuildLowbdLumaParams(av1_dist_wtd_convolve_y_neon_dotprod)); +#endif + +#if HAVE_NEON_I8MM +INSTANTIATE_TEST_SUITE_P( + NEON_I8MM, AV1ConvolveYCompoundTest, + BuildLowbdLumaParams(av1_dist_wtd_convolve_y_neon_i8mm)); +#endif + +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P(RVV, AV1ConvolveYCompoundTest, + BuildLowbdLumaParams(av1_dist_wtd_convolve_y_rvv)); +#endif + #if CONFIG_AV1_HIGHBITDEPTH ///////////////////////////////////////////////// // Compound convolve-y functions (high bit-depth) @@ -2132,6 +2160,12 @@ BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_y_neon)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P( + RVV, AV1ConvolveYHighbdCompoundTest, + BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_y_rvv)); +#endif + #if HAVE_SVE2 INSTANTIATE_TEST_SUITE_P( SVE2, AV1ConvolveYHighbdCompoundTest, @@ -2143,9 +2177,9 @@ ////////////////////////////////////////////////////// // Compound convolve-2d-copy functions (low bit-depth) ////////////////////////////////////////////////////// -typedef void (*compound_conv_2d_copy_func)(const uint8_t *src, int src_stride, - uint8_t *dst, int dst_stride, int w, - int h, ConvolveParams *conv_params); +using compound_conv_2d_copy_func = void (*)(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, + int h, ConvolveParams *conv_params); class AV1Convolve2DCopyCompoundTest : public AV1ConvolveTest<compound_conv_2d_copy_func> { @@ -2218,7 +2252,7 @@ } aom_usec_timer_mark(&timer); const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer)); - printf("Dist Weighted: %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", + printf("Dist Weighted: %d %3dx%-3d:%7.2f/%7.2fus (%3.2f)\n", compound.UseDistWtdCompAvg(), width, height, time1, time2, time1 / time2); } @@ -2262,15 +2296,19 @@ BuildLowbdLumaParams(av1_dist_wtd_convolve_2d_copy_neon)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P( + RVV, AV1Convolve2DCopyCompoundTest, + BuildLowbdLumaParams(av1_dist_wtd_convolve_2d_copy_rvv)); +#endif + #if CONFIG_AV1_HIGHBITDEPTH /////////////////////////////////////////////////////// // Compound convolve-2d-copy functions (high bit-depth) /////////////////////////////////////////////////////// -typedef void (*highbd_compound_conv_2d_copy_func)(const uint16_t *src, - int src_stride, uint16_t *dst, - int dst_stride, int w, int h, - ConvolveParams *conv_params, - int bd); +using highbd_compound_conv_2d_copy_func = + void (*)(const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, + int w, int h, ConvolveParams *conv_params, int bd); class AV1Convolve2DCopyHighbdCompoundTest : public AV1ConvolveTest<highbd_compound_conv_2d_copy_func> { @@ -2348,6 +2386,12 @@ BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_2d_copy_neon)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P( + RVV, AV1Convolve2DCopyHighbdCompoundTest, + BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_2d_copy_rvv)); +#endif + #endif // CONFIG_AV1_HIGHBITDEPTH ///////////////////////////////////////////////// @@ -2443,6 +2487,11 @@ BuildLowbdLumaParams(av1_dist_wtd_convolve_2d_neon)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P(RVV, AV1Convolve2DCompoundTest, + BuildLowbdLumaParams(av1_dist_wtd_convolve_2d_rvv)); +#endif + #if HAVE_NEON_DOTPROD INSTANTIATE_TEST_SUITE_P( NEON_DOTPROD, AV1Convolve2DCompoundTest, @@ -2560,6 +2609,12 @@ BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_2d_sve2)); #endif +#if HAVE_RVV +INSTANTIATE_TEST_SUITE_P( + RVV, AV1Convolve2DHighbdCompoundTest, + BuildHighbdLumaParams(av1_highbd_dist_wtd_convolve_2d_rvv)); +#endif + #endif // CONFIG_AV1_HIGHBITDEPTH } // namespace
diff --git a/test/av1_external_partition_test.cc b/test/av1_external_partition_test.cc index 65fa001..1aecb34 100644 --- a/test/av1_external_partition_test.cc +++ b/test/av1_external_partition_test.cc
@@ -31,11 +31,11 @@ constexpr int kFrameNum = 8; constexpr int kVersion = 1; -typedef struct TestData { +struct TestData { int version = kVersion; -} TestData; +}; -typedef struct ToyModel { +struct ToyModel { TestData *data; aom_ext_part_config_t config; aom_ext_part_funcs_t funcs; @@ -44,7 +44,7 @@ int frame_width; int frame_height; BLOCK_SIZE block_size; -} ToyModel; +}; // Note: // if CONFIG_PARTITION_SEARCH_ORDER = 0, we test APIs designed for the baseline
diff --git a/test/av1_fwd_txfm2d_test.cc b/test/av1_fwd_txfm2d_test.cc index d694a32..483cf81 100644 --- a/test/av1_fwd_txfm2d_test.cc +++ b/test/av1_fwd_txfm2d_test.cc
@@ -35,7 +35,7 @@ namespace { // tx_type_, tx_size_, max_error_, max_avg_error_ -typedef std::tuple<TX_TYPE, TX_SIZE, double, double> AV1FwdTxfm2dParam; +using AV1FwdTxfm2dParam = std::tuple<TX_TYPE, TX_SIZE, double, double>; class AV1FwdTxfm2d : public ::testing::TestWithParam<AV1FwdTxfm2dParam> { public: @@ -238,8 +238,8 @@ } } -typedef void (*lowbd_fwd_txfm_func)(const int16_t *src_diff, tran_low_t *coeff, - int diff_stride, TxfmParam *txfm_param); +using lowbd_fwd_txfm_func = void (*)(const int16_t *src_diff, tran_low_t *coeff, + int diff_stride, TxfmParam *txfm_param); void AV1FwdTxfm2dMatchTest(TX_SIZE tx_size, lowbd_fwd_txfm_func target_func) { const int bd = 8; @@ -356,7 +356,7 @@ } } -typedef std::tuple<TX_SIZE, lowbd_fwd_txfm_func> LbdFwdTxfm2dParam; +using LbdFwdTxfm2dParam = std::tuple<TX_SIZE, lowbd_fwd_txfm_func>; class AV1FwdTxfm2dTest : public ::testing::TestWithParam<LbdFwdTxfm2dParam> {}; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1FwdTxfm2dTest); @@ -521,8 +521,9 @@ #endif // HAVE_NEON -typedef void (*Highbd_fwd_txfm_func)(const int16_t *src_diff, tran_low_t *coeff, - int diff_stride, TxfmParam *txfm_param); +using Highbd_fwd_txfm_func = void (*)(const int16_t *src_diff, + tran_low_t *coeff, int diff_stride, + TxfmParam *txfm_param); void AV1HighbdFwdTxfm2dMatchTest(TX_SIZE tx_size, Highbd_fwd_txfm_func target_func) { @@ -648,7 +649,7 @@ } } -typedef std::tuple<TX_SIZE, Highbd_fwd_txfm_func> HighbdFwdTxfm2dParam; +using HighbdFwdTxfm2dParam = std::tuple<TX_SIZE, Highbd_fwd_txfm_func>; class AV1HighbdFwdTxfm2dTest : public ::testing::TestWithParam<HighbdFwdTxfm2dParam> {};
diff --git a/test/av1_highbd_iht_test.cc b/test/av1_highbd_iht_test.cc index 24cf9b0..5da77fc 100644 --- a/test/av1_highbd_iht_test.cc +++ b/test/av1_highbd_iht_test.cc
@@ -29,11 +29,11 @@ using libaom_test::ACMRandom; using std::tuple; -typedef void (*HbdHtFunc)(const int16_t *input, int32_t *output, int stride, - TX_TYPE tx_type, int bd); - -typedef void (*IHbdHtFunc)(const int32_t *coeff, uint16_t *output, int stride, +using HbdHtFunc = void (*)(const int16_t *input, int32_t *output, int stride, TX_TYPE tx_type, int bd); + +using IHbdHtFunc = void (*)(const int32_t *coeff, uint16_t *output, int stride, + TX_TYPE tx_type, int bd); static const char *tx_type_name[] = { "DCT_DCT", "ADST_DCT", @@ -59,7 +59,7 @@ // num_coeffs, // tx_type, // bit_depth> -typedef tuple<HbdHtFunc, IHbdHtFunc, IHbdHtFunc, int, TX_TYPE, int> IHbdHtParam; +using IHbdHtParam = tuple<HbdHtFunc, IHbdHtFunc, IHbdHtFunc, int, TX_TYPE, int>; class AV1HighbdInvHTNxN : public ::testing::TestWithParam<IHbdHtParam> { public: @@ -193,10 +193,10 @@ ::testing::ValuesIn(kArrayIhtParam)); #endif // HAVE_SSE4_1 -typedef void (*HighbdInvTxfm2dFunc)(const int32_t *input, uint8_t *output, - int stride, const TxfmParam *txfm_param); +using HighbdInvTxfm2dFunc = void (*)(const int32_t *input, uint8_t *output, + int stride, const TxfmParam *txfm_param); -typedef std::tuple<const HighbdInvTxfm2dFunc> AV1HighbdInvTxfm2dParam; +using AV1HighbdInvTxfm2dParam = std::tuple<const HighbdInvTxfm2dFunc>; class AV1HighbdInvTxfm2d : public ::testing::TestWithParam<AV1HighbdInvTxfm2dParam> { public:
diff --git a/test/av1_horz_only_frame_superres_test.cc b/test/av1_horz_only_frame_superres_test.cc index 20b1a97..d32ebd5 100644 --- a/test/av1_horz_only_frame_superres_test.cc +++ b/test/av1_horz_only_frame_superres_test.cc
@@ -257,14 +257,14 @@ TestImage<Pixel> *image_; }; -typedef void (*LowBDConvolveHorizRsFunc)(const uint8_t *src, int src_stride, - uint8_t *dst, int dst_stride, int w, - int h, const int16_t *x_filters, - const int x0_qn, const int x_step_qn); +using LowBDConvolveHorizRsFunc = void (*)(const uint8_t *src, int src_stride, + uint8_t *dst, int dst_stride, int w, + int h, const int16_t *x_filters, + const int x0_qn, const int x_step_qn); // Test parameter list: // <tst_fun_> -typedef tuple<LowBDConvolveHorizRsFunc> LowBDParams; +using LowBDParams = tuple<LowBDConvolveHorizRsFunc>; class LowBDConvolveHorizRSTest : public ConvolveHorizRSTestBase<uint8_t>, @@ -322,15 +322,15 @@ #endif #if CONFIG_AV1_HIGHBITDEPTH -typedef void (*HighBDConvolveHorizRsFunc)(const uint16_t *src, int src_stride, - uint16_t *dst, int dst_stride, int w, - int h, const int16_t *x_filters, - const int x0_qn, const int x_step_qn, - int bd); +using HighBDConvolveHorizRsFunc = void (*)(const uint16_t *src, int src_stride, + uint16_t *dst, int dst_stride, int w, + int h, const int16_t *x_filters, + const int x0_qn, const int x_step_qn, + int bd); // Test parameter list: // <tst_fun_, bd_> -typedef tuple<HighBDConvolveHorizRsFunc, int> HighBDParams; +using HighBDParams = tuple<HighBDConvolveHorizRsFunc, int>; class HighBDConvolveHorizRSTest : public ConvolveHorizRSTestBase<uint16_t>,
diff --git a/test/av1_inv_txfm1d_test.cc b/test/av1_inv_txfm1d_test.cc index 156fb40..30b9a14 100644 --- a/test/av1_inv_txfm1d_test.cc +++ b/test/av1_inv_txfm1d_test.cc
@@ -16,8 +16,6 @@ #include "av1/common/av1_inv_txfm1d.h" #include "av1/encoder/av1_fwd_txfm1d.h" -typedef TX_SIZE TxSize; - using libaom_test::ACMRandom; using libaom_test::input_base; @@ -75,7 +73,7 @@ ASSERT_EQ(NELEMENTS(inv_txfm_func_ls), TX_SIZES); for (int i = 0; i < count_test_block; ++i) { // choose a random transform to test - const TxSize tx_size = static_cast<TxSize>(rnd.Rand8() % TX_SIZES); + const TX_SIZE tx_size = static_cast<TX_SIZE>(rnd.Rand8() % TX_SIZES); const int txfm_size = txfm_size_ls[tx_size]; const TxfmFunc inv_txfm_func = inv_txfm_func_ls[tx_size][0]; @@ -154,4 +152,15 @@ } } +TEST(av1_inv_txfm1d, iadst4_overflow_bug471847850) { + // Test case to trigger integer overflow in av1_iadst4 + int32_t input[4] = { 300000, 0, 300000, 300000 }; + int32_t output[4]; + + av1_iadst4(input, output, 12, range_bit); + + // Verify that the transform completes and the output is greater than zero. + EXPECT_GT(output[0], 0); +} + } // namespace
diff --git a/test/av1_inv_txfm2d_test.cc b/test/av1_inv_txfm2d_test.cc index 1f62daf..c26fe70 100644 --- a/test/av1_inv_txfm2d_test.cc +++ b/test/av1_inv_txfm2d_test.cc
@@ -38,14 +38,11 @@ using std::vector; -typedef TX_TYPE TxType; -typedef TX_SIZE TxSize; - namespace { // AV1InvTxfm2dParam argument list: // tx_type_, tx_size_, max_error_, max_avg_error_ -typedef std::tuple<TxType, TxSize, int, double> AV1InvTxfm2dParam; +using AV1InvTxfm2dParam = std::tuple<TX_TYPE, TX_SIZE, int, double>; class AV1InvTxfm2d : public ::testing::TestWithParam<AV1InvTxfm2dParam> { public: @@ -91,7 +88,7 @@ } double ref_coeffs[64 * 64] = { 0 }; ASSERT_LE(txfm2d_size, NELEMENTS(ref_coeffs)); - ASSERT_EQ(tx_type_, static_cast<TxType>(DCT_DCT)); + ASSERT_EQ(tx_type_, static_cast<TX_TYPE>(DCT_DCT)); libaom_test::reference_hybrid_2d(ref_input, ref_coeffs, tx_type_, tx_size_); DECLARE_ALIGNED(16, int32_t, ref_coeffs_int[64 * 64]) = { 0 }; @@ -146,8 +143,8 @@ int max_error_; double max_avg_error_; - TxType tx_type_; - TxSize tx_size_; + TX_TYPE tx_type_; + TX_SIZE tx_size_; }; static const int max_error_ls[TX_SIZES_ALL] = { @@ -200,8 +197,8 @@ const int max_error = max_error_ls[s]; const double avg_error = avg_error_ls[s]; for (int t = 0; t < TX_TYPES; ++t) { - const TxType tx_type = static_cast<TxType>(t); - const TxSize tx_size = static_cast<TxSize>(s); + const TX_TYPE tx_type = static_cast<TX_TYPE>(t); + const TX_SIZE tx_size = static_cast<TX_SIZE>(s); if (libaom_test::IsTxSizeTypeValid(tx_size, tx_type)) { param_list.push_back( AV1InvTxfm2dParam(tx_type, tx_size, max_error, avg_error)); @@ -223,18 +220,18 @@ int8_t high_range = libaom_test::high_range_arr[bd_idx]; for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) { for (int tx_type = 0; tx_type < TX_TYPES; ++tx_type) { - if (libaom_test::IsTxSizeTypeValid(static_cast<TxSize>(tx_size), - static_cast<TxType>(tx_type)) == + if (libaom_test::IsTxSizeTypeValid(static_cast<TX_SIZE>(tx_size), + static_cast<TX_TYPE>(tx_type)) == false) { continue; } TXFM_2D_FLIP_CFG cfg; - av1_get_inv_txfm_cfg(static_cast<TxType>(tx_type), - static_cast<TxSize>(tx_size), &cfg); + av1_get_inv_txfm_cfg(static_cast<TX_TYPE>(tx_type), + static_cast<TX_SIZE>(tx_size), &cfg); int8_t stage_range_col[MAX_TXFM_STAGE_NUM]; int8_t stage_range_row[MAX_TXFM_STAGE_NUM]; av1_gen_inv_stage_range(stage_range_col, stage_range_row, &cfg, - static_cast<TxSize>(tx_size), bd); + static_cast<TX_SIZE>(tx_size), bd); libaom_test::txfm_stage_range_check(stage_range_col, cfg.stage_num_col, cfg.cos_bit_col, low_range, high_range); @@ -246,11 +243,11 @@ } } -typedef std::tuple<const LbdInvTxfm2dFunc> AV1LbdInvTxfm2dParam; +using AV1LbdInvTxfm2dParam = std::tuple<const LbdInvTxfm2dFunc>; class AV1LbdInvTxfm2d : public ::testing::TestWithParam<AV1LbdInvTxfm2dParam> { public: void SetUp() override { target_func_ = GET_PARAM(0); } - void RunAV1InvTxfm2dTest(TxType tx_type, TxSize tx_size, int run_times, + void RunAV1InvTxfm2dTest(TX_TYPE tx_type, TX_SIZE tx_size, int run_times, int gt_int16 = 0); private: @@ -258,7 +255,7 @@ }; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1LbdInvTxfm2d); -void AV1LbdInvTxfm2d::RunAV1InvTxfm2dTest(TxType tx_type, TxSize tx_size, +void AV1LbdInvTxfm2d::RunAV1InvTxfm2dTest(TX_TYPE tx_type, TX_SIZE tx_size, int run_times, int gt_int16) { FwdTxfm2dFunc fwd_func_ = libaom_test::fwd_txfm_func_ls[tx_size]; InvTxfm2dFunc ref_func_ = libaom_test::inv_txfm_func_ls[tx_size]; @@ -340,21 +337,23 @@ TEST_P(AV1LbdInvTxfm2d, match) { for (int j = 0; j < (int)(TX_SIZES_ALL); ++j) { for (int i = 0; i < (int)TX_TYPES; ++i) { - if (libaom_test::IsTxSizeTypeValid(static_cast<TxSize>(j), - static_cast<TxType>(i))) { - RunAV1InvTxfm2dTest(static_cast<TxType>(i), static_cast<TxSize>(j), 1); + if (libaom_test::IsTxSizeTypeValid(static_cast<TX_SIZE>(j), + static_cast<TX_TYPE>(i))) { + RunAV1InvTxfm2dTest(static_cast<TX_TYPE>(i), static_cast<TX_SIZE>(j), + 1); } } } } TEST_P(AV1LbdInvTxfm2d, gt_int16) { - static const TxType types[] = { DCT_DCT, ADST_DCT, FLIPADST_DCT, IDTX, - V_DCT, H_DCT, H_ADST, H_FLIPADST }; + static const TX_TYPE types[] = { + DCT_DCT, ADST_DCT, FLIPADST_DCT, IDTX, V_DCT, H_DCT, H_ADST, H_FLIPADST + }; for (int j = 0; j < (int)(TX_SIZES_ALL); ++j) { - const TxSize sz = static_cast<TxSize>(j); + const TX_SIZE sz = static_cast<TX_SIZE>(j); for (uint8_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { - const TxType tp = types[i]; + const TX_TYPE tp = types[i]; if (libaom_test::IsTxSizeTypeValid(sz, tp)) { RunAV1InvTxfm2dTest(tp, sz, 1, 1); } @@ -365,9 +364,9 @@ TEST_P(AV1LbdInvTxfm2d, DISABLED_Speed) { for (int j = 1; j < (int)(TX_SIZES_ALL); ++j) { for (int i = 0; i < (int)TX_TYPES; ++i) { - if (libaom_test::IsTxSizeTypeValid(static_cast<TxSize>(j), - static_cast<TxType>(i))) { - RunAV1InvTxfm2dTest(static_cast<TxType>(i), static_cast<TxSize>(j), + if (libaom_test::IsTxSizeTypeValid(static_cast<TX_SIZE>(j), + static_cast<TX_TYPE>(i))) { + RunAV1InvTxfm2dTest(static_cast<TX_TYPE>(i), static_cast<TX_SIZE>(j), 10000000); } } @@ -377,7 +376,7 @@ #if HAVE_SSSE3 extern "C" void av1_lowbd_inv_txfm2d_add_ssse3(const int32_t *input, uint8_t *output, int stride, - TxType tx_type, TxSize tx_size, + TX_TYPE tx_type, TX_SIZE tx_size, int eob); INSTANTIATE_TEST_SUITE_P(SSSE3, AV1LbdInvTxfm2d, ::testing::Values(av1_lowbd_inv_txfm2d_add_ssse3)); @@ -386,7 +385,7 @@ #if HAVE_AVX2 extern "C" void av1_lowbd_inv_txfm2d_add_avx2(const int32_t *input, uint8_t *output, int stride, - TxType tx_type, TxSize tx_size, + TX_TYPE tx_type, TX_SIZE tx_size, int eob); INSTANTIATE_TEST_SUITE_P(AVX2, AV1LbdInvTxfm2d,
diff --git a/test/av1_k_means_test.cc b/test/av1_k_means_test.cc index db738472..58dcb01 100644 --- a/test/av1_k_means_test.cc +++ b/test/av1_k_means_test.cc
@@ -28,20 +28,20 @@ #include "test/util.h" namespace AV1Kmeans { -typedef void (*av1_calc_indices_dim1_func)(const int16_t *data, - const int16_t *centroids, - uint8_t *indices, - int64_t *total_dist, int n, int k); -typedef void (*av1_calc_indices_dim2_func)(const int16_t *data, - const int16_t *centroids, - uint8_t *indices, - int64_t *total_dist, int n, int k); +using av1_calc_indices_dim1_func = void (*)(const int16_t *data, + const int16_t *centroids, + uint8_t *indices, + int64_t *total_dist, int n, int k); +using av1_calc_indices_dim2_func = void (*)(const int16_t *data, + const int16_t *centroids, + uint8_t *indices, + int64_t *total_dist, int n, int k); -typedef std::tuple<av1_calc_indices_dim1_func, BLOCK_SIZE> - av1_calc_indices_dim1Param; +using av1_calc_indices_dim1Param = + std::tuple<av1_calc_indices_dim1_func, BLOCK_SIZE>; -typedef std::tuple<av1_calc_indices_dim2_func, BLOCK_SIZE> - av1_calc_indices_dim2Param; +using av1_calc_indices_dim2Param = + std::tuple<av1_calc_indices_dim2_func, BLOCK_SIZE>; class AV1KmeansTest1 : public ::testing::TestWithParam<av1_calc_indices_dim1Param> {
diff --git a/test/av1_key_value_api_test.cc b/test/av1_key_value_api_test.cc index 03cdeee..d3e9dc1 100644 --- a/test/av1_key_value_api_test.cc +++ b/test/av1_key_value_api_test.cc
@@ -21,7 +21,7 @@ #include "gtest/gtest.h" namespace { -typedef std::tuple<const char *, const char *> KeyValParam; +using KeyValParam = std::tuple<const char *, const char *>; class BaseKeyValAPI : public testing::Test { public:
diff --git a/test/av1_nn_predict_test.cc b/test/av1_nn_predict_test.cc index 38981be..b05b83c 100644 --- a/test/av1_nn_predict_test.cc +++ b/test/av1_nn_predict_test.cc
@@ -24,11 +24,11 @@ #include "test/acm_random.h" namespace { -typedef void (*NnPredict_Func)(const float *const input_nodes, - const NN_CONFIG *const nn_config, - int reduce_prec, float *const output); +using NnPredict_Func = void (*)(const float *const input_nodes, + const NN_CONFIG *const nn_config, + int reduce_prec, float *const output); -typedef std::tuple<const NnPredict_Func> NnPredictTestParam; +using NnPredictTestParam = std::tuple<const NnPredict_Func>; const float epsilon = 1e-3f; // Error threshold for functional equivalence
diff --git a/test/av1_quantize_test.cc b/test/av1_quantize_test.cc index 101186b..4f0c178 100644 --- a/test/av1_quantize_test.cc +++ b/test/av1_quantize_test.cc
@@ -22,7 +22,7 @@ namespace { -typedef void (*QuantizeFpFunc)( +using QuantizeFpFunc = void (*)( const tran_low_t *coeff_ptr, intptr_t count, const int16_t *zbin_ptr, const int16_t *round_ptr, const int16_t *quant_ptr, const int16_t *quant_shift_ptr, tran_low_t *qcoeff_ptr,
diff --git a/test/av1_round_shift_array_test.cc b/test/av1_round_shift_array_test.cc index e9731b1..39e99f8 100644 --- a/test/av1_round_shift_array_test.cc +++ b/test/av1_round_shift_array_test.cc
@@ -25,7 +25,7 @@ namespace AV1CompRoundShift { -typedef void (*comp_round_shift_array_func)(int32_t *arr, int size, int bit); +using comp_round_shift_array_func = void (*)(int32_t *arr, int size, int bit); #if HAVE_SSE4_1 || HAVE_NEON const int kValidBitCheck[] = { @@ -33,8 +33,8 @@ }; #endif // HAVE_SSE4_1 || HAVE_NEON -typedef std::tuple<comp_round_shift_array_func, BLOCK_SIZE, int> - CompRoundShiftParam; +using CompRoundShiftParam = + std::tuple<comp_round_shift_array_func, BLOCK_SIZE, int>; class AV1CompRoundShiftTest : public ::testing::TestWithParam<CompRoundShiftParam> {
diff --git a/test/av1_temporal_denoiser_test.cc b/test/av1_temporal_denoiser_test.cc index 35a682a..0a80381 100644 --- a/test/av1_temporal_denoiser_test.cc +++ b/test/av1_temporal_denoiser_test.cc
@@ -32,12 +32,12 @@ const int kNumPixels = 128 * 128; -typedef int (*Av1DenoiserFilterFunc)(const uint8_t *sig, int sig_stride, - const uint8_t *mc_avg, int mc_avg_stride, - uint8_t *avg, int avg_stride, - int increase_denoising, BLOCK_SIZE bs, - int motion_magnitude); -typedef std::tuple<Av1DenoiserFilterFunc, BLOCK_SIZE> AV1DenoiserTestParam; +using Av1DenoiserFilterFunc = int (*)(const uint8_t *sig, int sig_stride, + const uint8_t *mc_avg, int mc_avg_stride, + uint8_t *avg, int avg_stride, + int increase_denoising, BLOCK_SIZE bs, + int motion_magnitude); +using AV1DenoiserTestParam = std::tuple<Av1DenoiserFilterFunc, BLOCK_SIZE>; class AV1DenoiserTest : public ::testing::Test,
diff --git a/test/av1_txfm_test.h b/test/av1_txfm_test.h index a7f4a2b..e50dace 100644 --- a/test/av1_txfm_test.h +++ b/test/av1_txfm_test.h
@@ -76,12 +76,12 @@ template <typename Type> void fliplrud(Type *dest, int width, int height, int stride); -typedef void (*TxfmFunc)(const int32_t *in, int32_t *out, const int8_t cos_bit, - const int8_t *range_bit); +using TxfmFunc = void (*)(const int32_t *in, int32_t *out, const int8_t cos_bit, + const int8_t *range_bit); -typedef void (*InvTxfm2dFunc)(const int32_t *, uint16_t *, int, TX_TYPE, int); -typedef void (*LbdInvTxfm2dFunc)(const int32_t *, uint8_t *, int, TX_TYPE, - TX_SIZE, int); +using InvTxfm2dFunc = void (*)(const int32_t *, uint16_t *, int, TX_TYPE, int); +using LbdInvTxfm2dFunc = void (*)(const int32_t *, uint8_t *, int, TX_TYPE, + TX_SIZE, int); static const int bd = 10; static const int input_base = (1 << bd);
diff --git a/test/av1_wedge_utils_test.cc b/test/av1_wedge_utils_test.cc index af11494..9719d2b 100644 --- a/test/av1_wedge_utils_test.cc +++ b/test/av1_wedge_utils_test.cc
@@ -156,9 +156,9 @@ // av1_wedge_sse_from_residuals - optimizations ////////////////////////////////////////////////////////////////////////////// -typedef uint64_t (*FSSE)(const int16_t *r1, const int16_t *d, const uint8_t *m, - int N); -typedef libaom_test::FuncParam<FSSE> TestFuncsFSSE; +using FSSE = uint64_t (*)(const int16_t *r1, const int16_t *d, const uint8_t *m, + int N); +using TestFuncsFSSE = libaom_test::FuncParam<FSSE>; class WedgeUtilsSSEOptTest : public FunctionEquivalenceTest<FSSE> { protected: @@ -222,9 +222,9 @@ // av1_wedge_sign_from_residuals ////////////////////////////////////////////////////////////////////////////// -typedef int8_t (*FSign)(const int16_t *ds, const uint8_t *m, int N, - int64_t limit); -typedef libaom_test::FuncParam<FSign> TestFuncsFSign; +using FSign = int8_t (*)(const int16_t *ds, const uint8_t *m, int N, + int64_t limit); +using TestFuncsFSign = libaom_test::FuncParam<FSign>; class WedgeUtilsSignOptTest : public FunctionEquivalenceTest<FSign> { protected: @@ -324,8 +324,8 @@ // av1_wedge_compute_delta_squares ////////////////////////////////////////////////////////////////////////////// -typedef void (*FDS)(int16_t *d, const int16_t *a, const int16_t *b, int N); -typedef libaom_test::FuncParam<FDS> TestFuncsFDS; +using FDS = void (*)(int16_t *d, const int16_t *a, const int16_t *b, int N); +using TestFuncsFDS = libaom_test::FuncParam<FDS>; class WedgeUtilsDeltaSquaresOptTest : public FunctionEquivalenceTest<FDS> { protected:
diff --git a/test/avg_test.cc b/test/avg_test.cc index d1698fc..d2b4dbc 100644 --- a/test/avg_test.cc +++ b/test/avg_test.cc
@@ -117,11 +117,11 @@ ACMRandom rnd_; }; -typedef unsigned int (*AverageFunction)(const uint8_t *s, int pitch); +using AverageFunction = unsigned int (*)(const uint8_t *s, int pitch); // Arguments: width, height, bit_depth, buffer start offset, block size, avg // function. -typedef std::tuple<int, int, int, int, int, AverageFunction> AvgFunc; +using AvgFunc = std::tuple<int, int, int, int, int, AverageFunction>; template <typename Pixel> class AverageTest : public AverageTestBase<Pixel>, @@ -216,13 +216,13 @@ int64_t opt_elapsed_time_ = 0; }; -typedef void (*AverageFunction_8x8_quad)(const uint8_t *s, int pitch, int x_idx, - int y_idx, int *avg); +using AverageFunction_8x8_quad = void (*)(const uint8_t *s, int pitch, + int x_idx, int y_idx, int *avg); // Arguments: width, height, bit_depth, buffer start offset, block size, avg // function. -typedef std::tuple<int, int, int, int, int, AverageFunction_8x8_quad> - AvgFunc_8x8_quad; +using AvgFunc_8x8_quad = + std::tuple<int, int, int, int, int, AverageFunction_8x8_quad>; template <typename Pixel> class AverageTest_8x8_quad @@ -350,12 +350,12 @@ } #endif // CONFIG_AV1_HIGHBITDEPTH -typedef void (*IntProRowFunc)(int16_t *hbuf, uint8_t const *ref, - const int ref_stride, const int width, - const int height, int norm_factor); +using IntProRowFunc = void (*)(int16_t *hbuf, uint8_t const *ref, + const int ref_stride, const int width, + const int height, int norm_factor); // Params: width, height, asm function, c function. -typedef std::tuple<int, int, IntProRowFunc, IntProRowFunc> IntProRowParam; +using IntProRowParam = std::tuple<int, int, IntProRowFunc, IntProRowFunc>; class IntProRowTest : public AverageTestBase<uint8_t>, public ::testing::WithParamInterface<IntProRowParam> { @@ -452,12 +452,12 @@ }; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IntProRowTest); -typedef void (*IntProColFunc)(int16_t *vbuf, uint8_t const *ref, - const int ref_stride, const int width, - const int height, int norm_factor); +using IntProColFunc = void (*)(int16_t *vbuf, uint8_t const *ref, + const int ref_stride, const int width, + const int height, int norm_factor); // Params: width, height, asm function, c function. -typedef std::tuple<int, int, IntProColFunc, IntProColFunc> IntProColParam; +using IntProColParam = std::tuple<int, int, IntProColFunc, IntProColFunc>; class IntProColTest : public AverageTestBase<uint8_t>, public ::testing::WithParamInterface<IntProColParam> { @@ -633,10 +633,10 @@ static const int num_random_cmp = 50; }; -typedef int (*VectorVarFunc)(const int16_t *ref, const int16_t *src, - const int bwl); +using VectorVarFunc = int (*)(const int16_t *ref, const int16_t *src, + const int bwl); -typedef std::tuple<int, VectorVarFunc, VectorVarFunc> VecVarFunc; +using VecVarFunc = std::tuple<int, VectorVarFunc, VectorVarFunc>; class VectorVarTest : public VectorVarTestBase, public ::testing::WithParamInterface<VecVarFunc> { @@ -858,8 +858,8 @@ #endif // HAVE_NEON #endif // CONFIG_AV1_HIGHBITDEPTH -typedef int (*SatdFunc)(const tran_low_t *coeffs, int length); -typedef int (*SatdLpFunc)(const int16_t *coeffs, int length); +using SatdFunc = int (*)(const tran_low_t *coeffs, int length); +using SatdLpFunc = int (*)(const int16_t *coeffs, int length); template <typename SatdFuncType> struct SatdTestParam {
diff --git a/test/blend_a64_mask_1d_test.cc b/test/blend_a64_mask_1d_test.cc index feee2d4..d20baa5 100644 --- a/test/blend_a64_mask_1d_test.cc +++ b/test/blend_a64_mask_1d_test.cc
@@ -114,10 +114,10 @@ // 8 bit version ////////////////////////////////////////////////////////////////////////////// -typedef void (*F8B)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, - uint32_t src0_stride, const uint8_t *src1, - uint32_t src1_stride, const uint8_t *mask, int w, int h); -typedef libaom_test::FuncParam<F8B> TestFuncs; +using F8B = void (*)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, + uint32_t src0_stride, const uint8_t *src1, + uint32_t src1_stride, const uint8_t *mask, int w, int h); +using TestFuncs = libaom_test::FuncParam<F8B>; class BlendA64Mask1DTest8B : public BlendA64Mask1DTest<F8B, uint8_t> { protected: @@ -219,11 +219,11 @@ // High bit-depth version ////////////////////////////////////////////////////////////////////////////// #if CONFIG_AV1_HIGHBITDEPTH -typedef void (*FHBD)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, - uint32_t src0_stride, const uint8_t *src1, - uint32_t src1_stride, const uint8_t *mask, int w, int h, - int bd); -typedef libaom_test::FuncParam<FHBD> TestFuncsHBD; +using FHBD = void (*)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, + uint32_t src0_stride, const uint8_t *src1, + uint32_t src1_stride, const uint8_t *mask, int w, int h, + int bd); +using TestFuncsHBD = libaom_test::FuncParam<FHBD>; class BlendA64Mask1DTestHBD : public BlendA64Mask1DTest<FHBD, uint16_t> { protected:
diff --git a/test/blend_a64_mask_test.cc b/test/blend_a64_mask_test.cc index 43d0162..c9dad97 100644 --- a/test/blend_a64_mask_test.cc +++ b/test/blend_a64_mask_test.cc
@@ -157,11 +157,11 @@ // 8 bit version ////////////////////////////////////////////////////////////////////////////// -typedef void (*F8B)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, - uint32_t src0_stride, const uint8_t *src1, - uint32_t src1_stride, const uint8_t *mask, - uint32_t mask_stride, int w, int h, int subx, int suby); -typedef libaom_test::FuncParam<F8B> TestFuncs; +using F8B = void (*)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, + uint32_t src0_stride, const uint8_t *src1, + uint32_t src1_stride, const uint8_t *mask, + uint32_t mask_stride, int w, int h, int subx, int suby); +using TestFuncs = libaom_test::FuncParam<F8B>; class BlendA64MaskTest8B : public BlendA64MaskTest<F8B, uint8_t, uint8_t> { protected: @@ -266,12 +266,13 @@ // 8 bit _d16 version ////////////////////////////////////////////////////////////////////////////// -typedef void (*F8B_D16)(uint8_t *dst, uint32_t dst_stride, const uint16_t *src0, - uint32_t src0_stride, const uint16_t *src1, - uint32_t src1_stride, const uint8_t *mask, - uint32_t mask_stride, int w, int h, int subx, int suby, - ConvolveParams *conv_params); -typedef libaom_test::FuncParam<F8B_D16> TestFuncs_d16; +using F8B_D16 = void (*)(uint8_t *dst, uint32_t dst_stride, + const uint16_t *src0, uint32_t src0_stride, + const uint16_t *src1, uint32_t src1_stride, + const uint8_t *mask, uint32_t mask_stride, int w, + int h, int subx, int suby, + ConvolveParams *conv_params); +using TestFuncs_d16 = libaom_test::FuncParam<F8B_D16>; class BlendA64MaskTest8B_d16 : public BlendA64MaskTest<F8B_D16, uint16_t, uint8_t> { @@ -387,12 +388,12 @@ // High bit-depth version ////////////////////////////////////////////////////////////////////////////// #if CONFIG_AV1_HIGHBITDEPTH -typedef void (*FHBD)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, - uint32_t src0_stride, const uint8_t *src1, - uint32_t src1_stride, const uint8_t *mask, - uint32_t mask_stride, int w, int h, int subx, int suby, - int bd); -typedef libaom_test::FuncParam<FHBD> TestFuncsHBD; +using FHBD = void (*)(uint8_t *dst, uint32_t dst_stride, const uint8_t *src0, + uint32_t src0_stride, const uint8_t *src1, + uint32_t src1_stride, const uint8_t *mask, + uint32_t mask_stride, int w, int h, int subx, int suby, + int bd); +using TestFuncsHBD = libaom_test::FuncParam<FHBD>; class BlendA64MaskTestHBD : public BlendA64MaskTest<FHBD, uint16_t, uint16_t> { protected: @@ -490,13 +491,13 @@ // HBD _d16 version ////////////////////////////////////////////////////////////////////////////// -typedef void (*FHBD_D16)(uint8_t *dst, uint32_t dst_stride, - const CONV_BUF_TYPE *src0, uint32_t src0_stride, - const CONV_BUF_TYPE *src1, uint32_t src1_stride, - const uint8_t *mask, uint32_t mask_stride, int w, - int h, int subx, int suby, ConvolveParams *conv_params, - const int bd); -typedef libaom_test::FuncParam<FHBD_D16> TestFuncsHBD_d16; +using FHBD_D16 = void (*)(uint8_t *dst, uint32_t dst_stride, + const CONV_BUF_TYPE *src0, uint32_t src0_stride, + const CONV_BUF_TYPE *src1, uint32_t src1_stride, + const uint8_t *mask, uint32_t mask_stride, int w, + int h, int subx, int suby, + ConvolveParams *conv_params, const int bd); +using TestFuncsHBD_d16 = libaom_test::FuncParam<FHBD_D16>; class BlendA64MaskTestHBD_d16 : public BlendA64MaskTest<FHBD_D16, uint16_t, uint16_t> {
diff --git a/test/cdef_test.cc b/test/cdef_test.cc index 958e6e5..6f985f0 100644 --- a/test/cdef_test.cc +++ b/test/cdef_test.cc
@@ -32,9 +32,9 @@ using CdefFilterBlockFunctions = std::array<cdef_filter_block_func, 4>; -typedef std::tuple<CdefFilterBlockFunctions, CdefFilterBlockFunctions, - BLOCK_SIZE, int, int> - cdef_dir_param_t; +using cdef_dir_param_t = + std::tuple<CdefFilterBlockFunctions, CdefFilterBlockFunctions, BLOCK_SIZE, + int, int>; class CDEFBlockTest : public ::testing::TestWithParam<cdef_dir_param_t> { public: @@ -56,13 +56,13 @@ }; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFBlockTest); -typedef CDEFBlockTest CDEFBlockHighbdTest; +using CDEFBlockHighbdTest = CDEFBlockTest; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFBlockHighbdTest); -typedef CDEFBlockTest CDEFSpeedTest; +using CDEFSpeedTest = CDEFBlockTest; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFSpeedTest); -typedef CDEFBlockTest CDEFSpeedHighbdTest; +using CDEFSpeedHighbdTest = CDEFBlockTest; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFSpeedHighbdTest); int64_t test_cdef(BLOCK_SIZE bsize, int iterations, @@ -206,10 +206,10 @@ << "SIMD time: " << elapsed_time << " us" << std::endl; } -typedef int (*find_dir_t)(const uint16_t *img, int stride, int32_t *var, - int coeff_shift); +using find_dir_t = int (*)(const uint16_t *img, int stride, int32_t *var, + int coeff_shift); -typedef std::tuple<find_dir_t, find_dir_t> find_dir_param_t; +using find_dir_param_t = std::tuple<find_dir_t, find_dir_t>; class CDEFFindDirTest : public ::testing::TestWithParam<find_dir_param_t> { public: @@ -225,7 +225,7 @@ }; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFFindDirTest); -typedef CDEFFindDirTest CDEFFindDirSpeedTest; +using CDEFFindDirSpeedTest = CDEFFindDirTest; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFFindDirSpeedTest); void test_finddir(int (*finddir)(const uint16_t *img, int stride, int32_t *var, @@ -293,11 +293,11 @@ << "SIMD time: " << elapsed_time << " us" << std::endl; } -typedef void (*find_dir_dual_t)(const uint16_t *img1, const uint16_t *img2, - int stride, int32_t *var1, int32_t *var2, - int coeff_shift, int *out1, int *out2); +using find_dir_dual_t = void (*)(const uint16_t *img1, const uint16_t *img2, + int stride, int32_t *var1, int32_t *var2, + int coeff_shift, int *out1, int *out2); -typedef std::tuple<find_dir_dual_t, find_dir_dual_t> find_dir_dual_param_t; +using find_dir_dual_param_t = std::tuple<find_dir_dual_t, find_dir_dual_t>; class CDEFFindDirDualTest : public ::testing::TestWithParam<find_dir_dual_param_t> { @@ -314,7 +314,7 @@ }; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFFindDirDualTest); -typedef CDEFFindDirDualTest CDEFFindDirDualSpeedTest; +using CDEFFindDirDualSpeedTest = CDEFFindDirDualTest; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CDEFFindDirDualSpeedTest); void test_finddir_dual(
diff --git a/test/cfl_test.cc b/test/cfl_test.cc index 3f93305..6002fd8 100644 --- a/test/cfl_test.cc +++ b/test/cfl_test.cc
@@ -172,8 +172,8 @@ } }; -typedef cfl_subtract_average_fn (*sub_avg_fn)(TX_SIZE tx_size); -typedef std::tuple<TX_SIZE, sub_avg_fn> sub_avg_param; +using sub_avg_fn = cfl_subtract_average_fn (*)(TX_SIZE tx_size); +using sub_avg_param = std::tuple<TX_SIZE, sub_avg_fn>; class CFLSubAvgTest : public ::testing::TestWithParam<sub_avg_param>, public CFLTestWithData<uint16_t> { public: @@ -280,10 +280,10 @@ } }; -typedef cfl_subsample_lbd_fn (*get_subsample_lbd_fn)(TX_SIZE tx_size); -typedef std::tuple<TX_SIZE, get_subsample_lbd_fn, get_subsample_lbd_fn, - get_subsample_lbd_fn> - subsample_lbd_param; +using get_subsample_lbd_fn = cfl_subsample_lbd_fn (*)(TX_SIZE tx_size); +using subsample_lbd_param = + std::tuple<TX_SIZE, get_subsample_lbd_fn, get_subsample_lbd_fn, + get_subsample_lbd_fn>; class CFLSubsampleLBDTest : public CFLSubsampleTest<subsample_lbd_param, cfl_subsample_lbd_fn, uint8_t> { @@ -324,10 +324,10 @@ } #if CONFIG_AV1_HIGHBITDEPTH -typedef cfl_subsample_hbd_fn (*get_subsample_hbd_fn)(TX_SIZE tx_size); -typedef std::tuple<TX_SIZE, get_subsample_hbd_fn, get_subsample_hbd_fn, - get_subsample_hbd_fn> - subsample_hbd_param; +using get_subsample_hbd_fn = cfl_subsample_hbd_fn (*)(TX_SIZE tx_size); +using subsample_hbd_param = + std::tuple<TX_SIZE, get_subsample_hbd_fn, get_subsample_hbd_fn, + get_subsample_hbd_fn>; class CFLSubsampleHBDTest : public CFLSubsampleTest<subsample_hbd_param, cfl_subsample_hbd_fn, uint16_t> { @@ -368,8 +368,8 @@ } #endif // CONFIG_AV1_HIGHBITDEPTH -typedef cfl_predict_lbd_fn (*get_predict_fn)(TX_SIZE tx_size); -typedef std::tuple<TX_SIZE, get_predict_fn> predict_param; +using get_predict_fn = cfl_predict_lbd_fn (*)(TX_SIZE tx_size); +using predict_param = std::tuple<TX_SIZE, get_predict_fn>; class CFLPredictTest : public ::testing::TestWithParam<predict_param>, public CFLTestWithAlignedData<uint8_t> { public: @@ -417,8 +417,8 @@ } #if CONFIG_AV1_HIGHBITDEPTH -typedef cfl_predict_hbd_fn (*get_predict_fn_hbd)(TX_SIZE tx_size); -typedef std::tuple<TX_SIZE, get_predict_fn_hbd> predict_param_hbd; +using get_predict_fn_hbd = cfl_predict_hbd_fn (*)(TX_SIZE tx_size); +using predict_param_hbd = std::tuple<TX_SIZE, get_predict_fn_hbd>; class CFLPredictHBDTest : public ::testing::TestWithParam<predict_param_hbd>, public CFLTestWithAlignedData<uint16_t> { public:
diff --git a/test/cnn_test.cc b/test/cnn_test.cc index 3012451..fdf71d0 100644 --- a/test/cnn_test.cc +++ b/test/cnn_test.cc
@@ -2510,13 +2510,13 @@ namespace { -typedef void (*CNNConvolveNoMaxpoolPaddingValidFunc)( - const float **input, int in_width, int in_height, int in_stride, - const CNN_LAYER_CONFIG *layer_config, float **output, int out_stride, - int start_idx, int cstep, int channel_step); +using CNNConvolveNoMaxpoolPaddingValidFunc = + void (*)(const float **input, int in_width, int in_height, int in_stride, + const CNN_LAYER_CONFIG *layer_config, float **output, + int out_stride, int start_idx, int cstep, int channel_step); -typedef libaom_test::FuncParam<CNNConvolveNoMaxpoolPaddingValidFunc> - CNNConvolveTestFuncs; +using CNNConvolveTestFuncs = + libaom_test::FuncParam<CNNConvolveNoMaxpoolPaddingValidFunc>; class CNNConvolveTest : public ::testing::TestWithParam<CNNConvolveTestFuncs> { protected:
diff --git a/test/comp_mask_pred_test.cc b/test/comp_mask_pred_test.cc index 953e481..f57160c 100644 --- a/test/comp_mask_pred_test.cc +++ b/test/comp_mask_pred_test.cc
@@ -30,14 +30,14 @@ #include "test/util.h" namespace { -typedef void (*comp_mask_pred_func)(uint8_t *comp_pred, const uint8_t *pred, - int width, int height, const uint8_t *ref, - int ref_stride, const uint8_t *mask, - int mask_stride, int invert_mask); +using comp_mask_pred_func = void (*)(uint8_t *comp_pred, const uint8_t *pred, + int width, int height, const uint8_t *ref, + int ref_stride, const uint8_t *mask, + int mask_stride, int invert_mask); -typedef void (*comp_avg_pred_func)(uint8_t *comp_pred, const uint8_t *pred, - int width, int height, const uint8_t *ref, - int ref_stride); +using comp_avg_pred_func = void (*)(uint8_t *comp_pred, const uint8_t *pred, + int width, int height, const uint8_t *ref, + int ref_stride); #if HAVE_SSSE3 || HAVE_SSE2 || HAVE_AVX2 || HAVE_NEON const BLOCK_SIZE kCompMaskPredParams[] = { @@ -81,9 +81,11 @@ void AV1CompMaskPredBase::SetUp() { rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed()); av1_init_wedge_masks(); - comp_pred1_ = (uint8_t *)aom_memalign(16, MAX_SB_SQUARE); + comp_pred1_ = + (uint8_t *)aom_memalign(16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE); ASSERT_NE(comp_pred1_, nullptr); - comp_pred2_ = (uint8_t *)aom_memalign(16, MAX_SB_SQUARE); + comp_pred2_ = + (uint8_t *)aom_memalign(16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE); ASSERT_NE(comp_pred2_, nullptr); pred_ = (uint8_t *)aom_memalign(16, MAX_SB_SQUARE); ASSERT_NE(pred_, nullptr); @@ -111,7 +113,7 @@ aom_free(ref_buffer_); } -typedef std::tuple<comp_mask_pred_func, BLOCK_SIZE> CompMaskPredParam; +using CompMaskPredParam = std::tuple<comp_mask_pred_func, BLOCK_SIZE>; class AV1CompMaskPredTest : public AV1CompMaskPredBase, @@ -207,14 +209,15 @@ }; #endif -typedef void (*upsampled_pred_func)(MACROBLOCKD *xd, const AV1_COMMON *const cm, - int mi_row, int mi_col, const MV *const mv, - uint8_t *comp_pred, int width, int height, - int subpel_x_q3, int subpel_y_q3, - const uint8_t *ref, int ref_stride, - int subpel_search); +using upsampled_pred_func = void (*)(MACROBLOCKD *xd, + const AV1_COMMON *const cm, int mi_row, + int mi_col, const MV *const mv, + uint8_t *comp_pred, int width, int height, + int subpel_x_q3, int subpel_y_q3, + const uint8_t *ref, int ref_stride, + int subpel_search); -typedef std::tuple<upsampled_pred_func, BLOCK_SIZE> UpsampledPredParam; +using UpsampledPredParam = std::tuple<upsampled_pred_func, BLOCK_SIZE>; class AV1UpsampledPredTest : public AV1CompMaskPredBase, @@ -299,7 +302,7 @@ ::testing::ValuesIn(kValidBlockSize))); #endif -typedef std::tuple<comp_avg_pred_func, BLOCK_SIZE> CompAvgPredParam; +using CompAvgPredParam = std::tuple<comp_avg_pred_func, BLOCK_SIZE>; class AV1CompAvgPredTest : public ::testing::TestWithParam<CompAvgPredParam> { public: @@ -454,11 +457,11 @@ rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed()); av1_init_wedge_masks(); - comp_pred1_ = - (uint16_t *)aom_memalign(16, MAX_SB_SQUARE * sizeof(*comp_pred1_)); + comp_pred1_ = (uint16_t *)aom_memalign( + 16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE * sizeof(*comp_pred1_)); ASSERT_NE(comp_pred1_, nullptr); - comp_pred2_ = - (uint16_t *)aom_memalign(16, MAX_SB_SQUARE * sizeof(*comp_pred2_)); + comp_pred2_ = (uint16_t *)aom_memalign( + 16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE * sizeof(*comp_pred2_)); ASSERT_NE(comp_pred2_, nullptr); pred_ = (uint16_t *)aom_memalign(16, MAX_SB_SQUARE * sizeof(*pred_)); ASSERT_NE(pred_, nullptr); @@ -481,14 +484,14 @@ aom_free(ref_buffer_); } -typedef void (*highbd_comp_mask_pred_func)(uint8_t *comp_pred8, - const uint8_t *pred8, int width, - int height, const uint8_t *ref8, - int ref_stride, const uint8_t *mask, - int mask_stride, int invert_mask); +using highbd_comp_mask_pred_func = void (*)(uint8_t *comp_pred8, + const uint8_t *pred8, int width, + int height, const uint8_t *ref8, + int ref_stride, const uint8_t *mask, + int mask_stride, int invert_mask); -typedef std::tuple<highbd_comp_mask_pred_func, BLOCK_SIZE, int> - HighbdCompMaskPredParam; +using HighbdCompMaskPredParam = + std::tuple<highbd_comp_mask_pred_func, BLOCK_SIZE, int>; class AV1HighbdCompMaskPredTest : public AV1HighbdCompMaskPredTestBase, @@ -607,14 +610,14 @@ ::testing::Range(8, 13, 2))); #endif -typedef void (*highbd_upsampled_pred_func)( - MACROBLOCKD *xd, const struct AV1Common *const cm, int mi_row, int mi_col, - const MV *const mv, uint8_t *comp_pred8, int width, int height, - int subpel_x_q3, int subpel_y_q3, const uint8_t *ref8, int ref_stride, - int bd, int subpel_search); +using highbd_upsampled_pred_func = + void (*)(MACROBLOCKD *xd, const struct AV1Common *const cm, int mi_row, + int mi_col, const MV *const mv, uint8_t *comp_pred8, int width, + int height, int subpel_x_q3, int subpel_y_q3, const uint8_t *ref8, + int ref_stride, int bd, int subpel_search); -typedef std::tuple<highbd_upsampled_pred_func, BLOCK_SIZE, int> - HighbdUpsampledPredParam; +using HighbdUpsampledPredParam = + std::tuple<highbd_upsampled_pred_func, BLOCK_SIZE, int>; class AV1HighbdUpsampledPredTest : public AV1HighbdCompMaskPredTestBase, @@ -728,13 +731,13 @@ ::testing::Range(8, 13, 2))); #endif -typedef void (*highbd_comp_avg_pred_func)(uint8_t *comp_pred, - const uint8_t *pred, int width, - int height, const uint8_t *ref, - int ref_stride); +using highbd_comp_avg_pred_func = void (*)(uint8_t *comp_pred, + const uint8_t *pred, int width, + int height, const uint8_t *ref, + int ref_stride); -typedef std::tuple<highbd_comp_avg_pred_func, BLOCK_SIZE, int> - HighbdCompAvgPredParam; +using HighbdCompAvgPredParam = + std::tuple<highbd_comp_avg_pred_func, BLOCK_SIZE, int>; class AV1HighbdCompAvgPredTest : public ::testing::TestWithParam<HighbdCompAvgPredParam> {
diff --git a/test/convolve_test.cc b/test/convolve_test.cc index 09e5f64..578d9a2 100644 --- a/test/convolve_test.cc +++ b/test/convolve_test.cc
@@ -40,11 +40,11 @@ static const int kNumFilterBanks = SWITCHABLE_FILTERS; static const int kNumFilters = 16; -typedef void (*ConvolveFunc)(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, - const int16_t *filter_x, int filter_x_stride, - const int16_t *filter_y, int filter_y_stride, - int w, int h); +using ConvolveFunc = void (*)(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const int16_t *filter_x, int filter_x_stride, + const int16_t *filter_y, int filter_y_stride, + int w, int h); struct ConvolveFunctions { ConvolveFunctions(ConvolveFunc h8, ConvolveFunc v8, int bd) @@ -55,7 +55,7 @@ int use_highbd_; // 0 if high bitdepth not used, else the actual bit depth. }; -typedef std::tuple<int, int, const ConvolveFunctions *> ConvolveParam; +using ConvolveParam = std::tuple<int, int, const ConvolveFunctions *>; #define ALL_SIZES_64(convolve_fn) \ make_tuple(4, 4, &convolve_fn), make_tuple(8, 4, &convolve_fn), \ @@ -940,13 +940,13 @@ #endif #endif // HAVE_SVE -typedef void (*ConvolveScale2DFunc)(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, - const InterpKernel *filter, int x0_q4, - int x_step_q4, int y0_q4, int y_step_q4, - int w, int h); +using ConvolveScale2DFunc = void (*)(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h); -typedef std::tuple<int, int, ConvolveScale2DFunc> ConvolveScale2DParam; +using ConvolveScale2DParam = std::tuple<int, int, ConvolveScale2DFunc>; class ConvolveScale2DTest : public ::testing::TestWithParam<ConvolveScale2DParam> {
diff --git a/test/corner_match_test.cc b/test/corner_match_test.cc index a805329..e1353b8 100644 --- a/test/corner_match_test.cc +++ b/test/corner_match_test.cc
@@ -27,19 +27,19 @@ using libaom_test::ACMRandom; -typedef bool (*ComputeMeanStddevFunc)(const unsigned char *frame, int stride, - int x, int y, double *mean, - double *one_over_stddev); -typedef double (*ComputeCorrFunc)(const unsigned char *frame1, int stride1, - int x1, int y1, double mean1, - double one_over_stddev1, - const unsigned char *frame2, int stride2, - int x2, int y2, double mean2, - double one_over_stddev2); +using ComputeMeanStddevFunc = bool (*)(const unsigned char *frame, int stride, + int x, int y, double *mean, + double *one_over_stddev); +using ComputeCorrFunc = double (*)(const unsigned char *frame1, int stride1, + int x1, int y1, double mean1, + double one_over_stddev1, + const unsigned char *frame2, int stride2, + int x2, int y2, double mean2, + double one_over_stddev2); using std::make_tuple; using std::tuple; -typedef tuple<int, ComputeMeanStddevFunc, ComputeCorrFunc> CornerMatchParam; +using CornerMatchParam = tuple<int, ComputeMeanStddevFunc, ComputeCorrFunc>; class AV1CornerMatchTest : public ::testing::TestWithParam<CornerMatchParam> { public:
diff --git a/test/datarate_test.cc b/test/datarate_test.cc index 3d86128..c777d4a 100644 --- a/test/datarate_test.cc +++ b/test/datarate_test.cc
@@ -52,7 +52,7 @@ void FillFrame() override { // Read frame from input_file and scale up. ASSERT_NE(input_file_, nullptr); - fread(img_input_->img_data, raw_size_, 1, input_file_); + ASSERT_EQ(fread(img_input_->img_data, raw_size_, 1, input_file_), 1); libyuv::I420Scale( img_input_->planes[AOM_PLANE_Y], img_input_->stride[AOM_PLANE_Y], img_input_->planes[AOM_PLANE_U], img_input_->stride[AOM_PLANE_U], @@ -103,37 +103,15 @@ ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, 30, 1, 0, 140); const int bitrate_array[2] = { 400, 800 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.7) - << " The datarate for the file is lower than target by too much!"; - // FIXME(jingning): Lower this test threshold after vbr mode can render - // sufficiently accurate bit rate. - ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.45) - << " The datarate for the file is greater than target by too much!"; + RunBasicRateTargetingTest(&video, bitrate_array[GET_PARAM(4)], 0.7, 1.45); } virtual void BasicRateTargetingCBRTest() { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; - cfg_.rc_dropframe_thresh = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; - + SetUpCBR(); ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, 30, 1, 0, 140); const int bitrate_array[2] = { 150, 550 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) - << " The datarate for the file is lower than target by too much!"; - ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.19) - << " The datarate for the file is greater than target by too much!"; + RunBasicRateTargetingTest(&video, bitrate_array[GET_PARAM(4)], 0.85, 1.19); } #if CONFIG_LIBYUV @@ -143,10 +121,8 @@ // with the flag avif_mode_. This test upsamples a QVGA clip to the target // resolution, using libyuv for the scaling. virtual void BasicRateTargetingCBRAssertAvifModeTest() { - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; + SetUpCBR(); + cfg_.rc_dropframe_thresh = 0; ResizingVideoSource video(2456, 2054, 320, 240, "pixel_capture_w320h240.yuv", 100); const int bitrate_array[2] = { 1000, 2000 }; @@ -164,14 +140,10 @@ #endif // CONFIG_LIBYUV virtual void BasicRateTargetingCBRSpikeTest() { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_dropframe_thresh = 0; cfg_.rc_min_quantizer = 2; cfg_.rc_max_quantizer = 56; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; cfg_.kf_max_dist = 3000; cfg_.kf_min_dist = 3000; @@ -192,14 +164,10 @@ } virtual void BasicRateTargetingCBRDynamicBitrateTest() { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_dropframe_thresh = 0; cfg_.rc_min_quantizer = 2; cfg_.rc_max_quantizer = 56; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; cfg_.kf_max_dist = 3000; cfg_.kf_min_dist = 3000; @@ -226,112 +194,56 @@ virtual void BasicRateTargetingMultiThreadCBRTest() { ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1, 0, 400); - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; - cfg_.rc_dropframe_thresh = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; + SetUpCBR(); cfg_.g_threads = 4; const int bitrate_array[2] = { 250, 650 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; ResetModel(); tile_columns_ = 2; - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 0.85) - << " The datarate for the file exceeds the target by too much!"; - ASSERT_LE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 1.15) - << " The datarate for the file missed the target!" - << cfg_.rc_target_bitrate << " " << effective_datarate_; + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.85, + 1.15); } virtual void ErrorResilienceOnSceneCuts() { if (GET_PARAM(4) > 0) return; - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_dropframe_thresh = 0; cfg_.g_error_resilient = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, 30, 1, 0, 300); - cfg_.rc_target_bitrate = 500; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) - << " The datarate for the file is lower than target by too much!"; - ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.15) - << " The datarate for the file is greater than target by too much!"; + RunBasicRateTargetingTest(&video, 500, 0.85, 1.15); } virtual void BasicRateTargetingCBRPeriodicKeyFrameTest() { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; - cfg_.rc_dropframe_thresh = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; + SetUpCBR(); // Periodic keyframe cfg_.kf_max_dist = 50; ::libaom_test::I420VideoSource video("pixel_capture_w320h240.yuv", 320, 240, 30, 1, 0, 310); const int bitrate_array[2] = { 150, 550 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) - << " The datarate for the file is lower than target by too much!"; - ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.15) - << " The datarate for the file is greater than target by too much!"; + RunBasicRateTargetingTest(&video, bitrate_array[GET_PARAM(4)], 0.85, 1.15); } virtual void CBRPeriodicKeyFrameOnSceneCuts() { if (GET_PARAM(4) > 0) return; - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_dropframe_thresh = 0; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; // Periodic keyframe cfg_.kf_max_dist = 30; cfg_.kf_min_dist = 30; ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, 30, 1, 0, 300); - cfg_.rc_target_bitrate = 500; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) - << " The datarate for the file is lower than target by too much!"; - ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.3) - << " The datarate for the file is greater than target by too much!"; + RunBasicRateTargetingTest(&video, 500, 0.85, 1.3); } virtual void BasicRateTargetingAQModeOnOffCBRTest() { if (GET_PARAM(4) > 0) return; - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_dropframe_thresh = 0; cfg_.rc_min_quantizer = 2; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; - cfg_.g_lag_in_frames = 0; cfg_.g_error_resilient = 0; cfg_.g_pass = AOM_RC_ONE_PASS; cfg_.g_usage = AOM_USAGE_REALTIME; @@ -339,13 +251,7 @@ ::libaom_test::I420VideoSource video("pixel_capture_w320h240.yuv", 320, 240, 30, 1, 0, 310); - cfg_.rc_target_bitrate = 60; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) - << " The datarate for the file is lower than target by too much!"; - ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.15) - << " The datarate for the file is greater than target by too much!"; + RunBasicRateTargetingTest(&video, 60, 0.85, 1.15); } virtual void BasicRateTargeting444CBRScreenTest() { @@ -354,26 +260,13 @@ cfg_.g_profile = 1; cfg_.g_timebase = video.timebase(); - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; - cfg_.rc_dropframe_thresh = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; + SetUpCBR(); const int bitrate_array[2] = { 250, 650 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; ResetModel(); screen_mode_ = true; - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 0.85) - << " The datarate for the file exceeds the target by too much!"; - ASSERT_LE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 1.15) - << " The datarate for the file missed the target!" - << cfg_.rc_target_bitrate << " " << effective_datarate_; + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.85, + 1.15); } virtual void BasicRateTargetingSuperresCBR() { @@ -383,29 +276,15 @@ cfg_.g_profile = 0; cfg_.g_timebase = video.timebase(); - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; - cfg_.rc_dropframe_thresh = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; + SetUpCBR(); cfg_.rc_superres_mode = AOM_SUPERRES_FIXED; cfg_.rc_superres_denominator = 16; cfg_.rc_superres_kf_denominator = 16; const int bitrate_array[2] = { 250, 650 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; - ResetModel(); - ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 0.85) - << " The datarate for the file exceeds the target by too much!"; - ASSERT_LE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 1.15) - << " The datarate for the file missed the target!" - << cfg_.rc_target_bitrate << " " << effective_datarate_; + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.85, + 1.15); } virtual void BasicRateTargetingSuperresCBRMultiThreads() { @@ -415,13 +294,7 @@ cfg_.g_profile = 0; cfg_.g_timebase = video.timebase(); - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; - cfg_.rc_dropframe_thresh = 1; - cfg_.rc_min_quantizer = 0; - cfg_.rc_max_quantizer = 63; - cfg_.rc_end_usage = AOM_CBR; + SetUpCBR(); cfg_.g_threads = 2; cfg_.rc_superres_mode = AOM_SUPERRES_FIXED; @@ -429,17 +302,81 @@ cfg_.rc_superres_kf_denominator = 16; const int bitrate_array[2] = { 250, 650 }; - cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; ResetModel(); tile_columns_ = 1; + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.85, + 1.15); + } + + virtual void BasicRateTargetingQvgaCBRKf() { + ::libaom_test::I420VideoSource video("desktop1.320_180.yuv", 320, 180, 30, + 1, 0, 500); + + cfg_.g_profile = 0; + cfg_.g_timebase = video.timebase(); + + SetUpCBR(); + cfg_.kf_max_dist = 10; + cfg_.kf_min_dist = 10; + const int bitrate_array[2] = { 250, 650 }; + ResetModel(); + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.5, + 1.5); + } + + virtual void BasicRateTargetingQvga2CBRKf() { + ::libaom_test::I420VideoSource video("desktopqvga2.320_240.yuv", 320, 240, + 30, 1, 0, 500); + + cfg_.g_profile = 0; + cfg_.g_timebase = video.timebase(); + + SetUpCBR(); + cfg_.kf_max_dist = 10; + cfg_.kf_min_dist = 10; + const int bitrate_array[2] = { 250, 650 }; + ResetModel(); + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.5, + 1.5); + } + + virtual void BasicRateTargetingCifCBRKf() { + ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, + 288, 30, 1, 0, 140); + + cfg_.g_profile = 0; + cfg_.g_timebase = video.timebase(); + + SetUpCBR(); + cfg_.kf_max_dist = 10; + cfg_.kf_min_dist = 10; + const int bitrate_array[2] = { 250, 650 }; + ResetModel(); + RunBasicRateTargetingTestReversed(&video, bitrate_array[GET_PARAM(4)], 0.5, + 1.5); + } + + virtual void BasicRateTargetingVBRLagRealtime() { + ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 320, 240, 30, + 1, 0, 200); + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.g_error_resilient = 0; + cfg_.rc_end_usage = AOM_VBR; + cfg_.g_lag_in_frames = 48; + cfg_.g_pass = AOM_RC_ONE_PASS; + cfg_.g_usage = AOM_USAGE_REALTIME; + cfg_.g_profile = 0; + cfg_.g_timebase = video.timebase(); + cfg_.g_threads = 1; + + ResetModel(); + lag_realtime_mode_ = 1; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_GE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 0.85) - << " The datarate for the file exceeds the target by too much!"; - ASSERT_LE(static_cast<double>(cfg_.rc_target_bitrate), - effective_datarate_ * 1.15) - << " The datarate for the file missed the target!" - << cfg_.rc_target_bitrate << " " << effective_datarate_; + ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) + << " The datarate for the file is lower than target by too much!"; + ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 2.0) + << " The datarate for the file is greater than target by too much!"; } }; @@ -463,17 +400,12 @@ } virtual void ChangingDropFrameThreshTest() { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_undershoot_pct = 20; cfg_.rc_undershoot_pct = 20; cfg_.rc_dropframe_thresh = 10; - cfg_.rc_min_quantizer = 0; cfg_.rc_max_quantizer = 50; - cfg_.rc_end_usage = AOM_CBR; cfg_.rc_target_bitrate = 200; - cfg_.g_lag_in_frames = 0; cfg_.g_error_resilient = 1; // TODO(marpan): Investigate datarate target failures with a smaller // keyframe interval (128). @@ -594,17 +526,12 @@ } virtual void ChangingSpeedTest() { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_undershoot_pct = 20; cfg_.rc_undershoot_pct = 20; cfg_.rc_dropframe_thresh = 10; - cfg_.rc_min_quantizer = 0; cfg_.rc_max_quantizer = 50; - cfg_.rc_end_usage = AOM_CBR; cfg_.rc_target_bitrate = 200; - cfg_.g_lag_in_frames = 0; cfg_.g_error_resilient = 1; // TODO(marpan): Investigate datarate target failures with a smaller // keyframe interval (128). @@ -691,6 +618,25 @@ BasicRateTargetingSuperresCBRMultiThreads(); } +// Check basic rate targeting for QVGA CBR with short keyframe spacing. +TEST_P(DatarateTestRealtime, BasicRateTargetingQvgaCBRKf) { + BasicRateTargetingQvgaCBRKf(); +} + +// Check basic rate targeting for QVGA CBR with short keyframe spacing. +TEST_P(DatarateTestRealtime, BasicRateTargetingQvga2CBRKf) { + BasicRateTargetingQvga2CBRKf(); +} + +// Check basic rate targeting for CIF CBR with short keyframe spacing. +TEST_P(DatarateTestRealtime, BasicRateTargetingCifCBRKf) { + BasicRateTargetingCifCBRKf(); +} + +TEST_P(DatarateTestRealtime, BasicRateTargetingVBRLagRealtime) { + BasicRateTargetingVBRLagRealtime(); +} + // Check that (1) the first dropped frame gets earlier and earlier // as the drop frame threshold is increased, and (2) that the total number of // frame drops does not decrease as we increase frame drop threshold. @@ -743,16 +689,11 @@ }; TEST_P(DatarateTestSetFrameQpRealtime, SetFrameQpOnePass) { - cfg_.rc_buf_initial_sz = 500; - cfg_.rc_buf_optimal_sz = 500; - cfg_.rc_buf_sz = 1000; + SetUpCBR(); cfg_.rc_undershoot_pct = 20; cfg_.rc_undershoot_pct = 20; - cfg_.rc_min_quantizer = 0; cfg_.rc_max_quantizer = 50; - cfg_.rc_end_usage = AOM_CBR; cfg_.rc_target_bitrate = 200; - cfg_.g_lag_in_frames = 0; cfg_.g_error_resilient = 1; cfg_.kf_max_dist = 9999; cfg_.rc_dropframe_thresh = 0; @@ -829,12 +770,12 @@ AV1_INSTANTIATE_TEST_SUITE(DatarateTestRealtime, ::testing::Values(::libaom_test::kRealTime), - ::testing::Range(7, 12), ::testing::Values(0, 3), + ::testing::Range(6, 12), ::testing::Values(0, 3), ::testing::Values(0, 1)); AV1_INSTANTIATE_TEST_SUITE(DatarateTestFrameDropRealtime, ::testing::Values(::libaom_test::kRealTime), - ::testing::Range(7, 12), ::testing::Values(0, 3)); + ::testing::Range(6, 12), ::testing::Values(0, 3)); AV1_INSTANTIATE_TEST_SUITE(DatarateTestSpeedChangeRealtime, ::testing::Values(::libaom_test::kRealTime),
diff --git a/test/datarate_test.h b/test/datarate_test.h index 9064d1d..9b91cb4 100644 --- a/test/datarate_test.h +++ b/test/datarate_test.h
@@ -30,6 +30,17 @@ protected: ~DatarateTest() override = default; + virtual void SetUpCBR() { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_dropframe_thresh = 1; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.rc_end_usage = AOM_CBR; + cfg_.g_lag_in_frames = 0; + } + virtual void ResetModel() { last_pts_ = 0; bits_in_buffer_model_ = cfg_.rc_target_bitrate * cfg_.rc_buf_initial_sz; @@ -58,6 +69,7 @@ effective_datarate_dynamic_[i] = 0.0; } avif_mode_ = 0; + lag_realtime_mode_ = 0; } void PreEncodeFrameHook(::libaom_test::VideoSource *video, @@ -106,6 +118,12 @@ encoder->Control(AOME_SET_CQ_LEVEL, 0); encoder->Control(AV1E_SET_AQ_MODE, (aq_mode_ > 0) ? 1 : 0); } + if (lag_realtime_mode_) { + encoder->Control(AV1E_SET_MIN_GF_INTERVAL, 16); + encoder->Control(AV1E_SET_MAX_GF_INTERVAL, 16); + encoder->Control(AV1E_SET_GF_MAX_PYRAMID_HEIGHT, 1); + encoder->Control(AV1E_SET_GF_MIN_PYRAMID_HEIGHT, 1); + } } if (speed_change_test_) { @@ -211,6 +229,33 @@ } } + void RunBasicRateTargetingTest(::libaom_test::VideoSource *video, + const int bitrate, double low_rate_err_limit, + double high_rate_err_limit) { + cfg_.rc_target_bitrate = bitrate; + ResetModel(); + ASSERT_NO_FATAL_FAILURE(RunLoop(video)); + ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * low_rate_err_limit) + << " The datarate for the file is lower than target by too much!"; + ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * high_rate_err_limit) + << " The datarate for the file is greater than target by too much!"; + } + + void RunBasicRateTargetingTestReversed(::libaom_test::VideoSource *video, + const int bitrate, + double low_rate_err_limit, + double high_rate_err_limit) { + cfg_.rc_target_bitrate = bitrate; + ASSERT_NO_FATAL_FAILURE(RunLoop(video)); + ASSERT_GE(static_cast<double>(cfg_.rc_target_bitrate), + effective_datarate_ * low_rate_err_limit) + << " The datarate for the file exceeds the target by too much!"; + ASSERT_LE(static_cast<double>(cfg_.rc_target_bitrate), + effective_datarate_ * high_rate_err_limit) + << " The datarate for the file missed the target!" + << cfg_.rc_target_bitrate << " " << effective_datarate_; + } + aom_codec_pts_t last_pts_; double timebase_; int frame_number_; // Counter for number of non-dropped/encoded frames. @@ -244,6 +289,7 @@ int64_t bits_total_dynamic_[3]; int frame_number_dynamic_[3]; int avif_mode_; + int lag_realtime_mode_; }; } // namespace
diff --git a/test/decode_frame_size_limit_test.cc b/test/decode_frame_size_limit_test.cc new file mode 100644 index 0000000..11d4921 --- /dev/null +++ b/test/decode_frame_size_limit_test.cc
@@ -0,0 +1,107 @@ +/* + * Copyright (c) 2026, 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 <ostream> + +#include "gtest/gtest.h" +#include "test/codec_factory.h" +#include "test/decode_test_driver.h" +#include "test/ivf_video_source.h" +#include "test/util.h" +#include "test/video_source.h" + +namespace { + +struct DecodeParam { + const char *filename; + unsigned int width; + unsigned int height; +}; + +std::ostream &operator<<(std::ostream &os, const DecodeParam &dp) { + return os << "file: " << dp.filename; +} + +class DecodeFrameSizeLimitTest + : public ::libaom_test::DecoderTest, + public ::libaom_test::CodecTestWithParam<DecodeParam> { + protected: + DecodeFrameSizeLimitTest() + : DecoderTest(GET_PARAM(0)), width_(GET_PARAM(1).width), + height_(GET_PARAM(1).height) {} + + ~DecodeFrameSizeLimitTest() override = default; + + void PreDecodeFrameHook(const libaom_test::CompressedVideoSource &video, + libaom_test::Decoder *decoder) override { + if (video.frame_number() == 0) + decoder->Control(AOMD_SET_FRAME_SIZE_LIMIT, frame_size_limit_); + } + + void DecompressedFrameHook(const aom_image_t &img, + const unsigned int /*frame_number*/) override { + EXPECT_EQ(img.d_w, width_); + EXPECT_EQ(img.d_h, height_); + } + + bool HandleDecodeResult(const aom_codec_err_t res_dec, + const libaom_test::CompressedVideoSource & /*video*/, + libaom_test::Decoder * /*decoder*/) override { + bool expect_failure = + frame_size_limit_ && width_ * height_ > frame_size_limit_; + if (expect_failure) { + EXPECT_EQ(res_dec, AOM_CODEC_CORRUPT_FRAME); + } else { + EXPECT_EQ(res_dec, AOM_CODEC_OK); + } + + return !HasFailure(); + } + + void RunTest() { + const DecodeParam input = GET_PARAM(1); + aom_codec_dec_cfg_t cfg = { 1, 0, 0, !FORCE_HIGHBITDEPTH_DECODING }; + libaom_test::IVFVideoSource decode_video(input.filename); + decode_video.Init(); + + ASSERT_NO_FATAL_FAILURE(RunLoop(&decode_video, cfg)); + } + + protected: + unsigned int frame_size_limit_ = 0; + + private: + unsigned int width_; + unsigned int height_; +}; + +TEST_P(DecodeFrameSizeLimitTest, Unlimited) { RunTest(); } + +TEST_P(DecodeFrameSizeLimitTest, LimitedBig) { + frame_size_limit_ = 226 * 210; + RunTest(); +} + +TEST_P(DecodeFrameSizeLimitTest, LimitedSmall) { + frame_size_limit_ = 226 * 210 - 1; + RunTest(); +} + +const DecodeParam kAV1DecodeFrameSizeLimitTests[] = { + // { filename, width, height } + { "av1-1-b8-01-size-16x16.ivf", 16, 16 }, + { "av1-1-b8-01-size-226x210.ivf", 226, 210 }, +}; + +AV1_INSTANTIATE_TEST_SUITE(DecodeFrameSizeLimitTest, + ::testing::ValuesIn(kAV1DecodeFrameSizeLimitTests)); + +} // namespace
diff --git a/test/decode_perf_test.cc b/test/decode_perf_test.cc index 14b0f9e..c751678 100644 --- a/test/decode_perf_test.cc +++ b/test/decode_perf_test.cc
@@ -37,7 +37,7 @@ /* DecodePerfTest takes a tuple of filename + number of threads to decode with */ -typedef std::tuple<const char *, unsigned> DecodePerfParam; +using DecodePerfParam = std::tuple<const char *, unsigned int>; // TODO(jimbankoski): Add actual test vectors here when available. // const DecodePerfParam kAV1DecodePerfVectors[] = {};
diff --git a/test/dr_prediction_test.cc b/test/dr_prediction_test.cc index de90ec7..4e4a06b 100644 --- a/test/dr_prediction_test.cc +++ b/test/dr_prediction_test.cc
@@ -50,19 +50,19 @@ using libaom_test::ACMRandom; -typedef void (*DrPred_Hbd)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, - const uint16_t *above, const uint16_t *left, - int upsample_above, int upsample_left, int dx, - int dy, int bd); +using DrPred_Hbd = void (*)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, + const uint16_t *above, const uint16_t *left, + int upsample_above, int upsample_left, int dx, + int dy, int bd); -typedef void (*DrPred)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, - const uint8_t *above, const uint8_t *left, - int upsample_above, int upsample_left, int dx, int dy, - int bd); +using DrPred = void (*)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, + const uint8_t *above, const uint8_t *left, + int upsample_above, int upsample_left, int dx, int dy, + int bd); -typedef void (*Z1_Lbd)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, - const uint8_t *above, const uint8_t *left, - int upsample_above, int dx, int dy); +using Z1_Lbd = void (*)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, + const uint8_t *above, const uint8_t *left, + int upsample_above, int dx, int dy); template <Z1_Lbd fn> void z1_wrapper(uint8_t *dst, ptrdiff_t stride, int bw, int bh, const uint8_t *above, const uint8_t *left, int upsample_above, @@ -72,9 +72,9 @@ fn(dst, stride, bw, bh, above, left, upsample_above, dx, dy); } -typedef void (*Z2_Lbd)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, - const uint8_t *above, const uint8_t *left, - int upsample_above, int upsample_left, int dx, int dy); +using Z2_Lbd = void (*)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, + const uint8_t *above, const uint8_t *left, + int upsample_above, int upsample_left, int dx, int dy); template <Z2_Lbd fn> void z2_wrapper(uint8_t *dst, ptrdiff_t stride, int bw, int bh, const uint8_t *above, const uint8_t *left, int upsample_above, @@ -84,9 +84,9 @@ fn(dst, stride, bw, bh, above, left, upsample_above, upsample_left, dx, dy); } -typedef void (*Z3_Lbd)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, - const uint8_t *above, const uint8_t *left, - int upsample_left, int dx, int dy); +using Z3_Lbd = void (*)(uint8_t *dst, ptrdiff_t stride, int bw, int bh, + const uint8_t *above, const uint8_t *left, + int upsample_left, int dx, int dy); template <Z3_Lbd fn> void z3_wrapper(uint8_t *dst, ptrdiff_t stride, int bw, int bh, const uint8_t *above, const uint8_t *left, int upsample_above, @@ -96,9 +96,9 @@ fn(dst, stride, bw, bh, above, left, upsample_left, dx, dy); } -typedef void (*Z1_Hbd)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, - const uint16_t *above, const uint16_t *left, - int upsample_above, int dx, int dy, int bd); +using Z1_Hbd = void (*)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, + const uint16_t *above, const uint16_t *left, + int upsample_above, int dx, int dy, int bd); template <Z1_Hbd fn> void z1_wrapper_hbd(uint16_t *dst, ptrdiff_t stride, int bw, int bh, const uint16_t *above, const uint16_t *left, @@ -109,10 +109,10 @@ fn(dst, stride, bw, bh, above, left, upsample_above, dx, dy, bd); } -typedef void (*Z2_Hbd)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, - const uint16_t *above, const uint16_t *left, - int upsample_above, int upsample_left, int dx, int dy, - int bd); +using Z2_Hbd = void (*)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, + const uint16_t *above, const uint16_t *left, + int upsample_above, int upsample_left, int dx, int dy, + int bd); template <Z2_Hbd fn> void z2_wrapper_hbd(uint16_t *dst, ptrdiff_t stride, int bw, int bh, const uint16_t *above, const uint16_t *left, @@ -123,9 +123,9 @@ bd); } -typedef void (*Z3_Hbd)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, - const uint16_t *above, const uint16_t *left, - int upsample_left, int dx, int dy, int bd); +using Z3_Hbd = void (*)(uint16_t *dst, ptrdiff_t stride, int bw, int bh, + const uint16_t *above, const uint16_t *left, + int upsample_left, int dx, int dy, int bd); template <Z3_Hbd fn> void z3_wrapper_hbd(uint16_t *dst, ptrdiff_t stride, int bw, int bh, const uint16_t *above, const uint16_t *left,
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc index bccbf30..0dd0af1 100644 --- a/test/encode_api_test.cc +++ b/test/encode_api_test.cc
@@ -9,12 +9,14 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <array> #include <cassert> #include <climits> #include <cmath> #include <cstdint> #include <cstdlib> #include <cstring> +#include <memory> #include <tuple> #include "gtest/gtest.h" @@ -25,6 +27,11 @@ #include "aom/aom_encoder.h" #include "aom/aom_image.h" +#include "test/codec_factory.h" +#include "test/encode_test_driver.h" +#include "test/util.h" +#include "test/video_source.h" + namespace { #if CONFIG_REALTIME_ONLY @@ -185,19 +192,99 @@ EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc)); } -TEST(EncodeAPI, TuneIqNotAllIntra) { - aom_codec_iface_t *iface = aom_codec_av1_cx(); +TEST(EncodeAPI, InvalidUVStrides) { + static constexpr std::array<aom_img_fmt_t, 9> kAv1ImageFormats = { + AOM_IMG_FMT_YV12, AOM_IMG_FMT_I420, AOM_IMG_FMT_I422, + AOM_IMG_FMT_I444, AOM_IMG_FMT_NV12, AOM_IMG_FMT_I42016, + AOM_IMG_FMT_YV1216, AOM_IMG_FMT_I42216, AOM_IMG_FMT_I44416 + }; + struct UVStride { + int u_stride; + int v_stride; + }; + constexpr int kWidth = 64; + constexpr int kHeight = 64; + static constexpr std::array<UVStride, 4> kUVStrides = { + UVStride{ kWidth, kWidth - 1 }, UVStride{ kWidth, kWidth + 1 }, + UVStride{ kWidth - 1, kWidth }, UVStride{ kWidth + 1, kWidth } + }; + aom_image_t img; + aom_codec_ctx_t enc; aom_codec_enc_cfg_t cfg; - ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME), + // Allocate a buffer large enough for a non-subsampled, high bitdepth image. + auto buf = std::make_unique<uint8_t[]>(kWidth * kHeight * 3 * 2); + + ASSERT_EQ(aom_codec_enc_config_default(aom_codec_av1_cx(), &cfg, + /*usage=*/AOM_USAGE_REALTIME), AOM_CODEC_OK); - aom_codec_ctx_t enc; - ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); + for (const auto img_fmt : kAv1ImageFormats) { + const bool high_bit_depth = + (img_fmt & AOM_IMG_FMT_HIGHBITDEPTH) == AOM_IMG_FMT_HIGHBITDEPTH; + if (high_bit_depth && !(aom_codec_get_caps(aom_codec_av1_cx()) & + AOM_CODEC_CAP_HIGHBITDEPTH)) { + break; + } + ASSERT_EQ(aom_img_wrap(&img, img_fmt, kWidth, kHeight, /*stride_align=*/1, + buf.get()), + &img) + << "Unable to wrap aom_image for format: " << img_fmt; + const bool is_444 = (img.x_chroma_shift == 0 && img.y_chroma_shift == 0); + const bool is_422 = (img.x_chroma_shift == 1 && img.y_chroma_shift == 0); + // 4:4:4 is allowed in profile 1, 4:2:2 in profile 2. + cfg.g_profile = is_444 + 2 * is_422; + cfg.g_w = kWidth; + cfg.g_h = kHeight; + cfg.g_bit_depth = high_bit_depth ? AOM_BITS_10 : AOM_BITS_8; + // Monochrome is not allowed in profile 1. + const unsigned int max_monochrome = (cfg.g_profile == 1) ? 0u : 1u; + for (cfg.monochrome = 0; cfg.monochrome <= max_monochrome; + ++cfg.monochrome) { + ASSERT_EQ( + aom_codec_enc_init(&enc, aom_codec_av1_cx(), &cfg, + high_bit_depth ? AOM_CODEC_USE_HIGHBITDEPTH : 0), + AOM_CODEC_OK) + << " high_bit_depth: " << high_bit_depth; - ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_SSIMULACRA2), - AOM_CODEC_INCAPABLE); + for (const auto uv_stride : kUVStrides) { + const UVStride orig = { img.stride[AOM_PLANE_U], + img.stride[AOM_PLANE_V] }; + img.stride[AOM_PLANE_U] = uv_stride.u_stride; + img.stride[AOM_PLANE_V] = + (img_fmt == AOM_IMG_FMT_NV12) ? 0 : uv_stride.v_stride; + img.monochrome = cfg.monochrome; + // Monochrome should ignore the U and V planes and NV12 only sets one + // stride value, they should always succeed. The AOM* aom_img_fmt_t + // variants are unsupported by the encoder. + const aom_codec_err_t expected_err = + (cfg.monochrome || img_fmt == AOM_IMG_FMT_NV12) + ? AOM_CODEC_OK + : AOM_CODEC_INVALID_PARAM; + EXPECT_EQ(aom_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1, + /*flags=*/0), + expected_err) + << "Error: " << aom_codec_error_detail(&enc) + << ", format: " << img_fmt << ", U stride: " << uv_stride.u_stride + << ", V stride: " << uv_stride.v_stride; - ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); + // Ensure the encoder can recover when given valid strides. + img.stride[AOM_PLANE_U] = orig.u_stride; + img.stride[AOM_PLANE_V] = orig.v_stride; + EXPECT_EQ(aom_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1, + /*flags=*/0), + AOM_CODEC_OK) + << "Error: " << aom_codec_error_detail(&enc) + << ", format: " << img_fmt << ", U stride: " << orig.u_stride + << ", V stride: " << orig.v_stride; + } + + EXPECT_EQ( + aom_codec_encode(&enc, /*img=*/nullptr, /*pts=*/0, /*duration=*/0, + /*flags=*/0), + AOM_CODEC_OK); + EXPECT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); + } + } } void EncodeSetSFrameOnFirstFrame(aom_img_fmt fmt, aom_codec_flags_t flag) { @@ -1367,6 +1454,521 @@ } #endif // !CONFIG_REALTIME_ONLY +TEST(EncodeAPI, FreezeInternalState) { + aom_codec_iface_t *iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, kUsage), AOM_CODEC_OK); + cfg.g_w = 176; + cfg.g_h = 144; + cfg.rc_target_bitrate = 200; + cfg.g_lag_in_frames = 0; // Needed for single frame updates + + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, AOM_CODEC_USE_PSNR), + AOM_CODEC_OK); + + aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); + ASSERT_NE(image, nullptr); + + // Encode Frame A (Keyframe) + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/0, /*duration=*/1, + /*flags=*/AOM_EFLAG_FORCE_KF), + AOM_CODEC_OK); + + aom_codec_iter_t iter = nullptr; + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + // Drain packets + } + + std::vector<uint8_t> bitstream1; + bool psnr1 = false; + + // Encode Frame B with freeze flag + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/1, /*duration=*/1, + /*flags=*/AOM_EFLAG_FREEZE_INTERNAL_STATE | + AOM_EFLAG_CALCULATE_PSNR), + AOM_CODEC_OK); + iter = nullptr; + const aom_codec_cx_pkt_t *pkt; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + bitstream1.assign((uint8_t *)pkt->data.frame.buf, + (uint8_t *)pkt->data.frame.buf + pkt->data.frame.sz); + } else if (pkt->kind == AOM_CODEC_PSNR_PKT) { + psnr1 = true; + } + } + EXPECT_TRUE(psnr1); + EXPECT_FALSE(bitstream1.empty()); + + std::vector<uint8_t> bitstream2; + bool psnr2 = false; + + // Encode Frame B again without freeze flag + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/1, /*duration=*/1, + /*flags=*/AOM_EFLAG_CALCULATE_PSNR), + AOM_CODEC_OK); + iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + bitstream2.assign((uint8_t *)pkt->data.frame.buf, + (uint8_t *)pkt->data.frame.buf + pkt->data.frame.sz); + } else if (pkt->kind == AOM_CODEC_PSNR_PKT) { + psnr2 = true; + } + } + EXPECT_TRUE(psnr2); + EXPECT_FALSE(bitstream2.empty()); + + // Bitstreams should be identical + EXPECT_EQ(bitstream1, bitstream2); + + // Encode one more frame for good measure. + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/2, /*duration=*/1, + /*flags=*/0), + AOM_CODEC_OK); + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + // Drain packets + } + + aom_img_free(image); + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} + +TEST(EncodeAPI, FreezeInternalStateSVC) { + aom_codec_iface_t *iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME), + AOM_CODEC_OK); + cfg.g_w = 176; + cfg.g_h = 144; + cfg.rc_target_bitrate = 300; + cfg.g_lag_in_frames = 0; + cfg.rc_end_usage = AOM_CBR; + + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, AOM_CODEC_USE_PSNR), + AOM_CODEC_OK); + + ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 7), AOM_CODEC_OK); + + aom_svc_params_t svc_params = {}; + svc_params.number_spatial_layers = 2; + svc_params.number_temporal_layers = 1; + svc_params.max_quantizers[0] = 56; + svc_params.min_quantizers[0] = 10; + svc_params.max_quantizers[1] = 56; + svc_params.min_quantizers[1] = 10; + svc_params.scaling_factor_num[0] = 1; + svc_params.scaling_factor_den[0] = 2; + svc_params.scaling_factor_num[1] = 1; + svc_params.scaling_factor_den[1] = 1; + svc_params.layer_target_bitrate[0] = cfg.rc_target_bitrate * 2 / 3; + svc_params.layer_target_bitrate[1] = cfg.rc_target_bitrate; + svc_params.framerate_factor[0] = 1; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_PARAMS, &svc_params), + AOM_CODEC_OK); + + aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); + ASSERT_NE(image, nullptr); + + aom_svc_layer_id_t layer_id = {}; + aom_svc_ref_frame_config_t ref_frame_config = {}; + + // Encode SL0 - Keyframe + layer_id.spatial_layer_id = 0; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/0, /*duration=*/1, + /*flags=*/AOM_EFLAG_FORCE_KF), + AOM_CODEC_OK); + + aom_codec_iter_t iter = nullptr; + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + } // Drain + + // Encode SL1 - Delta Frame with Freeze + layer_id.spatial_layer_id = 1; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ref_frame_config.refresh[0] = 1; + ref_frame_config.reference[0] = 1; + ref_frame_config.ref_idx[0] = 0; + ASSERT_EQ( + aom_codec_control(&enc, AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config), + AOM_CODEC_OK); + + std::vector<uint8_t> bitstream1; + bool psnr1 = false; + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/1, /*duration=*/1, + /*flags=*/AOM_EFLAG_FREEZE_INTERNAL_STATE | + AOM_EFLAG_CALCULATE_PSNR), + AOM_CODEC_OK); + iter = nullptr; + const aom_codec_cx_pkt_t *pkt; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + bitstream1.assign((uint8_t *)pkt->data.frame.buf, + (uint8_t *)pkt->data.frame.buf + pkt->data.frame.sz); + } else if (pkt->kind == AOM_CODEC_PSNR_PKT) { + psnr1 = true; + } + } + EXPECT_TRUE(psnr1); + EXPECT_FALSE(bitstream1.empty()); + + // Encode SL1 - Delta Frame again without Freeze + std::vector<uint8_t> bitstream2; + bool psnr2 = false; + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/1, /*duration=*/1, + /*flags=*/AOM_EFLAG_CALCULATE_PSNR), + AOM_CODEC_OK); + iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + bitstream2.assign((uint8_t *)pkt->data.frame.buf, + (uint8_t *)pkt->data.frame.buf + pkt->data.frame.sz); + } else if (pkt->kind == AOM_CODEC_PSNR_PKT) { + psnr2 = true; + } + } + EXPECT_TRUE(psnr2); + EXPECT_FALSE(bitstream2.empty()); + + EXPECT_EQ(bitstream1, bitstream2); + + // Encode one more frame for good measure. + layer_id.spatial_layer_id = 0; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ref_frame_config.refresh[0] = 1; + ref_frame_config.reference[0] = 1; + ref_frame_config.ref_idx[0] = 0; + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/2, /*duration=*/1, + /*flags=*/0), + AOM_CODEC_OK); + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + // Drain packets + } + + aom_img_free(image); + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} + +TEST(EncodeAPI, FreezeInternalStateTemporalLayers) { + aom_codec_iface_t *iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME), + AOM_CODEC_OK); + cfg.g_w = 176; + cfg.g_h = 144; + cfg.rc_target_bitrate = 300; + cfg.g_lag_in_frames = 0; + cfg.rc_end_usage = AOM_CBR; + + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, AOM_CODEC_USE_PSNR), + AOM_CODEC_OK); + + ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 7), AOM_CODEC_OK); + + aom_svc_params_t svc_params = {}; + svc_params.number_spatial_layers = 1; + svc_params.number_temporal_layers = 2; + svc_params.max_quantizers[0] = 56; + svc_params.min_quantizers[0] = 10; + svc_params.max_quantizers[1] = 56; + svc_params.min_quantizers[1] = 10; + svc_params.scaling_factor_num[0] = 1; + svc_params.scaling_factor_den[0] = 1; + svc_params.layer_target_bitrate[0] = cfg.rc_target_bitrate * 2 / 3; + svc_params.layer_target_bitrate[1] = cfg.rc_target_bitrate; + svc_params.framerate_factor[0] = 1; + svc_params.framerate_factor[1] = 2; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_PARAMS, &svc_params), + AOM_CODEC_OK); + + aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); + ASSERT_NE(image, nullptr); + + aom_svc_layer_id_t layer_id = {}; + aom_svc_ref_frame_config_t ref_frame_config = {}; + + // Encode a Keyframe. + layer_id.spatial_layer_id = 0; + layer_id.temporal_layer_id = 0; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/0, /*duration=*/1, + /*flags=*/AOM_EFLAG_FORCE_KF), + AOM_CODEC_OK); + + aom_codec_iter_t iter = nullptr; + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + } // Drain + + // Encode a TL1 - Delta Frame + layer_id.temporal_layer_id = 1; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ref_frame_config.refresh[0] = 0; + ref_frame_config.reference[0] = 1; + ref_frame_config.ref_idx[0] = 0; + ASSERT_EQ( + aom_codec_control(&enc, AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config), + AOM_CODEC_OK); + + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/1, /*duration=*/1, + /*flags=*/0), + AOM_CODEC_OK); + + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + } // Drain + + // Encode a TL0 - Delta Frame, with frozen state. + layer_id.temporal_layer_id = 0; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ref_frame_config.refresh[0] = 1; + ref_frame_config.reference[0] = 1; + ref_frame_config.ref_idx[0] = 0; + + std::vector<uint8_t> bitstream1; + bool psnr1 = false; + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/2, /*duration=*/1, + /*flags=*/AOM_EFLAG_FREEZE_INTERNAL_STATE | + AOM_EFLAG_CALCULATE_PSNR), + AOM_CODEC_OK); + iter = nullptr; + const aom_codec_cx_pkt_t *pkt; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + bitstream1.assign((uint8_t *)pkt->data.frame.buf, + (uint8_t *)pkt->data.frame.buf + pkt->data.frame.sz); + } else if (pkt->kind == AOM_CODEC_PSNR_PKT) { + psnr1 = true; + } + } + EXPECT_TRUE(psnr1); + EXPECT_FALSE(bitstream1.empty()); + + // Encode TL1 - Delta Frame again without frozen state. + std::vector<uint8_t> bitstream2; + bool psnr2 = false; + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/2, /*duration=*/1, + /*flags=*/AOM_EFLAG_CALCULATE_PSNR), + AOM_CODEC_OK); + iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + bitstream2.assign((uint8_t *)pkt->data.frame.buf, + (uint8_t *)pkt->data.frame.buf + pkt->data.frame.sz); + } else if (pkt->kind == AOM_CODEC_PSNR_PKT) { + psnr2 = true; + } + } + EXPECT_TRUE(psnr2); + EXPECT_FALSE(bitstream2.empty()); + + EXPECT_EQ(bitstream1, bitstream2); + + // Encode one more frame for good measure. + layer_id.temporal_layer_id = 1; + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id), + AOM_CODEC_OK); + ref_frame_config.refresh[0] = 0; + ref_frame_config.reference[0] = 1; + ref_frame_config.ref_idx[0] = 0; + ASSERT_EQ(aom_codec_encode(&enc, image, /*pts=*/3, /*duration=*/1, + /*flags=*/0), + AOM_CODEC_OK); + while (aom_codec_get_cx_data(&enc, &iter) != nullptr) { + // Drain packets + } + + aom_img_free(image); + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} + +TEST(EncodeAPI, FreezeInternalStateNotAllowedWithNonZeroLag) { + aom_codec_iface_t *iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, kUsage), AOM_CODEC_OK); + cfg.g_w = 176; + cfg.g_h = 144; + cfg.rc_target_bitrate = 200; + cfg.g_lag_in_frames = + 1; // Not supported with AOM_EFLAG_FREEZE_INTERNAL_STATE. + + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); + + aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); + ASSERT_NE(image, nullptr); + + ASSERT_EQ(aom_codec_encode( + &enc, image, /*pts=*/0, /*duration=*/1, + /*flags=*/AOM_EFLAG_FREEZE_INTERNAL_STATE | AOM_EFLAG_FORCE_KF), + AOM_CODEC_INCAPABLE); + + aom_img_free(image); + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} + +#if !CONFIG_REALTIME_ONLY +// This test is based on the issue:449376308. The segfault in +// av1_update_layer_context_change_config() is triggered +// by doing svc with nonzero lag_in_frames and good_quality usage. +// Note good_quality mode is needed because for realtime mode lag_in_frames +// is forced to 0 in set_encoder_config() . +TEST(EncodeAPI, Issue449376308) { + aom_codec_iface_t *iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_GOOD_QUALITY), + AOM_CODEC_OK); + cfg.g_w = 320; + cfg.g_h = 240; + cfg.g_timebase.num = 1; + cfg.g_timebase.den = 30; + cfg.rc_target_bitrate = 1000; + cfg.g_lag_in_frames = 25; + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); + // AV1E_SET_SVC_PARAMS + aom_svc_params_t svc_params = {}; + svc_params.number_spatial_layers = 3; + svc_params.number_temporal_layers = 2; + for (int i = 0; i < AOM_MAX_LAYERS; i++) { + svc_params.max_quantizers[i] = 30; + svc_params.min_quantizers[i] = 0; + svc_params.layer_target_bitrate[i] = 1000; + } + for (int i = 0; i < AOM_MAX_SS_LAYERS; i++) { + svc_params.scaling_factor_num[i] = 1; + svc_params.scaling_factor_den[i] = 1; + } + for (int i = 0; i < AOM_MAX_TS_LAYERS; i++) { + svc_params.framerate_factor[i] = 1; + } + // set_svc_params should fail since lag_in_frames > 0. + EXPECT_NE(aom_codec_control(&enc, AV1E_SET_SVC_PARAMS, &svc_params), + AOM_CODEC_OK); + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} +#endif + +// This test is based on the issue:449341177. The issue occurs in the +// codec_destroy after invalid params (quantizer out of range) are passed +// to the set_svc_params control. The issue can occur for realtime mode +// with lag_in_frames = 0. +TEST(EncodeAPI, Issue449341177) { + aom_codec_iface_t *iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME), + AOM_CODEC_OK); + cfg.g_w = 320; + cfg.g_h = 240; + cfg.rc_target_bitrate = 500; + cfg.g_timebase.num = 1; + cfg.g_timebase.den = 30; + cfg.rc_end_usage = AOM_CBR; + cfg.g_lag_in_frames = 0; + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); + // AV1E_SET_SVC_PARAMS + aom_svc_params_t svc_params = {}; + svc_params.number_spatial_layers = 3; + svc_params.number_temporal_layers = 2; + for (int i = 0; i < AOM_MAX_LAYERS; i++) { + // Set quantizer out of range. + svc_params.max_quantizers[i] = 80; + svc_params.min_quantizers[i] = 0; + svc_params.layer_target_bitrate[i] = 1000; + } + for (int i = 0; i < AOM_MAX_SS_LAYERS; i++) { + svc_params.scaling_factor_num[i] = 1; + svc_params.scaling_factor_den[i] = 1; + } + for (int i = 0; i < AOM_MAX_TS_LAYERS; i++) { + svc_params.framerate_factor[i] = 1; + } + // set_svc_params should fail because svc_params.max_quantizer[i] is set out + // of range. + EXPECT_NE(aom_codec_control(&enc, AV1E_SET_SVC_PARAMS, &svc_params), + AOM_CODEC_OK); + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} + +// This test is based on the issue:471095598. For profile 2 444 12 bit, +// assert is triggered on first frame in intra_mode_search.c, the function +// av1_count_colors_highbd, for good quality mode. +// Disabled until the assert issue is fixed. +TEST(EncodeAPI, DISABLED_Issue471095598) { + aom_codec_iface_t *encoder_iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + ASSERT_EQ( + aom_codec_enc_config_default(encoder_iface, &cfg, AOM_USAGE_GOOD_QUALITY), + AOM_CODEC_OK); + cfg.g_profile = 2; + cfg.g_bit_depth = AOM_BITS_12; + cfg.g_lag_in_frames = 0; + aom_codec_ctx_t encoder; + ASSERT_EQ(aom_codec_enc_init(&encoder, encoder_iface, &cfg, + AOM_CODEC_USE_HIGHBITDEPTH), + AOM_CODEC_OK); + aom_image_t *img = CreateGrayImage(AOM_IMG_FMT_I44416, cfg.g_w, cfg.g_h); + ASSERT_EQ(aom_codec_encode(&encoder, img, 0, 1, 0), AOM_CODEC_OK); + aom_img_free(img); + ASSERT_EQ(aom_codec_destroy(&encoder), AOM_CODEC_OK); +} + +#if !CONFIG_REALTIME_ONLY +class GetGopInfoTest : public ::libaom_test::EncoderTest, + public ::testing::Test { + protected: + GetGopInfoTest() : EncoderTest(&::libaom_test::kAV1) {} + ~GetGopInfoTest() override = default; + + void SetUp() override { + InitializeConfig(::libaom_test::kTwoPassGood); + cfg_.g_w = 176; + cfg_.g_h = 144; + cfg_.rc_target_bitrate = 200; + cfg_.g_lag_in_frames = 25; + cfg_.g_limit = kFrameLimit; + } + + void PreEncodeFrameHook(::libaom_test::VideoSource *video, + ::libaom_test::Encoder *encoder) override { + if (video->frame() == 0) { + encoder->Control(AOME_SET_CPUUSED, 3); + } + } + + void PostEncodeFrameHook(::libaom_test::Encoder *encoder) override { + if (cfg_.g_pass == AOM_RC_FIRST_PASS) return; + encoder->Control(AV1E_GET_GOP_INFO, &gop_info_); + } + + void FramePktHook(const aom_codec_cx_pkt_t * /*pkt*/) override { + // This is verified here (not in PostEncodeFrameHook) because + // PostEncodeFrameHook is also called when encoder is reading frames into + // lookahead buffer, when GOP structure hasn't been determined. + ASSERT_GT(gop_info_.gop_size, 0); + } + + aom_gop_info_t gop_info_; + static constexpr int kFrameLimit = 10; +}; + +TEST_F(GetGopInfoTest, GetGopInfo) { + ::libaom_test::RandomVideoSource video; + video.SetSize(cfg_.g_w, cfg_.g_h); + video.set_limit(kFrameLimit); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} +#endif // !CONFIG_REALTIME_ONLY + TEST(EncodeAPI, SizeAlignOverflow) { aom_codec_iface_t *iface = aom_codec_av1_cx(); aom_codec_enc_cfg_t cfg;
diff --git a/test/encode_test_driver.cc b/test/encode_test_driver.cc index 6ad10bd..6d42f4b 100644 --- a/test/encode_test_driver.cc +++ b/test/encode_test_driver.cc
@@ -196,7 +196,7 @@ ASSERT_NO_FATAL_FAILURE(video->Begin()); encoder->InitEncoder(video); - if (mode_ == kRealTime) { + if (mode_ == kRealTime && cfg_.g_lag_in_frames == 0) { encoder->Control(AOME_SET_ENABLEAUTOALTREF, 0); }
diff --git a/test/encode_test_driver.h b/test/encode_test_driver.h index a589ae2..a05d645 100644 --- a/test/encode_test_driver.h +++ b/test/encode_test_driver.h
@@ -159,7 +159,16 @@ const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); } -#endif + void Control(int ctrl_id, aom_rc_funcs_t *arg) { + const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); + ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); + } + + void Control(int ctrl_id, aom_gop_info_t *arg) { + const aom_codec_err_t res = aom_codec_control(&encoder_, ctrl_id, arg); + ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError(); + } +#endif // CONFIG_AV1_ENCODER void SetOption(const char *name, const char *value) { const aom_codec_err_t res = aom_codec_set_option(&encoder_, name, value);
diff --git a/test/encodetxb_test.cc b/test/encodetxb_test.cc index 55047e7..1881191 100644 --- a/test/encodetxb_test.cc +++ b/test/encodetxb_test.cc
@@ -32,11 +32,11 @@ namespace { using libaom_test::ACMRandom; -typedef void (*GetNzMapContextsFunc)(const uint8_t *const levels, - const int16_t *const scan, - const uint16_t eob, const TX_SIZE tx_size, - const TX_CLASS tx_class, - int8_t *const coeff_contexts); +using GetNzMapContextsFunc = void (*)(const uint8_t *const levels, + const int16_t *const scan, + const uint16_t eob, const TX_SIZE tx_size, + const TX_CLASS tx_class, + int8_t *const coeff_contexts); class EncodeTxbTest : public ::testing::TestWithParam<GetNzMapContextsFunc> { public: @@ -202,11 +202,11 @@ ::testing::Values(av1_get_nz_map_contexts_neon)); #endif -typedef void (*av1_txb_init_levels_func)(const tran_low_t *const coeff, - const int width, const int height, - uint8_t *const levels); +using av1_txb_init_levels_func = void (*)(const tran_low_t *const coeff, + const int width, const int height, + uint8_t *const levels); -typedef std::tuple<av1_txb_init_levels_func, int> TxbInitLevelParam; +using TxbInitLevelParam = std::tuple<av1_txb_init_levels_func, int>; class EncodeTxbInitLevelTest : public ::testing::TestWithParam<TxbInitLevelParam> {
diff --git a/test/end_to_end_psnr_test.cc b/test/end_to_end_psnr_test.cc index e0e744b..b006509 100644 --- a/test/end_to_end_psnr_test.cc +++ b/test/end_to_end_psnr_test.cc
@@ -38,13 +38,13 @@ { 34.9, 44.3, 38.5, 40.8 } }; -typedef struct { +struct TestVideoParam { const char *filename; unsigned int input_bit_depth; aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename @@ -81,6 +81,12 @@ { 1, 41.8 }, { 2, 41.4 }, { 3, 41.1 }, + } }, + { "SDR_Dance_ldsn_1080p.y4m", + { + { 1, 32.6 }, + { 2, 32.5 }, + { 3, 32.3 }, } } }; // Encoding modes tested @@ -93,6 +99,7 @@ const TestVideoParam kTestVectorsLcMode[] = { { "SDR_Animal_oqo7.y4m", 8, AOM_IMG_FMT_I420, AOM_BITS_8, 0 }, { "SDR_Health_rtd0_720p.y4m", 8, AOM_IMG_FMT_I420, AOM_BITS_8, 0 }, + { "SDR_Dance_ldsn_1080p.y4m", 8, AOM_IMG_FMT_I420, AOM_BITS_8, 0 }, }; // Speed settings tested @@ -138,8 +145,12 @@ if (video->frame() == 0) { if (encoding_mode_ == ::libaom_test::kLowComplexityDecode) { ASSERT_LE(cpu_used_, 3); + if (video->img()->d_h >= 1920) { + encoder->Control(AV1E_SET_TILE_ROWS, 2); + } else { + encoder->Control(AV1E_SET_TILE_ROWS, 1); + } encoder->Control(AV1E_SET_TILE_COLUMNS, 1); - encoder->Control(AV1E_SET_TILE_ROWS, 1); encoder->Control(AV1E_SET_ENABLE_CDEF, 0); encoder->Control(AV1E_SET_ENABLE_LOW_COMPLEXITY_DECODE, 1); } else {
diff --git a/test/end_to_end_qmpsnr_test.cc b/test/end_to_end_qmpsnr_test.cc index c5911ff..64aee79 100644 --- a/test/end_to_end_qmpsnr_test.cc +++ b/test/end_to_end_qmpsnr_test.cc
@@ -28,13 +28,13 @@ const double kSsimThreshold[] = { 83.4, 83.4, 83.4, 83.3, 83.3, 83.0, 82.3, 81.1, 81.1 }; -typedef struct { +struct TestVideoParam { const char *filename; unsigned int input_bit_depth; aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename
diff --git a/test/end_to_end_ssim_test.cc b/test/end_to_end_ssim_test.cc index 2b3fb87..730788b 100644 --- a/test/end_to_end_ssim_test.cc +++ b/test/end_to_end_ssim_test.cc
@@ -26,13 +26,13 @@ const double kSsimThreshold[] = { 83.4, 83.4, 83.4, 83.3, 83.3, 83.0, 82.3, 81.1, 81.1 }; -typedef struct { +struct TestVideoParam { const char *filename; unsigned int input_bit_depth; aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename
diff --git a/test/examples.sh b/test/examples.sh index 6869d02..ecc9a2b 100755 --- a/test/examples.sh +++ b/test/examples.sh
@@ -19,7 +19,7 @@ exclude_list="best_encode examples run_encodes tools_common" if [ "$(realtime_only_build)" = "yes" ]; then - exclude_list="${exclude_list} twopass_encoder simple_decoder lightfield_test" + exclude_list="${exclude_list} twopass_encoder simple_decoder lightfield_test low_complexity_test" fi # Filter out the scripts in $exclude_list.
diff --git a/test/ext_ratectrl_test.cc b/test/ext_ratectrl_test.cc new file mode 100644 index 0000000..57944d9 --- /dev/null +++ b/test/ext_ratectrl_test.cc
@@ -0,0 +1,408 @@ +/* + * Copyright (c) 2025, 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 "test/codec_factory.h" +#include "test/encode_test_driver.h" +#include "test/util.h" +#include "test/y4m_video_source.h" +#include "aom/aom_ext_ratectrl.h" +#include "gtest/gtest.h" + +namespace { + +const int kFrameNum = 5; + +// A mock rate control model. +struct MockRateCtrlModel {}; + +// A mock private data for rate control callbacks. +struct MockRC {}; +struct MockRC g_priv; + +// A flag to indicate if create_model() is called. +bool is_create_model_called = false; + +// A flag to indicate if delete_model() is called. +bool is_delete_model_called = false; + +// A flag to indicate if send_firstpass_stats() is called. +bool is_send_firstpass_stats_called = false; + +// A flag to indicate if send_tpl_gop_stats() is called. +bool is_send_extrc_tpl_gop_stats_called = false; + +// A flag to indicate if get_gop_decision() is called. +bool is_get_gop_decision_called = false; + +// A flag to indicate if update_encodeframe_result() is called. +bool is_update_encodeframe_result_called = false; + +// A flag to indicate if get_key_frame_decision() is called. +bool is_get_key_frame_decision_called = false; + +// Variables to store the parameters passed to update_encodeframe_result(). +int64_t bit_count = 0; +int actual_encoding_qindex = 0; + +// Construct a single ALTREF GOP. +const int kGopFrameCount = kFrameNum + 1; +aom_rc_gop_frame_t gop_frame_list[kGopFrameCount]; + +aom_rc_status_t mock_get_key_frame_decision( + aom_rc_model_t /*ratectrl_model*/, + aom_rc_key_frame_decision_t *key_frame_decision) { + key_frame_decision->key_frame_group_size = 1; + is_get_key_frame_decision_called = true; + return AOM_RC_OK; +} + +aom_rc_status_t mock_get_gop_decision(aom_rc_model_t /*ratectrl_model*/, + aom_rc_gop_decision_t *gop_decision) { + gop_decision->gop_frame_count = kGopFrameCount; + gop_decision->gop_frame_list = gop_frame_list; + static const aom_rc_ref_name_t ref_names[] = { + AOM_RC_LAST_FRAME, AOM_RC_LAST2_FRAME, AOM_RC_LAST3_FRAME, + AOM_RC_GOLDEN_FRAME, AOM_RC_BWDREF_FRAME, AOM_RC_ALTREF2_FRAME, + AOM_RC_ALTREF_FRAME + }; + for (int i = 0; i < kGopFrameCount; ++i) { + auto current_gop_frame = &gop_decision->gop_frame_list[i]; + current_gop_frame->coding_idx = i; + // Set all references to buffer 0 (KF) by default. + for (int j = 0; j < 7; ++j) { + current_gop_frame->ref_frame_list.name[j] = ref_names[j]; + current_gop_frame->ref_frame_list.index[j] = 0; + } + current_gop_frame->update_ref_idx = -1; + current_gop_frame->primary_ref_frame.name = AOM_RC_INVALID_REF_FRAME; + current_gop_frame->primary_ref_frame.index = -1; + + if (i == 0) { + // Key frame + current_gop_frame->is_key_frame = true; + current_gop_frame->update_type = AOM_RC_KF_UPDATE; + current_gop_frame->layer_depth = 0; + current_gop_frame->display_idx = 0; + current_gop_frame->update_ref_idx = 0; + current_gop_frame->order_idx = 0; + } else { + current_gop_frame->is_key_frame = false; + if (i == 1) { + // ALTREF + current_gop_frame->update_type = AOM_RC_ARF_UPDATE; + current_gop_frame->layer_depth = 1; + current_gop_frame->display_idx = 1; + current_gop_frame->update_ref_idx = 1; + current_gop_frame->order_idx = 4; + } else if (i == 5) { + // Overlay frame + current_gop_frame->is_key_frame = false; + current_gop_frame->update_type = AOM_RC_OVERLAY_UPDATE; + current_gop_frame->layer_depth = AOM_RC_MAX_ARF_LAYERS - 1; + current_gop_frame->display_idx = 4; + current_gop_frame->order_idx = 4; + } else { + // Leaf frames + current_gop_frame->is_key_frame = false; + current_gop_frame->update_type = AOM_RC_LF_UPDATE; + current_gop_frame->layer_depth = AOM_RC_MAX_ARF_LAYERS - 1; + current_gop_frame->display_idx = i - 1; // Key in the front + current_gop_frame->order_idx = i - 1; + current_gop_frame->update_ref_idx = 2; + } + // For leaf and overlay frames, set ALTREF to buffer 1. + if (i >= 2) { + current_gop_frame->ref_frame_list.index[AOM_RC_ALTREF_FRAME] = 1; + } + // For leaf frames after the first leaf, use buffer 2 as LAST. + if (i > 2 && i < 5) { + current_gop_frame->ref_frame_list.index[AOM_RC_LAST_FRAME] = 2; + } + + current_gop_frame->primary_ref_frame.name = AOM_RC_LAST_FRAME; + current_gop_frame->primary_ref_frame.index = (i > 2 && i < 5) ? 2 : 0; + } + } + gop_decision->global_order_idx_offset = 0; + is_get_gop_decision_called = true; + return AOM_RC_OK; +} + +aom_rc_status_t mock_create_model(void *priv, + const aom_rc_config_t *ratectrl_config, + aom_rc_model_t *ratectrl_model) { + (void)priv; + (void)ratectrl_config; + EXPECT_NE(ratectrl_model, nullptr); + *ratectrl_model = (aom_rc_model_t)(new MockRateCtrlModel()); + is_create_model_called = true; + return AOM_RC_OK; +} + +aom_rc_status_t mock_delete_model(aom_rc_model_t ratectrl_model) { + EXPECT_NE(ratectrl_model, nullptr); + delete (MockRateCtrlModel *)ratectrl_model; + is_delete_model_called = true; + return AOM_RC_OK; +} + +aom_rc_status_t mock_send_firstpass_stats( + aom_rc_model_t ratectrl_model, + const aom_rc_firstpass_stats_t *firstpass_stats) { + EXPECT_NE(ratectrl_model, nullptr); + EXPECT_NE(firstpass_stats, nullptr); + EXPECT_EQ(firstpass_stats->num_frames, kFrameNum); + EXPECT_NE(firstpass_stats->frame_stats, nullptr); + is_send_firstpass_stats_called = true; + return AOM_RC_OK; +} + +aom_rc_status_t mock_send_extrc_tpl_gop_stats( + aom_rc_model_t ratectrl_model, const AomTplGopStats *extrc_tpl_gop_stats) { + EXPECT_NE(ratectrl_model, nullptr); + EXPECT_NE(extrc_tpl_gop_stats, nullptr); + EXPECT_GT(extrc_tpl_gop_stats->size, 0); + EXPECT_NE(extrc_tpl_gop_stats->frame_stats_list, nullptr); + is_send_extrc_tpl_gop_stats_called = true; + return AOM_RC_OK; +} + +aom_rc_status_t mock_update_encodeframe_result( + aom_rc_model_t /* ratectrl_model */, + const aom_rc_encodeframe_result_t *encode_frame_result) { + EXPECT_NE(encode_frame_result, nullptr); + bit_count = encode_frame_result->bit_count; + actual_encoding_qindex = encode_frame_result->actual_encoding_qindex; + is_update_encodeframe_result_called = true; + return AOM_RC_OK; +} + +class ExtRateCtrlTest : public ::libaom_test::EncoderTest, + public ::libaom_test::CodecTestWith2Params<int, int> { + protected: + ExtRateCtrlTest() : EncoderTest(GET_PARAM(0)), cpu_used_(GET_PARAM(2)) { + aom_rc_funcs_t *rc_funcs = &rc_funcs_; + rc_funcs->priv = &g_priv; + rc_funcs->rc_type = AOM_RC_QP; + rc_funcs->create_model = mock_create_model; + rc_funcs->delete_model = mock_delete_model; + rc_funcs->send_firstpass_stats = mock_send_firstpass_stats; + rc_funcs->send_tpl_gop_stats = mock_send_extrc_tpl_gop_stats; + rc_funcs->get_gop_decision = nullptr; + rc_funcs->get_key_frame_decision = nullptr; + rc_funcs->get_encodeframe_decision = nullptr; + rc_funcs->update_encodeframe_result = nullptr; + } + ~ExtRateCtrlTest() override = default; + + void SetUp() override { + InitializeConfig(static_cast<libaom_test::TestMode>(GET_PARAM(1))); + cfg_.g_threads = 1; + cfg_.g_limit = kFrameNum; + is_create_model_called = false; + is_delete_model_called = false; + is_send_firstpass_stats_called = false; + is_send_extrc_tpl_gop_stats_called = false; + is_get_gop_decision_called = false; + is_update_encodeframe_result_called = false; + is_get_key_frame_decision_called = false; + } + + void PreEncodeFrameHook(::libaom_test::VideoSource *video, + ::libaom_test::Encoder *encoder) override { + if (video->frame() == 0) { + encoder->Control(AOME_SET_CPUUSED, cpu_used_); + encoder->Control(AV1E_SET_EXTERNAL_RATE_CONTROL, &rc_funcs_); + } + current_encoder_ = encoder; + } + + ::libaom_test::Encoder *current_encoder_; + aom_rc_funcs_t rc_funcs_; + int cpu_used_; +}; + +TEST_P(ExtRateCtrlTest, TestExternalRateCtrl) { + ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, kFrameNum); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + EXPECT_TRUE(is_create_model_called); + EXPECT_TRUE(is_send_firstpass_stats_called); + EXPECT_TRUE(is_send_extrc_tpl_gop_stats_called); + EXPECT_TRUE(is_delete_model_called); +} + +AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlTest, + ::testing::Values(::libaom_test::kTwoPassGood), + ::testing::Values(3)); + +// Corresponds to QP 43 for 8-bit video. +const int kFrameQindex = 172; + +aom_rc_status_t mock_get_encodeframe_decision_const_q( + aom_rc_model_t /*ratectrl_model*/, int /*frame_gop_index*/, + aom_rc_encodeframe_decision_t *frame_decision) { + // Set constant QP. + frame_decision->q_index = kFrameQindex; + frame_decision->sb_params_list = NULL; // Initialize to NULL + return AOM_RC_OK; +} + +class ExtRateCtrlQpTest : public ExtRateCtrlTest { + protected: + ExtRateCtrlQpTest() { + rc_funcs_.get_encodeframe_decision = mock_get_encodeframe_decision_const_q; + } + ~ExtRateCtrlQpTest() override = default; + + void SetUp() override { + InitializeConfig(static_cast<libaom_test::TestMode>(GET_PARAM(1))); + cfg_.g_threads = 1; + cfg_.g_limit = kFrameNum; + is_create_model_called = false; + is_delete_model_called = false; + } + + void FramePktHook(const aom_codec_cx_pkt_t *pkt) override { + if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) return; + int q_index = -1; + current_encoder_->Control(AOME_GET_LAST_QUANTIZER, &q_index); + EXPECT_EQ(q_index, kFrameQindex); + } +}; + +TEST_P(ExtRateCtrlQpTest, TestExternalRateCtrlConstQp) { + ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, kFrameNum); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + EXPECT_TRUE(is_create_model_called); + EXPECT_TRUE(is_delete_model_called); +} + +AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlQpTest, + ::testing::Values(::libaom_test::kTwoPassGood), + ::testing::Values(3)); + +class ExtRateCtrlUpdateEncodeFrameResultTest : public ExtRateCtrlTest { + protected: + ExtRateCtrlUpdateEncodeFrameResultTest() { + rc_funcs_.rc_type = AOM_RC_QP; + rc_funcs_.get_encodeframe_decision = mock_get_encodeframe_decision_const_q; + rc_funcs_.update_encodeframe_result = mock_update_encodeframe_result; + } + ~ExtRateCtrlUpdateEncodeFrameResultTest() override = default; + + void SetUp() override { + ExtRateCtrlTest::SetUp(); + is_update_encodeframe_result_called = false; + bit_count = 0; + actual_encoding_qindex = 0; + } + + void FramePktHook(const aom_codec_cx_pkt_t *pkt) override { + if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) return; + int q_index = -1; + current_encoder_->Control(AOME_GET_LAST_QUANTIZER, &q_index); + EXPECT_EQ(q_index, kFrameQindex); + EXPECT_EQ(actual_encoding_qindex, kFrameQindex); + // The second packet includes both invisible and visible frames. To verify + // frame size, there's no move_offset (obu_header_size + obu_payload_size) + // for vis_frame_size. + const int pkt_size_padding = pkt_count_ == 1 ? 0 : 2; + // Due to av1 packing invisible and visible frames together, we can only + // verify the size of the visible frame in the packet using vis_frame_size. + EXPECT_EQ((bit_count >> 3) + pkt_size_padding, + pkt->data.frame.vis_frame_size); + pkt_count_++; + } + + private: + int pkt_count_ = 0; +}; + +TEST_P(ExtRateCtrlUpdateEncodeFrameResultTest, + TestExternalRateCtrlUpdateEncodeFrameResult) { + ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, kFrameNum); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + EXPECT_TRUE(is_create_model_called); + EXPECT_TRUE(is_delete_model_called); + EXPECT_TRUE(is_update_encodeframe_result_called); +} + +AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlUpdateEncodeFrameResultTest, + ::testing::Values(::libaom_test::kTwoPassGood), + ::testing::Values(3)); + +class ExtRateCtrlGopTest : public ExtRateCtrlTest { + protected: + ExtRateCtrlGopTest() { + rc_funcs_.rc_type = AOM_RC_GOP; + rc_funcs_.get_gop_decision = mock_get_gop_decision; + } + + void PostEncodeFrameHook(::libaom_test::Encoder *encoder) override { + if (cfg_.g_pass == AOM_RC_FIRST_PASS) return; + encoder->Control(AV1E_GET_GOP_INFO, &gop_info_); + } + + void FramePktHook(const aom_codec_cx_pkt_t * /*pkt*/) override { + // This is verified here (not in PostEncodeFrameHook) because + // PostEncodeFrameHook is also called when encoder is reading frames into + // lookahead buffer, when GOP structure hasn't been determined. + ASSERT_EQ(gop_info_.gop_size, kGopFrameCount); + } + + ~ExtRateCtrlGopTest() override = default; + aom_gop_info_t gop_info_; +}; + +TEST_P(ExtRateCtrlGopTest, TestExternalRateCtrlGop) { + ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, kFrameNum); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + EXPECT_TRUE(is_create_model_called); + EXPECT_TRUE(is_get_gop_decision_called); + EXPECT_TRUE(is_delete_model_called); +} + +AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlGopTest, + ::testing::Values(::libaom_test::kTwoPassGood), + ::testing::Values(3)); + +class ExtRateCtrlKeyFrameTest : public ExtRateCtrlTest { + protected: + ExtRateCtrlKeyFrameTest() { + rc_funcs_.rc_type = AOM_RC_GOP; + rc_funcs_.get_key_frame_decision = mock_get_key_frame_decision; + } + + ~ExtRateCtrlKeyFrameTest() override = default; + + void SetUp() override { + ExtRateCtrlTest::SetUp(); + is_get_key_frame_decision_called = false; + } + + void FramePktHook(const aom_codec_cx_pkt_t *pkt) override { + if (pkt->kind != AOM_CODEC_CX_FRAME_PKT) return; + EXPECT_TRUE(pkt->data.frame.flags & AOM_FRAME_IS_KEY); + } +}; + +TEST_P(ExtRateCtrlKeyFrameTest, TestExternalRateCtrlKeyFrame) { + ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, kFrameNum); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + EXPECT_TRUE(is_create_model_called); + EXPECT_TRUE(is_get_key_frame_decision_called); + EXPECT_TRUE(is_delete_model_called); +} + +AV1_INSTANTIATE_TEST_SUITE(ExtRateCtrlKeyFrameTest, + ::testing::Values(::libaom_test::kTwoPassGood), + ::testing::Values(3)); +} // namespace
diff --git a/test/fft_test.cc b/test/fft_test.cc index 1d3d59a..a17249d 100644 --- a/test/fft_test.cc +++ b/test/fft_test.cc
@@ -25,7 +25,7 @@ namespace { -typedef void (*tform_fun_t)(const float *input, float *temp, float *output); +using tform_fun_t = void (*)(const float *input, float *temp, float *output); // Simple 1D FFT implementation template <typename InputType>
diff --git a/test/filterintra_test.cc b/test/filterintra_test.cc index d425287..0375f09 100644 --- a/test/filterintra_test.cc +++ b/test/filterintra_test.cc
@@ -25,15 +25,15 @@ using libaom_test::ACMRandom; using std::tuple; -typedef void (*Predictor)(uint8_t *dst, ptrdiff_t stride, TX_SIZE tx_size, - const uint8_t *above, const uint8_t *left, int mode); +using Predictor = void (*)(uint8_t *dst, ptrdiff_t stride, TX_SIZE tx_size, + const uint8_t *above, const uint8_t *left, int mode); // Note: // Test parameter list: // Reference predictor, optimized predictor, prediction mode, tx size // -typedef tuple<Predictor, Predictor, int> PredFuncMode; -typedef tuple<PredFuncMode, TX_SIZE> PredParams; +using PredFuncMode = tuple<Predictor, Predictor, int>; +using PredParams = tuple<PredFuncMode, TX_SIZE>; const int MaxTxSize = 32; @@ -171,8 +171,6 @@ #endif // HAVE_SSE4_1 #if HAVE_NEON -// TODO(aomedia:349436249): enable for armv7 after SIGBUS is fixed. -#if AOM_ARCH_AARCH64 const PredFuncMode kPredFuncMdArrayNEON[] = { make_tuple(&av1_filter_intra_predictor_c, &av1_filter_intra_predictor_neon, FILTER_DC_PRED), @@ -194,9 +192,31 @@ NEON, AV1FilterIntraPredTest, ::testing::Combine(::testing::ValuesIn(kPredFuncMdArrayNEON), ::testing::ValuesIn(kTxSizeNEON))); -#else // !AOM_ARCH_AARCH64 -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AV1FilterIntraPredTest); -#endif // AOM_ARCH_AARCH64 #endif // HAVE_NEON +#if HAVE_NEON_I8MM +const PredFuncMode kPredFuncMdArrayNEON_I8MM[] = { + make_tuple(&av1_filter_intra_predictor_c, + &av1_filter_intra_predictor_neon_i8mm, FILTER_DC_PRED), + make_tuple(&av1_filter_intra_predictor_c, + &av1_filter_intra_predictor_neon_i8mm, FILTER_V_PRED), + make_tuple(&av1_filter_intra_predictor_c, + &av1_filter_intra_predictor_neon_i8mm, FILTER_H_PRED), + make_tuple(&av1_filter_intra_predictor_c, + &av1_filter_intra_predictor_neon_i8mm, FILTER_D157_PRED), + make_tuple(&av1_filter_intra_predictor_c, + &av1_filter_intra_predictor_neon_i8mm, FILTER_PAETH_PRED), +}; + +const TX_SIZE kTxSizeNEON_I8MM[] = { TX_4X4, TX_8X8, TX_16X16, TX_32X32, + TX_4X8, TX_8X4, TX_8X16, TX_16X8, + TX_16X32, TX_32X16, TX_4X16, TX_16X4, + TX_8X32, TX_32X8 }; + +INSTANTIATE_TEST_SUITE_P( + NEON_I8MM, AV1FilterIntraPredTest, + ::testing::Combine(::testing::ValuesIn(kPredFuncMdArrayNEON_I8MM), + ::testing::ValuesIn(kTxSizeNEON_I8MM))); +#endif // HAVE_NEON_I8MM + } // namespace
diff --git a/test/frame_resize_test.cc b/test/frame_resize_test.cc index a9acb24..f9557e0 100644 --- a/test/frame_resize_test.cc +++ b/test/frame_resize_test.cc
@@ -30,7 +30,7 @@ const int kIters = 1000; -typedef tuple<int, int> FrameDimension; +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, @@ -50,12 +50,12 @@ } } -typedef bool (*LowBDResizeFunc)(uint8_t *intbuf, uint8_t *output, - int out_stride, int height, int height2, - int stride, int start_wd); +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> -typedef tuple<LowBDResizeFunc, FrameDimension> ResizeTestParams; +using ResizeTestParams = tuple<LowBDResizeFunc, FrameDimension>; class AV1ResizeYTest : public ::testing::TestWithParam<ResizeTestParams> { public: @@ -166,11 +166,11 @@ ::testing::ValuesIn(kFrameDim))); #endif -typedef void (*LowBDResize_x_Func)(const uint8_t *const input, int in_stride, - uint8_t *intbuf, int height, - int filtered_length, int width2); +using LowBDResize_x_Func = void (*)(const uint8_t *const input, int in_stride, + uint8_t *intbuf, int height, + int filtered_length, int width2); -typedef tuple<LowBDResize_x_Func, FrameDimension> Resize_x_TestParams; +using Resize_x_TestParams = tuple<LowBDResize_x_Func, FrameDimension>; class AV1ResizeXTest : public ::testing::TestWithParam<Resize_x_TestParams> { public:
diff --git a/test/frame_size_tests.cc b/test/frame_size_tests.cc index 65269aa..4ed8354 100644 --- a/test/frame_size_tests.cc +++ b/test/frame_size_tests.cc
@@ -327,10 +327,10 @@ ::testing::Combine(::testing::Values(AOM_USAGE_ALL_INTRA), ::testing::Values(AOM_Q), ::testing::Range(6, 10))); -typedef struct { +struct FrameSizeParam { unsigned int width; unsigned int height; -} FrameSizeParam; +}; const FrameSizeParam FrameSizeTestParams[] = { { 96, 96 }, { 176, 144 } };
diff --git a/test/fwht4x4_test.cc b/test/fwht4x4_test.cc index 8e9600a..c68879e 100644 --- a/test/fwht4x4_test.cc +++ b/test/fwht4x4_test.cc
@@ -31,13 +31,13 @@ using libaom_test::ACMRandom; namespace { -typedef void (*FdctFunc)(const int16_t *in, tran_low_t *out, int stride); -typedef void (*IdctFunc)(const tran_low_t *in, uint8_t *out, int stride); +using FdctFunc = void (*)(const int16_t *in, tran_low_t *out, int stride); +using IdctFunc = void (*)(const tran_low_t *in, uint8_t *out, int stride); using libaom_test::FhtFunc; -typedef std::tuple<FdctFunc, IdctFunc, TX_TYPE, aom_bit_depth_t, int, FdctFunc> - Dct4x4Param; +using Dct4x4Param = + std::tuple<FdctFunc, IdctFunc, TX_TYPE, aom_bit_depth_t, int, FdctFunc>; void fwht4x4_ref(const int16_t *in, tran_low_t *out, int stride, TxfmParam * /*txfm_param*/) {
diff --git a/test/hash_test.cc b/test/hash_test.cc index f824184..adc072e 100644 --- a/test/hash_test.cc +++ b/test/hash_test.cc
@@ -24,10 +24,10 @@ namespace { -typedef uint32_t (*get_crc32c_value_func)(void *calculator, const uint8_t *p, - size_t length); +using get_crc32c_value_func = uint32_t (*)(void *calculator, const uint8_t *p, + size_t length); -typedef std::tuple<get_crc32c_value_func, int> HashParam; +using HashParam = std::tuple<get_crc32c_value_func, int>; class AV1Crc32cHashTest : public ::testing::TestWithParam<HashParam> { public:
diff --git a/test/hbd_metrics_test.cc b/test/hbd_metrics_test.cc index af050a0..6dfe4cc 100644 --- a/test/hbd_metrics_test.cc +++ b/test/hbd_metrics_test.cc
@@ -29,11 +29,11 @@ namespace { -typedef double (*LBDMetricFunc)(const YV12_BUFFER_CONFIG *source, - const YV12_BUFFER_CONFIG *dest); -typedef double (*HBDMetricFunc)(const YV12_BUFFER_CONFIG *source, - const YV12_BUFFER_CONFIG *dest, uint32_t in_bd, - uint32_t bd); +using LBDMetricFunc = double (*)(const YV12_BUFFER_CONFIG *source, + const YV12_BUFFER_CONFIG *dest); +using HBDMetricFunc = double (*)(const YV12_BUFFER_CONFIG *source, + const YV12_BUFFER_CONFIG *dest, uint32_t in_bd, + uint32_t bd); double compute_hbd_psnr(const YV12_BUFFER_CONFIG *source, const YV12_BUFFER_CONFIG *dest, uint32_t in_bd, @@ -173,8 +173,8 @@ HBDMetricFunc hbd_metric_; }; -typedef std::tuple<LBDMetricFunc, HBDMetricFunc, int, int, double> - MetricTestTParam; +using MetricTestTParam = + std::tuple<LBDMetricFunc, HBDMetricFunc, int, int, double>; class HBDMetricsTest : public HBDMetricsTestBase, public ::testing::TestWithParam<MetricTestTParam> { public:
diff --git a/test/hiprec_convolve_test_util.h b/test/hiprec_convolve_test_util.h index 52a43e9..d33baa5 100644 --- a/test/hiprec_convolve_test_util.h +++ b/test/hiprec_convolve_test_util.h
@@ -29,14 +29,14 @@ namespace AV1HiprecConvolve { -typedef void (*hiprec_convolve_func)(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, - const int16_t *filter_x, int x_step_q4, - const int16_t *filter_y, int y_step_q4, - int w, int h, - const WienerConvolveParams *conv_params); +using hiprec_convolve_func = void (*)(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const int16_t *filter_x, int x_step_q4, + const int16_t *filter_y, int y_step_q4, + int w, int h, + const WienerConvolveParams *conv_params); -typedef std::tuple<int, int, int, hiprec_convolve_func> HiprecConvolveParam; +using HiprecConvolveParam = std::tuple<int, int, int, hiprec_convolve_func>; ::testing::internal::ParamGenerator<HiprecConvolveParam> BuildParams( hiprec_convolve_func filter); @@ -58,14 +58,14 @@ #if CONFIG_AV1_HIGHBITDEPTH namespace AV1HighbdHiprecConvolve { -typedef void (*highbd_hiprec_convolve_func)( - const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, - ptrdiff_t dst_stride, const int16_t *filter_x, int x_step_q4, - const int16_t *filter_y, int y_step_q4, int w, int h, - const WienerConvolveParams *conv_params, int bps); +using highbd_hiprec_convolve_func = + void (*)(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const int16_t *filter_x, int x_step_q4, + const int16_t *filter_y, int y_step_q4, int w, int h, + const WienerConvolveParams *conv_params, int bps); -typedef std::tuple<int, int, int, int, highbd_hiprec_convolve_func> - HighbdHiprecConvolveParam; +using HighbdHiprecConvolveParam = + std::tuple<int, int, int, int, highbd_hiprec_convolve_func>; ::testing::internal::ParamGenerator<HighbdHiprecConvolveParam> BuildParams( highbd_hiprec_convolve_func filter);
diff --git a/test/horver_correlation_test.cc b/test/horver_correlation_test.cc index bab3912..c9212de 100644 --- a/test/horver_correlation_test.cc +++ b/test/horver_correlation_test.cc
@@ -26,10 +26,10 @@ using libaom_test::ACMRandom; namespace { -typedef void (*HorverFunc)(const int16_t *diff, int stride, int w, int h, - float *hcorr, float *vcorr); +using HorverFunc = void (*)(const int16_t *diff, int stride, int w, int h, + float *hcorr, float *vcorr); -typedef std::tuple<const HorverFunc> HorverTestParam; +using HorverTestParam = std::tuple<const HorverFunc>; class HorverTest : public ::testing::TestWithParam<HorverTestParam> { public:
diff --git a/test/horz_superres_test.cc b/test/horz_superres_test.cc index fee0cca..6d4ca8a 100644 --- a/test/horz_superres_test.cc +++ b/test/horz_superres_test.cc
@@ -32,7 +32,7 @@ const int kBitrate = 40; -typedef struct { +struct TestVideoParam { const char *filename; aom_img_fmt fmt; aom_bit_depth_t bit_depth; @@ -41,7 +41,7 @@ unsigned int screen_content; double psnr_threshold; // used by modes other than AOM_SUPERRES_AUTO double psnr_threshold2; // used by AOM_SUPERRES_AUTO -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename @@ -69,7 +69,7 @@ AOM_SUPERRES_AUTO }; // Superres denominators and superres kf denominators to be tested -typedef tuple<int, int> SuperresDenominatorPair; +using SuperresDenominatorPair = tuple<int, int>; const SuperresDenominatorPair kSuperresDenominators[] = { make_tuple(16, 9), make_tuple(13, 11), make_tuple(9, 9), make_tuple(13, 13), make_tuple(11, 16), make_tuple(8, 16), @@ -77,7 +77,7 @@ }; // Superres q thresholds and superres kf q thresholds to be tested -typedef tuple<int, int> SuperresQThresholdPair; +using SuperresQThresholdPair = tuple<int, int>; const SuperresQThresholdPair kSuperresQThresholds[] = { make_tuple(63, 63), make_tuple(63, 41), make_tuple(17, 63), make_tuple(41, 11), make_tuple(1, 37), make_tuple(11, 11), @@ -88,9 +88,8 @@ // Test parameter list: // <[needed for EncoderTest], test_video_param_, superres_mode_> -typedef tuple<const libaom_test::CodecFactory *, TestVideoParam, - aom_superres_mode> - HorzSuperresTestParam; +using HorzSuperresTestParam = + tuple<const libaom_test::CodecFactory *, TestVideoParam, aom_superres_mode>; class HorzSuperresEndToEndTest : public ::testing::TestWithParam<HorzSuperresTestParam>, @@ -187,9 +186,9 @@ // Test parameter list: // <[needed for EncoderTest], test_video_param_, tuple(superres_denom_, // superres_kf_denom_)> -typedef tuple<const libaom_test::CodecFactory *, TestVideoParam, - SuperresDenominatorPair> - HorzSuperresFixedTestParam; +using HorzSuperresFixedTestParam = + tuple<const libaom_test::CodecFactory *, TestVideoParam, + SuperresDenominatorPair>; class HorzSuperresFixedEndToEndTest : public ::testing::TestWithParam<HorzSuperresFixedTestParam>, @@ -297,9 +296,9 @@ // Test parameter list: // <[needed for EncoderTest], test_video_param_, // tuple(superres_qthresh_,superres_kf_qthresh_)> -typedef tuple<const libaom_test::CodecFactory *, TestVideoParam, - SuperresQThresholdPair> - HorzSuperresQThreshTestParam; +using HorzSuperresQThreshTestParam = + tuple<const libaom_test::CodecFactory *, TestVideoParam, + SuperresQThresholdPair>; class HorzSuperresQThreshEndToEndTest : public ::testing::TestWithParam<HorzSuperresQThreshTestParam>,
diff --git a/test/intra_edge_test.cc b/test/intra_edge_test.cc index d5cc3d5..9b992d2 100644 --- a/test/intra_edge_test.cc +++ b/test/intra_edge_test.cc
@@ -62,8 +62,8 @@ int size_; }; -typedef void (*UP8B)(uint8_t *p, int size); -typedef libaom_test::FuncParam<UP8B> TestFuncs; +using UP8B = void (*)(uint8_t *p, int size); +using TestFuncs = libaom_test::FuncParam<UP8B>; class UpsampleTest8B : public UpsampleTest<UP8B, uint8_t> { protected: @@ -154,8 +154,8 @@ int strength_; }; -typedef void (*FE8B)(uint8_t *p, int size, int strength); -typedef libaom_test::FuncParam<FE8B> FilterEdgeTestFuncs; +using FE8B = void (*)(uint8_t *p, int size, int strength); +using FilterEdgeTestFuncs = libaom_test::FuncParam<FE8B>; class FilterEdgeTest8B : public FilterEdgeTest<FE8B, uint8_t> { protected: @@ -212,8 +212,8 @@ #if CONFIG_AV1_HIGHBITDEPTH -typedef void (*UPHB)(uint16_t *p, int size, int bd); -typedef libaom_test::FuncParam<UPHB> TestFuncsHBD; +using UPHB = void (*)(uint16_t *p, int size, int bd); +using TestFuncsHBD = libaom_test::FuncParam<UPHB>; class UpsampleTestHB : public UpsampleTest<UPHB, uint16_t> { protected: @@ -281,8 +281,8 @@ av1_highbd_upsample_intra_edge_neon))); #endif // HAVE_NEON -typedef void (*FEHB)(uint16_t *p, int size, int strength); -typedef libaom_test::FuncParam<FEHB> FilterEdgeTestFuncsHBD; +using FEHB = void (*)(uint16_t *p, int size, int strength); +using FilterEdgeTestFuncsHBD = libaom_test::FuncParam<FEHB>; class FilterEdgeTestHB : public FilterEdgeTest<FEHB, uint16_t> { protected:
diff --git a/test/intrapred_test.cc b/test/intrapred_test.cc index bf9ab75..f3a246b 100644 --- a/test/intrapred_test.cc +++ b/test/intrapred_test.cc
@@ -30,11 +30,11 @@ const int count_test_block = 100000; -typedef void (*HighbdIntraPred)(uint16_t *dst, ptrdiff_t stride, - const uint16_t *above, const uint16_t *left, - int bps); -typedef void (*IntraPred)(uint8_t *dst, ptrdiff_t stride, const uint8_t *above, - const uint8_t *left); +using HighbdIntraPred = void (*)(uint16_t *dst, ptrdiff_t stride, + const uint16_t *above, const uint16_t *left, + int bps); +using IntraPred = void (*)(uint8_t *dst, ptrdiff_t stride, const uint8_t *above, + const uint8_t *left); } // namespace
diff --git a/test/kf_test.cc b/test/kf_test.cc index 3d76873..5d7cfdb 100644 --- a/test/kf_test.cc +++ b/test/kf_test.cc
@@ -107,10 +107,10 @@ } } -typedef struct { +struct kfIntervalParam { const unsigned int min_kf_dist; const unsigned int max_kf_dist; -} kfIntervalParam; +}; const kfIntervalParam kfTestParams[] = { { 1, 1 }, { 0, 10 }, { 10, 10 }, { 0, 30 }, { 30, 30 }
diff --git a/test/level_test.cc b/test/level_test.cc index dd8981c..70f4b21 100644 --- a/test/level_test.cc +++ b/test/level_test.cc
@@ -135,12 +135,12 @@ // To save run time, we only test speed 4. if (cpu_used_ == 4) { libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, - 30, 1, 0, 30); + 30, 1, 0, 40); target_level_ = kLevelKeepStats; cfg_.rc_target_bitrate = 1000; - cfg_.g_limit = 30; + cfg_.g_limit = 40; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_LE(level_[0], 0); + ASSERT_EQ(level_[0], 0); } } @@ -148,12 +148,12 @@ // To save run time, we only test speed 4. if (cpu_used_ == 4) { libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, - 30, 1, 0, 30); + 30, 1, 0, 40); target_level_ = kLevelKeepStats; cfg_.rc_target_bitrate = 4000; - cfg_.g_limit = 30; + cfg_.g_limit = 40; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_LE(level_[0], 4); + ASSERT_EQ(level_[0], 4); } } @@ -166,7 +166,7 @@ target_level_ = target_level; cfg_.rc_target_bitrate = 4000; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - ASSERT_LE(level_[0], target_level); + ASSERT_EQ(level_[0], target_level); } }
diff --git a/test/loopfilter_control_test.cc b/test/loopfilter_control_test.cc index 30f04b2..f498559 100644 --- a/test/loopfilter_control_test.cc +++ b/test/loopfilter_control_test.cc
@@ -46,13 +46,13 @@ { 2, { { 0, 31.0 }, { 3, 31.0 } } }, { 3, { { 0, 31.0 }, { 3, 31.0 } } } } } }; -typedef struct { +struct TestVideoParam { const char *filename; unsigned int input_bit_depth; aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename
diff --git a/test/low_complexity_test.sh b/test/low_complexity_test.sh new file mode 100755 index 0000000..2521b2b --- /dev/null +++ b/test/low_complexity_test.sh
@@ -0,0 +1,89 @@ +#!/bin/sh +## Copyright (c) 2026, 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. +## +## This file tests the libaom low-complexity decode mode using simple_encoder +## and simple_decoder example. The decoder outputs the instruction counts that +## can be used for decoder speed monitoring. +## +. $(dirname $0)/tools_common.sh + +# Environment check: $infile is required. +low_complexity_test_verify_environment() { + if [ ! "$(av1_encode_available)" = "yes" ] || \ + [ ! "$(av1_decode_available)" = "yes" ]; then + echo "Encoder and decoder needs to be available." + return 1 + fi + + local infile="${LIBAOM_TEST_DATA_PATH}/SDR_Sports_6mug_608p_30fps_90f.yuv" + if [ ! -e "${infile}" ]; then + echo "Libaom test data must exist in LIBAOM_TEST_DATA_PATH." + return 1 + fi + + command -v perf >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Perf does not exist. Please install it." + return 1 + fi +} + +# Runs libaom low-complexity decode mode test +low_complexity_test() { + local img_width=608 + local img_height=1080 + + # Encode in low-complexity decode mode + local encoder="${LIBAOM_BIN_PATH}/low_complexity_mode_encoder${AOM_TEST_EXE_SUFFIX}" + local input_file="${LIBAOM_TEST_DATA_PATH}/SDR_Sports_6mug_608p_30fps_90f.yuv" + local ivf_file="${AOM_TEST_OUTPUT_DIR}/low_complexity_mode_encoder.bin" + + if [ ! -x "${encoder}" ]; then + elog "${encoder} does not exist or is not executable." + return 1 + fi + + echo "The LC mode encoder is started." + local enc_cmd="$(echo "${AOM_TEST_PREFIX}" "${encoder}" av1 "${img_width}" \ + "${img_height}" "${input_file}" "${ivf_file}" 150 90 "\ + "${devnull})" + echo "$enc_cmd" + eval "$enc_cmd" || return 1 + + [ -e "${ivf_file}" ] || return 1 + echo "The LC mode encoder is completed." + + # Use simple_decoder to decode the generated bitstream and collect the + # instruction counts. + local decoder="${LIBAOM_BIN_PATH}/simple_decoder${AOM_TEST_EXE_SUFFIX}" + local perfstat_file="${AOM_TEST_OUTPUT_DIR}/perfstat.txt" + local perf_prefix="perf stat --no-big-num -e instructions:u -o ${perfstat_file}" + local output_file="${AOM_TEST_OUTPUT_DIR}/low_complexity_mode_encoder.out" + + if [ ! -x "${decoder}" ]; then + elog "${decoder} does not exist or is not executable." + return 1 + fi + + local dec_cmd="$(echo "${perf_prefix}" "${AOM_TEST_PREFIX}" "${decoder}" \ + "${ivf_file}" "${output_file}" ${devnull})" + echo "$dec_cmd" + eval "$dec_cmd" || return 1 + + # Get perf user instruction count from perf output + local instruction_count="$(awk '/instructions:u/ {print $1}' \ + "${perfstat_file}")" + echo ${instruction_count} +} + +low_complexity_decode_mode_test="low_complexity_test" + +run_tests low_complexity_test_verify_environment \ + "${low_complexity_decode_mode_test}"
diff --git a/test/lpf_test.cc b/test/lpf_test.cc index edb107b..da9d162 100644 --- a/test/lpf_test.cc +++ b/test/lpf_test.cc
@@ -45,16 +45,16 @@ const uint8_t *thresh0, const uint8_t *blimit1, const uint8_t *limit1, \ const uint8_t *thresh1 -typedef void (*loop_op_t)(uint8_t *s, LOOP_PARAM); -typedef void (*dual_loop_op_t)(uint8_t *s, DUAL_LOOP_PARAM); -typedef void (*hbdloop_op_t)(uint16_t *s, LOOP_PARAM, int bd); -typedef void (*hbddual_loop_op_t)(uint16_t *s, DUAL_LOOP_PARAM, int bd); +using loop_op_t = void (*)(uint8_t *s, LOOP_PARAM); +using dual_loop_op_t = void (*)(uint8_t *s, DUAL_LOOP_PARAM); +using hbdloop_op_t = void (*)(uint16_t *s, LOOP_PARAM, int bd); +using hbddual_loop_op_t = void (*)(uint16_t *s, DUAL_LOOP_PARAM, int bd); -typedef std::tuple<hbdloop_op_t, hbdloop_op_t, int> hbdloop_param_t; -typedef std::tuple<hbddual_loop_op_t, hbddual_loop_op_t, int> - hbddual_loop_param_t; -typedef std::tuple<loop_op_t, loop_op_t, int> loop_param_t; -typedef std::tuple<dual_loop_op_t, dual_loop_op_t, int> dual_loop_param_t; +using hbdloop_param_t = std::tuple<hbdloop_op_t, hbdloop_op_t, int>; +using hbddual_loop_param_t = + std::tuple<hbddual_loop_op_t, hbddual_loop_op_t, int>; +using loop_param_t = std::tuple<loop_op_t, loop_op_t, int>; +using dual_loop_param_t = std::tuple<dual_loop_op_t, dual_loop_op_t, int>; template <typename Pixel_t, int PIXEL_WIDTH_t> void InitInput(Pixel_t *s, Pixel_t *ref_s, ACMRandom *rnd, const uint8_t limit, @@ -161,15 +161,15 @@ } #if CONFIG_AV1_HIGHBITDEPTH -typedef LoopTestParam<hbdloop_op_t, hbdloop_param_t> Loop8Test6Param_hbd; +using Loop8Test6Param_hbd = LoopTestParam<hbdloop_op_t, hbdloop_param_t>; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Loop8Test6Param_hbd); -typedef LoopTestParam<hbddual_loop_op_t, hbddual_loop_param_t> - Loop8Test9Param_hbd; +using Loop8Test9Param_hbd = + LoopTestParam<hbddual_loop_op_t, hbddual_loop_param_t>; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Loop8Test9Param_hbd); #endif -typedef LoopTestParam<loop_op_t, loop_param_t> Loop8Test6Param_lbd; +using Loop8Test6Param_lbd = LoopTestParam<loop_op_t, loop_param_t>; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Loop8Test6Param_lbd); -typedef LoopTestParam<dual_loop_op_t, dual_loop_param_t> Loop8Test9Param_lbd; +using Loop8Test9Param_lbd = LoopTestParam<dual_loop_op_t, dual_loop_param_t>; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Loop8Test9Param_lbd); #define OPCHECK(a, b) \
diff --git a/test/masked_sad_test.cc b/test/masked_sad_test.cc index 1b9e953..4de17ae 100644 --- a/test/masked_sad_test.cc +++ b/test/masked_sad_test.cc
@@ -28,20 +28,20 @@ namespace { const int number_of_iterations = 200; -typedef unsigned int (*MaskedSADFunc)(const uint8_t *src, int src_stride, - const uint8_t *ref, int ref_stride, - const uint8_t *second_pred, - const uint8_t *msk, int msk_stride, - int invert_mask); -typedef std::tuple<MaskedSADFunc, MaskedSADFunc> MaskedSADParam; +using MaskedSADFunc = unsigned int (*)(const uint8_t *src, int src_stride, + const uint8_t *ref, int ref_stride, + const uint8_t *second_pred, + const uint8_t *msk, int msk_stride, + int invert_mask); +using MaskedSADParam = std::tuple<MaskedSADFunc, MaskedSADFunc>; -typedef void (*MaskedSADx4Func)(const uint8_t *src, int src_stride, - const uint8_t *ref[], int ref_stride, - const uint8_t *second_pred, const uint8_t *msk, - int msk_stride, int invert_mask, - unsigned sads[]); +using MaskedSADx4Func = void (*)(const uint8_t *src, int src_stride, + const uint8_t *ref[], int ref_stride, + const uint8_t *second_pred, const uint8_t *msk, + int msk_stride, int invert_mask, + unsigned sads[]); -typedef std::tuple<MaskedSADx4Func, MaskedSADx4Func> MaskedSADx4Param; +using MaskedSADx4Param = std::tuple<MaskedSADx4Func, MaskedSADx4Func>; class MaskedSADTestBase : public ::testing::Test { public: @@ -195,13 +195,13 @@ TEST_P(MaskedSADTest, DISABLED_Speed) { runMaskedSADTest(2000000); } #if CONFIG_AV1_HIGHBITDEPTH -typedef unsigned int (*HighbdMaskedSADFunc)(const uint8_t *src, int src_stride, - const uint8_t *ref, int ref_stride, - const uint8_t *second_pred, - const uint8_t *msk, int msk_stride, - int invert_mask); -typedef std::tuple<HighbdMaskedSADFunc, HighbdMaskedSADFunc> - HighbdMaskedSADParam; +using HighbdMaskedSADFunc = unsigned int (*)(const uint8_t *src, int src_stride, + const uint8_t *ref, int ref_stride, + const uint8_t *second_pred, + const uint8_t *msk, int msk_stride, + int invert_mask); +using HighbdMaskedSADParam = + std::tuple<HighbdMaskedSADFunc, HighbdMaskedSADFunc>; class HighbdMaskedSADTest : public ::testing::TestWithParam<HighbdMaskedSADParam> {
diff --git a/test/masked_variance_test.cc b/test/masked_variance_test.cc index 4d9ebe9..e527efa 100644 --- a/test/masked_variance_test.cc +++ b/test/masked_variance_test.cc
@@ -32,13 +32,13 @@ namespace { const int number_of_iterations = 200; -typedef unsigned int (*MaskedSubPixelVarianceFunc)( +using MaskedSubPixelVarianceFunc = unsigned int (*)( const uint8_t *src, int src_stride, int xoffset, int yoffset, const uint8_t *ref, int ref_stride, const uint8_t *second_pred, const uint8_t *msk, int msk_stride, int invert_mask, unsigned int *sse); -typedef std::tuple<MaskedSubPixelVarianceFunc, MaskedSubPixelVarianceFunc> - MaskedSubPixelVarianceParam; +using MaskedSubPixelVarianceParam = + std::tuple<MaskedSubPixelVarianceFunc, MaskedSubPixelVarianceFunc>; class MaskedSubPixelVarianceTest : public ::testing::TestWithParam<MaskedSubPixelVarianceParam> { @@ -170,9 +170,9 @@ } #if CONFIG_AV1_HIGHBITDEPTH -typedef std::tuple<MaskedSubPixelVarianceFunc, MaskedSubPixelVarianceFunc, - aom_bit_depth_t> - HighbdMaskedSubPixelVarianceParam; +using HighbdMaskedSubPixelVarianceParam = + std::tuple<MaskedSubPixelVarianceFunc, MaskedSubPixelVarianceFunc, + aom_bit_depth_t>; class HighbdMaskedSubPixelVarianceTest : public ::testing::TestWithParam<HighbdMaskedSubPixelVarianceParam> {
diff --git a/test/metadata_test.cc b/test/metadata_test.cc index da0fb13..c34f4da 100644 --- a/test/metadata_test.cc +++ b/test/metadata_test.cc
@@ -35,6 +35,12 @@ 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; +const size_t kMetadataPayloadSizeT35Two = 10; +// 0xB5 stands for the itut t35 metadata country code for the Unites States +const uint8_t kMetadataPayloadT35Two[kMetadataPayloadSizeT35] = { + 0xB5, 0x01, 0x02, 0x42, 0xff, 0xff, 0x00, 0x07, 0x08, 0x09 +}; + const size_t kMetadataPayloadSizeMdcv = 24; // Arbitrary content. const uint8_t kMetadataPayloadMdcv[kMetadataPayloadSizeMdcv] = { @@ -96,11 +102,18 @@ ASSERT_EQ(aom_img_add_metadata(current_frame, OBU_METADATA_TYPE_ITUT_T35, nullptr, 0, AOM_MIF_ANY_FRAME), -1); + ASSERT_EQ(aom_img_add_metadata(current_frame, OBU_METADATA_TYPE_ITUT_T35, kMetadataPayloadT35, kMetadataPayloadSizeT35, AOM_MIF_ANY_FRAME), 0); + ASSERT_EQ( + aom_img_add_metadata(current_frame, OBU_METADATA_TYPE_ITUT_T35, + kMetadataPayloadT35Two, kMetadataPayloadSizeT35Two, + AOM_MIF_ANY_FRAME_LAYER_SPECIFIC), + 0); + ASSERT_EQ(aom_img_add_metadata(current_frame, OBU_METADATA_TYPE_HDR_MDCV, kMetadataPayloadMdcv, kMetadataPayloadSizeMdcv, AOM_MIF_KEY_FRAME), @@ -143,32 +156,42 @@ ASSERT_NE(img.metadata, nullptr); - ASSERT_EQ(img.metadata->sz, is_key_frame ? 3 : 1); + ASSERT_EQ(img.metadata->sz, is_key_frame ? 4 : 2); - ASSERT_EQ(OBU_METADATA_TYPE_ITUT_T35, - img.metadata->metadata_array[0]->type); - ASSERT_EQ(kMetadataPayloadSizeT35, img.metadata->metadata_array[0]->sz); + aom_metadata_t *metadata = img.metadata->metadata_array[0]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_ITUT_T35); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeT35); EXPECT_EQ( - memcmp(kMetadataPayloadT35, img.metadata->metadata_array[0]->payload, - kMetadataPayloadSizeT35), + memcmp(kMetadataPayloadT35, metadata->payload, kMetadataPayloadSizeT35), 0); - if (is_key_frame) { - ASSERT_EQ(OBU_METADATA_TYPE_HDR_MDCV, - img.metadata->metadata_array[1]->type); - ASSERT_EQ(kMetadataPayloadSizeMdcv, img.metadata->metadata_array[1]->sz); - EXPECT_EQ( - memcmp(kMetadataPayloadMdcv, img.metadata->metadata_array[1]->payload, - kMetadataPayloadSizeMdcv), - 0); + metadata = img.metadata->metadata_array[1]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_ITUT_T35); + // AOM_MIF_ANY_FRAME and not AOM_MIF_ANY_FRAME_LAYER_SPECIFIC because the + // stream does not contain layers. + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeT35Two); + EXPECT_EQ(memcmp(kMetadataPayloadT35Two, metadata->payload, + kMetadataPayloadSizeT35Two), + 0); - ASSERT_EQ(OBU_METADATA_TYPE_HDR_CLL, - img.metadata->metadata_array[2]->type); - ASSERT_EQ(kMetadataPayloadSizeCll, img.metadata->metadata_array[2]->sz); - EXPECT_EQ( - memcmp(kMetadataPayloadCll, img.metadata->metadata_array[2]->payload, - kMetadataPayloadSizeCll), - 0); + if (is_key_frame) { + metadata = img.metadata->metadata_array[2]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_HDR_MDCV); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeMdcv); + EXPECT_EQ(memcmp(kMetadataPayloadMdcv, metadata->payload, + kMetadataPayloadSizeMdcv), + 0); + + metadata = img.metadata->metadata_array[3]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_HDR_CLL); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeCll); + EXPECT_EQ(memcmp(kMetadataPayloadCll, metadata->payload, + kMetadataPayloadSizeCll), + 0); } } @@ -243,6 +266,12 @@ AOM_MIF_ANY_FRAME), 0); + ASSERT_EQ( + aom_img_add_metadata(current_frame, OBU_METADATA_TYPE_ITUT_T35, + kMetadataPayloadT35Two, kMetadataPayloadSizeT35Two, + AOM_MIF_ANY_FRAME_LAYER_SPECIFIC), + 0); + ASSERT_EQ(aom_img_add_metadata(current_frame, OBU_METADATA_TYPE_HDR_MDCV, kMetadataPayloadMdcv, kMetadataPayloadSizeMdcv, AOM_MIF_KEY_FRAME), @@ -288,32 +317,40 @@ ASSERT_NE(img.metadata, nullptr); - ASSERT_EQ(img.metadata->sz, is_key_frame ? 3 : 1); + ASSERT_EQ(img.metadata->sz, is_key_frame ? 4 : 2); - ASSERT_EQ(OBU_METADATA_TYPE_ITUT_T35, - img.metadata->metadata_array[0]->type); - ASSERT_EQ(kMetadataPayloadSizeT35, img.metadata->metadata_array[0]->sz); + aom_metadata_t *metadata = img.metadata->metadata_array[0]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_ITUT_T35); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeT35); EXPECT_EQ( - memcmp(kMetadataPayloadT35, img.metadata->metadata_array[0]->payload, - kMetadataPayloadSizeT35), + memcmp(kMetadataPayloadT35, metadata->payload, kMetadataPayloadSizeT35), 0); - if (is_key_frame) { - ASSERT_EQ(OBU_METADATA_TYPE_HDR_MDCV, - img.metadata->metadata_array[1]->type); - ASSERT_EQ(kMetadataPayloadSizeMdcv, img.metadata->metadata_array[1]->sz); - EXPECT_EQ( - memcmp(kMetadataPayloadMdcv, img.metadata->metadata_array[1]->payload, - kMetadataPayloadSizeMdcv), - 0); + metadata = img.metadata->metadata_array[1]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_ITUT_T35); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME_LAYER_SPECIFIC); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeT35Two); + EXPECT_EQ(memcmp(kMetadataPayloadT35Two, metadata->payload, + kMetadataPayloadSizeT35Two), + 0); - ASSERT_EQ(OBU_METADATA_TYPE_HDR_CLL, - img.metadata->metadata_array[2]->type); - ASSERT_EQ(kMetadataPayloadSizeCll, img.metadata->metadata_array[2]->sz); - EXPECT_EQ( - memcmp(kMetadataPayloadCll, img.metadata->metadata_array[2]->payload, - kMetadataPayloadSizeCll), - 0); + if (is_key_frame) { + metadata = img.metadata->metadata_array[2]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_HDR_MDCV); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeMdcv); + EXPECT_EQ(memcmp(kMetadataPayloadMdcv, metadata->payload, + kMetadataPayloadSizeMdcv), + 0); + + metadata = img.metadata->metadata_array[3]; + ASSERT_EQ(metadata->type, OBU_METADATA_TYPE_HDR_CLL); + ASSERT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeCll); + EXPECT_EQ(memcmp(kMetadataPayloadCll, metadata->payload, + kMetadataPayloadSizeCll), + 0); } } @@ -402,6 +439,33 @@ -1); } +TEST(MetadataTest, AddLayerSpecificMetadataToImage) { + aom_image_t image; + image.metadata = nullptr; + + ASSERT_EQ( + aom_img_add_metadata( + &image, OBU_METADATA_TYPE_ITUT_T35, kMetadataPayloadT35, + kMetadataPayloadSizeT35, + (aom_metadata_insert_flags_t)(AOM_MIF_ANY_FRAME_LAYER_SPECIFIC)), + 0); + aom_img_metadata_array_free(image.metadata); +} + +TEST(MetadataTest, AddLayerSpecificMetadataToImageNotAllowed) { + aom_image_t image; + image.metadata = nullptr; + + // OBU_METADATA_TYPE_SCALABILITY cannot be layer specific. + ASSERT_EQ( + aom_img_add_metadata( + &image, OBU_METADATA_TYPE_SCALABILITY, kMetadataPayloadT35, + kMetadataPayloadSizeT35, + (aom_metadata_insert_flags_t)(AOM_MIF_ANY_FRAME_LAYER_SPECIFIC)), + -1); + aom_img_metadata_array_free(image.metadata); +} + TEST(MetadataTest, RemoveMetadataFromImage) { aom_image_t image; image.metadata = nullptr; @@ -454,9 +518,13 @@ kMetadataPayloadT35, kMetadataPayloadSizeT35, AOM_MIF_ANY_FRAME), 0); + ASSERT_EQ(aom_img_add_metadata(&image, OBU_METADATA_TYPE_ITUT_T35, + kMetadataPayloadT35, kMetadataPayloadSizeT35, + AOM_MIF_ANY_FRAME_LAYER_SPECIFIC), + 0); EXPECT_EQ(aom_img_get_metadata(nullptr, 0), nullptr); - EXPECT_EQ(aom_img_get_metadata(&image, 1u), nullptr); + EXPECT_EQ(aom_img_get_metadata(&image, 2u), nullptr); EXPECT_EQ(aom_img_get_metadata(&image, 10u), nullptr); const aom_metadata_t *metadata = aom_img_get_metadata(&image, 0); @@ -465,6 +533,15 @@ EXPECT_EQ( memcmp(kMetadataPayloadT35, metadata->payload, kMetadataPayloadSizeT35), 0); + EXPECT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME); + + metadata = aom_img_get_metadata(&image, 1); + ASSERT_NE(metadata, nullptr); + ASSERT_EQ(metadata->sz, kMetadataPayloadSizeT35); + EXPECT_EQ( + memcmp(kMetadataPayloadT35, metadata->payload, kMetadataPayloadSizeT35), + 0); + EXPECT_EQ(metadata->insert_flag, AOM_MIF_ANY_FRAME_LAYER_SPECIFIC); aom_img_metadata_array_free(image.metadata); }
diff --git a/test/minmax_test.cc b/test/minmax_test.cc index b87ee97..bd1a965 100644 --- a/test/minmax_test.cc +++ b/test/minmax_test.cc
@@ -26,8 +26,8 @@ using ::libaom_test::ACMRandom; -typedef void (*MinMaxFunc)(const uint8_t *a, int a_stride, const uint8_t *b, - int b_stride, int *min, int *max); +using MinMaxFunc = void (*)(const uint8_t *a, int a_stride, const uint8_t *b, + int b_stride, int *min, int *max); class MinMaxTest : public ::testing::TestWithParam<MinMaxFunc> { public:
diff --git a/test/monochrome_test.cc b/test/monochrome_test.cc index 78afd4f..da2b3d9 100644 --- a/test/monochrome_test.cc +++ b/test/monochrome_test.cc
@@ -34,7 +34,7 @@ // kPsnrFluctuation represents the maximum allowed psnr fluctuation w.r.t first // frame. The indices correspond to one/two-pass, allintra and realtime // encoding modes. -const double kPsnrFluctuation[3] = { 2.5, 0.3, 17.0 }; +const double kPsnrFluctuation[3] = { 8.0, 0.3, 17.0 }; class MonochromeTest : public ::libaom_test::CodecTestWith3Params<libaom_test::TestMode, int,
diff --git a/test/multilayer_metadata_test.cc b/test/multilayer_metadata_test.cc index 33ce45f..faaf75f 100644 --- a/test/multilayer_metadata_test.cc +++ b/test/multilayer_metadata_test.cc
@@ -22,27 +22,27 @@ TEST(MultilayerMetadataTest, ParseAlpha) { const std::string metadata = R"( -use_case: 1 # global alpha -layers: - - layer_type: 5 # alpha - luma_plane_only_flag: 1 - layer_metadata_scope: 2 # global - alpha: - alpha_use_idc: 1 # premultiplied - alpha_bit_depth: 8 - alpha_transparent_value: 0 - alpha_opaque_value: 4 + use_case: 1 # global alpha + layers: + - layer_type: 5 # alpha + luma_plane_only_flag: 1 + layer_metadata_scope: 2 # global + alpha: + alpha_use_idc: 1 # premultiplied + alpha_bit_depth: 8 + alpha_transparent_value: 0 + alpha_opaque_value: 4 - - layer_type: 1 # texture - luma_plane_only_flag: 0 - layer_metadata_scope: 2 # global - layer_color_description: - color_range: 1 - color_primaries: 1 - transfer_characteristics: 13 - matrix_coefficients: 6 + - layer_type: 1 # texture + luma_plane_only_flag: 0 + layer_metadata_scope: 2 # global + layer_color_description: + color_range: 1 + color_primaries: 1 + transfer_characteristics: 13 + matrix_coefficients: 6 - )"; + )"; libaom_test::TempOutFile tmp_file(/*text_mode=*/true); fprintf(tmp_file.file(), "%s", metadata.c_str()); fflush(tmp_file.file()); @@ -55,10 +55,10 @@ EXPECT_EQ(multilayer.layers[0].layer_type, 5); EXPECT_EQ(multilayer.layers[0].luma_plane_only_flag, 1); EXPECT_EQ(multilayer.layers[0].layer_metadata_scope, 2); - EXPECT_EQ(multilayer.layers[0].global_alpha_info.alpha_use_idc, 1); - EXPECT_EQ(multilayer.layers[0].global_alpha_info.alpha_bit_depth, 8); - EXPECT_EQ(multilayer.layers[0].global_alpha_info.alpha_transparent_value, 0); - EXPECT_EQ(multilayer.layers[0].global_alpha_info.alpha_opaque_value, 4); + EXPECT_EQ(multilayer.layers[0].alpha.alpha_use_idc, 1); + EXPECT_EQ(multilayer.layers[0].alpha.alpha_bit_depth, 8); + EXPECT_EQ(multilayer.layers[0].alpha.alpha_transparent_value, 0); + EXPECT_EQ(multilayer.layers[0].alpha.alpha_opaque_value, 4); EXPECT_EQ(multilayer.layers[1].layer_type, 1); EXPECT_EQ(multilayer.layers[1].luma_plane_only_flag, 0); EXPECT_EQ(multilayer.layers[1].layer_metadata_scope, 2); @@ -76,26 +76,26 @@ TEST(MultilayerMetadataTest, ParseDepth) { const std::string metadata = R"( -use_case: 2 # global depth -layers: - - layer_type: 6 # depth - luma_plane_only_flag: 1 - layer_metadata_scope: 2 # global - depth: - z_near: 1.456 - z_far: 9.786 - depth_representation_type: 2 + use_case: 2 # global depth + layers: + - layer_type: 6 # depth + luma_plane_only_flag: 1 + layer_metadata_scope: 2 # global + depth: + z_near: 1.456 + z_far: 9.786 + depth_representation_type: 2 - - layer_type: 1 # texture - luma_plane_only_flag: 0 - layer_metadata_scope: 2 # global - layer_color_description: - color_range: 1 - color_primaries: 1 - transfer_characteristics: 13 - matrix_coefficients: 6 + - layer_type: 1 # texture + luma_plane_only_flag: 0 + layer_metadata_scope: 2 # global + layer_color_description: + color_range: 1 + color_primaries: 1 + transfer_characteristics: 13 + matrix_coefficients: 6 - )"; + )"; libaom_test::TempOutFile tmp_file(/*text_mode=*/true); fprintf(tmp_file.file(), "%s", metadata.c_str()); fflush(tmp_file.file()); @@ -107,16 +107,15 @@ EXPECT_EQ(multilayer.layers[0].layer_type, 6); EXPECT_EQ(multilayer.layers[0].luma_plane_only_flag, 1); EXPECT_EQ(multilayer.layers[0].layer_metadata_scope, 2); - EXPECT_TRUE(multilayer.layers[0].global_depth_info.z_near.second); + EXPECT_TRUE(multilayer.layers[0].depth.z_near.second); EXPECT_NEAR(depth_representation_element_to_double( - multilayer.layers[0].global_depth_info.z_near.first), + multilayer.layers[0].depth.z_near.first), 1.456, 0.00001); - EXPECT_TRUE(multilayer.layers[0].global_depth_info.z_far.second); + EXPECT_TRUE(multilayer.layers[0].depth.z_far.second); EXPECT_NEAR(depth_representation_element_to_double( - multilayer.layers[0].global_depth_info.z_far.first), + multilayer.layers[0].depth.z_far.first), 9.786, 0.00001); - EXPECT_EQ(multilayer.layers[0].global_depth_info.depth_representation_type, - 2); + EXPECT_EQ(multilayer.layers[0].depth.depth_representation_type, 2); EXPECT_EQ(multilayer.layers[1].layer_type, 1); EXPECT_EQ(multilayer.layers[1].luma_plane_only_flag, 0); EXPECT_EQ(multilayer.layers[1].layer_metadata_scope, 2); @@ -132,22 +131,115 @@ 6); } -TEST(MultilayerMetadataTest, ParseInvalid) { +TEST(MultilayerMetadataTest, ParseLocalDepth) { const std::string metadata = R"( - -use_case: 1 # global alpha +use_case: 4 # depth layers: - - layer_type: 5 # alpha + - layer_type: 6 # depth luma_plane_only_flag: 1 - layer_metadata_scope: 2 # global + layer_metadata_scope: 3 # mixed + depth: + z_near: 1.456 + z_far: 9.786 + depth_representation_type: 2 + local_metadata: + - frame_idx: 4 + depth: + z_near: 2.78933 + z_far: 20.663 + depth_representation_type: 0 + - frame_idx: 100 + depth: + z_near: 0 + z_far: 24 + depth_representation_type: 0 - layer_type: 1 # texture luma_plane_only_flag: 0 - layer_metadata_scope: 2 # global + layer_metadata_scope: 3 # mixed + layer_color_description: + color_range: 1 + color_primaries: 1 + transfer_characteristics: 13 + matrix_coefficients: 6 + )"; + libaom_test::TempOutFile tmp_file(/*text_mode=*/true); + fprintf(tmp_file.file(), "%s", metadata.c_str()); + fflush(tmp_file.file()); + + MultilayerMetadata multilayer; + EXPECT_TRUE(parse_multilayer_file(tmp_file.file_name().c_str(), &multilayer)); + EXPECT_EQ(multilayer.use_case, 4); + ASSERT_EQ(multilayer.layers.size(), 2); + EXPECT_EQ(multilayer.layers[0].layer_type, 6); + EXPECT_EQ(multilayer.layers[0].luma_plane_only_flag, 1); + EXPECT_EQ(multilayer.layers[0].layer_metadata_scope, 3); + EXPECT_TRUE(multilayer.layers[0].depth.z_near.second); + EXPECT_NEAR(depth_representation_element_to_double( + multilayer.layers[0].depth.z_near.first), + 1.456, 0.00001); + EXPECT_TRUE(multilayer.layers[0].depth.z_far.second); + EXPECT_NEAR(depth_representation_element_to_double( + multilayer.layers[0].depth.z_far.first), + 9.786, 0.00001); + EXPECT_EQ(multilayer.layers[0].depth.depth_representation_type, 2); + ASSERT_EQ(multilayer.layers[0].local_metadata.size(), 2); + EXPECT_EQ(multilayer.layers[0].local_metadata[0].frame_idx, 4); + EXPECT_TRUE(multilayer.layers[0].local_metadata[0].depth.z_near.second); + EXPECT_NEAR(depth_representation_element_to_double( + multilayer.layers[0].local_metadata[0].depth.z_near.first), + 2.78933, 0.00001); + EXPECT_TRUE(multilayer.layers[0].local_metadata[0].depth.z_far.second); + EXPECT_NEAR(depth_representation_element_to_double( + multilayer.layers[0].local_metadata[0].depth.z_far.first), + 20.663, 0.00001); + EXPECT_EQ( + multilayer.layers[0].local_metadata[0].depth.depth_representation_type, + 0); + EXPECT_EQ(multilayer.layers[0].local_metadata[1].frame_idx, 100); + EXPECT_TRUE(multilayer.layers[0].local_metadata[1].depth.z_near.second); + EXPECT_NEAR(depth_representation_element_to_double( + multilayer.layers[0].local_metadata[1].depth.z_near.first), + 0, 0.00001); + EXPECT_TRUE(multilayer.layers[0].local_metadata[1].depth.z_far.second); + EXPECT_NEAR(depth_representation_element_to_double( + multilayer.layers[0].local_metadata[1].depth.z_far.first), + 24, 0.00001); + EXPECT_EQ( + multilayer.layers[0].local_metadata[1].depth.depth_representation_type, + 0); + EXPECT_EQ(multilayer.layers[1].layer_type, 1); + EXPECT_EQ(multilayer.layers[1].luma_plane_only_flag, 0); + EXPECT_EQ(multilayer.layers[1].layer_metadata_scope, 3); + EXPECT_TRUE(multilayer.layers[1].layer_color_description.second); + EXPECT_EQ(multilayer.layers[1].layer_color_description.first.color_range, 1); + EXPECT_EQ(multilayer.layers[1].layer_color_description.first.color_primaries, + 1); + EXPECT_EQ(multilayer.layers[1] + .layer_color_description.first.transfer_characteristics, + 13); + EXPECT_EQ( + multilayer.layers[1].layer_color_description.first.matrix_coefficients, + 6); + EXPECT_EQ(multilayer.layers[1].local_metadata.size(), 0); +} + +TEST(MultilayerMetadataTest, ParseInvalid) { + const std::string metadata = R"( + +use_case: 3 # alpha +layers: + - layer_type: 5 # alpha + luma_plane_only_flag: 1 + layer_metadata_scope: 3 # mixed + + - layer_type: 1 # texture + luma_plane_only_flag: 0 + layer_metadata_scope: 3 # mixed - layer_type: 6 # depth => bad layer type luma_plane_only_flag: 1 - layer_metadata_scope: 2 # global + layer_metadata_scope: 3 # mixed )"; libaom_test::TempOutFile tmp_file(/*text_mode=*/true); fprintf(tmp_file.file(), "%s", metadata.c_str()); @@ -162,16 +254,16 @@ TEST(MultilayerMetadataTest, ParseBadIndent) { const std::string metadata = R"( -use_case: 1 # global alpha -layers: - - layer_type: 5 # alpha - luma_plane_only_flag: 1 - layer_metadata_scope: 2 # global + use_case: 1 # global alpha + layers: + - layer_type: 5 # alpha + luma_plane_only_flag: 1 + layer_metadata_scope: 2 # global - - layer_type: 1 # texture - luma_plane_only_flag: 0 - layer_metadata_scope: 2 # global - )"; + - layer_type: 1 # texture + luma_plane_only_flag: 0 + layer_metadata_scope: 2 # global + )"; libaom_test::TempOutFile tmp_file(/*text_mode=*/true); fprintf(tmp_file.file(), "%s", metadata.c_str()); fflush(tmp_file.file()); @@ -185,17 +277,17 @@ TEST(MultilayerMetadataTest, ParseUnknownField) { const std::string metadata = R"( -use_case: 1 # global alpha -layers: - - layer_type: 5 # alpha - luma_plane_only_flag: 1 - layer_metadata_scope: 2 # global - foobar: 42 + use_case: 1 # global alpha + layers: + - layer_type: 5 # alpha + luma_plane_only_flag: 1 + layer_metadata_scope: 2 # global + foobar: 42 - - layer_type: 1 # texture - luma_plane_only_flag: 0 - layer_metadata_scope: 2 # global - )"; + - layer_type: 1 # texture + luma_plane_only_flag: 0 + layer_metadata_scope: 2 # global + )"; libaom_test::TempOutFile tmp_file(/*text_mode=*/true); fprintf(tmp_file.file(), "%s", metadata.c_str()); fflush(tmp_file.file());
diff --git a/test/noise_model_test.cc b/test/noise_model_test.cc index 9f05a44..23f300c 100644 --- a/test/noise_model_test.cc +++ b/test/noise_model_test.cc
@@ -359,7 +359,7 @@ // (uint8_t and uint16_t) and bit depths (8, 10, 12). template <typename T, int bit_depth, bool use_highbd> struct BitDepthParams { - typedef T data_type_t; + using data_type_t = T; static const int kBitDepth = bit_depth; static const bool kUseHighBD = use_highbd; }; @@ -368,7 +368,7 @@ class FlatBlockEstimatorTest : public ::testing::Test, public T { public: void SetUp() override { random_.Reset(171); } - typedef std::vector<typename T::data_type_t> VecType; + using VecType = std::vector<typename T::data_type_t>; VecType data_; libaom_test::ACMRandom random_; }; @@ -527,11 +527,9 @@ REGISTER_TYPED_TEST_SUITE_P(FlatBlockEstimatorTest, ExtractBlock, FindFlatBlocks); -typedef ::testing::Types<BitDepthParams<uint8_t, 8, false>, // lowbd - BitDepthParams<uint16_t, 8, true>, // lowbd in 16-bit - BitDepthParams<uint16_t, 10, true>, // highbd data - BitDepthParams<uint16_t, 12, true> > - AllBitDepthParams; +using AllBitDepthParams = ::testing::Types< + BitDepthParams<uint8_t, 8, false>, BitDepthParams<uint16_t, 8, true>, + BitDepthParams<uint16_t, 10, true>, BitDepthParams<uint16_t, 12, true>>; // Note the empty final argument can be removed if C++20 is made the minimum // requirement. INSTANTIATE_TYPED_TEST_SUITE_P(FlatBlockInstatiation, FlatBlockEstimatorTest,
diff --git a/test/obmc_sad_test.cc b/test/obmc_sad_test.cc index dd6484f..17f3251 100644 --- a/test/obmc_sad_test.cc +++ b/test/obmc_sad_test.cc
@@ -28,9 +28,9 @@ static const int kIterations = 1000; static const int kMaskMax = 64; -typedef unsigned int (*ObmcSadF)(const uint8_t *pre, int pre_stride, - const int32_t *wsrc, const int32_t *mask); -typedef libaom_test::FuncParam<ObmcSadF> TestFuncs; +using ObmcSadF = unsigned int (*)(const uint8_t *pre, int pre_stride, + const int32_t *wsrc, const int32_t *mask); +using TestFuncs = libaom_test::FuncParam<ObmcSadF>; //////////////////////////////////////////////////////////////////////////////// // 8 bit
diff --git a/test/obmc_variance_test.cc b/test/obmc_variance_test.cc index 39b816e..16482f7 100644 --- a/test/obmc_variance_test.cc +++ b/test/obmc_variance_test.cc
@@ -30,10 +30,10 @@ static const int kIterations = 1000; static const int kMaskMax = 64; -typedef unsigned int (*ObmcVarF)(const uint8_t *pre, int pre_stride, - const int32_t *wsrc, const int32_t *mask, - unsigned int *sse); -typedef libaom_test::FuncParam<ObmcVarF> TestFuncs; +using ObmcVarF = unsigned int (*)(const uint8_t *pre, int pre_stride, + const int32_t *wsrc, const int32_t *mask, + unsigned int *sse); +using TestFuncs = libaom_test::FuncParam<ObmcVarF>; //////////////////////////////////////////////////////////////////////////////// // 8 bit
diff --git a/test/pickrst_test.cc b/test/pickrst_test.cc index fd25b0c..98b686d 100644 --- a/test/pickrst_test.cc +++ b/test/pickrst_test.cc
@@ -29,7 +29,7 @@ namespace pickrst_test_lowbd { static const int kIterations = 100; -typedef int64_t (*lowbd_pixel_proj_error_func)( +using lowbd_pixel_proj_error_func = int64_t (*)( const uint8_t *src8, int width, int height, int src_stride, const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride, int32_t *flt1, int flt1_stride, int xq[2], const sgr_params_type *params); @@ -38,7 +38,7 @@ // 8 bit //////////////////////////////////////////////////////////////////////////////// -typedef std::tuple<const lowbd_pixel_proj_error_func> PixelProjErrorTestParam; +using PixelProjErrorTestParam = std::tuple<const lowbd_pixel_proj_error_func>; class PixelProjErrorTest : public ::testing::TestWithParam<PixelProjErrorTestParam> { @@ -201,7 +201,7 @@ namespace pickrst_test_highbd { static const int kIterations = 100; -typedef int64_t (*highbd_pixel_proj_error_func)( +using highbd_pixel_proj_error_func = int64_t (*)( const uint8_t *src8, int width, int height, int src_stride, const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride, int32_t *flt1, int flt1_stride, int xq[2], const sgr_params_type *params); @@ -210,7 +210,7 @@ // High bit-depth //////////////////////////////////////////////////////////////////////////////// -typedef std::tuple<const highbd_pixel_proj_error_func> PixelProjErrorTestParam; +using PixelProjErrorTestParam = std::tuple<const highbd_pixel_proj_error_func>; class PixelProjHighbdErrorTest : public ::testing::TestWithParam<PixelProjErrorTestParam> { @@ -379,15 +379,15 @@ namespace get_proj_subspace_test_lowbd { static const int kIterations = 100; -typedef void (*set_get_proj_subspace)(const uint8_t *src8, int width, - int height, int src_stride, - const uint8_t *dat8, int dat_stride, - int32_t *flt0, int flt0_stride, - int32_t *flt1, int flt1_stride, - int64_t H[2][2], int64_t C[2], - const sgr_params_type *params); +using set_get_proj_subspace = void (*)(const uint8_t *src8, int width, + int height, int src_stride, + const uint8_t *dat8, int dat_stride, + int32_t *flt0, int flt0_stride, + int32_t *flt1, int flt1_stride, + int64_t H[2][2], int64_t C[2], + const sgr_params_type *params); -typedef std::tuple<const set_get_proj_subspace> GetProjSubspaceTestParam; +using GetProjSubspaceTestParam = std::tuple<const set_get_proj_subspace>; class GetProjSubspaceTest : public ::testing::TestWithParam<GetProjSubspaceTestParam> { @@ -564,15 +564,15 @@ namespace get_proj_subspace_test_hbd { static const int kIterations = 100; -typedef void (*set_get_proj_subspace_hbd)(const uint8_t *src8, int width, - int height, int src_stride, - const uint8_t *dat8, int dat_stride, - int32_t *flt0, int flt0_stride, - int32_t *flt1, int flt1_stride, - int64_t H[2][2], int64_t C[2], - const sgr_params_type *params); +using set_get_proj_subspace_hbd = void (*)(const uint8_t *src8, int width, + int height, int src_stride, + const uint8_t *dat8, int dat_stride, + int32_t *flt0, int flt0_stride, + int32_t *flt1, int flt1_stride, + int64_t H[2][2], int64_t C[2], + const sgr_params_type *params); -typedef std::tuple<const set_get_proj_subspace_hbd> GetProjSubspaceHBDTestParam; +using GetProjSubspaceHBDTestParam = std::tuple<const set_get_proj_subspace_hbd>; class GetProjSubspaceTestHBD : public ::testing::TestWithParam<GetProjSubspaceHBDTestParam> {
diff --git a/test/quant_test.cc b/test/quant_test.cc index 120eae7..505d19d 100644 --- a/test/quant_test.cc +++ b/test/quant_test.cc
@@ -91,10 +91,10 @@ ::testing::Range(5, 9)); #if !CONFIG_REALTIME_ONLY -typedef struct { +struct QuantParam { const unsigned int min_q; const unsigned int max_q; -} QuantParam; +}; const QuantParam QuantTestParams[] = { { 0, 10 }, { 0, 60 }, { 20, 35 }, { 35, 50 }, { 50, 63 }
diff --git a/test/quantize_func_test.cc b/test/quantize_func_test.cc index 782f93c..64d9ce4 100644 --- a/test/quantize_func_test.cc +++ b/test/quantize_func_test.cc
@@ -43,9 +43,9 @@ const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, \ const int16_t *iscan -typedef void (*LPQuantizeFunc)(LP_QUANTIZE_PARAM_LIST); -typedef void (*QuantizeFunc)(QUAN_PARAM_LIST); -typedef void (*QuantizeFuncHbd)(QUAN_PARAM_LIST, int log_scale); +using LPQuantizeFunc = void (*)(LP_QUANTIZE_PARAM_LIST); +using QuantizeFunc = void (*)(QUAN_PARAM_LIST); +using QuantizeFuncHbd = void (*)(QUAN_PARAM_LIST, int log_scale); #undef LP_QUANTIZE_PARAM_LIST @@ -83,10 +83,10 @@ using QuantizeParam = tuple<FuncType, FuncType, TX_SIZE, QuantType, aom_bit_depth_t>; -typedef struct { +struct QuanTable { QUANTS quant; Dequants dequant; -} QuanTable; +}; const int kTestNum = 1000;
diff --git a/test/resize_test.cc b/test/resize_test.cc index 26392a8..3ddd969 100644 --- a/test/resize_test.cc +++ b/test/resize_test.cc
@@ -365,7 +365,7 @@ libaom_test::Encoder *encoder) override { if (video->frame() == 0) { encoder->Control(AOME_SET_CPUUSED, cpu_used_); - encoder->Control(AOME_SET_TUNING, AOM_TUNE_SSIM); + encoder->Control(AOME_SET_TUNING, AOM_TUNE_IQ); encoder->Control(AOME_SET_CQ_LEVEL, first_frame_cq_level_); encoder->Control(AV1E_SET_ROW_MT, 1); encoder->Control(AV1E_SET_TILE_COLUMNS, first_tile_rows_cols_); @@ -400,6 +400,7 @@ cfg_.g_input_bit_depth = 8; cfg_.g_threads = 14; cfg_.rc_end_usage = AOM_Q; + cfg_.use_fixed_qp_offsets = 2; first_frame_cq_level_ = 59; second_frame_cq_level_ = 59; @@ -419,6 +420,7 @@ cfg_.g_input_bit_depth = 8; cfg_.g_threads = 4; cfg_.rc_end_usage = AOM_Q; + cfg_.use_fixed_qp_offsets = 2; first_frame_cq_level_ = 63; second_frame_cq_level_ = 50;
diff --git a/test/rt_end_to_end_test.cc b/test/rt_end_to_end_test.cc index dced915..9459aae 100644 --- a/test/rt_end_to_end_test.cc +++ b/test/rt_end_to_end_test.cc
@@ -33,40 +33,40 @@ std::unordered_map<int, std::unordered_map<int, double>>> kPsnrThreshold = { { "park_joy_90p_8_420.y4m", { { 5, { { 0, 34.0 }, { 3, 35.0 } } }, - { 6, { { 0, 34.0 }, { 3, 35.0 } } }, + { 6, { { 0, 33.8 }, { 3, 34.8 } } }, { 7, { { 0, 33.0 }, { 3, 34.0 } } }, { 8, { { 0, 33.0 }, { 3, 34.0 } } }, { 9, { { 0, 33.0 }, { 3, 34.0 } } }, { 10, { { 0, 33.0 }, { 3, 34.0 } } } } }, { "paris_352_288_30.y4m", { { 5, { { 0, 35.0 }, { 3, 35.0 } } }, - { 6, { { 0, 35.0 }, { 3, 35.0 } } }, + { 6, { { 0, 34.8 }, { 3, 34.8 } } }, { 7, { { 0, 34.0 }, { 3, 34.0 } } }, { 8, { { 0, 34.0 }, { 3, 35.0 } } }, { 9, { { 0, 34.0 }, { 3, 34.0 } } }, { 10, { { 0, 34.0 }, { 3, 34.0 } } } } }, { "niklas_1280_720_30.y4m", { { 5, { { 0, 32.0 }, { 3, 32.0 } } }, - { 6, { { 0, 32.0 }, { 3, 32.0 } } }, + { 6, { { 0, 31.8 }, { 3, 31.8 } } }, { 7, { { 0, 31.0 }, { 3, 31.0 } } }, { 8, { { 0, 31.0 }, { 3, 31.0 } } }, { 9, { { 0, 31.0 }, { 3, 31.0 } } }, { 10, { { 0, 31.0 }, { 3, 31.0 } } } } }, { "hantro_collage_w352h288_nv12.yuv", { { 5, { { 0, 32.0 }, { 3, 32.0 } } }, - { 6, { { 0, 32.0 }, { 3, 32.0 } } }, - { 7, { { 0, 32.0 }, { 3, 32.0 } } }, - { 8, { { 0, 32.0 }, { 3, 32.0 } } }, + { 6, { { 0, 31.8 }, { 3, 31.8 } } }, + { 7, { { 0, 31.8 }, { 3, 31.8 } } }, + { 8, { { 0, 31.8 }, { 3, 31.8 } } }, { 9, { { 0, 31.0 }, { 3, 31.0 } } }, { 10, { { 0, 31.0 }, { 3, 31.0 } } } } } }; -typedef struct { +struct TestVideoParam { const char *filename; unsigned int input_bit_depth; aom_img_fmt fmt; aom_bit_depth_t bit_depth; unsigned int profile; -} TestVideoParam; +}; std::ostream &operator<<(std::ostream &os, const TestVideoParam &test_arg) { return os << "TestVideoParam { filename:" << test_arg.filename
diff --git a/test/sad_test.cc b/test/sad_test.cc index af73b68..416f0ee 100644 --- a/test/sad_test.cc +++ b/test/sad_test.cc
@@ -26,41 +26,41 @@ #include "aom_mem/aom_mem.h" #include "aom_ports/mem.h" -typedef unsigned int (*SadMxNFunc)(const uint8_t *src_ptr, int src_stride, - const uint8_t *ref_ptr, int ref_stride); -typedef std::tuple<int, int, SadMxNFunc, int> SadMxNParam; +using SadMxNFunc = unsigned int (*)(const uint8_t *src_ptr, int src_stride, + const uint8_t *ref_ptr, int ref_stride); +using SadMxNParam = std::tuple<int, int, SadMxNFunc, int>; -typedef unsigned int (*SadSkipMxNFunc)(const uint8_t *src_ptr, int src_stride, - const uint8_t *ref_ptr, int ref_stride); -typedef std::tuple<int, int, SadSkipMxNFunc, int> SadSkipMxNParam; +using SadSkipMxNFunc = unsigned int (*)(const uint8_t *src_ptr, int src_stride, + const uint8_t *ref_ptr, int ref_stride); +using SadSkipMxNParam = std::tuple<int, int, SadSkipMxNFunc, int>; -typedef uint32_t (*SadMxNAvgFunc)(const uint8_t *src_ptr, int src_stride, - const uint8_t *ref_ptr, int ref_stride, - const uint8_t *second_pred); -typedef std::tuple<int, int, SadMxNAvgFunc, int> SadMxNAvgParam; +using SadMxNAvgFunc = uint32_t (*)(const uint8_t *src_ptr, int src_stride, + const uint8_t *ref_ptr, int ref_stride, + const uint8_t *second_pred); +using SadMxNAvgParam = std::tuple<int, int, SadMxNAvgFunc, int>; -typedef unsigned int (*DistWtdSadMxhFunc)(const uint8_t *src_ptr, - int src_stride, - const uint8_t *ref_ptr, - int ref_stride, int width, - int height); -typedef std::tuple<int, int, DistWtdSadMxhFunc, int> DistWtdSadMxhParam; +using DistWtdSadMxhFunc = unsigned int (*)(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int width, + int height); +using DistWtdSadMxhParam = std::tuple<int, int, DistWtdSadMxhFunc, int>; -typedef void (*SadMxNx4Func)(const uint8_t *src_ptr, int src_stride, - const uint8_t *const ref_ptr[], int ref_stride, - uint32_t *sad_array); -typedef std::tuple<int, int, SadMxNx4Func, int> SadMxNx4Param; +using SadMxNx4Func = void (*)(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[], int ref_stride, + uint32_t *sad_array); +using SadMxNx4Param = std::tuple<int, int, SadMxNx4Func, int>; -typedef void (*SadSkipMxNx4Func)(const uint8_t *src_ptr, int src_stride, +using SadSkipMxNx4Func = void (*)(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_ptr[], + int ref_stride, uint32_t *sad_array); +using SadSkipMxNx4Param = std::tuple<int, int, SadSkipMxNx4Func, int>; + +using SadMxNx4AvgFunc = void (*)(const uint8_t *src_ptr, int src_stride, const uint8_t *const ref_ptr[], int ref_stride, + const uint8_t *second_pred, uint32_t *sad_array); -typedef std::tuple<int, int, SadSkipMxNx4Func, int> SadSkipMxNx4Param; - -typedef void (*SadMxNx4AvgFunc)(const uint8_t *src_ptr, int src_stride, - const uint8_t *const ref_ptr[], int ref_stride, - const uint8_t *second_pred, - uint32_t *sad_array); -typedef std::tuple<int, int, SadMxNx4AvgFunc, int> SadMxNx4AvgParam; +using SadMxNx4AvgParam = std::tuple<int, int, SadMxNx4AvgFunc, int>; using libaom_test::ACMRandom;
diff --git a/test/selfguided_filter_test.cc b/test/selfguided_filter_test.cc index 7540036..4a2d814 100644 --- a/test/selfguided_filter_test.cc +++ b/test/selfguided_filter_test.cc
@@ -31,13 +31,13 @@ using std::make_tuple; using std::tuple; -typedef int (*SgrFunc)(const uint8_t *dat8, int width, int height, int stride, - int eps, const int *xqd, uint8_t *dst8, int dst_stride, - int32_t *tmpbuf, int bit_depth, int highbd); +using SgrFunc = int (*)(const uint8_t *dat8, int width, int height, int stride, + int eps, const int *xqd, uint8_t *dst8, int dst_stride, + int32_t *tmpbuf, int bit_depth, int highbd); // Test parameter list: // <tst_fun_> -typedef tuple<SgrFunc> FilterTestParam; +using FilterTestParam = tuple<SgrFunc>; class AV1SelfguidedFilterTest : public ::testing::TestWithParam<FilterTestParam> { @@ -232,7 +232,7 @@ #if CONFIG_AV1_HIGHBITDEPTH // Test parameter list: // <tst_fun_, bit_depth> -typedef tuple<SgrFunc, int> HighbdFilterTestParam; +using HighbdFilterTestParam = tuple<SgrFunc, int>; class AV1HighbdSelfguidedFilterTest : public ::testing::TestWithParam<HighbdFilterTestParam> {
diff --git a/test/simd_cmp_impl.inc b/test/simd_cmp_impl.inc index 0a9a195..a2373d7 100644 --- a/test/simd_cmp_impl.inc +++ b/test/simd_cmp_impl.inc
@@ -463,13 +463,13 @@ return c_v256_ssd_s16_sum(::c_v256_ssd_s16(c_v256_ssd_s16_init(), a, b)); } -typedef void (*fptr)(); +using fptr = void (*)(); -typedef struct { +struct mapping { const char *name; fptr ref; fptr simd; -} mapping; +}; #define MAP(name) \ { #name, reinterpret_cast < fptr>(c_##name), reinterpret_cast < fptr>(name) }
diff --git a/test/simd_impl.h b/test/simd_impl.h index 20737a0..cb5b2d6 100644 --- a/test/simd_impl.h +++ b/test/simd_impl.h
@@ -35,9 +35,9 @@ }; // Create one typedef for each function signature -#define TYPEDEF_SIMD(name) \ - typedef TestIntrinsic<std::tuple<uint32_t, uint32_t, const char *> > \ - ARCH_POSTFIX(name) +#define TYPEDEF_SIMD(name) \ + using ARCH_POSTFIX(name) = \ + TestIntrinsic<std::tuple<uint32_t, uint32_t, const char *> > TYPEDEF_SIMD(V64_U8); TYPEDEF_SIMD(V64_U16); @@ -87,17 +87,17 @@ TYPEDEF_SIMD(V64_V256); // Google Test allows up to 50 tests per case, so split the largest -typedef ARCH_POSTFIX(V64_V64) ARCH_POSTFIX(V64_V64_Part2); -typedef ARCH_POSTFIX(V64_V64V64) ARCH_POSTFIX(V64_V64V64_Part2); -typedef ARCH_POSTFIX(V128_V128) ARCH_POSTFIX(V128_V128_Part2); -typedef ARCH_POSTFIX(V128_V128) ARCH_POSTFIX(V128_V128_Part3); -typedef ARCH_POSTFIX(V128_V128) ARCH_POSTFIX(V128_V128_Part4); -typedef ARCH_POSTFIX(V128_V128V128) ARCH_POSTFIX(V128_V128V128_Part2); -typedef ARCH_POSTFIX(V256_V256) ARCH_POSTFIX(V256_V256_Part2); -typedef ARCH_POSTFIX(V256_V256) ARCH_POSTFIX(V256_V256_Part3); -typedef ARCH_POSTFIX(V256_V256) ARCH_POSTFIX(V256_V256_Part4); -typedef ARCH_POSTFIX(V256_V256) ARCH_POSTFIX(V256_V256_Part5); -typedef ARCH_POSTFIX(V256_V256V256) ARCH_POSTFIX(V256_V256V256_Part2); +using ARCH_POSTFIX(V64_V64_Part2) = ARCH_POSTFIX(V64_V64); +using ARCH_POSTFIX(V64_V64V64_Part2) = ARCH_POSTFIX(V64_V64V64); +using ARCH_POSTFIX(V128_V128_Part2) = ARCH_POSTFIX(V128_V128); +using ARCH_POSTFIX(V128_V128_Part3) = ARCH_POSTFIX(V128_V128); +using ARCH_POSTFIX(V128_V128_Part4) = ARCH_POSTFIX(V128_V128); +using ARCH_POSTFIX(V128_V128V128_Part2) = ARCH_POSTFIX(V128_V128V128); +using ARCH_POSTFIX(V256_V256_Part2) = ARCH_POSTFIX(V256_V256); +using ARCH_POSTFIX(V256_V256_Part3) = ARCH_POSTFIX(V256_V256); +using ARCH_POSTFIX(V256_V256_Part4) = ARCH_POSTFIX(V256_V256); +using ARCH_POSTFIX(V256_V256_Part5) = ARCH_POSTFIX(V256_V256); +using ARCH_POSTFIX(V256_V256V256_Part2) = ARCH_POSTFIX(V256_V256V256); // These functions are machine tuned located elsewhere template <typename c_ret, typename c_arg>
diff --git a/test/sse_sum_test.cc b/test/sse_sum_test.cc index 54fbeae..8b33cf1 100644 --- a/test/sse_sum_test.cc +++ b/test/sse_sum_test.cc
@@ -35,9 +35,9 @@ namespace { const int kNumIterations = 10000; -typedef uint64_t (*SSI16Func)(const int16_t *src, int src_stride, int width, - int height, int *sum); -typedef libaom_test::FuncParam<SSI16Func> TestFuncs; +using SSI16Func = uint64_t (*)(const int16_t *src, int src_stride, int width, + int height, int *sum); +using TestFuncs = libaom_test::FuncParam<SSI16Func>; class SumSSETest : public ::testing::TestWithParam<TestFuncs> { public:
diff --git a/test/subtract_test.cc b/test/subtract_test.cc index ca2b2a9..c478210 100644 --- a/test/subtract_test.cc +++ b/test/subtract_test.cc
@@ -24,10 +24,10 @@ #include "aom_mem/aom_mem.h" #include "aom_ports/mem.h" -typedef void (*SubtractFunc)(int rows, int cols, int16_t *diff_ptr, - ptrdiff_t diff_stride, const uint8_t *src_ptr, - ptrdiff_t src_stride, const uint8_t *pred_ptr, - ptrdiff_t pred_stride); +using SubtractFunc = void (*)(int rows, int cols, int16_t *diff_ptr, + ptrdiff_t diff_stride, const uint8_t *src_ptr, + ptrdiff_t src_stride, const uint8_t *pred_ptr, + ptrdiff_t pred_stride); namespace {
diff --git a/test/sum_squares_test.cc b/test/sum_squares_test.cc index 26d0361..09b3c05 100644 --- a/test/sum_squares_test.cc +++ b/test/sum_squares_test.cc
@@ -38,9 +38,9 @@ static const int16_t kInt13Max = (1 << 12) - 1; -typedef uint64_t (*SSI16Func)(const int16_t *src, int stride, int width, - int height); -typedef libaom_test::FuncParam<SSI16Func> TestFuncs; +using SSI16Func = uint64_t (*)(const int16_t *src, int stride, int width, + int height); +using TestFuncs = libaom_test::FuncParam<SSI16Func>; class SumSquaresTest : public ::testing::TestWithParam<TestFuncs> { public: @@ -191,8 +191,8 @@ // 1D version ////////////////////////////////////////////////////////////////////////////// -typedef uint64_t (*F1D)(const int16_t *src, uint32_t n); -typedef libaom_test::FuncParam<F1D> TestFuncs1D; +using F1D = uint64_t (*)(const int16_t *src, uint32_t n); +using TestFuncs1D = libaom_test::FuncParam<F1D>; class SumSquares1DTest : public FunctionEquivalenceTest<F1D> { protected: @@ -261,11 +261,11 @@ #endif // HAVE_SVE -typedef int64_t (*SSEFunc)(const uint8_t *a, int a_stride, const uint8_t *b, - int b_stride, int width, int height); -typedef libaom_test::FuncParam<SSEFunc> TestSSEFuncs; +using SSEFunc = int64_t (*)(const uint8_t *a, int a_stride, const uint8_t *b, + int b_stride, int width, int height); +using TestSSEFuncs = libaom_test::FuncParam<SSEFunc>; -typedef std::tuple<TestSSEFuncs, int> SSETestParam; +using SSETestParam = std::tuple<TestSSEFuncs, int>; class SSETest : public ::testing::TestWithParam<SSETestParam> { public: @@ -471,11 +471,11 @@ // get_blk sum squares test functions ////////////////////////////////////////////////////////////////////////////// -typedef void (*sse_sum_func)(const int16_t *data, int stride, int bw, int bh, - int *x_sum, int64_t *x2_sum); -typedef libaom_test::FuncParam<sse_sum_func> TestSSE_SumFuncs; +using sse_sum_func = void (*)(const int16_t *data, int stride, int bw, int bh, + int *x_sum, int64_t *x2_sum); +using TestSSE_SumFuncs = libaom_test::FuncParam<sse_sum_func>; -typedef std::tuple<TestSSE_SumFuncs, TX_SIZE> SSE_SumTestParam; +using SSE_SumTestParam = std::tuple<TestSSE_SumFuncs, TX_SIZE>; class SSE_Sum_Test : public ::testing::TestWithParam<SSE_SumTestParam> { public: @@ -631,8 +631,8 @@ // 2D Variance test functions ////////////////////////////////////////////////////////////////////////////// -typedef uint64_t (*Var2DFunc)(uint8_t *src, int stride, int width, int height); -typedef libaom_test::FuncParam<Var2DFunc> TestFuncVar2D; +using Var2DFunc = uint64_t (*)(uint8_t *src, int stride, int width, int height); +using TestFuncVar2D = libaom_test::FuncParam<Var2DFunc>; const uint16_t test_block_size[2] = { 128, 256 };
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc index 762a36b..2f68ba7 100644 --- a/test/svc_datarate_test.cc +++ b/test/svc_datarate_test.cc
@@ -247,6 +247,7 @@ external_resize_pattern_ = 0; dynamic_tl_ = false; dynamic_scale_factors_ = false; + disable_last_ref_ = false; } void PreEncodeFrameHook(::libaom_test::VideoSource *video, @@ -302,7 +303,7 @@ spatial_layer_id, multi_ref_, comp_pred_, (video->frame() % cfg_.kf_max_dist) == 0, dynamic_enable_disable_mode_, rps_mode_, rps_recovery_frame_, simulcast_mode_, use_last_as_scaled_, - use_last_as_scaled_single_ref_); + use_last_as_scaled_single_ref_, disable_last_ref_); if (intra_only_ == 1 && frame_sync_ > 0) { // Set an Intra-only frame on SL0 at frame_sync_. // In order to allow decoding to start on SL0 in mid-sequence we need to @@ -964,7 +965,7 @@ int multi_ref, int comp_pred, int is_key_frame, int dynamic_enable_disable_mode, int rps_mode, int rps_recovery_frame, int simulcast_mode, bool use_last_as_scaled, - bool use_last_as_scaled_single_ref) { + bool use_last_as_scaled_single_ref, bool disable_last_ref) { int lag_index = 0; int base_count = frame_cnt >> 2; layer_id->spatial_layer_id = spatial_layer; @@ -1164,6 +1165,11 @@ if (dynamic_enable_disable_mode == 1 && layer_id->spatial_layer_id == number_spatial_layers_ - 1) ref_frame_config->reference[0] = 0; + // Always disable LAST reference under this flag. use GOLDEN reference. + if (disable_last_ref) { + ref_frame_config->reference[0] = 0; + ref_frame_config->reference[3] = 1; + } return layer_flags; } @@ -1225,6 +1231,46 @@ #endif } + virtual void BasicRateTargetingSVC3TL1SLQvgaLowFramerateTest() { + SetUpCbr(); + cfg_.g_error_resilient = 0; + cfg_.g_threads = 2; + cfg_.kf_max_dist = 30; + cfg_.kf_min_dist = 30; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_min_quantizer = 2; + cfg_.rc_max_quantizer = 50; + + ::libaom_test::I420VideoSource video("desktop1.320_180.yuv", 320, 180, 10, + 1, 0, 800); + const int bitrate_array[2] = { 50, 200 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + ResetModel(); + tile_columns_ = 1; + SetTargetBitratesFor1SL3TL(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + + virtual void BasicRateTargetingSVC3TL1SLQvgaHighBitrateLowFramerateTest() { + SetUpCbr(); + cfg_.g_error_resilient = 0; + cfg_.g_threads = 2; + cfg_.kf_max_dist = 30; + cfg_.kf_min_dist = 30; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_min_quantizer = 2; + cfg_.rc_max_quantizer = 50; + + ::libaom_test::I420VideoSource video("desktop1.320_180.yuv", 320, 180, 10, + 1, 0, 800); + const int bitrate_array[2] = { 500, 1000 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + ResetModel(); + tile_columns_ = 1; + SetTargetBitratesFor1SL3TL(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + virtual void SetFrameQpSVC3TL1SLTest() { SetUpCbr(); cfg_.g_error_resilient = 1; @@ -1468,6 +1514,23 @@ CheckDatarate(0.80, 1.60); } + virtual void BasicRateTargetingSVC1TL2SLDisableLASTTest() { + SetUpCbr(); + cfg_.g_error_resilient = 0; + + ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, + 288, 30, 1, 0, 300); + const int bitrate_array[2] = { 300, 600 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + ResetModel(); + disable_last_ref_ = true; + screen_mode_ = true; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +#if CONFIG_AV1_DECODER + EXPECT_EQ((int)GetMismatchFrames(), 0); +#endif + } + virtual void BasicRateTargetingSVC3TL3SLIntraStartDecodeBaseMidSeq() { SetUpCbr(); cfg_.rc_max_quantizer = 56; @@ -2340,6 +2403,7 @@ int external_resize_pattern_; bool dynamic_tl_; bool dynamic_scale_factors_; + bool disable_last_ref_; }; // Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial. @@ -2347,6 +2411,19 @@ BasicRateTargetingSVC3TL1SLTest(); } +// Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial, +// QVGA, low framerate. +TEST_P(DatarateTestSVC, BasicRateTargetingSVC3TL1SLQvgaLowFrameate) { + BasicRateTargetingSVC3TL1SLQvgaLowFramerateTest(); +} + +// Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial, +// QVGA, high bitrate and low framerate. +TEST_P(DatarateTestSVC, + BasicRateTargetingSVC3TL1SLQvgaHighBitrateLowFramerateTest) { + BasicRateTargetingSVC3TL1SLQvgaHighBitrateLowFramerateTest(); +} + TEST_P(DatarateTestSVC, SetFrameQpSVC3TL1SL) { SetFrameQpSVC3TL1SLTest(); } TEST_P(DatarateTestSVC, SetFrameQpSVC3TL3SL) { SetFrameQpSVC3TL3SLTest(); } @@ -2405,6 +2482,12 @@ BasicRateTargetingSVC1TL2SLTest(); } +// Check basic rate targeting for CBR, for 2 spatial layers, 1 temporal. +// Disable the usage of LAST referenc frame. +TEST_P(DatarateTestSVC, BasicRateTargetingSVC1TL2SLDisableLAST) { + BasicRateTargetingSVC1TL2SLDisableLASTTest(); +} + // Check basic rate targeting for CBR, for 3 spatial layers, 3 temporal, // with Intra-only frame inserted in the stream. Verify that we can start // decoding the SL0 stream at the intra_only frame in mid-sequence. @@ -2723,10 +2806,18 @@ EXPECT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); } +// Speed 6 takes too long on valgrind, so do only 1 bitrate and one aq_mode. +#ifdef AOM_VALGRIND_BUILD AV1_INSTANTIATE_TEST_SUITE(DatarateTestSVC, ::testing::Values(::libaom_test::kRealTime), - ::testing::Range(7, 12), ::testing::Values(0, 3), + ::testing::Range(6, 12), ::testing::Values(3), + ::testing::Values(1)); +#else // AOM_VALGRIND_BUILD +AV1_INSTANTIATE_TEST_SUITE(DatarateTestSVC, + ::testing::Values(::libaom_test::kRealTime), + ::testing::Range(6, 12), ::testing::Values(0, 3), ::testing::Values(0, 1)); +#endif } // namespace } // namespace datarate_test
diff --git a/test/temporal_filter_test.cc b/test/temporal_filter_test.cc index cda06c5..2d17c2e 100644 --- a/test/temporal_filter_test.cc +++ b/test/temporal_filter_test.cc
@@ -37,22 +37,22 @@ #if !CONFIG_REALTIME_ONLY namespace { -typedef enum { +enum ColorFormat { I400, // Monochrome I420, // 4:2:0 I422, // 4:2:2 I444, // 4:4:4 -} ColorFormat; +}; static const char *color_fmt_str[] = { "I400", "I420", "I422", "I444" }; -typedef void (*TemporalFilterFunc)( +using TemporalFilterFunc = void (*)( const YV12_BUFFER_CONFIG *frame_to_filter, const MACROBLOCKD *mbd, const BLOCK_SIZE block_size, const int mb_row, const int mb_col, const int num_planes, const double *noise_level, const MV *subblock_mvs, const int *subblock_mses, const int q_factor, const int filter_strength, int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count); -typedef libaom_test::FuncParam<TemporalFilterFunc> TemporalFilterFuncParam; +using TemporalFilterFuncParam = libaom_test::FuncParam<TemporalFilterFunc>; -typedef std::tuple<TemporalFilterFuncParam, int> TemporalFilterWithParam; +using TemporalFilterWithParam = std::tuple<TemporalFilterFuncParam, int>; class TemporalFilterTest : public ::testing::TestWithParam<TemporalFilterWithParam> { @@ -89,7 +89,13 @@ for (int ii = 0; ii < plane_h; ii++) { for (int jj = 0; jj < plane_w; jj++) { src1p[jj] = rnd_.Rand8(); - src2p[jj] = rnd_.Rand8(); + // Keep each pixel of `src2_` within [-7, 7] of `src1_` so the + // window_error stays small in apply_temporal_filter. Otherwise + // scaled_error clamps to 7 regardless of other factors and masks bugs + // in subblock_mvs and subblock_mses handling. + do { + src2p[jj] = rnd_.Rand8(); + } while (abs(src2p[jj] - src1p[jj]) >= 8); } src1p += plane_stride; src2p += plane_stride2; @@ -131,9 +137,10 @@ ColorFormat color_fmt) { aom_usec_timer ref_timer, test_timer; const BLOCK_SIZE block_size = TF_BLOCK_SIZE; - static_assert(block_size == BLOCK_32X32, ""); - const int width = 32; - const int height = 32; + static_assert(block_size == BLOCK_64X64, ""); + const int width = 64; + const int height = 64; + const int pels = width * height; int num_planes = MAX_MB_PLANE; int subsampling_x = 0; int subsampling_y = 0; @@ -165,18 +172,23 @@ } double sigma[MAX_MB_PLANE] = { 2.1002103677063437, 2.1002103677063437, 2.1002103677063437 }; - DECLARE_ALIGNED(16, unsigned int, accumulator_ref[1024 * 3]); - DECLARE_ALIGNED(16, uint16_t, count_ref[1024 * 3]); - memset(accumulator_ref, 0, 1024 * 3 * sizeof(accumulator_ref[0])); - memset(count_ref, 0, 1024 * 3 * sizeof(count_ref[0])); - DECLARE_ALIGNED(16, unsigned int, accumulator_mod[1024 * 3]); - DECLARE_ALIGNED(16, uint16_t, count_mod[1024 * 3]); - memset(accumulator_mod, 0, 1024 * 3 * sizeof(accumulator_mod[0])); - memset(count_mod, 0, 1024 * 3 * sizeof(count_mod[0])); + DECLARE_ALIGNED(16, unsigned int, accumulator_ref[pels * 3]); + DECLARE_ALIGNED(16, uint16_t, count_ref[pels * 3]); + memset(accumulator_ref, 0, pels * 3 * sizeof(accumulator_ref[0])); + memset(count_ref, 0, pels * 3 * sizeof(count_ref[0])); + DECLARE_ALIGNED(16, unsigned int, accumulator_mod[pels * 3]); + DECLARE_ALIGNED(16, uint16_t, count_mod[pels * 3]); + memset(accumulator_mod, 0, pels * 3 * sizeof(accumulator_mod[0])); + memset(count_mod, 0, pels * 3 * sizeof(count_mod[0])); - static_assert(width == 32 && height == 32, ""); - const MV subblock_mvs[4] = { { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 } }; - const int subblock_mses[4] = { 15, 16, 17, 18 }; + static_assert(width == 64 && height == 64, ""); + const MV subblock_mvs[NUM_16X16] = { + { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 }, { 0, 0 }, { 5, 5 }, + { 7, 8 }, { 2, 10 }, { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 }, + { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 } + }; + const int subblock_mses[NUM_16X16] = { 15, 16, 17, 18, 15, 16, 17, 18, + 15, 16, 17, 18, 15, 16, 17, 18 }; const int q_factor = 12; const int filter_strength = 5; const int mb_row = 0; @@ -190,21 +202,25 @@ frame_to_filter->heights[PLANE_TYPE_UV] = height >> subsampling_y; frame_to_filter->strides[PLANE_TYPE_Y] = stride; frame_to_filter->strides[PLANE_TYPE_UV] = stride >> subsampling_x; - DECLARE_ALIGNED(16, uint8_t, src[1024 * 3]); + DECLARE_ALIGNED(16, uint8_t, src[pels * 3]); frame_to_filter->buffer_alloc = src; frame_to_filter->flags = 0; // Only support low bit-depth test. - memcpy(src, src1_, 1024 * 3 * sizeof(uint8_t)); + memcpy(src, src1_, pels * 3 * sizeof(uint8_t)); std::unique_ptr<MACROBLOCKD> mbd(new (std::nothrow) MACROBLOCKD); ASSERT_NE(mbd, nullptr); mbd->bd = 8; + int plane_offset = 0; for (int plane = AOM_PLANE_Y; plane < num_planes; plane++) { - int plane_height = plane ? height >> subsampling_y : height; - int plane_stride = plane ? stride >> subsampling_x : stride; + int plane_height = + plane ? (height + subsampling_y) >> subsampling_y : height; + int plane_stride = + plane ? (stride + subsampling_x) >> subsampling_x : stride; frame_to_filter->buffers[plane] = - frame_to_filter->buffer_alloc + plane * plane_stride * plane_height; + frame_to_filter->buffer_alloc + plane_offset; mbd->plane[plane].subsampling_x = plane ? subsampling_x : 0; mbd->plane[plane].subsampling_y = plane ? subsampling_y : 0; + plane_offset += plane_height * plane_stride; } params_.ref_func(frame_to_filter.get(), mbd.get(), block_size, mb_row, @@ -247,17 +263,26 @@ height, color_fmt_str[color_fmt]); } else { - for (int i = 0, l = 0; i < height; i++) { - for (int j = 0; j < width; j++, l++) { - EXPECT_EQ(accumulator_ref[l], accumulator_mod[l]) - << "Error:" << k << " SSE Sum Test [" << width << "x" << height - << "] " << color_fmt_str[color_fmt] - << " C accumulator does not match optimized accumulator."; - EXPECT_EQ(count_ref[l], count_mod[l]) - << "Error:" << k << " SSE Sum Test [" << width << "x" << height - << "] " << color_fmt_str[color_fmt] - << " count does not match optimized count."; + plane_offset = 0; + for (int plane = AOM_PLANE_Y; plane < num_planes; plane++) { + int plane_height = + plane ? (height + subsampling_y) >> subsampling_y : height; + int plane_stride = + plane ? (stride + subsampling_x) >> subsampling_x : stride; + for (int i = 0, l = 0; i < plane_height; i++) { + for (int j = 0; j < plane_stride; j++, l++) { + EXPECT_EQ(accumulator_ref[l + plane_offset], + accumulator_mod[l + plane_offset]) + << "Error:" << k << " SSE Sum Test [" << width << "x" << height + << "] " << color_fmt_str[color_fmt] + << " C accumulator does not match optimized accumulator."; + EXPECT_EQ(count_ref[l + plane_offset], count_mod[l + plane_offset]) + << "Error:" << k << " SSE Sum Test [" << width << "x" << height + << "] " << color_fmt_str[color_fmt] + << " count does not match optimized count."; + } } + plane_offset += plane_height * plane_stride; } } } @@ -325,11 +350,11 @@ const int kHeights[] = { 2160, 1080, 720, 600, 480, 240, 237 }; #endif // HAVE_AVX2 || HAVE_NEON -typedef double (*EstimateNoiseFunc)(const uint8_t *src, int height, int width, - int stride, int edge_thresh); +using EstimateNoiseFunc = double (*)(const uint8_t *src, int height, int width, + int stride, int edge_thresh); -typedef std::tuple<EstimateNoiseFunc, EstimateNoiseFunc, int, int> - EstimateNoiseWithParam; +using EstimateNoiseWithParam = + std::tuple<EstimateNoiseFunc, EstimateNoiseFunc, int, int>; class EstimateNoiseTest : public ::testing::TestWithParam<EstimateNoiseWithParam> { @@ -423,16 +448,16 @@ #if CONFIG_AV1_HIGHBITDEPTH -typedef void (*HBDTemporalFilterFunc)( +using HBDTemporalFilterFunc = void (*)( const YV12_BUFFER_CONFIG *frame_to_filter, const MACROBLOCKD *mbd, const BLOCK_SIZE block_size, const int mb_row, const int mb_col, const int num_planes, const double *noise_level, const MV *subblock_mvs, const int *subblock_mses, const int q_factor, const int filter_strength, int tf_wgt_calc_lvl, const uint8_t *pred, uint32_t *accum, uint16_t *count); -typedef libaom_test::FuncParam<HBDTemporalFilterFunc> - HBDTemporalFilterFuncParam; +using HBDTemporalFilterFuncParam = + libaom_test::FuncParam<HBDTemporalFilterFunc>; -typedef std::tuple<HBDTemporalFilterFuncParam, int> HBDTemporalFilterWithParam; +using HBDTemporalFilterWithParam = std::tuple<HBDTemporalFilterFuncParam, int>; class HBDTemporalFilterTest : public ::testing::TestWithParam<HBDTemporalFilterWithParam> { @@ -514,9 +539,10 @@ ColorFormat color_fmt) { aom_usec_timer ref_timer, test_timer; const BLOCK_SIZE block_size = TF_BLOCK_SIZE; - static_assert(block_size == BLOCK_32X32, ""); - const int width = 32; - const int height = 32; + static_assert(block_size == BLOCK_64X64, ""); + const int width = 64; + const int height = 64; + const int pels = width * height; int num_planes = MAX_MB_PLANE; int subsampling_x = 0; int subsampling_y = 0; @@ -548,18 +574,23 @@ } double sigma[MAX_MB_PLANE] = { 2.1002103677063437, 2.1002103677063437, 2.1002103677063437 }; - DECLARE_ALIGNED(16, unsigned int, accumulator_ref[1024 * 3]); - DECLARE_ALIGNED(16, uint16_t, count_ref[1024 * 3]); - memset(accumulator_ref, 0, 1024 * 3 * sizeof(accumulator_ref[0])); - memset(count_ref, 0, 1024 * 3 * sizeof(count_ref[0])); - DECLARE_ALIGNED(16, unsigned int, accumulator_mod[1024 * 3]); - DECLARE_ALIGNED(16, uint16_t, count_mod[1024 * 3]); - memset(accumulator_mod, 0, 1024 * 3 * sizeof(accumulator_mod[0])); - memset(count_mod, 0, 1024 * 3 * sizeof(count_mod[0])); + DECLARE_ALIGNED(16, unsigned int, accumulator_ref[pels * 3]); + DECLARE_ALIGNED(16, uint16_t, count_ref[pels * 3]); + memset(accumulator_ref, 0, pels * 3 * sizeof(accumulator_ref[0])); + memset(count_ref, 0, pels * 3 * sizeof(count_ref[0])); + DECLARE_ALIGNED(16, unsigned int, accumulator_mod[pels * 3]); + DECLARE_ALIGNED(16, uint16_t, count_mod[pels * 3]); + memset(accumulator_mod, 0, pels * 3 * sizeof(accumulator_mod[0])); + memset(count_mod, 0, pels * 3 * sizeof(count_mod[0])); - static_assert(width == 32 && height == 32, ""); - const MV subblock_mvs[4] = { { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 } }; - const int subblock_mses[4] = { 15, 16, 17, 18 }; + static_assert(width == 64 && height == 64, ""); + const MV subblock_mvs[NUM_16X16] = { + { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 }, { 0, 0 }, { 5, 5 }, + { 7, 8 }, { 2, 10 }, { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 }, + { 0, 0 }, { 5, 5 }, { 7, 8 }, { 2, 10 } + }; + const int subblock_mses[NUM_16X16] = { 15, 16, 17, 18, 15, 16, 17, 18, + 15, 16, 17, 18, 15, 16, 17, 18 }; const int q_factor = 12; const int filter_strength = 5; const int mb_row = 0; @@ -573,11 +604,11 @@ frame_to_filter->heights[PLANE_TYPE_UV] = height >> subsampling_y; frame_to_filter->strides[PLANE_TYPE_Y] = stride; frame_to_filter->strides[PLANE_TYPE_UV] = stride >> subsampling_x; - DECLARE_ALIGNED(16, uint16_t, src[1024 * 3]); + DECLARE_ALIGNED(16, uint16_t, src[pels * 3]); frame_to_filter->buffer_alloc = CONVERT_TO_BYTEPTR(src); frame_to_filter->flags = YV12_FLAG_HIGHBITDEPTH; // Only Hihgbd bit-depth test. - memcpy(src, src1_, 1024 * 3 * sizeof(uint16_t)); + memcpy(src, src1_, pels * 3 * sizeof(uint16_t)); std::unique_ptr<MACROBLOCKD> mbd(new (std::nothrow) MACROBLOCKD); ASSERT_NE(mbd, nullptr); @@ -667,6 +698,7 @@ RunTest(1, 100000, 10, I422); RunTest(1, 100000, 10, I444); } + #if HAVE_SSE2 HBDTemporalFilterFuncParam HBDtemporal_filter_test_sse2[] = { HBDTemporalFilterFuncParam(&av1_highbd_apply_temporal_filter_c, @@ -686,15 +718,17 @@ Values(0, 1))); #endif // HAVE_AVX2 -#if HAVE_NEON -HBDTemporalFilterFuncParam HBDtemporal_filter_test_neon[] = { - HBDTemporalFilterFuncParam(&av1_highbd_apply_temporal_filter_c, - &av1_highbd_apply_temporal_filter_neon) -}; -INSTANTIATE_TEST_SUITE_P(NEON, HBDTemporalFilterTest, - Combine(ValuesIn(HBDtemporal_filter_test_neon), - Values(0, 1))); -#endif // HAVE_NEON +// av1_apply_temporal_filter_c works on 64x64 TF block now, the SIMD function +// needs to be updated. +// #if HAVE_NEON +// HBDTemporalFilterFuncParam HBDtemporal_filter_test_neon[] = { +// HBDTemporalFilterFuncParam(&av1_highbd_apply_temporal_filter_c, +// &av1_highbd_apply_temporal_filter_neon) +//}; +// INSTANTIATE_TEST_SUITE_P(NEON, HBDTemporalFilterTest, +// Combine(ValuesIn(HBDtemporal_filter_test_neon), +// Values(0, 1))); +// #endif // HAVE_NEON using HBDEstimateNoiseFunc = double (*)(const uint16_t *src, int height, int width, int stride, int bit_depth,
diff --git a/test/test-data.sha1 b/test/test-data.sha1 index 88ada14..3cf1354 100644 --- a/test/test-data.sha1 +++ b/test/test-data.sha1
@@ -578,3 +578,4 @@ 5605058cf45cc959c34c8407fad9b9ff2ec7ca28 *SDR_Animal_oqo7.y4m 234e498cc29b8f4bb4f0fbaeda867fbefbddcaa8 *SDR_Health_rtd0_720p.y4m 514c8334a883f1b156433c3e56f42a0330231ace *SDR_Dance_ldsn_1080p.y4m +e4c7ad520f69dc09227eb999540ff518964d30f6 *SDR_Sports_6mug_608p_30fps_90f.yuv
diff --git a/test/test.cmake b/test/test.cmake index 273a41e..f15db99 100644 --- a/test/test.cmake +++ b/test/test.cmake
@@ -51,6 +51,7 @@ add_to_libaom_test_srcs(AOM_UNIT_TEST_COMMON_SOURCES) list(APPEND AOM_UNIT_TEST_DECODER_SOURCES "${AOM_ROOT}/test/decode_api_test.cc" + "${AOM_ROOT}/test/decode_frame_size_limit_test.cc" "${AOM_ROOT}/test/decode_scalability_test.cc" "${AOM_ROOT}/test/external_frame_buffer_test.cc" "${AOM_ROOT}/test/invalid_file_test.cc" @@ -77,6 +78,7 @@ "${AOM_ROOT}/test/encode_test_driver.cc" "${AOM_ROOT}/test/encode_test_driver.h" "${AOM_ROOT}/test/end_to_end_psnr_test.cc" + "${AOM_ROOT}/test/ext_ratectrl_test.cc" "${AOM_ROOT}/test/forced_max_frame_width_height_test.cc" "${AOM_ROOT}/test/force_key_frame_test.cc" "${AOM_ROOT}/test/gf_pyr_height_test.cc" @@ -129,6 +131,7 @@ "${AOM_ROOT}/test/deltaq_mode_test.cc" "${AOM_ROOT}/test/dropframe_encode_test.cc" "${AOM_ROOT}/test/end_to_end_psnr_test.cc" + "${AOM_ROOT}/test/ext_ratectrl_test.cc" "${AOM_ROOT}/test/force_key_frame_test.cc" "${AOM_ROOT}/test/gf_pyr_height_test.cc" "${AOM_ROOT}/test/horz_superres_test.cc" @@ -235,6 +238,7 @@ "${AOM_ROOT}/test/subtract_test.cc" "${AOM_ROOT}/test/sum_squares_test.cc" "${AOM_ROOT}/test/sse_sum_test.cc" + "${AOM_ROOT}/test/use_fixed_qp_offsets_test.cc" "${AOM_ROOT}/test/variance_test.cc" "${AOM_ROOT}/test/warp_filter_test.cc" "${AOM_ROOT}/test/warp_filter_test_util.cc" @@ -422,6 +426,8 @@ aom_gtest STATIC "${AOM_ROOT}/third_party/googletest/src/googletest/src/gtest-all.cc") set_property(TARGET aom_gtest PROPERTY FOLDER ${AOM_IDE_TEST_FOLDER}) + # Starting from the 1.17.0 release, GoogleTest requires at least C++17. + target_compile_features(aom_gtest PUBLIC cxx_std_17) # There are -Wundef warnings in the gtest headers. Tell the compiler to treat # the gtest include directories as system include directories and suppress # compiler warnings in the gtest headers.
diff --git a/test/test_data_util.cmake b/test/test_data_util.cmake index 76e77a5..e9e0f02 100644 --- a/test/test_data_util.cmake +++ b/test/test_data_util.cmake
@@ -45,7 +45,8 @@ "wikipedia_420_360p_60f.y4m" "SDR_Animal_oqo7.y4m" "SDR_Health_rtd0_720p.y4m" - "SDR_Dance_ldsn_1080p.y4m") + "SDR_Dance_ldsn_1080p.y4m" + "SDR_Sports_6mug_608p_30fps_90f.yuv") if(ENABLE_DECODE_PERF_TESTS AND CONFIG_AV1_ENCODER) list(APPEND AOM_TEST_DATA_FILE_NAMES "niklas_1280_720_30.yuv")
diff --git a/test/test_intra_pred_speed.cc b/test/test_intra_pred_speed.cc index 65468d9..80e1719 100644 --- a/test/test_intra_pred_speed.cc +++ b/test/test_intra_pred_speed.cc
@@ -35,8 +35,8 @@ // 0: Generate MD5 array as required #define APPLY_UNIT_TESTS 1 -typedef void (*AvxPredFunc)(uint8_t *dst, ptrdiff_t y_stride, - const uint8_t *above, const uint8_t *left); +using AvxPredFunc = void (*)(uint8_t *dst, ptrdiff_t y_stride, + const uint8_t *above, const uint8_t *left); const int kBPS = 64; const int kTotalPixels = kBPS * kBPS; @@ -87,7 +87,7 @@ // ----------------------------------------------------------------------------- // Low Bittdepth -typedef IntraPredTestMem<uint8_t> Av1IntraPredTestMem; +using Av1IntraPredTestMem = IntraPredTestMem<uint8_t>; static const char *const kTxSizeStrings[TX_SIZES_ALL] = { "4X4", "8X8", "16X16", "32X32", "64X64", "4X8", "8X4", @@ -1007,11 +1007,11 @@ // High Bitdepth namespace { -typedef void (*AvxHighbdPredFunc)(uint16_t *dst, ptrdiff_t y_stride, - const uint16_t *above, const uint16_t *left, - int bd); +using AvxHighbdPredFunc = void (*)(uint16_t *dst, ptrdiff_t y_stride, + const uint16_t *above, const uint16_t *left, + int bd); -typedef IntraPredTestMem<uint16_t> Av1HighbdIntraPredTestMem; +using Av1HighbdIntraPredTestMem = IntraPredTestMem<uint16_t>; void TestHighbdIntraPred(TX_SIZE tx_size, AvxHighbdPredFunc const *pred_funcs, const char *const signatures[]) {
diff --git a/test/test_vector_test.cc b/test/test_vector_test.cc index b521e93..6c132b6 100644 --- a/test/test_vector_test.cc +++ b/test/test_vector_test.cc
@@ -34,7 +34,7 @@ const int kFileName = 1; const int kRowMT = 2; -typedef std::tuple<int, const char *, int> DecodeParam; +using DecodeParam = std::tuple<int, const char *, int>; class TestVectorTest : public ::libaom_test::DecoderTest, public ::libaom_test::CodecTestWithParam<DecodeParam> {
diff --git a/test/tile_config_test.cc b/test/tile_config_test.cc index ed3216f..ff7a460 100644 --- a/test/tile_config_test.cc +++ b/test/tile_config_test.cc
@@ -19,14 +19,14 @@ #include "test/util.h" namespace { -typedef struct { +struct uniformTileConfigParam { // Superblock size const unsigned int sb_size; // log2(number of tile rows) const unsigned int tile_rows; // log2(number of tile columns) const unsigned int tile_cols; -} uniformTileConfigParam; +}; const libaom_test::TestMode kTestModeParams[] = #if CONFIG_REALTIME_ONLY @@ -42,7 +42,7 @@ { 64, 2, 2 }, { 64, 3, 3 }, { 64, 4, 4 } }; -typedef struct { +struct nonUniformTileConfigParam { // Superblock size const unsigned int sb_size; // number of tile widths @@ -53,7 +53,7 @@ const unsigned int tile_height_count; // list of tile heights int tile_heights[AOM_MAX_TILE_ROWS]; -} nonUniformTileConfigParam; +}; const nonUniformTileConfigParam nonUniformTileConfigParams[] = { { 64, 1, { 3 }, 1, { 3 } }, { 64, 2, { 1, 2 }, 2, { 1, 2 } }, @@ -271,14 +271,14 @@ ::testing::ValuesIn(nonUniformTileConfigParams), ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ)); -typedef struct { +struct TileGroupConfigParams { // Number of tile groups to set. const int num_tg; // Number of tile rows to set const int num_tile_rows; // Number of tile columns to set const int num_tile_cols; -} TileGroupConfigParams; +}; static const TileGroupConfigParams tileGroupTestParams[] = { { 5, 4, 4 }, { 3, 3, 3 }, { 5, 3, 3 }, { 7, 5, 5 }, { 7, 3, 3 }, { 7, 4, 4 }
diff --git a/test/use_fixed_qp_offsets_test.cc b/test/use_fixed_qp_offsets_test.cc new file mode 100644 index 0000000..1131f8a --- /dev/null +++ b/test/use_fixed_qp_offsets_test.cc
@@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, 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 "config/aom_config.h" + +#include "gtest/gtest.h" +#include "test/codec_factory.h" +#include "test/encode_test_driver.h" +#include "test/i420_video_source.h" +#include "test/util.h" + +namespace { + +const ::libaom_test::TestMode kTestMode[] = +#if CONFIG_REALTIME_ONLY + { ::libaom_test::kRealTime }; +#else + { ::libaom_test::kRealTime, ::libaom_test::kOnePassGood }; +#endif + +const int kUseFixedQPOffsetsMode[] = { 1, 2 }; + +class UseFixedQPOffsetsTest + : public ::libaom_test::CodecTestWith2Params<libaom_test::TestMode, int>, + public ::libaom_test::EncoderTest { + protected: + UseFixedQPOffsetsTest() : EncoderTest(GET_PARAM(0)) {} + ~UseFixedQPOffsetsTest() override = default; + + void SetUp() override { + InitializeConfig(GET_PARAM(1)); + cfg_.kf_max_dist = 9999; + cfg_.rc_end_usage = AOM_Q; + cfg_.use_fixed_qp_offsets = GET_PARAM(2); + } + + void PreEncodeFrameHook(::libaom_test::VideoSource *video, + ::libaom_test::Encoder *encoder) override { + if (video->frame() == 0) { + encoder->Control(AOME_SET_CPUUSED, 6); + encoder->Control(AOME_SET_CQ_LEVEL, frame_qp_); + } + } + + void PostEncodeFrameHook(::libaom_test::Encoder *encoder) override { + int qp = 0; + encoder->Control(AOME_GET_LAST_QUANTIZER_64, &qp); + + // If a call to encoder->EncodeFrame() results in a last QP of 0, + // interpret as the frame being read into the lookahead buffer. + if (qp == 0) return; + + if (cfg_.use_fixed_qp_offsets == 2) { + // Setting use_fixed_qp_offsets = 2 means every frame should use the same + // QP + ASSERT_EQ(qp, frame_qp_); + } else { + ASSERT_LE(qp, frame_qp_); + } + } + + void DoTest() { + ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, + 288, 30, 1, 0, 33); + frame_qp_ = 35; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + + int frame_qp_; +}; + +TEST_P(UseFixedQPOffsetsTest, TestQPOffsets) { DoTest(); } + +AV1_INSTANTIATE_TEST_SUITE(UseFixedQPOffsetsTest, + ::testing::ValuesIn(kTestMode), + ::testing::ValuesIn(kUseFixedQPOffsetsMode)); +} // namespace
diff --git a/test/variance_test.cc b/test/variance_test.cc index 1999cc9..ff5caeb 100644 --- a/test/variance_test.cc +++ b/test/variance_test.cc
@@ -30,39 +30,40 @@ namespace { -typedef uint64_t (*MseWxH16bitFunc)(uint8_t *dst, int dstride, uint16_t *src, - int sstride, int w, int h); -typedef uint64_t (*Mse16xH16bitFunc)(uint8_t *dst, int dstride, uint16_t *src, - int w, int h); -typedef unsigned int (*VarianceMxNFunc)(const uint8_t *a, int a_stride, - const uint8_t *b, int b_stride, - unsigned int *sse); -typedef void (*GetSseSum8x8QuadFunc)(const uint8_t *a, int a_stride, - const uint8_t *b, int b_stride, - uint32_t *sse8x8, int *sum8x8, - unsigned int *tot_sse, int *tot_sum, - uint32_t *var8x8); -typedef void (*GetSseSum16x16DualFunc)(const uint8_t *a, int a_stride, - const uint8_t *b, int b_stride, - uint32_t *sse16x16, - unsigned int *tot_sse, int *tot_sum, - uint32_t *var16x16); -typedef unsigned int (*SubpixVarMxNFunc)(const uint8_t *a, int a_stride, - int xoffset, int yoffset, +using MseWxH16bitFunc = uint64_t (*)(uint8_t *dst, int dstride, uint16_t *src, + int sstride, int w, int h); +using Mse16xH16bitFunc = uint64_t (*)(uint8_t *dst, int dstride, uint16_t *src, + int w, int h); +using VarianceMxNFunc = unsigned int (*)(const uint8_t *a, int a_stride, const uint8_t *b, int b_stride, unsigned int *sse); -typedef unsigned int (*SubpixAvgVarMxNFunc)(const uint8_t *a, int a_stride, - int xoffset, int yoffset, - const uint8_t *b, int b_stride, - uint32_t *sse, - const uint8_t *second_pred); -typedef unsigned int (*SumOfSquaresFunction)(const int16_t *src); +using GetSseSum8x8QuadFunc = void (*)(const uint8_t *a, int a_stride, + const uint8_t *b, int b_stride, + uint32_t *sse8x8, int *sum8x8, + unsigned int *tot_sse, int *tot_sum, + uint32_t *var8x8); +using GetSseSum16x16DualFunc = void (*)(const uint8_t *a, int a_stride, + const uint8_t *b, int b_stride, + uint32_t *sse16x16, + unsigned int *tot_sse, int *tot_sum, + uint32_t *var16x16); +using SubpixVarMxNFunc = unsigned int (*)(const uint8_t *a, int a_stride, + int xoffset, int yoffset, + const uint8_t *b, int b_stride, + unsigned int *sse); +using SubpixAvgVarMxNFunc = unsigned int (*)(const uint8_t *a, int a_stride, + int xoffset, int yoffset, + const uint8_t *b, int b_stride, + uint32_t *sse, + const uint8_t *second_pred); +using SumOfSquaresFunction = unsigned int (*)(const int16_t *src); #if !CONFIG_REALTIME_ONLY -typedef uint32_t (*ObmcSubpelVarFunc)(const uint8_t *pre, int pre_stride, - int xoffset, int yoffset, - const int32_t *wsrc, const int32_t *mask, - unsigned int *sse); +using ObmcSubpelVarFunc = uint32_t (*)(const uint8_t *pre, int pre_stride, + int xoffset, int yoffset, + const int32_t *wsrc, const int32_t *mask, + unsigned int *sse); + #endif using libaom_test::ACMRandom; @@ -1437,7 +1438,7 @@ static const int kMaskMax = 64; -typedef TestParams<ObmcSubpelVarFunc> ObmcSubpelVarianceParams; +using ObmcSubpelVarianceParams = TestParams<ObmcSubpelVarFunc>; template <typename FunctionType> class ObmcVarianceTest @@ -1592,19 +1593,19 @@ #endif // !CONFIG_REALTIME_ONLY -typedef MseWxHTestClass<MseWxH16bitFunc> MseWxHTest; -typedef Mse16xHTestClass<Mse16xH16bitFunc> Mse16xHTest; -typedef MainTestClass<VarianceMxNFunc> AvxMseTest; -typedef MainTestClass<VarianceMxNFunc> AvxVarianceTest; -typedef MainTestClass<GetSseSum8x8QuadFunc> GetSseSum8x8QuadTest; -typedef MainTestClass<GetSseSum16x16DualFunc> GetSseSum16x16DualTest; -typedef SubpelVarianceTest<SubpixVarMxNFunc> AvxSubpelVarianceTest; -typedef SubpelVarianceTest<SubpixAvgVarMxNFunc> AvxSubpelAvgVarianceTest; +using MseWxHTest = MseWxHTestClass<MseWxH16bitFunc>; +using Mse16xHTest = Mse16xHTestClass<Mse16xH16bitFunc>; +using AvxMseTest = MainTestClass<VarianceMxNFunc>; +using AvxVarianceTest = MainTestClass<VarianceMxNFunc>; +using GetSseSum8x8QuadTest = MainTestClass<GetSseSum8x8QuadFunc>; +using GetSseSum16x16DualTest = MainTestClass<GetSseSum16x16DualFunc>; +using AvxSubpelVarianceTest = SubpelVarianceTest<SubpixVarMxNFunc>; +using AvxSubpelAvgVarianceTest = SubpelVarianceTest<SubpixAvgVarMxNFunc>; #if !CONFIG_REALTIME_ONLY -typedef ObmcVarianceTest<ObmcSubpelVarFunc> AvxObmcSubpelVarianceTest; +using AvxObmcSubpelVarianceTest = ObmcVarianceTest<ObmcSubpelVarFunc>; #endif -typedef TestParams<MseWxH16bitFunc> MseWxHParams; -typedef TestParams<Mse16xH16bitFunc> Mse16xHParams; +using MseWxHParams = TestParams<MseWxH16bitFunc>; +using Mse16xHParams = TestParams<Mse16xH16bitFunc>; TEST_P(MseWxHTest, RefMse) { RefMatchTestMse(); } TEST_P(MseWxHTest, DISABLED_SpeedMse) { SpeedTest(); } @@ -1659,14 +1660,14 @@ ::testing::Values(aom_get_mb_ss_c)); #endif // !CONFIG_REALTIME_ONLY -typedef TestParams<VarianceMxNFunc> MseParams; +using MseParams = TestParams<VarianceMxNFunc>; INSTANTIATE_TEST_SUITE_P(C, AvxMseTest, ::testing::Values(MseParams(4, 4, &aom_mse16x16_c), MseParams(4, 3, &aom_mse16x8_c), MseParams(3, 4, &aom_mse8x16_c), MseParams(3, 3, &aom_mse8x8_c))); -typedef TestParams<VarianceMxNFunc> VarianceParams; +using VarianceParams = TestParams<VarianceMxNFunc>; const VarianceParams kArrayVariance_c[] = { VarianceParams(7, 7, &aom_variance128x128_c), VarianceParams(7, 6, &aom_variance128x64_c), @@ -1696,7 +1697,7 @@ INSTANTIATE_TEST_SUITE_P(C, AvxVarianceTest, ::testing::ValuesIn(kArrayVariance_c)); -typedef TestParams<GetSseSum8x8QuadFunc> GetSseSumParams; +using GetSseSumParams = TestParams<GetSseSum8x8QuadFunc>; const GetSseSumParams kArrayGetSseSum8x8Quad_c[] = { GetSseSumParams(7, 7, &aom_get_var_sse_sum_8x8_quad_c, 0), GetSseSumParams(6, 6, &aom_get_var_sse_sum_8x8_quad_c, 0), @@ -1706,7 +1707,7 @@ INSTANTIATE_TEST_SUITE_P(C, GetSseSum8x8QuadTest, ::testing::ValuesIn(kArrayGetSseSum8x8Quad_c)); -typedef TestParams<GetSseSum16x16DualFunc> GetSseSumParamsDual; +using GetSseSumParamsDual = TestParams<GetSseSum16x16DualFunc>; const GetSseSumParamsDual kArrayGetSseSum16x16Dual_c[] = { GetSseSumParamsDual(7, 7, &aom_get_var_sse_sum_16x16_dual_c, 0), GetSseSumParamsDual(6, 6, &aom_get_var_sse_sum_16x16_dual_c, 0), @@ -1717,7 +1718,7 @@ INSTANTIATE_TEST_SUITE_P(C, GetSseSum16x16DualTest, ::testing::ValuesIn(kArrayGetSseSum16x16Dual_c)); -typedef TestParams<SubpixVarMxNFunc> SubpelVarianceParams; +using SubpelVarianceParams = TestParams<SubpixVarMxNFunc>; const SubpelVarianceParams kArraySubpelVariance_c[] = { SubpelVarianceParams(7, 7, &aom_sub_pixel_variance128x128_c, 0), SubpelVarianceParams(7, 6, &aom_sub_pixel_variance128x64_c, 0), @@ -1747,7 +1748,7 @@ INSTANTIATE_TEST_SUITE_P(C, AvxSubpelVarianceTest, ::testing::ValuesIn(kArraySubpelVariance_c)); -typedef TestParams<SubpixAvgVarMxNFunc> SubpelAvgVarianceParams; +using SubpelAvgVarianceParams = TestParams<SubpixAvgVarMxNFunc>; const SubpelAvgVarianceParams kArraySubpelAvgVariance_c[] = { SubpelAvgVarianceParams(7, 7, &aom_sub_pixel_avg_variance128x128_c, 0), SubpelAvgVarianceParams(7, 6, &aom_sub_pixel_avg_variance128x64_c, 0), @@ -1808,9 +1809,8 @@ #endif #if CONFIG_AV1_HIGHBITDEPTH -typedef uint64_t (*MseHBDWxH16bitFunc)(uint16_t *dst, int dstride, - uint16_t *src, int sstride, int w, - int h); +using MseHBDWxH16bitFunc = uint64_t (*)(uint16_t *, int, uint16_t *, int, int, + int); template <typename FunctionType> class MseHBDWxHTestClass @@ -1909,15 +1909,15 @@ } } -typedef TestParams<MseHBDWxH16bitFunc> MseHBDWxHParams; -typedef MseHBDWxHTestClass<MseHBDWxH16bitFunc> MseHBDWxHTest; -typedef MainTestClass<VarianceMxNFunc> AvxHBDMseTest; +using MseHBDWxHParams = TestParams<MseHBDWxH16bitFunc>; +using MseHBDWxHTest = MseHBDWxHTestClass<MseHBDWxH16bitFunc>; +using AvxHBDMseTest = MainTestClass<VarianceMxNFunc>; GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AvxHBDMseTest); -typedef MainTestClass<VarianceMxNFunc> AvxHBDVarianceTest; -typedef SubpelVarianceTest<SubpixVarMxNFunc> AvxHBDSubpelVarianceTest; -typedef SubpelVarianceTest<SubpixAvgVarMxNFunc> AvxHBDSubpelAvgVarianceTest; +using AvxHBDVarianceTest = MainTestClass<VarianceMxNFunc>; +using AvxHBDSubpelVarianceTest = SubpelVarianceTest<SubpixVarMxNFunc>; +using AvxHBDSubpelAvgVarianceTest = SubpelVarianceTest<SubpixAvgVarMxNFunc>; #if !CONFIG_REALTIME_ONLY -typedef ObmcVarianceTest<ObmcSubpelVarFunc> AvxHBDObmcSubpelVarianceTest; +using AvxHBDObmcSubpelVarianceTest = ObmcVarianceTest<ObmcSubpelVarFunc>; #endif GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AvxHBDObmcSubpelVarianceTest); @@ -3661,7 +3661,6 @@ VarianceParams(3, 3, &aom_variance8x8_neon_dotprod), VarianceParams(3, 2, &aom_variance8x4_neon_dotprod), VarianceParams(2, 3, &aom_variance4x8_neon_dotprod), - VarianceParams(2, 2, &aom_variance4x4_neon_dotprod), #if !CONFIG_REALTIME_ONLY VarianceParams(2, 4, &aom_variance4x16_neon_dotprod), VarianceParams(4, 2, &aom_variance16x4_neon_dotprod), @@ -3675,6 +3674,35 @@ INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, AvxVarianceTest, ::testing::ValuesIn(kArrayVariance_neon_dotprod)); +const SubpelVarianceParams kArraySubpelVariance_neon_dotprod[] = { + SubpelVarianceParams(7, 7, &aom_sub_pixel_variance128x128_neon_dotprod, 0), + SubpelVarianceParams(7, 6, &aom_sub_pixel_variance128x64_neon_dotprod, 0), + SubpelVarianceParams(6, 7, &aom_sub_pixel_variance64x128_neon_dotprod, 0), + SubpelVarianceParams(6, 6, &aom_sub_pixel_variance64x64_neon_dotprod, 0), + SubpelVarianceParams(6, 5, &aom_sub_pixel_variance64x32_neon_dotprod, 0), + SubpelVarianceParams(5, 6, &aom_sub_pixel_variance32x64_neon_dotprod, 0), + SubpelVarianceParams(5, 5, &aom_sub_pixel_variance32x32_neon_dotprod, 0), + SubpelVarianceParams(5, 4, &aom_sub_pixel_variance32x16_neon_dotprod, 0), + SubpelVarianceParams(4, 5, &aom_sub_pixel_variance16x32_neon_dotprod, 0), + SubpelVarianceParams(4, 4, &aom_sub_pixel_variance16x16_neon_dotprod, 0), + SubpelVarianceParams(4, 3, &aom_sub_pixel_variance16x8_neon_dotprod, 0), + SubpelVarianceParams(3, 4, &aom_sub_pixel_variance8x16_neon_dotprod, 0), + SubpelVarianceParams(3, 3, &aom_sub_pixel_variance8x8_neon_dotprod, 0), + SubpelVarianceParams(3, 2, &aom_sub_pixel_variance8x4_neon_dotprod, 0), + SubpelVarianceParams(2, 3, &aom_sub_pixel_variance4x8_neon_dotprod, 0), +#if !CONFIG_REALTIME_ONLY + SubpelVarianceParams(6, 4, &aom_sub_pixel_variance64x16_neon_dotprod, 0), + SubpelVarianceParams(4, 6, &aom_sub_pixel_variance16x64_neon_dotprod, 0), + SubpelVarianceParams(5, 3, &aom_sub_pixel_variance32x8_neon_dotprod, 0), + SubpelVarianceParams(3, 5, &aom_sub_pixel_variance8x32_neon_dotprod, 0), + SubpelVarianceParams(4, 2, &aom_sub_pixel_variance16x4_neon_dotprod, 0), + SubpelVarianceParams(2, 4, &aom_sub_pixel_variance4x16_neon_dotprod, 0), +#endif +}; +INSTANTIATE_TEST_SUITE_P( + NEON_DOTPROD, AvxSubpelVarianceTest, + ::testing::ValuesIn(kArraySubpelVariance_neon_dotprod)); + const GetSseSumParams kArrayGetSseSum8x8Quad_neon_dotprod[] = { GetSseSumParams(7, 7, &aom_get_var_sse_sum_8x8_quad_neon_dotprod, 0), GetSseSumParams(6, 6, &aom_get_var_sse_sum_8x8_quad_neon_dotprod, 0),
diff --git a/test/warp_filter_test.cc b/test/warp_filter_test.cc index 3a35075..530f2aa 100644 --- a/test/warp_filter_test.cc +++ b/test/warp_filter_test.cc
@@ -66,6 +66,12 @@ #endif // CONFIG_AV1_HIGHBITDEPTH #endif // HAVE_AVX2 +#if CONFIG_HIGHWAY && HAVE_AVX512 +INSTANTIATE_TEST_SUITE_P( + AVX512, AV1WarpFilterTest, + libaom_test::AV1WarpFilter::BuildParams(av1_warp_affine_avx512)); +#endif // CONFIG_HIGHWAY && HAVE_AVX512 + #if HAVE_NEON INSTANTIATE_TEST_SUITE_P( NEON, AV1WarpFilterTest,
diff --git a/test/warp_filter_test_util.cc b/test/warp_filter_test_util.cc index 9dadbaf..19683f0 100644 --- a/test/warp_filter_test_util.cc +++ b/test/warp_filter_test_util.cc
@@ -258,9 +258,13 @@ conv_params.fwd_offset = quant_dist_lookup_table[jj][ii]; conv_params.bck_offset = quant_dist_lookup_table[jj][1 - ii]; } - av1_warp_affine_c(mat, input, w, h, stride, output.get(), 32, 32, - out_w, out_h, out_w, sub_x, sub_y, &conv_params, - alpha, beta, gamma, delta); + // Around 6% of the time, use a random column/row to test + // out-of-bounds filtering cases. + const int col = rnd_.Rand8() < 16 ? rnd_.Rand8() : 32; + const int row = rnd_.Rand8() < 16 ? rnd_.Rand8() : 32; + av1_warp_affine_c(mat, input, w, h, stride, output.get(), col, + row, out_w, out_h, out_w, sub_x, sub_y, + &conv_params, alpha, beta, gamma, delta); if (use_no_round) { conv_params = get_conv_params_no_round( do_average, 0, dstb.get(), out_w, 1, bd); @@ -272,9 +276,9 @@ conv_params.fwd_offset = quant_dist_lookup_table[jj][ii]; conv_params.bck_offset = quant_dist_lookup_table[jj][1 - ii]; } - test_impl(mat, input, w, h, stride, output2.get(), 32, 32, out_w, - out_h, out_w, sub_x, sub_y, &conv_params, alpha, beta, - gamma, delta); + test_impl(mat, input, w, h, stride, output2.get(), col, row, + out_w, out_h, out_w, sub_x, sub_y, &conv_params, alpha, + beta, gamma, delta); if (use_no_round) { for (int j = 0; j < out_w * out_h; ++j) ASSERT_EQ(dsta[j], dstb[j]) @@ -458,8 +462,12 @@ conv_params.bck_offset = quant_dist_lookup_table[jj][1 - ii]; } + // Around 6% of the time, use a random column/row to test + // out-of-bounds filtering cases. + const int col = rnd_.Rand8() < 16 ? rnd_.Rand8() : 32; + const int row = rnd_.Rand8() < 16 ? rnd_.Rand8() : 32; av1_highbd_warp_affine_c(mat, input, w, h, stride, output.get(), - 32, 32, out_w, out_h, out_w, sub_x, + col, row, out_w, out_h, out_w, sub_x, sub_y, bd, &conv_params, alpha, beta, gamma, delta); if (use_no_round) { @@ -475,9 +483,9 @@ conv_params.fwd_offset = quant_dist_lookup_table[jj][ii]; conv_params.bck_offset = quant_dist_lookup_table[jj][1 - ii]; } - test_impl(mat, input, w, h, stride, output2.get(), 32, 32, out_w, - out_h, out_w, sub_x, sub_y, bd, &conv_params, alpha, - beta, gamma, delta); + test_impl(mat, input, w, h, stride, output2.get(), col, row, + out_w, out_h, out_w, sub_x, sub_y, bd, &conv_params, + alpha, beta, gamma, delta); if (use_no_round) { for (int j = 0; j < out_w * out_h; ++j)
diff --git a/test/warp_filter_test_util.h b/test/warp_filter_test_util.h index 7e401d8..25fa110 100644 --- a/test/warp_filter_test_util.h +++ b/test/warp_filter_test_util.h
@@ -34,16 +34,16 @@ namespace AV1WarpFilter { -typedef void (*warp_affine_func)(const int32_t *mat, const uint8_t *ref, - int width, int height, int stride, - uint8_t *pred, int p_col, int p_row, - int p_width, int p_height, int p_stride, - int subsampling_x, int subsampling_y, - ConvolveParams *conv_params, int16_t alpha, - int16_t beta, int16_t gamma, int16_t delta); +using warp_affine_func = void (*)(const int32_t *mat, const uint8_t *ref, + int width, int height, int stride, + uint8_t *pred, int p_col, int p_row, + int p_width, int p_height, int p_stride, + int subsampling_x, int subsampling_y, + ConvolveParams *conv_params, int16_t alpha, + int16_t beta, int16_t gamma, int16_t delta); -typedef std::tuple<int, int, int, warp_affine_func> WarpTestParam; -typedef std::tuple<WarpTestParam, int, int, int, int> WarpTestParams; +using WarpTestParam = std::tuple<int, int, int, warp_affine_func>; +using WarpTestParams = std::tuple<WarpTestParam, int, int, int, int>; ::testing::internal::ParamGenerator<WarpTestParams> BuildParams( warp_affine_func filter); @@ -64,19 +64,17 @@ #if CONFIG_AV1_HIGHBITDEPTH namespace AV1HighbdWarpFilter { -typedef void (*highbd_warp_affine_func)(const int32_t *mat, const uint16_t *ref, - int width, int height, int stride, - uint16_t *pred, int p_col, int p_row, - int p_width, int p_height, int p_stride, - int subsampling_x, int subsampling_y, - int bd, ConvolveParams *conv_params, - int16_t alpha, int16_t beta, - int16_t gamma, int16_t delta); +using highbd_warp_affine_func = + void (*)(const int32_t *mat, const uint16_t *ref, int width, int height, + int stride, uint16_t *pred, int p_col, int p_row, int p_width, + int p_height, int p_stride, int subsampling_x, int subsampling_y, + int bd, ConvolveParams *conv_params, int16_t alpha, int16_t beta, + int16_t gamma, int16_t delta); -typedef std::tuple<int, int, int, int, highbd_warp_affine_func> - HighbdWarpTestParam; -typedef std::tuple<HighbdWarpTestParam, int, int, int, int> - HighbdWarpTestParams; +using HighbdWarpTestParam = + std::tuple<int, int, int, int, highbd_warp_affine_func>; +using HighbdWarpTestParams = + std::tuple<HighbdWarpTestParam, int, int, int, int>; ::testing::internal::ParamGenerator<HighbdWarpTestParams> BuildParams( highbd_warp_affine_func filter);
diff --git a/test/wiener_test.cc b/test/wiener_test.cc index fe6e40e..8d5950d 100644 --- a/test/wiener_test.cc +++ b/test/wiener_test.cc
@@ -9,6 +9,7 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <cinttypes> #include <tuple> #include <utility> #include <vector> @@ -175,18 +176,18 @@ } static const int kIterations = 100; -typedef void (*compute_stats_Func)(int wiener_win, const uint8_t *dgd, - const uint8_t *src, int16_t *dgd_avg, - int16_t *src_avg, int h_start, int h_end, - int v_start, int v_end, int dgd_stride, - int src_stride, int64_t *M, int64_t *H, - int use_downsampled_wiener_stats); +using compute_stats_Func = void (*)(int wiener_win, const uint8_t *dgd, + const uint8_t *src, int16_t *dgd_avg, + int16_t *src_avg, int h_start, int h_end, + int v_start, int v_end, int dgd_stride, + int src_stride, int64_t *M, int64_t *H, + int use_downsampled_wiener_stats); //////////////////////////////////////////////////////////////////////////////// // 8 bit //////////////////////////////////////////////////////////////////////////////// -typedef std::tuple<const compute_stats_Func> WienerTestParam; +using WienerTestParam = std::tuple<const compute_stats_Func>; class WienerTest : public ::testing::TestWithParam<WienerTestParam> { public: @@ -537,14 +538,14 @@ } static const int kIterations = 100; -typedef void (*compute_stats_Func)(int wiener_win, const uint8_t *dgd, - const uint8_t *src, int16_t *d, int16_t *s, - int h_start, int h_end, int v_start, - int v_end, int dgd_stride, int src_stride, - int64_t *M, int64_t *H, - aom_bit_depth_t bit_depth); +using compute_stats_Func = void (*)(int wiener_win, const uint8_t *dgd, + const uint8_t *src, int16_t *d, int16_t *s, + int h_start, int h_end, int v_start, + int v_end, int dgd_stride, int src_stride, + int64_t *M, int64_t *H, + aom_bit_depth_t bit_depth); -typedef std::tuple<const compute_stats_Func> WienerTestParam; +using WienerTestParam = std::tuple<const compute_stats_Func>; class WienerTestHighbd : public ::testing::TestWithParam<WienerTestParam> { public:
diff --git a/third_party/SVT-AV1/EbMemory_AVX2.h b/third_party/SVT-AV1/EbMemory_AVX2.h deleted file mode 100644 index bd0c4fb..0000000 --- a/third_party/SVT-AV1/EbMemory_AVX2.h +++ /dev/null
@@ -1,110 +0,0 @@ -/* - * Copyright(c) 2019 Intel Corporation - * - * 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 https://www.aomedia.org/license/software-license. 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 - * https://www.aomedia.org/license/patent-license. - */ - -#ifndef AOM_THIRD_PARTY_SVT_AV1_EBMEMORY_AVX2_H_ -#define AOM_THIRD_PARTY_SVT_AV1_EBMEMORY_AVX2_H_ - -#include <immintrin.h> - -#include "config/aom_config.h" - -#include "aom/aom_integer.h" - -#ifndef _mm256_set_m128i -#define _mm256_set_m128i(/* __m128i */ hi, /* __m128i */ lo) \ - _mm256_insertf128_si256(_mm256_castsi128_si256(lo), (hi), 0x1) -#endif - -#ifndef _mm256_setr_m128i -#define _mm256_setr_m128i(/* __m128i */ lo, /* __m128i */ hi) \ - _mm256_set_m128i((hi), (lo)) -#endif - -static inline __m256i load_u8_4x2_avx2(const uint8_t *const src, - const ptrdiff_t stride) { - __m128i src01; - src01 = _mm_cvtsi32_si128(*(int32_t *)(src + 0 * stride)); - src01 = _mm_insert_epi32(src01, *(int32_t *)(src + 1 * stride), 1); - return _mm256_setr_m128i(src01, _mm_setzero_si128()); -} - -static inline __m256i load_u8_4x4_avx2(const uint8_t *const src, - const ptrdiff_t stride) { - __m128i src01, src23; - src01 = _mm_cvtsi32_si128(*(int32_t *)(src + 0 * stride)); - src01 = _mm_insert_epi32(src01, *(int32_t *)(src + 1 * stride), 1); - src23 = _mm_cvtsi32_si128(*(int32_t *)(src + 2 * stride)); - src23 = _mm_insert_epi32(src23, *(int32_t *)(src + 3 * stride), 1); - return _mm256_setr_m128i(src01, src23); -} - -static inline __m256i load_u8_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride) { - const __m128i src0 = _mm_loadl_epi64((__m128i *)(src + 0 * stride)); - const __m128i src1 = _mm_loadl_epi64((__m128i *)(src + 1 * stride)); - return _mm256_setr_m128i(src0, src1); -} - -static inline __m256i load_u8_8x4_avx2(const uint8_t *const src, - const ptrdiff_t stride) { - __m128i src01, src23; - src01 = _mm_loadl_epi64((__m128i *)(src + 0 * stride)); - src01 = _mm_castpd_si128(_mm_loadh_pd(_mm_castsi128_pd(src01), - (double *)(void *)(src + 1 * stride))); - src23 = _mm_loadl_epi64((__m128i *)(src + 2 * stride)); - src23 = _mm_castpd_si128(_mm_loadh_pd(_mm_castsi128_pd(src23), - (double *)(void *)(src + 3 * stride))); - return _mm256_setr_m128i(src01, src23); -} - -static inline __m256i loadu_8bit_16x2_avx2(const void *const src, - const ptrdiff_t strideInByte) { - const __m128i src0 = _mm_loadu_si128((__m128i *)src); - const __m128i src1 = - _mm_loadu_si128((__m128i *)((uint8_t *)src + strideInByte)); - return _mm256_setr_m128i(src0, src1); -} - -static inline __m256i loadu_u8_16x2_avx2(const uint8_t *const src, - const ptrdiff_t stride) { - return loadu_8bit_16x2_avx2(src, sizeof(*src) * stride); -} - -static inline __m256i loadu_u16_8x2_avx2(const uint16_t *const src, - const ptrdiff_t stride) { - return loadu_8bit_16x2_avx2(src, sizeof(*src) * stride); -} - -static inline void storeu_8bit_16x2_avx2(const __m256i src, void *const dst, - const ptrdiff_t strideInByte) { - const __m128i d0 = _mm256_castsi256_si128(src); - const __m128i d1 = _mm256_extracti128_si256(src, 1); - _mm_storeu_si128((__m128i *)dst, d0); - _mm_storeu_si128((__m128i *)((uint8_t *)dst + strideInByte), d1); -} - -static inline void storeu_u8_16x2_avx2(const __m256i src, uint8_t *const dst, - const ptrdiff_t stride) { - storeu_8bit_16x2_avx2(src, dst, sizeof(*dst) * stride); -} - -static inline void storeu_s16_8x2_avx2(const __m256i src, int16_t *const dst, - const ptrdiff_t stride) { - storeu_8bit_16x2_avx2(src, dst, sizeof(*dst) * stride); -} - -static inline void storeu_u16_8x2_avx2(const __m256i src, uint16_t *const dst, - const ptrdiff_t stride) { - storeu_8bit_16x2_avx2(src, dst, sizeof(*dst) * stride); -} - -#endif // AOM_THIRD_PARTY_SVT_AV1_EBMEMORY_AVX2_H_
diff --git a/third_party/SVT-AV1/EbMemory_SSE4_1.h b/third_party/SVT-AV1/EbMemory_SSE4_1.h deleted file mode 100644 index a1ab531..0000000 --- a/third_party/SVT-AV1/EbMemory_SSE4_1.h +++ /dev/null
@@ -1,39 +0,0 @@ -/* - * Copyright(c) 2019 Intel Corporation - * - * 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 https://www.aomedia.org/license/software-license. 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 - * https://www.aomedia.org/license/patent-license. - */ - -#ifndef AOM_THIRD_PARTY_SVT_AV1_EBMEMORY_SSE4_1_H_ -#define AOM_THIRD_PARTY_SVT_AV1_EBMEMORY_SSE4_1_H_ - -#include <smmintrin.h> - -#include "config/aom_config.h" - -#include "aom/aom_integer.h" -#include "aom_dsp/x86/mem_sse2.h" - -static inline __m128i load8bit_4x2_sse4_1(const void *const src, - const ptrdiff_t strideInByte) { - const __m128i s = _mm_cvtsi32_si128(loadu_int32(src)); - return _mm_insert_epi32(s, loadu_int32((uint8_t *)src + strideInByte), 1); -} - -static inline __m128i load_u8_4x2_sse4_1(const uint8_t *const src, - const ptrdiff_t stride) { - return load8bit_4x2_sse4_1(src, sizeof(*src) * stride); -} - -static inline __m128i load_u16_2x2_sse4_1(const uint16_t *const src, - const ptrdiff_t stride) { - return load8bit_4x2_sse4_1(src, sizeof(*src) * stride); -} - -#endif // AOM_THIRD_PARTY_SVT_AV1_EBMEMORY_SSE4_1_H_
diff --git a/third_party/SVT-AV1/LICENSE.md b/third_party/SVT-AV1/LICENSE.md deleted file mode 100644 index aff96d1..0000000 --- a/third_party/SVT-AV1/LICENSE.md +++ /dev/null
@@ -1,32 +0,0 @@ -BSD 3-Clause Clear License -The Clear BSD License - -Copyright (c) 2021, Alliance for Open Media - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted (subject to the limitations in the disclaimer below) -provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - -3. Neither the name of the Alliance for Open Media nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/SVT-AV1/PATENTS.md b/third_party/SVT-AV1/PATENTS.md deleted file mode 100644 index 1de4dd7..0000000 --- a/third_party/SVT-AV1/PATENTS.md +++ /dev/null
@@ -1,107 +0,0 @@ -**Alliance for Open Media Patent License 1.0** - - 1. **License Terms.** - - **Patent License.** Subject to the terms and conditions of this License, each - Licensor, on behalf of itself and successors in interest and assigns, - grants Licensee a non-sublicensable, perpetual, worldwide, non-exclusive, - no-charge, royalty-free, irrevocable (except as expressly stated in this - License) patent license to its Necessary Claims to make, use, sell, offer - for sale, import or distribute any Implementation. - - **Conditions.** - - *Availability.* As a condition to the grant of rights to Licensee to make, - sell, offer for sale, import or distribute an Implementation under - Section 1.1, Licensee must make its Necessary Claims available under - this License, and must reproduce this License with any Implementation - as follows: - - a. For distribution in source code, by including this License in the - root directory of the source code with its Implementation. - - b. For distribution in any other form (including binary, object form, - and/or hardware description code (e.g., HDL, RTL, Gate Level Netlist, - GDSII, etc.)), by including this License in the documentation, legal - notices, and/or other written materials provided with the - Implementation. - - *Additional Conditions.* This license is directly from Licensor to - Licensee. Licensee acknowledges as a condition of benefiting from it - that no rights from Licensor are received from suppliers, distributors, - or otherwise in connection with this License. - - **Defensive Termination**. If any Licensee, its Affiliates, or its agents - initiates patent litigation or files, maintains, or voluntarily - participates in a lawsuit against another entity or any person asserting - that any Implementation infringes Necessary Claims, any patent licenses - granted under this License directly to the Licensee are immediately - terminated as of the date of the initiation of action unless 1) that suit - was in response to a corresponding suit regarding an Implementation first - brought against an initiating entity, or 2) that suit was brought to - enforce the terms of this License (including intervention in a third-party - action by a Licensee). - - **Disclaimers.** The Reference Implementation and Specification are provided - "AS IS" and without warranty. The entire risk as to implementing or - otherwise using the Reference Implementation or Specification is assumed - by the implementer and user. Licensor expressly disclaims any warranties - (express, implied, or otherwise), including implied warranties of - merchantability, non-infringement, fitness for a particular purpose, or - title, related to the material. IN NO EVENT WILL LICENSOR BE LIABLE TO - ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, - INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF - ACTION OF ANY KIND WITH RESPECT TO THIS LICENSE, WHETHER BASED ON BREACH - OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR - NOT THE OTHER PARTRY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -2. **Definitions.** - - **Affiliate.** "Affiliate" means an entity that directly or indirectly - Controls, is Controlled by, or is under common Control of that party. - - **Control.** "Control" means direct or indirect control of more than 50% of - the voting power to elect directors of that corporation, or for any other - entity, the power to direct management of such entity. - - **Decoder.** "Decoder" means any decoder that conforms fully with all - non-optional portions of the Specification. - - **Encoder.** "Encoder" means any encoder that produces a bitstream that can - be decoded by a Decoder only to the extent it produces such a bitstream. - - **Final Deliverable.** "Final Deliverable" means the final version of a - deliverable approved by the Alliance for Open Media as a Final - Deliverable. - - **Implementation.** "Implementation" means any implementation, including the - Reference Implementation, that is an Encoder and/or a Decoder. An - Implementation also includes components of an Implementation only to the - extent they are used as part of an Implementation. - - **License.** "License" means this license. - - **Licensee.** "Licensee" means any person or entity who exercises patent - rights granted under this License. - - **Licensor.** "Licensor" means (i) any Licensee that makes, sells, offers - for sale, imports or distributes any Implementation, or (ii) a person - or entity that has a licensing obligation to the Implementation as a - result of its membership and/or participation in the Alliance for Open - Media working group that developed the Specification. - - **Necessary Claims.** "Necessary Claims" means all claims of patents or - patent applications, (a) that currently or at any time in the future, - are owned or controlled by the Licensor, and (b) (i) would be an - Essential Claim as defined by the W3C Policy as of February 5, 2004 - (https://www.w3.org/Consortium/Patent-Policy-20040205/#def-essential) - as if the Specification was a W3C Recommendation; or (ii) are infringed - by the Reference Implementation. - - **Reference Implementation.** "Reference Implementation" means an Encoder - and/or Decoder released by the Alliance for Open Media as a Final - Deliverable. - - **Specification.** "Specification" means the specification designated by - the Alliance for Open Media as a Final Deliverable for which this - License was issued.
diff --git a/third_party/SVT-AV1/README.libaom b/third_party/SVT-AV1/README.libaom deleted file mode 100644 index 1dadd49..0000000 --- a/third_party/SVT-AV1/README.libaom +++ /dev/null
@@ -1,16 +0,0 @@ -URL: https://gitlab.com/AOMediaCodec/SVT-AV1 - -Version: 8ff99c90359330d2e807757c9425560bbc452ff3 -License: BSD-3-clause clear -License File: LICENSE.md - -Description: -Port the x86 intrinsics used for single reference convolve reconstructions. - -Local Changes: -Only ported the functions pertinent to single reference convolves. -All functions are made static inline to avoid function call overheads. -References to some arrays are changed to libaom version when applicable. -Some extra intrinsic functions are added to support missing block sizes. -Unaligned integer loads are changed to use functions from -aom_dsp/x86/mem_sse2.h.
diff --git a/third_party/SVT-AV1/convolve_2d_avx2.h b/third_party/SVT-AV1/convolve_2d_avx2.h deleted file mode 100644 index 64cd810..0000000 --- a/third_party/SVT-AV1/convolve_2d_avx2.h +++ /dev/null
@@ -1,1199 +0,0 @@ -/* - * Copyright (c) 2017, 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. - */ - -#ifndef THIRD_PARTY_SVT_AV1_CONVOLVE_2D_AVX2_H_ -#define THIRD_PARTY_SVT_AV1_CONVOLVE_2D_AVX2_H_ - -#include "convolve_avx2.h" - -static void convolve_2d_sr_hor_2tap_avx2( - const uint8_t *const src, const int32_t src_stride, const int32_t w, - const int32_t h, const InterpFilterParams *const filter_params_x, - const int32_t subpel_x_q4, int16_t *const im_block) { - const uint8_t *src_ptr = src; - int32_t y = h; - int16_t *im = im_block; - - if (w <= 8) { - __m128i coeffs_128; - - prepare_half_coeffs_2tap_ssse3(filter_params_x, subpel_x_q4, &coeffs_128); - - if (w == 2) { - do { - const __m128i r = - x_convolve_2tap_2x2_sse4_1(src_ptr, src_stride, &coeffs_128); - xy_x_round_store_2x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 2; - y -= 2; - } while (y); - } else if (w == 4) { - do { - const __m128i r = - x_convolve_2tap_4x2_ssse3(src_ptr, src_stride, &coeffs_128); - xy_x_round_store_4x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 4; - y -= 2; - } while (y); - } else { - assert(w == 8); - - do { - __m128i r[2]; - - x_convolve_2tap_8x2_ssse3(src_ptr, src_stride, &coeffs_128, r); - xy_x_round_store_8x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 8; - y -= 2; - } while (y); - } - } else { - __m256i coeffs_256; - - prepare_half_coeffs_2tap_avx2(filter_params_x, subpel_x_q4, &coeffs_256); - - if (w == 16) { - do { - __m256i r[2]; - - x_convolve_2tap_16x2_avx2(src_ptr, src_stride, &coeffs_256, r); - xy_x_round_store_32_avx2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 16; - y -= 2; - } while (y); - } else if (w == 32) { - do { - xy_x_2tap_32_avx2(src_ptr, &coeffs_256, im); - src_ptr += src_stride; - im += 32; - } while (--y); - } else if (w == 64) { - do { - xy_x_2tap_32_avx2(src_ptr + 0 * 32, &coeffs_256, im + 0 * 32); - xy_x_2tap_32_avx2(src_ptr + 1 * 32, &coeffs_256, im + 1 * 32); - src_ptr += src_stride; - im += 64; - } while (--y); - } else { - assert(w == 128); - - do { - xy_x_2tap_32_avx2(src_ptr + 0 * 32, &coeffs_256, im + 0 * 32); - xy_x_2tap_32_avx2(src_ptr + 1 * 32, &coeffs_256, im + 1 * 32); - xy_x_2tap_32_avx2(src_ptr + 2 * 32, &coeffs_256, im + 2 * 32); - xy_x_2tap_32_avx2(src_ptr + 3 * 32, &coeffs_256, im + 3 * 32); - src_ptr += src_stride; - im += 128; - } while (--y); - } - } -} - -static void convolve_2d_sr_hor_4tap_ssse3( - const uint8_t *const src, const int32_t src_stride, const int32_t w, - const int32_t h, const InterpFilterParams *const filter_params_x, - const int32_t subpel_x_q4, int16_t *const im_block) { - const uint8_t *src_ptr = src - 1; - int32_t y = h; - int16_t *im = im_block; - - if (w <= 4) { - __m128i coeffs_128[2]; - - prepare_half_coeffs_4tap_ssse3(filter_params_x, subpel_x_q4, coeffs_128); - if (w == 2) { - do { - const __m128i r = - x_convolve_4tap_2x2_ssse3(src_ptr, src_stride, coeffs_128); - xy_x_round_store_2x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 2; - y -= 2; - } while (y); - } else if (w == 4) { - do { - const __m128i r = - x_convolve_4tap_4x2_ssse3(src_ptr, src_stride, coeffs_128); - xy_x_round_store_4x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 4; - y -= 2; - } while (y); - } - } else { - // TODO(chiyotsai@google.com): Add better optimization - __m256i coeffs_256[2], filt_256[2]; - - prepare_half_coeffs_4tap_avx2(filter_params_x, subpel_x_q4, coeffs_256); - filt_256[0] = _mm256_load_si256((__m256i const *)filt1_global_avx2); - filt_256[1] = _mm256_load_si256((__m256i const *)filt2_global_avx2); - - if (w == 8) { - do { - __m256i res = - x_convolve_4tap_8x2_avx2(src_ptr, src_stride, coeffs_256, filt_256); - xy_x_round_store_8x2_avx2(res, im); - - src_ptr += 2 * src_stride; - im += 2 * 8; - y -= 2; - } while (y); - } else if (w == 16) { - do { - __m256i r[2]; - - x_convolve_4tap_16x2_avx2(src_ptr, src_stride, coeffs_256, filt_256, r); - xy_x_round_store_32_avx2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 16; - y -= 2; - } while (y); - } else if (w == 32) { - do { - xy_x_4tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - - src_ptr += src_stride; - im += 32; - } while (--y); - } else if (w == 64) { - do { - xy_x_4tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - xy_x_4tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, im + 32); - src_ptr += src_stride; - im += 64; - } while (--y); - } else { - assert(w == 128); - - do { - xy_x_4tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - xy_x_4tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, im + 32); - xy_x_4tap_32_avx2(src_ptr + 64, coeffs_256, filt_256, im + 64); - xy_x_4tap_32_avx2(src_ptr + 96, coeffs_256, filt_256, im + 96); - src_ptr += src_stride; - im += 128; - } while (--y); - } - } -} - -static void convolve_2d_sr_hor_6tap_avx2( - const uint8_t *const src, const int32_t src_stride, const int32_t w, - const int32_t h, const InterpFilterParams *const filter_params_x, - const int32_t subpel_x_q4, int16_t *const im_block) { - const uint8_t *src_ptr = src - 2; - int32_t y = h; - int16_t *im = im_block; - - if (w <= 4) { - __m128i coeffs_128[3]; - - prepare_half_coeffs_6tap_ssse3(filter_params_x, subpel_x_q4, coeffs_128); - if (w == 2) { - do { - const __m128i r = - x_convolve_6tap_2x2_ssse3(src_ptr, src_stride, coeffs_128); - xy_x_round_store_2x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 2; - y -= 2; - } while (y); - } else if (w == 4) { - do { - const __m128i r = - x_convolve_6tap_4x2_ssse3(src_ptr, src_stride, coeffs_128); - xy_x_round_store_4x2_sse2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 4; - y -= 2; - } while (y); - } - } else { - __m256i coeffs_256[3], filt_256[3]; - - filt_256[0] = _mm256_loadu_si256((__m256i const *)filt1_global_avx2); - filt_256[1] = _mm256_loadu_si256((__m256i const *)filt2_global_avx2); - filt_256[2] = _mm256_loadu_si256((__m256i const *)filt3_global_avx2); - - prepare_half_coeffs_6tap_avx2(filter_params_x, subpel_x_q4, coeffs_256); - - if (w == 8) { - do { - const __m256i res = - x_convolve_6tap_8x2_avx2(src_ptr, src_stride, coeffs_256, filt_256); - xy_x_round_store_8x2_avx2(res, im); - - src_ptr += 2 * src_stride; - im += 2 * 8; - y -= 2; - } while (y); - } else if (w == 16) { - do { - __m256i r[2]; - - x_convolve_6tap_16x2_avx2(src_ptr, src_stride, coeffs_256, filt_256, r); - xy_x_round_store_32_avx2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 16; - y -= 2; - } while (y); - } else if (w == 32) { - do { - xy_x_6tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - src_ptr += src_stride; - im += 32; - } while (--y); - } else if (w == 64) { - do { - xy_x_6tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - xy_x_6tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, im + 32); - src_ptr += src_stride; - im += 64; - } while (--y); - } else { - assert(w == 128); - - do { - xy_x_6tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - xy_x_6tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, im + 32); - xy_x_6tap_32_avx2(src_ptr + 64, coeffs_256, filt_256, im + 64); - xy_x_6tap_32_avx2(src_ptr + 96, coeffs_256, filt_256, im + 96); - src_ptr += src_stride; - im += 128; - } while (--y); - } - } -} - -static void convolve_2d_sr_hor_8tap_avx2( - const uint8_t *const src, const int32_t src_stride, const int32_t w, - const int32_t h, const InterpFilterParams *const filter_params_x, - const int32_t subpel_x_q4, int16_t *const im_block) { - const uint8_t *src_ptr = src - 3; - int32_t y = h; - int16_t *im = im_block; - __m256i coeffs_256[4], filt_256[4]; - - filt_256[0] = _mm256_loadu_si256((__m256i const *)filt1_global_avx2); - filt_256[1] = _mm256_loadu_si256((__m256i const *)filt2_global_avx2); - filt_256[2] = _mm256_loadu_si256((__m256i const *)filt3_global_avx2); - filt_256[3] = _mm256_loadu_si256((__m256i const *)filt4_global_avx2); - - prepare_half_coeffs_8tap_avx2(filter_params_x, subpel_x_q4, coeffs_256); - - if (w == 8) { - do { - const __m256i res = - x_convolve_8tap_8x2_avx2(src_ptr, src_stride, coeffs_256, filt_256); - xy_x_round_store_8x2_avx2(res, im); - src_ptr += 2 * src_stride; - im += 2 * 8; - y -= 2; - } while (y); - } else if (w == 16) { - do { - __m256i r[2]; - - x_convolve_8tap_16x2_avx2(src_ptr, src_stride, coeffs_256, filt_256, r); - xy_x_round_store_32_avx2(r, im); - src_ptr += 2 * src_stride; - im += 2 * 16; - y -= 2; - } while (y); - } else if (w == 32) { - do { - xy_x_8tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - src_ptr += src_stride; - im += 32; - } while (--y); - } else if (w == 64) { - do { - xy_x_8tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - xy_x_8tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, im + 32); - src_ptr += src_stride; - im += 64; - } while (--y); - } else { - assert(w == 128); - - do { - xy_x_8tap_32_avx2(src_ptr, coeffs_256, filt_256, im); - xy_x_8tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, im + 32); - xy_x_8tap_32_avx2(src_ptr + 64, coeffs_256, filt_256, im + 64); - xy_x_8tap_32_avx2(src_ptr + 96, coeffs_256, filt_256, im + 96); - src_ptr += src_stride; - im += 128; - } while (--y); - } -} - -static void convolve_2d_sr_ver_2tap_avx2( - const int16_t *const im_block, const int32_t w, const int32_t h, - const InterpFilterParams *const filter_params_y, const int32_t subpel_y_q4, - uint8_t *dst, const int32_t dst_stride) { - const int16_t *im = im_block; - int32_t y = h; - - if (w <= 4) { - __m128i coeffs_128; - - prepare_coeffs_2tap_sse2(filter_params_y, subpel_y_q4, &coeffs_128); - - if (w == 2) { - __m128i s_32[2]; - - s_32[0] = _mm_cvtsi32_si128(*(int32_t *)im); - - do { - const __m128i res = xy_y_convolve_2tap_2x2_sse2(im, s_32, &coeffs_128); - xy_y_round_store_2x2_sse2(res, dst, dst_stride); - im += 2 * 2; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m128i s_64[2], r[2]; - - assert(w == 4); - - s_64[0] = _mm_loadl_epi64((__m128i *)im); - - do { - xy_y_convolve_2tap_4x2_sse2(im, s_64, &coeffs_128, r); - r[0] = xy_y_round_sse2(r[0]); - r[1] = xy_y_round_sse2(r[1]); - const __m128i rr = _mm_packs_epi32(r[0], r[1]); - pack_store_4x2_sse2(rr, dst, dst_stride); - im += 2 * 4; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - __m256i coeffs_256; - - prepare_coeffs_2tap_avx2(filter_params_y, subpel_y_q4, &coeffs_256); - - if (w == 8) { - __m128i s_128[2]; - __m256i r[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)im); - - do { - xy_y_convolve_2tap_8x2_avx2(im, s_128, &coeffs_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - __m256i s_256[2], r[4]; - - s_256[0] = _mm256_loadu_si256((__m256i *)im); - - do { - xy_y_convolve_2tap_16x2_avx2(im, s_256, &coeffs_256, r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - __m256i s_256[2][2]; - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(im + 0 * 16)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(im + 1 * 16)); - - do { - xy_y_convolve_2tap_32_all_avx2(im + 32, s_256[0], s_256[1], &coeffs_256, - dst); - im += 2 * 32; - xy_y_convolve_2tap_32_all_avx2(im, s_256[1], s_256[0], &coeffs_256, - dst + dst_stride); - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 64) { - __m256i s_256[2][4]; - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(im + 0 * 16)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(im + 1 * 16)); - s_256[0][2] = _mm256_loadu_si256((__m256i *)(im + 2 * 16)); - s_256[0][3] = _mm256_loadu_si256((__m256i *)(im + 3 * 16)); - - do { - xy_y_convolve_2tap_32_all_avx2(im + 64, s_256[0] + 0, s_256[1] + 0, - &coeffs_256, dst); - xy_y_convolve_2tap_32_all_avx2(im + 96, s_256[0] + 2, s_256[1] + 2, - &coeffs_256, dst + 32); - im += 2 * 64; - xy_y_convolve_2tap_32_all_avx2(im, s_256[1] + 0, s_256[0] + 0, - &coeffs_256, dst + dst_stride); - xy_y_convolve_2tap_32_all_avx2(im + 32, s_256[1] + 2, s_256[0] + 2, - &coeffs_256, dst + dst_stride + 32); - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i s_256[2][8]; - - assert(w == 128); - - load_16bit_8rows_avx2(im, 16, s_256[0]); - - do { - xy_y_convolve_2tap_32_all_avx2(im + 128, s_256[0] + 0, s_256[1] + 0, - &coeffs_256, dst); - xy_y_convolve_2tap_32_all_avx2(im + 160, s_256[0] + 2, s_256[1] + 2, - &coeffs_256, dst + 1 * 32); - xy_y_convolve_2tap_32_all_avx2(im + 192, s_256[0] + 4, s_256[1] + 4, - &coeffs_256, dst + 2 * 32); - xy_y_convolve_2tap_32_all_avx2(im + 224, s_256[0] + 6, s_256[1] + 6, - &coeffs_256, dst + 3 * 32); - im += 2 * 128; - xy_y_convolve_2tap_32_all_avx2(im, s_256[1] + 0, s_256[0] + 0, - &coeffs_256, dst + dst_stride); - xy_y_convolve_2tap_32_all_avx2(im + 32, s_256[1] + 2, s_256[0] + 2, - &coeffs_256, dst + dst_stride + 1 * 32); - xy_y_convolve_2tap_32_all_avx2(im + 64, s_256[1] + 4, s_256[0] + 4, - &coeffs_256, dst + dst_stride + 2 * 32); - xy_y_convolve_2tap_32_all_avx2(im + 96, s_256[1] + 6, s_256[0] + 6, - &coeffs_256, dst + dst_stride + 3 * 32); - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } -} - -static void convolve_2d_sr_ver_2tap_half_avx2( - const int16_t *const im_block, const int32_t w, const int32_t h, - const InterpFilterParams *const filter_params_y, const int32_t subpel_y_q4, - uint8_t *dst, const int32_t dst_stride) { - const int16_t *im = im_block; - int32_t y = h; - - (void)filter_params_y; - (void)subpel_y_q4; - - if (w == 2) { - __m128i s_32[2]; - - s_32[0] = _mm_cvtsi32_si128(*(int32_t *)im); - - do { - const __m128i res = xy_y_convolve_2tap_2x2_half_pel_sse2(im, s_32); - const __m128i r = xy_y_round_half_pel_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - im += 2 * 2; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 4) { - __m128i s_64[2]; - - s_64[0] = _mm_loadl_epi64((__m128i *)im); - - do { - const __m128i res = xy_y_convolve_2tap_4x2_half_pel_sse2(im, s_64); - const __m128i r = xy_y_round_half_pel_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - im += 2 * 4; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 8) { - __m128i s_128[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)im); - - do { - const __m256i res = xy_y_convolve_2tap_8x2_half_pel_avx2(im, s_128); - const __m256i r = xy_y_round_half_pel_avx2(res); - pack_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - __m256i s_256[2], r[2]; - - s_256[0] = _mm256_loadu_si256((__m256i *)im); - - do { - xy_y_convolve_2tap_16x2_half_pel_avx2(im, s_256, r); - r[0] = xy_y_round_half_pel_avx2(r[0]); - r[1] = xy_y_round_half_pel_avx2(r[1]); - xy_y_pack_store_16x2_avx2(r[0], r[1], dst, dst_stride); - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - __m256i s_256[2][2]; - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(im + 0 * 16)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(im + 1 * 16)); - - do { - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 32, s_256[0], s_256[1], dst); - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 2 * 32, s_256[1], s_256[0], - dst + dst_stride); - im += 2 * 32; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 64) { - __m256i s_256[2][4]; - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(im + 0 * 16)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(im + 1 * 16)); - s_256[0][2] = _mm256_loadu_si256((__m256i *)(im + 2 * 16)); - s_256[0][3] = _mm256_loadu_si256((__m256i *)(im + 3 * 16)); - - do { - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 64, s_256[0] + 0, - s_256[1] + 0, dst); - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 96, s_256[0] + 2, - s_256[1] + 2, dst + 32); - im += 2 * 64; - xy_y_convolve_2tap_half_pel_32_all_avx2(im, s_256[1] + 0, s_256[0] + 0, - dst + dst_stride); - xy_y_convolve_2tap_half_pel_32_all_avx2( - im + 32, s_256[1] + 2, s_256[0] + 2, dst + dst_stride + 32); - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i s_256[2][8]; - - assert(w == 128); - - load_16bit_8rows_avx2(im, 16, s_256[0]); - - do { - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 128, s_256[0] + 0, - s_256[1] + 0, dst); - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 160, s_256[0] + 2, - s_256[1] + 2, dst + 1 * 32); - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 192, s_256[0] + 4, - s_256[1] + 4, dst + 2 * 32); - xy_y_convolve_2tap_half_pel_32_all_avx2(im + 224, s_256[0] + 6, - s_256[1] + 6, dst + 3 * 32); - im += 2 * 128; - xy_y_convolve_2tap_half_pel_32_all_avx2(im, s_256[1] + 0, s_256[0] + 0, - dst + dst_stride); - xy_y_convolve_2tap_half_pel_32_all_avx2( - im + 32, s_256[1] + 2, s_256[0] + 2, dst + dst_stride + 1 * 32); - xy_y_convolve_2tap_half_pel_32_all_avx2( - im + 64, s_256[1] + 4, s_256[0] + 4, dst + dst_stride + 2 * 32); - xy_y_convolve_2tap_half_pel_32_all_avx2( - im + 96, s_256[1] + 6, s_256[0] + 6, dst + dst_stride + 3 * 32); - dst += 2 * dst_stride; - y -= 2; - } while (y); - } -} - -static void convolve_2d_sr_ver_4tap_avx2( - const int16_t *const im_block, const int32_t w, const int32_t h, - const InterpFilterParams *const filter_params_y, const int32_t subpel_y_q4, - uint8_t *dst, const int32_t dst_stride) { - const int16_t *im = im_block; - int32_t y = h; - - if (w == 2) { - __m128i coeffs_128[2], s_32[4], ss_128[2]; - - prepare_coeffs_4tap_sse2(filter_params_y, subpel_y_q4, coeffs_128); - - s_32[0] = _mm_cvtsi32_si128(*(int32_t *)(im + 0 * 2)); - s_32[1] = _mm_cvtsi32_si128(*(int32_t *)(im + 1 * 2)); - s_32[2] = _mm_cvtsi32_si128(*(int32_t *)(im + 2 * 2)); - - const __m128i src01 = _mm_unpacklo_epi32(s_32[0], s_32[1]); - const __m128i src12 = _mm_unpacklo_epi32(s_32[1], s_32[2]); - - ss_128[0] = _mm_unpacklo_epi16(src01, src12); - - do { - const __m128i res = - xy_y_convolve_4tap_2x2_sse2(im, s_32, ss_128, coeffs_128); - xy_y_round_store_2x2_sse2(res, dst, dst_stride); - im += 2 * 2; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i coeffs_256[2]; - - prepare_coeffs_4tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 4) { - __m128i s_64[4]; - __m256i s_256[2], ss_256[2]; - - s_64[0] = _mm_loadl_epi64((__m128i *)(im + 0 * 4)); - s_64[1] = _mm_loadl_epi64((__m128i *)(im + 1 * 4)); - s_64[2] = _mm_loadl_epi64((__m128i *)(im + 2 * 4)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - s_256[0] = _mm256_setr_m128i(s_64[0], s_64[1]); - s_256[1] = _mm256_setr_m128i(s_64[1], s_64[2]); - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - - do { - const __m256i res = - xy_y_convolve_4tap_4x2_avx2(im, s_64, ss_256, coeffs_256); - xy_y_round_store_4x2_avx2(res, dst, dst_stride); - im += 2 * 4; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 8) { - __m256i s_256[4], r[2]; - - s_256[0] = _mm256_loadu_si256((__m256i *)(im + 0 * 8)); - s_256[1] = _mm256_loadu_si256((__m256i *)(im + 1 * 8)); - - if (subpel_y_q4 != 8) { - __m256i ss_256[4]; - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[2] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - - do { - xy_y_convolve_4tap_8x2_avx2(im, ss_256, coeffs_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - do { - xy_y_convolve_4tap_8x2_half_pel_avx2(im, coeffs_256, s_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else if (w == 16) { - __m256i s_256[5]; - - s_256[0] = _mm256_loadu_si256((__m256i *)(im + 0 * 16)); - s_256[1] = _mm256_loadu_si256((__m256i *)(im + 1 * 16)); - s_256[2] = _mm256_loadu_si256((__m256i *)(im + 2 * 16)); - - if (subpel_y_q4 != 8) { - __m256i ss_256[4], tt_256[4], r[4]; - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[2] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - - tt_256[0] = _mm256_unpacklo_epi16(s_256[1], s_256[2]); - tt_256[2] = _mm256_unpackhi_epi16(s_256[1], s_256[2]); - - do { - xy_y_convolve_4tap_16x2_avx2(im, s_256, ss_256, tt_256, coeffs_256, - r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i r[4]; - - do { - xy_y_convolve_4tap_16x2_half_pelavx2(im, s_256, coeffs_256, r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - /*It's a special condition for OBMC. A/c to Av1 spec 4-tap won't - support for width(w)>16, but for OBMC while predicting above block - it reduces size block to Wx(h/2), for example, if above block size - is 32x8, we get block size as 32x4 for OBMC.*/ - int32_t x = 0; - - assert(!(w % 32)); - - __m256i s_256[2][4], ss_256[2][4], tt_256[2][4], r0[4], r1[4]; - do { - const int16_t *s = im + x; - uint8_t *d = dst + x; - - loadu_unpack_16bit_3rows_avx2(s, w, s_256[0], ss_256[0], tt_256[0]); - loadu_unpack_16bit_3rows_avx2(s + 16, w, s_256[1], ss_256[1], - tt_256[1]); - - y = h; - do { - xy_y_convolve_4tap_32x2_avx2(s, w, s_256[0], ss_256[0], tt_256[0], - coeffs_256, r0); - xy_y_convolve_4tap_32x2_avx2(s + 16, w, s_256[1], ss_256[1], - tt_256[1], coeffs_256, r1); - - xy_y_round_store_32_avx2(r0 + 0, r1 + 0, d); - xy_y_round_store_32_avx2(r0 + 2, r1 + 2, d + dst_stride); - - s += 2 * w; - d += 2 * dst_stride; - y -= 2; - } while (y); - - x += 32; - } while (x < w); - } - } -} - -static void convolve_2d_sr_ver_6tap_avx2( - const int16_t *const im_block, const int32_t w, const int32_t h, - const InterpFilterParams *const filter_params_y, const int32_t subpel_y_q4, - uint8_t *dst, const int32_t dst_stride) { - const int16_t *im = im_block; - int32_t y; - - if (w == 2) { - __m128i coeffs_128[3], s_32[6], ss_128[3]; - - prepare_coeffs_6tap_ssse3(filter_params_y, subpel_y_q4, coeffs_128); - - s_32[0] = _mm_cvtsi32_si128(*(int32_t *)(im + 0 * 2)); - s_32[1] = _mm_cvtsi32_si128(*(int32_t *)(im + 1 * 2)); - s_32[2] = _mm_cvtsi32_si128(*(int32_t *)(im + 2 * 2)); - s_32[3] = _mm_cvtsi32_si128(*(int32_t *)(im + 3 * 2)); - s_32[4] = _mm_cvtsi32_si128(*(int32_t *)(im + 4 * 2)); - - const __m128i src01 = _mm_unpacklo_epi32(s_32[0], s_32[1]); - const __m128i src12 = _mm_unpacklo_epi32(s_32[1], s_32[2]); - const __m128i src23 = _mm_unpacklo_epi32(s_32[2], s_32[3]); - const __m128i src34 = _mm_unpacklo_epi32(s_32[3], s_32[4]); - - ss_128[0] = _mm_unpacklo_epi16(src01, src12); - ss_128[1] = _mm_unpacklo_epi16(src23, src34); - - y = h; - do { - const __m128i res = - xy_y_convolve_6tap_2x2_sse2(im, s_32, ss_128, coeffs_128); - xy_y_round_store_2x2_sse2(res, dst, dst_stride); - im += 2 * 2; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i coeffs_256[3]; - - prepare_coeffs_6tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 4) { - __m128i s_64[6]; - __m256i s_256[6], ss_256[3]; - - s_64[0] = _mm_loadl_epi64((__m128i *)(im + 0 * 4)); - s_64[1] = _mm_loadl_epi64((__m128i *)(im + 1 * 4)); - s_64[2] = _mm_loadl_epi64((__m128i *)(im + 2 * 4)); - s_64[3] = _mm_loadl_epi64((__m128i *)(im + 3 * 4)); - s_64[4] = _mm_loadl_epi64((__m128i *)(im + 4 * 4)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - s_256[0] = _mm256_setr_m128i(s_64[0], s_64[1]); - s_256[1] = _mm256_setr_m128i(s_64[1], s_64[2]); - s_256[2] = _mm256_setr_m128i(s_64[2], s_64[3]); - s_256[3] = _mm256_setr_m128i(s_64[3], s_64[4]); - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - - y = h; - do { - const __m256i res = - xy_y_convolve_6tap_4x2_avx2(im, s_64, ss_256, coeffs_256); - xy_y_round_store_4x2_avx2(res, dst, dst_stride); - im += 2 * 4; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 8) { - __m256i s_256[6], r[2]; - - s_256[0] = _mm256_loadu_si256((__m256i *)(im + 0 * 8)); - s_256[1] = _mm256_loadu_si256((__m256i *)(im + 1 * 8)); - s_256[2] = _mm256_loadu_si256((__m256i *)(im + 2 * 8)); - s_256[3] = _mm256_loadu_si256((__m256i *)(im + 3 * 8)); - y = h; - - if (subpel_y_q4 != 8) { - __m256i ss_256[6]; - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - - ss_256[3] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - ss_256[4] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - - do { - xy_y_convolve_6tap_8x2_avx2(im, ss_256, coeffs_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - do { - xy_y_convolve_6tap_8x2_half_pel_avx2(im, coeffs_256, s_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else if (w == 16) { - __m256i s_256[6]; - - s_256[0] = _mm256_loadu_si256((__m256i *)(im + 0 * 16)); - s_256[1] = _mm256_loadu_si256((__m256i *)(im + 1 * 16)); - s_256[2] = _mm256_loadu_si256((__m256i *)(im + 2 * 16)); - s_256[3] = _mm256_loadu_si256((__m256i *)(im + 3 * 16)); - s_256[4] = _mm256_loadu_si256((__m256i *)(im + 4 * 16)); - y = h; - - if (subpel_y_q4 != 8) { - __m256i ss_256[6], tt_256[6], r[4]; - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - ss_256[4] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - - tt_256[0] = _mm256_unpacklo_epi16(s_256[1], s_256[2]); - tt_256[1] = _mm256_unpacklo_epi16(s_256[3], s_256[4]); - tt_256[3] = _mm256_unpackhi_epi16(s_256[1], s_256[2]); - tt_256[4] = _mm256_unpackhi_epi16(s_256[3], s_256[4]); - - do { - xy_y_convolve_6tap_16x2_avx2(im, 16, s_256, ss_256, tt_256, - coeffs_256, r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i ss_256[4], r[4]; - - do { - xy_y_convolve_6tap_16x2_half_pel_avx2(im, 16, s_256, ss_256, - coeffs_256, r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - int32_t x = 0; - - assert(!(w % 32)); - - __m256i s_256[2][6], ss_256[2][6], tt_256[2][6], r0[4], r1[4]; - - do { - const int16_t *s = im + x; - uint8_t *d = dst + x; - - loadu_unpack_16bit_5rows_avx2(s, w, s_256[0], ss_256[0], tt_256[0]); - loadu_unpack_16bit_5rows_avx2(s + 16, w, s_256[1], ss_256[1], - tt_256[1]); - - y = h; - do { - xy_y_convolve_6tap_16x2_avx2(s, w, s_256[0], ss_256[0], tt_256[0], - coeffs_256, r0); - xy_y_convolve_6tap_16x2_avx2(s + 16, w, s_256[1], ss_256[1], - tt_256[1], coeffs_256, r1); - - xy_y_round_store_32_avx2(r0 + 0, r1 + 0, d); - xy_y_round_store_32_avx2(r0 + 2, r1 + 2, d + dst_stride); - - s += 2 * w; - d += 2 * dst_stride; - y -= 2; - } while (y); - - x += 32; - } while (x < w); - } - } -} - -static void convolve_2d_sr_ver_8tap_avx2( - const int16_t *const im_block, const int32_t w, const int32_t h, - const InterpFilterParams *const filter_params_y, const int32_t subpel_y_q4, - uint8_t *dst, const int32_t dst_stride) { - const int16_t *im = im_block; - int32_t y; - - if (w == 2) { - __m128i coeffs_128[4], s_32[8], ss_128[4]; - - prepare_coeffs_8tap_sse2(filter_params_y, subpel_y_q4, coeffs_128); - - s_32[0] = _mm_cvtsi32_si128(*(int32_t *)(im + 0 * 2)); - s_32[1] = _mm_cvtsi32_si128(*(int32_t *)(im + 1 * 2)); - s_32[2] = _mm_cvtsi32_si128(*(int32_t *)(im + 2 * 2)); - s_32[3] = _mm_cvtsi32_si128(*(int32_t *)(im + 3 * 2)); - s_32[4] = _mm_cvtsi32_si128(*(int32_t *)(im + 4 * 2)); - s_32[5] = _mm_cvtsi32_si128(*(int32_t *)(im + 5 * 2)); - s_32[6] = _mm_cvtsi32_si128(*(int32_t *)(im + 6 * 2)); - - const __m128i src01 = _mm_unpacklo_epi32(s_32[0], s_32[1]); - const __m128i src12 = _mm_unpacklo_epi32(s_32[1], s_32[2]); - const __m128i src23 = _mm_unpacklo_epi32(s_32[2], s_32[3]); - const __m128i src34 = _mm_unpacklo_epi32(s_32[3], s_32[4]); - const __m128i src45 = _mm_unpacklo_epi32(s_32[4], s_32[5]); - const __m128i src56 = _mm_unpacklo_epi32(s_32[5], s_32[6]); - - ss_128[0] = _mm_unpacklo_epi16(src01, src12); - ss_128[1] = _mm_unpacklo_epi16(src23, src34); - ss_128[2] = _mm_unpacklo_epi16(src45, src56); - - y = h; - do { - const __m128i res = - xy_y_convolve_8tap_2x2_sse2(im, s_32, ss_128, coeffs_128); - xy_y_round_store_2x2_sse2(res, dst, dst_stride); - im += 2 * 2; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i coeffs_256[4]; - - prepare_coeffs_8tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 4) { - __m128i s_64[8]; - __m256i s_256[8], ss_256[4]; - - s_64[0] = _mm_loadl_epi64((__m128i *)(im + 0 * 4)); - s_64[1] = _mm_loadl_epi64((__m128i *)(im + 1 * 4)); - s_64[2] = _mm_loadl_epi64((__m128i *)(im + 2 * 4)); - s_64[3] = _mm_loadl_epi64((__m128i *)(im + 3 * 4)); - s_64[4] = _mm_loadl_epi64((__m128i *)(im + 4 * 4)); - s_64[5] = _mm_loadl_epi64((__m128i *)(im + 5 * 4)); - s_64[6] = _mm_loadl_epi64((__m128i *)(im + 6 * 4)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - s_256[0] = _mm256_setr_m128i(s_64[0], s_64[1]); - s_256[1] = _mm256_setr_m128i(s_64[1], s_64[2]); - s_256[2] = _mm256_setr_m128i(s_64[2], s_64[3]); - s_256[3] = _mm256_setr_m128i(s_64[3], s_64[4]); - s_256[4] = _mm256_setr_m128i(s_64[4], s_64[5]); - s_256[5] = _mm256_setr_m128i(s_64[5], s_64[6]); - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[2] = _mm256_unpacklo_epi16(s_256[4], s_256[5]); - - y = h; - do { - const __m256i res = - xy_y_convolve_8tap_4x2_avx2(im, s_64, ss_256, coeffs_256); - xy_y_round_store_4x2_avx2(res, dst, dst_stride); - im += 2 * 4; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 8) { - __m256i s_256[8], r[2]; - - s_256[0] = _mm256_loadu_si256((__m256i *)(im + 0 * 8)); - s_256[1] = _mm256_loadu_si256((__m256i *)(im + 1 * 8)); - s_256[2] = _mm256_loadu_si256((__m256i *)(im + 2 * 8)); - s_256[3] = _mm256_loadu_si256((__m256i *)(im + 3 * 8)); - s_256[4] = _mm256_loadu_si256((__m256i *)(im + 4 * 8)); - s_256[5] = _mm256_loadu_si256((__m256i *)(im + 5 * 8)); - y = h; - - if (subpel_y_q4 != 8) { - __m256i ss_256[8]; - - convolve_8tap_unpack_avx2(s_256, ss_256); - - do { - xy_y_convolve_8tap_8x2_avx2(im, ss_256, coeffs_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - do { - xy_y_convolve_8tap_8x2_half_pel_avx2(im, coeffs_256, s_256, r); - xy_y_round_store_8x2_avx2(r, dst, dst_stride); - im += 2 * 8; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else if (w == 16) { - __m256i s_256[8], r[4]; - - load_16bit_7rows_avx2(im, 16, s_256); - y = h; - - if (subpel_y_q4 != 8) { - __m256i ss_256[8], tt_256[8]; - - convolve_8tap_unpack_avx2(s_256, ss_256); - convolve_8tap_unpack_avx2(s_256 + 1, tt_256); - - do { - xy_y_convolve_8tap_16x2_avx2(im, 16, coeffs_256, s_256, ss_256, - tt_256, r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - do { - xy_y_convolve_8tap_16x2_half_pel_avx2(im, 16, coeffs_256, s_256, r); - xy_y_round_store_16x2_avx2(r, dst, dst_stride); - - im += 2 * 16; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - int32_t x = 0; - __m256i s_256[2][8], r0[4], r1[4]; - - assert(!(w % 32)); - - __m256i ss_256[2][8], tt_256[2][8]; - - do { - const int16_t *s = im + x; - uint8_t *d = dst + x; - - load_16bit_7rows_avx2(s, w, s_256[0]); - convolve_8tap_unpack_avx2(s_256[0], ss_256[0]); - convolve_8tap_unpack_avx2(s_256[0] + 1, tt_256[0]); - - load_16bit_7rows_avx2(s + 16, w, s_256[1]); - convolve_8tap_unpack_avx2(s_256[1], ss_256[1]); - convolve_8tap_unpack_avx2(s_256[1] + 1, tt_256[1]); - - y = h; - do { - xy_y_convolve_8tap_16x2_avx2(s, w, coeffs_256, s_256[0], ss_256[0], - tt_256[0], r0); - xy_y_convolve_8tap_16x2_avx2(s + 16, w, coeffs_256, s_256[1], - ss_256[1], tt_256[1], r1); - xy_y_round_store_32_avx2(r0 + 0, r1 + 0, d); - xy_y_round_store_32_avx2(r0 + 2, r1 + 2, d + dst_stride); - - s += 2 * w; - d += 2 * dst_stride; - y -= 2; - } while (y); - - x += 32; - } while (x < w); - } - } -} - -typedef void (*Convolve2dSrHorTapFunc)( - const uint8_t *const src, const int32_t src_stride, const int32_t w, - const int32_t h, const InterpFilterParams *const filter_params_x, - const int32_t subpel_x_q4, int16_t *const im_block); - -typedef void (*Convolve2dSrVerTapFunc)( - const int16_t *const im_block, const int32_t w, const int32_t h, - const InterpFilterParams *const filter_params_y, const int32_t subpel_y_q4, - uint8_t *dst, const int32_t dst_stride); - -static AOM_FORCE_INLINE void av1_convolve_2d_sr_specialized_avx2( - const uint8_t *src, int32_t src_stride, uint8_t *dst, int32_t dst_stride, - int32_t w, int32_t h, const InterpFilterParams *filter_params_x, - const InterpFilterParams *filter_params_y, const int32_t subpel_x_q4, - const int32_t subpel_y_q4, ConvolveParams *conv_params) { - static const Convolve2dSrHorTapFunc - convolve_2d_sr_hor_tap_func_table[MAX_FILTER_TAP + 1] = { - NULL, - NULL, - convolve_2d_sr_hor_2tap_avx2, - NULL, - convolve_2d_sr_hor_4tap_ssse3, - NULL, - convolve_2d_sr_hor_6tap_avx2, - NULL, - convolve_2d_sr_hor_8tap_avx2 - }; - static const Convolve2dSrVerTapFunc - convolve_2d_sr_ver_tap_func_table[MAX_FILTER_TAP + 1] = { - NULL, - convolve_2d_sr_ver_2tap_half_avx2, - convolve_2d_sr_ver_2tap_avx2, - convolve_2d_sr_ver_4tap_avx2, - convolve_2d_sr_ver_4tap_avx2, - convolve_2d_sr_ver_6tap_avx2, - convolve_2d_sr_ver_6tap_avx2, - convolve_2d_sr_ver_8tap_avx2, - convolve_2d_sr_ver_8tap_avx2 - }; - const int32_t tap_x = get_filter_tap(filter_params_x, subpel_x_q4); - const int32_t tap_y = get_filter_tap(filter_params_y, subpel_y_q4); - - assert(tap_x != 12 && tap_y != 12); - - const uint8_t *src_ptr = src - ((tap_y >> 1) - 1) * src_stride; - // Note: im_block is 8-pixel interlaced for width 32 and up, to avoid data - // permutation. - DECLARE_ALIGNED(32, int16_t, - im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * MAX_SB_SIZE]); - - (void)conv_params; - - assert(conv_params->round_0 == 3); - assert(conv_params->round_1 == 11); - - // horizontal filter - int32_t hh = h + tap_y; - assert(!(hh % 2)); - - convolve_2d_sr_hor_tap_func_table[tap_x]( - src_ptr, src_stride, w, hh, filter_params_x, subpel_x_q4, im_block); - - // vertical filter - convolve_2d_sr_ver_tap_func_table[tap_y - (subpel_y_q4 == 8)]( - im_block, w, h, filter_params_y, subpel_y_q4, dst, dst_stride); -} - -#endif // THIRD_PARTY_SVT_AV1_CONVOLVE_2D_AVX2_H_
diff --git a/third_party/SVT-AV1/convolve_avx2.h b/third_party/SVT-AV1/convolve_avx2.h deleted file mode 100644 index 0e7e436..0000000 --- a/third_party/SVT-AV1/convolve_avx2.h +++ /dev/null
@@ -1,3335 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - -#ifndef THIRD_PARTY_SVT_AV1_CONVOLVE_AVX2_H_ -#define THIRD_PARTY_SVT_AV1_CONVOLVE_AVX2_H_ - -#include "EbMemory_AVX2.h" -#include "EbMemory_SSE4_1.h" -#include "synonyms.h" - -#include "aom_dsp/aom_filter.h" -#include "aom_dsp/x86/convolve_avx2.h" -#include "aom_dsp/x86/mem_sse2.h" - -static inline void populate_coeffs_4tap_avx2(const __m128i coeffs_128, - __m256i coeffs[2]) { - const __m256i coeffs_256 = _mm256_broadcastsi128_si256(coeffs_128); - - // coeffs 2 3 2 3 2 3 2 3 - coeffs[0] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0604u)); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[1] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0a08u)); -} - -static inline void populate_coeffs_6tap_avx2(const __m128i coeffs_128, - __m256i coeffs[3]) { - const __m256i coeffs_256 = _mm256_broadcastsi128_si256(coeffs_128); - - // coeffs 1 2 1 2 1 2 1 2 - coeffs[0] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0402u)); - // coeffs 3 4 3 4 3 4 3 4 - coeffs[1] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0806u)); - // coeffs 5 6 5 6 5 6 5 6 - coeffs[2] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0C0Au)); -} - -static inline void populate_coeffs_8tap_avx2(const __m128i coeffs_128, - __m256i coeffs[4]) { - const __m256i coeffs_256 = _mm256_broadcastsi128_si256(coeffs_128); - - // coeffs 0 1 0 1 0 1 0 1 - coeffs[0] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0200u)); - // coeffs 2 3 2 3 2 3 2 3 - coeffs[1] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0604u)); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[2] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0a08u)); - // coeffs 6 7 6 7 6 7 6 7 - coeffs[3] = _mm256_shuffle_epi8(coeffs_256, _mm256_set1_epi16(0x0e0cu)); -} - -static inline void prepare_half_coeffs_2tap_ssse3( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [1] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_cvtsi32_si128(loadu_int32(filter + 3)); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - - // coeffs 3 4 3 4 3 4 3 4 - *coeffs = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0200u)); -} - -static inline void prepare_half_coeffs_4tap_ssse3( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [2] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - - // coeffs 2 3 2 3 2 3 2 3 - coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0604u)); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[1] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0a08u)); -} - -static inline void prepare_half_coeffs_6tap_ssse3( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [3] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - - // coeffs 1 2 1 2 1 2 1 2 - coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0402u)); - // coeffs 3 4 3 4 3 4 3 4 - coeffs[1] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0806u)); - // coeffs 5 6 5 6 5 6 5 6 - coeffs[2] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0C0Au)); -} - -static inline void prepare_half_coeffs_8tap_ssse3( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [4] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - - // coeffs 0 1 0 1 0 1 0 1 - coeffs[0] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0200u)); - // coeffs 2 3 2 3 2 3 2 3 - coeffs[1] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0604u)); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[2] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0a08u)); - // coeffs 6 7 6 7 6 7 6 7 - coeffs[3] = _mm_shuffle_epi8(coeffs_1, _mm_set1_epi16(0x0e0cu)); -} - -static inline void prepare_half_coeffs_2tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [1] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_cvtsi32_si128(loadu_int32(filter + 3)); - const __m256i filter_coeffs = _mm256_broadcastsi128_si256(coeffs_8); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - - const __m256i coeffs_1 = _mm256_srai_epi16(filter_coeffs, 1); - - // coeffs 3 4 3 4 3 4 3 4 - *coeffs = _mm256_shuffle_epi8(coeffs_1, _mm256_set1_epi16(0x0200u)); -} - -static inline void prepare_half_coeffs_4tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [2] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - populate_coeffs_4tap_avx2(coeffs_1, coeffs); -} - -static inline void prepare_half_coeffs_6tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [3] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - populate_coeffs_6tap_avx2(coeffs_1, coeffs); -} - -static inline void prepare_half_coeffs_8tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [4] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - - // right shift all filter co-efficients by 1 to reduce the bits required. - // This extra right shift will be taken care of at the end while rounding - // the result. - // Since all filter co-efficients are even, this change will not affect the - // end result - assert(_mm_test_all_zeros(_mm_and_si128(coeffs_8, _mm_set1_epi16(1)), - _mm_set1_epi16((short)0xffff))); - const __m128i coeffs_1 = _mm_srai_epi16(coeffs_8, 1); - populate_coeffs_8tap_avx2(coeffs_1, coeffs); -} - -static inline void prepare_coeffs_2tap_sse2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [1] */) { - const int16_t *filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - - const __m128i coeff = _mm_cvtsi32_si128(loadu_int32(filter + 3)); - - // coeffs 3 4 3 4 3 4 3 4 - coeffs[0] = _mm_shuffle_epi32(coeff, 0x00); -} - -static inline void prepare_coeffs_4tap_sse2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [2] */) { - const int16_t *filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - - const __m128i coeff = _mm_loadu_si128((__m128i *)filter); - - // coeffs 2 3 2 3 2 3 2 3 - coeffs[0] = _mm_shuffle_epi32(coeff, 0x55); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[1] = _mm_shuffle_epi32(coeff, 0xaa); -} - -static inline void prepare_coeffs_6tap_ssse3( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [3] */) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeff = _mm_loadu_si128((__m128i *)filter); - - // coeffs 1 2 1 2 1 2 1 2 - coeffs[0] = _mm_shuffle_epi8(coeff, _mm_set1_epi32(0x05040302u)); - // coeffs 3 4 3 4 3 4 3 4 - coeffs[1] = _mm_shuffle_epi8(coeff, _mm_set1_epi32(0x09080706u)); - // coeffs 5 6 5 6 5 6 5 6 - coeffs[2] = _mm_shuffle_epi8(coeff, _mm_set1_epi32(0x0D0C0B0Au)); -} - -static inline void prepare_coeffs_8tap_sse2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m128i *const coeffs /* [4] */) { - const int16_t *filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - - const __m128i coeff = _mm_loadu_si128((__m128i *)filter); - - // coeffs 0 1 0 1 0 1 0 1 - coeffs[0] = _mm_shuffle_epi32(coeff, 0x00); - // coeffs 2 3 2 3 2 3 2 3 - coeffs[1] = _mm_shuffle_epi32(coeff, 0x55); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[2] = _mm_shuffle_epi32(coeff, 0xaa); - // coeffs 6 7 6 7 6 7 6 7 - coeffs[3] = _mm_shuffle_epi32(coeff, 0xff); -} - -static inline void prepare_coeffs_2tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [1] */) { - const int16_t *filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - - const __m128i coeff_8 = _mm_cvtsi32_si128(loadu_int32(filter + 3)); - const __m256i coeff = _mm256_broadcastsi128_si256(coeff_8); - - // coeffs 3 4 3 4 3 4 3 4 - coeffs[0] = _mm256_shuffle_epi32(coeff, 0x00); -} - -static inline void prepare_coeffs_4tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [2] */) { - const int16_t *filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - - const __m128i coeff_8 = _mm_loadu_si128((__m128i *)filter); - const __m256i coeff = _mm256_broadcastsi128_si256(coeff_8); - - // coeffs 2 3 2 3 2 3 2 3 - coeffs[0] = _mm256_shuffle_epi32(coeff, 0x55); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[1] = _mm256_shuffle_epi32(coeff, 0xaa); -} - -static inline void prepare_coeffs_6tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [3]*/) { - const int16_t *const filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - const __m128i coeffs_8 = _mm_loadu_si128((__m128i *)filter); - const __m256i coeff = _mm256_broadcastsi128_si256(coeffs_8); - - // coeffs 1 2 1 2 1 2 1 2 - coeffs[0] = _mm256_shuffle_epi8(coeff, _mm256_set1_epi32(0x05040302u)); - // coeffs 3 4 3 4 3 4 3 4 - coeffs[1] = _mm256_shuffle_epi8(coeff, _mm256_set1_epi32(0x09080706u)); - // coeffs 5 6 5 6 5 6 5 6 - coeffs[2] = _mm256_shuffle_epi8(coeff, _mm256_set1_epi32(0x0D0C0B0Au)); -} - -static inline void prepare_coeffs_8tap_avx2( - const InterpFilterParams *const filter_params, const int32_t subpel_q4, - __m256i *const coeffs /* [4] */) { - const int16_t *filter = av1_get_interp_filter_subpel_kernel( - filter_params, subpel_q4 & SUBPEL_MASK); - - const __m128i coeff_8 = _mm_loadu_si128((__m128i *)filter); - const __m256i coeff = _mm256_broadcastsi128_si256(coeff_8); - - // coeffs 0 1 0 1 0 1 0 1 - coeffs[0] = _mm256_shuffle_epi32(coeff, 0x00); - // coeffs 2 3 2 3 2 3 2 3 - coeffs[1] = _mm256_shuffle_epi32(coeff, 0x55); - // coeffs 4 5 4 5 4 5 4 5 - coeffs[2] = _mm256_shuffle_epi32(coeff, 0xaa); - // coeffs 6 7 6 7 6 7 6 7 - coeffs[3] = _mm256_shuffle_epi32(coeff, 0xff); -} - -static inline void load_16bit_5rows_avx2(const int16_t *const src, - const ptrdiff_t stride, - __m256i dst[5]) { - dst[0] = _mm256_loadu_si256((__m256i *)(src + 0 * stride)); - dst[1] = _mm256_loadu_si256((__m256i *)(src + 1 * stride)); - dst[2] = _mm256_loadu_si256((__m256i *)(src + 2 * stride)); - dst[3] = _mm256_loadu_si256((__m256i *)(src + 3 * stride)); - dst[4] = _mm256_loadu_si256((__m256i *)(src + 4 * stride)); -} - -static inline void load_16bit_7rows_avx2(const int16_t *const src, - const ptrdiff_t stride, - __m256i dst[7]) { - dst[0] = _mm256_loadu_si256((__m256i *)(src + 0 * stride)); - dst[1] = _mm256_loadu_si256((__m256i *)(src + 1 * stride)); - dst[2] = _mm256_loadu_si256((__m256i *)(src + 2 * stride)); - dst[3] = _mm256_loadu_si256((__m256i *)(src + 3 * stride)); - dst[4] = _mm256_loadu_si256((__m256i *)(src + 4 * stride)); - dst[5] = _mm256_loadu_si256((__m256i *)(src + 5 * stride)); - dst[6] = _mm256_loadu_si256((__m256i *)(src + 6 * stride)); -} - -static AOM_FORCE_INLINE void load_16bit_8rows_avx2(const int16_t *const src, - const ptrdiff_t stride, - __m256i dst[8]) { - dst[0] = _mm256_loadu_si256((__m256i *)(src + 0 * stride)); - dst[1] = _mm256_loadu_si256((__m256i *)(src + 1 * stride)); - dst[2] = _mm256_loadu_si256((__m256i *)(src + 2 * stride)); - dst[3] = _mm256_loadu_si256((__m256i *)(src + 3 * stride)); - dst[4] = _mm256_loadu_si256((__m256i *)(src + 4 * stride)); - dst[5] = _mm256_loadu_si256((__m256i *)(src + 5 * stride)); - dst[6] = _mm256_loadu_si256((__m256i *)(src + 6 * stride)); - dst[7] = _mm256_loadu_si256((__m256i *)(src + 7 * stride)); -} - -static AOM_FORCE_INLINE void loadu_unpack_16bit_5rows_avx2( - const int16_t *const src, const ptrdiff_t stride, __m256i s_256[5], - __m256i ss_256[5], __m256i tt_256[5]) { - s_256[0] = _mm256_loadu_si256((__m256i *)(src + 0 * stride)); - s_256[1] = _mm256_loadu_si256((__m256i *)(src + 1 * stride)); - s_256[2] = _mm256_loadu_si256((__m256i *)(src + 2 * stride)); - s_256[3] = _mm256_loadu_si256((__m256i *)(src + 3 * stride)); - s_256[4] = _mm256_loadu_si256((__m256i *)(src + 4 * stride)); - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - ss_256[4] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - - tt_256[0] = _mm256_unpacklo_epi16(s_256[1], s_256[2]); - tt_256[1] = _mm256_unpacklo_epi16(s_256[3], s_256[4]); - tt_256[3] = _mm256_unpackhi_epi16(s_256[1], s_256[2]); - tt_256[4] = _mm256_unpackhi_epi16(s_256[3], s_256[4]); -} - -static AOM_FORCE_INLINE void loadu_unpack_16bit_3rows_avx2( - const int16_t *const src, const ptrdiff_t stride, __m256i s_256[3], - __m256i ss_256[3], __m256i tt_256[3]) { - s_256[0] = _mm256_loadu_si256((__m256i *)(src + 0 * stride)); - s_256[1] = _mm256_loadu_si256((__m256i *)(src + 1 * stride)); - s_256[2] = _mm256_loadu_si256((__m256i *)(src + 2 * stride)); - - ss_256[0] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[2] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - - tt_256[0] = _mm256_unpacklo_epi16(s_256[1], s_256[2]); - tt_256[2] = _mm256_unpackhi_epi16(s_256[1], s_256[2]); -} - -static inline void convolve_8tap_unpack_avx2(const __m256i s[6], - __m256i ss[7]) { - ss[0] = _mm256_unpacklo_epi16(s[0], s[1]); - ss[1] = _mm256_unpacklo_epi16(s[2], s[3]); - ss[2] = _mm256_unpacklo_epi16(s[4], s[5]); - ss[4] = _mm256_unpackhi_epi16(s[0], s[1]); - ss[5] = _mm256_unpackhi_epi16(s[2], s[3]); - ss[6] = _mm256_unpackhi_epi16(s[4], s[5]); -} - -static inline __m128i convolve_2tap_ssse3(const __m128i ss[1], - const __m128i coeffs[1]) { - return _mm_maddubs_epi16(ss[0], coeffs[0]); -} - -static inline __m128i convolve_4tap_ssse3(const __m128i ss[2], - const __m128i coeffs[2]) { - const __m128i res_23 = _mm_maddubs_epi16(ss[0], coeffs[0]); - const __m128i res_45 = _mm_maddubs_epi16(ss[1], coeffs[1]); - return _mm_add_epi16(res_23, res_45); -} - -static inline __m128i convolve_6tap_ssse3(const __m128i ss[3], - const __m128i coeffs[3]) { - const __m128i res_12 = _mm_maddubs_epi16(ss[0], coeffs[0]); - const __m128i res_34 = _mm_maddubs_epi16(ss[1], coeffs[1]); - const __m128i res_56 = _mm_maddubs_epi16(ss[2], coeffs[2]); - const __m128i res_1256 = _mm_add_epi16(res_12, res_56); - return _mm_add_epi16(res_1256, res_34); -} - -static inline __m128i convolve_8tap_ssse3(const __m128i ss[4], - const __m128i coeffs[4]) { - const __m128i res_01 = _mm_maddubs_epi16(ss[0], coeffs[0]); - const __m128i res_23 = _mm_maddubs_epi16(ss[1], coeffs[1]); - const __m128i res_45 = _mm_maddubs_epi16(ss[2], coeffs[2]); - const __m128i res_67 = _mm_maddubs_epi16(ss[3], coeffs[3]); - const __m128i res_0145 = _mm_add_epi16(res_01, res_45); - const __m128i res_2367 = _mm_add_epi16(res_23, res_67); - return _mm_add_epi16(res_0145, res_2367); -} - -static inline __m256i convolve_2tap_avx2(const __m256i ss[1], - const __m256i coeffs[1]) { - return _mm256_maddubs_epi16(ss[0], coeffs[0]); -} - -static inline __m256i convolve_4tap_avx2(const __m256i ss[2], - const __m256i coeffs[2]) { - const __m256i res_23 = _mm256_maddubs_epi16(ss[0], coeffs[0]); - const __m256i res_45 = _mm256_maddubs_epi16(ss[1], coeffs[1]); - return _mm256_add_epi16(res_23, res_45); -} - -static inline __m256i convolve_6tap_avx2(const __m256i ss[3], - const __m256i coeffs[3]) { - const __m256i res_01 = _mm256_maddubs_epi16(ss[0], coeffs[0]); - const __m256i res_23 = _mm256_maddubs_epi16(ss[1], coeffs[1]); - const __m256i res_45 = _mm256_maddubs_epi16(ss[2], coeffs[2]); - const __m256i res_0145 = _mm256_add_epi16(res_01, res_45); - return _mm256_add_epi16(res_0145, res_23); -} - -static inline __m256i convolve_8tap_avx2(const __m256i ss[4], - const __m256i coeffs[4]) { - const __m256i res_01 = _mm256_maddubs_epi16(ss[0], coeffs[0]); - const __m256i res_23 = _mm256_maddubs_epi16(ss[1], coeffs[1]); - const __m256i res_45 = _mm256_maddubs_epi16(ss[2], coeffs[2]); - const __m256i res_67 = _mm256_maddubs_epi16(ss[3], coeffs[3]); - const __m256i res_0145 = _mm256_add_epi16(res_01, res_45); - const __m256i res_2367 = _mm256_add_epi16(res_23, res_67); - return _mm256_add_epi16(res_0145, res_2367); -} - -static inline __m128i convolve16_2tap_sse2(const __m128i ss[1], - const __m128i coeffs[1]) { - return _mm_madd_epi16(ss[0], coeffs[0]); -} - -static inline __m128i convolve16_4tap_sse2(const __m128i ss[2], - const __m128i coeffs[2]) { - const __m128i res_01 = _mm_madd_epi16(ss[0], coeffs[0]); - const __m128i res_23 = _mm_madd_epi16(ss[1], coeffs[1]); - return _mm_add_epi32(res_01, res_23); -} - -static inline __m128i convolve16_6tap_sse2(const __m128i ss[3], - const __m128i coeffs[3]) { - const __m128i res_01 = _mm_madd_epi16(ss[0], coeffs[0]); - const __m128i res_23 = _mm_madd_epi16(ss[1], coeffs[1]); - const __m128i res_45 = _mm_madd_epi16(ss[2], coeffs[2]); - const __m128i res_0123 = _mm_add_epi32(res_01, res_23); - return _mm_add_epi32(res_0123, res_45); -} - -static inline __m128i convolve16_8tap_sse2(const __m128i ss[4], - const __m128i coeffs[4]) { - const __m128i res_01 = _mm_madd_epi16(ss[0], coeffs[0]); - const __m128i res_23 = _mm_madd_epi16(ss[1], coeffs[1]); - const __m128i res_45 = _mm_madd_epi16(ss[2], coeffs[2]); - const __m128i res_67 = _mm_madd_epi16(ss[3], coeffs[3]); - const __m128i res_0123 = _mm_add_epi32(res_01, res_23); - const __m128i res_4567 = _mm_add_epi32(res_45, res_67); - return _mm_add_epi32(res_0123, res_4567); -} - -static inline __m256i convolve16_2tap_avx2(const __m256i ss[1], - const __m256i coeffs[1]) { - return _mm256_madd_epi16(ss[0], coeffs[0]); -} - -static inline __m256i convolve16_4tap_avx2(const __m256i ss[2], - const __m256i coeffs[2]) { - const __m256i res_1 = _mm256_madd_epi16(ss[0], coeffs[0]); - const __m256i res_2 = _mm256_madd_epi16(ss[1], coeffs[1]); - return _mm256_add_epi32(res_1, res_2); -} - -static inline __m256i convolve16_6tap_avx2(const __m256i ss[3], - const __m256i coeffs[3]) { - const __m256i res_01 = _mm256_madd_epi16(ss[0], coeffs[0]); - const __m256i res_23 = _mm256_madd_epi16(ss[1], coeffs[1]); - const __m256i res_45 = _mm256_madd_epi16(ss[2], coeffs[2]); - const __m256i res_0123 = _mm256_add_epi32(res_01, res_23); - return _mm256_add_epi32(res_0123, res_45); -} - -static inline __m256i convolve16_8tap_avx2(const __m256i ss[4], - const __m256i coeffs[4]) { - const __m256i res_01 = _mm256_madd_epi16(ss[0], coeffs[0]); - const __m256i res_23 = _mm256_madd_epi16(ss[1], coeffs[1]); - const __m256i res_45 = _mm256_madd_epi16(ss[2], coeffs[2]); - const __m256i res_67 = _mm256_madd_epi16(ss[3], coeffs[3]); - const __m256i res_0123 = _mm256_add_epi32(res_01, res_23); - const __m256i res_4567 = _mm256_add_epi32(res_45, res_67); - return _mm256_add_epi32(res_0123, res_4567); -} - -static inline __m256i x_convolve_4tap_avx2(const __m256i data, - const __m256i coeffs[2], - const __m256i filt[2]) { - __m256i ss[2]; - - ss[0] = _mm256_shuffle_epi8(data, filt[0]); - ss[1] = _mm256_shuffle_epi8(data, filt[1]); - - return convolve_4tap_avx2(ss, coeffs); -} - -static inline __m256i x_convolve_6tap_avx2(const __m256i data, - const __m256i coeffs[3], - const __m256i filt[3]) { - __m256i ss[3]; - - ss[0] = _mm256_shuffle_epi8(data, filt[0]); - ss[1] = _mm256_shuffle_epi8(data, filt[1]); - ss[2] = _mm256_shuffle_epi8(data, filt[2]); - - return convolve_6tap_avx2(ss, coeffs); -} - -static inline __m256i x_convolve_8tap_avx2(const __m256i data, - const __m256i coeffs[4], - const __m256i filt[4]) { - __m256i ss[4]; - - ss[0] = _mm256_shuffle_epi8(data, filt[0]); - ss[1] = _mm256_shuffle_epi8(data, filt[1]); - ss[2] = _mm256_shuffle_epi8(data, filt[2]); - ss[3] = _mm256_shuffle_epi8(data, filt[3]); - - return convolve_8tap_avx2(ss, coeffs); -} - -static inline __m256i sr_y_round_avx2(const __m256i src) { - const __m256i round = _mm256_set1_epi16(32); - const __m256i dst = _mm256_add_epi16(src, round); - return _mm256_srai_epi16(dst, FILTER_BITS - 1); -} - -static inline __m128i xy_x_round_sse2(const __m128i src) { - const __m128i round = _mm_set1_epi16(2); - const __m128i dst = _mm_add_epi16(src, round); - return _mm_srai_epi16(dst, 2); -} - -static inline __m256i xy_x_round_avx2(const __m256i src) { - const __m256i round = _mm256_set1_epi16(2); - const __m256i dst = _mm256_add_epi16(src, round); - return _mm256_srai_epi16(dst, 2); -} - -static inline void xy_x_round_store_2x2_sse2(const __m128i res, - int16_t *const dst) { - const __m128i d = xy_x_round_sse2(res); - _mm_storel_epi64((__m128i *)dst, d); -} - -static inline void xy_x_round_store_4x2_sse2(const __m128i res, - int16_t *const dst) { - const __m128i d = xy_x_round_sse2(res); - _mm_storeu_si128((__m128i *)dst, d); -} - -static inline void xy_x_round_store_8x2_sse2(const __m128i res[2], - int16_t *const dst) { - __m128i r[2]; - - r[0] = xy_x_round_sse2(res[0]); - r[1] = xy_x_round_sse2(res[1]); - _mm_storeu_si128((__m128i *)dst, r[0]); - _mm_storeu_si128((__m128i *)(dst + 8), r[1]); -} - -static inline void xy_x_round_store_8x2_avx2(const __m256i res, - int16_t *const dst) { - const __m256i d = xy_x_round_avx2(res); - _mm256_storeu_si256((__m256i *)dst, d); -} - -static inline void xy_x_round_store_32_avx2(const __m256i res[2], - int16_t *const dst) { - __m256i r[2]; - - r[0] = xy_x_round_avx2(res[0]); - r[1] = xy_x_round_avx2(res[1]); - const __m256i d0 = - _mm256_inserti128_si256(r[0], _mm256_castsi256_si128(r[1]), 1); - const __m256i d1 = - _mm256_inserti128_si256(r[1], _mm256_extracti128_si256(r[0], 1), 0); - _mm256_storeu_si256((__m256i *)dst, d0); - _mm256_storeu_si256((__m256i *)(dst + 16), d1); -} - -static inline __m128i xy_y_round_sse2(const __m128i src) { - const __m128i round = _mm_set1_epi32(1024); - const __m128i dst = _mm_add_epi32(src, round); - return _mm_srai_epi32(dst, 11); -} - -static inline __m128i xy_y_round_half_pel_sse2(const __m128i src) { - const __m128i round = _mm_set1_epi16(16); - const __m128i dst = _mm_add_epi16(src, round); - return _mm_srai_epi16(dst, 5); -} - -static inline __m256i xy_y_round_avx2(const __m256i src) { - const __m256i round = _mm256_set1_epi32(1024); - const __m256i dst = _mm256_add_epi32(src, round); - return _mm256_srai_epi32(dst, 11); -} - -static inline __m256i xy_y_round_16_avx2(const __m256i r[2]) { - const __m256i r0 = xy_y_round_avx2(r[0]); - const __m256i r1 = xy_y_round_avx2(r[1]); - return _mm256_packs_epi32(r0, r1); -} - -static inline __m256i xy_y_round_half_pel_avx2(const __m256i src) { - const __m256i round = _mm256_set1_epi16(16); - const __m256i dst = _mm256_add_epi16(src, round); - return _mm256_srai_epi16(dst, 5); -} - -static inline void pack_store_2x2_sse2(const __m128i res, uint8_t *const dst, - const ptrdiff_t stride) { - const __m128i d = _mm_packus_epi16(res, res); - *(int16_t *)dst = (int16_t)_mm_cvtsi128_si32(d); - *(int16_t *)(dst + stride) = (int16_t)_mm_extract_epi16(d, 1); -} - -static inline void pack_store_4x2_sse2(const __m128i res, uint8_t *const dst, - const ptrdiff_t stride) { - const __m128i d = _mm_packus_epi16(res, res); - store_u8_4x2_sse2(d, dst, stride); -} - -static inline void pack_store_4x2_avx2(const __m256i res, uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i d = _mm256_packus_epi16(res, res); - const __m128i d0 = _mm256_castsi256_si128(d); - const __m128i d1 = _mm256_extracti128_si256(d, 1); - - xx_storel_32(dst, d0); - xx_storel_32(dst + stride, d1); -} - -static inline void pack_store_8x2_avx2(const __m256i res, uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i d = _mm256_packus_epi16(res, res); - const __m128i d0 = _mm256_castsi256_si128(d); - const __m128i d1 = _mm256_extracti128_si256(d, 1); - _mm_storel_epi64((__m128i *)dst, d0); - _mm_storel_epi64((__m128i *)(dst + stride), d1); -} - -static inline void pack_store_16x2_avx2(const __m256i res0, const __m256i res1, - uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i d = _mm256_packus_epi16(res0, res1); - storeu_u8_16x2_avx2(d, dst, stride); -} - -static inline void xy_y_pack_store_16x2_avx2(const __m256i res0, - const __m256i res1, - uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i t = _mm256_packus_epi16(res0, res1); - const __m256i d = _mm256_permute4x64_epi64(t, 0xD8); - storeu_u8_16x2_avx2(d, dst, stride); -} - -static inline void pack_store_32_avx2(const __m256i res0, const __m256i res1, - uint8_t *const dst) { - const __m256i t = _mm256_packus_epi16(res0, res1); - const __m256i d = _mm256_permute4x64_epi64(t, 0xD8); - _mm256_storeu_si256((__m256i *)dst, d); -} - -static inline void xy_y_round_store_2x2_sse2(const __m128i res, - uint8_t *const dst, - const ptrdiff_t stride) { - const __m128i r = xy_y_round_sse2(res); - const __m128i rr = _mm_packs_epi32(r, r); - pack_store_2x2_sse2(rr, dst, stride); -} - -static inline void xy_y_round_store_4x2_avx2(const __m256i res, - uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i r = xy_y_round_avx2(res); - const __m256i rr = _mm256_packs_epi32(r, r); - pack_store_4x2_avx2(rr, dst, stride); -} - -static inline void xy_y_pack_store_32_avx2(const __m256i res0, - const __m256i res1, - uint8_t *const dst) { - const __m256i d = _mm256_packus_epi16(res0, res1); - // d = _mm256_permute4x64_epi64(d, 0xD8); - _mm256_storeu_si256((__m256i *)dst, d); -} - -static inline void xy_y_round_store_32_avx2(const __m256i r0[2], - const __m256i r1[2], - uint8_t *const dst) { - const __m256i ra = xy_y_round_16_avx2(r0); - const __m256i rb = xy_y_round_16_avx2(r1); - xy_y_pack_store_32_avx2(ra, rb, dst); -} - -static inline void convolve_store_32_avx2(const __m256i res0, - const __m256i res1, - uint8_t *const dst) { - const __m256i d = _mm256_packus_epi16(res0, res1); - _mm256_storeu_si256((__m256i *)dst, d); -} - -static inline __m128i sr_x_round_sse2(const __m128i src) { - const __m128i round = _mm_set1_epi16(34); - const __m128i dst = _mm_add_epi16(src, round); - return _mm_srai_epi16(dst, 6); -} - -static inline __m256i sr_x_round_avx2(const __m256i src) { - const __m256i round = _mm256_set1_epi16(34); - const __m256i dst = _mm256_add_epi16(src, round); - return _mm256_srai_epi16(dst, 6); -} - -static inline __m128i sr_y_round_sse2(const __m128i src) { - const __m128i round = _mm_set1_epi16(32); - const __m128i dst = _mm_add_epi16(src, round); - return _mm_srai_epi16(dst, FILTER_BITS - 1); -} - -static inline void sr_x_round_store_8x2_avx2(const __m256i res, - uint8_t *const dst, - const ptrdiff_t dst_stride) { - const __m256i r = sr_x_round_avx2(res); - pack_store_8x2_avx2(r, dst, dst_stride); -} - -static inline void sr_x_round_store_16x2_avx2(const __m256i res[2], - uint8_t *const dst, - const ptrdiff_t dst_stride) { - __m256i r[2]; - - r[0] = sr_x_round_avx2(res[0]); - r[1] = sr_x_round_avx2(res[1]); - pack_store_16x2_avx2(r[0], r[1], dst, dst_stride); -} - -static inline void sr_x_round_store_32_avx2(const __m256i res[2], - uint8_t *const dst) { - __m256i r[2]; - - r[0] = sr_x_round_avx2(res[0]); - r[1] = sr_x_round_avx2(res[1]); - convolve_store_32_avx2(r[0], r[1], dst); -} - -static inline void sr_y_round_store_8x2_avx2(const __m256i res, - uint8_t *const dst, - const ptrdiff_t dst_stride) { - const __m256i r = sr_y_round_avx2(res); - pack_store_8x2_avx2(r, dst, dst_stride); -} - -static inline void sr_y_round_store_16x2_avx2(const __m256i res[2], - uint8_t *const dst, - const ptrdiff_t dst_stride) { - __m256i r[2]; - - r[0] = sr_y_round_avx2(res[0]); - r[1] = sr_y_round_avx2(res[1]); - pack_store_16x2_avx2(r[0], r[1], dst, dst_stride); -} - -static inline void sr_y_2tap_32_avg_avx2(const uint8_t *const src, - const __m256i s0, __m256i *const s1, - uint8_t *const dst) { - *s1 = _mm256_loadu_si256((__m256i *)src); - const __m256i d = _mm256_avg_epu8(s0, *s1); - _mm256_storeu_si256((__m256i *)dst, d); -} - -static inline void sr_x_2tap_32_avg_avx2(const uint8_t *const src, - uint8_t *const dst) { - const __m256i s0 = _mm256_loadu_si256((__m256i *)src); - const __m256i s1 = _mm256_loadu_si256((__m256i *)(src + 1)); - const __m256i d = _mm256_avg_epu8(s0, s1); - _mm256_storeu_si256((__m256i *)dst, d); -} - -static inline __m128i x_convolve_2tap_2x2_sse4_1(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[1]) { - const __m128i sfl = - _mm_setr_epi8(0, 1, 1, 2, 4, 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i s_128 = load_u8_4x2_sse4_1(src, stride); - const __m128i ss = _mm_shuffle_epi8(s_128, sfl); - return convolve_2tap_ssse3(&ss, coeffs); -} - -static inline __m128i x_convolve_2tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[1]) { - const __m128i sfl = - _mm_setr_epi8(0, 1, 1, 2, 2, 3, 3, 4, 8, 9, 9, 10, 10, 11, 11, 12); - const __m128i s_128 = load_u8_8x2_sse2(src, stride); - const __m128i ss = _mm_shuffle_epi8(s_128, sfl); - return convolve_2tap_ssse3(&ss, coeffs); -} - -static inline void x_convolve_2tap_8x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[1], - __m128i r[2]) { - __m128i ss[2]; - const __m128i s00 = _mm_loadu_si128((__m128i *)src); - const __m128i s10 = _mm_loadu_si128((__m128i *)(src + stride)); - const __m128i s01 = _mm_srli_si128(s00, 1); - const __m128i s11 = _mm_srli_si128(s10, 1); - ss[0] = _mm_unpacklo_epi8(s00, s01); - ss[1] = _mm_unpacklo_epi8(s10, s11); - - r[0] = convolve_2tap_ssse3(&ss[0], coeffs); - r[1] = convolve_2tap_ssse3(&ss[1], coeffs); -} - -static inline __m256i x_convolve_2tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[1]) { - __m128i s_128[2][2]; - __m256i s_256[2]; - - s_128[0][0] = _mm_loadu_si128((__m128i *)src); - s_128[1][0] = _mm_loadu_si128((__m128i *)(src + stride)); - s_128[0][1] = _mm_srli_si128(s_128[0][0], 1); - s_128[1][1] = _mm_srli_si128(s_128[1][0], 1); - s_256[0] = _mm256_setr_m128i(s_128[0][0], s_128[1][0]); - s_256[1] = _mm256_setr_m128i(s_128[0][1], s_128[1][1]); - const __m256i ss = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - return convolve_2tap_avx2(&ss, coeffs); -} - -static inline void x_convolve_2tap_16x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[1], - __m256i r[2]) { - const __m256i s0_256 = loadu_8bit_16x2_avx2(src, stride); - const __m256i s1_256 = loadu_8bit_16x2_avx2(src + 1, stride); - const __m256i s0 = _mm256_unpacklo_epi8(s0_256, s1_256); - const __m256i s1 = _mm256_unpackhi_epi8(s0_256, s1_256); - r[0] = convolve_2tap_avx2(&s0, coeffs); - r[1] = convolve_2tap_avx2(&s1, coeffs); -} - -static inline void x_convolve_2tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[1], - __m256i r[2]) { - const __m256i s0 = _mm256_loadu_si256((__m256i *)src); - const __m256i s1 = _mm256_loadu_si256((__m256i *)(src + 1)); - const __m256i ss0 = _mm256_unpacklo_epi8(s0, s1); - const __m256i ss1 = _mm256_unpackhi_epi8(s0, s1); - - r[0] = convolve_2tap_avx2(&ss0, coeffs); - r[1] = convolve_2tap_avx2(&ss1, coeffs); -} - -static inline __m128i x_convolve_4tap_2x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[2]) { - const __m128i sfl0 = - _mm_setr_epi8(0, 1, 1, 2, 8, 9, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i sfl1 = - _mm_setr_epi8(2, 3, 3, 4, 10, 11, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i s = load_u8_8x2_sse2(src, stride); - __m128i ss[2]; - - ss[0] = _mm_shuffle_epi8(s, sfl0); - ss[1] = _mm_shuffle_epi8(s, sfl1); - return convolve_4tap_ssse3(ss, coeffs); -} - -static inline __m128i x_convolve_4tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[2]) { - const __m128i s = load_u8_8x2_sse2(src, stride); - const __m128i sfl0 = - _mm_setr_epi8(0, 1, 1, 2, 2, 3, 3, 4, 8, 9, 9, 10, 10, 11, 11, 12); - const __m128i sfl1 = - _mm_setr_epi8(2, 3, 3, 4, 4, 5, 5, 6, 10, 11, 11, 12, 12, 13, 13, 14); - __m128i ss[2]; - - ss[0] = _mm_shuffle_epi8(s, sfl0); - ss[1] = _mm_shuffle_epi8(s, sfl1); - return convolve_4tap_ssse3(ss, coeffs); -} - -static inline __m256i x_convolve_4tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[2], - const __m256i filt[2]) { - const __m256i s_256 = loadu_8bit_16x2_avx2(src, stride); - return x_convolve_4tap_avx2(s_256, coeffs, filt); -} - -static inline void x_convolve_4tap_16x2_avx2(const uint8_t *const src, - const int32_t src_stride, - const __m256i coeffs[2], - const __m256i filt[2], - __m256i r[2]) { - r[0] = x_convolve_4tap_8x2_avx2(src + 0, src_stride, coeffs, filt); - r[1] = x_convolve_4tap_8x2_avx2(src + 8, src_stride, coeffs, filt); -} - -static inline void x_convolve_4tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[2], - const __m256i filt[2], - __m256i r[2]) { - const __m256i s0_256 = _mm256_loadu_si256((__m256i *)src); - const __m256i s1_256 = _mm256_loadu_si256((__m256i *)(src + 8)); - - r[0] = x_convolve_4tap_avx2(s0_256, coeffs, filt); - r[1] = x_convolve_4tap_avx2(s1_256, coeffs, filt); -} - -static inline __m128i x_convolve_6tap_2x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[3]) { - const __m128i sfl0 = - _mm_setr_epi8(0, 1, 1, 2, 8, 9, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i sfl1 = - _mm_setr_epi8(2, 3, 3, 4, 10, 11, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i sfl2 = - _mm_setr_epi8(4, 5, 5, 6, 12, 13, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0); - - const __m128i s = load_u8_8x2_sse2(src, stride); - __m128i ss[3]; - - ss[0] = _mm_shuffle_epi8(s, sfl0); - ss[1] = _mm_shuffle_epi8(s, sfl1); - ss[2] = _mm_shuffle_epi8(s, sfl2); - return convolve_6tap_ssse3(ss, coeffs); -} - -static inline __m128i x_convolve_6tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[3]) { - const __m128i s = load_u8_8x2_sse2(src, stride); - const __m128i sfl0 = - _mm_setr_epi8(0, 1, 1, 2, 8, 9, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i sfl1 = - _mm_setr_epi8(2, 3, 3, 4, 10, 11, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i sfl2 = - _mm_setr_epi8(4, 5, 5, 6, 12, 13, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0); - __m128i ss[3]; - - ss[0] = _mm_shuffle_epi8(s, sfl0); - ss[1] = _mm_shuffle_epi8(s, sfl1); - ss[2] = _mm_shuffle_epi8(s, sfl2); - return convolve_6tap_ssse3(ss, coeffs); -} - -static inline __m256i x_convolve_6tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[3], - const __m256i filt[3]) { - const __m256i s_256 = loadu_8bit_16x2_avx2(src, stride); - return x_convolve_6tap_avx2(s_256, coeffs, filt); -} - -static inline void x_convolve_6tap_16x2_avx2(const uint8_t *const src, - const int32_t src_stride, - const __m256i coeffs[3], - const __m256i filt[3], - __m256i r[2]) { - r[0] = x_convolve_6tap_8x2_avx2(src + 0, src_stride, coeffs, filt); - r[1] = x_convolve_6tap_8x2_avx2(src + 8, src_stride, coeffs, filt); -} - -static inline void x_convolve_6tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[3], - const __m256i filt[3], - __m256i r[2]) { - const __m256i s0_256 = _mm256_loadu_si256((__m256i *)src); - const __m256i s1_256 = _mm256_loadu_si256((__m256i *)(src + 8)); - - r[0] = x_convolve_6tap_avx2(s0_256, coeffs, filt); - r[1] = x_convolve_6tap_avx2(s1_256, coeffs, filt); -} - -static inline __m256i x_convolve_8tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[4], - const __m256i filt[4]) { - const __m256i s_256 = loadu_8bit_16x2_avx2(src, stride); - return x_convolve_8tap_avx2(s_256, coeffs, filt); -} - -static AOM_FORCE_INLINE void x_convolve_8tap_16x2_avx2(const uint8_t *const src, - const int32_t src_stride, - const __m256i coeffs[4], - const __m256i filt[4], - __m256i r[2]) { - r[0] = x_convolve_8tap_8x2_avx2(src + 0, src_stride, coeffs, filt); - r[1] = x_convolve_8tap_8x2_avx2(src + 8, src_stride, coeffs, filt); -} - -static AOM_FORCE_INLINE void x_convolve_8tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[4], - const __m256i filt[4], - __m256i r[2]) { - const __m256i s0_256 = _mm256_loadu_si256((__m256i *)src); - const __m256i s1_256 = _mm256_loadu_si256((__m256i *)(src + 8)); - - r[0] = x_convolve_8tap_avx2(s0_256, coeffs, filt); - r[1] = x_convolve_8tap_avx2(s1_256, coeffs, filt); -} - -static inline __m128i y_convolve_2tap_2x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[1], - __m128i s_16[2]) { - __m128i s_128[2]; - - s_16[1] = _mm_cvtsi32_si128(*(int16_t *)(src + stride)); - s_128[0] = _mm_unpacklo_epi16(s_16[0], s_16[1]); - s_16[0] = _mm_cvtsi32_si128(*(int16_t *)(src + 2 * stride)); - s_128[1] = _mm_unpacklo_epi16(s_16[1], s_16[0]); - const __m128i ss = _mm_unpacklo_epi8(s_128[0], s_128[1]); - return convolve_2tap_ssse3(&ss, coeffs); -} - -static inline __m128i y_convolve_2tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[1], - __m128i s_32[2]) { - __m128i s_128[2]; - - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src + stride)); - s_128[0] = _mm_unpacklo_epi32(s_32[0], s_32[1]); - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src + 2 * stride)); - s_128[1] = _mm_unpacklo_epi32(s_32[1], s_32[0]); - const __m128i ss = _mm_unpacklo_epi8(s_128[0], s_128[1]); - return convolve_2tap_ssse3(&ss, coeffs); -} - -static inline __m256i y_convolve_2tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[1], - __m128i s_64[2]) { - __m256i s_256[2]; - - s_64[1] = _mm_loadl_epi64((__m128i *)(src + stride)); - s_256[0] = _mm256_setr_m128i(s_64[0], s_64[1]); - s_64[0] = _mm_loadl_epi64((__m128i *)(src + 2 * stride)); - s_256[1] = _mm256_setr_m128i(s_64[1], s_64[0]); - const __m256i ss = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - return convolve_2tap_avx2(&ss, coeffs); -} - -static inline void y_convolve_2tap_16x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[1], - __m128i s_128[2], __m256i r[2]) { - __m256i s_256[2]; - - s_128[1] = _mm_loadu_si128((__m128i *)(src + stride)); - s_256[0] = _mm256_setr_m128i(s_128[0], s_128[1]); - s_128[0] = _mm_loadu_si128((__m128i *)(src + 2 * stride)); - s_256[1] = _mm256_setr_m128i(s_128[1], s_128[0]); - const __m256i ss0 = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - const __m256i ss1 = _mm256_unpackhi_epi8(s_256[0], s_256[1]); - r[0] = convolve_2tap_avx2(&ss0, coeffs); - r[1] = convolve_2tap_avx2(&ss1, coeffs); -} - -static inline void y_convolve_2tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[1], - const __m256i s0, __m256i *const s1, - __m256i r[2]) { - *s1 = _mm256_loadu_si256((__m256i *)src); - const __m256i ss0 = _mm256_unpacklo_epi8(s0, *s1); - const __m256i ss1 = _mm256_unpackhi_epi8(s0, *s1); - r[0] = convolve_2tap_avx2(&ss0, coeffs); - r[1] = convolve_2tap_avx2(&ss1, coeffs); -} - -static inline __m128i y_convolve_4tap_2x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[2], - __m128i s_16[4], - __m128i ss_128[2]) { - s_16[3] = _mm_cvtsi32_si128(loadu_int16(src + stride)); - const __m128i src23 = _mm_unpacklo_epi16(s_16[2], s_16[3]); - s_16[2] = _mm_cvtsi32_si128(loadu_int16(src + 2 * stride)); - const __m128i src34 = _mm_unpacklo_epi16(s_16[3], s_16[2]); - ss_128[1] = _mm_unpacklo_epi8(src23, src34); - return convolve_4tap_ssse3(ss_128, coeffs); -} - -static inline __m128i y_convolve_4tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[2], - __m128i s_32[4], - __m128i ss_128[2]) { - s_32[3] = _mm_cvtsi32_si128(loadu_int32(src + stride)); - const __m128i src23 = _mm_unpacklo_epi32(s_32[2], s_32[3]); - s_32[2] = _mm_cvtsi32_si128(loadu_int32(src + 2 * stride)); - const __m128i src34 = _mm_unpacklo_epi32(s_32[3], s_32[2]); - ss_128[1] = _mm_unpacklo_epi8(src23, src34); - return convolve_4tap_ssse3(ss_128, coeffs); -} - -static inline __m256i y_convolve_4tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[2], - __m128i s_64[4], - __m256i ss_256[2]) { - s_64[3] = _mm_loadl_epi64((__m128i *)(src + stride)); - const __m256i src23 = _mm256_setr_m128i(s_64[2], s_64[3]); - s_64[2] = _mm_loadl_epi64((__m128i *)(src + 2 * stride)); - const __m256i src34 = _mm256_setr_m128i(s_64[3], s_64[2]); - ss_256[1] = _mm256_unpacklo_epi8(src23, src34); - return convolve_4tap_avx2(ss_256, coeffs); -} - -static inline void y_convolve_4tap_16x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[2], - __m128i s_128[4], - __m256i ss_256[4], __m256i r[2]) { - s_128[3] = _mm_loadu_si128((__m128i *)(src + stride)); - const __m256i src23 = _mm256_setr_m128i(s_128[2], s_128[3]); - s_128[2] = _mm_loadu_si128((__m128i *)(src + 2 * stride)); - const __m256i src34 = _mm256_setr_m128i(s_128[3], s_128[2]); - ss_256[1] = _mm256_unpacklo_epi8(src23, src34); - ss_256[3] = _mm256_unpackhi_epi8(src23, src34); - r[0] = convolve_4tap_avx2(ss_256, coeffs); - r[1] = convolve_4tap_avx2(ss_256 + 2, coeffs); -} - -static inline __m128i y_convolve_6tap_2x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[3], - __m128i s_16[6], - __m128i ss_128[3]) { - s_16[5] = _mm_cvtsi32_si128(loadu_int16(src + 3 * stride)); - const __m128i src45 = _mm_unpacklo_epi16(s_16[4], s_16[5]); - s_16[4] = _mm_cvtsi32_si128(loadu_int16(src + 4 * stride)); - const __m128i src56 = _mm_unpacklo_epi16(s_16[5], s_16[4]); - ss_128[2] = _mm_unpacklo_epi8(src45, src56); - return convolve_6tap_ssse3(ss_128, coeffs); -} - -static inline void y_convolve_4tap_32x2_avx2( - const uint8_t *const src, const ptrdiff_t stride, const __m256i coeffs[2], - __m256i s_256[4], __m256i ss_256[4], __m256i tt_256[4], __m256i r[4]) { - s_256[3] = _mm256_loadu_si256((__m256i *)(src + 1 * stride)); - ss_256[1] = _mm256_unpacklo_epi8(s_256[2], s_256[3]); - ss_256[3] = _mm256_unpackhi_epi8(s_256[2], s_256[3]); - s_256[2] = _mm256_loadu_si256((__m256i *)(src + 2 * stride)); - tt_256[1] = _mm256_unpacklo_epi8(s_256[3], s_256[2]); - tt_256[3] = _mm256_unpackhi_epi8(s_256[3], s_256[2]); - r[0] = convolve_4tap_avx2(ss_256 + 0, coeffs); - r[1] = convolve_4tap_avx2(ss_256 + 2, coeffs); - r[2] = convolve_4tap_avx2(tt_256 + 0, coeffs); - r[3] = convolve_4tap_avx2(tt_256 + 2, coeffs); -} - -static inline __m128i y_convolve_6tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[3], - __m128i s_32[6], - __m128i ss_128[3]) { - s_32[5] = _mm_cvtsi32_si128(loadu_int32(src + 3 * stride)); - const __m128i src45 = _mm_unpacklo_epi32(s_32[4], s_32[5]); - s_32[4] = _mm_cvtsi32_si128(loadu_int32(src + 4 * stride)); - const __m128i src56 = _mm_unpacklo_epi32(s_32[5], s_32[4]); - ss_128[2] = _mm_unpacklo_epi8(src45, src56); - return convolve_6tap_ssse3(ss_128, coeffs); -} - -static inline __m256i y_convolve_6tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[3], - __m128i s_64[6], - __m256i ss_256[3]) { - s_64[5] = _mm_loadl_epi64((__m128i *)(src + 3 * stride)); - const __m256i src45 = _mm256_setr_m128i(s_64[4], s_64[5]); - s_64[4] = _mm_loadl_epi64((__m128i *)(src + 4 * stride)); - const __m256i src56 = _mm256_setr_m128i(s_64[5], s_64[4]); - ss_256[2] = _mm256_unpacklo_epi8(src45, src56); - return convolve_6tap_avx2(ss_256, coeffs); -} - -static inline void y_convolve_6tap_16x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[3], - __m128i s_128[6], - __m256i ss_256[6], __m256i r[2]) { - s_128[5] = _mm_loadu_si128((__m128i *)(src + 3 * stride)); - const __m256i src45 = _mm256_setr_m128i(s_128[4], s_128[5]); - s_128[4] = _mm_loadu_si128((__m128i *)(src + 4 * stride)); - const __m256i src56 = _mm256_setr_m128i(s_128[5], s_128[4]); - ss_256[2] = _mm256_unpacklo_epi8(src45, src56); - ss_256[5] = _mm256_unpackhi_epi8(src45, src56); - r[0] = convolve_6tap_avx2(ss_256, coeffs); - r[1] = convolve_6tap_avx2(ss_256 + 3, coeffs); -} - -static inline void y_convolve_6tap_32x2_avx2( - const uint8_t *const src, const ptrdiff_t stride, const __m256i coeffs[3], - __m256i s_256[6], __m256i ss_256[6], __m256i tt_256[6], __m256i r[4]) { - s_256[5] = _mm256_loadu_si256((__m256i *)(src + 3 * stride)); - ss_256[2] = _mm256_unpacklo_epi8(s_256[4], s_256[5]); - ss_256[5] = _mm256_unpackhi_epi8(s_256[4], s_256[5]); - s_256[4] = _mm256_loadu_si256((__m256i *)(src + 4 * stride)); - tt_256[2] = _mm256_unpacklo_epi8(s_256[5], s_256[4]); - tt_256[5] = _mm256_unpackhi_epi8(s_256[5], s_256[4]); - r[0] = convolve_6tap_avx2(ss_256 + 0, coeffs); - r[1] = convolve_6tap_avx2(ss_256 + 3, coeffs); - r[2] = convolve_6tap_avx2(tt_256 + 0, coeffs); - r[3] = convolve_6tap_avx2(tt_256 + 3, coeffs); -} - -static inline __m128i y_convolve_8tap_2x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[4], - __m128i s_16[8], - __m128i ss_128[4]) { - s_16[7] = _mm_cvtsi32_si128(loadu_int16(src + 7 * stride)); - const __m128i src67 = _mm_unpacklo_epi16(s_16[6], s_16[7]); - s_16[6] = _mm_cvtsi32_si128(loadu_int16(src + 8 * stride)); - const __m128i src78 = _mm_unpacklo_epi16(s_16[7], s_16[6]); - ss_128[3] = _mm_unpacklo_epi8(src67, src78); - return convolve_8tap_ssse3(ss_128, coeffs); -} - -static inline __m128i y_convolve_8tap_4x2_ssse3(const uint8_t *const src, - const ptrdiff_t stride, - const __m128i coeffs[4], - __m128i s_32[8], - __m128i ss_128[4]) { - s_32[7] = _mm_cvtsi32_si128(loadu_int32(src + 7 * stride)); - const __m128i src67 = _mm_unpacklo_epi32(s_32[6], s_32[7]); - s_32[6] = _mm_cvtsi32_si128(loadu_int32(src + 8 * stride)); - const __m128i src78 = _mm_unpacklo_epi32(s_32[7], s_32[6]); - ss_128[3] = _mm_unpacklo_epi8(src67, src78); - return convolve_8tap_ssse3(ss_128, coeffs); -} - -static inline __m256i y_convolve_8tap_8x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[4], - __m128i s_64[8], - __m256i ss_256[4]) { - s_64[7] = _mm_loadl_epi64((__m128i *)(src + 7 * stride)); - const __m256i src67 = _mm256_setr_m128i(s_64[6], s_64[7]); - s_64[6] = _mm_loadl_epi64((__m128i *)(src + 8 * stride)); - const __m256i src78 = _mm256_setr_m128i(s_64[7], s_64[6]); - ss_256[3] = _mm256_unpacklo_epi8(src67, src78); - return convolve_8tap_avx2(ss_256, coeffs); -} - -static inline void y_convolve_8tap_16x2_avx2(const uint8_t *const src, - const ptrdiff_t stride, - const __m256i coeffs[4], - __m128i s_128[8], - __m256i ss_256[8], __m256i r[2]) { - s_128[7] = _mm_loadu_si128((__m128i *)(src + 7 * stride)); - const __m256i src67 = _mm256_setr_m128i(s_128[6], s_128[7]); - s_128[6] = _mm_loadu_si128((__m128i *)(src + 8 * stride)); - const __m256i src78 = _mm256_setr_m128i(s_128[7], s_128[6]); - ss_256[3] = _mm256_unpacklo_epi8(src67, src78); - ss_256[7] = _mm256_unpackhi_epi8(src67, src78); - r[0] = convolve_8tap_avx2(ss_256, coeffs); - r[1] = convolve_8tap_avx2(ss_256 + 4, coeffs); -} - -static inline void y_convolve_8tap_32x2_avx2( - const uint8_t *const src, const ptrdiff_t stride, const __m256i coeffs[4], - __m256i s_256[8], __m256i ss_256[8], __m256i tt_256[8], __m256i r[4]) { - s_256[7] = _mm256_loadu_si256((__m256i *)(src + 7 * stride)); - ss_256[3] = _mm256_unpacklo_epi8(s_256[6], s_256[7]); - ss_256[7] = _mm256_unpackhi_epi8(s_256[6], s_256[7]); - s_256[6] = _mm256_loadu_si256((__m256i *)(src + 8 * stride)); - tt_256[3] = _mm256_unpacklo_epi8(s_256[7], s_256[6]); - tt_256[7] = _mm256_unpackhi_epi8(s_256[7], s_256[6]); - r[0] = convolve_8tap_avx2(ss_256 + 0, coeffs); - r[1] = convolve_8tap_avx2(ss_256 + 4, coeffs); - r[2] = convolve_8tap_avx2(tt_256 + 0, coeffs); - r[3] = convolve_8tap_avx2(tt_256 + 4, coeffs); -} - -static inline void xy_x_convolve_2tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[1], - __m256i r[2]) { - const __m256i s0 = _mm256_loadu_si256((__m256i *)src); - const __m256i s1 = _mm256_loadu_si256((__m256i *)(src + 1)); - const __m256i ss0 = _mm256_unpacklo_epi8(s0, s1); - const __m256i ss1 = _mm256_unpackhi_epi8(s0, s1); - - r[0] = convolve_2tap_avx2(&ss0, coeffs); - r[1] = convolve_2tap_avx2(&ss1, coeffs); -} - -static inline void xy_x_2tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[1], - int16_t *const dst) { - __m256i r[2]; - - xy_x_convolve_2tap_32_avx2(src, coeffs, r); - const __m256i d0 = xy_x_round_avx2(r[0]); - const __m256i d1 = xy_x_round_avx2(r[1]); - _mm256_storeu_si256((__m256i *)dst, d0); - _mm256_storeu_si256((__m256i *)(dst + 16), d1); -} - -static inline void xy_x_4tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[2], - const __m256i filt[2], - int16_t *const dst) { - __m256i r[2]; - - x_convolve_4tap_32_avx2(src, coeffs, filt, r); - const __m256i d0 = xy_x_round_avx2(r[0]); - const __m256i d1 = xy_x_round_avx2(r[1]); - _mm256_storeu_si256((__m256i *)dst, d0); - _mm256_storeu_si256((__m256i *)(dst + 16), d1); -} - -static inline void xy_x_6tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[3], - const __m256i filt[3], - int16_t *const dst) { - __m256i r[2]; - - x_convolve_6tap_32_avx2(src, coeffs, filt, r); - const __m256i d0 = xy_x_round_avx2(r[0]); - const __m256i d1 = xy_x_round_avx2(r[1]); - _mm256_storeu_si256((__m256i *)dst, d0); - _mm256_storeu_si256((__m256i *)(dst + 16), d1); -} - -static inline void xy_x_8tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[4], - const __m256i filt[4], - int16_t *const dst) { - __m256i r[2]; - - x_convolve_8tap_32_avx2(src, coeffs, filt, r); - const __m256i d0 = xy_x_round_avx2(r[0]); - const __m256i d1 = xy_x_round_avx2(r[1]); - _mm256_storeu_si256((__m256i *)dst, d0); - _mm256_storeu_si256((__m256i *)(dst + 16), d1); -} - -static inline __m128i xy_y_convolve_2tap_2x2_sse2(const int16_t *const src, - __m128i s_32[2], - const __m128i coeffs[1]) { - __m128i s_128[2]; - - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src + 2)); - s_128[0] = _mm_unpacklo_epi32(s_32[0], s_32[1]); - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src + 2 * 2)); - s_128[1] = _mm_unpacklo_epi32(s_32[1], s_32[0]); - const __m128i ss = _mm_unpacklo_epi16(s_128[0], s_128[1]); - return convolve16_2tap_sse2(&ss, coeffs); -} - -static inline __m128i xy_y_convolve_2tap_2x2_half_pel_sse2( - const int16_t *const src, __m128i s_32[2]) { - __m128i s_128[2]; - - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src + 2)); - s_128[0] = _mm_unpacklo_epi32(s_32[0], s_32[1]); - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src + 2 * 2)); - s_128[1] = _mm_unpacklo_epi32(s_32[1], s_32[0]); - return _mm_add_epi16(s_128[0], s_128[1]); -} - -static inline void xy_y_convolve_2tap_4x2_sse2(const int16_t *const src, - __m128i s_64[2], - const __m128i coeffs[1], - __m128i r[2]) { - __m128i s_128[2]; - - s_64[1] = _mm_loadl_epi64((__m128i *)(src + 4)); - s_128[0] = _mm_unpacklo_epi64(s_64[0], s_64[1]); - s_64[0] = _mm_loadl_epi64((__m128i *)(src + 2 * 4)); - s_128[1] = _mm_unpacklo_epi64(s_64[1], s_64[0]); - const __m128i ss0 = _mm_unpacklo_epi16(s_128[0], s_128[1]); - const __m128i ss1 = _mm_unpackhi_epi16(s_128[0], s_128[1]); - r[0] = convolve16_2tap_sse2(&ss0, coeffs); - r[1] = convolve16_2tap_sse2(&ss1, coeffs); -} - -static inline __m128i xy_y_convolve_2tap_4x2_half_pel_sse2( - const int16_t *const src, __m128i s_64[2]) { - __m128i s_128[2]; - - s_64[1] = _mm_loadl_epi64((__m128i *)(src + 4)); - s_128[0] = _mm_unpacklo_epi64(s_64[0], s_64[1]); - s_64[0] = _mm_loadl_epi64((__m128i *)(src + 2 * 4)); - s_128[1] = _mm_unpacklo_epi64(s_64[1], s_64[0]); - return _mm_add_epi16(s_128[0], s_128[1]); -} - -static inline void xy_y_convolve_2tap_16_avx2(const __m256i s0, - const __m256i s1, - const __m256i coeffs[1], - __m256i r[2]) { - const __m256i ss0 = _mm256_unpacklo_epi16(s0, s1); - const __m256i ss1 = _mm256_unpackhi_epi16(s0, s1); - r[0] = convolve16_2tap_avx2(&ss0, coeffs); - r[1] = convolve16_2tap_avx2(&ss1, coeffs); -} - -static inline void xy_y_convolve_2tap_8x2_avx2(const int16_t *const src, - __m128i s_128[2], - const __m256i coeffs[1], - __m256i r[2]) { - __m256i s_256[2]; - s_128[1] = _mm_loadu_si128((__m128i *)(src + 8)); - s_256[0] = _mm256_setr_m128i(s_128[0], s_128[1]); - s_128[0] = _mm_loadu_si128((__m128i *)(src + 2 * 8)); - s_256[1] = _mm256_setr_m128i(s_128[1], s_128[0]); - xy_y_convolve_2tap_16_avx2(s_256[0], s_256[1], coeffs, r); -} - -static inline __m256i xy_y_convolve_2tap_8x2_half_pel_avx2( - const int16_t *const src, __m128i s_128[2]) { - __m256i s_256[2]; - s_128[1] = _mm_loadu_si128((__m128i *)(src + 8)); - s_256[0] = _mm256_setr_m128i(s_128[0], s_128[1]); - s_128[0] = _mm_loadu_si128((__m128i *)(src + 2 * 8)); - s_256[1] = _mm256_setr_m128i(s_128[1], s_128[0]); - return _mm256_add_epi16(s_256[0], s_256[1]); -} - -static inline void xy_y_convolve_2tap_16x2_half_pel_avx2( - const int16_t *const src, __m256i s_256[2], __m256i r[2]) { - s_256[1] = _mm256_loadu_si256((__m256i *)(src + 16)); - r[0] = _mm256_add_epi16(s_256[0], s_256[1]); - s_256[0] = _mm256_loadu_si256((__m256i *)(src + 2 * 16)); - r[1] = _mm256_add_epi16(s_256[1], s_256[0]); -} - -static inline void xy_y_store_16x2_avx2(const __m256i r[2], uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i t = _mm256_packus_epi16(r[0], r[1]); - const __m256i d = _mm256_permute4x64_epi64(t, 0xD8); - storeu_u8_16x2_avx2(d, dst, stride); -} - -static inline void xy_y_convolve_2tap_16x2_avx2(const int16_t *const src, - __m256i s[2], - const __m256i coeffs[1], - __m256i r[4]) { - s[1] = _mm256_loadu_si256((__m256i *)(src + 16)); - xy_y_convolve_2tap_16_avx2(s[0], s[1], coeffs, r + 0); - s[0] = _mm256_loadu_si256((__m256i *)(src + 2 * 16)); - xy_y_convolve_2tap_16_avx2(s[1], s[0], coeffs, r + 2); -} - -static inline void xy_y_convolve_2tap_32_avx2(const int16_t *const src, - const __m256i s0[2], - __m256i s1[2], - const __m256i coeffs[1], - __m256i r[4]) { - s1[0] = _mm256_loadu_si256((__m256i *)src); - s1[1] = _mm256_loadu_si256((__m256i *)(src + 16)); - xy_y_convolve_2tap_16_avx2(s0[0], s1[0], coeffs, r + 0); - xy_y_convolve_2tap_16_avx2(s0[1], s1[1], coeffs, r + 2); -} - -static inline void xy_y_convolve_2tap_32_all_avx2(const int16_t *const src, - const __m256i s0[2], - __m256i s1[2], - const __m256i coeffs[1], - uint8_t *const dst) { - __m256i r[4]; - - xy_y_convolve_2tap_32_avx2(src, s0, s1, coeffs, r); - xy_y_round_store_32_avx2(r + 0, r + 2, dst); -} - -static inline void xy_y_convolve_2tap_half_pel_32_avx2(const int16_t *const src, - const __m256i s0[2], - __m256i s1[2], - __m256i r[2]) { - s1[0] = _mm256_loadu_si256((__m256i *)src); - s1[1] = _mm256_loadu_si256((__m256i *)(src + 16)); - r[0] = _mm256_add_epi16(s0[0], s1[0]); - r[1] = _mm256_add_epi16(s0[1], s1[1]); -} - -static inline void xy_y_convolve_2tap_half_pel_32_all_avx2( - const int16_t *const src, const __m256i s0[2], __m256i s1[2], - uint8_t *const dst) { - __m256i r[2]; - - xy_y_convolve_2tap_half_pel_32_avx2(src, s0, s1, r); - r[0] = xy_y_round_half_pel_avx2(r[0]); - r[1] = xy_y_round_half_pel_avx2(r[1]); - xy_y_pack_store_32_avx2(r[0], r[1], dst); -} - -static inline __m128i xy_y_convolve_4tap_2x2_sse2(const int16_t *const src, - __m128i s_32[4], - __m128i ss_128[2], - const __m128i coeffs[2]) { - s_32[3] = _mm_cvtsi32_si128(loadu_int32(src + 3 * 2)); - const __m128i src23 = _mm_unpacklo_epi32(s_32[2], s_32[3]); - s_32[2] = _mm_cvtsi32_si128(loadu_int32(src + 4 * 2)); - const __m128i src34 = _mm_unpacklo_epi32(s_32[3], s_32[2]); - ss_128[1] = _mm_unpacklo_epi16(src23, src34); - const __m128i r = convolve16_4tap_sse2(ss_128, coeffs); - ss_128[0] = ss_128[1]; - return r; -} - -static inline __m256i xy_y_convolve_4tap_4x2_avx2(const int16_t *const src, - __m128i s_64[4], - __m256i ss_256[2], - const __m256i coeffs[2]) { - __m256i s_256[2]; - s_64[3] = _mm_loadl_epi64((__m128i *)(src + 3 * 4)); - s_256[0] = _mm256_setr_m128i(s_64[2], s_64[3]); - s_64[2] = _mm_loadl_epi64((__m128i *)(src + 4 * 4)); - s_256[1] = _mm256_setr_m128i(s_64[3], s_64[2]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - const __m256i r = convolve16_4tap_avx2(ss_256, coeffs); - ss_256[0] = ss_256[1]; - return r; -} - -static inline void xy_y_convolve_4tap_16_avx2(const __m256i *const ss, - const __m256i coeffs[2], - __m256i r[2]) { - r[0] = convolve16_4tap_avx2(ss, coeffs); - r[1] = convolve16_4tap_avx2(ss + 2, coeffs); -} - -static inline void xy_y_convolve_4tap_8x2_avx2(const int16_t *const src, - __m256i ss_256[4], - const __m256i coeffs[2], - __m256i r[2]) { - __m256i s_256[2]; - s_256[0] = _mm256_loadu_si256((__m256i *)(src + 2 * 8)); - s_256[1] = _mm256_loadu_si256((__m256i *)(src + 3 * 8)); - ss_256[1] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r); - ss_256[0] = ss_256[1]; - ss_256[2] = ss_256[3]; -} - -static inline void xy_y_convolve_4tap_8x2_half_pel_avx2( - const int16_t *const src, const __m256i coeffs[1], __m256i s_256[4], - __m256i r[2]) { - __m256i a_256[2]; - s_256[2] = _mm256_loadu_si256((__m256i *)(src + 2 * 8)); - s_256[3] = _mm256_loadu_si256((__m256i *)(src + 3 * 8)); - a_256[0] = _mm256_add_epi16(s_256[0], s_256[3]); - a_256[1] = _mm256_add_epi16(s_256[1], s_256[2]); - xy_y_convolve_2tap_16_avx2(a_256[0], a_256[1], coeffs, r); - s_256[0] = s_256[2]; - s_256[1] = s_256[3]; -} - -static inline void xy_y_convolve_4tap_16x2_avx2( - const int16_t *const src, __m256i s_256[4], __m256i ss_256[4], - __m256i tt_256[4], const __m256i coeffs[2], __m256i r[4]) { - s_256[3] = _mm256_loadu_si256((__m256i *)(src + 3 * 16)); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - s_256[2] = _mm256_loadu_si256((__m256i *)(src + 4 * 16)); - tt_256[1] = _mm256_unpacklo_epi16(s_256[3], s_256[2]); - tt_256[3] = _mm256_unpackhi_epi16(s_256[3], s_256[2]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r + 0); - xy_y_convolve_4tap_16_avx2(tt_256, coeffs, r + 2); - ss_256[0] = ss_256[1]; - ss_256[2] = ss_256[3]; - tt_256[0] = tt_256[1]; - tt_256[2] = tt_256[3]; -} - -static inline void xy_y_convolve_4tap_32x2_avx2( - const int16_t *const src, const ptrdiff_t stride, __m256i s_256[4], - __m256i ss_256[4], __m256i tt_256[4], const __m256i coeffs[2], - __m256i r[4]) { - s_256[3] = _mm256_loadu_si256((__m256i *)(src + 3 * stride)); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - s_256[2] = _mm256_loadu_si256((__m256i *)(src + 4 * stride)); - tt_256[1] = _mm256_unpacklo_epi16(s_256[3], s_256[2]); - tt_256[3] = _mm256_unpackhi_epi16(s_256[3], s_256[2]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r + 0); - xy_y_convolve_4tap_16_avx2(tt_256, coeffs, r + 2); - ss_256[0] = ss_256[1]; - ss_256[2] = ss_256[3]; - tt_256[0] = tt_256[1]; - tt_256[2] = tt_256[3]; -} - -static inline void xy_y_convolve_4tap_16x2_half_pelavx2( - const int16_t *const src, __m256i s_256[5], const __m256i coeffs[1], - __m256i r[4]) { - __m256i a_256[2]; - - s_256[3] = _mm256_loadu_si256((__m256i *)(src + 3 * 16)); - s_256[4] = _mm256_loadu_si256((__m256i *)(src + 4 * 16)); - - a_256[0] = _mm256_add_epi16(s_256[0], s_256[3]); - a_256[1] = _mm256_add_epi16(s_256[1], s_256[2]); - xy_y_convolve_2tap_16_avx2(a_256[0], a_256[1], coeffs, r + 0); - - a_256[0] = _mm256_add_epi16(s_256[1], s_256[4]); - a_256[1] = _mm256_add_epi16(s_256[2], s_256[3]); - xy_y_convolve_2tap_16_avx2(a_256[0], a_256[1], coeffs, r + 2); - - s_256[0] = s_256[2]; - s_256[1] = s_256[3]; - s_256[2] = s_256[4]; -} - -static inline __m128i xy_y_convolve_6tap_2x2_sse2(const int16_t *const src, - __m128i s_32[6], - __m128i ss_128[3], - const __m128i coeffs[3]) { - s_32[5] = _mm_cvtsi32_si128(loadu_int32(src + 5 * 2)); - const __m128i src45 = _mm_unpacklo_epi32(s_32[4], s_32[5]); - s_32[4] = _mm_cvtsi32_si128(loadu_int32(src + 6 * 2)); - const __m128i src56 = _mm_unpacklo_epi32(s_32[5], s_32[4]); - ss_128[2] = _mm_unpacklo_epi16(src45, src56); - const __m128i r = convolve16_6tap_sse2(ss_128, coeffs); - ss_128[0] = ss_128[1]; - ss_128[1] = ss_128[2]; - return r; -} - -static inline __m256i xy_y_convolve_6tap_4x2_avx2(const int16_t *const src, - __m128i s_64[6], - __m256i ss_256[3], - const __m256i coeffs[3]) { - __m256i s_256[2]; - s_64[5] = _mm_loadl_epi64((__m128i *)(src + 5 * 4)); - s_256[0] = _mm256_setr_m128i(s_64[4], s_64[5]); - s_64[4] = _mm_loadl_epi64((__m128i *)(src + 6 * 4)); - s_256[1] = _mm256_setr_m128i(s_64[5], s_64[4]); - ss_256[2] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - const __m256i r = convolve16_6tap_avx2(ss_256, coeffs); - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - return r; -} - -static inline void xy_y_convolve_6tap_16_avx2(const __m256i ss[6], - const __m256i coeffs[3], - __m256i r[2]) { - r[0] = convolve16_6tap_avx2(ss, coeffs); - r[1] = convolve16_6tap_avx2(ss + 3, coeffs); -} - -static inline void xy_y_convolve_6tap_8x2_avx2(const int16_t *const src, - __m256i ss_256[6], - const __m256i coeffs[3], - __m256i r[2]) { - __m256i s_256[2]; - s_256[0] = _mm256_loadu_si256((__m256i *)(src + 4 * 8)); - s_256[1] = _mm256_loadu_si256((__m256i *)(src + 5 * 8)); - ss_256[2] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[5] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - xy_y_convolve_6tap_16_avx2(ss_256, coeffs, r); - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[3] = ss_256[4]; - ss_256[4] = ss_256[5]; -} - -static inline void xy_y_convolve_6tap_8x2_half_pel_avx2( - const int16_t *const src, const __m256i coeffs[2], __m256i s_256[6], - __m256i r[2]) { - __m256i a_256[2], ss_256[4]; - s_256[4] = _mm256_loadu_si256((__m256i *)(src + 4 * 8)); - s_256[5] = _mm256_loadu_si256((__m256i *)(src + 5 * 8)); - a_256[0] = _mm256_add_epi16(s_256[0], s_256[5]); - a_256[1] = _mm256_add_epi16(s_256[1], s_256[4]); - ss_256[0] = _mm256_unpacklo_epi16(a_256[0], a_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[2] = _mm256_unpackhi_epi16(a_256[0], a_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r); - s_256[0] = s_256[2]; - s_256[1] = s_256[3]; - s_256[2] = s_256[4]; - s_256[3] = s_256[5]; -} - -static inline void xy_y_convolve_6tap_16x2_avx2( - const int16_t *const src, const ptrdiff_t stride, __m256i s_256[6], - __m256i ss_256[6], __m256i tt_256[6], const __m256i coeffs[3], - __m256i r[4]) { - s_256[5] = _mm256_loadu_si256((__m256i *)(src + 5 * stride)); - ss_256[2] = _mm256_unpacklo_epi16(s_256[4], s_256[5]); - ss_256[5] = _mm256_unpackhi_epi16(s_256[4], s_256[5]); - s_256[4] = _mm256_loadu_si256((__m256i *)(src + 6 * stride)); - tt_256[2] = _mm256_unpacklo_epi16(s_256[5], s_256[4]); - tt_256[5] = _mm256_unpackhi_epi16(s_256[5], s_256[4]); - - xy_y_convolve_6tap_16_avx2(ss_256, coeffs, r + 0); - xy_y_convolve_6tap_16_avx2(tt_256, coeffs, r + 2); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[3] = ss_256[4]; - ss_256[4] = ss_256[5]; - - tt_256[0] = tt_256[1]; - tt_256[1] = tt_256[2]; - tt_256[3] = tt_256[4]; - tt_256[4] = tt_256[5]; -} - -static inline void xy_y_convolve_6tap_16x2_half_pel_avx2( - const int16_t *const src, const ptrdiff_t stride, __m256i s_256[6], - __m256i ss_256[4], const __m256i coeffs[2], __m256i r[4]) { - __m256i a_256[2]; - - s_256[5] = _mm256_loadu_si256((__m256i *)(src + 5 * stride)); - a_256[0] = _mm256_add_epi16(s_256[0], s_256[5]); - a_256[1] = _mm256_add_epi16(s_256[1], s_256[4]); - ss_256[0] = _mm256_unpacklo_epi16(a_256[0], a_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[2], s_256[3]); - ss_256[2] = _mm256_unpackhi_epi16(a_256[0], a_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[2], s_256[3]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r + 0); - - a_256[1] = _mm256_add_epi16(s_256[2], s_256[5]); - s_256[0] = s_256[2]; - s_256[2] = s_256[4]; - s_256[4] = _mm256_loadu_si256((__m256i *)(src + 6 * stride)); - a_256[0] = _mm256_add_epi16(s_256[1], s_256[4]); - s_256[1] = s_256[3]; - s_256[3] = s_256[5]; - ss_256[0] = _mm256_unpacklo_epi16(a_256[0], a_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(s_256[1], s_256[2]); - ss_256[2] = _mm256_unpackhi_epi16(a_256[0], a_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(s_256[1], s_256[2]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r + 2); -} - -static inline __m128i xy_y_convolve_8tap_2x2_sse2(const int16_t *const src, - __m128i s_32[8], - __m128i ss_128[4], - const __m128i coeffs[4]) { - s_32[7] = _mm_cvtsi32_si128(loadu_int32(src + 7 * 2)); - const __m128i src67 = _mm_unpacklo_epi32(s_32[6], s_32[7]); - s_32[6] = _mm_cvtsi32_si128(loadu_int32(src + 8 * 2)); - const __m128i src78 = _mm_unpacklo_epi32(s_32[7], s_32[6]); - ss_128[3] = _mm_unpacklo_epi16(src67, src78); - const __m128i r = convolve16_8tap_sse2(ss_128, coeffs); - ss_128[0] = ss_128[1]; - ss_128[1] = ss_128[2]; - ss_128[2] = ss_128[3]; - return r; -} - -static inline __m256i xy_y_convolve_8tap_4x2_avx2(const int16_t *const src, - __m128i s_64[8], - __m256i ss_256[4], - const __m256i coeffs[4]) { - __m256i s_256[2]; - s_64[7] = _mm_loadl_epi64((__m128i *)(src + 7 * 4)); - s_256[0] = _mm256_setr_m128i(s_64[6], s_64[7]); - s_64[6] = _mm_loadl_epi64((__m128i *)(src + 8 * 4)); - s_256[1] = _mm256_setr_m128i(s_64[7], s_64[6]); - ss_256[3] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - const __m256i r = convolve16_8tap_avx2(ss_256, coeffs); - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[2] = ss_256[3]; - return r; -} - -static inline void xy_y_convolve_8tap_16_avx2(const __m256i *const ss, - const __m256i coeffs[4], - __m256i r[2]) { - r[0] = convolve16_8tap_avx2(ss, coeffs); - r[1] = convolve16_8tap_avx2(ss + 4, coeffs); -} - -static inline void xy_y_convolve_8tap_8x2_avx2(const int16_t *const src, - __m256i ss_256[8], - const __m256i coeffs[4], - __m256i r[2]) { - __m256i s_256[2]; - s_256[0] = _mm256_loadu_si256((__m256i *)(src + 6 * 8)); - s_256[1] = _mm256_loadu_si256((__m256i *)(src + 7 * 8)); - ss_256[3] = _mm256_unpacklo_epi16(s_256[0], s_256[1]); - ss_256[7] = _mm256_unpackhi_epi16(s_256[0], s_256[1]); - xy_y_convolve_8tap_16_avx2(ss_256, coeffs, r); - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[2] = ss_256[3]; - ss_256[4] = ss_256[5]; - ss_256[5] = ss_256[6]; - ss_256[6] = ss_256[7]; -} - -static inline void xy_y_convolve_8tap_8x2_half_pel_avx2( - const int16_t *const src, const __m256i coeffs[2], __m256i s_256[8], - __m256i r[2]) { - __m256i a_256[4], ss_256[4]; - - s_256[6] = _mm256_loadu_si256((__m256i *)(src + 6 * 8)); - s_256[7] = _mm256_loadu_si256((__m256i *)(src + 7 * 8)); - a_256[0] = _mm256_add_epi16(s_256[0], s_256[7]); - a_256[1] = _mm256_add_epi16(s_256[1], s_256[6]); - a_256[2] = _mm256_add_epi16(s_256[2], s_256[5]); - a_256[3] = _mm256_add_epi16(s_256[3], s_256[4]); - ss_256[0] = _mm256_unpacklo_epi16(a_256[0], a_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(a_256[2], a_256[3]); - ss_256[2] = _mm256_unpackhi_epi16(a_256[0], a_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(a_256[2], a_256[3]); - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r); - s_256[0] = s_256[2]; - s_256[1] = s_256[3]; - s_256[2] = s_256[4]; - s_256[3] = s_256[5]; - s_256[4] = s_256[6]; - s_256[5] = s_256[7]; -} - -static AOM_FORCE_INLINE void xy_y_convolve_8tap_16x2_avx2( - const int16_t *const src, const ptrdiff_t stride, const __m256i coeffs[4], - __m256i s_256[8], __m256i ss_256[8], __m256i tt_256[8], __m256i r[4]) { - s_256[7] = _mm256_loadu_si256((__m256i *)(src + 7 * stride)); - ss_256[3] = _mm256_unpacklo_epi16(s_256[6], s_256[7]); - ss_256[7] = _mm256_unpackhi_epi16(s_256[6], s_256[7]); - s_256[6] = _mm256_loadu_si256((__m256i *)(src + 8 * stride)); - tt_256[3] = _mm256_unpacklo_epi16(s_256[7], s_256[6]); - tt_256[7] = _mm256_unpackhi_epi16(s_256[7], s_256[6]); - - xy_y_convolve_8tap_16_avx2(ss_256, coeffs, r + 0); - xy_y_convolve_8tap_16_avx2(tt_256, coeffs, r + 2); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[2] = ss_256[3]; - ss_256[4] = ss_256[5]; - ss_256[5] = ss_256[6]; - ss_256[6] = ss_256[7]; - - tt_256[0] = tt_256[1]; - tt_256[1] = tt_256[2]; - tt_256[2] = tt_256[3]; - tt_256[4] = tt_256[5]; - tt_256[5] = tt_256[6]; - tt_256[6] = tt_256[7]; -} - -static inline void xy_y_convolve_8tap_16x2_half_pel_avx2( - const int16_t *const src, const ptrdiff_t stride, const __m256i coeffs[4], - __m256i s_256[8], __m256i r[4]) { - __m256i a_256[4], ss_256[4]; - s_256[7] = _mm256_loadu_si256((__m256i *)(src + 7 * stride)); - - a_256[0] = _mm256_add_epi16(s_256[0], s_256[7]); - a_256[1] = _mm256_add_epi16(s_256[1], s_256[6]); - a_256[2] = _mm256_add_epi16(s_256[2], s_256[5]); - a_256[3] = _mm256_add_epi16(s_256[3], s_256[4]); - ss_256[0] = _mm256_unpacklo_epi16(a_256[0], a_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(a_256[2], a_256[3]); - ss_256[2] = _mm256_unpackhi_epi16(a_256[0], a_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(a_256[2], a_256[3]); - - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r + 0); - - a_256[1] = _mm256_add_epi16(s_256[2], s_256[7]); - a_256[2] = _mm256_add_epi16(s_256[3], s_256[6]); - a_256[3] = _mm256_add_epi16(s_256[4], s_256[5]); - s_256[0] = s_256[2]; - s_256[2] = s_256[4]; - s_256[4] = s_256[6]; - s_256[6] = _mm256_loadu_si256((__m256i *)(src + 8 * stride)); - - a_256[0] = _mm256_add_epi16(s_256[1], s_256[6]); - s_256[1] = s_256[3]; - s_256[3] = s_256[5]; - s_256[5] = s_256[7]; - ss_256[0] = _mm256_unpacklo_epi16(a_256[0], a_256[1]); - ss_256[1] = _mm256_unpacklo_epi16(a_256[2], a_256[3]); - ss_256[2] = _mm256_unpackhi_epi16(a_256[0], a_256[1]); - ss_256[3] = _mm256_unpackhi_epi16(a_256[2], a_256[3]); - - xy_y_convolve_4tap_16_avx2(ss_256, coeffs, r + 2); -} - -static inline void xy_y_round_store_8x2_avx2(const __m256i res[2], - uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i r = xy_y_round_16_avx2(res); - pack_store_8x2_avx2(r, dst, stride); -} - -static inline void xy_y_round_store_16x2_avx2(const __m256i res[4], - uint8_t *const dst, - const ptrdiff_t stride) { - const __m256i r0 = xy_y_round_16_avx2(res + 0); - const __m256i r1 = xy_y_round_16_avx2(res + 2); - xy_y_pack_store_16x2_avx2(r0, r1, dst, stride); -} - -static inline void sr_y_round_store_32_avx2(const __m256i res[2], - uint8_t *const dst) { - __m256i r[2]; - - r[0] = sr_y_round_avx2(res[0]); - r[1] = sr_y_round_avx2(res[1]); - convolve_store_32_avx2(r[0], r[1], dst); -} - -static inline void sr_y_round_store_32x2_avx2(const __m256i res[4], - uint8_t *const dst, - const int32_t dst_stride) { - sr_y_round_store_32_avx2(res, dst); - sr_y_round_store_32_avx2(res + 2, dst + dst_stride); -} - -static inline void sr_y_2tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[1], const __m256i s0, - __m256i *const s1, uint8_t *const dst) { - __m256i r[2]; - y_convolve_2tap_32_avx2(src, coeffs, s0, s1, r); - sr_y_round_store_32_avx2(r, dst); -} - -static AOM_FORCE_INLINE void av1_convolve_y_sr_specialized_avx2( - const uint8_t *src, int32_t src_stride, uint8_t *dst, int32_t dst_stride, - int32_t w, int32_t h, const InterpFilterParams *filter_params_y, - const int32_t subpel_y_q4) { - int32_t x, y; - __m128i coeffs_128[4]; - __m256i coeffs_256[4]; - - int vert_tap = get_filter_tap(filter_params_y, subpel_y_q4); - - if (vert_tap == 2) { - // vert_filt as 2 tap - const uint8_t *src_ptr = src; - - y = h; - - if (subpel_y_q4 != 8) { - if (w <= 8) { - prepare_half_coeffs_2tap_ssse3(filter_params_y, subpel_y_q4, - coeffs_128); - - if (w == 2) { - __m128i s_16[2]; - - s_16[0] = _mm_cvtsi32_si128(*(int16_t *)src_ptr); - - do { - const __m128i res = y_convolve_2tap_2x2_ssse3(src_ptr, src_stride, - coeffs_128, s_16); - const __m128i r = sr_y_round_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 4) { - __m128i s_32[2]; - - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src_ptr)); - - do { - const __m128i res = y_convolve_2tap_4x2_ssse3(src_ptr, src_stride, - coeffs_128, s_32); - const __m128i r = sr_y_round_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m128i s_64[2], s_128[2]; - - assert(w == 8); - - s_64[0] = _mm_loadl_epi64((__m128i *)src_ptr); - - do { - // Note: Faster than binding to AVX2 registers. - s_64[1] = _mm_loadl_epi64((__m128i *)(src_ptr + src_stride)); - s_128[0] = _mm_unpacklo_epi64(s_64[0], s_64[1]); - s_64[0] = _mm_loadl_epi64((__m128i *)(src_ptr + 2 * src_stride)); - s_128[1] = _mm_unpacklo_epi64(s_64[1], s_64[0]); - const __m128i ss0 = _mm_unpacklo_epi8(s_128[0], s_128[1]); - const __m128i ss1 = _mm_unpackhi_epi8(s_128[0], s_128[1]); - const __m128i res0 = convolve_2tap_ssse3(&ss0, coeffs_128); - const __m128i res1 = convolve_2tap_ssse3(&ss1, coeffs_128); - const __m128i r0 = sr_y_round_sse2(res0); - const __m128i r1 = sr_y_round_sse2(res1); - const __m128i d = _mm_packus_epi16(r0, r1); - _mm_storel_epi64((__m128i *)dst, d); - _mm_storeh_epi64((__m128i *)(dst + dst_stride), d); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - prepare_half_coeffs_2tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 16) { - __m128i s_128[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)src_ptr); - - do { - __m256i r[2]; - - y_convolve_2tap_16x2_avx2(src_ptr, src_stride, coeffs_256, s_128, - r); - sr_y_round_store_16x2_avx2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - __m256i s_256[2]; - - s_256[0] = _mm256_loadu_si256((__m256i *)src_ptr); - - do { - sr_y_2tap_32_avx2(src_ptr + src_stride, coeffs_256, s_256[0], - &s_256[1], dst); - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride, coeffs_256, s_256[1], - &s_256[0], dst + dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 64) { - __m256i s_256[2][2]; - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(src_ptr + 0 * 32)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(src_ptr + 1 * 32)); - - do { - sr_y_2tap_32_avx2(src_ptr + src_stride, coeffs_256, s_256[0][0], - &s_256[1][0], dst); - sr_y_2tap_32_avx2(src_ptr + src_stride + 32, coeffs_256, - s_256[0][1], &s_256[1][1], dst + 32); - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride, coeffs_256, s_256[1][0], - &s_256[0][0], dst + dst_stride); - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride + 32, coeffs_256, - s_256[1][1], &s_256[0][1], dst + dst_stride + 32); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i s_256[2][4]; - - assert(w == 128); - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(src_ptr + 0 * 32)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(src_ptr + 1 * 32)); - s_256[0][2] = _mm256_loadu_si256((__m256i *)(src_ptr + 2 * 32)); - s_256[0][3] = _mm256_loadu_si256((__m256i *)(src_ptr + 3 * 32)); - - do { - sr_y_2tap_32_avx2(src_ptr + src_stride, coeffs_256, s_256[0][0], - &s_256[1][0], dst); - sr_y_2tap_32_avx2(src_ptr + src_stride + 1 * 32, coeffs_256, - s_256[0][1], &s_256[1][1], dst + 1 * 32); - sr_y_2tap_32_avx2(src_ptr + src_stride + 2 * 32, coeffs_256, - s_256[0][2], &s_256[1][2], dst + 2 * 32); - sr_y_2tap_32_avx2(src_ptr + src_stride + 3 * 32, coeffs_256, - s_256[0][3], &s_256[1][3], dst + 3 * 32); - - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride, coeffs_256, s_256[1][0], - &s_256[0][0], dst + dst_stride); - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride + 1 * 32, coeffs_256, - s_256[1][1], &s_256[0][1], - dst + dst_stride + 1 * 32); - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride + 2 * 32, coeffs_256, - s_256[1][2], &s_256[0][2], - dst + dst_stride + 2 * 32); - sr_y_2tap_32_avx2(src_ptr + 2 * src_stride + 3 * 32, coeffs_256, - s_256[1][3], &s_256[0][3], - dst + dst_stride + 3 * 32); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } - } else { - // average to get half pel - if (w <= 8) { - if (w == 2) { - __m128i s_16[2]; - - s_16[0] = _mm_cvtsi32_si128(*(int16_t *)src_ptr); - - do { - s_16[1] = _mm_cvtsi32_si128(*(int16_t *)(src_ptr + src_stride)); - const __m128i d0 = _mm_avg_epu8(s_16[0], s_16[1]); - *(int16_t *)dst = (int16_t)_mm_cvtsi128_si32(d0); - s_16[0] = _mm_cvtsi32_si128(*(int16_t *)(src_ptr + 2 * src_stride)); - const __m128i d1 = _mm_avg_epu8(s_16[1], s_16[0]); - *(int16_t *)(dst + dst_stride) = (int16_t)_mm_cvtsi128_si32(d1); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 4) { - __m128i s_32[2]; - - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src_ptr)); - - do { - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src_ptr + src_stride)); - const __m128i d0 = _mm_avg_epu8(s_32[0], s_32[1]); - xx_storel_32(dst, d0); - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 2 * src_stride)); - const __m128i d1 = _mm_avg_epu8(s_32[1], s_32[0]); - xx_storel_32(dst + dst_stride, d1); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m128i s_64[2]; - - assert(w == 8); - - s_64[0] = _mm_loadl_epi64((__m128i *)src_ptr); - - do { - // Note: Faster than binding to AVX2 registers. - s_64[1] = _mm_loadl_epi64((__m128i *)(src_ptr + src_stride)); - const __m128i d0 = _mm_avg_epu8(s_64[0], s_64[1]); - _mm_storel_epi64((__m128i *)dst, d0); - s_64[0] = _mm_loadl_epi64((__m128i *)(src_ptr + 2 * src_stride)); - const __m128i d1 = _mm_avg_epu8(s_64[1], s_64[0]); - _mm_storel_epi64((__m128i *)(dst + dst_stride), d1); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else if (w == 16) { - __m128i s_128[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)src_ptr); - - do { - s_128[1] = _mm_loadu_si128((__m128i *)(src_ptr + src_stride)); - const __m128i d0 = _mm_avg_epu8(s_128[0], s_128[1]); - _mm_storeu_si128((__m128i *)dst, d0); - s_128[0] = _mm_loadu_si128((__m128i *)(src_ptr + 2 * src_stride)); - const __m128i d1 = _mm_avg_epu8(s_128[1], s_128[0]); - _mm_storeu_si128((__m128i *)(dst + dst_stride), d1); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - __m256i s_256[2]; - - s_256[0] = _mm256_loadu_si256((__m256i *)src_ptr); - - do { - sr_y_2tap_32_avg_avx2(src_ptr + src_stride, s_256[0], &s_256[1], dst); - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride, s_256[1], &s_256[0], - dst + dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 64) { - __m256i s_256[2][2]; - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(src_ptr + 0 * 32)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(src_ptr + 1 * 32)); - - do { - sr_y_2tap_32_avg_avx2(src_ptr + src_stride, s_256[0][0], &s_256[1][0], - dst); - sr_y_2tap_32_avg_avx2(src_ptr + src_stride + 32, s_256[0][1], - &s_256[1][1], dst + 32); - - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride, s_256[1][0], - &s_256[0][0], dst + dst_stride); - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride + 32, s_256[1][1], - &s_256[0][1], dst + dst_stride + 32); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i s_256[2][4]; - - assert(w == 128); - - s_256[0][0] = _mm256_loadu_si256((__m256i *)(src_ptr + 0 * 32)); - s_256[0][1] = _mm256_loadu_si256((__m256i *)(src_ptr + 1 * 32)); - s_256[0][2] = _mm256_loadu_si256((__m256i *)(src_ptr + 2 * 32)); - s_256[0][3] = _mm256_loadu_si256((__m256i *)(src_ptr + 3 * 32)); - - do { - sr_y_2tap_32_avg_avx2(src_ptr + src_stride, s_256[0][0], &s_256[1][0], - dst); - sr_y_2tap_32_avg_avx2(src_ptr + src_stride + 1 * 32, s_256[0][1], - &s_256[1][1], dst + 1 * 32); - sr_y_2tap_32_avg_avx2(src_ptr + src_stride + 2 * 32, s_256[0][2], - &s_256[1][2], dst + 2 * 32); - sr_y_2tap_32_avg_avx2(src_ptr + src_stride + 3 * 32, s_256[0][3], - &s_256[1][3], dst + 3 * 32); - - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride, s_256[1][0], - &s_256[0][0], dst + dst_stride); - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride + 1 * 32, s_256[1][1], - &s_256[0][1], dst + dst_stride + 1 * 32); - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride + 2 * 32, s_256[1][2], - &s_256[0][2], dst + dst_stride + 2 * 32); - sr_y_2tap_32_avg_avx2(src_ptr + 2 * src_stride + 3 * 32, s_256[1][3], - &s_256[0][3], dst + dst_stride + 3 * 32); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } - } else if (vert_tap == 4) { - // vert_filt as 4 tap - const uint8_t *src_ptr = src - src_stride; - - y = h; - - if (w <= 4) { - prepare_half_coeffs_4tap_ssse3(filter_params_y, subpel_y_q4, coeffs_128); - - if (w == 2) { - __m128i s_16[4], ss_128[2]; - - s_16[0] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 0 * src_stride)); - s_16[1] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 1 * src_stride)); - s_16[2] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 2 * src_stride)); - - const __m128i src01 = _mm_unpacklo_epi16(s_16[0], s_16[1]); - const __m128i src12 = _mm_unpacklo_epi16(s_16[1], s_16[2]); - - ss_128[0] = _mm_unpacklo_epi8(src01, src12); - - do { - src_ptr += 2 * src_stride; - const __m128i res = y_convolve_4tap_2x2_ssse3( - src_ptr, src_stride, coeffs_128, s_16, ss_128); - const __m128i r = sr_y_round_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - - ss_128[0] = ss_128[1]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m128i s_32[4], ss_128[2]; - - assert(w == 4); - - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 0 * src_stride)); - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 1 * src_stride)); - s_32[2] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 2 * src_stride)); - - const __m128i src01 = _mm_unpacklo_epi32(s_32[0], s_32[1]); - const __m128i src12 = _mm_unpacklo_epi32(s_32[1], s_32[2]); - - ss_128[0] = _mm_unpacklo_epi8(src01, src12); - - do { - src_ptr += 2 * src_stride; - const __m128i res = y_convolve_4tap_4x2_ssse3( - src_ptr, src_stride, coeffs_128, s_32, ss_128); - const __m128i r = sr_y_round_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - - ss_128[0] = ss_128[1]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - prepare_half_coeffs_4tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 8) { - __m128i s_64[4]; - __m256i ss_256[2]; - - s_64[0] = _mm_loadl_epi64((__m128i *)(src_ptr + 0 * src_stride)); - s_64[1] = _mm_loadl_epi64((__m128i *)(src_ptr + 1 * src_stride)); - s_64[2] = _mm_loadl_epi64((__m128i *)(src_ptr + 2 * src_stride)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src01 = _mm256_setr_m128i(s_64[0], s_64[1]); - const __m256i src12 = _mm256_setr_m128i(s_64[1], s_64[2]); - - ss_256[0] = _mm256_unpacklo_epi8(src01, src12); - - do { - src_ptr += 2 * src_stride; - const __m256i res = y_convolve_4tap_8x2_avx2( - src_ptr, src_stride, coeffs_256, s_64, ss_256); - sr_y_round_store_8x2_avx2(res, dst, dst_stride); - - ss_256[0] = ss_256[1]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - __m128i s_128[4]; - __m256i ss_256[4], r[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)(src_ptr + 0 * src_stride)); - s_128[1] = _mm_loadu_si128((__m128i *)(src_ptr + 1 * src_stride)); - s_128[2] = _mm_loadu_si128((__m128i *)(src_ptr + 2 * src_stride)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src01 = _mm256_setr_m128i(s_128[0], s_128[1]); - const __m256i src12 = _mm256_setr_m128i(s_128[1], s_128[2]); - - ss_256[0] = _mm256_unpacklo_epi8(src01, src12); - ss_256[2] = _mm256_unpackhi_epi8(src01, src12); - - do { - src_ptr += 2 * src_stride; - y_convolve_4tap_16x2_avx2(src_ptr, src_stride, coeffs_256, s_128, - ss_256, r); - sr_y_round_store_16x2_avx2(r, dst, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[2] = ss_256[3]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - // AV1 standard won't have 32x4 case. - // This only favors some optimization feature which - // subsamples 32x8 to 32x4 and triggers 4-tap filter. - - __m256i s_256[4], ss_256[4], tt_256[4], r[4]; - - s_256[0] = _mm256_loadu_si256((__m256i *)(src_ptr + 0 * src_stride)); - s_256[1] = _mm256_loadu_si256((__m256i *)(src_ptr + 1 * src_stride)); - s_256[2] = _mm256_loadu_si256((__m256i *)(src_ptr + 2 * src_stride)); - - ss_256[0] = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - ss_256[2] = _mm256_unpackhi_epi8(s_256[0], s_256[1]); - - tt_256[0] = _mm256_unpacklo_epi8(s_256[1], s_256[2]); - tt_256[2] = _mm256_unpackhi_epi8(s_256[1], s_256[2]); - - do { - src_ptr += 2 * src_stride; - y_convolve_4tap_32x2_avx2(src_ptr, src_stride, coeffs_256, s_256, - ss_256, tt_256, r); - sr_y_round_store_32x2_avx2(r, dst, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[2] = ss_256[3]; - - tt_256[0] = tt_256[1]; - tt_256[2] = tt_256[3]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - assert(!(w % 32)); - - __m256i s_256[4], ss_256[4], tt_256[4], r[4]; - x = 0; - do { - const uint8_t *s = src_ptr + x; - uint8_t *d = dst + x; - s_256[0] = _mm256_loadu_si256((__m256i *)(s + 0 * src_stride)); - s_256[1] = _mm256_loadu_si256((__m256i *)(s + 1 * src_stride)); - s_256[2] = _mm256_loadu_si256((__m256i *)(s + 2 * src_stride)); - - ss_256[0] = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - ss_256[2] = _mm256_unpackhi_epi8(s_256[0], s_256[1]); - - tt_256[0] = _mm256_unpacklo_epi8(s_256[1], s_256[2]); - tt_256[2] = _mm256_unpackhi_epi8(s_256[1], s_256[2]); - - y = h; - do { - s += 2 * src_stride; - y_convolve_4tap_32x2_avx2(s, src_stride, coeffs_256, s_256, ss_256, - tt_256, r); - sr_y_round_store_32x2_avx2(r, d, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[2] = ss_256[3]; - - tt_256[0] = tt_256[1]; - tt_256[2] = tt_256[3]; - d += 2 * dst_stride; - y -= 2; - } while (y); - x += 32; - } while (x < w); - } - } - } else if (vert_tap == 6) { - // vert_filt as 6 tap - const uint8_t *src_ptr = src - 2 * src_stride; - - if (w <= 4) { - prepare_half_coeffs_6tap_ssse3(filter_params_y, subpel_y_q4, coeffs_128); - - y = h; - - if (w == 2) { - __m128i s_16[6], ss_128[3]; - - s_16[0] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 0 * src_stride)); - s_16[1] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 1 * src_stride)); - s_16[2] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 2 * src_stride)); - s_16[3] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 3 * src_stride)); - s_16[4] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 4 * src_stride)); - - const __m128i src01 = _mm_unpacklo_epi16(s_16[0], s_16[1]); - const __m128i src12 = _mm_unpacklo_epi16(s_16[1], s_16[2]); - const __m128i src23 = _mm_unpacklo_epi16(s_16[2], s_16[3]); - const __m128i src34 = _mm_unpacklo_epi16(s_16[3], s_16[4]); - - ss_128[0] = _mm_unpacklo_epi8(src01, src12); - ss_128[1] = _mm_unpacklo_epi8(src23, src34); - - do { - src_ptr += 2 * src_stride; - const __m128i res = y_convolve_6tap_2x2_ssse3( - src_ptr, src_stride, coeffs_128, s_16, ss_128); - const __m128i r = sr_y_round_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - - ss_128[0] = ss_128[1]; - ss_128[1] = ss_128[2]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m128i s_32[6], ss_128[3]; - - assert(w == 4); - - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 0 * src_stride)); - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 1 * src_stride)); - s_32[2] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 2 * src_stride)); - s_32[3] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 3 * src_stride)); - s_32[4] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 4 * src_stride)); - - const __m128i src01 = _mm_unpacklo_epi32(s_32[0], s_32[1]); - const __m128i src12 = _mm_unpacklo_epi32(s_32[1], s_32[2]); - const __m128i src23 = _mm_unpacklo_epi32(s_32[2], s_32[3]); - const __m128i src34 = _mm_unpacklo_epi32(s_32[3], s_32[4]); - - ss_128[0] = _mm_unpacklo_epi8(src01, src12); - ss_128[1] = _mm_unpacklo_epi8(src23, src34); - - do { - src_ptr += 2 * src_stride; - const __m128i res = y_convolve_6tap_4x2_ssse3( - src_ptr, src_stride, coeffs_128, s_32, ss_128); - const __m128i r = sr_y_round_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - - ss_128[0] = ss_128[1]; - ss_128[1] = ss_128[2]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - prepare_half_coeffs_6tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 8) { - __m128i s_64[6]; - __m256i ss_256[3]; - - s_64[0] = _mm_loadl_epi64((__m128i *)(src_ptr + 0 * src_stride)); - s_64[1] = _mm_loadl_epi64((__m128i *)(src_ptr + 1 * src_stride)); - s_64[2] = _mm_loadl_epi64((__m128i *)(src_ptr + 2 * src_stride)); - s_64[3] = _mm_loadl_epi64((__m128i *)(src_ptr + 3 * src_stride)); - s_64[4] = _mm_loadl_epi64((__m128i *)(src_ptr + 4 * src_stride)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src01 = _mm256_setr_m128i(s_64[0], s_64[1]); - const __m256i src12 = _mm256_setr_m128i(s_64[1], s_64[2]); - const __m256i src23 = _mm256_setr_m128i(s_64[2], s_64[3]); - const __m256i src34 = _mm256_setr_m128i(s_64[3], s_64[4]); - - ss_256[0] = _mm256_unpacklo_epi8(src01, src12); - ss_256[1] = _mm256_unpacklo_epi8(src23, src34); - - y = h; - do { - src_ptr += 2 * src_stride; - const __m256i res = y_convolve_6tap_8x2_avx2( - src_ptr, src_stride, coeffs_256, s_64, ss_256); - sr_y_round_store_8x2_avx2(res, dst, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - __m128i s_128[6]; - __m256i ss_256[6], r[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)(src_ptr + 0 * src_stride)); - s_128[1] = _mm_loadu_si128((__m128i *)(src_ptr + 1 * src_stride)); - s_128[2] = _mm_loadu_si128((__m128i *)(src_ptr + 2 * src_stride)); - s_128[3] = _mm_loadu_si128((__m128i *)(src_ptr + 3 * src_stride)); - s_128[4] = _mm_loadu_si128((__m128i *)(src_ptr + 4 * src_stride)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src01 = _mm256_setr_m128i(s_128[0], s_128[1]); - const __m256i src12 = _mm256_setr_m128i(s_128[1], s_128[2]); - const __m256i src23 = _mm256_setr_m128i(s_128[2], s_128[3]); - const __m256i src34 = _mm256_setr_m128i(s_128[3], s_128[4]); - - ss_256[0] = _mm256_unpacklo_epi8(src01, src12); - ss_256[1] = _mm256_unpacklo_epi8(src23, src34); - - ss_256[3] = _mm256_unpackhi_epi8(src01, src12); - ss_256[4] = _mm256_unpackhi_epi8(src23, src34); - - y = h; - do { - src_ptr += 2 * src_stride; - y_convolve_6tap_16x2_avx2(src_ptr, src_stride, coeffs_256, s_128, - ss_256, r); - sr_y_round_store_16x2_avx2(r, dst, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - - ss_256[3] = ss_256[4]; - ss_256[4] = ss_256[5]; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i s_256[6], ss_256[6], tt_256[6], r[4]; - - assert(!(w % 32)); - - x = 0; - do { - const uint8_t *s = src_ptr + x; - uint8_t *d = dst + x; - - s_256[0] = _mm256_loadu_si256((__m256i *)(s + 0 * src_stride)); - s_256[1] = _mm256_loadu_si256((__m256i *)(s + 1 * src_stride)); - s_256[2] = _mm256_loadu_si256((__m256i *)(s + 2 * src_stride)); - s_256[3] = _mm256_loadu_si256((__m256i *)(s + 3 * src_stride)); - s_256[4] = _mm256_loadu_si256((__m256i *)(s + 4 * src_stride)); - - ss_256[0] = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi8(s_256[2], s_256[3]); - ss_256[3] = _mm256_unpackhi_epi8(s_256[0], s_256[1]); - ss_256[4] = _mm256_unpackhi_epi8(s_256[2], s_256[3]); - - tt_256[0] = _mm256_unpacklo_epi8(s_256[1], s_256[2]); - tt_256[1] = _mm256_unpacklo_epi8(s_256[3], s_256[4]); - tt_256[3] = _mm256_unpackhi_epi8(s_256[1], s_256[2]); - tt_256[4] = _mm256_unpackhi_epi8(s_256[3], s_256[4]); - - y = h; - do { - s += 2 * src_stride; - y_convolve_6tap_32x2_avx2(s, src_stride, coeffs_256, s_256, ss_256, - tt_256, r); - sr_y_round_store_32x2_avx2(r, d, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[3] = ss_256[4]; - ss_256[4] = ss_256[5]; - - tt_256[0] = tt_256[1]; - tt_256[1] = tt_256[2]; - tt_256[3] = tt_256[4]; - tt_256[4] = tt_256[5]; - d += 2 * dst_stride; - y -= 2; - } while (y); - - x += 32; - } while (x < w); - } - } - } else if (vert_tap == 8) { - // vert_filt as 8 tap - const uint8_t *src_ptr = src - 3 * src_stride; - - if (w <= 4) { - prepare_half_coeffs_8tap_ssse3(filter_params_y, subpel_y_q4, coeffs_128); - - y = h; - - if (w == 2) { - __m128i s_16[8], ss_128[4]; - - s_16[0] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 0 * src_stride)); - s_16[1] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 1 * src_stride)); - s_16[2] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 2 * src_stride)); - s_16[3] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 3 * src_stride)); - s_16[4] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 4 * src_stride)); - s_16[5] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 5 * src_stride)); - s_16[6] = _mm_cvtsi32_si128(loadu_int16(src_ptr + 6 * src_stride)); - - const __m128i src01 = _mm_unpacklo_epi16(s_16[0], s_16[1]); - const __m128i src12 = _mm_unpacklo_epi16(s_16[1], s_16[2]); - const __m128i src23 = _mm_unpacklo_epi16(s_16[2], s_16[3]); - const __m128i src34 = _mm_unpacklo_epi16(s_16[3], s_16[4]); - const __m128i src45 = _mm_unpacklo_epi16(s_16[4], s_16[5]); - const __m128i src56 = _mm_unpacklo_epi16(s_16[5], s_16[6]); - - ss_128[0] = _mm_unpacklo_epi8(src01, src12); - ss_128[1] = _mm_unpacklo_epi8(src23, src34); - ss_128[2] = _mm_unpacklo_epi8(src45, src56); - - do { - const __m128i res = y_convolve_8tap_2x2_ssse3( - src_ptr, src_stride, coeffs_128, s_16, ss_128); - const __m128i r = sr_y_round_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - ss_128[0] = ss_128[1]; - ss_128[1] = ss_128[2]; - ss_128[2] = ss_128[3]; - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m128i s_32[8], ss_128[4]; - - assert(w == 4); - - s_32[0] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 0 * src_stride)); - s_32[1] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 1 * src_stride)); - s_32[2] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 2 * src_stride)); - s_32[3] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 3 * src_stride)); - s_32[4] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 4 * src_stride)); - s_32[5] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 5 * src_stride)); - s_32[6] = _mm_cvtsi32_si128(loadu_int32(src_ptr + 6 * src_stride)); - - const __m128i src01 = _mm_unpacklo_epi32(s_32[0], s_32[1]); - const __m128i src12 = _mm_unpacklo_epi32(s_32[1], s_32[2]); - const __m128i src23 = _mm_unpacklo_epi32(s_32[2], s_32[3]); - const __m128i src34 = _mm_unpacklo_epi32(s_32[3], s_32[4]); - const __m128i src45 = _mm_unpacklo_epi32(s_32[4], s_32[5]); - const __m128i src56 = _mm_unpacklo_epi32(s_32[5], s_32[6]); - - ss_128[0] = _mm_unpacklo_epi8(src01, src12); - ss_128[1] = _mm_unpacklo_epi8(src23, src34); - ss_128[2] = _mm_unpacklo_epi8(src45, src56); - - do { - const __m128i res = y_convolve_8tap_4x2_ssse3( - src_ptr, src_stride, coeffs_128, s_32, ss_128); - const __m128i r = sr_y_round_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - ss_128[0] = ss_128[1]; - ss_128[1] = ss_128[2]; - ss_128[2] = ss_128[3]; - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - prepare_half_coeffs_8tap_avx2(filter_params_y, subpel_y_q4, coeffs_256); - - if (w == 8) { - __m128i s_64[8]; - __m256i ss_256[4]; - - s_64[0] = _mm_loadl_epi64((__m128i *)(src_ptr + 0 * src_stride)); - s_64[1] = _mm_loadl_epi64((__m128i *)(src_ptr + 1 * src_stride)); - s_64[2] = _mm_loadl_epi64((__m128i *)(src_ptr + 2 * src_stride)); - s_64[3] = _mm_loadl_epi64((__m128i *)(src_ptr + 3 * src_stride)); - s_64[4] = _mm_loadl_epi64((__m128i *)(src_ptr + 4 * src_stride)); - s_64[5] = _mm_loadl_epi64((__m128i *)(src_ptr + 5 * src_stride)); - s_64[6] = _mm_loadl_epi64((__m128i *)(src_ptr + 6 * src_stride)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src01 = _mm256_setr_m128i(s_64[0], s_64[1]); - const __m256i src12 = _mm256_setr_m128i(s_64[1], s_64[2]); - const __m256i src23 = _mm256_setr_m128i(s_64[2], s_64[3]); - const __m256i src34 = _mm256_setr_m128i(s_64[3], s_64[4]); - const __m256i src45 = _mm256_setr_m128i(s_64[4], s_64[5]); - const __m256i src56 = _mm256_setr_m128i(s_64[5], s_64[6]); - - ss_256[0] = _mm256_unpacklo_epi8(src01, src12); - ss_256[1] = _mm256_unpacklo_epi8(src23, src34); - ss_256[2] = _mm256_unpacklo_epi8(src45, src56); - - y = h; - do { - const __m256i res = y_convolve_8tap_8x2_avx2( - src_ptr, src_stride, coeffs_256, s_64, ss_256); - sr_y_round_store_8x2_avx2(res, dst, dst_stride); - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[2] = ss_256[3]; - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - __m128i s_128[8]; - __m256i ss_256[8], r[2]; - - s_128[0] = _mm_loadu_si128((__m128i *)(src_ptr + 0 * src_stride)); - s_128[1] = _mm_loadu_si128((__m128i *)(src_ptr + 1 * src_stride)); - s_128[2] = _mm_loadu_si128((__m128i *)(src_ptr + 2 * src_stride)); - s_128[3] = _mm_loadu_si128((__m128i *)(src_ptr + 3 * src_stride)); - s_128[4] = _mm_loadu_si128((__m128i *)(src_ptr + 4 * src_stride)); - s_128[5] = _mm_loadu_si128((__m128i *)(src_ptr + 5 * src_stride)); - s_128[6] = _mm_loadu_si128((__m128i *)(src_ptr + 6 * src_stride)); - - // Load lines a and b. Line a to lower 128, line b to upper 128 - const __m256i src01 = _mm256_setr_m128i(s_128[0], s_128[1]); - const __m256i src12 = _mm256_setr_m128i(s_128[1], s_128[2]); - const __m256i src23 = _mm256_setr_m128i(s_128[2], s_128[3]); - const __m256i src34 = _mm256_setr_m128i(s_128[3], s_128[4]); - const __m256i src45 = _mm256_setr_m128i(s_128[4], s_128[5]); - const __m256i src56 = _mm256_setr_m128i(s_128[5], s_128[6]); - - ss_256[0] = _mm256_unpacklo_epi8(src01, src12); - ss_256[1] = _mm256_unpacklo_epi8(src23, src34); - ss_256[2] = _mm256_unpacklo_epi8(src45, src56); - - ss_256[4] = _mm256_unpackhi_epi8(src01, src12); - ss_256[5] = _mm256_unpackhi_epi8(src23, src34); - ss_256[6] = _mm256_unpackhi_epi8(src45, src56); - - y = h; - do { - y_convolve_8tap_16x2_avx2(src_ptr, src_stride, coeffs_256, s_128, - ss_256, r); - sr_y_round_store_16x2_avx2(r, dst, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[2] = ss_256[3]; - - ss_256[4] = ss_256[5]; - ss_256[5] = ss_256[6]; - ss_256[6] = ss_256[7]; - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - __m256i s_256[8], ss_256[8], tt_256[8], r[4]; - - assert(!(w % 32)); - - x = 0; - do { - const uint8_t *s = src_ptr + x; - uint8_t *d = dst + x; - - s_256[0] = _mm256_loadu_si256((__m256i *)(s + 0 * src_stride)); - s_256[1] = _mm256_loadu_si256((__m256i *)(s + 1 * src_stride)); - s_256[2] = _mm256_loadu_si256((__m256i *)(s + 2 * src_stride)); - s_256[3] = _mm256_loadu_si256((__m256i *)(s + 3 * src_stride)); - s_256[4] = _mm256_loadu_si256((__m256i *)(s + 4 * src_stride)); - s_256[5] = _mm256_loadu_si256((__m256i *)(s + 5 * src_stride)); - s_256[6] = _mm256_loadu_si256((__m256i *)(s + 6 * src_stride)); - - ss_256[0] = _mm256_unpacklo_epi8(s_256[0], s_256[1]); - ss_256[1] = _mm256_unpacklo_epi8(s_256[2], s_256[3]); - ss_256[2] = _mm256_unpacklo_epi8(s_256[4], s_256[5]); - ss_256[4] = _mm256_unpackhi_epi8(s_256[0], s_256[1]); - ss_256[5] = _mm256_unpackhi_epi8(s_256[2], s_256[3]); - ss_256[6] = _mm256_unpackhi_epi8(s_256[4], s_256[5]); - - tt_256[0] = _mm256_unpacklo_epi8(s_256[1], s_256[2]); - tt_256[1] = _mm256_unpacklo_epi8(s_256[3], s_256[4]); - tt_256[2] = _mm256_unpacklo_epi8(s_256[5], s_256[6]); - tt_256[4] = _mm256_unpackhi_epi8(s_256[1], s_256[2]); - tt_256[5] = _mm256_unpackhi_epi8(s_256[3], s_256[4]); - tt_256[6] = _mm256_unpackhi_epi8(s_256[5], s_256[6]); - - y = h; - do { - y_convolve_8tap_32x2_avx2(s, src_stride, coeffs_256, s_256, ss_256, - tt_256, r); - sr_y_round_store_32x2_avx2(r, d, dst_stride); - - ss_256[0] = ss_256[1]; - ss_256[1] = ss_256[2]; - ss_256[2] = ss_256[3]; - ss_256[4] = ss_256[5]; - ss_256[5] = ss_256[6]; - ss_256[6] = ss_256[7]; - - tt_256[0] = tt_256[1]; - tt_256[1] = tt_256[2]; - tt_256[2] = tt_256[3]; - tt_256[4] = tt_256[5]; - tt_256[5] = tt_256[6]; - tt_256[6] = tt_256[7]; - s += 2 * src_stride; - d += 2 * dst_stride; - y -= 2; - } while (y); - - x += 32; - } while (x < w); - } - } - } -} - -static inline void sr_x_2tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[1], - uint8_t *const dst) { - __m256i r[2]; - - x_convolve_2tap_32_avx2(src, coeffs, r); - sr_x_round_store_32_avx2(r, dst); -} - -static inline void sr_x_6tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[3], - const __m256i filt[3], - uint8_t *const dst) { - __m256i r[2]; - - x_convolve_6tap_32_avx2(src, coeffs, filt, r); - sr_x_round_store_32_avx2(r, dst); -} - -static AOM_FORCE_INLINE void sr_x_8tap_32_avx2(const uint8_t *const src, - const __m256i coeffs[4], - const __m256i filt[4], - uint8_t *const dst) { - __m256i r[2]; - - x_convolve_8tap_32_avx2(src, coeffs, filt, r); - sr_x_round_store_32_avx2(r, dst); -} - -static AOM_FORCE_INLINE void av1_convolve_x_sr_specialized_avx2( - const uint8_t *src, int32_t src_stride, uint8_t *dst, int32_t dst_stride, - int32_t w, int32_t h, const InterpFilterParams *filter_params_x, - const int32_t subpel_x_q4, ConvolveParams *conv_params) { - int32_t y = h; - __m128i coeffs_128[4]; - __m256i coeffs_256[4]; - - assert(conv_params->round_0 == 3); - assert((FILTER_BITS - conv_params->round_1) >= 0 || - ((conv_params->round_0 + conv_params->round_1) == 2 * FILTER_BITS)); - (void)conv_params; - - const int horz_tap = get_filter_tap(filter_params_x, subpel_x_q4); - - if (horz_tap == 2) { - // horz_filt as 2 tap - const uint8_t *src_ptr = src; - - if (subpel_x_q4 != 8) { - if (w <= 8) { - prepare_half_coeffs_2tap_ssse3(filter_params_x, subpel_x_q4, - coeffs_128); - - if (w == 2) { - do { - const __m128i res = - x_convolve_2tap_2x2_sse4_1(src_ptr, src_stride, coeffs_128); - const __m128i r = sr_x_round_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 4) { - do { - const __m128i res = - x_convolve_2tap_4x2_ssse3(src_ptr, src_stride, coeffs_128); - const __m128i r = sr_x_round_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else { - assert(w == 8); - - do { - __m128i res[2]; - - x_convolve_2tap_8x2_ssse3(src_ptr, src_stride, coeffs_128, res); - res[0] = sr_x_round_sse2(res[0]); - res[1] = sr_x_round_sse2(res[1]); - const __m128i d = _mm_packus_epi16(res[0], res[1]); - _mm_storel_epi64((__m128i *)dst, d); - _mm_storeh_epi64((__m128i *)(dst + dst_stride), d); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } - } else { - prepare_half_coeffs_2tap_avx2(filter_params_x, subpel_x_q4, coeffs_256); - - if (w == 16) { - do { - __m256i r[2]; - - x_convolve_2tap_16x2_avx2(src_ptr, src_stride, coeffs_256, r); - sr_x_round_store_16x2_avx2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - do { - sr_x_2tap_32_avx2(src_ptr, coeffs_256, dst); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else if (w == 64) { - do { - sr_x_2tap_32_avx2(src_ptr + 0 * 32, coeffs_256, dst + 0 * 32); - sr_x_2tap_32_avx2(src_ptr + 1 * 32, coeffs_256, dst + 1 * 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else { - assert(w == 128); - - do { - sr_x_2tap_32_avx2(src_ptr + 0 * 32, coeffs_256, dst + 0 * 32); - sr_x_2tap_32_avx2(src_ptr + 1 * 32, coeffs_256, dst + 1 * 32); - sr_x_2tap_32_avx2(src_ptr + 2 * 32, coeffs_256, dst + 2 * 32); - sr_x_2tap_32_avx2(src_ptr + 3 * 32, coeffs_256, dst + 3 * 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } - } - } else { - // average to get half pel - if (w == 2) { - do { - __m128i s_128; - - s_128 = load_u8_4x2_sse4_1(src_ptr, src_stride); - const __m128i s1 = _mm_srli_si128(s_128, 1); - const __m128i d = _mm_avg_epu8(s_128, s1); - *(uint16_t *)dst = (uint16_t)_mm_cvtsi128_si32(d); - *(uint16_t *)(dst + dst_stride) = _mm_extract_epi16(d, 2); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 4) { - do { - __m128i s_128; - - s_128 = load_u8_8x2_sse2(src_ptr, src_stride); - const __m128i s1 = _mm_srli_si128(s_128, 1); - const __m128i d = _mm_avg_epu8(s_128, s1); - xx_storel_32(dst, d); - *(int32_t *)(dst + dst_stride) = _mm_extract_epi32(d, 2); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 8) { - do { - const __m128i s00 = _mm_loadu_si128((__m128i *)src_ptr); - const __m128i s10 = - _mm_loadu_si128((__m128i *)(src_ptr + src_stride)); - const __m128i s01 = _mm_srli_si128(s00, 1); - const __m128i s11 = _mm_srli_si128(s10, 1); - const __m128i d0 = _mm_avg_epu8(s00, s01); - const __m128i d1 = _mm_avg_epu8(s10, s11); - _mm_storel_epi64((__m128i *)dst, d0); - _mm_storel_epi64((__m128i *)(dst + dst_stride), d1); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - do { - const __m128i s00 = _mm_loadu_si128((__m128i *)src_ptr); - const __m128i s01 = _mm_loadu_si128((__m128i *)(src_ptr + 1)); - const __m128i s10 = - _mm_loadu_si128((__m128i *)(src_ptr + src_stride)); - const __m128i s11 = - _mm_loadu_si128((__m128i *)(src_ptr + src_stride + 1)); - const __m128i d0 = _mm_avg_epu8(s00, s01); - const __m128i d1 = _mm_avg_epu8(s10, s11); - _mm_storeu_si128((__m128i *)dst, d0); - _mm_storeu_si128((__m128i *)(dst + dst_stride), d1); - - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - do { - sr_x_2tap_32_avg_avx2(src_ptr, dst); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else if (w == 64) { - do { - sr_x_2tap_32_avg_avx2(src_ptr + 0 * 32, dst + 0 * 32); - sr_x_2tap_32_avg_avx2(src_ptr + 1 * 32, dst + 1 * 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else { - assert(w == 128); - - do { - sr_x_2tap_32_avg_avx2(src_ptr + 0 * 32, dst + 0 * 32); - sr_x_2tap_32_avg_avx2(src_ptr + 1 * 32, dst + 1 * 32); - sr_x_2tap_32_avg_avx2(src_ptr + 2 * 32, dst + 2 * 32); - sr_x_2tap_32_avg_avx2(src_ptr + 3 * 32, dst + 3 * 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } - } - } else if (horz_tap == 4) { - // horz_filt as 4 tap - const uint8_t *src_ptr = src - 1; - - prepare_half_coeffs_4tap_ssse3(filter_params_x, subpel_x_q4, coeffs_128); - - if (w == 2) { - do { - const __m128i res = - x_convolve_4tap_2x2_ssse3(src_ptr, src_stride, coeffs_128); - const __m128i r = sr_x_round_sse2(res); - pack_store_2x2_sse2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 4) { - do { - const __m128i res = - x_convolve_4tap_4x2_ssse3(src_ptr, src_stride, coeffs_128); - const __m128i r = sr_x_round_sse2(res); - pack_store_4x2_sse2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 8) { - // TODO(chiyotsai@google.com): Reuse the old SIMD code here. Need to - // rewrite this for better performance later. - __m256i filt_256[2]; - prepare_coeffs_lowbd(filter_params_x, subpel_x_q4, coeffs_256); - - filt_256[0] = _mm256_loadu_si256((__m256i const *)filt1_global_avx2); - filt_256[1] = _mm256_loadu_si256((__m256i const *)filt2_global_avx2); - for (int i = 0; i < h; i += 2) { - const __m256i data = _mm256_permute2x128_si256( - _mm256_castsi128_si256( - _mm_loadu_si128((__m128i *)(&src_ptr[i * src_stride]))), - _mm256_castsi128_si256(_mm_loadu_si128( - (__m128i *)(&src_ptr[i * src_stride + src_stride]))), - 0x20); - - __m256i res_16b = convolve_lowbd_x_4tap(data, coeffs_256 + 1, filt_256); - res_16b = sr_x_round_avx2(res_16b); - - __m256i res_8b = _mm256_packus_epi16(res_16b, res_16b); - - const __m128i res_0 = _mm256_castsi256_si128(res_8b); - const __m128i res_1 = _mm256_extracti128_si256(res_8b, 1); - - _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_0); - _mm_storel_epi64((__m128i *)&dst[i * dst_stride + dst_stride], res_1); - } - } else { - assert(!(w % 16)); - // TODO(chiyotsai@google.com): Reuse the old SIMD code here. Need to - // rewrite this for better performance later. - __m256i filt_256[2]; - prepare_coeffs_lowbd(filter_params_x, subpel_x_q4, coeffs_256); - filt_256[0] = _mm256_loadu_si256((__m256i const *)filt1_global_avx2); - filt_256[1] = _mm256_loadu_si256((__m256i const *)filt2_global_avx2); - - for (int i = 0; i < h; ++i) { - for (int j = 0; j < w; j += 16) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15 16 17 - // 18 19 20 21 22 23 - const __m256i data = _mm256_inserti128_si256( - _mm256_loadu_si256((__m256i *)&src_ptr[(i * src_stride) + j]), - _mm_loadu_si128((__m128i *)&src_ptr[(i * src_stride) + (j + 8)]), - 1); - - __m256i res_16b = - convolve_lowbd_x_4tap(data, coeffs_256 + 1, filt_256); - res_16b = sr_x_round_avx2(res_16b); - - /* rounding code */ - // 8 bit conversion and saturation to uint8 - __m256i res_8b = _mm256_packus_epi16(res_16b, res_16b); - - // Store values into the destination buffer - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - res_8b = _mm256_permute4x64_epi64(res_8b, 216); - __m128i res = _mm256_castsi256_si128(res_8b); - _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res); - } - } - } - } else { - __m256i filt_256[4]; - - filt_256[0] = _mm256_loadu_si256((__m256i const *)filt1_global_avx2); - filt_256[1] = _mm256_loadu_si256((__m256i const *)filt2_global_avx2); - filt_256[2] = _mm256_loadu_si256((__m256i const *)filt3_global_avx2); - - if (horz_tap == 6) { - // horz_filt as 6 tap - const uint8_t *src_ptr = src - 2; - - prepare_half_coeffs_6tap_avx2(filter_params_x, subpel_x_q4, coeffs_256); - - if (w == 8) { - do { - const __m256i res = x_convolve_6tap_8x2_avx2(src_ptr, src_stride, - coeffs_256, filt_256); - sr_x_round_store_8x2_avx2(res, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - do { - __m256i r[2]; - - x_convolve_6tap_16x2_avx2(src_ptr, src_stride, coeffs_256, filt_256, - r); - sr_x_round_store_16x2_avx2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - do { - sr_x_6tap_32_avx2(src_ptr, coeffs_256, filt_256, dst); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else if (w == 64) { - do { - sr_x_6tap_32_avx2(src_ptr, coeffs_256, filt_256, dst); - sr_x_6tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, dst + 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else { - assert(w == 128); - - do { - sr_x_6tap_32_avx2(src_ptr, coeffs_256, filt_256, dst); - sr_x_6tap_32_avx2(src_ptr + 1 * 32, coeffs_256, filt_256, - dst + 1 * 32); - sr_x_6tap_32_avx2(src_ptr + 2 * 32, coeffs_256, filt_256, - dst + 2 * 32); - sr_x_6tap_32_avx2(src_ptr + 3 * 32, coeffs_256, filt_256, - dst + 3 * 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } - } else if (horz_tap == 8) { - // horz_filt as 8 tap - const uint8_t *src_ptr = src - 3; - - filt_256[3] = _mm256_loadu_si256((__m256i const *)filt4_global_avx2); - - prepare_half_coeffs_8tap_avx2(filter_params_x, subpel_x_q4, coeffs_256); - - if (w == 8) { - do { - const __m256i res = x_convolve_8tap_8x2_avx2(src_ptr, src_stride, - coeffs_256, filt_256); - sr_x_round_store_8x2_avx2(res, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 16) { - do { - __m256i r[2]; - - x_convolve_8tap_16x2_avx2(src_ptr, src_stride, coeffs_256, filt_256, - r); - sr_x_round_store_16x2_avx2(r, dst, dst_stride); - src_ptr += 2 * src_stride; - dst += 2 * dst_stride; - y -= 2; - } while (y); - } else if (w == 32) { - do { - sr_x_8tap_32_avx2(src_ptr, coeffs_256, filt_256, dst); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else if (w == 64) { - do { - sr_x_8tap_32_avx2(src_ptr, coeffs_256, filt_256, dst); - sr_x_8tap_32_avx2(src_ptr + 32, coeffs_256, filt_256, dst + 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } else { - assert(w == 128); - - do { - sr_x_8tap_32_avx2(src_ptr, coeffs_256, filt_256, dst); - sr_x_8tap_32_avx2(src_ptr + 1 * 32, coeffs_256, filt_256, - dst + 1 * 32); - sr_x_8tap_32_avx2(src_ptr + 2 * 32, coeffs_256, filt_256, - dst + 2 * 32); - sr_x_8tap_32_avx2(src_ptr + 3 * 32, coeffs_256, filt_256, - dst + 3 * 32); - src_ptr += src_stride; - dst += dst_stride; - } while (--y); - } - } - } -} - -#endif // THIRD_PARTY_SVT_AV1_CONVOLVE_AVX2_H_
diff --git a/third_party/SVT-AV1/synonyms.h b/third_party/SVT-AV1/synonyms.h deleted file mode 100644 index 736be4d..0000000 --- a/third_party/SVT-AV1/synonyms.h +++ /dev/null
@@ -1,31 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ - -#ifndef AOM_THIRD_PARTY_SVT_AV1_SYNONYMS_H_ -#define AOM_THIRD_PARTY_SVT_AV1_SYNONYMS_H_ - -#include "aom_dsp/x86/mem_sse2.h" -#include "aom_dsp/x86/synonyms.h" - -static inline __m128i load_u8_8x2_sse2(const uint8_t *const src, - const ptrdiff_t stride) { - return load_8bit_8x2_to_1_reg_sse2(src, (int)(sizeof(*src) * stride)); -} - -static AOM_FORCE_INLINE void store_u8_4x2_sse2(const __m128i src, - uint8_t *const dst, - const ptrdiff_t stride) { - xx_storel_32(dst, src); - *(uint32_t *)(dst + stride) = - ((uint32_t)_mm_extract_epi16(src, 3) << 16) | _mm_extract_epi16(src, 2); -} - -#endif // AOM_THIRD_PARTY_SVT_AV1_SYNONYMS_H_
diff --git a/third_party/fastfeat/LICENSE b/third_party/fastfeat/LICENSE index f347008..ee48fe6 100644 --- a/third_party/fastfeat/LICENSE +++ b/third_party/fastfeat/LICENSE
@@ -13,8 +13,8 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - *Neither the name of the University of Cambridge nor the names of - its contributors may be used to endorse or promote products derived + *Neither the name of the University of Cambridge nor the names of + its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
diff --git a/third_party/fastfeat/README.libaom b/third_party/fastfeat/README.libaom index 75e164f..2aab235 100644 --- a/third_party/fastfeat/README.libaom +++ b/third_party/fastfeat/README.libaom
@@ -1,7 +1,12 @@ +Name: FAST feature detectors in C Version 2.0 URL: https://github.com/edrosten/fast-C-src -Version: 391d5e939eb1545d24c10533d7de424db8d9c191 -License: BSD +Version: N/A +Update Mechanism: Manual +Revision: 391d5e939eb1545d24c10533d7de424db8d9c191 +License: BSD-3-Clause License File: LICENSE +Shipped in Chromium: no +Security Critical: yes Description: Library to compute FAST features with non-maximum suppression.
diff --git a/third_party/googletest/README.libaom b/third_party/googletest/README.libaom index 3b21c8e..150a23f 100644 --- a/third_party/googletest/README.libaom +++ b/third_party/googletest/README.libaom
@@ -1,15 +1,21 @@ +Name: Google Test: Google's C++ Testing Framework +Short Name: googletest URL: https://github.com/google/googletest -Version: release-1.12.1 -License: BSD -License File: LICENSE +Version: 1.17.0 +Update Mechanism: Manual +Revision: 52eb8108c5bdec04579160ae17225d66034bd723 +License: BSD-3-Clause +License File: src/LICENSE +Shipped in Chromium: no +Security Critical: no Description: Google's framework for writing C++ tests on a variety of platforms -(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the -xUnit architecture. Supports automatic test discovery, a rich set of +(Linux, macOS, Windows, Android NDK, etc). Based on the xUnit +testing framework. Supports automatic test discovery, a rich set of assertions, user-defined assertions, death tests, fatal and non-fatal -failures, various options for running the tests, and XML test report -generation. +failures, value-parameterized tests, type-parameterized tests, and +various options for running the tests. Local Modifications: - Remove everything but: @@ -24,9 +30,10 @@ src LICENSE README.md -- In googletest/include/gtest/internal/custom/gtest-port.h, define - GTEST_HAS_NOTIFICATION_ as 1 and use a stub Notification class to fix - the mingw32 g++ compilation errors caused by the lack of std::mutex - and std::condition_variable in the <mutex> and <condition_variable> - headers if mingw32 is configured with the win32 threads option. See +- Apply the patch googletest.patch. The patch defines the Notification + class for MinGW by using a Windows manual-reset event object to fix + the MinGW GCC (version < 13) compilation errors caused by the lack of + std::mutex and std::condition_variable in the <mutex> and + <condition_variable> headers if GCC is configured with the win32 + thread model. See https://stackoverflow.com/questions/17242516/mingw-w64-threads-posix-vs-win32
diff --git a/third_party/googletest/googletest.patch b/third_party/googletest/googletest.patch new file mode 100644 index 0000000..28b1a7f --- /dev/null +++ b/third_party/googletest/googletest.patch
@@ -0,0 +1,89 @@ +diff --git a/googletest/include/gtest/internal/gtest-port.h b/googletest/include/gtest/internal/gtest-port.h +index 25b7d194..4a5cde33 100644 +--- a/googletest/include/gtest/internal/gtest-port.h ++++ b/googletest/include/gtest/internal/gtest-port.h +@@ -1237,9 +1237,6 @@ class GTEST_API_ AutoHandle { + // Nothing to do here. + + #else +-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +-/* class A needs to have dll-interface to be used by clients of class B */) +- + // Allows a controller thread to pause execution of newly created + // threads until notified. Instances of this class must be created + // and destroyed in the controller thread. +@@ -1247,6 +1244,39 @@ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ + // This class is only for testing Google Test's own constructs. Do not + // use it in user tests, either directly or indirectly. + // TODO(b/203539622): Replace unconditionally with absl::Notification. ++#ifdef GTEST_OS_WINDOWS_MINGW ++// GCC version < 13 with the win32 thread model does not provide std::mutex and ++// std::condition_variable in the <mutex> and <condition_variable> headers. So ++// we implement the Notification class using a Windows manual-reset event. See ++// https://gcc.gnu.org/gcc-13/changes.html#windows. ++class GTEST_API_ Notification { ++ public: ++ Notification(); ++ Notification(const Notification&) = delete; ++ Notification& operator=(const Notification&) = delete; ++ ~Notification(); ++ ++ // Notifies all threads created with this notification to start. Must ++ // be called from the controller thread. ++ void Notify(); ++ ++ // Blocks until the controller thread notifies. Must be called from a test ++ // thread. ++ void WaitForNotification(); ++ ++ private: ++ // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to ++ // avoid including <windows.h> in this header file. Including <windows.h> is ++ // undesirable because it defines a lot of symbols and macros that tend to ++ // conflict with client code. This assumption is verified by ++ // WindowsTypesTest.HANDLEIsVoidStar. ++ typedef void* Handle; ++ Handle event_; ++}; ++#else ++GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ ++/* class A needs to have dll-interface to be used by clients of class B */) ++ + class GTEST_API_ Notification { + public: + Notification() : notified_(false) {} +@@ -1274,6 +1304,7 @@ class GTEST_API_ Notification { + bool notified_; + }; + GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 ++#endif // GTEST_OS_WINDOWS_MINGW + #endif // GTEST_HAS_NOTIFICATION_ + + // On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +diff --git a/googletest/src/gtest-port.cc b/googletest/src/gtest-port.cc +index 1038ad7b..53d41d38 100644 +--- a/googletest/src/gtest-port.cc ++++ b/googletest/src/gtest-port.cc +@@ -302,6 +302,22 @@ bool AutoHandle::IsCloseable() const { + return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE; + } + ++#if !GTEST_HAS_NOTIFICATION_ && defined(GTEST_OS_WINDOWS_MINGW) ++Notification::Notification() { ++ // Create a manual-reset event object. ++ event_ = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); ++ GTEST_CHECK_(event_ != nullptr); ++} ++ ++Notification::~Notification() { ::CloseHandle(event_); } ++ ++void Notification::Notify() { GTEST_CHECK_(::SetEvent(event_)); } ++ ++void Notification::WaitForNotification() { ++ GTEST_CHECK_(::WaitForSingleObject(event_, INFINITE) == WAIT_OBJECT_0); ++} ++#endif // !GTEST_HAS_NOTIFICATION_ && defined(GTEST_OS_WINDOWS_MINGW) ++ + Mutex::Mutex() + : owner_thread_id_(0), + type_(kDynamic),
diff --git a/third_party/googletest/src/CMakeLists.txt b/third_party/googletest/src/CMakeLists.txt index 102e28c..0567ae7 100644 --- a/third_party/googletest/src/CMakeLists.txt +++ b/third_party/googletest/src/CMakeLists.txt
@@ -1,18 +1,10 @@ # Note: CMake support is community-based. The maintainers do not use CMake # internally. -cmake_minimum_required(VERSION 3.5) - -if (POLICY CMP0048) - cmake_policy(SET CMP0048 NEW) -endif (POLICY CMP0048) - -if (POLICY CMP0077) - cmake_policy(SET CMP0077 NEW) -endif (POLICY CMP0077) +cmake_minimum_required(VERSION 3.16) project(googletest-distribution) -set(GOOGLETEST_VERSION 1.12.1) +set(GOOGLETEST_VERSION 1.17.0) if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX) set(CMAKE_CXX_EXTENSIONS OFF) @@ -23,9 +15,19 @@ include(CMakeDependentOption) include(GNUInstallDirs) -#Note that googlemock target already builds googletest +# Note that googlemock target already builds googletest. option(BUILD_GMOCK "Builds the googlemock subproject" ON) option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON) +option(GTEST_HAS_ABSL "Use Abseil and RE2. Requires Abseil and RE2 to be separately added to the build." OFF) + +if(GTEST_HAS_ABSL) + if(NOT TARGET absl::base) + find_package(absl REQUIRED) + endif() + if(NOT TARGET re2::re2) + find_package(re2 REQUIRED) + endif() +endif() if(BUILD_GMOCK) add_subdirectory( googlemock )
diff --git a/third_party/googletest/src/CONTRIBUTORS b/third_party/googletest/src/CONTRIBUTORS index 77397a5..ccea41e 100644 --- a/third_party/googletest/src/CONTRIBUTORS +++ b/third_party/googletest/src/CONTRIBUTORS
@@ -55,6 +55,7 @@ Russ Rufer <russ@pentad.com> Sean Mcafee <eefacm@gmail.com> Sigurður Ásgeirsson <siggi@google.com> +Soyeon Kim <sxshx818@naver.com> Sverre Sundsdal <sundsdal@gmail.com> Szymon Sobik <sobik.szymon@gmail.com> Takeshi Yoshino <tyoshino@google.com>
diff --git a/third_party/googletest/src/README.md b/third_party/googletest/src/README.md index 30edaec..598cf31 100644 --- a/third_party/googletest/src/README.md +++ b/third_party/googletest/src/README.md
@@ -2,29 +2,27 @@ ### Announcements -#### Live at Head - -GoogleTest now follows the -[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support). -We recommend -[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it). - #### Documentation Updates Our documentation is now live on GitHub Pages at https://google.github.io/googletest/. We recommend browsing the documentation on GitHub Pages rather than directly in the repository. -#### Release 1.11.0 +#### Release 1.17.0 -[Release 1.11.0](https://github.com/google/googletest/releases/tag/release-1.11.0) -is now available. +[Release 1.17.0](https://github.com/google/googletest/releases/tag/v1.17.0) is +now available. + +The 1.17.x branch [requires at least C++17]((https://opensource.google/documentation/policies/cplusplus-support#c_language_standard). + +#### Continuous Integration + +We use Google's internal systems for continuous integration. #### Coming Soon * We are planning to take a dependency on [Abseil](https://github.com/abseil/abseil-cpp). -* More documentation improvements are planned. ## Welcome to **GoogleTest**, Google's C++ test framework! @@ -43,64 +41,58 @@ ## Features -* An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework. -* Test discovery. -* A rich set of assertions. -* User-defined assertions. -* Death tests. -* Fatal and non-fatal failures. -* Value-parameterized tests. -* Type-parameterized tests. -* Various options for running the tests. -* XML test report generation. +* xUnit test framework: \ + Googletest is based on the [xUnit](https://en.wikipedia.org/wiki/XUnit) + testing framework, a popular architecture for unit testing +* Test discovery: \ + Googletest automatically discovers and runs your tests, eliminating the need + to manually register your tests +* Rich set of assertions: \ + Googletest provides a variety of assertions, such as equality, inequality, + exceptions, and more, making it easy to test your code +* User-defined assertions: \ + You can define your own assertions with Googletest, making it simple to + write tests that are specific to your code +* Death tests: \ + Googletest supports death tests, which verify that your code exits in a + certain way, making it useful for testing error-handling code +* Fatal and non-fatal failures: \ + You can specify whether a test failure should be treated as fatal or + non-fatal with Googletest, allowing tests to continue running even if a + failure occurs +* Value-parameterized tests: \ + Googletest supports value-parameterized tests, which run multiple times with + different input values, making it useful for testing functions that take + different inputs +* Type-parameterized tests: \ + Googletest also supports type-parameterized tests, which run with different + data types, making it useful for testing functions that work with different + data types +* Various options for running tests: \ + Googletest provides many options for running tests including running + individual tests, running tests in a specific order and running tests in + parallel ## Supported Platforms -GoogleTest requires a codebase and compiler compliant with the C++11 standard or -newer. - -The GoogleTest code is officially supported on the following platforms. -Operating systems or tools not listed below are community-supported. For -community-supported platforms, patches that do not complicate the code may be -considered. - -If you notice any problems on your platform, please file an issue on the -[GoogleTest GitHub Issue Tracker](https://github.com/google/googletest/issues). -Pull requests containing fixes are welcome! - -### Operating Systems - -* Linux -* macOS -* Windows - -### Compilers - -* gcc 5.0+ -* clang 5.0+ -* MSVC 2015+ - -**macOS users:** Xcode 9.3+ provides clang 5.0+. - -### Build Systems - -* [Bazel](https://bazel.build/) -* [CMake](https://cmake.org/) - -**Note:** Bazel is the build system used by the team internally and in tests. -CMake is supported on a best-effort basis and by the community. +GoogleTest follows Google's +[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support). +See +[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md) +for a list of currently supported versions of compilers, platforms, and build +tools. ## Who Is Using GoogleTest? In addition to many internal projects at Google, GoogleTest is also used by the following notable projects: -* The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser - and Chrome OS). -* The [LLVM](http://llvm.org/) compiler. +* The [Chromium projects](https://www.chromium.org/) (behind the Chrome + browser and Chrome OS). +* The [LLVM](https://llvm.org/) compiler. * [Protocol Buffers](https://github.com/google/protobuf), Google's data interchange format. -* The [OpenCV](http://opencv.org/) computer vision library. +* The [OpenCV](https://opencv.org/) computer vision library. ## Related Open Source Projects @@ -135,7 +127,7 @@ ## Contributing Changes Please read -[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/master/CONTRIBUTING.md) +[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/main/CONTRIBUTING.md) for details on how to contribute to this project. Happy testing!
diff --git a/third_party/googletest/src/googletest/CMakeLists.txt b/third_party/googletest/src/googletest/CMakeLists.txt index aa00a5f..dce6a7c 100644 --- a/third_party/googletest/src/googletest/CMakeLists.txt +++ b/third_party/googletest/src/googletest/CMakeLists.txt
@@ -5,7 +5,7 @@ # CMake build script for Google Test. # # To run the tests for Google Test itself on Linux, use 'make test' or -# ctest. You can select which tests to run using 'ctest -R regex'. +# ctest. You can select which tests to run using 'ctest -R regex'. # For more options, run 'ctest --help'. # When other libraries are using a shared version of runtime libraries, @@ -35,7 +35,7 @@ ######################################################################## # -# Project-wide settings +# Project-wide settings. # Name of the project. # @@ -44,21 +44,16 @@ # ${gtest_BINARY_DIR}. # Language "C" is required for find_package(Threads). -# Project version: +# Project version. -cmake_minimum_required(VERSION 3.5) -cmake_policy(SET CMP0048 NEW) +cmake_minimum_required(VERSION 3.13) project(gtest VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C) -if (POLICY CMP0063) # Visibility - cmake_policy(SET CMP0063 NEW) -endif (POLICY CMP0063) - if (COMMAND set_up_hermetic_build) set_up_hermetic_build() endif() -# These commands only run if this is the main project +# These commands only run if this is the main project. if(CMAKE_PROJECT_NAME STREQUAL "gtest" OR CMAKE_PROJECT_NAME STREQUAL "googletest-distribution") # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to @@ -88,7 +83,7 @@ config_compiler_and_linker() # Defined in internal_utils.cmake. # Needed to set the namespace for both the export targets and the -# alias libraries +# alias libraries. set(cmake_package_name GTest CACHE INTERNAL "") # Create the CMake package file descriptors. @@ -100,12 +95,14 @@ set(version_file "${generated_dir}/${cmake_package_name}ConfigVersion.cmake") write_basic_package_version_file(${version_file} VERSION ${GOOGLETEST_VERSION} COMPATIBILITY AnyNewerVersion) install(EXPORT ${targets_export_name} + COMPONENT "${PROJECT_NAME}" NAMESPACE ${cmake_package_name}:: DESTINATION ${cmake_files_install_dir}) set(config_file "${generated_dir}/${cmake_package_name}Config.cmake") configure_package_config_file("${gtest_SOURCE_DIR}/cmake/Config.cmake.in" "${config_file}" INSTALL_DESTINATION ${cmake_files_install_dir}) install(FILES ${version_file} ${config_file} + COMPONENT "${PROJECT_NAME}" DESTINATION ${cmake_files_install_dir}) endif() @@ -117,44 +114,55 @@ ######################################################################## # -# Defines the gtest & gtest_main libraries. User tests should link +# Defines the gtest & gtest_main libraries. User tests should link # with one of them. -# Google Test libraries. We build them using more strict warnings than what +# Google Test libraries. We build them using more strict warnings than what # are used for other targets, to ensure that gtest can be compiled by a user # aggressive about warnings. cxx_library(gtest "${cxx_strict}" src/gtest-all.cc) set_target_properties(gtest PROPERTIES VERSION ${GOOGLETEST_VERSION}) +if(GTEST_HAS_ABSL) + target_compile_definitions(gtest PUBLIC GTEST_HAS_ABSL=1) + target_link_libraries(gtest PUBLIC + absl::failure_signal_handler + absl::stacktrace + absl::symbolize + absl::flags_parse + absl::flags_reflection + absl::flags_usage + absl::strings + absl::any + absl::optional + absl::variant + re2::re2 + ) +endif() cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc) set_target_properties(gtest_main PROPERTIES VERSION ${GOOGLETEST_VERSION}) -# If the CMake version supports it, attach header directory information -# to the targets for when we are part of a parent build (ie being pulled -# in via add_subdirectory() rather than being a standalone build). -if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") - string(REPLACE ";" "$<SEMICOLON>" dirs "${gtest_build_include_dirs}") - target_include_directories(gtest SYSTEM INTERFACE - "$<BUILD_INTERFACE:${dirs}>" - "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>") - target_include_directories(gtest_main SYSTEM INTERFACE - "$<BUILD_INTERFACE:${dirs}>" - "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>") -endif() -if(CMAKE_SYSTEM_NAME MATCHES "QNX") +string(REPLACE ";" "$<SEMICOLON>" dirs "${gtest_build_include_dirs}") +target_include_directories(gtest SYSTEM INTERFACE + "$<BUILD_INTERFACE:${dirs}>" + "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>") +target_include_directories(gtest_main SYSTEM INTERFACE + "$<BUILD_INTERFACE:${dirs}>" + "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>") +if(CMAKE_SYSTEM_NAME MATCHES "QNX" AND CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL 7.1) target_link_libraries(gtest PUBLIC regex) endif() target_link_libraries(gtest_main PUBLIC gtest) ######################################################################## # -# Install rules +# Install rules. install_project(gtest gtest_main) ######################################################################## # # Samples on how to link user tests with gtest or gtest_main. # -# They are not built by default. To build them, set the -# gtest_build_samples option to ON. You can do it by running ccmake +# They are not built by default. To build them, set the +# gtest_build_samples option to ON. You can do it by running ccmake # or specifying the -Dgtest_build_samples=ON flag when running cmake. if (gtest_build_samples) @@ -177,8 +185,8 @@ # You can skip this section if you aren't interested in testing # Google Test itself. # -# The tests are not built by default. To build them, set the -# gtest_build_tests option to ON. You can do it by running ccmake +# The tests are not built by default. To build them, set the +# gtest_build_tests option to ON. You can do it by running ccmake # or specifying the -Dgtest_build_tests=ON flag when running cmake. if (gtest_build_tests) @@ -260,7 +268,7 @@ py_test(gtest_skip_environment_check_output_test) # Visual Studio .NET 2003 does not support STL with exceptions disabled. - if (NOT MSVC OR MSVC_VERSION GREATER 1310) # 1310 is Visual Studio .NET 2003 + if (NOT MSVC OR MSVC_VERSION GREATER 1310) # 1310 is Visual Studio .NET 2003 cxx_executable_with_flags( googletest-catch-exceptions-no-ex-test_ "${cxx_no_exception}"
diff --git a/third_party/googletest/src/googletest/README.md b/third_party/googletest/src/googletest/README.md index d26b309..a760759 100644 --- a/third_party/googletest/src/googletest/README.md +++ b/third_party/googletest/src/googletest/README.md
@@ -9,10 +9,10 @@ ### Build with CMake GoogleTest comes with a CMake build script -([CMakeLists.txt](https://github.com/google/googletest/blob/master/CMakeLists.txt)) +([CMakeLists.txt](https://github.com/google/googletest/blob/main/CMakeLists.txt)) that can be used on a wide range of platforms ("C" stands for cross-platform.). If you don't have CMake installed already, you can download it for free from -<http://www.cmake.org/>. +<https://cmake.org/>. CMake works by generating native makefiles or build projects that can be used in the compiler environment of your choice. You can either build GoogleTest as a @@ -25,7 +25,7 @@ with ``` -git clone https://github.com/google/googletest.git -b release-1.11.0 +git clone https://github.com/google/googletest.git -b v1.17.0 cd googletest # Main directory of the cloned repository. mkdir build # Create a directory to hold the build output. cd build @@ -94,7 +94,7 @@ FetchContent_Declare( googletest # Specify the commit you depend on and update it regularly. - URL https://github.com/google/googletest/archive/e2239ee6043f73722e7aa812a459f54a28552929.zip + URL https://github.com/google/googletest/archive/5376968f6948923e2411081fd9372e71a59d8e77.zip ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) @@ -124,12 +124,12 @@ #### C++ Standard Version -An environment that supports C++11 is required in order to successfully build +An environment that supports C++17 is required in order to successfully build GoogleTest. One way to ensure this is to specify the standard in the top-level -project, for example by using the `set(CMAKE_CXX_STANDARD 11)` command. If this -is not feasible, for example in a C project using GoogleTest for validation, -then it can be specified by adding it to the options for cmake via the -`DCMAKE_CXX_FLAGS` option. +project, for example by using the `set(CMAKE_CXX_STANDARD 17)` command along +with `set(CMAKE_CXX_STANDARD_REQUIRED ON)`. If this is not feasible, for example +in a C project using GoogleTest for validation, then it can be specified by +adding it to the options for cmake via the`-DCMAKE_CXX_FLAGS` option. ### Tweaking GoogleTest @@ -140,23 +140,27 @@ them to either 1 or 0 to enable or disable a certain feature. We list the most frequently used macros below. For a complete list, see file -[include/gtest/internal/gtest-port.h](https://github.com/google/googletest/blob/master/googletest/include/gtest/internal/gtest-port.h). +[include/gtest/internal/gtest-port.h](https://github.com/google/googletest/blob/main/googletest/include/gtest/internal/gtest-port.h). ### Multi-threaded Tests GoogleTest is thread-safe where the pthread library is available. After -`#include "gtest/gtest.h"`, you can check the +`#include <gtest/gtest.h>`, you can check the `GTEST_IS_THREADSAFE` macro to see whether this is the case (yes if the macro is `#defined` to 1, no if it's undefined.). If GoogleTest doesn't correctly detect whether pthread is available in your environment, you can force it with - -DGTEST_HAS_PTHREAD=1 +``` +-DGTEST_HAS_PTHREAD=1 +``` or - -DGTEST_HAS_PTHREAD=0 +``` +-DGTEST_HAS_PTHREAD=0 +``` When GoogleTest uses pthread, you may need to add flags to your compiler and/or linker to select the pthread library, or you'll get link errors. If you use the @@ -172,23 +176,27 @@ To compile *gtest* as a shared library, add - -DGTEST_CREATE_SHARED_LIBRARY=1 +``` +-DGTEST_CREATE_SHARED_LIBRARY=1 +``` to the compiler flags. You'll also need to tell the linker to produce a shared library instead - consult your linker's manual for how to do it. To compile your *tests* that use the gtest shared library, add - -DGTEST_LINKED_AS_SHARED_LIBRARY=1 +``` +-DGTEST_LINKED_AS_SHARED_LIBRARY=1 +``` to the compiler flags. Note: while the above steps aren't technically necessary today when using some compilers (e.g. GCC), they may become necessary in the future, if we decide to improve the speed of loading the library (see -<http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are recommended -to always add the above flags when using GoogleTest as a shared library. -Otherwise a future release of GoogleTest may break your build script. +<https://gcc.gnu.org/wiki/Visibility> for details). Therefore you are +recommended to always add the above flags when using GoogleTest as a shared +library. Otherwise a future release of GoogleTest may break your build script. ### Avoiding Macro Name Clashes @@ -200,7 +208,9 @@ Specifically, if both GoogleTest and some other code define macro FOO, you can add - -DGTEST_DONT_DEFINE_FOO=1 +``` +-DGTEST_DONT_DEFINE_FOO=1 +``` to the compiler flags to tell GoogleTest to change the macro's name from `FOO` to `GTEST_FOO`. Currently `FOO` can be `ASSERT_EQ`, `ASSERT_FALSE`, `ASSERT_GE`, @@ -208,10 +218,14 @@ `EXPECT_FALSE`, `EXPECT_TRUE`, `FAIL`, `SUCCEED`, `TEST`, or `TEST_F`. For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll need to write - GTEST_TEST(SomeTest, DoesThis) { ... } +``` +GTEST_TEST(SomeTest, DoesThis) { ... } +``` instead of - TEST(SomeTest, DoesThis) { ... } +``` +TEST(SomeTest, DoesThis) { ... } +``` in order to define a test.
diff --git a/third_party/googletest/src/googletest/cmake/Config.cmake.in b/third_party/googletest/src/googletest/cmake/Config.cmake.in index 12be449..3f70661 100644 --- a/third_party/googletest/src/googletest/cmake/Config.cmake.in +++ b/third_party/googletest/src/googletest/cmake/Config.cmake.in
@@ -4,6 +4,10 @@ set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@) find_dependency(Threads) endif() +if (@GTEST_HAS_ABSL@) + find_dependency(absl) + find_dependency(re2) +endif() include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") check_required_components("@project_name@")
diff --git a/third_party/googletest/src/googletest/cmake/internal_utils.cmake b/third_party/googletest/src/googletest/cmake/internal_utils.cmake index 5a34c07..7ca256a 100644 --- a/third_party/googletest/src/googletest/cmake/internal_utils.cmake +++ b/third_party/googletest/src/googletest/cmake/internal_utils.cmake
@@ -12,17 +12,14 @@ # Test and Google Mock's option() definitions, and thus must be # called *after* the options have been defined. -if (POLICY CMP0054) - cmake_policy(SET CMP0054 NEW) -endif (POLICY CMP0054) - # Tweaks CMake's default compiler/linker settings to suit Google Test's needs. # # This must be a macro(), as inside a function string() can only # update variables in the function scope. macro(fix_default_compiler_settings_) - if (MSVC) - # For MSVC, CMake sets certain flags to defaults we want to override. + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC|Clang") + # For MSVC and Clang, CMake sets certain flags to defaults we want to + # override. # This replacement code is taken from sample in the CMake Wiki at # https://gitlab.kitware.com/cmake/community/wikis/FAQ#dynamic-replace. foreach (flag_var @@ -32,13 +29,17 @@ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt) # When Google Test is built as a shared library, it should also use - # shared runtime libraries. Otherwise, it may end up with multiple + # shared runtime libraries. Otherwise, it may end up with multiple # copies of runtime library data in different modules, resulting in # hard-to-find crashes. When it is built as a static library, it is # preferable to use CRT as static libraries, as we don't have to rely # on CRT DLLs being available. CMake always defaults to using shared # CRT libraries, so we override that default here. string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}") + + # When using Ninja with Clang, static builds pass -D_DLL on Windows. + # This is incorrect and should not happen, so we fix that here. + string(REPLACE "-D_DLL" "" ${flag_var} "${${flag_var}}") endif() # We prefer more strict warning checking for building Google Test. @@ -54,11 +55,11 @@ endmacro() # Defines the compiler/linker flags used to build Google Test and -# Google Mock. You can tweak these definitions to suit your need. A +# Google Mock. You can tweak these definitions to suit your need. A # variable's value is empty before it's explicitly assigned to. macro(config_compiler_and_linker) # Note: pthreads on MinGW is not supported, even if available - # instead, we use windows threading primitives + # instead, we use windows threading primitives. unset(GTEST_HAS_PTHREAD) if (NOT gtest_disable_pthreads AND NOT MINGW) # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT. @@ -78,26 +79,38 @@ set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") set(cxx_no_exception_flags "-EHs-c- -D_HAS_EXCEPTIONS=0") set(cxx_no_rtti_flags "-GR-") - # Suppress "unreachable code" warning - # http://stackoverflow.com/questions/3232669 explains the issue. + # Suppress "unreachable code" warning, + # https://stackoverflow.com/questions/3232669 explains the issue. set(cxx_base_flags "${cxx_base_flags} -wd4702") # Ensure MSVC treats source files as UTF-8 encoded. - set(cxx_base_flags "${cxx_base_flags} -utf-8") - elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(cxx_base_flags "-Wall -Wshadow -Wconversion") + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(cxx_base_flags "${cxx_base_flags} -utf-8") + endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(cxx_base_flags "${cxx_base_flags} /fp:precise -Wno-inconsistent-missing-override -Wno-microsoft-exception-spec -Wno-unused-function -Wno-unused-but-set-variable") + endif() + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(cxx_base_flags "-Wall -Wshadow -Wconversion -Wundef") set(cxx_exception_flags "-fexceptions") set(cxx_no_exception_flags "-fno-exceptions") - set(cxx_strict_flags "-W -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wunused-parameter -Wcast-align -Wchar-subscripts -Winline -Wredundant-decls") + set(cxx_strict_flags "-W -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wunused-parameter -Wcast-align -Winline -Wredundant-decls") set(cxx_no_rtti_flags "-fno-rtti") + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(cxx_strict_flags "${cxx_strict_flags} -Wchar-subscripts") + endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(cxx_base_flags "${cxx_base_flags} -Wno-implicit-float-size-conversion -ffp-model=precise") + endif() elseif (CMAKE_COMPILER_IS_GNUCXX) - set(cxx_base_flags "-Wall -Wshadow") + set(cxx_base_flags "-Wall -Wshadow -Wundef") if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0) set(cxx_base_flags "${cxx_base_flags} -Wno-error=dangling-else") endif() set(cxx_exception_flags "-fexceptions") set(cxx_no_exception_flags "-fno-exceptions") # Until version 4.3.2, GCC doesn't define a macro to indicate - # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI + # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI # explicitly. set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0") set(cxx_strict_flags @@ -114,7 +127,7 @@ set(cxx_exception_flags "-qeh") set(cxx_no_exception_flags "-qnoeh") # Until version 9.0, Visual Age doesn't define a macro to indicate - # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI + # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI # explicitly. set(cxx_no_rtti_flags "-qnortti -DGTEST_HAS_RTTI=0") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "HP") @@ -144,7 +157,7 @@ set(cxx_strict "${cxx_default} ${cxx_strict_flags}") endmacro() -# Defines the gtest & gtest_main libraries. User tests should link +# Defines the gtest & gtest_main libraries. User tests should link # with one of them. function(cxx_library_with_type name type cxx_flags) # type can be either STATIC or SHARED to denote a static or shared library. @@ -154,14 +167,15 @@ set_target_properties(${name} PROPERTIES COMPILE_FLAGS "${cxx_flags}") - # Set the output directory for build artifacts + # Set the output directory for build artifacts. set_target_properties(${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" - PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - # make PDBs match library name + PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" + COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + # Make PDBs match library name. get_target_property(pdb_debug_postfix ${name} DEBUG_POSTFIX) set_target_properties(${name} PROPERTIES @@ -174,23 +188,14 @@ set_target_properties(${name} PROPERTIES COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1") - if (NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") - target_compile_definitions(${name} INTERFACE - $<INSTALL_INTERFACE:GTEST_LINKED_AS_SHARED_LIBRARY=1>) - endif() + target_compile_definitions(${name} INTERFACE + $<INSTALL_INTERFACE:GTEST_LINKED_AS_SHARED_LIBRARY=1>) endif() if (DEFINED GTEST_HAS_PTHREAD) - if ("${CMAKE_VERSION}" VERSION_LESS "3.1.0") - set(threads_spec ${CMAKE_THREAD_LIBS_INIT}) - else() - set(threads_spec Threads::Threads) - endif() - target_link_libraries(${name} PUBLIC ${threads_spec}) + target_link_libraries(${name} PUBLIC Threads::Threads) endif() - if (NOT "${CMAKE_VERSION}" VERSION_LESS "3.8") - target_compile_features(${name} PUBLIC cxx_std_11) - endif() + target_compile_features(${name} PUBLIC cxx_std_17) endfunction() ######################################################################## @@ -207,7 +212,7 @@ # cxx_executable_with_flags(name cxx_flags libs srcs...) # -# creates a named C++ executable that depends on the given libraries and +# Creates a named C++ executable that depends on the given libraries and # is built from the given source files with the given compiler flags. function(cxx_executable_with_flags name cxx_flags libs) add_executable(${name} ${ARGN}) @@ -234,26 +239,21 @@ # cxx_executable(name dir lib srcs...) # -# creates a named target that depends on the given libs and is built -# from the given source files. dir/name.cc is implicitly included in +# Creates a named target that depends on the given libs and is built +# from the given source files. dir/name.cc is implicitly included in # the source file list. function(cxx_executable name dir libs) cxx_executable_with_flags( ${name} "${cxx_default}" "${libs}" "${dir}/${name}.cc" ${ARGN}) endfunction() -# Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE. -if ("${CMAKE_VERSION}" VERSION_LESS "3.12.0") - find_package(PythonInterp) -else() - find_package(Python COMPONENTS Interpreter) - set(PYTHONINTERP_FOUND ${Python_Interpreter_FOUND}) - set(PYTHON_EXECUTABLE ${Python_EXECUTABLE}) +if(gtest_build_tests) + find_package(Python3) endif() # cxx_test_with_flags(name cxx_flags libs srcs...) # -# creates a named C++ test that depends on the given libs and is built +# Creates a named C++ test that depends on the given libs and is built # from the given source files with the given compiler flags. function(cxx_test_with_flags name cxx_flags libs) cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN}) @@ -262,8 +262,8 @@ # cxx_test(name libs srcs...) # -# creates a named test target that depends on the given libs and is -# built from the given source files. Unlike cxx_test_with_flags, +# Creates a named test target that depends on the given libs and is +# built from the given source files. Unlike cxx_test_with_flags, # test/name.cc is already implicitly included in the source file list. function(cxx_test name libs) cxx_test_with_flags("${name}" "${cxx_default}" "${libs}" @@ -272,37 +272,25 @@ # py_test(name) # -# creates a Python test with the given name whose main module is in -# test/name.py. It does nothing if Python is not installed. +# Creates a Python test with the given name whose main module is in +# test/name.py. It does nothing if Python is not installed. function(py_test name) - if (PYTHONINTERP_FOUND) - if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 3.1) - if (CMAKE_CONFIGURATION_TYPES) - # Multi-configuration build generators as for Visual Studio save - # output in a subdirectory of CMAKE_CURRENT_BINARY_DIR (Debug, - # Release etc.), so we have to provide it here. - add_test(NAME ${name} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py - --build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG> ${ARGN}) - else (CMAKE_CONFIGURATION_TYPES) - # Single-configuration build generators like Makefile generators - # don't have subdirs below CMAKE_CURRENT_BINARY_DIR. - add_test(NAME ${name} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py - --build_dir=${CMAKE_CURRENT_BINARY_DIR} ${ARGN}) - endif (CMAKE_CONFIGURATION_TYPES) - else() - # ${CMAKE_CURRENT_BINARY_DIR} is known at configuration time, so we can - # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known - # only at ctest runtime (by calling ctest -c <Configuration>), so - # we have to escape $ to delay variable substitution here. - add_test(NAME ${name} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py - --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN}) - endif() - # Make the Python import path consistent between Bazel and CMake. - set_tests_properties(${name} PROPERTIES ENVIRONMENT PYTHONPATH=${CMAKE_SOURCE_DIR}) - endif(PYTHONINTERP_FOUND) + if (NOT Python3_Interpreter_FOUND) + return() + endif() + + get_cmake_property(is_multi "GENERATOR_IS_MULTI_CONFIG") + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}") + if (is_multi) + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>") + endif() + + add_test(NAME ${name} + COMMAND Python3::Interpreter ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py + --build_dir=${build_dir} ${ARGN}) + + # Make the Python import path consistent between Bazel and CMake. + set_tests_properties(${name} PROPERTIES ENVIRONMENT PYTHONPATH=${CMAKE_SOURCE_DIR}) endfunction() # install_project(targets...) @@ -311,21 +299,24 @@ function(install_project) if(INSTALL_GTEST) install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" + COMPONENT "${PROJECT_NAME}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") # Install the project targets. install(TARGETS ${ARGN} EXPORT ${targets_export_name} + COMPONENT "${PROJECT_NAME}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - # Install PDBs + # Install PDBs. foreach(t ${ARGN}) get_target_property(t_pdb_name ${t} COMPILE_PDB_NAME) get_target_property(t_pdb_name_debug ${t} COMPILE_PDB_NAME_DEBUG) get_target_property(t_pdb_output_directory ${t} PDB_OUTPUT_DIRECTORY) install(FILES "${t_pdb_output_directory}/\${CMAKE_INSTALL_CONFIG_NAME}/$<$<CONFIG:Debug>:${t_pdb_name_debug}>$<$<NOT:$<CONFIG:Debug>>:${t_pdb_name}>.pdb" + COMPONENT "${PROJECT_NAME}" DESTINATION ${CMAKE_INSTALL_LIBDIR} OPTIONAL) endforeach() @@ -336,6 +327,7 @@ configure_file("${PROJECT_SOURCE_DIR}/cmake/${t}.pc.in" "${configured_pc}" @ONLY) install(FILES "${configured_pc}" + COMPONENT "${PROJECT_NAME}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endforeach() endif()
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-assertion-result.h b/third_party/googletest/src/googletest/include/gtest/gtest-assertion-result.h index addbb59..954e7c4 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-assertion-result.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-assertion-result.h
@@ -130,6 +130,13 @@ // Expected: Foo() is even // Actual: it's 5 // + +// Returned AssertionResult objects may not be ignored. +// Note: Disabled for SWIG as it doesn't parse attributes correctly. +#if !defined(SWIG) +class [[nodiscard]] AssertionResult; +#endif // !SWIG + class GTEST_API_ AssertionResult { public: // Copy constructor. @@ -181,7 +188,7 @@ // assertion's expectation). When nothing has been streamed into the // object, returns an empty string. const char* message() const { - return message_.get() != nullptr ? message_->c_str() : ""; + return message_ != nullptr ? message_->c_str() : ""; } // Deprecated; please use message() instead. const char* failure_message() const { return message(); } @@ -204,7 +211,7 @@ private: // Appends the contents of message to message_. void AppendMessage(const Message& a_message) { - if (message_.get() == nullptr) message_.reset(new ::std::string); + if (message_ == nullptr) message_ = ::std::make_unique<::std::string>(); message_->append(a_message.GetString().c_str()); }
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-death-test.h b/third_party/googletest/src/googletest/include/gtest/gtest-death-test.h index 84e5a5b..3c61909 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-death-test.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-death-test.h
@@ -51,7 +51,7 @@ namespace testing { -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST namespace internal { @@ -203,7 +203,7 @@ const int exit_code_; }; -#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +#if !defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_FUCHSIA) // Tests that an exit code describes an exit due to termination by a // given signal. class GTEST_API_ KilledBySignal { @@ -293,8 +293,8 @@ // statement is compiled but not executed, to ensure that // EXPECT_DEATH_IF_SUPPORTED compiles with a certain // parameter if and only if EXPECT_DEATH compiles with it. -// regex - A regex that a macro such as EXPECT_DEATH would use to test -// the output of statement. This parameter has to be +// regex_or_matcher - A regex that a macro such as EXPECT_DEATH would use +// to test the output of statement. This parameter has to be // compiled but not evaluated by this macro, to ensure that // this macro only accepts expressions that a macro such as // EXPECT_DEATH would accept. @@ -311,13 +311,13 @@ // statement unconditionally returns or throws. The Message constructor at // the end allows the syntax of streaming additional messages into the // macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. -#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ +#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex_or_matcher, terminator) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (::testing::internal::AlwaysTrue()) { \ GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \ << "Statement '" #statement "' cannot be verified."; \ } else if (::testing::internal::AlwaysFalse()) { \ - ::testing::internal::RE::PartialMatch(".*", (regex)); \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ terminator; \ } else \ @@ -328,7 +328,7 @@ // death tests are supported; otherwise they just issue a warning. This is // useful when you are combining death test assertions with normal test // assertions in one test. -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST #define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ EXPECT_DEATH(statement, regex) #define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-matchers.h b/third_party/googletest/src/googletest/include/gtest/gtest-matchers.h index bffa00c..78160f0 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-matchers.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-matchers.h
@@ -40,6 +40,7 @@ #define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ #include <atomic> +#include <functional> #include <memory> #include <ostream> #include <string> @@ -66,10 +67,10 @@ // To implement a matcher Foo for type T, define: // 1. a class FooMatcherMatcher that implements the matcher interface: // using is_gtest_matcher = void; -// bool MatchAndExplain(const T&, std::ostream*); +// bool MatchAndExplain(const T&, std::ostream*) const; // (MatchResultListener* can also be used instead of std::ostream*) -// void DescribeTo(std::ostream*); -// void DescribeNegationTo(std::ostream*); +// void DescribeTo(std::ostream*) const; +// void DescribeNegationTo(std::ostream*) const; // // 2. a factory function that creates a Matcher<T> object from a // FooMatcherMatcher. @@ -106,13 +107,13 @@ MatchResultListener& operator=(const MatchResultListener&) = delete; }; -inline MatchResultListener::~MatchResultListener() {} +inline MatchResultListener::~MatchResultListener() = default; // An instance of a subclass of this knows how to describe itself as a // matcher. class GTEST_API_ MatcherDescriberInterface { public: - virtual ~MatcherDescriberInterface() {} + virtual ~MatcherDescriberInterface() = default; // Describes this matcher to an ostream. The function should print // a verb phrase that describes the property a value matching this @@ -178,43 +179,6 @@ namespace internal { -struct AnyEq { - template <typename A, typename B> - bool operator()(const A& a, const B& b) const { - return a == b; - } -}; -struct AnyNe { - template <typename A, typename B> - bool operator()(const A& a, const B& b) const { - return a != b; - } -}; -struct AnyLt { - template <typename A, typename B> - bool operator()(const A& a, const B& b) const { - return a < b; - } -}; -struct AnyGt { - template <typename A, typename B> - bool operator()(const A& a, const B& b) const { - return a > b; - } -}; -struct AnyLe { - template <typename A, typename B> - bool operator()(const A& a, const B& b) const { - return a <= b; - } -}; -struct AnyGe { - template <typename A, typename B> - bool operator()(const A& a, const B& b) const { - return a >= b; - } -}; - // A match result listener that ignores the explanation. class DummyMatchResultListener : public MatchResultListener { public: @@ -530,7 +494,7 @@ class GTEST_API_ Matcher<const std::string&> : public internal::MatcherBase<const std::string&> { public: - Matcher() {} + Matcher() = default; explicit Matcher(const MatcherInterface<const std::string&>* impl) : internal::MatcherBase<const std::string&>(impl) {} @@ -552,7 +516,7 @@ class GTEST_API_ Matcher<std::string> : public internal::MatcherBase<std::string> { public: - Matcher() {} + Matcher() = default; explicit Matcher(const MatcherInterface<const std::string&>* impl) : internal::MatcherBase<std::string>(impl) {} @@ -580,7 +544,7 @@ class GTEST_API_ Matcher<const internal::StringView&> : public internal::MatcherBase<const internal::StringView&> { public: - Matcher() {} + Matcher() = default; explicit Matcher(const MatcherInterface<const internal::StringView&>* impl) : internal::MatcherBase<const internal::StringView&>(impl) {} @@ -606,7 +570,7 @@ class GTEST_API_ Matcher<internal::StringView> : public internal::MatcherBase<internal::StringView> { public: - Matcher() {} + Matcher() = default; explicit Matcher(const MatcherInterface<const internal::StringView&>* impl) : internal::MatcherBase<internal::StringView>(impl) {} @@ -758,50 +722,53 @@ }; template <typename Rhs> -class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> { +class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, std::equal_to<>> { public: explicit EqMatcher(const Rhs& rhs) - : ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) {} + : ComparisonBase<EqMatcher<Rhs>, Rhs, std::equal_to<>>(rhs) {} static const char* Desc() { return "is equal to"; } static const char* NegatedDesc() { return "isn't equal to"; } }; template <typename Rhs> -class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> { +class NeMatcher + : public ComparisonBase<NeMatcher<Rhs>, Rhs, std::not_equal_to<>> { public: explicit NeMatcher(const Rhs& rhs) - : ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) {} + : ComparisonBase<NeMatcher<Rhs>, Rhs, std::not_equal_to<>>(rhs) {} static const char* Desc() { return "isn't equal to"; } static const char* NegatedDesc() { return "is equal to"; } }; template <typename Rhs> -class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> { +class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, std::less<>> { public: explicit LtMatcher(const Rhs& rhs) - : ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) {} + : ComparisonBase<LtMatcher<Rhs>, Rhs, std::less<>>(rhs) {} static const char* Desc() { return "is <"; } static const char* NegatedDesc() { return "isn't <"; } }; template <typename Rhs> -class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> { +class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, std::greater<>> { public: explicit GtMatcher(const Rhs& rhs) - : ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) {} + : ComparisonBase<GtMatcher<Rhs>, Rhs, std::greater<>>(rhs) {} static const char* Desc() { return "is >"; } static const char* NegatedDesc() { return "isn't >"; } }; template <typename Rhs> -class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> { +class LeMatcher + : public ComparisonBase<LeMatcher<Rhs>, Rhs, std::less_equal<>> { public: explicit LeMatcher(const Rhs& rhs) - : ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) {} + : ComparisonBase<LeMatcher<Rhs>, Rhs, std::less_equal<>>(rhs) {} static const char* Desc() { return "is <="; } static const char* NegatedDesc() { return "isn't <="; } }; template <typename Rhs> -class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> { +class GeMatcher + : public ComparisonBase<GeMatcher<Rhs>, Rhs, std::greater_equal<>> { public: explicit GeMatcher(const Rhs& rhs) - : ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) {} + : ComparisonBase<GeMatcher<Rhs>, Rhs, std::greater_equal<>>(rhs) {} static const char* Desc() { return "is >="; } static const char* NegatedDesc() { return "isn't >="; } }; @@ -842,7 +809,7 @@ template <class MatcheeStringType> bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { - const std::string& s2(s); + const std::string s2(s); return full_match_ ? RE::FullMatch(s2, *regex_) : RE::PartialMatch(s2, *regex_); }
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-message.h b/third_party/googletest/src/googletest/include/gtest/gtest-message.h index 6c8bf90..448ac6b 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-message.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-message.h
@@ -50,10 +50,19 @@ #include <limits> #include <memory> +#include <ostream> #include <sstream> +#include <string> #include "gtest/internal/gtest-port.h" +#ifdef GTEST_HAS_ABSL +#include <type_traits> + +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ /* class A needs to have dll-interface to be used by clients of class B */) @@ -109,8 +118,17 @@ *ss_ << str; } - // Streams a non-pointer value to this object. - template <typename T> + // Streams a non-pointer value to this object. If building a version of + // GoogleTest with ABSL, this overload is only enabled if the value does not + // have an AbslStringify definition. + template < + typename T +#ifdef GTEST_HAS_ABSL + , + typename std::enable_if<!absl::HasAbslStringify<T>::value, // NOLINT + int>::type = 0 +#endif // GTEST_HAS_ABSL + > inline Message& operator<<(const T& val) { // Some libraries overload << for STL containers. These // overloads are defined in the global namespace instead of ::std. @@ -131,6 +149,21 @@ return *this; } +#ifdef GTEST_HAS_ABSL + // Streams a non-pointer value with an AbslStringify definition to this + // object. + template <typename T, + typename std::enable_if<absl::HasAbslStringify<T>::value, // NOLINT + int>::type = 0> + inline Message& operator<<(const T& val) { + // ::operator<< is needed here for a similar reason as with the non-Abseil + // version above + using ::operator<<; + *ss_ << absl::StrCat(val); + return *this; + } +#endif // GTEST_HAS_ABSL + // Streams a pointer value to this object. // // This function is an overload of the previous one. When you
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-param-test.h b/third_party/googletest/src/googletest/include/gtest/gtest-param-test.h index b55119a..9e023f9 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-param-test.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-param-test.h
@@ -174,11 +174,12 @@ #endif // 0 +#include <functional> #include <iterator> #include <utility> #include "gtest/internal/gtest-internal.h" -#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-param-util.h" // IWYU pragma: export #include "gtest/internal/gtest-port.h" namespace testing { @@ -407,9 +408,106 @@ return internal::CartesianProductHolder<Generator...>(g...); } +// ConvertGenerator() wraps a parameter generator in order to cast each produced +// value through a known type before supplying it to the test suite +// +// Synopsis: +// ConvertGenerator<T>(gen) +// - returns a generator producing the same elements as generated by gen, but +// each T-typed element is static_cast to a type deduced from the interface +// that accepts this generator, and then returned +// +// It is useful when using the Combine() function to get the generated +// parameters in a custom type instead of std::tuple +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// struct ParamType { +// using TupleT = std::tuple<const char*, Color>; +// std::string animal; +// Color color; +// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {} +// }; +// class AnimalTest +// : public testing::TestWithParam<ParamType> {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// ConvertGenerator<ParamType::TupleT>( +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE)))); +// +template <typename RequestedT> +internal::ParamConverterGenerator<RequestedT> ConvertGenerator( + internal::ParamGenerator<RequestedT> gen) { + return internal::ParamConverterGenerator<RequestedT>(std::move(gen)); +} + +// As above, but takes a callable as a second argument. The callable converts +// the generated parameter to the test fixture's parameter type. This allows you +// to use a parameter type that does not have a converting constructor from the +// generated type. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// struct ParamType { +// std::string animal; +// Color color; +// }; +// class AnimalTest +// : public testing::TestWithParam<ParamType> {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P( +// AnimalVariations, AnimalTest, +// ConvertGenerator(Combine(Values("cat", "dog"), Values(BLACK, WHITE)), +// [](std::tuple<std::string, Color> t) { +// return ParamType{.animal = std::get<0>(t), +// .color = std::get<1>(t)}; +// })); +// +template <typename T, int&... ExplicitArgumentBarrier, typename Gen, + typename Func, + typename StdFunction = decltype(std::function(std::declval<Func>()))> +internal::ParamConverterGenerator<T, StdFunction> ConvertGenerator(Gen&& gen, + Func&& f) { + return internal::ParamConverterGenerator<T, StdFunction>( + std::forward<Gen>(gen), std::forward<Func>(f)); +} + +// As above, but infers the T from the supplied std::function instead of +// having the caller specify it. +template <int&... ExplicitArgumentBarrier, typename Gen, typename Func, + typename StdFunction = decltype(std::function(std::declval<Func>()))> +auto ConvertGenerator(Gen&& gen, Func&& f) { + constexpr bool is_single_arg_std_function = + internal::IsSingleArgStdFunction<StdFunction>::value; + if constexpr (is_single_arg_std_function) { + return ConvertGenerator< + typename internal::FuncSingleParamType<StdFunction>::type>( + std::forward<Gen>(gen), std::forward<Func>(f)); + } else { + static_assert(is_single_arg_std_function, + "The call signature must contain a single argument."); + } +} + #define TEST_P(test_suite_name, test_name) \ class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ - : public test_suite_name { \ + : public test_suite_name, \ + private ::testing::internal::GTestNonCopyable { \ public: \ GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ void TestBody() override; \ @@ -428,12 +526,7 @@ ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ return 0; \ } \ - static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ - GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ - (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ - GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ - const GTEST_TEST_CLASS_NAME_(test_suite_name, \ - test_name) &) = delete; /* NOLINT */ \ + [[maybe_unused]] static int gtest_registering_dummy_; \ }; \ int GTEST_TEST_CLASS_NAME_(test_suite_name, \ test_name)::gtest_registering_dummy_ = \ @@ -457,39 +550,38 @@ #define GTEST_GET_FIRST_(first, ...) first #define GTEST_GET_SECOND_(first, second, ...) second -#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ - static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \ - gtest_##prefix##test_suite_name##_EvalGenerator_() { \ - return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ - } \ - static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ - const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \ - if (::testing::internal::AlwaysFalse()) { \ - ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ - __VA_ARGS__, \ - ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \ - DUMMY_PARAM_))); \ - auto t = std::make_tuple(__VA_ARGS__); \ - static_assert(std::tuple_size<decltype(t)>::value <= 2, \ - "Too Many Args!"); \ - } \ - return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ - __VA_ARGS__, \ - ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \ - DUMMY_PARAM_))))(info); \ - } \ - static int gtest_##prefix##test_suite_name##_dummy_ \ - GTEST_ATTRIBUTE_UNUSED_ = \ - ::testing::UnitTest::GetInstance() \ - ->parameterized_test_registry() \ - .GetTestSuitePatternHolder<test_suite_name>( \ - GTEST_STRINGIFY_(test_suite_name), \ - ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ - ->AddTestSuiteInstantiation( \ - GTEST_STRINGIFY_(prefix), \ - >est_##prefix##test_suite_name##_EvalGenerator_, \ - >est_##prefix##test_suite_name##_EvalGenerateName_, \ - __FILE__, __LINE__) +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size<decltype(t)>::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName<test_suite_name::ParamType>, \ + DUMMY_PARAM_))))(info); \ + } \ + [[maybe_unused]] static int gtest_##prefix##test_suite_name##_dummy_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder<test_suite_name>( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, __FILE__, \ + __LINE__) // Allow Marking a Parameterized test class as not needing to be instantiated. #define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-printers.h b/third_party/googletest/src/googletest/include/gtest/gtest-printers.h index a91e8b8..198a769 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-printers.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-printers.h
@@ -43,6 +43,9 @@ // 1. foo::PrintTo(const T&, ostream*) // 2. operator<<(ostream&, const T&) defined in either foo or the // global namespace. +// * Prefer AbslStringify(..) to operator<<(..), per https://abseil.io/tips/215. +// * Define foo::PrintTo(..) if the type already has AbslStringify(..), but an +// alternative presentation in test results is of interest. // // However if T is an STL-style container then it is printed element-wise // unless foo::PrintTo(const T&, ostream*) is defined. Note that @@ -108,12 +111,25 @@ #include <string> #include <tuple> #include <type_traits> +#include <typeinfo> #include <utility> #include <vector> +#ifdef GTEST_HAS_ABSL +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL #include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-port.h" +#if GTEST_INTERNAL_HAS_STD_SPAN +#include <span> // NOLINT +#endif // GTEST_INTERNAL_HAS_STD_SPAN + +#if GTEST_INTERNAL_HAS_COMPARE_LIB +#include <compare> // NOLINT +#endif // GTEST_INTERNAL_HAS_COMPARE_LIB + namespace testing { // Definitions in the internal* namespaces are subject to change without notice. @@ -123,13 +139,32 @@ template <typename T> void UniversalPrint(const T& value, ::std::ostream* os); +template <typename T> +struct IsStdSpan { + static constexpr bool value = false; +}; + +#if GTEST_INTERNAL_HAS_STD_SPAN +template <typename E> +struct IsStdSpan<std::span<E>> { + static constexpr bool value = true; +}; +#endif // GTEST_INTERNAL_HAS_STD_SPAN + // Used to print an STL-style container when the user doesn't define // a PrintTo() for it. +// +// NOTE: Since std::span does not have const_iterator until C++23, it would +// fail IsContainerTest before C++23. However, IsContainerTest only uses +// the presence of const_iterator to avoid treating iterators as containers +// because of iterator::iterator. Which means std::span satisfies the *intended* +// condition of IsContainerTest. struct ContainerPrinter { template <typename T, typename = typename std::enable_if< - (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) && - !IsRecursiveContainer<T>::value>::type> + ((sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) && + !IsRecursiveContainer<T>::value) || + IsStdSpan<T>::value>::type> static void PrintValue(const T& container, std::ostream* os) { const size_t kMaxCount = 32; // The maximum number of elements to print. *os << '{'; @@ -205,12 +240,13 @@ // Don't accept member pointers here. We'd print them via implicit // conversion to bool, which isn't useful. typename = typename std::enable_if< - !std::is_member_pointer<T>::value>::type, - // Only accept types for which we can find a streaming operator via - // ADL (possibly involving implicit conversions). - typename = decltype(std::declval<std::ostream&>() - << std::declval<const T&>())> - static void PrintValue(const T& value, ::std::ostream* os) { + !std::is_member_pointer<T>::value>::type> + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + // (Use SFINAE via return type, because it seems GCC < 12 doesn't handle name + // lookup properly when we do it in the template parameter list.) + static auto PrintValue(const T& value, + ::std::ostream* os) -> decltype((void)(*os << value)) { // Call streaming operator found by ADL, possibly with implicit conversions // of the arguments. *os << value; @@ -258,6 +294,17 @@ #endif }; +#ifdef GTEST_HAS_ABSL +struct ConvertibleToAbslStringifyPrinter { + template <typename T, + typename = typename std::enable_if< + absl::HasAbslStringify<T>::value>::type> // NOLINT + static void PrintValue(const T& value, ::std::ostream* os) { + *os << absl::StrCat(value); + } +}; +#endif // GTEST_HAS_ABSL + // Prints the given number of bytes in the given object to the given // ostream. GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, @@ -296,8 +343,8 @@ // - Print containers (they have begin/end/etc). // - Print function pointers. // - Print object pointers. -// - Use the stream operator, if available. // - Print protocol buffers. +// - Use the stream operator, if available. // - Print types convertible to BiggestInt. // - Print types convertible to StringView, if available. // - Fallback to printing the raw bytes of the object. @@ -305,9 +352,13 @@ void PrintWithFallback(const T& value, ::std::ostream* os) { using Printer = typename FindFirstPrinter< T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + ProtobufPrinter, +#ifdef GTEST_HAS_ABSL + ConvertibleToAbslStringifyPrinter, +#endif // GTEST_HAS_ABSL internal_stream_operator_without_lexical_name_lookup::StreamPrinter, - ProtobufPrinter, ConvertibleToIntegerPrinter, - ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type; + ConvertibleToIntegerPrinter, ConvertibleToStringViewPrinter, + RawBytesPrinter, FallbackPrinter>::type; Printer::PrintValue(value, os); } @@ -384,7 +435,7 @@ GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); #endif @@ -472,7 +523,7 @@ inline void PrintTo(char16_t c, ::std::ostream* os) { PrintTo(ImplicitCast_<char32_t>(c), os); } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t inline void PrintTo(char8_t c, ::std::ostream* os) { PrintTo(ImplicitCast_<char32_t>(c), os); } @@ -484,6 +535,101 @@ GTEST_API_ void PrintTo(__int128_t v, ::std::ostream* os); #endif // __SIZEOF_INT128__ +// The default resolution used to print floating-point values uses only +// 6 digits, which can be confusing if a test compares two values whose +// difference lies in the 7th digit. So we'd like to print out numbers +// in full precision. +// However if the value is something simple like 1.1, full will print a +// long string like 1.100000001 due to floating-point numbers not using +// a base of 10. This routiune returns an appropriate resolution for a +// given floating-point number, that is, 6 if it will be accurate, or a +// max_digits10 value (full precision) if it won't, for values between +// 0.0001 and one million. +// It does this by computing what those digits would be (by multiplying +// by an appropriate power of 10), then dividing by that power again to +// see if gets the original value back. +// A similar algorithm applies for values larger than one million; note +// that for those values, we must divide to get a six-digit number, and +// then multiply to possibly get the original value again. +template <typename FloatType> +int AppropriateResolution(FloatType val) { + int full = std::numeric_limits<FloatType>::max_digits10; + if (val < 0) val = -val; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (val < 1000000) { + FloatType mulfor6 = 1e10; + // Without these static casts, the template instantiation for float would + // fail to compile when -Wdouble-promotion is enabled, as the arithmetic and + // comparison logic would promote floats to doubles. + if (val >= static_cast<FloatType>(100000.0)) { // 100,000 to 999,999 + mulfor6 = 1.0; + } else if (val >= static_cast<FloatType>(10000.0)) { + mulfor6 = 1e1; + } else if (val >= static_cast<FloatType>(1000.0)) { + mulfor6 = 1e2; + } else if (val >= static_cast<FloatType>(100.0)) { + mulfor6 = 1e3; + } else if (val >= static_cast<FloatType>(10.0)) { + mulfor6 = 1e4; + } else if (val >= static_cast<FloatType>(1.0)) { + mulfor6 = 1e5; + } else if (val >= static_cast<FloatType>(0.1)) { + mulfor6 = 1e6; + } else if (val >= static_cast<FloatType>(0.01)) { + mulfor6 = 1e7; + } else if (val >= static_cast<FloatType>(0.001)) { + mulfor6 = 1e8; + } else if (val >= static_cast<FloatType>(0.0001)) { + mulfor6 = 1e9; + } + if (static_cast<FloatType>(static_cast<int32_t>( + val * mulfor6 + (static_cast<FloatType>(0.5)))) / + mulfor6 == + val) + return 6; + } else if (val < static_cast<FloatType>(1e10)) { + FloatType divfor6 = static_cast<FloatType>(1.0); + if (val >= static_cast<FloatType>(1e9)) { // 1,000,000,000 to 9,999,999,999 + divfor6 = 10000; + } else if (val >= + static_cast<FloatType>(1e8)) { // 100,000,000 to 999,999,999 + divfor6 = 1000; + } else if (val >= + static_cast<FloatType>(1e7)) { // 10,000,000 to 99,999,999 + divfor6 = 100; + } else if (val >= static_cast<FloatType>(1e6)) { // 1,000,000 to 9,999,999 + divfor6 = 10; + } + if (static_cast<FloatType>(static_cast<int32_t>( + val / divfor6 + (static_cast<FloatType>(0.5)))) * + divfor6 == + val) + return 6; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + return full; +} + +inline void PrintTo(float f, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(f)); + *os << f; + os->precision(old_precision); +} + +inline void PrintTo(double d, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(d)); + *os << d; + os->precision(old_precision); +} + // Overloads for C strings. GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); inline void PrintTo(char* s, ::std::ostream* os) { @@ -504,7 +650,7 @@ inline void PrintTo(unsigned char* s, ::std::ostream* os) { PrintTo(ImplicitCast_<const void*>(s), os); } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t // Overloads for u8 strings. GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); inline void PrintTo(char8_t* s, ::std::ostream* os) { @@ -556,7 +702,7 @@ } // Overloads for ::std::u8string -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { PrintU8StringTo(s, os); @@ -640,6 +786,41 @@ (PrintSmartPointer<T>)(ptr, os, 0); } +#if GTEST_INTERNAL_HAS_COMPARE_LIB +template <typename T> +void PrintOrderingHelper(T ordering, std::ostream* os) { + if (ordering == T::less) { + *os << "(less)"; + } else if (ordering == T::greater) { + *os << "(greater)"; + } else if (ordering == T::equivalent) { + *os << "(equivalent)"; + } else { + *os << "(unknown ordering)"; + } +} + +inline void PrintTo(std::strong_ordering ordering, std::ostream* os) { + if (ordering == std::strong_ordering::equal) { + *os << "(equal)"; + } else { + PrintOrderingHelper(ordering, os); + } +} + +inline void PrintTo(std::partial_ordering ordering, std::ostream* os) { + if (ordering == std::partial_ordering::unordered) { + *os << "(unordered)"; + } else { + PrintOrderingHelper(ordering, os); + } +} + +inline void PrintTo(std::weak_ordering ordering, std::ostream* os) { + PrintOrderingHelper(ordering, os); +} +#endif + // Helper function for printing a tuple. T must be instantiated with // a tuple type. template <typename T> @@ -774,7 +955,7 @@ public: static void Print(const Variant<T...>& value, ::std::ostream* os) { *os << '('; -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL absl::visit(Visitor{os, value.index()}, value); #else std::visit(Visitor{os, value.index()}, value); @@ -824,7 +1005,7 @@ GTEST_API_ void UniversalPrintArray(const char* begin, size_t len, ::std::ostream* os); -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t // This overload prints a (const) char8_t array compactly. GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, ::std::ostream* os); @@ -891,6 +1072,13 @@ UniversalPrint(value, os); } }; +template <typename T> +class UniversalTersePrinter<std::reference_wrapper<T>> { + public: + static void Print(std::reference_wrapper<T> value, ::std::ostream* os) { + UniversalTersePrinter<T>::Print(value.get(), os); + } +}; template <typename T, size_t N> class UniversalTersePrinter<T[N]> { public: @@ -913,7 +1101,7 @@ class UniversalTersePrinter<char*> : public UniversalTersePrinter<const char*> { }; -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t template <> class UniversalTersePrinter<const char8_t*> { public:
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-spi.h b/third_party/googletest/src/googletest/include/gtest/gtest-spi.h index bec8c48..c0613b6 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-spi.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-spi.h
@@ -33,6 +33,8 @@ #ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ #define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#include <string> + #include "gtest/gtest.h" GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-test-part.h b/third_party/googletest/src/googletest/include/gtest/gtest-test-part.h index 09cc8c3..41c8a9a 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-test-part.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-test-part.h
@@ -35,6 +35,8 @@ #define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ #include <iosfwd> +#include <ostream> +#include <string> #include <vector> #include "gtest/internal/gtest-internal.h" @@ -131,7 +133,7 @@ // virtual. class GTEST_API_ TestPartResultArray { public: - TestPartResultArray() {} + TestPartResultArray() = default; // Appends the given TestPartResult to the array. void Append(const TestPartResult& result); @@ -152,7 +154,7 @@ // This interface knows how to report a test part result. class GTEST_API_ TestPartResultReporterInterface { public: - virtual ~TestPartResultReporterInterface() {} + virtual ~TestPartResultReporterInterface() = default; virtual void ReportTestPartResult(const TestPartResult& result) = 0; };
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest-typed-test.h b/third_party/googletest/src/googletest/include/gtest/gtest-typed-test.h index bd35a32..442e00b 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest-typed-test.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest-typed-test.h
@@ -205,8 +205,8 @@ typedef gtest_TypeParam_ TypeParam; \ void TestBody() override; \ }; \ - static bool gtest_##CaseName##_##TestName##_registered_ \ - GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \ + [[maybe_unused]] static bool gtest_##CaseName##_##TestName##_registered_ = \ + ::testing::internal::TypeParameterizedTest< \ CaseName, \ ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \ TestName)>, \ @@ -267,31 +267,31 @@ TYPED_TEST_SUITE_P #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ -#define TYPED_TEST_P(SuiteName, TestName) \ - namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ - template <typename gtest_TypeParam_> \ - class TestName : public SuiteName<gtest_TypeParam_> { \ - private: \ - typedef SuiteName<gtest_TypeParam_> TestFixture; \ - typedef gtest_TypeParam_ TypeParam; \ - void TestBody() override; \ - }; \ - static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ - GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ - __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ - GTEST_STRINGIFY_(TestName)); \ - } \ - template <typename gtest_TypeParam_> \ - void GTEST_SUITE_NAMESPACE_( \ +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template <typename gtest_TypeParam_> \ + class TestName : public SuiteName<gtest_TypeParam_> { \ + private: \ + typedef SuiteName<gtest_TypeParam_> TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + [[maybe_unused]] static bool gtest_##TestName##_defined_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template <typename gtest_TypeParam_> \ + void GTEST_SUITE_NAMESPACE_( \ SuiteName)::TestName<gtest_TypeParam_>::TestBody() // Note: this won't work correctly if the trailing arguments are macros. #define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ - typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ } \ - static const char* const GTEST_REGISTERED_TEST_NAMES_( \ - SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \ + [[maybe_unused]] static const char* const GTEST_REGISTERED_TEST_NAMES_( \ + SuiteName) = \ GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) @@ -306,7 +306,7 @@ #define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ "test-suit-prefix must not be empty"); \ - static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \ + [[maybe_unused]] static bool gtest_##Prefix##_##SuiteName = \ ::testing::internal::TypeParameterizedTestSuite< \ SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ ::testing::internal::GenerateTypeList<Types>::type>:: \
diff --git a/third_party/googletest/src/googletest/include/gtest/gtest.h b/third_party/googletest/src/googletest/include/gtest/gtest.h index d19a587..7be0caa 100644 --- a/third_party/googletest/src/googletest/include/gtest/gtest.h +++ b/third_party/googletest/src/googletest/include/gtest/gtest.h
@@ -50,22 +50,26 @@ #define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ #include <cstddef> +#include <cstdint> #include <limits> #include <memory> #include <ostream> +#include <set> +#include <sstream> +#include <string> #include <type_traits> #include <vector> -#include "gtest/gtest-assertion-result.h" -#include "gtest/gtest-death-test.h" -#include "gtest/gtest-matchers.h" -#include "gtest/gtest-message.h" -#include "gtest/gtest-param-test.h" -#include "gtest/gtest-printers.h" -#include "gtest/gtest-test-part.h" -#include "gtest/gtest-typed-test.h" -#include "gtest/gtest_pred_impl.h" -#include "gtest/gtest_prod.h" +#include "gtest/gtest-assertion-result.h" // IWYU pragma: export +#include "gtest/gtest-death-test.h" // IWYU pragma: export +#include "gtest/gtest-matchers.h" // IWYU pragma: export +#include "gtest/gtest-message.h" // IWYU pragma: export +#include "gtest/gtest-param-test.h" // IWYU pragma: export +#include "gtest/gtest-printers.h" // IWYU pragma: export +#include "gtest/gtest-test-part.h" // IWYU pragma: export +#include "gtest/gtest-typed-test.h" // IWYU pragma: export +#include "gtest/gtest_pred_impl.h" // IWYU pragma: export +#include "gtest/gtest_prod.h" // IWYU pragma: export #include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-string.h" @@ -161,11 +165,7 @@ // Silence C4100 (unreferenced formal parameter) and 4805 // unsafe mix of type 'const int' and type 'const bool' -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4805) -#pragma warning(disable : 4100) -#endif +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4805 4100) // The upper limit for valid stack trace depths. const int kMaxStackTraceDepth = 100; @@ -190,6 +190,17 @@ const std::string& message); std::set<std::string>* GetIgnoredParameterizedTestSuites(); +// A base class that prevents subclasses from being copyable. +// We do this instead of using '= delete' so as to avoid triggering warnings +// inside user code regarding any of our declarations. +class GTestNonCopyable { + public: + GTestNonCopyable() = default; + GTestNonCopyable(const GTestNonCopyable&) = delete; + GTestNonCopyable& operator=(const GTestNonCopyable&) = delete; + ~GTestNonCopyable() = default; +}; + } // namespace internal // The friend relationship of some of these classes is cyclic. @@ -285,7 +296,13 @@ // SetUp/TearDown method of Environment objects registered with Google // Test) will be output as attributes of the <testsuites> element. static void RecordProperty(const std::string& key, const std::string& value); - static void RecordProperty(const std::string& key, int value); + // We do not define a custom serialization except for values that can be + // converted to int64_t, but other values could be logged in this way. + template <typename T, std::enable_if_t<std::is_convertible<T, int64_t>::value, + bool> = true> + static void RecordProperty(const std::string& key, const T& value) { + RecordProperty(key, (Message() << value).GetString()); + } protected: // Creates a Test object. @@ -533,14 +550,14 @@ // Returns the name of the parameter type, or NULL if this is not a typed // or a type-parameterized test. const char* type_param() const { - if (type_param_.get() != nullptr) return type_param_->c_str(); + if (type_param_ != nullptr) return type_param_->c_str(); return nullptr; } // Returns the text representation of the value parameter, or NULL if this // is not a value-parameterized test. const char* value_param() const { - if (value_param_.get() != nullptr) return value_param_->c_str(); + if (value_param_ != nullptr) return value_param_->c_str(); return nullptr; } @@ -582,7 +599,7 @@ const TestResult* result() const { return &result_; } private: -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST friend class internal::DefaultDeathTestFactory; #endif // GTEST_HAS_DEATH_TEST friend class Test; @@ -590,7 +607,7 @@ friend class internal::UnitTestImpl; friend class internal::StreamingListenerTest; friend TestInfo* internal::MakeAndRegisterTestInfo( - const char* test_suite_name, const char* name, const char* type_param, + std::string test_suite_name, const char* name, const char* type_param, const char* value_param, internal::CodeLocation code_location, internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc, @@ -598,7 +615,7 @@ // Constructs a TestInfo object. The newly constructed instance assumes // ownership of the factory object. - TestInfo(const std::string& test_suite_name, const std::string& name, + TestInfo(std::string test_suite_name, std::string name, const char* a_type_param, // NULL if not a type-parameterized test const char* a_value_param, // NULL if not a value-parameterized test internal::CodeLocation a_code_location, @@ -666,7 +683,7 @@ // this is not a type-parameterized test. // set_up_tc: pointer to the function that sets up the test suite // tear_down_tc: pointer to the function that tears down the test suite - TestSuite(const char* name, const char* a_type_param, + TestSuite(const std::string& name, const char* a_type_param, internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc); @@ -679,7 +696,7 @@ // Returns the name of the parameter type, or NULL if this is not a // type-parameterized test suite. const char* type_param() const { - if (type_param_.get() != nullptr) return type_param_->c_str(); + if (type_param_ != nullptr) return type_param_->c_str(); return nullptr; } @@ -876,7 +893,7 @@ class Environment { public: // The d'tor is virtual as we need to subclass Environment. - virtual ~Environment() {} + virtual ~Environment() = default; // Override this to define how to set up the environment. virtual void SetUp() {} @@ -907,7 +924,7 @@ // the order the corresponding events are fired. class TestEventListener { public: - virtual ~TestEventListener() {} + virtual ~TestEventListener() = default; // Fired before any test activity starts. virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; @@ -1037,6 +1054,10 @@ return default_xml_generator_; } + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + void SuppressEventForwarding(bool); + private: friend class TestSuite; friend class TestInfo; @@ -1066,7 +1087,6 @@ // Controls whether events will be forwarded by the repeater to the // listeners in the list. bool EventForwardingEnabled() const; - void SuppressEventForwarding(); // The actual list of listeners. internal::TestEventRepeater* repeater_; @@ -1103,7 +1123,7 @@ // This method can only be called from the main thread. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - int Run() GTEST_MUST_USE_RESULT_; + [[nodiscard]] int Run(); // Returns the working directory when the first TEST() or TEST_F() // was executed. The UnitTest object owns the string. @@ -1242,6 +1262,20 @@ // total_test_suite_count() - 1. If i is not in that range, returns NULL. TestSuite* GetMutableTestSuite(int i); + // Invokes OsStackTrackGetterInterface::UponLeavingGTest. UponLeavingGTest() + // should be called immediately before Google Test calls user code. It saves + // some information about the current stack that CurrentStackTrace() will use + // to find and hide Google Test stack frames. + void UponLeavingGTest(); + + // Sets the TestSuite object for the test that's currently running. + void set_current_test_suite(TestSuite* a_current_test_suite) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Sets the TestInfo object for the test that's currently running. + void set_current_test_info(TestInfo* a_current_test_info) + GTEST_LOCK_EXCLUDED_(mutex_); + // Accessors for the implementation object. internal::UnitTestImpl* impl() { return impl_; } const internal::UnitTestImpl* impl() const { return impl_; } @@ -1250,6 +1284,8 @@ // members of UnitTest. friend class ScopedTrace; friend class Test; + friend class TestInfo; + friend class TestSuite; friend class internal::AssertHelper; friend class internal::StreamingListenerTest; friend class internal::UnitTestRecordPropertyTestHelper; @@ -1553,12 +1589,12 @@ } ::std::stringstream lhs_ss; - lhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) - << lhs_value; + lhs_ss.precision(std::numeric_limits<RawType>::digits10 + 2); + lhs_ss << lhs_value; ::std::stringstream rhs_ss; - rhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) - << rhs_value; + rhs_ss.precision(std::numeric_limits<RawType>::digits10 + 2); + rhs_ss << rhs_value; return EqFailure(lhs_expression, rhs_expression, StringStreamToString(&lhs_ss), StringStreamToString(&rhs_ss), @@ -1625,7 +1661,7 @@ // the GetParam() method. // // Use it with one of the parameter generator defining functions, like Range(), -// Values(), ValuesIn(), Bool(), and Combine(). +// Values(), ValuesIn(), Bool(), Combine(), and ConvertGenerator<T>(). // // class FooTest : public ::testing::TestWithParam<int> { // protected: @@ -1653,7 +1689,7 @@ class WithParamInterface { public: typedef T ParamType; - virtual ~WithParamInterface() {} + virtual ~WithParamInterface() = default; // The current parameter value. Is also available in the test fixture's // constructor. @@ -1723,14 +1759,15 @@ #define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") // Like GTEST_FAIL(), but at the given source file location. -#define GTEST_FAIL_AT(file, line) \ - GTEST_MESSAGE_AT_(file, line, "Failed", \ - ::testing::TestPartResult::kFatalFailure) +#define GTEST_FAIL_AT(file, line) \ + return GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) // Define this macro to 1 to omit the definition of FAIL(), which is a // generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_FAIL +#if !(defined(GTEST_DONT_DEFINE_FAIL) && GTEST_DONT_DEFINE_FAIL) #define FAIL() GTEST_FAIL() +#define FAIL_AT(file, line) GTEST_FAIL_AT(file, line) #endif // Generates a success with a generic message. @@ -1738,7 +1775,7 @@ // Define this macro to 1 to omit the definition of SUCCEED(), which // is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_SUCCEED +#if !(defined(GTEST_DONT_DEFINE_SUCCEED) && GTEST_DONT_DEFINE_SUCCEED) #define SUCCEED() GTEST_SUCCEED() #endif @@ -1782,19 +1819,19 @@ // Define these macros to 1 to omit the definition of the corresponding // EXPECT or ASSERT, which clashes with some users' own code. -#if !GTEST_DONT_DEFINE_EXPECT_TRUE +#if !(defined(GTEST_DONT_DEFINE_EXPECT_TRUE) && GTEST_DONT_DEFINE_EXPECT_TRUE) #define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) #endif -#if !GTEST_DONT_DEFINE_EXPECT_FALSE +#if !(defined(GTEST_DONT_DEFINE_EXPECT_FALSE) && GTEST_DONT_DEFINE_EXPECT_FALSE) #define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) #endif -#if !GTEST_DONT_DEFINE_ASSERT_TRUE +#if !(defined(GTEST_DONT_DEFINE_ASSERT_TRUE) && GTEST_DONT_DEFINE_ASSERT_TRUE) #define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) #endif -#if !GTEST_DONT_DEFINE_ASSERT_FALSE +#if !(defined(GTEST_DONT_DEFINE_ASSERT_FALSE) && GTEST_DONT_DEFINE_ASSERT_FALSE) #define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) #endif @@ -1873,27 +1910,27 @@ // Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of // ASSERT_XY(), which clashes with some users' own code. -#if !GTEST_DONT_DEFINE_ASSERT_EQ +#if !(defined(GTEST_DONT_DEFINE_ASSERT_EQ) && GTEST_DONT_DEFINE_ASSERT_EQ) #define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) #endif -#if !GTEST_DONT_DEFINE_ASSERT_NE +#if !(defined(GTEST_DONT_DEFINE_ASSERT_NE) && GTEST_DONT_DEFINE_ASSERT_NE) #define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) #endif -#if !GTEST_DONT_DEFINE_ASSERT_LE +#if !(defined(GTEST_DONT_DEFINE_ASSERT_LE) && GTEST_DONT_DEFINE_ASSERT_LE) #define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) #endif -#if !GTEST_DONT_DEFINE_ASSERT_LT +#if !(defined(GTEST_DONT_DEFINE_ASSERT_LT) && GTEST_DONT_DEFINE_ASSERT_LT) #define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) #endif -#if !GTEST_DONT_DEFINE_ASSERT_GE +#if !(defined(GTEST_DONT_DEFINE_ASSERT_GE) && GTEST_DONT_DEFINE_ASSERT_GE) #define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) #endif -#if !GTEST_DONT_DEFINE_ASSERT_GT +#if !(defined(GTEST_DONT_DEFINE_ASSERT_GT) && GTEST_DONT_DEFINE_ASSERT_GT) #define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) #endif @@ -1981,7 +2018,7 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, double val1, double val2); -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Macros that test for HRESULT failure and success, these are only useful // on Windows, and rely on Windows SDK macros and APIs to compile. @@ -2063,9 +2100,7 @@ ScopedTrace(const ScopedTrace&) = delete; ScopedTrace& operator=(const ScopedTrace&) = delete; -} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its - // c'tor and d'tor. Therefore it doesn't - // need to be used otherwise. +}; // Causes a trace (including the source file path, the current line // number, and the given message) to be included in every test failure @@ -2082,8 +2117,8 @@ // Assuming that each thread maintains its own stack of traces. // Therefore, a SCOPED_TRACE() would (correctly) only affect the // assertions in its own thread. -#define SCOPED_TRACE(message) \ - ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)( \ +#define SCOPED_TRACE(message) \ + const ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)( \ __FILE__, __LINE__, (message)) // Compile-time assertion for type equality. @@ -2153,7 +2188,7 @@ // Define this macro to 1 to omit the definition of TEST(), which // is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_TEST +#if !(defined(GTEST_DONT_DEFINE_TEST) && GTEST_DONT_DEFINE_TEST) #define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) #endif @@ -2185,17 +2220,22 @@ #define GTEST_TEST_F(test_fixture, test_name) \ GTEST_TEST_(test_fixture, test_name, test_fixture, \ ::testing::internal::GetTypeId<test_fixture>()) -#if !GTEST_DONT_DEFINE_TEST_F +#if !(defined(GTEST_DONT_DEFINE_TEST_F) && GTEST_DONT_DEFINE_TEST_F) #define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name) #endif -// Returns a path to temporary directory. -// Tries to determine an appropriate directory for the platform. +// Returns a path to a temporary directory, which should be writable. It is +// implementation-dependent whether or not the path is terminated by the +// directory-separator character. GTEST_API_ std::string TempDir(); -#ifdef _MSC_VER -#pragma warning(pop) -#endif +// Returns a path to a directory that contains ancillary data files that might +// be used by tests. It is implementation dependent whether or not the path is +// terminated by the directory-separator character. The directory and the files +// in it should be considered read-only. +GTEST_API_ std::string SrcDir(); + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4805 4100 // Dynamically registers a test with the framework. // @@ -2284,11 +2324,12 @@ // tests are successful, or 1 otherwise. // // RUN_ALL_TESTS() should be invoked after the command line has been -// parsed by InitGoogleTest(). +// parsed by InitGoogleTest(). RUN_ALL_TESTS will tear down and delete any +// installed environments and should only be called once per binary. // // This function was formerly a macro; thus, it is in the global // namespace and has an all-caps name. -int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; +[[nodiscard]] int RUN_ALL_TESTS(); inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); }
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/custom/gtest-port.h b/third_party/googletest/src/googletest/include/gtest/internal/custom/gtest-port.h index 9b7fb42..db02881 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/custom/gtest-port.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/custom/gtest-port.h
@@ -34,35 +34,4 @@ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ -// Use a stub Notification class. -// -// The built-in Notification class in GoogleTest v1.12.1 uses std::mutex and -// std::condition_variable. The <mutex> and <condition_variable> headers of -// mingw32 g++ (GNU 10.0.0) define std::mutex and std::condition_variable only -// when configured with the posix threads option but don't define them when -// configured with the win32 threads option. The Notification class is only -// used in GoogleTest's internal tests. Since we don't build GoogleTest's -// internal tests, we don't need a working Notification class. Although it's -// not hard to fix the mingw32 g++ compilation errors by implementing the -// Notification class using Windows CRITICAL_SECTION and CONDITION_VARIABLE, -// it's simpler to just use a stub Notification class on all platforms. -// -// The default constructor of the stub class is deleted and the declaration of -// the Notify() method is commented out, so that compilation will fail if any -// code actually uses the Notification class. - -#define GTEST_HAS_NOTIFICATION_ 1 -namespace testing { -namespace internal { -class Notification { - public: - Notification() = delete; - Notification(const Notification&) = delete; - Notification& operator=(const Notification&) = delete; - // void Notify(); - void WaitForNotification() {} -}; -} // namespace internal -} // namespace testing - #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-death-test-internal.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-death-test-internal.h index 45580ae..b363259 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-death-test-internal.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-death-test-internal.h
@@ -42,21 +42,43 @@ #include <stdio.h> #include <memory> +#include <string> #include "gtest/gtest-matchers.h" #include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" GTEST_DECLARE_string_(internal_run_death_test); namespace testing { namespace internal { -// Names of the flags (needed for parsing Google Test flags). -const char kDeathTestStyleFlag[] = "death_test_style"; -const char kDeathTestUseFork[] = "death_test_use_fork"; +// Name of the flag (needed for parsing Google Test flag). const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; -#if GTEST_HAS_DEATH_TEST +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher<const ::std::string&> MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher<const ::std::string&> MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher<const ::std::string&> MakeDeathTestMatcher( + Matcher<const ::std::string&> matcher) { + return matcher; +} + +#ifdef GTEST_HAS_DEATH_TEST GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ /* class A needs to have dll-interface to be used by clients of class B */) @@ -72,7 +94,7 @@ // // exit status: The integer exit information in the format specified // by wait(2) -// exit code: The integer code passed to exit(3), _exit(2), or +// exit code: The integer code passed to exit(3), _Exit(2), or // returned from main() class GTEST_API_ DeathTest { public: @@ -87,7 +109,7 @@ static bool Create(const char* statement, Matcher<const std::string&> matcher, const char* file, int line, DeathTest** test); DeathTest(); - virtual ~DeathTest() {} + virtual ~DeathTest() = default; // A helper class that aborts a death test when it's deleted. class ReturnSentinel { @@ -99,7 +121,7 @@ DeathTest* const test_; ReturnSentinel(const ReturnSentinel&) = delete; ReturnSentinel& operator=(const ReturnSentinel&) = delete; - } GTEST_ATTRIBUTE_UNUSED_; + }; // An enumeration of possible roles that may be taken when a death // test is encountered. EXECUTE means that the death test logic should @@ -152,7 +174,7 @@ // Factory interface for death tests. May be mocked out for testing. class DeathTestFactory { public: - virtual ~DeathTestFactory() {} + virtual ~DeathTestFactory() = default; virtual bool Create(const char* statement, Matcher<const std::string&> matcher, const char* file, int line, DeathTest** test) = 0; @@ -169,28 +191,6 @@ // by a signal, or exited normally with a nonzero exit code. GTEST_API_ bool ExitedUnsuccessfully(int exit_status); -// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads -// and interpreted as a regex (rather than an Eq matcher) for legacy -// compatibility. -inline Matcher<const ::std::string&> MakeDeathTestMatcher( - ::testing::internal::RE regex) { - return ContainsRegex(regex.pattern()); -} -inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) { - return ContainsRegex(regex); -} -inline Matcher<const ::std::string&> MakeDeathTestMatcher( - const ::std::string& regex) { - return ContainsRegex(regex); -} - -// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's -// used directly. -inline Matcher<const ::std::string&> MakeDeathTestMatcher( - Matcher<const ::std::string&> matcher) { - return matcher; -} - // Traps C++ exceptions escaping statement and reports them as test // failures. Note that trapping SEH exceptions is not implemented here. #if GTEST_HAS_EXCEPTIONS @@ -237,7 +237,7 @@ } \ break; \ case ::testing::internal::DeathTest::EXECUTE_TEST: { \ - ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + const ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ gtest_dt); \ GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-filepath.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-filepath.h index a2a60a9..6dc47be 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-filepath.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-filepath.h
@@ -42,11 +42,17 @@ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#include <string> +#include <utility> + +#include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-string.h" GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ /* class A needs to have dll-interface to be used by clients of class B */) +#if GTEST_HAS_FILE_SYSTEM + namespace testing { namespace internal { @@ -65,8 +71,9 @@ public: FilePath() : pathname_("") {} FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {} + FilePath(FilePath&& rhs) noexcept : pathname_(std::move(rhs.pathname_)) {} - explicit FilePath(const std::string& pathname) : pathname_(pathname) { + explicit FilePath(std::string pathname) : pathname_(std::move(pathname)) { Normalize(); } @@ -74,6 +81,10 @@ Set(rhs); return *this; } + FilePath& operator=(FilePath&& rhs) noexcept { + pathname_ = std::move(rhs.pathname_); + return *this; + } void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; } @@ -199,6 +210,16 @@ // separators. Returns NULL if no path separator was found. const char* FindLastPathSeparator() const; + // Returns the length of the path root, including the directory separator at + // the end of the prefix. Returns zero by definition if the path is relative. + // Examples: + // - [Windows] "..\Sibling" => 0 + // - [Windows] "\Windows" => 1 + // - [Windows] "C:/Windows\Notepad.exe" => 3 + // - [Windows] "\\Host\Share\C$/Windows" => 13 + // - [UNIX] "/bin" => 1 + size_t CalculateRootLength() const; + std::string pathname_; }; // class FilePath @@ -207,4 +228,6 @@ GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // GTEST_HAS_FILE_SYSTEM + #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-internal.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-internal.h index 9b04e4c..808d89b 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-internal.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-internal.h
@@ -41,7 +41,7 @@ #include "gtest/internal/gtest-port.h" -#if GTEST_OS_LINUX +#ifdef GTEST_OS_LINUX #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> @@ -57,12 +57,13 @@ #include <string.h> #include <cstdint> -#include <iomanip> +#include <functional> #include <limits> #include <map> #include <set> #include <string> #include <type_traits> +#include <utility> #include <vector> #include "gtest/gtest-message.h" @@ -77,7 +78,7 @@ // // will result in the token foo__LINE__, instead of foo followed by // the current line number. For more details, see -// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +// https://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 #define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo##bar @@ -168,7 +169,7 @@ // All edits cost the same, with replace having lower priority than // add/remove. // Simple implementation of the Wagner-Fischer algorithm. -// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +// See https://en.wikipedia.org/wiki/Wagner-Fischer_algorithm enum EditType { kMatch, kAdd, kRemove, kReplace }; GTEST_API_ std::vector<EditType> CalculateOptimalEdits( const std::vector<size_t>& left, const std::vector<size_t>& right); @@ -235,7 +236,7 @@ // For double, there are 11 exponent bits and 52 fraction bits. // // More details can be found at -// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// https://en.wikipedia.org/wiki/IEEE_floating-point_standard. // // Template parameter: // @@ -280,7 +281,7 @@ // bits. Therefore, 4 should be enough for ordinary use. // // See the following article for more details on ULP: - // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ static const uint32_t kMaxUlps = 4; // Constructs a FloatingPoint from a raw floating-point number. @@ -289,38 +290,35 @@ // around may change its bits, although the new value is guaranteed // to be also a NAN. Therefore, don't expect this constructor to // preserve the bits in x when x is a NAN. - explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + explicit FloatingPoint(RawType x) { memcpy(&bits_, &x, sizeof(x)); } // Static methods // Reinterprets a bit pattern as a floating-point number. // // This function is needed to test the AlmostEquals() method. - static RawType ReinterpretBits(const Bits bits) { - FloatingPoint fp(0); - fp.u_.bits_ = bits; - return fp.u_.value_; + static RawType ReinterpretBits(Bits bits) { + RawType fp; + memcpy(&fp, &bits, sizeof(fp)); + return fp; } // Returns the floating-point number that represent positive infinity. static RawType Infinity() { return ReinterpretBits(kExponentBitMask); } - // Returns the maximum representable finite floating-point number. - static RawType Max(); - // Non-static methods // Returns the bits that represents this number. - const Bits& bits() const { return u_.bits_; } + const Bits& bits() const { return bits_; } // Returns the exponent bits of this number. - Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + Bits exponent_bits() const { return kExponentBitMask & bits_; } // Returns the fraction bits of this number. - Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + Bits fraction_bits() const { return kFractionBitMask & bits_; } // Returns the sign bit of this number. - Bits sign_bit() const { return kSignBitMask & u_.bits_; } + Bits sign_bit() const { return kSignBitMask & bits_; } // Returns true if and only if this is NAN (not a number). bool is_nan() const { @@ -334,23 +332,16 @@ // // - returns false if either number is (or both are) NAN. // - treats really large numbers as almost equal to infinity. - // - thinks +0.0 and -0.0 are 0 DLP's apart. + // - thinks +0.0 and -0.0 are 0 ULP's apart. bool AlmostEquals(const FloatingPoint& rhs) const { // The IEEE standard says that any comparison operation involving // a NAN must return false. if (is_nan() || rhs.is_nan()) return false; - return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) <= - kMaxUlps; + return DistanceBetweenSignAndMagnitudeNumbers(bits_, rhs.bits_) <= kMaxUlps; } private: - // The data type used to store the actual floating-point number. - union FloatingPointUnion { - RawType value_; // The raw floating-point number. - Bits bits_; // The bits that represent the number. - }; - // Converts an integer from the sign-and-magnitude representation to // the biased representation. More precisely, let N be 2 to the // power of (kBitCount - 1), an integer x is represented by the @@ -364,9 +355,9 @@ // N - 1 (the biggest number representable using // sign-and-magnitude) is represented by 2N - 1. // - // Read http://en.wikipedia.org/wiki/Signed_number_representations + // Read https://en.wikipedia.org/wiki/Signed_number_representations // for more details on signed number representations. - static Bits SignAndMagnitudeToBiased(const Bits& sam) { + static Bits SignAndMagnitudeToBiased(Bits sam) { if (kSignBitMask & sam) { // sam represents a negative number. return ~sam + 1; @@ -378,27 +369,15 @@ // Given two numbers in the sign-and-magnitude representation, // returns the distance between them as an unsigned number. - static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits& sam1, - const Bits& sam2) { + static Bits DistanceBetweenSignAndMagnitudeNumbers(Bits sam1, Bits sam2) { const Bits biased1 = SignAndMagnitudeToBiased(sam1); const Bits biased2 = SignAndMagnitudeToBiased(sam2); return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); } - FloatingPointUnion u_; + Bits bits_; // The bits that represent the number. }; -// We cannot use std::numeric_limits<T>::max() as it clashes with the max() -// macro defined by <windows.h>. -template <> -inline float FloatingPoint<float>::Max() { - return FLT_MAX; -} -template <> -inline double FloatingPoint<double>::Max() { - return DBL_MAX; -} - // Typedefs the instances of the FloatingPoint template class that we // care to use. typedef FloatingPoint<float> Float; @@ -447,7 +426,7 @@ // of a Test object. class TestFactoryBase { public: - virtual ~TestFactoryBase() {} + virtual ~TestFactoryBase() = default; // Creates a test instance to run. The instance is both created and destroyed // within TestInfoImpl::Run() @@ -461,7 +440,7 @@ TestFactoryBase& operator=(const TestFactoryBase&) = delete; }; -// This class provides implementation of TeastFactoryBase interface. +// This class provides implementation of TestFactoryBase interface. // It is used in TEST and TEST_F macros. template <class TestClass> class TestFactoryImpl : public TestFactoryBase { @@ -469,7 +448,7 @@ Test* CreateTest() override { return new TestClass; } }; -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Predicate-formatters for implementing the HRESULT checking macros // {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} @@ -487,8 +466,8 @@ using TearDownTestSuiteFunc = void (*)(); struct CodeLocation { - CodeLocation(const std::string& a_file, int a_line) - : file(a_file), line(a_line) {} + CodeLocation(std::string a_file, int a_line) + : file(std::move(a_file)), line(a_line) {} std::string file; int line; @@ -568,7 +547,7 @@ // type_param: the name of the test's type parameter, or NULL if // this is not a typed or a type-parameterized test. // value_param: text representation of the test's value parameter, -// or NULL if this is not a type-parameterized test. +// or NULL if this is not a value-parameterized test. // code_location: code location where the test is defined // fixture_class_id: ID of the test fixture class // set_up_tc: pointer to the function that sets up the test suite @@ -577,7 +556,7 @@ // The newly created TestInfo instance will assume // ownership of the factory object. GTEST_API_ TestInfo* MakeAndRegisterTestInfo( - const char* test_suite_name, const char* name, const char* type_param, + std::string test_suite_name, const char* name, const char* type_param, const char* value_param, CodeLocation code_location, TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); @@ -608,8 +587,7 @@ fflush(stderr); posix::Abort(); } - registered_tests_.insert( - ::std::make_pair(test_name, CodeLocation(file, line))); + registered_tests_.emplace(test_name, CodeLocation(file, line)); return true; } @@ -631,7 +609,7 @@ const char* registered_tests); private: - typedef ::std::map<std::string, CodeLocation> RegisteredTestsMap; + typedef ::std::map<std::string, CodeLocation, std::less<>> RegisteredTestsMap; bool registered_; RegisteredTestsMap registered_tests_; @@ -713,7 +691,7 @@ // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, // Types). Valid values for 'index' are [0, N - 1] where N is the // length of Types. - static bool Register(const char* prefix, const CodeLocation& code_location, + static bool Register(const char* prefix, CodeLocation code_location, const char* case_name, const char* test_names, int index, const std::vector<std::string>& type_names = GenerateNames<DefaultNameGenerator, Types>()) { @@ -725,8 +703,7 @@ // list. MakeAndRegisterTestInfo( (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + - "/" + type_names[static_cast<size_t>(index)]) - .c_str(), + "/" + type_names[static_cast<size_t>(index)]), StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), GetTypeName<Type>().c_str(), nullptr, // No value parameter. @@ -738,13 +715,9 @@ new TestFactoryImpl<TestClass>); // Next, recurses (at compile time) with the tail of the type list. - return TypeParameterizedTest<Fixture, TestSel, - typename Types::Tail>::Register(prefix, - code_location, - case_name, - test_names, - index + 1, - type_names); + return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>:: + Register(prefix, std::move(code_location), case_name, test_names, + index + 1, type_names); } }; @@ -752,7 +725,7 @@ template <GTEST_TEMPLATE_ Fixture, class TestSel> class TypeParameterizedTest<Fixture, TestSel, internal::None> { public: - static bool Register(const char* /*prefix*/, const CodeLocation&, + static bool Register(const char* /*prefix*/, CodeLocation, const char* /*case_name*/, const char* /*test_names*/, int /*index*/, const std::vector<std::string>& = @@ -799,7 +772,8 @@ // Next, recurses (at compile time) with the tail of the test list. return TypeParameterizedTestSuite<Fixture, typename Tests::Tail, - Types>::Register(prefix, code_location, + Types>::Register(prefix, + std::move(code_location), state, case_name, SkipComma(test_names), type_names); @@ -829,8 +803,7 @@ // For example, if Foo() calls Bar(), which in turn calls // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, - int skip_count); +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(int skip_count); // Helpers for suppressing warnings on unreachable code or constant // condition. @@ -913,9 +886,6 @@ HasDebugStringType::value && HasShortDebugStringType::value; }; -template <typename T> -constexpr bool HasDebugStringAndShortDebugString<T>::value; - // When the compiler sees expression IsContainerTest<C>(0), if C is an // STL-style container class, the first overload of IsContainerTest // will be viable (since both C::iterator* and C::const_iterator* are @@ -1154,40 +1124,6 @@ void (NativeArray::*clone_)(const Element*, size_t); }; -// Backport of std::index_sequence. -template <size_t... Is> -struct IndexSequence { - using type = IndexSequence; -}; - -// Double the IndexSequence, and one if plus_one is true. -template <bool plus_one, typename T, size_t sizeofT> -struct DoubleSequence; -template <size_t... I, size_t sizeofT> -struct DoubleSequence<true, IndexSequence<I...>, sizeofT> { - using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>; -}; -template <size_t... I, size_t sizeofT> -struct DoubleSequence<false, IndexSequence<I...>, sizeofT> { - using type = IndexSequence<I..., (sizeofT + I)...>; -}; - -// Backport of std::make_index_sequence. -// It uses O(ln(N)) instantiation depth. -template <size_t N> -struct MakeIndexSequenceImpl - : DoubleSequence<N % 2 == 1, typename MakeIndexSequenceImpl<N / 2>::type, - N / 2>::type {}; - -template <> -struct MakeIndexSequenceImpl<0> : IndexSequence<> {}; - -template <size_t N> -using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::type; - -template <typename... T> -using IndexSequenceFor = typename MakeIndexSequence<sizeof...(T)>::type; - template <size_t> struct Ignore { Ignore(...); // NOLINT @@ -1196,7 +1132,7 @@ template <typename> struct ElemFromListImpl; template <size_t... I> -struct ElemFromListImpl<IndexSequence<I...>> { +struct ElemFromListImpl<std::index_sequence<I...>> { // We make Ignore a template to solve a problem with MSVC. // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but // MSVC doesn't understand how to deal with that pack expansion. @@ -1207,9 +1143,8 @@ template <size_t N, typename... T> struct ElemFromList { - using type = - decltype(ElemFromListImpl<typename MakeIndexSequence<N>::type>::Apply( - static_cast<T (*)()>(nullptr)...)); + using type = decltype(ElemFromListImpl<std::make_index_sequence<N>>::Apply( + static_cast<T (*)()>(nullptr)...)); }; struct FlatTupleConstructTag {}; @@ -1234,9 +1169,9 @@ struct FlatTupleBase; template <size_t... Idx, typename... T> -struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>> +struct FlatTupleBase<FlatTuple<T...>, std::index_sequence<Idx...>> : FlatTupleElemBase<FlatTuple<T...>, Idx>... { - using Indices = IndexSequence<Idx...>; + using Indices = std::index_sequence<Idx...>; FlatTupleBase() = default; template <typename... Args> explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) @@ -1271,14 +1206,15 @@ // implementations. // FlatTuple and ElemFromList are not recursive and have a fixed depth // regardless of T... -// MakeIndexSequence, on the other hand, it is recursive but with an +// std::make_index_sequence, on the other hand, it is recursive but with an // instantiation depth of O(ln(N)). template <typename... T> class FlatTuple : private FlatTupleBase<FlatTuple<T...>, - typename MakeIndexSequence<sizeof...(T)>::type> { - using Indices = typename FlatTupleBase< - FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>::Indices; + std::make_index_sequence<sizeof...(T)>> { + using Indices = + typename FlatTupleBase<FlatTuple<T...>, + std::make_index_sequence<sizeof...(T)>>::Indices; public: FlatTuple() = default; @@ -1292,30 +1228,40 @@ // Utility functions to be called with static_assert to induce deprecation // warnings. -GTEST_INTERNAL_DEPRECATED( +[[deprecated( "INSTANTIATE_TEST_CASE_P is deprecated, please use " - "INSTANTIATE_TEST_SUITE_P") -constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + "INSTANTIATE_TEST_SUITE_P")]] +constexpr bool InstantiateTestCase_P_IsDeprecated() { + return true; +} -GTEST_INTERNAL_DEPRECATED( +[[deprecated( "TYPED_TEST_CASE_P is deprecated, please use " - "TYPED_TEST_SUITE_P") -constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + "TYPED_TEST_SUITE_P")]] +constexpr bool TypedTestCase_P_IsDeprecated() { + return true; +} -GTEST_INTERNAL_DEPRECATED( +[[deprecated( "TYPED_TEST_CASE is deprecated, please use " - "TYPED_TEST_SUITE") -constexpr bool TypedTestCaseIsDeprecated() { return true; } + "TYPED_TEST_SUITE")]] +constexpr bool TypedTestCaseIsDeprecated() { + return true; +} -GTEST_INTERNAL_DEPRECATED( +[[deprecated( "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " - "REGISTER_TYPED_TEST_SUITE_P") -constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + "REGISTER_TYPED_TEST_SUITE_P")]] +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { + return true; +} -GTEST_INTERNAL_DEPRECATED( +[[deprecated( "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " - "INSTANTIATE_TYPED_TEST_SUITE_P") -constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + "INSTANTIATE_TYPED_TEST_SUITE_P")]] +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { + return true; +} } // namespace internal } // namespace testing @@ -1508,19 +1454,20 @@ gtest_ar_, text, #actual, #expected) \ .c_str()) -#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__) \ - : fail("Expected: " #statement \ - " doesn't generate new fatal " \ - "failures in the current thread.\n" \ +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::HasNewFatalFailureHelper \ + gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else /* NOLINT */ \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__) \ + : fail("Expected: " #statement \ + " doesn't generate new fatal " \ + "failures in the current thread.\n" \ " Actual: it does.") // Expands to the name of the class that implements the given test. @@ -1551,7 +1498,7 @@ \ private: \ void TestBody() override; \ - static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ + [[maybe_unused]] static ::testing::TestInfo* const test_info_; \ }; \ \ ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-param-util.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-param-util.h index e7af2f9..a092a86 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-param-util.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-param-util.h
@@ -39,11 +39,16 @@ #include <ctype.h> #include <cassert> +#include <functional> #include <iterator> +#include <map> #include <memory> +#include <ostream> #include <set> +#include <string> #include <tuple> #include <type_traits> +#include <unordered_map> #include <utility> #include <vector> @@ -82,7 +87,7 @@ // TEST_P macro is used to define two tests with the same name // but in different namespaces. GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, - CodeLocation code_location); + const CodeLocation& code_location); template <typename> class ParamGeneratorInterface; @@ -94,7 +99,7 @@ template <typename T> class ParamIteratorInterface { public: - virtual ~ParamIteratorInterface() {} + virtual ~ParamIteratorInterface() = default; // A pointer to the base generator instance. // Used only for the purposes of iterator comparison // to make sure that two iterators belong to the same generator. @@ -168,7 +173,7 @@ public: typedef T ParamType; - virtual ~ParamGeneratorInterface() {} + virtual ~ParamGeneratorInterface() = default; // Generator interface definition virtual ParamIteratorInterface<T>* Begin() const = 0; @@ -212,7 +217,7 @@ end_(end), step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} - ~RangeGenerator() override {} + ~RangeGenerator() override = default; ParamIteratorInterface<T>* Begin() const override { return new Iterator(this, begin_, 0, step_); @@ -227,7 +232,7 @@ Iterator(const ParamGeneratorInterface<T>* base, T value, int index, IncrementT step) : base_(base), value_(value), index_(index), step_(step) {} - ~Iterator() override {} + ~Iterator() override = default; const ParamGeneratorInterface<T>* BaseGenerator() const override { return base_; @@ -296,7 +301,7 @@ template <typename ForwardIterator> ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) : container_(begin, end) {} - ~ValuesInIteratorRangeGenerator() override {} + ~ValuesInIteratorRangeGenerator() override = default; ParamIteratorInterface<T>* Begin() const override { return new Iterator(this, container_.begin()); @@ -313,7 +318,7 @@ Iterator(const ParamGeneratorInterface<T>* base, typename ContainerType::const_iterator iterator) : base_(base), iterator_(iterator) {} - ~Iterator() override {} + ~Iterator() override = default; const ParamGeneratorInterface<T>* BaseGenerator() const override { return base_; @@ -376,9 +381,7 @@ // integer test parameter index. template <class ParamType> std::string DefaultParamName(const TestParamInfo<ParamType>& info) { - Message name_stream; - name_stream << info.index; - return name_stream.GetString(); + return std::to_string(info.index); } template <typename T = int> @@ -417,7 +420,7 @@ template <class ParamType> class TestMetaFactoryBase { public: - virtual ~TestMetaFactoryBase() {} + virtual ~TestMetaFactoryBase() = default; virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; }; @@ -436,7 +439,7 @@ public: using ParamType = typename TestSuite::ParamType; - TestMetaFactory() {} + TestMetaFactory() = default; TestFactoryBase* CreateTestFactory(ParamType parameter) override { return new ParameterizedTestFactory<TestSuite>(parameter); @@ -459,7 +462,7 @@ // and calls RegisterTests() on each of them when asked. class ParameterizedTestSuiteInfoBase { public: - virtual ~ParameterizedTestSuiteInfoBase() {} + virtual ~ParameterizedTestSuiteInfoBase() = default; // Base part of test suite name for display purposes. virtual const std::string& GetTestSuiteName() const = 0; @@ -510,9 +513,10 @@ typedef ParamGenerator<ParamType>(GeneratorCreationFunc)(); using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&); - explicit ParameterizedTestSuiteInfo(const char* name, + explicit ParameterizedTestSuiteInfo(std::string name, CodeLocation code_location) - : test_suite_name_(name), code_location_(code_location) {} + : test_suite_name_(std::move(name)), + code_location_(std::move(code_location)) {} // Test suite base name for display purposes. const std::string& GetTestSuiteName() const override { @@ -526,20 +530,20 @@ // prefix). test_base_name is the name of an individual test without // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is // test suite base name and DoBar is test base name. - void AddTestPattern(const char* test_suite_name, const char* test_base_name, + void AddTestPattern(const char*, const char* test_base_name, TestMetaFactoryBase<ParamType>* meta_factory, CodeLocation code_location) { - tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo( - test_suite_name, test_base_name, meta_factory, code_location))); + tests_.emplace_back( + new TestInfo(test_base_name, meta_factory, std::move(code_location))); } // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information // about a generator. - int AddTestSuiteInstantiation(const std::string& instantiation_name, + int AddTestSuiteInstantiation(std::string instantiation_name, GeneratorCreationFunc* func, ParamNameGeneratorFunc* name_func, const char* file, int line) { - instantiations_.push_back( - InstantiationInfo(instantiation_name, func, name_func, file, line)); + instantiations_.emplace_back(std::move(instantiation_name), func, name_func, + file, line); return 0; // Return value used only to run this method in namespace scope. } // UnitTest class invokes this method to register tests in this test suite @@ -550,60 +554,61 @@ void RegisterTests() override { bool generated_instantiations = false; - for (typename TestInfoContainer::iterator test_it = tests_.begin(); - test_it != tests_.end(); ++test_it) { - std::shared_ptr<TestInfo> test_info = *test_it; - for (typename InstantiationContainer::iterator gen_it = - instantiations_.begin(); - gen_it != instantiations_.end(); ++gen_it) { - const std::string& instantiation_name = gen_it->name; - ParamGenerator<ParamType> generator((*gen_it->generator)()); - ParamNameGeneratorFunc* name_func = gen_it->name_func; - const char* file = gen_it->file; - int line = gen_it->line; + std::string test_suite_name; + std::string test_name; + for (const std::shared_ptr<TestInfo>& test_info : tests_) { + for (const InstantiationInfo& instantiation : instantiations_) { + const std::string& instantiation_name = instantiation.name; + ParamGenerator<ParamType> generator((*instantiation.generator)()); + ParamNameGeneratorFunc* name_func = instantiation.name_func; + const char* file = instantiation.file; + int line = instantiation.line; - std::string test_suite_name; if (!instantiation_name.empty()) test_suite_name = instantiation_name + "/"; - test_suite_name += test_info->test_suite_base_name; + else + test_suite_name.clear(); + test_suite_name += test_suite_name_; size_t i = 0; std::set<std::string> test_param_names; - for (typename ParamGenerator<ParamType>::iterator param_it = - generator.begin(); - param_it != generator.end(); ++param_it, ++i) { + for (const auto& param : generator) { generated_instantiations = true; - Message test_name_stream; + test_name.clear(); std::string param_name = - name_func(TestParamInfo<ParamType>(*param_it, i)); + name_func(TestParamInfo<ParamType>(param, i)); GTEST_CHECK_(IsValidParamName(param_name)) << "Parameterized test name '" << param_name - << "' is invalid, in " << file << " line " << line << std::endl; + << "' is invalid (contains spaces, dashes, or any " + "non-alphanumeric characters other than underscores), in " + << file << " line " << line << "" << std::endl; GTEST_CHECK_(test_param_names.count(param_name) == 0) << "Duplicate parameterized test name '" << param_name << "', in " << file << " line " << line << std::endl; - test_param_names.insert(param_name); - if (!test_info->test_base_name.empty()) { - test_name_stream << test_info->test_base_name << "/"; + test_name.append(test_info->test_base_name).append("/"); } - test_name_stream << param_name; + test_name += param_name; + + test_param_names.insert(std::move(param_name)); + MakeAndRegisterTestInfo( - test_suite_name.c_str(), test_name_stream.GetString().c_str(), + test_suite_name, test_name.c_str(), nullptr, // No type parameter. - PrintToString(*param_it).c_str(), test_info->code_location, + PrintToString(param).c_str(), test_info->code_location, GetTestSuiteTypeId(), SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line), SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line), - test_info->test_meta_factory->CreateTestFactory(*param_it)); - } // for param_it - } // for gen_it - } // for test_it + test_info->test_meta_factory->CreateTestFactory(param)); + ++i; + } // for param + } // for instantiation + } // for test_info if (!generated_instantiations) { // There are no generaotrs, or they all generate nothing ... @@ -616,15 +621,13 @@ // LocalTestInfo structure keeps information about a single test registered // with TEST_P macro. struct TestInfo { - TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name, + TestInfo(const char* a_test_base_name, TestMetaFactoryBase<ParamType>* a_test_meta_factory, CodeLocation a_code_location) - : test_suite_base_name(a_test_suite_base_name), - test_base_name(a_test_base_name), + : test_base_name(a_test_base_name), test_meta_factory(a_test_meta_factory), - code_location(a_code_location) {} + code_location(std::move(a_code_location)) {} - const std::string test_suite_base_name; const std::string test_base_name; const std::unique_ptr<TestMetaFactoryBase<ParamType>> test_meta_factory; const CodeLocation code_location; @@ -634,11 +637,10 @@ // <Instantiation name, Sequence generator creation function, // Name generator function, Source file, Source line> struct InstantiationInfo { - InstantiationInfo(const std::string& name_in, - GeneratorCreationFunc* generator_in, + InstantiationInfo(std::string name_in, GeneratorCreationFunc* generator_in, ParamNameGeneratorFunc* name_func_in, const char* file_in, int line_in) - : name(name_in), + : name(std::move(name_in)), generator(generator_in), name_func(name_func_in), file(file_in), @@ -688,7 +690,7 @@ // ParameterizedTestSuiteInfo descriptors. class ParameterizedTestSuiteRegistry { public: - ParameterizedTestSuiteRegistry() {} + ParameterizedTestSuiteRegistry() = default; ~ParameterizedTestSuiteRegistry() { for (auto& test_suite_info : test_suite_infos_) { delete test_suite_info; @@ -699,29 +701,32 @@ // tests and instantiations of a particular test suite. template <class TestSuite> ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder( - const char* test_suite_name, CodeLocation code_location) { + std::string test_suite_name, CodeLocation code_location) { ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr; - for (auto& test_suite_info : test_suite_infos_) { - if (test_suite_info->GetTestSuiteName() == test_suite_name) { - if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) { - // Complain about incorrect usage of Google Test facilities - // and terminate the program since we cannot guaranty correct - // test suite setup and tear-down in this case. - ReportInvalidTestSuiteType(test_suite_name, code_location); - posix::Abort(); - } else { - // At this point we are sure that the object we found is of the same - // type we are looking for, so we downcast it to that type - // without further checks. - typed_test_info = CheckedDowncastToActualType< - ParameterizedTestSuiteInfo<TestSuite>>(test_suite_info); - } - break; + + auto item_it = suite_name_to_info_index_.find(test_suite_name); + if (item_it != suite_name_to_info_index_.end()) { + auto* test_suite_info = test_suite_infos_[item_it->second]; + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name.c_str(), code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = + CheckedDowncastToActualType<ParameterizedTestSuiteInfo<TestSuite>>( + test_suite_info); } } if (typed_test_info == nullptr) { typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>( - test_suite_name, code_location); + test_suite_name, std::move(code_location)); + suite_name_to_info_index_.emplace(std::move(test_suite_name), + test_suite_infos_.size()); test_suite_infos_.push_back(typed_test_info); } return typed_test_info; @@ -735,8 +740,9 @@ #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ template <class TestCase> ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder( - const char* test_case_name, CodeLocation code_location) { - return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location); + std::string test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder<TestCase>(std::move(test_case_name), + std::move(code_location)); } #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ @@ -745,6 +751,7 @@ using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>; TestSuiteInfoContainer test_suite_infos_; + ::std::unordered_map<std::string, size_t> suite_name_to_info_index_; ParameterizedTestSuiteRegistry(const ParameterizedTestSuiteRegistry&) = delete; @@ -771,7 +778,7 @@ private: struct TypeParameterizedTestSuiteInfo { explicit TypeParameterizedTestSuiteInfo(CodeLocation c) - : code_location(c), instantiated(false) {} + : code_location(std::move(c)), instantiated(false) {} CodeLocation code_location; bool instantiated; @@ -791,10 +798,7 @@ namespace internal { // Used in the Values() function to provide polymorphic capabilities. -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100) -#endif +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4100) template <typename... Ts> class ValueArray { @@ -803,21 +807,19 @@ template <typename T> operator ParamGenerator<T>() const { // NOLINT - return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>())); + return ValuesIn(MakeVector<T>(std::make_index_sequence<sizeof...(Ts)>())); } private: template <typename T, size_t... I> - std::vector<T> MakeVector(IndexSequence<I...>) const { + std::vector<T> MakeVector(std::index_sequence<I...>) const { return std::vector<T>{static_cast<T>(v_.template Get<I>())...}; } FlatTuple<Ts...> v_; }; -#ifdef _MSC_VER -#pragma warning(pop) -#endif +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4100 template <typename... T> class CartesianProductGenerator @@ -827,7 +829,7 @@ CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g) : generators_(g) {} - ~CartesianProductGenerator() override {} + ~CartesianProductGenerator() override = default; ParamIteratorInterface<ParamType>* Begin() const override { return new Iterator(this, generators_, false); @@ -840,7 +842,7 @@ template <class I> class IteratorImpl; template <size_t... I> - class IteratorImpl<IndexSequence<I...>> + class IteratorImpl<std::index_sequence<I...>> : public ParamIteratorInterface<ParamType> { public: IteratorImpl(const ParamGeneratorInterface<ParamType>* base, @@ -852,7 +854,7 @@ current_(is_end ? end_ : begin_) { ComputeCurrentValue(); } - ~IteratorImpl() override {} + ~IteratorImpl() override = default; const ParamGeneratorInterface<ParamType>* BaseGenerator() const override { return base_; @@ -931,7 +933,7 @@ std::shared_ptr<ParamType> current_value_; }; - using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>; + using Iterator = IteratorImpl<std::make_index_sequence<sizeof...(T)>>; std::tuple<ParamGenerator<T>...> generators_; }; @@ -950,6 +952,112 @@ std::tuple<Gen...> generators_; }; +template <typename From, typename To, typename Func> +class ParamGeneratorConverter : public ParamGeneratorInterface<To> { + public: + ParamGeneratorConverter(ParamGenerator<From> gen, Func converter) // NOLINT + : generator_(std::move(gen)), converter_(std::move(converter)) {} + + ParamIteratorInterface<To>* Begin() const override { + return new Iterator(this, generator_.begin(), generator_.end()); + } + ParamIteratorInterface<To>* End() const override { + return new Iterator(this, generator_.end(), generator_.end()); + } + + // Returns the std::function wrapping the user-supplied converter callable. It + // is used by the iterator (see class Iterator below) to convert the object + // (of type FROM) returned by the ParamGenerator to an object of a type that + // can be static_cast to type TO. + const Func& TypeConverter() const { return converter_; } + + private: + class Iterator : public ParamIteratorInterface<To> { + public: + Iterator(const ParamGeneratorConverter* base, ParamIterator<From> it, + ParamIterator<From> end) + : base_(base), it_(it), end_(end) { + if (it_ != end_) + value_ = + std::make_shared<To>(static_cast<To>(base->TypeConverter()(*it_))); + } + ~Iterator() override = default; + + const ParamGeneratorInterface<To>* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++it_; + if (it_ != end_) + value_ = + std::make_shared<To>(static_cast<To>(base_->TypeConverter()(*it_))); + } + ParamIteratorInterface<To>* Clone() const override { + return new Iterator(*this); + } + const To* Current() const override { return value_.get(); } + bool Equals(const ParamIteratorInterface<To>& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const ParamIterator<From> other_it = + CheckedDowncastToActualType<const Iterator>(&other)->it_; + return it_ == other_it; + } + + private: + Iterator(const Iterator& other) = default; + + const ParamGeneratorConverter* const base_; + ParamIterator<From> it_; + ParamIterator<From> end_; + std::shared_ptr<To> value_; + }; // class ParamGeneratorConverter::Iterator + + ParamGenerator<From> generator_; + Func converter_; +}; // class ParamGeneratorConverter + +template <class GeneratedT, + typename StdFunction = + std::function<const GeneratedT&(const GeneratedT&)>> +class ParamConverterGenerator { + public: + ParamConverterGenerator(ParamGenerator<GeneratedT> g) // NOLINT + : generator_(std::move(g)), converter_(Identity) {} + + ParamConverterGenerator(ParamGenerator<GeneratedT> g, StdFunction converter) + : generator_(std::move(g)), converter_(std::move(converter)) {} + + template <typename T> + operator ParamGenerator<T>() const { // NOLINT + return ParamGenerator<T>( + new ParamGeneratorConverter<GeneratedT, T, StdFunction>(generator_, + converter_)); + } + + private: + static const GeneratedT& Identity(const GeneratedT& v) { return v; } + + ParamGenerator<GeneratedT> generator_; + StdFunction converter_; +}; + +// Template to determine the param type of a single-param std::function. +template <typename T> +struct FuncSingleParamType; +template <typename R, typename P> +struct FuncSingleParamType<std::function<R(P)>> { + using type = std::remove_cv_t<std::remove_reference_t<P>>; +}; + +template <typename T> +struct IsSingleArgStdFunction : public std::false_type {}; +template <typename R, typename P> +struct IsSingleArgStdFunction<std::function<R(P)>> : public std::true_type {}; + } // namespace internal } // namespace testing
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-port-arch.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-port-arch.h index f025db7..7ec968f 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-port-arch.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-port-arch.h
@@ -56,6 +56,8 @@ #elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) #define GTEST_OS_WINDOWS_PHONE 1 #define GTEST_OS_WINDOWS_TV_TITLE 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_GAMES) +#define GTEST_OS_WINDOWS_GAMES 1 #else // WINAPI_FAMILY defined but no known partition matched. // Default to desktop. @@ -111,6 +113,12 @@ #define GTEST_OS_ESP32 1 #elif defined(__XTENSA__) #define GTEST_OS_XTENSA 1 +#elif defined(__hexagon__) +#define GTEST_OS_QURT 1 +#elif defined(CPU_QN9090) || defined(CPU_QN9090HN) +#define GTEST_OS_NXP_QN9090 1 +#elif defined(NRF52) +#define GTEST_OS_NRF52 1 #endif // __CYGWIN__ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-port.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-port.h index 0003d27..4a5cde3 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-port.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-port.h
@@ -83,6 +83,8 @@ // GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that // std::wstring does/doesn't work (Google Test can // be used where std::wstring is unavailable). +// GTEST_HAS_FILE_SYSTEM - Define it to 1/0 to indicate whether or not a +// file system is/isn't available. // GTEST_HAS_SEH - Define it to 1/0 to indicate whether the // compiler supports Microsoft's "Structured // Exception Handling". @@ -159,10 +161,10 @@ // NOT define them. // // These macros are public so that portable tests can be written. -// Such tests typically surround code using a feature with an #if +// Such tests typically surround code using a feature with an #ifdef // which controls that code. For example: // -// #if GTEST_HAS_DEATH_TEST +// #ifdef GTEST_HAS_DEATH_TEST // EXPECT_DEATH(DoSomethingDeadly()); // #endif // @@ -176,6 +178,7 @@ // define themselves. // GTEST_USES_SIMPLE_RE - our own simple regex is used; // the above RE\b(s) are mutually exclusive. +// GTEST_HAS_ABSL - Google Test is compiled with Abseil. // Misc public macros // ------------------ @@ -191,25 +194,32 @@ // // Macros for basic C++ coding: // GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. -// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a -// variable don't have to be used. -// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. // GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is // suppressed (constant conditional). // GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 // is suppressed. // GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter<std::any> or // UniversalPrinter<absl::any> specializations. +// Always defined to 0 or 1. // GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter<std::optional> // or // UniversalPrinter<absl::optional> -// specializations. +// specializations. Always defined to 0 or 1. +// GTEST_INTERNAL_HAS_STD_SPAN - for enabling UniversalPrinter<std::span> +// specializations. Always defined to 0 or 1 // GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher<std::string_view> or // Matcher<absl::string_view> -// specializations. +// specializations. Always defined to 0 or 1. // GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter<std::variant> or // UniversalPrinter<absl::variant> -// specializations. +// specializations. Always defined to 0 or 1. +// GTEST_USE_OWN_FLAGFILE_FLAG_ - Always defined to 0 or 1. +// GTEST_HAS_CXXABI_H_ - Always defined to 0 or 1. +// GTEST_CAN_STREAM_RESULTS_ - Always defined to 0 or 1. +// GTEST_HAS_ALT_PATH_SEP_ - Always defined to 0 or 1. +// GTEST_WIDE_STRING_USES_UTF16_ - Always defined to 0 or 1. +// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Always defined to 0 or 1. +// GTEST_HAS_NOTIFICATION_- Always defined to 0 or 1. // // Synchronization: // Mutex, MutexLock, ThreadLocal, GetThreadCount() @@ -249,11 +259,39 @@ // BoolFromGTestEnv() - parses a bool environment variable. // Int32FromGTestEnv() - parses an int32_t environment variable. // StringFromGTestEnv() - parses a string environment variable. + +// The definition of GTEST_INTERNAL_CPLUSPLUS_LANG comes first because it can +// potentially be used as an #include guard. +#if defined(_MSVC_LANG) +#define GTEST_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define GTEST_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + +#if !defined(GTEST_INTERNAL_CPLUSPLUS_LANG) || \ + GTEST_INTERNAL_CPLUSPLUS_LANG < 201703L +#error C++ versions less than C++17 are not supported. +#endif + +// MSVC >= 19.11 (VS 2017 Update 3) supports __has_include. +#ifdef __has_include +#define GTEST_INTERNAL_HAS_INCLUDE __has_include +#else +#define GTEST_INTERNAL_HAS_INCLUDE(...) 0 +#endif + +// Detect C++ feature test macros as gracefully as possible. +// MSVC >= 19.15, Clang >= 3.4.1, and GCC >= 4.1.2 support feature test macros. // -// Deprecation warnings: -// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as -// deprecated; calling a marked function -// should generate a compiler warning +// GCC15 warns that <ciso646> is deprecated in C++17 and suggests using +// <version> instead, even though <version> is not available in C++17 mode prior +// to GCC9. +#if GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L || \ + GTEST_INTERNAL_HAS_INCLUDE(<version>) +#include <version> // C++20 or <version> support. +#else +#include <ciso646> // Pre-C++20 +#endif #include <ctype.h> // for isspace, etc #include <stddef.h> // for ptrdiff_t @@ -268,6 +306,7 @@ #include <limits> #include <locale> #include <memory> +#include <ostream> #include <string> // #include <mutex> // Guarded by GTEST_IS_THREADSAFE below #include <tuple> @@ -287,7 +326,16 @@ #include "gtest/internal/custom/gtest-port.h" #include "gtest/internal/gtest-port-arch.h" -#if GTEST_HAS_ABSL +#ifndef GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +#define GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ 0 +#endif + +#ifndef GTEST_HAS_NOTIFICATION_ +#define GTEST_HAS_NOTIFICATION_ 0 +#endif + +#if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) +#define GTEST_INTERNAL_HAS_ABSL_FLAGS // Used only in this file. #include "absl/flags/declare.h" #include "absl/flags/flag.h" #include "absl/flags/reflection.h" @@ -345,13 +393,13 @@ // Brings in definitions for functions used in the testing::internal::posix // namespace (read, write, close, chdir, isatty, stat). We do not currently // use them on Windows Mobile. -#if GTEST_OS_WINDOWS -#if !GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS +#ifndef GTEST_OS_WINDOWS_MOBILE #include <direct.h> #include <io.h> #endif // In order to avoid having to include <windows.h>, use forward declaration -#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +#if defined(GTEST_OS_WINDOWS_MINGW) && !defined(__MINGW64_VERSION_MAJOR) // MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two // separate (equivalent) structs, instead of using typedef typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; @@ -361,7 +409,7 @@ // WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; #endif -#elif GTEST_OS_XTENSA +#elif defined(GTEST_OS_XTENSA) #include <unistd.h> // Xtensa toolchains define strcasecmp in the string.h header instead of // strings.h. string.h is already included. @@ -373,7 +421,7 @@ #include <unistd.h> #endif // GTEST_OS_WINDOWS -#if GTEST_OS_LINUX_ANDROID +#ifdef GTEST_OS_LINUX_ANDROID // Used to define __ANDROID_API__ matching the target NDK API level. #include <android/api-level.h> // NOLINT #endif @@ -381,16 +429,21 @@ // Defines this to true if and only if Google Test can use POSIX regular // expressions. #ifndef GTEST_HAS_POSIX_RE -#if GTEST_OS_LINUX_ANDROID +#ifdef GTEST_OS_LINUX_ANDROID // On Android, <regex.h> is only available starting with Gingerbread. #define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) #else -#define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS && !GTEST_OS_XTENSA) +#if !(defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT)) +#define GTEST_HAS_POSIX_RE 1 +#else +#define GTEST_HAS_POSIX_RE 0 #endif +#endif // GTEST_OS_LINUX_ANDROID #endif // Select the regular expression implementation. -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL // When using Abseil, RE2 is required. #include "absl/strings/string_view.h" #include "re2/re2.h" @@ -426,8 +479,12 @@ // cleanups prior to that. To reliably check for C++ exception availability with // clang, check for // __EXCEPTIONS && __has_feature(cxx_exceptions). -#define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) -#elif defined(__GNUC__) && __EXCEPTIONS +#if defined(__EXCEPTIONS) && __EXCEPTIONS && __has_feature(cxx_exceptions) +#define GTEST_HAS_EXCEPTIONS 1 +#else +#define GTEST_HAS_EXCEPTIONS 0 +#endif +#elif defined(__GNUC__) && defined(__EXCEPTIONS) && __EXCEPTIONS // gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. #define GTEST_HAS_EXCEPTIONS 1 #elif defined(__SUNPRO_CC) @@ -435,7 +492,7 @@ // detecting whether they are enabled or not. Therefore, we assume that // they are enabled unless the user tells us otherwise. #define GTEST_HAS_EXCEPTIONS 1 -#elif defined(__IBMCPP__) && __EXCEPTIONS +#elif defined(__IBMCPP__) && defined(__EXCEPTIONS) && __EXCEPTIONS // xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. #define GTEST_HAS_EXCEPTIONS 1 #elif defined(__HP_aCC) @@ -455,12 +512,22 @@ // Cygwin 1.7 and below doesn't support ::std::wstring. // Solaris' libc++ doesn't support it either. Android has // no support for it at least as recent as Froyo (2.2). -#define GTEST_HAS_STD_WSTRING \ - (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ - GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || GTEST_OS_XTENSA)) - +#if (!(defined(GTEST_OS_LINUX_ANDROID) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_HAIKU) || \ + defined(GTEST_OS_ESP32) || defined(GTEST_OS_ESP8266) || \ + defined(GTEST_OS_XTENSA) || defined(GTEST_OS_QURT) || \ + defined(GTEST_OS_NXP_QN9090) || defined(GTEST_OS_NRF52))) +#define GTEST_HAS_STD_WSTRING 1 +#else +#define GTEST_HAS_STD_WSTRING 0 +#endif #endif // GTEST_HAS_STD_WSTRING +#ifndef GTEST_HAS_FILE_SYSTEM +// Most platforms support a file system. +#define GTEST_HAS_FILE_SYSTEM 1 +#endif // GTEST_HAS_FILE_SYSTEM + // Determines whether RTTI is available. #ifndef GTEST_HAS_RTTI // The user didn't tell us whether RTTI is enabled, so we need to @@ -483,7 +550,8 @@ // -frtti -fno-exceptions, the build fails at link time with undefined // references to __cxa_bad_typeid. Note sure if STL or toolchain bug, // so disable RTTI when detected. -#if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && !defined(__EXCEPTIONS) +#if defined(GTEST_OS_LINUX_ANDROID) && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) #define GTEST_HAS_RTTI 0 #else #define GTEST_HAS_RTTI 1 @@ -531,11 +599,18 @@ // // To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 // to your compiler flags. -#define GTEST_HAS_PTHREAD \ - (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ - GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ - GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD || \ - GTEST_OS_HAIKU || GTEST_OS_GNU_HURD) +#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_MAC) || \ + defined(GTEST_OS_HPUX) || defined(GTEST_OS_QNX) || \ + defined(GTEST_OS_FREEBSD) || defined(GTEST_OS_NACL) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_OPENBSD) || defined(GTEST_OS_HAIKU) || \ + defined(GTEST_OS_GNU_HURD) || defined(GTEST_OS_SOLARIS) || \ + defined(GTEST_OS_AIX) || defined(GTEST_OS_ZOS)) +#define GTEST_HAS_PTHREAD 1 +#else +#define GTEST_HAS_PTHREAD 0 +#endif #endif // GTEST_HAS_PTHREAD #if GTEST_HAS_PTHREAD @@ -550,12 +625,12 @@ // Determines whether clone(2) is supported. // Usually it will only be available on Linux, excluding // Linux on the Itanium architecture. -// Also see http://linux.die.net/man/2/clone. +// Also see https://linux.die.net/man/2/clone. #ifndef GTEST_HAS_CLONE // The user didn't tell us, so we need to figure it out. -#if GTEST_OS_LINUX && !defined(__ia64__) -#if GTEST_OS_LINUX_ANDROID +#if defined(GTEST_OS_LINUX) && !defined(__ia64__) +#if defined(GTEST_OS_LINUX_ANDROID) // On Android, clone() became available at different API levels for each 32-bit // architecture. #if defined(__LP64__) || (defined(__arm__) && __ANDROID_API__ >= 9) || \ @@ -578,9 +653,12 @@ // output correctness and to implement death tests. #ifndef GTEST_HAS_STREAM_REDIRECTION // By default, we assume that stream redirection is supported on all -// platforms except known mobile ones. -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ - GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA +// platforms except known mobile / embedded ones. Also, if the port doesn't have +// a file system, stream redirection is not supported. +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_WINDOWS_GAMES) || \ + defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) || !GTEST_HAS_FILE_SYSTEM #define GTEST_HAS_STREAM_REDIRECTION 0 #else #define GTEST_HAS_STREAM_REDIRECTION 1 @@ -589,14 +667,20 @@ // Determines whether to support death tests. // pops up a dialog window that cannot be suppressed programmatically. -#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ - (GTEST_OS_MAC && !GTEST_OS_IOS) || \ - (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW || \ - GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \ - GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ - GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU || \ - GTEST_OS_GNU_HURD) +#if (defined(GTEST_OS_LINUX) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_SOLARIS) || defined(GTEST_OS_ZOS) || \ + (defined(GTEST_OS_MAC) && !defined(GTEST_OS_IOS)) || \ + (defined(GTEST_OS_WINDOWS_DESKTOP) && _MSC_VER) || \ + defined(GTEST_OS_WINDOWS_MINGW) || defined(GTEST_OS_AIX) || \ + defined(GTEST_OS_HPUX) || defined(GTEST_OS_OPENBSD) || \ + defined(GTEST_OS_QNX) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_FUCHSIA) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_HAIKU) || defined(GTEST_OS_GNU_HURD)) +// Death tests require a file system to work properly. +#if GTEST_HAS_FILE_SYSTEM #define GTEST_HAS_DEATH_TEST 1 +#endif // GTEST_HAS_FILE_SYSTEM #endif // Determines whether to support type-driven tests. @@ -610,14 +694,21 @@ #endif // Determines whether the system compiler uses UTF-16 for encoding wide strings. -#define GTEST_WIDE_STRING_USES_UTF16_ \ - (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2) +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_CYGWIN) || \ + defined(GTEST_OS_AIX) || defined(GTEST_OS_OS2) +#define GTEST_WIDE_STRING_USES_UTF16_ 1 +#else +#define GTEST_WIDE_STRING_USES_UTF16_ 0 +#endif // Determines whether test results can be streamed to a socket. -#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \ - GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD || \ - GTEST_OS_GNU_HURD +#if defined(GTEST_OS_LINUX) || defined(GTEST_OS_GNU_KFREEBSD) || \ + defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_NETBSD) || defined(GTEST_OS_OPENBSD) || \ + defined(GTEST_OS_GNU_HURD) || defined(GTEST_OS_MAC) #define GTEST_CAN_STREAM_RESULTS_ 1 +#else +#define GTEST_CAN_STREAM_RESULTS_ 0 #endif // Defines some utility macros. @@ -639,56 +730,60 @@ default: // NOLINT #endif -// Use this annotation at the end of a struct/class definition to -// prevent the compiler from optimizing away instances that are never -// used. This is useful when all interesting logic happens inside the -// c'tor and / or d'tor. Example: +// GTEST_HAVE_ATTRIBUTE_ // -// struct Foo { -// Foo() { ... } -// } GTEST_ATTRIBUTE_UNUSED_; +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. // -// Also use it after a variable or parameter declaration to tell the -// compiler the variable/parameter does not have to be used. -#if defined(__GNUC__) && !defined(COMPILER_ICC) -#define GTEST_ATTRIBUTE_UNUSED_ __attribute__((unused)) -#elif defined(__clang__) -#if __has_attribute(unused) -#define GTEST_ATTRIBUTE_UNUSED_ __attribute__((unused)) +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define GTEST_HAVE_ATTRIBUTE_(x) __has_attribute(x) +#else +#define GTEST_HAVE_ATTRIBUTE_(x) 0 #endif + +// GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE +// +// A function-like feature checking macro that accepts C++11 style attributes. +// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// find `__has_cpp_attribute`, will evaluate to 0. +#if defined(__has_cpp_attribute) +// NOTE: requiring __cplusplus above should not be necessary, but +// works around https://bugs.llvm.org/show_bug.cgi?id=23435. +#define GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define GTEST_INTERNAL_HAVE_CPP_ATTRIBUTE(x) 0 #endif -#ifndef GTEST_ATTRIBUTE_UNUSED_ -#define GTEST_ATTRIBUTE_UNUSED_ + +// GTEST_HAVE_FEATURE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_feature`. +#ifdef __has_feature +#define GTEST_HAVE_FEATURE_(x) __has_feature(x) +#else +#define GTEST_HAVE_FEATURE_(x) 0 #endif // Use this annotation before a function that takes a printf format string. -#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC) -#if defined(__MINGW_PRINTF_FORMAT) +#if GTEST_HAVE_ATTRIBUTE_(format) && defined(__MINGW_PRINTF_FORMAT) // MinGW has two different printf implementations. Ensure the format macro // matches the selected implementation. See // https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. #define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ - __attribute__(( \ - __format__(__MINGW_PRINTF_FORMAT, string_index, first_to_check))) -#else + __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, first_to_check))) +#elif GTEST_HAVE_ATTRIBUTE_(format) #define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ - __attribute__((__format__(__printf__, string_index, first_to_check))) -#endif + __attribute__((format(printf, string_index, first_to_check))) #else #define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) #endif -// Tell the compiler to warn about unused return values for functions declared -// with this macro. The macro should be used on function declarations -// following the argument list: -// -// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; -#if defined(__GNUC__) && !defined(COMPILER_ICC) -#define GTEST_MUST_USE_RESULT_ __attribute__((warn_unused_result)) -#else -#define GTEST_MUST_USE_RESULT_ -#endif // __GNUC__ && !COMPILER_ICC - // MS C++ compiler emits warning when a conditional expression is compile time // constant. In some contexts this warning is false positive and needs to be // suppressed. Use the following two macros in such cases: @@ -719,14 +814,16 @@ #ifndef GTEST_IS_THREADSAFE -#define GTEST_IS_THREADSAFE \ - (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ - (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \ - GTEST_HAS_PTHREAD) +#if (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT)) || \ + GTEST_HAS_PTHREAD) +#define GTEST_IS_THREADSAFE 1 +#endif #endif // GTEST_IS_THREADSAFE -#if GTEST_IS_THREADSAFE +#ifdef GTEST_IS_THREADSAFE // Some platforms don't support including these threading related headers. #include <condition_variable> // NOLINT #include <mutex> // NOLINT @@ -738,12 +835,12 @@ #ifndef GTEST_API_ #ifdef _MSC_VER -#if GTEST_LINKED_AS_SHARED_LIBRARY +#if defined(GTEST_LINKED_AS_SHARED_LIBRARY) && GTEST_LINKED_AS_SHARED_LIBRARY #define GTEST_API_ __declspec(dllimport) -#elif GTEST_CREATE_SHARED_LIBRARY +#elif defined(GTEST_CREATE_SHARED_LIBRARY) && GTEST_CREATE_SHARED_LIBRARY #define GTEST_API_ __declspec(dllexport) #endif -#elif __GNUC__ >= 4 || defined(__clang__) +#elif GTEST_HAVE_ATTRIBUTE_(visibility) #define GTEST_API_ __attribute__((visibility("default"))) #endif // _MSC_VER @@ -757,21 +854,18 @@ #define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" #endif // GTEST_DEFAULT_DEATH_TEST_STYLE -#ifdef __GNUC__ +#if GTEST_HAVE_ATTRIBUTE_(noinline) // Ask the compiler to never inline a given function. #define GTEST_NO_INLINE_ __attribute__((noinline)) #else #define GTEST_NO_INLINE_ #endif -#if defined(__clang__) -// Nested ifs to avoid triggering MSVC warning. -#if __has_attribute(disable_tail_calls) +#if GTEST_HAVE_ATTRIBUTE_(disable_tail_calls) // Ask the compiler not to perform tail call optimization inside // the marked function. #define GTEST_NO_TAIL_CALL_ __attribute__((disable_tail_calls)) -#endif -#elif __GNUC__ +#elif defined(__GNUC__) && !defined(__NVCOMPILER) #define GTEST_NO_TAIL_CALL_ \ __attribute__((optimize("no-optimize-sibling-calls"))) #else @@ -789,50 +883,35 @@ // A function level attribute to disable checking for use of uninitialized // memory when built with MemorySanitizer. -#if defined(__clang__) -#if __has_feature(memory_sanitizer) +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_memory) #define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ __attribute__((no_sanitize_memory)) #else #define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ -#endif // __has_feature(memory_sanitizer) -#else -#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ -#endif // __clang__ +#endif // A function level attribute to disable AddressSanitizer instrumentation. -#if defined(__clang__) -#if __has_feature(address_sanitizer) +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_address) #define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ __attribute__((no_sanitize_address)) #else #define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ -#endif // __has_feature(address_sanitizer) -#else -#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ -#endif // __clang__ +#endif // A function level attribute to disable HWAddressSanitizer instrumentation. -#if defined(__clang__) -#if __has_feature(hwaddress_sanitizer) +#if GTEST_HAVE_FEATURE_(hwaddress_sanitizer) && \ + GTEST_HAVE_ATTRIBUTE_(no_sanitize) #define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ __attribute__((no_sanitize("hwaddress"))) #else #define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ -#endif // __has_feature(hwaddress_sanitizer) -#else -#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ -#endif // __clang__ +#endif // A function level attribute to disable ThreadSanitizer instrumentation. -#if defined(__clang__) -#if __has_feature(thread_sanitizer) -#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ __attribute__((no_sanitize_thread)) +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_thread) +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ __attribute((no_sanitize_thread)) #else #define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ -#endif // __has_feature(thread_sanitizer) -#else -#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ -#endif // __clang__ +#endif namespace testing { @@ -849,9 +928,11 @@ namespace internal { // A secret type that Google Test users don't know about. It has no -// definition on purpose. Therefore it's impossible to create a +// accessible constructors on purpose. Therefore it's impossible to create a // Secret object, which is what we want. -class Secret; +class Secret { + Secret(const Secret&) = delete; +}; // A helper for suppressing warnings on constant condition. It just // returns 'condition'. @@ -859,7 +940,7 @@ // Defines RE. -#if GTEST_USES_RE2 +#ifdef GTEST_USES_RE2 // This is almost `using RE = ::RE2`, except it is copy-constructible, and it // needs to disambiguate the `std::string`, `absl::string_view`, and `const @@ -884,7 +965,9 @@ RE2 regex_; }; -#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE +#elif defined(GTEST_USES_POSIX_RE) || defined(GTEST_USES_SIMPLE_RE) +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) // A simple C++ wrapper for <regex.h>. It uses the POSIX Extended // Regular Expression syntax. @@ -901,7 +984,7 @@ ~RE(); // Returns the string representation of the regex. - const char* pattern() const { return pattern_; } + const char* pattern() const { return pattern_.c_str(); } // FullMatch(str, re) returns true if and only if regular expression re // matches the entire str. @@ -919,21 +1002,21 @@ private: void Init(const char* regex); - const char* pattern_; + std::string pattern_; bool is_valid_; -#if GTEST_USES_POSIX_RE +#ifdef GTEST_USES_POSIX_RE regex_t full_regex_; // For FullMatch(). regex_t partial_regex_; // For PartialMatch(). #else // GTEST_USES_SIMPLE_RE - const char* full_pattern_; // For FullMatch(); + std::string full_pattern_; // For FullMatch(); #endif }; - +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 #endif // ::testing::internal::RE implementation // Formats a source file path and a line number as they would appear @@ -1066,47 +1149,6 @@ return x; } -// When you upcast (that is, cast a pointer from type Foo to type -// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts -// always succeed. When you downcast (that is, cast a pointer from -// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because -// how do you know the pointer is really of type SubclassOfFoo? It -// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, -// when you downcast, you should use this macro. In debug mode, we -// use dynamic_cast<> to double-check the downcast is legal (we die -// if it's not). In normal mode, we do the efficient static_cast<> -// instead. Thus, it's important to test in debug mode to make sure -// the cast is legal! -// This is the only place in the code we should use dynamic_cast<>. -// In particular, you SHOULDN'T be using dynamic_cast<> in order to -// do RTTI (eg code like this: -// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo); -// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo); -// You should design the code some other way not to need this. -// -// This relatively ugly name is intentional. It prevents clashes with -// similar functions users may have (e.g., down_cast). The internal -// namespace alone is not enough because the function can be found by ADL. -template <typename To, typename From> // use like this: DownCast_<T*>(foo); -inline To DownCast_(From* f) { // so we only accept pointers - // Ensures that To is a sub-type of From *. This test is here only - // for compile-time type checking, and has no overhead in an - // optimized build at run-time, as it will be optimized away - // completely. - GTEST_INTENTIONAL_CONST_COND_PUSH_() - if (false) { - GTEST_INTENTIONAL_CONST_COND_POP_() - const To to = nullptr; - ::testing::internal::ImplicitCast_<From*>(to); - } - -#if GTEST_HAS_RTTI - // RTTI: debug mode only! - GTEST_CHECK_(f == nullptr || dynamic_cast<To>(f) != nullptr); -#endif - return static_cast<To>(f); -} - // Downcasts the pointer of type Base to Derived. // Derived must be a subclass of Base. The parameter MUST // point to a class of type Derived, not any subclass of it. @@ -1114,17 +1156,12 @@ // check to enforce this. template <class Derived, class Base> Derived* CheckedDowncastToActualType(Base* base) { + static_assert(std::is_base_of<Base, Derived>::value, + "target type not derived from source type"); #if GTEST_HAS_RTTI - GTEST_CHECK_(typeid(*base) == typeid(Derived)); + GTEST_CHECK_(base == nullptr || dynamic_cast<Derived*>(base) != nullptr); #endif - -#if GTEST_HAS_DOWNCAST_ - return ::down_cast<Derived*>(base); -#elif GTEST_HAS_RTTI - return dynamic_cast<Derived*>(base); // NOLINT -#else - return static_cast<Derived*>(base); // Poor man's downcast. -#endif + return static_cast<Derived*>(base); } #if GTEST_HAS_STREAM_REDIRECTION @@ -1150,7 +1187,7 @@ // All command line arguments. GTEST_API_ std::vector<std::string> GetArgvs(); -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST std::vector<std::string> GetInjectableArgvs(); // Deprecated: pass the args vector by value instead. @@ -1161,9 +1198,9 @@ #endif // GTEST_HAS_DEATH_TEST // Defines synchronization primitives. -#if GTEST_IS_THREADSAFE +#ifdef GTEST_IS_THREADSAFE -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Provides leak-safe Windows kernel handle ownership. // Used in death tests and in threading support. class GTEST_API_ AutoHandle { @@ -1200,9 +1237,6 @@ // Nothing to do here. #else -GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ -/* class A needs to have dll-interface to be used by clients of class B */) - // Allows a controller thread to pause execution of newly created // threads until notified. Instances of this class must be created // and destroyed in the controller thread. @@ -1210,6 +1244,39 @@ // This class is only for testing Google Test's own constructs. Do not // use it in user tests, either directly or indirectly. // TODO(b/203539622): Replace unconditionally with absl::Notification. +#ifdef GTEST_OS_WINDOWS_MINGW +// GCC version < 13 with the win32 thread model does not provide std::mutex and +// std::condition_variable in the <mutex> and <condition_variable> headers. So +// we implement the Notification class using a Windows manual-reset event. See +// https://gcc.gnu.org/gcc-13/changes.html#windows. +class GTEST_API_ Notification { + public: + Notification(); + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + ~Notification(); + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify(); + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification(); + + private: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including <windows.h> in this header file. Including <windows.h> is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + Handle event_; +}; +#else +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + class GTEST_API_ Notification { public: Notification() : notified_(false) {} @@ -1237,12 +1304,13 @@ bool notified_; }; GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // GTEST_OS_WINDOWS_MINGW #endif // GTEST_HAS_NOTIFICATION_ // On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD // defined, but we don't want to use MinGW's pthreads implementation, which // has conformance problems with some versions of the POSIX standard. -#if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW +#if GTEST_HAS_PTHREAD && !defined(GTEST_OS_WINDOWS_MINGW) // As a C-function, ThreadFuncWithCLinkage cannot be templated itself. // Consequently, it cannot select a correct instantiation of ThreadWithParam @@ -1251,7 +1319,7 @@ // problem. class ThreadWithParamBase { public: - virtual ~ThreadWithParamBase() {} + virtual ~ThreadWithParamBase() = default; virtual void Run() = 0; }; @@ -1328,7 +1396,8 @@ // Mutex and ThreadLocal have already been imported into the namespace. // Nothing to do here. -#elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT +#elif defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) // Mutex implements mutex on Windows platforms. It is used in conjunction // with class MutexLock: @@ -1710,9 +1779,9 @@ // C-linkage. Therefore it cannot be templatized to access // ThreadLocal<T>. Hence the need for class // ThreadLocalValueHolderBase. -class ThreadLocalValueHolderBase { +class GTEST_API_ ThreadLocalValueHolderBase { public: - virtual ~ThreadLocalValueHolderBase() {} + virtual ~ThreadLocalValueHolderBase() = default; }; // Called by pthread to delete thread-local data stored by @@ -1784,8 +1853,8 @@ class ValueHolderFactory { public: - ValueHolderFactory() {} - virtual ~ValueHolderFactory() {} + ValueHolderFactory() = default; + virtual ~ValueHolderFactory() = default; virtual ValueHolder* MakeNewHolder() const = 0; private: @@ -1795,7 +1864,7 @@ class DefaultValueHolderFactory : public ValueHolderFactory { public: - DefaultValueHolderFactory() {} + DefaultValueHolderFactory() = default; ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } private: @@ -1881,7 +1950,7 @@ // we cannot detect it. GTEST_API_ size_t GetThreadCount(); -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS #define GTEST_PATH_SEP_ "\\" #define GTEST_HAS_ALT_PATH_SEP_ 1 #else @@ -1917,7 +1986,7 @@ inline bool IsXDigit(char ch) { return isxdigit(static_cast<unsigned char>(ch)) != 0; } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t inline bool IsXDigit(char8_t ch) { return isxdigit(static_cast<unsigned char>(ch)) != 0; } @@ -1956,21 +2025,71 @@ namespace posix { -// Functions with a different name on Windows. - -#if GTEST_OS_WINDOWS +// File system porting. +// Note: Not every I/O-related function is related to file systems, so don't +// just disable all of them here. For example, fileno() and isatty(), etc. must +// always be available in order to detect if a pipe points to a terminal. +#ifdef GTEST_OS_WINDOWS typedef struct _stat StatStruct; +#ifdef GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +#else +inline int FileNo(FILE* file) { return _fileno(file); } +#if GTEST_HAS_FILE_SYSTEM +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; } +#endif +#endif // GTEST_OS_WINDOWS_MOBILE + +#elif defined(GTEST_OS_ESP8266) +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +#if GTEST_HAS_FILE_SYSTEM +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } +#endif + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +#if GTEST_HAS_FILE_SYSTEM +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +#ifdef GTEST_OS_QURT +// QuRT doesn't support any directory functions, including rmdir +inline int RmDir(const char*) { return 0; } +#else +inline int RmDir(const char* dir) { return rmdir(dir); } +#endif +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } +#endif + +#endif // GTEST_OS_WINDOWS + +// Other functions with a different name on Windows. + +#ifdef GTEST_OS_WINDOWS + #ifdef __BORLANDC__ inline int DoIsATTY(int fd) { return isatty(fd); } inline int StrCaseCmp(const char* s1, const char* s2) { return stricmp(s1, s2); } -inline char* StrDup(const char* src) { return strdup(src); } #else // !__BORLANDC__ -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS || GTEST_OS_IOS || \ - GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT || defined(ESP_PLATFORM) +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_ZOS) || \ + defined(GTEST_OS_IOS) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(ESP_PLATFORM) inline int DoIsATTY(int /* fd */) { return 0; } #else inline int DoIsATTY(int fd) { return _isatty(fd); } @@ -1978,49 +2097,14 @@ inline int StrCaseCmp(const char* s1, const char* s2) { return _stricmp(s1, s2); } -inline char* StrDup(const char* src) { return _strdup(src); } #endif // __BORLANDC__ -#if GTEST_OS_WINDOWS_MOBILE -inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); } -// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this -// time and thus not defined there. #else -inline int FileNo(FILE* file) { return _fileno(file); } -inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } -inline int RmDir(const char* dir) { return _rmdir(dir); } -inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; } -#endif // GTEST_OS_WINDOWS_MOBILE -#elif GTEST_OS_ESP8266 -typedef struct stat StatStruct; - -inline int FileNo(FILE* file) { return fileno(file); } inline int DoIsATTY(int fd) { return isatty(fd); } -inline int Stat(const char* path, StatStruct* buf) { - // stat function not implemented on ESP8266 - return 0; -} inline int StrCaseCmp(const char* s1, const char* s2) { return strcasecmp(s1, s2); } -inline char* StrDup(const char* src) { return strdup(src); } -inline int RmDir(const char* dir) { return rmdir(dir); } -inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } - -#else - -typedef struct stat StatStruct; - -inline int FileNo(FILE* file) { return fileno(file); } -inline int DoIsATTY(int fd) { return isatty(fd); } -inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } -inline int StrCaseCmp(const char* s1, const char* s2) { - return strcasecmp(s1, s2); -} -inline char* StrDup(const char* src) { return strdup(src); } -inline int RmDir(const char* dir) { return rmdir(dir); } -inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } #endif // GTEST_OS_WINDOWS @@ -2042,13 +2126,15 @@ // ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and // StrError() aren't needed on Windows CE at this time and thus not // defined there. - -#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ - !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA +#if GTEST_HAS_FILE_SYSTEM +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_WINDOWS_GAMES) && \ + !defined(GTEST_OS_ESP8266) && !defined(GTEST_OS_XTENSA) && \ + !defined(GTEST_OS_QURT) inline int ChDir(const char* dir) { return chdir(dir); } #endif inline FILE* FOpen(const char* path, const char* mode) { -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW) struct wchar_codecvt : public std::codecvt<wchar_t, char, std::mbstate_t> {}; std::wstring_convert<wchar_codecvt> converter; std::wstring wide_path = converter.from_bytes(path); @@ -2058,14 +2144,14 @@ return fopen(path, mode); #endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW } -#if !GTEST_OS_WINDOWS_MOBILE +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) inline FILE* FReopen(const char* path, const char* mode, FILE* stream) { return freopen(path, mode, stream); } inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } -#endif +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT inline int FClose(FILE* fp) { return fclose(fp); } -#if !GTEST_OS_WINDOWS_MOBILE +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) inline int Read(int fd, void* buf, unsigned int count) { return static_cast<int>(read(fd, buf, count)); } @@ -2073,11 +2159,17 @@ return static_cast<int>(write(fd, buf, count)); } inline int Close(int fd) { return close(fd); } +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +#endif // GTEST_HAS_FILE_SYSTEM + +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_QURT) inline const char* StrError(int errnum) { return strerror(errnum); } -#endif +#endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT + inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ - GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) // We are on an embedded platform, which has no environment variables. static_cast<void>(name); // To prevent 'unused argument' warning. return nullptr; @@ -2093,7 +2185,7 @@ GTEST_DISABLE_MSC_DEPRECATED_POP_() -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE // Windows CE has no C library. The abort() function is used in // several places in Google Test. This implementation provides a reasonable // imitation of standard behaviour. @@ -2109,7 +2201,7 @@ // MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate // function in order to achieve that. We use macro definition here because // snprintf is a variadic function. -#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE +#if defined(_MSC_VER) && !defined(GTEST_OS_WINDOWS_MOBILE) // MSVC 2005 and above support variadic macros. #define GTEST_SNPRINTF_(buffer, size, format, ...) \ _snprintf_s(buffer, size, size, format, __VA_ARGS__) @@ -2182,7 +2274,7 @@ #endif // !defined(GTEST_FLAG) // Pick a command line flags implementation. -#if GTEST_HAS_ABSL +#ifdef GTEST_INTERNAL_HAS_ABSL_FLAGS // Macros for defining flags. #define GTEST_DEFINE_bool_(name, default_val, doc) \ @@ -2207,7 +2299,8 @@ (void)(::absl::SetFlag(>EST_FLAG(name), value)) #define GTEST_USE_OWN_FLAGFILE_FLAG_ 0 -#else // GTEST_HAS_ABSL +#undef GTEST_INTERNAL_HAS_ABSL_FLAGS +#else // ndef GTEST_INTERNAL_HAS_ABSL_FLAGS // Macros for defining flags. #define GTEST_DEFINE_bool_(name, default_val, doc) \ @@ -2249,7 +2342,7 @@ #define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value) #define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 -#endif // GTEST_HAS_ABSL +#endif // GTEST_INTERNAL_HAS_ABSL_FLAGS // Thread annotations #if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) @@ -2273,27 +2366,7 @@ } // namespace internal } // namespace testing -#if !defined(GTEST_INTERNAL_DEPRECATED) - -// Internal Macro to mark an API deprecated, for googletest usage only -// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or -// GTEST_INTERNAL_DEPRECATED(message) <return_type> myFunction(); Every usage of -// a deprecated entity will trigger a warning when compiled with -// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). -// For msvc /W3 option will need to be used -// Note that for 'other' compilers this macro evaluates to nothing to prevent -// compilations errors. -#if defined(_MSC_VER) -#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) -#elif defined(__GNUC__) -#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) -#else -#define GTEST_INTERNAL_DEPRECATED(message) -#endif - -#endif // !defined(GTEST_INTERNAL_DEPRECATED) - -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL // Always use absl::any for UniversalPrinter<> specializations if googletest // is built with absl support. #define GTEST_INTERNAL_HAS_ANY 1 @@ -2304,8 +2377,9 @@ } // namespace internal } // namespace testing #else -#ifdef __has_include -#if __has_include(<any>) && __cplusplus >= 201703L +#if defined(__cpp_lib_any) || (GTEST_INTERNAL_HAS_INCLUDE(<any>) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L && \ + (!defined(_MSC_VER) || GTEST_HAS_RTTI)) // Otherwise for C++17 and higher use std::any for UniversalPrinter<> // specializations. #define GTEST_INTERNAL_HAS_ANY 1 @@ -2317,11 +2391,14 @@ } // namespace testing // The case where absl is configured NOT to alias std::any is not // supported. -#endif // __has_include(<any>) && __cplusplus >= 201703L -#endif // __has_include +#endif // __cpp_lib_any #endif // GTEST_HAS_ABSL -#if GTEST_HAS_ABSL +#ifndef GTEST_INTERNAL_HAS_ANY +#define GTEST_INTERNAL_HAS_ANY 0 +#endif + +#ifdef GTEST_HAS_ABSL // Always use absl::optional for UniversalPrinter<> specializations if // googletest is built with absl support. #define GTEST_INTERNAL_HAS_OPTIONAL 1 @@ -2334,8 +2411,8 @@ } // namespace internal } // namespace testing #else -#ifdef __has_include -#if __has_include(<optional>) && __cplusplus >= 201703L +#if defined(__cpp_lib_optional) || (GTEST_INTERNAL_HAS_INCLUDE(<optional>) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L) // Otherwise for C++17 and higher use std::optional for UniversalPrinter<> // specializations. #define GTEST_INTERNAL_HAS_OPTIONAL 1 @@ -2349,11 +2426,23 @@ } // namespace testing // The case where absl is configured NOT to alias std::optional is not // supported. -#endif // __has_include(<optional>) && __cplusplus >= 201703L -#endif // __has_include +#endif // __cpp_lib_optional #endif // GTEST_HAS_ABSL -#if GTEST_HAS_ABSL +#ifndef GTEST_INTERNAL_HAS_OPTIONAL +#define GTEST_INTERNAL_HAS_OPTIONAL 0 +#endif + +#if defined(__cpp_lib_span) || (GTEST_INTERNAL_HAS_INCLUDE(<span>) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L) +#define GTEST_INTERNAL_HAS_STD_SPAN 1 +#endif // __cpp_lib_span + +#ifndef GTEST_INTERNAL_HAS_STD_SPAN +#define GTEST_INTERNAL_HAS_STD_SPAN 0 +#endif + +#ifdef GTEST_HAS_ABSL // Always use absl::string_view for Matcher<> specializations if googletest // is built with absl support. #define GTEST_INTERNAL_HAS_STRING_VIEW 1 @@ -2364,8 +2453,9 @@ } // namespace internal } // namespace testing #else -#ifdef __has_include -#if __has_include(<string_view>) && __cplusplus >= 201703L +#if defined(__cpp_lib_string_view) || \ + (GTEST_INTERNAL_HAS_INCLUDE(<string_view>) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L) // Otherwise for C++17 and higher use std::string_view for Matcher<> // specializations. #define GTEST_INTERNAL_HAS_STRING_VIEW 1 @@ -2377,11 +2467,14 @@ } // namespace testing // The case where absl is configured NOT to alias std::string_view is not // supported. -#endif // __has_include(<string_view>) && __cplusplus >= 201703L -#endif // __has_include +#endif // __cpp_lib_string_view #endif // GTEST_HAS_ABSL -#if GTEST_HAS_ABSL +#ifndef GTEST_INTERNAL_HAS_STRING_VIEW +#define GTEST_INTERNAL_HAS_STRING_VIEW 0 +#endif + +#ifdef GTEST_HAS_ABSL // Always use absl::variant for UniversalPrinter<> specializations if googletest // is built with absl support. #define GTEST_INTERNAL_HAS_VARIANT 1 @@ -2393,8 +2486,8 @@ } // namespace internal } // namespace testing #else -#ifdef __has_include -#if __has_include(<variant>) && __cplusplus >= 201703L +#if defined(__cpp_lib_variant) || (GTEST_INTERNAL_HAS_INCLUDE(<variant>) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201703L) // Otherwise for C++17 and higher use std::variant for UniversalPrinter<> // specializations. #define GTEST_INTERNAL_HAS_VARIANT 1 @@ -2406,8 +2499,19 @@ } // namespace internal } // namespace testing // The case where absl is configured NOT to alias std::variant is not supported. -#endif // __has_include(<variant>) && __cplusplus >= 201703L -#endif // __has_include +#endif // __cpp_lib_variant #endif // GTEST_HAS_ABSL +#ifndef GTEST_INTERNAL_HAS_VARIANT +#define GTEST_INTERNAL_HAS_VARIANT 0 +#endif + +#if (defined(__cpp_lib_three_way_comparison) || \ + (GTEST_INTERNAL_HAS_INCLUDE(<compare>) && \ + GTEST_INTERNAL_CPLUSPLUS_LANG >= 201907L)) +#define GTEST_INTERNAL_HAS_COMPARE_LIB 1 +#else +#define GTEST_INTERNAL_HAS_COMPARE_LIB 0 +#endif + #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-string.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-string.h index cca2e1f..7c05b58 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-string.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-string.h
@@ -51,6 +51,7 @@ #include <string.h> #include <cstdint> +#include <sstream> #include <string> #include "gtest/internal/gtest-port.h" @@ -72,7 +73,7 @@ // memory using malloc(). static const char* CloneCString(const char* c_str); -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be // able to pass strings to Win32 APIs on CE we need to convert them // to 'Unicode', UTF-16.
diff --git a/third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h b/third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h index 6bc02a7..78da053 100644 --- a/third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h +++ b/third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h
@@ -37,6 +37,10 @@ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#include <string> +#include <type_traits> +#include <typeinfo> + #include "gtest/internal/gtest-port.h" // #ifdef __GNUC__ is too general here. It is possible to use gcc without using @@ -63,6 +67,22 @@ s.erase(strlen("std"), end - strlen("std")); } } + + // Strip redundant spaces in typename to match MSVC + // For example, std::pair<int, bool> -> std::pair<int,bool> + static const char to_search[] = ", "; + const char replace_char = ','; + size_t pos = 0; + while (true) { + // Get the next occurrence from the current position + pos = s.find(to_search, pos); + if (pos == std::string::npos) { + break; + } + // Replace this occurrence of substring + s.replace(pos, strlen(to_search), 1, replace_char); + ++pos; + } return s; } @@ -81,6 +101,20 @@ const std::string name_str(status == 0 ? readable_name : name); free(readable_name); return CanonicalizeForStdLibVersioning(name_str); +#elif defined(_MSC_VER) + // Strip struct and class due to differences between + // MSVC and other compilers. std::pair<int,bool> is printed as + // "struct std::pair<int,bool>" when using MSVC vs "std::pair<int, bool>" with + // other compilers. + std::string s = name; + // Only strip the leading "struct " and "class ", so uses rfind == 0 to + // ensure that + if (s.rfind("struct ", 0) == 0) { + s = s.substr(strlen("struct ")); + } else if (s.rfind("class ", 0) == 0) { + s = s.substr(strlen("class ")); + } + return s; #else return name; #endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
diff --git a/third_party/googletest/src/googletest/src/gtest-assertion-result.cc b/third_party/googletest/src/googletest/src/gtest-assertion-result.cc index f1c0b10..3998921 100644 --- a/third_party/googletest/src/googletest/src/gtest-assertion-result.cc +++ b/third_party/googletest/src/googletest/src/gtest-assertion-result.cc
@@ -44,7 +44,7 @@ // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult::AssertionResult(const AssertionResult& other) : success_(other.success_), - message_(other.message_.get() != nullptr + message_(other.message_ != nullptr ? new ::std::string(*other.message_) : static_cast< ::std::string*>(nullptr)) {} @@ -58,7 +58,7 @@ // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. AssertionResult AssertionResult::operator!() const { AssertionResult negation(!success_); - if (message_.get() != nullptr) negation << *message_; + if (message_ != nullptr) negation << *message_; return negation; }
diff --git a/third_party/googletest/src/googletest/src/gtest-death-test.cc b/third_party/googletest/src/googletest/src/gtest-death-test.cc index e6abc62..15472f1 100644 --- a/third_party/googletest/src/googletest/src/gtest-death-test.cc +++ b/third_party/googletest/src/googletest/src/gtest-death-test.cc
@@ -32,15 +32,21 @@ #include "gtest/gtest-death-test.h" +#include <stdlib.h> + #include <functional> +#include <memory> +#include <sstream> +#include <string> #include <utility> +#include <vector> #include "gtest/internal/custom/gtest.h" #include "gtest/internal/gtest-port.h" -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST -#if GTEST_OS_MAC +#ifdef GTEST_OS_MAC #include <crt_externs.h> #endif // GTEST_OS_MAC @@ -48,24 +54,24 @@ #include <fcntl.h> #include <limits.h> -#if GTEST_OS_LINUX +#ifdef GTEST_OS_LINUX #include <signal.h> #endif // GTEST_OS_LINUX #include <stdarg.h> -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS #include <windows.h> #else #include <sys/mman.h> #include <sys/wait.h> #endif // GTEST_OS_WINDOWS -#if GTEST_OS_QNX +#ifdef GTEST_OS_QNX #include <spawn.h> #endif // GTEST_OS_QNX -#if GTEST_OS_FUCHSIA +#ifdef GTEST_OS_FUCHSIA #include <lib/fdio/fd.h> #include <lib/fdio/io.h> #include <lib/fdio/spawn.h> @@ -111,7 +117,7 @@ GTEST_DEFINE_bool_( death_test_use_fork, testing::internal::BoolFromGTestEnv("death_test_use_fork", false), - "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Instructs to use fork()/_Exit() instead of clone() in death tests. " "Ignored and always uses fork() on POSIX systems where clone() is not " "implemented. Useful when running under valgrind or similar tools if " "those do not support clone(). Valgrind 3.3.1 will just fail if " @@ -131,13 +137,13 @@ namespace testing { -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST namespace internal { // Valid only for fast death tests. Indicates the code is running in the // child process of a fast style death test. -#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +#if !defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_FUCHSIA) static bool g_in_fast_death_test_child = false; #endif @@ -147,7 +153,7 @@ // tests. IMPORTANT: This is an internal utility. Using it may break the // implementation of death tests. User code MUST NOT use it. bool InDeathTestChild() { -#if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_FUCHSIA) // On Windows and Fuchsia, death tests are thread-safe regardless of the value // of the death_test_style flag. @@ -169,7 +175,7 @@ // ExitedWithCode function-call operator. bool ExitedWithCode::operator()(int exit_status) const { -#if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_FUCHSIA) return exit_status == exit_code_; @@ -180,7 +186,7 @@ #endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA } -#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +#if !defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_FUCHSIA) // KilledBySignal constructor. KilledBySignal::KilledBySignal(int signum) : signum_(signum) {} @@ -207,7 +213,7 @@ static std::string ExitSummary(int exit_code) { Message m; -#if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_FUCHSIA) m << "Exited with exit status " << exit_code; @@ -234,7 +240,7 @@ return !ExitedWithCode(0)(exit_status); } -#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +#if !defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_FUCHSIA) // Generates a textual failure message when a death test finds more than // one thread running, or cannot determine the number of threads, prior // to executing the given statement. It is the responsibility of the @@ -249,7 +255,7 @@ msg << "detected " << thread_count << " threads."; } msg << " See " - "https://github.com/google/googletest/blob/master/docs/" + "https://github.com/google/googletest/blob/main/docs/" "advanced.md#death-tests-and-threads" << " for more explanation and suggested solutions, especially if" << " this is the last message you see before your test times out."; @@ -263,7 +269,7 @@ static const char kDeathTestThrew = 'T'; static const char kDeathTestInternalError = 'I'; -#if GTEST_OS_FUCHSIA +#ifdef GTEST_OS_FUCHSIA // File descriptor used for the pipe in the child process. static const int kFuchsiaReadPipeFd = 3; @@ -284,7 +290,7 @@ // message is propagated back to the parent process. Otherwise, the // message is simply printed to stderr. In either case, the program // then exits with status 1. -static void DeathTestAbort(const std::string& message) { +[[noreturn]] static void DeathTestAbort(const std::string& message) { // On a POSIX system, this function may be called from a threadsafe-style // death test child process, which operates on a very small stack. Use // the heap for any additional non-minuscule memory requirements. @@ -295,7 +301,7 @@ fputc(kDeathTestInternalError, parent); fprintf(parent, "%s", message.c_str()); fflush(parent); - _exit(1); + _Exit(1); } else { fprintf(stderr, "%s", message.c_str()); fflush(stderr); @@ -507,7 +513,7 @@ // Signals that the death test code which should have exited, didn't. // Should be called only in a death test child process. // Writes a status byte to the child's status file descriptor, then -// calls _exit(1). +// calls _Exit(1). void DeathTestImpl::Abort(AbortReason reason) { // The parent process considers the death test to be a failure if // it finds any data in our pipe. So, here we write a single flag byte @@ -519,13 +525,13 @@ GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); // We are leaking the descriptor here because on some platforms (i.e., // when built as Windows DLL), destructors of global objects will still - // run after calling _exit(). On such systems, write_fd_ will be + // run after calling _Exit(). On such systems, write_fd_ will be // indirectly closed from the destructor of UnitTestImpl, causing double // close if it is also closed here. On debug configurations, double close // may assert. As there are no in-process buffers to flush here, we are // relying on the OS to close the descriptor after the process terminates // when the destructors are not run. - _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) + _Exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) } // Returns an indented copy of stderr output for a death test. @@ -621,7 +627,21 @@ return success; } -#if GTEST_OS_WINDOWS +#ifndef GTEST_OS_WINDOWS +// Note: The return value points into args, so the return value's lifetime is +// bound to that of args. +static std::vector<char*> CreateArgvFromArgs(std::vector<std::string>& args) { + std::vector<char*> result; + result.reserve(args.size() + 1); + for (auto& arg : args) { + result.push_back(&arg[0]); + } + result.push_back(nullptr); // Extra null terminator. + return result; +} +#endif + +#ifdef GTEST_OS_WINDOWS // WindowsDeathTest implements death tests on Windows. Due to the // specifics of starting new processes on Windows, death tests there are // always threadsafe, and Google Test considers the @@ -765,7 +785,7 @@ StreamableToString(static_cast<unsigned int>(::GetCurrentProcessId())) + // size_t has the same width as pointers on both 32-bit and 64-bit // Windows platforms. - // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + // See https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. "|" + StreamableToString(reinterpret_cast<size_t>(write_handle)) + "|" + StreamableToString(reinterpret_cast<size_t>(event_handle_.Get())); @@ -808,7 +828,7 @@ return OVERSEE_TEST; } -#elif GTEST_OS_FUCHSIA +#elif defined(GTEST_OS_FUCHSIA) class FuchsiaDeathTest : public DeathTestImpl { public: @@ -836,36 +856,6 @@ zx::socket stderr_socket_; }; -// Utility class for accumulating command-line arguments. -class Arguments { - public: - Arguments() { args_.push_back(nullptr); } - - ~Arguments() { - for (std::vector<char*>::iterator i = args_.begin(); i != args_.end(); - ++i) { - free(*i); - } - } - void AddArgument(const char* argument) { - args_.insert(args_.end() - 1, posix::StrDup(argument)); - } - - template <typename Str> - void AddArguments(const ::std::vector<Str>& arguments) { - for (typename ::std::vector<Str>::const_iterator i = arguments.begin(); - i != arguments.end(); ++i) { - args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); - } - } - char* const* Argv() { return &args_[0]; } - - int size() { return static_cast<int>(args_.size()) - 1; } - - private: - std::vector<char*> args_; -}; - // Waits for the child in a death test to exit, returning its exit // status, or 0 if no child process exists. As a side effect, sets the // outcome data member. @@ -986,10 +976,10 @@ kInternalRunDeathTestFlag + "=" + file_ + "|" + StreamableToString(line_) + "|" + StreamableToString(death_test_index); - Arguments args; - args.AddArguments(GetInjectableArgvs()); - args.AddArgument(filter_flag.c_str()); - args.AddArgument(internal_flag.c_str()); + + std::vector<std::string> args = GetInjectableArgvs(); + args.push_back(filter_flag); + args.push_back(internal_flag); // Build the pipe for communication with the child. zx_status_t status; @@ -1041,8 +1031,14 @@ GTEST_DEATH_TEST_CHECK_(status == ZX_OK); // Spawn the child process. - status = fdio_spawn_etc(child_job, FDIO_SPAWN_CLONE_ALL, args.Argv()[0], - args.Argv(), nullptr, 2, spawn_actions, + // Note: The test component must have `fuchsia.process.Launcher` declared + // in its manifest. (Fuchsia integration tests require creating a + // "Fuchsia Test Component" which contains a "Fuchsia Component Manifest") + // Launching processes is a privileged operation in Fuchsia, and the + // declaration indicates that the ability is required for the component. + std::vector<char*> argv = CreateArgvFromArgs(args); + status = fdio_spawn_etc(child_job, FDIO_SPAWN_CLONE_ALL, argv[0], argv.data(), + nullptr, 2, spawn_actions, child_process_.reset_and_get_address(), nullptr); GTEST_DEATH_TEST_CHECK_(status == ZX_OK); @@ -1134,7 +1130,7 @@ LogToStderr(); // Event forwarding to the listeners of event listener API mush be shut // down in death test subprocesses. - GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + GetUnitTestImpl()->listeners()->SuppressEventForwarding(true); g_in_fast_death_test_child = true; return EXECUTE_TEST; } else { @@ -1173,34 +1169,6 @@ const int line_; }; -// Utility class for accumulating command-line arguments. -class Arguments { - public: - Arguments() { args_.push_back(nullptr); } - - ~Arguments() { - for (std::vector<char*>::iterator i = args_.begin(); i != args_.end(); - ++i) { - free(*i); - } - } - void AddArgument(const char* argument) { - args_.insert(args_.end() - 1, posix::StrDup(argument)); - } - - template <typename Str> - void AddArguments(const ::std::vector<Str>& arguments) { - for (typename ::std::vector<Str>::const_iterator i = arguments.begin(); - i != arguments.end(); ++i) { - args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); - } - } - char* const* Argv() { return &args_[0]; } - - private: - std::vector<char*> args_; -}; - // A struct that encompasses the arguments to the child process of a // threadsafe-style death test process. struct ExecDeathTestArgs { @@ -1208,7 +1176,7 @@ int close_fd; // File descriptor to close; the read end of a pipe }; -#if GTEST_OS_QNX +#ifdef GTEST_OS_QNX extern "C" char** environ; #else // GTEST_OS_QNX // The main function for a threadsafe-style death test child process. @@ -1289,7 +1257,7 @@ ExecDeathTestArgs args = {argv, close_fd}; pid_t child_pid = -1; -#if GTEST_OS_QNX +#ifdef GTEST_OS_QNX // Obtains the current directory and sets it to be closed in the child // process. const int cwd_fd = open(".", O_RDONLY); @@ -1320,7 +1288,7 @@ GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); #else // GTEST_OS_QNX -#if GTEST_OS_LINUX +#ifdef GTEST_OS_LINUX // When a SIGPROF signal is received while fork() or clone() are executing, // the process may hang. To avoid this, we ignore SIGPROF here and re-enable // it after the call to fork()/clone() is complete. @@ -1367,11 +1335,10 @@ #endif // GTEST_HAS_CLONE if (use_fork && (child_pid = fork()) == 0) { - ExecDeathTestChildMain(&args); - _exit(0); + _Exit(ExecDeathTestChildMain(&args)); } #endif // GTEST_OS_QNX -#if GTEST_OS_LINUX +#ifdef GTEST_OS_LINUX GTEST_DEATH_TEST_CHECK_SYSCALL_( sigaction(SIGPROF, &saved_sigprof_action, nullptr)); #endif // GTEST_OS_LINUX @@ -1410,10 +1377,9 @@ StreamableToString(line_) + "|" + StreamableToString(death_test_index) + "|" + StreamableToString(pipe_fd[1]); - Arguments args; - args.AddArguments(GetArgvsForDeathTestChildProcess()); - args.AddArgument(filter_flag.c_str()); - args.AddArgument(internal_flag.c_str()); + std::vector<std::string> args = GetArgvsForDeathTestChildProcess(); + args.push_back(filter_flag); + args.push_back(internal_flag); DeathTest::set_last_death_test_message(""); @@ -1422,7 +1388,8 @@ // is necessary. FlushInfoLog(); - const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + std::vector<char*> argv = CreateArgvFromArgs(args); + const pid_t child_pid = ExecDeathTestSpawnChild(argv.data(), pipe_fd[0]); GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); set_child_pid(child_pid); set_read_fd(pipe_fd[0]); @@ -1463,14 +1430,14 @@ } } -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS if (GTEST_FLAG_GET(death_test_style) == "threadsafe" || GTEST_FLAG_GET(death_test_style) == "fast") { *test = new WindowsDeathTest(statement, std::move(matcher), file, line); } -#elif GTEST_OS_FUCHSIA +#elif defined(GTEST_OS_FUCHSIA) if (GTEST_FLAG_GET(death_test_style) == "threadsafe" || GTEST_FLAG_GET(death_test_style) == "fast") { @@ -1497,7 +1464,7 @@ return true; } -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Recreates the pipe and event handles from the provided parameters, // signals the event, and returns a file descriptor wrapped around the pipe // handle. This function is called in the child process only. @@ -1564,7 +1531,7 @@ // initialized from the GTEST_FLAG(internal_run_death_test) flag if // the flag is specified; otherwise returns NULL. InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { - if (GTEST_FLAG_GET(internal_run_death_test) == "") return nullptr; + if (GTEST_FLAG_GET(internal_run_death_test).empty()) return nullptr; // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we // can use it here. @@ -1574,7 +1541,7 @@ SplitString(GTEST_FLAG_GET(internal_run_death_test), '|', &fields); int write_fd = -1; -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS unsigned int parent_process_id = 0; size_t write_handle_as_size_t = 0; @@ -1591,7 +1558,7 @@ write_fd = GetStatusFileDescriptor(parent_process_id, write_handle_as_size_t, event_handle_as_size_t); -#elif GTEST_OS_FUCHSIA +#elif defined(GTEST_OS_FUCHSIA) if (fields.size() != 3 || !ParseNaturalNumber(fields[1], &line) || !ParseNaturalNumber(fields[2], &index)) {
diff --git a/third_party/googletest/src/googletest/src/gtest-filepath.cc b/third_party/googletest/src/googletest/src/gtest-filepath.cc index f6ee90c..902d8c7 100644 --- a/third_party/googletest/src/googletest/src/gtest-filepath.cc +++ b/third_party/googletest/src/googletest/src/gtest-filepath.cc
@@ -31,12 +31,15 @@ #include <stdlib.h> +#include <iterator> +#include <string> + #include "gtest/gtest-message.h" #include "gtest/internal/gtest-port.h" -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE #include <windows.h> -#elif GTEST_OS_WINDOWS +#elif defined(GTEST_OS_WINDOWS) #include <direct.h> #include <io.h> #else @@ -47,7 +50,7 @@ #include "gtest/internal/gtest-string.h" -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS #define GTEST_PATH_MAX_ _MAX_PATH #elif defined(PATH_MAX) #define GTEST_PATH_MAX_ PATH_MAX @@ -57,10 +60,12 @@ #define GTEST_PATH_MAX_ _POSIX_PATH_MAX #endif // GTEST_OS_WINDOWS +#if GTEST_HAS_FILE_SYSTEM + namespace testing { namespace internal { -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // On Windows, '\\' is the standard path separator, but many tools and the // Windows API also accept '/' as an alternate path separator. Unless otherwise // noted, a file path can contain either kind of path separators, or a mixture @@ -68,7 +73,7 @@ const char kPathSeparator = '\\'; const char kAlternatePathSeparator = '/'; const char kAlternatePathSeparatorString[] = "/"; -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE // Windows CE doesn't have a current directory. You should not use // the current directory in tests on Windows CE, but this at least // provides a reasonable fallback. @@ -94,19 +99,21 @@ // Returns the current working directory, or "" if unsuccessful. FilePath FilePath::GetCurrentDir() { -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ - GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \ - GTEST_OS_XTENSA +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_PHONE) || \ + defined(GTEST_OS_WINDOWS_RT) || defined(GTEST_OS_ESP8266) || \ + defined(GTEST_OS_ESP32) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) || defined(GTEST_OS_NXP_QN9090) || \ + defined(GTEST_OS_NRF52) // These platforms do not have a current directory, so we just return // something reasonable. return FilePath(kCurrentDirectoryString); -#elif GTEST_OS_WINDOWS +#elif defined(GTEST_OS_WINDOWS) char cwd[GTEST_PATH_MAX_ + 1] = {'\0'}; return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd); #else char cwd[GTEST_PATH_MAX_ + 1] = {'\0'}; char* result = getcwd(cwd, sizeof(cwd)); -#if GTEST_OS_NACL +#ifdef GTEST_OS_NACL // getcwd will likely fail in NaCl due to the sandbox, so return something // reasonable. The user may have provided a shim implementation for getcwd, // however, so fallback only when failure is detected. @@ -145,6 +152,44 @@ return last_sep; } +size_t FilePath::CalculateRootLength() const { + const auto& path = pathname_; + auto s = path.begin(); + auto end = path.end(); +#ifdef GTEST_OS_WINDOWS + if (end - s >= 2 && s[1] == ':' && (end - s == 2 || IsPathSeparator(s[2])) && + (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))) { + // A typical absolute path like "C:\Windows" or "D:" + s += 2; + if (s != end) { + ++s; + } + } else if (end - s >= 3 && IsPathSeparator(*s) && IsPathSeparator(*(s + 1)) && + !IsPathSeparator(*(s + 2))) { + // Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder" + s += 2; + // Skip 2 components and their following separators ("Server\" and "Share\") + for (int i = 0; i < 2; ++i) { + while (s != end) { + bool stop = IsPathSeparator(*s); + ++s; + if (stop) { + break; + } + } + } + } else if (s != end && IsPathSeparator(*s)) { + // A drive-rooted path like "\Windows" + ++s; + } +#else + if (s != end && IsPathSeparator(*s)) { + ++s; + } +#endif + return static_cast<size_t>(s - path.begin()); +} + // Returns a copy of the FilePath with the directory part removed. // Example: FilePath("path/to/file").RemoveDirectoryName() returns // FilePath("file"). If there is no directory part ("just_a_file"), it returns @@ -204,7 +249,7 @@ // Returns true if pathname describes something findable in the file-system, // either a file, directory, or whatever. bool FilePath::FileOrDirectoryExists() const { -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); const DWORD attributes = GetFileAttributes(unicode); delete[] unicode; @@ -219,7 +264,7 @@ // that exists. bool FilePath::DirectoryExists() const { bool result = false; -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Don't strip off trailing separator if path is a root directory on // Windows (like "C:\\"). const FilePath& path(IsRootDirectory() ? *this @@ -228,7 +273,7 @@ const FilePath& path(*this); #endif -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); const DWORD attributes = GetFileAttributes(unicode); delete[] unicode; @@ -246,27 +291,15 @@ } // Returns true if pathname describes a root directory. (Windows has one -// root directory per disk drive.) +// root directory per disk drive. UNC share roots are also included.) bool FilePath::IsRootDirectory() const { -#if GTEST_OS_WINDOWS - return pathname_.length() == 3 && IsAbsolutePath(); -#else - return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); -#endif + size_t root_length = CalculateRootLength(); + return root_length > 0 && root_length == pathname_.size() && + IsPathSeparator(pathname_[root_length - 1]); } // Returns true if pathname describes an absolute path. -bool FilePath::IsAbsolutePath() const { - const char* const name = pathname_.c_str(); -#if GTEST_OS_WINDOWS - return pathname_.length() >= 3 && - ((name[0] >= 'a' && name[0] <= 'z') || - (name[0] >= 'A' && name[0] <= 'Z')) && - name[1] == ':' && IsPathSeparator(name[2]); -#else - return IsPathSeparator(name[0]); -#endif -} +bool FilePath::IsAbsolutePath() const { return CalculateRootLength() > 0; } // Returns a pathname for a file that does not currently exist. The pathname // will be directory/base_name.extension or @@ -303,7 +336,7 @@ return false; } - if (pathname_.length() == 0 || this->DirectoryExists()) { + if (pathname_.empty() || this->DirectoryExists()) { return true; } @@ -316,14 +349,16 @@ // directory for any reason, including if the parent directory does not // exist. Not named "CreateDirectory" because that's a macro on Windows. bool FilePath::CreateFolder() const { -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE FilePath removed_sep(this->RemoveTrailingPathSeparator()); LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); int result = CreateDirectory(unicode, nullptr) ? 0 : -1; delete[] unicode; -#elif GTEST_OS_WINDOWS +#elif defined(GTEST_OS_WINDOWS) int result = _mkdir(pathname_.c_str()); -#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA +#elif defined(GTEST_OS_ESP8266) || defined(GTEST_OS_XTENSA) || \ + defined(GTEST_OS_QURT) || defined(GTEST_OS_NXP_QN9090) || \ + defined(GTEST_OS_NRF52) // do nothing int result = 0; #else @@ -347,17 +382,27 @@ // Removes any redundant separators that might be in the pathname. // For example, "bar///foo" becomes "bar/foo". Does not eliminate other // redundancies that might be in a pathname involving "." or "..". +// Note that "\\Host\Share" does not contain a redundancy on Windows! void FilePath::Normalize() { auto out = pathname_.begin(); - for (const char character : pathname_) { + auto i = pathname_.cbegin(); +#ifdef GTEST_OS_WINDOWS + // UNC paths are treated specially + if (pathname_.end() - i >= 3 && IsPathSeparator(*i) && + IsPathSeparator(*(i + 1)) && !IsPathSeparator(*(i + 2))) { + *(out++) = kPathSeparator; + *(out++) = kPathSeparator; + } +#endif + while (i != pathname_.end()) { + const char character = *i; if (!IsPathSeparator(character)) { *(out++) = character; } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) { *(out++) = kPathSeparator; - } else { - continue; } + ++i; } pathname_.erase(out, pathname_.end()); @@ -365,3 +410,5 @@ } // namespace internal } // namespace testing + +#endif // GTEST_HAS_FILE_SYSTEM
diff --git a/third_party/googletest/src/googletest/src/gtest-internal-inl.h b/third_party/googletest/src/googletest/src/gtest-internal-inl.h index 0b9e929..6a39b93 100644 --- a/third_party/googletest/src/googletest/src/gtest-internal-inl.h +++ b/third_party/googletest/src/googletest/src/gtest-internal-inl.h
@@ -44,7 +44,9 @@ #include <algorithm> #include <cstdint> #include <memory> +#include <set> #include <string> +#include <unordered_map> #include <vector> #include "gtest/internal/gtest-port.h" @@ -54,7 +56,7 @@ #include <netdb.h> // NOLINT #endif -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS #include <windows.h> // NOLINT #endif // GTEST_OS_WINDOWS @@ -91,7 +93,8 @@ // Returns true if and only if Google Test should use colors in the output. GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); -// Formats the given time in milliseconds as seconds. +// Formats the given time in milliseconds as seconds. If the input is an exact N +// seconds, the output has a trailing decimal point (e.g., "N." instead of "N"). GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); // Converts the given time in milliseconds to a date string in the ISO 8601 @@ -212,7 +215,7 @@ int32_t stack_trace_depth_; std::string stream_result_to_; bool throw_on_failure_; -} GTEST_ATTRIBUTE_UNUSED_; +}; // Converts a Unicode code point to a narrow string in UTF-8 encoding. // code_point parameter is of type UInt32 because wchar_t may not be @@ -310,7 +313,7 @@ << begin << ", " << size << "]."; // Fisher-Yates shuffle, from - // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + // https://en.wikipedia.org/wiki/Fisher-Yates_shuffle for (int range_width = end - begin; range_width >= 2; range_width--) { const int last_in_range = begin + range_width - 1; const int selected = @@ -382,13 +385,13 @@ static bool FilterMatchesTest(const std::string& test_suite_name, const std::string& test_name); -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Function for supporting the gtest_catch_exception flag. - // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the - // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // Returns EXCEPTION_EXECUTE_HANDLER if given SEH exception was handled, or + // EXCEPTION_CONTINUE_SEARCH otherwise. // This function is useful as an __except condition. - static int GTestShouldProcessSEH(DWORD exception_code); + static int GTestProcessSEH(DWORD seh_code, const char* location); #endif // GTEST_OS_WINDOWS // Returns true if "name" matches the ':' separated list of glob-style @@ -396,15 +399,17 @@ static bool MatchesFilter(const std::string& name, const char* filter); }; +#if GTEST_HAS_FILE_SYSTEM // Returns the current application's name, removing directory path if that // is present. Used by UnitTestOptions::GetOutputFile. GTEST_API_ FilePath GetCurrentExecutableName(); +#endif // GTEST_HAS_FILE_SYSTEM // The role interface for getting the OS stack trace as a string. class OsStackTraceGetterInterface { public: - OsStackTraceGetterInterface() {} - virtual ~OsStackTraceGetterInterface() {} + OsStackTraceGetterInterface() = default; + virtual ~OsStackTraceGetterInterface() = default; // Returns the current OS stack trace as an std::string. Parameters: // @@ -432,13 +437,13 @@ // A working implementation of the OsStackTraceGetterInterface interface. class OsStackTraceGetter : public OsStackTraceGetterInterface { public: - OsStackTraceGetter() {} + OsStackTraceGetter() = default; std::string CurrentStackTrace(int max_depth, int skip_count) override; void UponLeavingGTest() override; private: -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL Mutex mutex_; // Protects all internal state. // We save the stack frame below the frame that calls user code. @@ -507,9 +512,9 @@ virtual ~UnitTestImpl(); // There are two different ways to register your own TestPartResultReporter. - // You can register your own repoter to listen either only for test results + // You can register your own reporter to listen either only for test results // from the current thread or for results from all threads. - // By default, each per-thread test result repoter just passes a new + // By default, each per-thread test result reporter just passes a new // TestPartResult to the global test result reporter, which registers the // test part result for the currently running test. @@ -645,13 +650,15 @@ // this is not a typed or a type-parameterized test. // set_up_tc: pointer to the function that sets up the test suite // tear_down_tc: pointer to the function that tears down the test suite - TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param, + TestSuite* GetTestSuite(const std::string& test_suite_name, + const char* type_param, internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc); // Legacy API is deprecated but still available #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ - TestCase* GetTestCase(const char* test_case_name, const char* type_param, + TestCase* GetTestCase(const std::string& test_case_name, + const char* type_param, internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc) { return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc); @@ -668,7 +675,7 @@ void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc, TestInfo* test_info) { -#if GTEST_HAS_DEATH_TEST +#if GTEST_HAS_FILE_SYSTEM // In order to support thread-safe death tests, we need to // remember the original working directory when the test program // was first invoked. We cannot do this in RUN_ALL_TESTS(), as @@ -677,13 +684,13 @@ // AddTestInfo(), which is called to register a TEST or TEST_F // before main() is reached. if (original_working_dir_.IsEmpty()) { - original_working_dir_.Set(FilePath::GetCurrentDir()); + original_working_dir_ = FilePath::GetCurrentDir(); GTEST_CHECK_(!original_working_dir_.IsEmpty()) << "Failed to get the current working directory."; } -#endif // GTEST_HAS_DEATH_TEST +#endif // GTEST_HAS_FILE_SYSTEM - GetTestSuite(test_info->test_suite_name(), test_info->type_param(), + GetTestSuite(test_info->test_suite_name_, test_info->type_param(), set_up_tc, tear_down_tc) ->AddTestInfo(test_info); } @@ -705,18 +712,6 @@ return type_parameterized_test_registry_; } - // Sets the TestSuite object for the test that's currently running. - void set_current_test_suite(TestSuite* a_current_test_suite) { - current_test_suite_ = a_current_test_suite; - } - - // Sets the TestInfo object for the test that's currently running. If - // current_test_info is NULL, the assertion results will be stored in - // ad_hoc_test_result_. - void set_current_test_info(TestInfo* a_current_test_info) { - current_test_info_ = a_current_test_info; - } - // Registers all parameterized tests defined using TEST_P and // INSTANTIATE_TEST_SUITE_P, creating regular tests for each test/parameter // combination. This method can be called more then once; it has guards @@ -774,7 +769,7 @@ return gtest_trace_stack_.get(); } -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST void InitDeathTestSubprocessControlInfo() { internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); } @@ -831,18 +826,42 @@ bool catch_exceptions() const { return catch_exceptions_; } private: + // Returns true if a warning should be issued if no tests match the test + // filter flag. + bool ShouldWarnIfNoTestsMatchFilter() const; + + struct CompareTestSuitesByPointer { + bool operator()(const TestSuite* lhs, const TestSuite* rhs) const { + return lhs->name_ < rhs->name_; + } + }; + friend class ::testing::UnitTest; // Used by UnitTest::Run() to capture the state of // GTEST_FLAG(catch_exceptions) at the moment it starts. void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + // Sets the TestSuite object for the test that's currently running. + void set_current_test_suite(TestSuite* a_current_test_suite) { + current_test_suite_ = a_current_test_suite; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + // The UnitTest object that owns this implementation object. UnitTest* const parent_; +#if GTEST_HAS_FILE_SYSTEM // The working directory when the first TEST() or TEST_F() was // executed. internal::FilePath original_working_dir_; +#endif // GTEST_HAS_FILE_SYSTEM // The default test part result reporters. DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; @@ -850,7 +869,7 @@ default_per_thread_test_part_result_reporter_; // Points to (but doesn't own) the global test part result reporter. - TestPartResultReporterInterface* global_test_part_result_repoter_; + TestPartResultReporterInterface* global_test_part_result_reporter_; // Protects read and write access to global_test_part_result_reporter_. internal::Mutex global_test_part_result_reporter_mutex_; @@ -867,6 +886,9 @@ // elements in the vector. std::vector<TestSuite*> test_suites_; + // The set of TestSuites by name. + std::unordered_map<std::string, TestSuite*> test_suites_by_name_; + // Provides a level of indirection for the test suite list to allow // easy shuffling and restoring the test suite order. The i-th // element of this vector is the index of the i-th test suite in the @@ -937,7 +959,7 @@ // How long the test took to run, in milliseconds. TimeInMillis elapsed_time_; -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST // The decomposed components of the gtest_internal_run_death_test flag, // parsed when RUN_ALL_TESTS is called. std::unique_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_; @@ -961,7 +983,7 @@ return UnitTest::GetInstance()->impl(); } -#if GTEST_USES_SIMPLE_RE +#ifdef GTEST_USES_SIMPLE_RE // Internal helper functions for implementing the simple regular // expression matcher. @@ -987,7 +1009,7 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST // Returns the message describing the last system error, regardless of the // platform. @@ -1058,7 +1080,7 @@ // Abstract base class for writing strings to a socket. class AbstractSocketWriter { public: - virtual ~AbstractSocketWriter() {} + virtual ~AbstractSocketWriter() = default; // Sends a string to the socket. virtual void Send(const std::string& message) = 0;
diff --git a/third_party/googletest/src/googletest/src/gtest-port.cc b/third_party/googletest/src/googletest/src/gtest-port.cc index d797fe4..53d41d3 100644 --- a/third_party/googletest/src/googletest/src/gtest-port.cc +++ b/third_party/googletest/src/googletest/src/gtest-port.cc
@@ -37,8 +37,12 @@ #include <cstdint> #include <fstream> #include <memory> +#include <ostream> +#include <string> +#include <utility> +#include <vector> -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS #include <io.h> #include <sys/stat.h> #include <windows.h> @@ -51,32 +55,34 @@ #include <unistd.h> #endif // GTEST_OS_WINDOWS -#if GTEST_OS_MAC +#ifdef GTEST_OS_MAC #include <mach/mach_init.h> #include <mach/task.h> #include <mach/vm_map.h> #endif // GTEST_OS_MAC -#if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \ - GTEST_OS_NETBSD || GTEST_OS_OPENBSD +#if defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_GNU_KFREEBSD) || defined(GTEST_OS_NETBSD) || \ + defined(GTEST_OS_OPENBSD) #include <sys/sysctl.h> -#if GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD +#if defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_GNU_KFREEBSD) #include <sys/user.h> #endif #endif -#if GTEST_OS_QNX +#ifdef GTEST_OS_QNX #include <devctl.h> #include <fcntl.h> #include <sys/procfs.h> #endif // GTEST_OS_QNX -#if GTEST_OS_AIX +#ifdef GTEST_OS_AIX #include <procinfo.h> #include <sys/types.h> #endif // GTEST_OS_AIX -#if GTEST_OS_FUCHSIA +#ifdef GTEST_OS_FUCHSIA #include <zircon/process.h> #include <zircon/syscalls.h> #endif // GTEST_OS_FUCHSIA @@ -90,7 +96,7 @@ namespace testing { namespace internal { -#if GTEST_OS_LINUX || GTEST_OS_GNU_HURD +#if defined(GTEST_OS_LINUX) || defined(GTEST_OS_GNU_HURD) namespace { template <typename T> @@ -113,7 +119,7 @@ return ReadProcFileField<size_t>(filename, 19); } -#elif GTEST_OS_MAC +#elif defined(GTEST_OS_MAC) size_t GetThreadCount() { const task_t task = mach_task_self(); @@ -131,20 +137,20 @@ } } -#elif GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \ - GTEST_OS_NETBSD +#elif defined(GTEST_OS_DRAGONFLY) || defined(GTEST_OS_FREEBSD) || \ + defined(GTEST_OS_GNU_KFREEBSD) || defined(GTEST_OS_NETBSD) -#if GTEST_OS_NETBSD +#ifdef GTEST_OS_NETBSD #undef KERN_PROC #define KERN_PROC KERN_PROC2 #define kinfo_proc kinfo_proc2 #endif -#if GTEST_OS_DRAGONFLY +#ifdef GTEST_OS_DRAGONFLY #define KP_NLWP(kp) (kp.kp_nthreads) -#elif GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD +#elif defined(GTEST_OS_FREEBSD) || defined(GTEST_OS_GNU_KFREEBSD) #define KP_NLWP(kp) (kp.ki_numthreads) -#elif GTEST_OS_NETBSD +#elif defined(GTEST_OS_NETBSD) #define KP_NLWP(kp) (kp.p_nlwps) #endif @@ -152,13 +158,13 @@ // we cannot detect it. size_t GetThreadCount() { int mib[] = { - CTL_KERN, - KERN_PROC, - KERN_PROC_PID, - getpid(), -#if GTEST_OS_NETBSD - sizeof(struct kinfo_proc), - 1, + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid(), +#ifdef GTEST_OS_NETBSD + sizeof(struct kinfo_proc), + 1, #endif }; u_int miblen = sizeof(mib) / sizeof(mib[0]); @@ -169,7 +175,7 @@ } return static_cast<size_t>(KP_NLWP(info)); } -#elif GTEST_OS_OPENBSD +#elif defined(GTEST_OS_OPENBSD) // Returns the number of threads running in the process, or 0 to indicate that // we cannot detect it. @@ -193,8 +199,8 @@ mib[5] = static_cast<int>(size / static_cast<size_t>(mib[4])); // populate array of structs - struct kinfo_proc info[mib[5]]; - if (sysctl(mib, miblen, &info, &size, NULL, 0)) { + std::vector<struct kinfo_proc> info(mib[5]); + if (sysctl(mib, miblen, info.data(), &size, NULL, 0)) { return 0; } @@ -206,7 +212,7 @@ return nthreads; } -#elif GTEST_OS_QNX +#elif defined(GTEST_OS_QNX) // Returns the number of threads running in the process, or 0 to indicate that // we cannot detect it. @@ -226,7 +232,7 @@ } } -#elif GTEST_OS_AIX +#elif defined(GTEST_OS_AIX) size_t GetThreadCount() { struct procentry64 entry; @@ -239,7 +245,7 @@ } } -#elif GTEST_OS_FUCHSIA +#elif defined(GTEST_OS_FUCHSIA) size_t GetThreadCount() { int dummy_buffer; @@ -264,7 +270,7 @@ #endif // GTEST_OS_LINUX -#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS +#if defined(GTEST_IS_THREADSAFE) && defined(GTEST_OS_WINDOWS) AutoHandle::AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} @@ -296,6 +302,22 @@ return handle_ != nullptr && handle_ != INVALID_HANDLE_VALUE; } +#if !GTEST_HAS_NOTIFICATION_ && defined(GTEST_OS_WINDOWS_MINGW) +Notification::Notification() { + // Create a manual-reset event object. + event_ = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + GTEST_CHECK_(event_ != nullptr); +} + +Notification::~Notification() { ::CloseHandle(event_); } + +void Notification::Notify() { GTEST_CHECK_(::SetEvent(event_)); } + +void Notification::WaitForNotification() { + GTEST_CHECK_(::WaitForSingleObject(event_, INFINITE) == WAIT_OBJECT_0); +} +#endif // !GTEST_HAS_NOTIFICATION_ && defined(GTEST_OS_WINDOWS_MINGW) + Mutex::Mutex() : owner_thread_id_(0), type_(kDynamic), @@ -581,9 +603,11 @@ // thread's ID. typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals; - // Holds the thread id and thread handle that we pass from - // StartWatcherThreadFor to WatcherThreadFunc. - typedef std::pair<DWORD, HANDLE> ThreadIdAndHandle; + struct WatcherThreadParams { + DWORD thread_id; + HANDLE handle; + Notification has_initialized; + }; static void StartWatcherThreadFor(DWORD thread_id) { // The returned handle will be kept in thread_map and closed by @@ -591,15 +615,20 @@ HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, thread_id); GTEST_CHECK_(thread != nullptr); + + WatcherThreadParams* watcher_thread_params = new WatcherThreadParams; + watcher_thread_params->thread_id = thread_id; + watcher_thread_params->handle = thread; + // We need to pass a valid thread ID pointer into CreateThread for it // to work correctly under Win98. DWORD watcher_thread_id; - HANDLE watcher_thread = ::CreateThread( - nullptr, // Default security. - 0, // Default stack size - &ThreadLocalRegistryImpl::WatcherThreadFunc, - reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)), - CREATE_SUSPENDED, &watcher_thread_id); + HANDLE watcher_thread = + ::CreateThread(nullptr, // Default security. + 0, // Default stack size + &ThreadLocalRegistryImpl::WatcherThreadFunc, + reinterpret_cast<LPVOID>(watcher_thread_params), + CREATE_SUSPENDED, &watcher_thread_id); GTEST_CHECK_(watcher_thread != nullptr) << "CreateThread failed with error " << ::GetLastError() << "."; // Give the watcher thread the same priority as ours to avoid being @@ -608,17 +637,25 @@ ::GetThreadPriority(::GetCurrentThread())); ::ResumeThread(watcher_thread); ::CloseHandle(watcher_thread); + + // Wait for the watcher thread to start to avoid race conditions. + // One specific race condition that can happen is that we have returned + // from main and have started to tear down, the newly spawned watcher + // thread may access already-freed variables, like global shared_ptrs. + watcher_thread_params->has_initialized.WaitForNotification(); } // Monitors exit from a given thread and notifies those // ThreadIdToThreadLocals about thread termination. static DWORD WINAPI WatcherThreadFunc(LPVOID param) { - const ThreadIdAndHandle* tah = - reinterpret_cast<const ThreadIdAndHandle*>(param); - GTEST_CHECK_(::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0); - OnThreadExit(tah->first); - ::CloseHandle(tah->second); - delete tah; + WatcherThreadParams* watcher_thread_params = + reinterpret_cast<WatcherThreadParams*>(param); + watcher_thread_params->has_initialized.Notify(); + GTEST_CHECK_(::WaitForSingleObject(watcher_thread_params->handle, + INFINITE) == WAIT_OBJECT_0); + OnThreadExit(watcher_thread_params->thread_id); + ::CloseHandle(watcher_thread_params->handle); + delete watcher_thread_params; return 0; } @@ -655,7 +692,7 @@ #endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS -#if GTEST_USES_POSIX_RE +#ifdef GTEST_USES_POSIX_RE // Implements RE. Currently only needed for death tests. @@ -668,7 +705,6 @@ regfree(&partial_regex_); regfree(&full_regex_); } - free(const_cast<char*>(pattern_)); } // Returns true if and only if regular expression re matches the entire str. @@ -690,7 +726,18 @@ // Initializes an RE from its string representation. void RE::Init(const char* regex) { - pattern_ = posix::StrDup(regex); + pattern_ = regex; + + // NetBSD (and Android, which takes its regex implemntation from NetBSD) does + // not include the GNU regex extensions (such as Perl style character classes + // like \w) in REG_EXTENDED. REG_EXTENDED is only specified to include the + // [[:alpha:]] style character classes. Enable REG_GNU wherever it is defined + // so users can use those extensions. +#if defined(REG_GNU) + constexpr int reg_flags = REG_EXTENDED | REG_GNU; +#else + constexpr int reg_flags = REG_EXTENDED; +#endif // Reserves enough bytes to hold the regular expression used for a // full match. @@ -698,7 +745,7 @@ char* const full_pattern = new char[full_regex_len]; snprintf(full_pattern, full_regex_len, "^(%s)$", regex); - is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + is_valid_ = regcomp(&full_regex_, full_pattern, reg_flags) == 0; // We want to call regcomp(&partial_regex_, ...) even if the // previous expression returns false. Otherwise partial_regex_ may // not be properly initialized can may cause trouble when it's @@ -709,7 +756,7 @@ // regex. We change it to an equivalent form "()" to be safe. if (is_valid_) { const char* const partial_regex = (*regex == '\0') ? "()" : regex; - is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + is_valid_ = regcomp(&partial_regex_, partial_regex, reg_flags) == 0; } EXPECT_TRUE(is_valid_) << "Regular expression \"" << regex @@ -718,7 +765,7 @@ delete[] full_pattern; } -#elif GTEST_USES_SIMPLE_RE +#elif defined(GTEST_USES_SIMPLE_RE) // Returns true if and only if ch appears anywhere in str (excluding the // terminating '\0' character). @@ -920,27 +967,26 @@ // Implements the RE class. -RE::~RE() { - free(const_cast<char*>(pattern_)); - free(const_cast<char*>(full_pattern_)); -} +RE::~RE() = default; // Returns true if and only if regular expression re matches the entire str. bool RE::FullMatch(const char* str, const RE& re) { - return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_.c_str(), str); } // Returns true if and only if regular expression re matches a substring of // str (including str itself). bool RE::PartialMatch(const char* str, const RE& re) { - return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); + return re.is_valid_ && MatchRegexAnywhere(re.pattern_.c_str(), str); } // Initializes an RE from its string representation. void RE::Init(const char* regex) { - pattern_ = full_pattern_ = nullptr; + full_pattern_.clear(); + pattern_.clear(); + if (regex != nullptr) { - pattern_ = posix::StrDup(regex); + pattern_ = regex; } is_valid_ = ValidateRegex(regex); @@ -949,25 +995,19 @@ return; } - const size_t len = strlen(regex); // Reserves enough bytes to hold the regular expression used for a - // full match: we need space to prepend a '^', append a '$', and - // terminate the string with '\0'. - char* buffer = static_cast<char*>(malloc(len + 3)); - full_pattern_ = buffer; + // full match: we need space to prepend a '^' and append a '$'. + full_pattern_.reserve(pattern_.size() + 2); - if (*regex != '^') - *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + if (pattern_.empty() || pattern_.front() != '^') { + full_pattern_.push_back('^'); // Makes sure full_pattern_ starts with '^'. + } - // We don't use snprintf or strncpy, as they trigger a warning when - // compiled with VC++ 8.0. - memcpy(buffer, regex, len); - buffer += len; + full_pattern_.append(pattern_); - if (len == 0 || regex[len - 1] != '$') - *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. - - *buffer = '\0'; + if (pattern_.empty() || pattern_.back() != '$') { + full_pattern_.push_back('$'); // Makes sure full_pattern_ ends with '$'. + } } #endif // GTEST_USES_POSIX_RE @@ -1024,18 +1064,28 @@ } } +#if GTEST_HAS_STREAM_REDIRECTION + // Disable Microsoft deprecation warnings for POSIX functions called from // this class (creat, dup, dup2, and close) GTEST_DISABLE_MSC_DEPRECATED_PUSH_() -#if GTEST_HAS_STREAM_REDIRECTION +namespace { + +#if defined(GTEST_OS_LINUX_ANDROID) || defined(GTEST_OS_IOS) +bool EndsWithPathSeparator(const std::string& path) { + return !path.empty() && path.back() == GTEST_PATH_SEP_[0]; +} +#endif + +} // namespace // Object that captures an output stream (stdout/stderr). class CapturedStream { public: // The ctor redirects the stream to a temporary file. explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS char temp_dir_path[MAX_PATH + 1] = {'\0'}; // NOLINT char temp_file_path[MAX_PATH + 1] = {'\0'}; // NOLINT @@ -1054,7 +1104,7 @@ // directory, so we create the temporary file in a temporary directory. std::string name_template; -#if GTEST_OS_LINUX_ANDROID +#ifdef GTEST_OS_LINUX_ANDROID // Note: Android applications are expected to call the framework's // Context.getExternalStorageDirectory() method through JNI to get // the location of the world-writable SD Card directory. However, @@ -1066,8 +1116,14 @@ // The location /data/local/tmp is directly accessible from native code. // '/sdcard' and other variants cannot be relied on, as they are not // guaranteed to be mounted, or may have a delay in mounting. - name_template = "/data/local/tmp/"; -#elif GTEST_OS_IOS + // + // However, prefer using the TMPDIR environment variable if set, as newer + // devices may have /data/local/tmp read-only. + name_template = TempDir(); + if (!EndsWithPathSeparator(name_template)) + name_template.push_back(GTEST_PATH_SEP_[0]); + +#elif defined(GTEST_OS_IOS) char user_temp_dir[PATH_MAX + 1]; // Documented alternative to NSTemporaryDirectory() (for obtaining creating @@ -1086,7 +1142,7 @@ ::confstr(_CS_DARWIN_USER_TEMP_DIR, user_temp_dir, sizeof(user_temp_dir)); name_template = user_temp_dir; - if (name_template.back() != GTEST_PATH_SEP_[0]) + if (!EndsWithPathSeparator(name_template)) name_template.push_back(GTEST_PATH_SEP_[0]); #else name_template = "/tmp/"; @@ -1227,7 +1283,7 @@ return content; } -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST static const std::vector<std::string>* g_injected_test_argvs = nullptr; // Owned. @@ -1254,7 +1310,7 @@ } #endif // GTEST_HAS_DEATH_TEST -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE namespace posix { void Abort() { DebugBreak(); @@ -1308,8 +1364,8 @@ ) { Message msg; msg << "WARNING: " << src_text - << " is expected to be a 32-bit integer, but actually" - << " has value " << str << ", which overflows.\n"; + << " is expected to be a 32-bit integer, but actually" << " has value " + << str << ", which overflows.\n"; printf("%s", msg.GetString().c_str()); fflush(stdout); return false;
diff --git a/third_party/googletest/src/googletest/src/gtest-printers.cc b/third_party/googletest/src/googletest/src/gtest-printers.cc index f3976d2..e3acecb 100644 --- a/third_party/googletest/src/googletest/src/gtest-printers.cc +++ b/third_party/googletest/src/googletest/src/gtest-printers.cc
@@ -47,6 +47,8 @@ #include <cctype> #include <cstdint> #include <cwchar> +#include <iomanip> +#include <ios> #include <ostream> // NOLINT #include <string> #include <type_traits> @@ -214,7 +216,7 @@ static const char* GetCharWidthPrefix(unsigned char) { return ""; } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t static const char* GetCharWidthPrefix(char8_t) { return "u8"; } #endif @@ -230,7 +232,7 @@ return PrintAsStringLiteralTo(ToChar32(c), os); } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t static CharFormat PrintAsStringLiteralTo(char8_t c, ostream* os) { return PrintAsStringLiteralTo(ToChar32(c), os); } @@ -315,7 +317,7 @@ low = low / 10 + high_mod * 1844674407370955161 + carry / 10; char digit = static_cast<char>(carry % 10); - *--p = '0' + digit; + *--p = static_cast<char>('0' + digit); } *os << p; } @@ -393,7 +395,7 @@ UniversalPrintCharArray(begin, len, os); } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t // Prints a (const) char8_t array of 'len' elements, starting at address // 'begin'. void UniversalPrintArray(const char8_t* begin, size_t len, ostream* os) { @@ -436,7 +438,7 @@ void PrintTo(const char* s, ostream* os) { PrintCStringTo(s, os); } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t void PrintTo(const char8_t* s, ostream* os) { PrintCStringTo(s, os); } #endif @@ -528,7 +530,7 @@ } } -#ifdef __cpp_char8_t +#ifdef __cpp_lib_char8_t void PrintU8StringTo(const ::std::u8string& s, ostream* os) { PrintCharsAsStringTo(s.data(), s.size(), os); }
diff --git a/third_party/googletest/src/googletest/src/gtest-test-part.cc b/third_party/googletest/src/googletest/src/gtest-test-part.cc index eb7c8d1..6f8ddd7 100644 --- a/third_party/googletest/src/googletest/src/gtest-test-part.cc +++ b/third_party/googletest/src/googletest/src/gtest-test-part.cc
@@ -32,13 +32,14 @@ #include "gtest/gtest-test-part.h" +#include <ostream> +#include <string> + #include "gtest/internal/gtest-port.h" #include "src/gtest-internal-inl.h" namespace testing { -using internal::GetUnitTestImpl; - // Gets the summary of the failure message by omitting the stack trace // in it. std::string TestPartResult::ExtractSummary(const char* message) {
diff --git a/third_party/googletest/src/googletest/src/gtest-typed-test.cc b/third_party/googletest/src/googletest/src/gtest-typed-test.cc index a2828b8..b251c09 100644 --- a/third_party/googletest/src/googletest/src/gtest-typed-test.cc +++ b/third_party/googletest/src/googletest/src/gtest-typed-test.cc
@@ -29,6 +29,10 @@ #include "gtest/gtest-typed-test.h" +#include <set> +#include <string> +#include <vector> + #include "gtest/gtest.h" namespace testing { @@ -90,7 +94,7 @@ } const std::string& errors_str = errors.GetString(); - if (errors_str != "") { + if (!errors_str.empty()) { fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), errors_str.c_str()); fflush(stderr);
diff --git a/third_party/googletest/src/googletest/src/gtest.cc b/third_party/googletest/src/googletest/src/gtest.cc index 6f31dd2..09af151 100644 --- a/third_party/googletest/src/googletest/src/gtest.cc +++ b/third_party/googletest/src/googletest/src/gtest.cc
@@ -43,23 +43,31 @@ #include <algorithm> #include <chrono> // NOLINT #include <cmath> +#include <csignal> // NOLINT: raise(3) is used on some platforms #include <cstdint> +#include <cstdlib> +#include <cstring> #include <initializer_list> #include <iomanip> +#include <ios> +#include <iostream> #include <iterator> #include <limits> #include <list> #include <map> #include <ostream> // NOLINT +#include <set> #include <sstream> #include <unordered_set> +#include <utility> #include <vector> #include "gtest/gtest-assertion-result.h" #include "gtest/gtest-spi.h" #include "gtest/internal/custom/gtest.h" +#include "gtest/internal/gtest-port.h" -#if GTEST_OS_LINUX +#ifdef GTEST_OS_LINUX #include <fcntl.h> // NOLINT #include <limits.h> // NOLINT @@ -72,18 +80,18 @@ #include <string> -#elif GTEST_OS_ZOS +#elif defined(GTEST_OS_ZOS) #include <sys/time.h> // NOLINT // On z/OS we additionally need strings.h for strcasecmp. #include <strings.h> // NOLINT -#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. +#elif defined(GTEST_OS_WINDOWS_MOBILE) // We are on Windows CE. #include <windows.h> // NOLINT #undef min -#elif GTEST_OS_WINDOWS // We are on Windows proper. +#elif defined(GTEST_OS_WINDOWS) // We are on Windows proper. #include <windows.h> // NOLINT #undef min @@ -97,7 +105,7 @@ #include <sys/timeb.h> // NOLINT #include <sys/types.h> // NOLINT -#if GTEST_OS_WINDOWS_MINGW +#ifdef GTEST_OS_WINDOWS_MINGW #include <sys/time.h> // NOLINT #endif // GTEST_OS_WINDOWS_MINGW @@ -123,17 +131,18 @@ #include "src/gtest-internal-inl.h" -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS #define vsnprintf _vsnprintf #endif // GTEST_OS_WINDOWS -#if GTEST_OS_MAC +#ifdef GTEST_OS_MAC #ifndef GTEST_OS_IOS #include <crt_externs.h> #endif #endif -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL +#include "absl/container/flat_hash_set.h" #include "absl/debugging/failure_signal_handler.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" @@ -141,8 +150,22 @@ #include "absl/flags/usage.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" #endif // GTEST_HAS_ABSL +// Checks builtin compiler feature |x| while avoiding an extra layer of #ifdefs +// at the callsite. +#if defined(__has_builtin) +#define GTEST_HAS_BUILTIN(x) __has_builtin(x) +#else +#define GTEST_HAS_BUILTIN(x) 0 +#endif // defined(__has_builtin) + +#if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) +#define GTEST_HAS_ABSL_FLAGS +#endif + namespace testing { using internal::CountIf; @@ -169,12 +192,17 @@ // The default output file. static const char kDefaultOutputFile[] = "test_detail"; +// These environment variables are set by Bazel. +// https://bazel.build/reference/test-encyclopedia#initial-conditions +// // The environment variable name for the test shard index. static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; // The environment variable name for the total number of test shards. static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; // The environment variable name for the test shard status file. static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; +// The environment variable name for the test output warnings file. +static const char kTestWarningsOutputFile[] = "TEST_WARNINGS_OUTPUT_FILE"; namespace internal { @@ -186,6 +214,7 @@ // is specified on the command line. bool g_help_flag = false; +#if GTEST_HAS_FILE_SYSTEM // Utility function to Open File for Writing static FILE* OpenFileForWriting(const std::string& output_file) { FILE* fileout = nullptr; @@ -200,6 +229,7 @@ } return fileout; } +#endif // GTEST_HAS_FILE_SYSTEM } // namespace internal @@ -234,6 +264,12 @@ "True if and only if a test failure should stop further test execution."); GTEST_DEFINE_bool_( + fail_if_no_test_linked, + testing::internal::BoolFromGTestEnv("fail_if_no_test_linked", false), + "True if and only if the test should fail if no test case (including " + "disabled test cases) is linked."); + +GTEST_DEFINE_bool_( also_run_disabled_tests, testing::internal::BoolFromGTestEnv("also_run_disabled_tests", false), "Run disabled tests too, in addition to the tests normally being run."); @@ -354,7 +390,7 @@ testing::internal::StringFromGTestEnv("stream_result_to", ""), "This flag specifies the host name and the port number on which to stream " "test results. Example: \"localhost:555\". The flag is effective only on " - "Linux."); + "Linux and macOS."); GTEST_DEFINE_bool_( throw_on_failure, @@ -372,6 +408,8 @@ namespace testing { namespace internal { +const uint32_t Random::kMaxRange; + // Generates a random number from [0, range), using a Linear // Congruential Generator (LCG). Crashes if 'range' is 0 or greater // than kMaxRange. @@ -394,7 +432,7 @@ // GTestIsInitialized() returns true if and only if the user has initialized // Google Test. Useful for catching the user mistake of not initializing // Google Test before calling RUN_ALL_TESTS(). -static bool GTestIsInitialized() { return GetArgvs().size() > 0; } +static bool GTestIsInitialized() { return !GetArgvs().empty(); } // Iterates over a vector of TestSuites, keeping a running sum of the // results of calling a given int-returning method on each. @@ -424,6 +462,19 @@ return test_suite->should_run(); } +namespace { + +// Returns true if test part results of type `type` should include a stack +// trace. +bool ShouldEmitStackTraceForResultType(TestPartResult::Type type) { + // Suppress emission of the stack trace for SUCCEED() since it likely never + // requires investigation, and GTEST_SKIP() since skipping is an intentional + // act by the developer rather than a failure requiring investigation. + return type != TestPartResult::kSuccess && type != TestPartResult::kSkip; +} + +} // namespace + // AssertHelper constructor. AssertHelper::AssertHelper(TestPartResult::Type type, const char* file, int line, const char* message) @@ -436,7 +487,9 @@ UnitTest::GetInstance()->AddTestPartResult( data_->type, data_->file, data_->line, AppendUserMessage(data_->message, message), - UnitTest::GetInstance()->impl()->CurrentOsStackTraceExceptTop(1) + ShouldEmitStackTraceForResultType(data_->type) + ? UnitTest::GetInstance()->impl()->CurrentOsStackTraceExceptTop(1) + : "" // Skips the stack frame for this function itself. ); // NOLINT } @@ -494,7 +547,8 @@ if (ignored.find(name) != ignored.end()) return; const char kMissingInstantiation[] = // - " is defined via TEST_P, but never instantiated. None of the test cases " + " is defined via TEST_P, but never instantiated. None of the test " + "cases " "will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only " "ones provided expand to nothing." "\n\n" @@ -535,7 +589,7 @@ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, CodeLocation code_location) { GetUnitTestImpl()->type_parameterized_test_registry().RegisterTestSuite( - test_suite_name, code_location); + test_suite_name, std::move(code_location)); } void RegisterTypeParameterizedTestSuiteInstantiation(const char* case_name) { @@ -546,7 +600,7 @@ void TypeParameterizedTestSuiteRegistry::RegisterTestSuite( const char* test_suite_name, CodeLocation code_location) { suites_.emplace(std::string(test_suite_name), - TypeParameterizedTestSuiteInfo(code_location)); + TypeParameterizedTestSuiteInfo(std::move(code_location))); } void TypeParameterizedTestSuiteRegistry::RegisterInstantiation( @@ -573,10 +627,12 @@ "\n\n" "Ideally, TYPED_TEST_P definitions should only ever be included as " "part of binaries that intend to use them. (As opposed to, for " - "example, being placed in a library that may be linked in to get other " + "example, being placed in a library that may be linked in to get " + "other " "utilities.)" "\n\n" - "To suppress this error for this test suite, insert the following line " + "To suppress this error for this test suite, insert the following " + "line " "(in a non-header) in the namespace it is defined in:" "\n\n" "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" + @@ -610,19 +666,24 @@ #endif // defined(GTEST_CUSTOM_GET_ARGVS_) } +#if GTEST_HAS_FILE_SYSTEM // Returns the current application's name, removing directory path if that // is present. FilePath GetCurrentExecutableName() { FilePath result; -#if GTEST_OS_WINDOWS || GTEST_OS_OS2 - result.Set(FilePath(GetArgvs()[0]).RemoveExtension("exe")); + auto args = GetArgvs(); + if (!args.empty()) { +#if defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_OS2) + result.Set(FilePath(args[0]).RemoveExtension("exe")); #else - result.Set(FilePath(GetArgvs()[0])); + result.Set(FilePath(args[0])); #endif // GTEST_OS_WINDOWS + } return result.RemoveDirectoryName(); } +#endif // GTEST_HAS_FILE_SYSTEM // Functions for processing the gtest_output flag. @@ -637,6 +698,7 @@ static_cast<size_t>(colon - gtest_output_flag)); } +#if GTEST_HAS_FILE_SYSTEM // Returns the name of the requested output file, or the default if none // was explicitly specified. std::string UnitTestOptions::GetAbsolutePathToOutputFile() { @@ -667,6 +729,7 @@ GetOutputFormat().c_str())); return result.string(); } +#endif // GTEST_HAS_FILE_SYSTEM // Returns true if and only if the wildcard pattern matches the string. Each // pattern consists of regular characters, single-character wildcards (?), and @@ -752,7 +815,7 @@ // Returns true if and only if name matches at least one of the patterns in // the filter. bool MatchesName(const std::string& name) const { - return exact_match_patterns_.count(name) > 0 || + return exact_match_patterns_.find(name) != exact_match_patterns_.end() || std::any_of(glob_patterns_.begin(), glob_patterns_.end(), [&name](const std::string& pattern) { return PatternMatchesString( @@ -837,30 +900,39 @@ } #if GTEST_HAS_SEH -// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the -// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. -// This function is useful as an __except condition. -int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { +static std::string FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << exception_code + << std::setbase(10) << " thrown in " << location << "."; + return message.GetString(); +} + +int UnitTestOptions::GTestProcessSEH(DWORD seh_code, const char* location) { // Google Test should handle a SEH exception if: // 1. the user wants it to, AND - // 2. this is not a breakpoint exception, AND + // 2. this is not a breakpoint exception or stack overflow, AND // 3. this is not a C++ exception (VC++ implements them via SEH, // apparently). // // SEH exception code for C++ exceptions. - // (see http://support.microsoft.com/kb/185294 for more information). + // (see https://support.microsoft.com/kb/185294 for more information). const DWORD kCxxExceptionCode = 0xe06d7363; - bool should_handle = true; + if (!GTEST_FLAG_GET(catch_exceptions) || seh_code == kCxxExceptionCode || + seh_code == EXCEPTION_BREAKPOINT || + seh_code == EXCEPTION_STACK_OVERFLOW) { + return EXCEPTION_CONTINUE_SEARCH; // Don't handle these exceptions + } - if (!GTEST_FLAG_GET(catch_exceptions)) - should_handle = false; - else if (exception_code == EXCEPTION_BREAKPOINT) - should_handle = false; - else if (exception_code == kCxxExceptionCode) - should_handle = false; + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatSehExceptionMessage(seh_code, location) + + "\n" + "Stack trace:\n" + + ::testing::internal::GetCurrentOsStackTraceExceptTop(1)); - return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; + return EXCEPTION_EXECUTE_HANDLER; } #endif // GTEST_HAS_SEH @@ -1008,14 +1080,14 @@ TestPartResultReporterInterface* UnitTestImpl::GetGlobalTestPartResultReporter() { internal::MutexLock lock(&global_test_part_result_reporter_mutex_); - return global_test_part_result_repoter_; + return global_test_part_result_reporter_; } // Sets the global test part result reporter. void UnitTestImpl::SetGlobalTestPartResultReporter( TestPartResultReporterInterface* reporter) { internal::MutexLock lock(&global_test_part_result_reporter_mutex_); - global_test_part_result_repoter_ = reporter; + global_test_part_result_reporter_ = reporter; } // Returns the test part result reporter for the current thread. @@ -1113,17 +1185,24 @@ // A helper class for measuring elapsed times. class Timer { public: - Timer() : start_(std::chrono::steady_clock::now()) {} + Timer() : start_(clock::now()) {} // Return time elapsed in milliseconds since the timer was created. TimeInMillis Elapsed() { - return std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now() - start_) + return std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - + start_) .count(); } private: - std::chrono::steady_clock::time_point start_; + // Fall back to the system_clock when building with newlib on a system + // without a monotonic clock. +#if defined(_NEWLIB_VERSION) && !defined(CLOCK_MONOTONIC) + using clock = std::chrono::system_clock; +#else + using clock = std::chrono::steady_clock; +#endif + clock::time_point start_; }; // Returns a timestamp as milliseconds since the epoch. Note this time may jump @@ -1140,7 +1219,7 @@ // class String. -#if GTEST_OS_WINDOWS_MOBILE +#ifdef GTEST_OS_WINDOWS_MOBILE // Creates a UTF-16 wide string from the given ANSI string, allocating // memory using new. The caller is responsible for deleting the return // value using delete[]. Returns the wide string, or NULL if the @@ -1592,10 +1671,25 @@ return msg.GetString(); } -// Helper function for implementing ASSERT_NEAR. +// Helper function for implementing ASSERT_NEAR. Treats infinity as a specific +// value, such that comparing infinity to infinity is equal, the distance +// between -infinity and +infinity is infinity, and infinity <= infinity is +// true. AssertionResult DoubleNearPredFormat(const char* expr1, const char* expr2, const char* abs_error_expr, double val1, double val2, double abs_error) { + // We want to return success when the two values are infinity and at least + // one of the following is true: + // * The values are the same-signed infinity. + // * The error limit itself is infinity. + // This is done here so that we don't end up with a NaN when calculating the + // difference in values. + if (std::isinf(val1) && std::isinf(val2) && + (std::signbit(val1) == std::signbit(val2) || + (abs_error > 0.0 && std::isinf(abs_error)))) { + return AssertionSuccess(); + } + const double diff = fabs(val1 - val2); if (diff <= abs_error) return AssertionSuccess(); @@ -1842,14 +1936,14 @@ namespace internal { -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS namespace { // Helper function for IsHRESULT{SuccessFailure} predicates AssertionResult HRESULTFailureHelper(const char* expr, const char* expected, long hr) { // NOLINT -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE +#if defined(GTEST_OS_WINDOWS_MOBILE) || defined(GTEST_OS_WINDOWS_TV_TITLE) // Windows CE doesn't support FormatMessage. const char error_text[] = ""; @@ -2110,9 +2204,9 @@ if (rhs == nullptr) return false; -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS return _wcsicmp(lhs, rhs) == 0; -#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID +#elif defined(GTEST_OS_LINUX) && !defined(GTEST_OS_LINUX_ANDROID) return wcscasecmp(lhs, rhs) == 0; #else // Android, Mac OS X and Cygwin don't define wcscasecmp. @@ -2212,7 +2306,7 @@ : death_test_count_(0), start_timestamp_(0), elapsed_time_(0) {} // D'tor. -TestResult::~TestResult() {} +TestResult::~TestResult() = default; // Returns the i-th test part result among all the results. i can // range from 0 to total_part_count() - 1. If i is not in that range, @@ -2275,7 +2369,7 @@ "type_param", "value_param", "file", "line"}; // Use a slightly different set for allowed output to ensure existing tests can -// still RecordProperty("result") or "RecordProperty(timestamp") +// still RecordProperty("result") or RecordProperty("timestamp") static const char* const kReservedOutputTestCaseAttributes[] = { "classname", "name", "status", "time", "type_param", "value_param", "file", "line", "result", "timestamp"}; @@ -2300,7 +2394,9 @@ return std::vector<std::string>(); } +#if GTEST_HAS_FILE_SYSTEM // TODO(jdesprez): Merge the two getReserved attributes once skip is improved +// This function is only used when file systems are enabled. static std::vector<std::string> GetReservedOutputAttributesForElement( const std::string& xml_element) { if (xml_element == "testsuites") { @@ -2315,6 +2411,7 @@ // This code is unreachable but some compilers may not realizes that. return std::vector<std::string>(); } +#endif static std::string FormatWordList(const std::vector<std::string>& words) { Message word_list; @@ -2418,7 +2515,7 @@ // The d'tor restores the states of all flags. The actual work is // done by the d'tor of the gtest_flag_saver_ field, and thus not // visible here. -Test::~Test() {} +Test::~Test() = default; // Sets up the test fixture. // @@ -2435,13 +2532,6 @@ UnitTest::GetInstance()->RecordProperty(key, value); } -// Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const std::string& key, int value) { - Message value_message; - value_message << value; - RecordProperty(key, value_message.GetString().c_str()); -} - namespace internal { void ReportFailureInUnknownLocation(TestPartResult::Type result_type, @@ -2524,23 +2614,6 @@ return true; } -#if GTEST_HAS_SEH - -// Adds an "exception thrown" fatal failure to the current test. This -// function returns its result via an output parameter pointer because VC++ -// prohibits creation of objects with destructors on stack in functions -// using __try (see error C2712). -static std::string* FormatSehExceptionMessage(DWORD exception_code, - const char* location) { - Message message; - message << "SEH exception with code 0x" << std::setbase(16) << exception_code - << std::setbase(10) << " thrown in " << location << "."; - - return new std::string(message.GetString()); -} - -#endif // GTEST_HAS_SEH - namespace internal { #if GTEST_HAS_EXCEPTIONS @@ -2582,16 +2655,8 @@ #if GTEST_HAS_SEH __try { return (object->*method)(); - } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT - GetExceptionCode())) { - // We create the exception message on the heap because VC++ prohibits - // creation of objects with destructors on stack in functions using __try - // (see error C2712). - std::string* exception_message = - FormatSehExceptionMessage(GetExceptionCode(), location); - internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, - *exception_message); - delete exception_message; + } __except (internal::UnitTestOptions::GTestProcessSEH( // NOLINT + GetExceptionCode(), location)) { return static_cast<Result>(0); } #else @@ -2704,17 +2769,16 @@ // Constructs a TestInfo object. It assumes ownership of the test factory // object. -TestInfo::TestInfo(const std::string& a_test_suite_name, - const std::string& a_name, const char* a_type_param, - const char* a_value_param, +TestInfo::TestInfo(std::string a_test_suite_name, std::string a_name, + const char* a_type_param, const char* a_value_param, internal::CodeLocation a_code_location, internal::TypeId fixture_class_id, internal::TestFactoryBase* factory) - : test_suite_name_(a_test_suite_name), - name_(a_name), + : test_suite_name_(std::move(a_test_suite_name)), + name_(std::move(a_name)), type_param_(a_type_param ? new std::string(a_type_param) : nullptr), value_param_(a_value_param ? new std::string(a_value_param) : nullptr), - location_(a_code_location), + location_(std::move(a_code_location)), fixture_class_id_(fixture_class_id), should_run_(false), is_disabled_(false), @@ -2747,19 +2811,19 @@ // The newly created TestInfo instance will assume // ownership of the factory object. TestInfo* MakeAndRegisterTestInfo( - const char* test_suite_name, const char* name, const char* type_param, + std::string test_suite_name, const char* name, const char* type_param, const char* value_param, CodeLocation code_location, TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) { TestInfo* const test_info = - new TestInfo(test_suite_name, name, type_param, value_param, - code_location, fixture_class_id, factory); + new TestInfo(std::move(test_suite_name), name, type_param, value_param, + std::move(code_location), fixture_class_id, factory); GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); return test_info; } void ReportInvalidTestSuiteType(const char* test_suite_name, - CodeLocation code_location) { + const CodeLocation& code_location) { Message errors; errors << "Attempted redefinition of test suite " << test_suite_name << ".\n" @@ -2775,37 +2839,6 @@ code_location.line) << " " << errors.GetString(); } -} // namespace internal - -namespace { - -// A predicate that checks the test name of a TestInfo against a known -// value. -// -// This is used for implementation of the TestSuite class only. We put -// it in the anonymous namespace to prevent polluting the outer -// namespace. -// -// TestNameIs is copyable. -class TestNameIs { - public: - // Constructor. - // - // TestNameIs has NO default constructor. - explicit TestNameIs(const char* name) : name_(name) {} - - // Returns true if and only if the test name of test_info matches name_. - bool operator()(const TestInfo* test_info) const { - return test_info && test_info->name() == name_; - } - - private: - std::string name_; -}; - -} // namespace - -namespace internal { // This method expands all parameterized tests registered with macros TEST_P // and INSTANTIATE_TEST_SUITE_P into regular tests and registers those. @@ -2830,14 +2863,13 @@ } // Tells UnitTest where to store test result. - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_info(this); + UnitTest::GetInstance()->set_current_test_info(this); // Notifies the unit test event listeners that a test is about to start. repeater->OnTestStart(*this); result_.set_start_timestamp(internal::GetTimeInMillis()); internal::Timer timer; - impl->os_stack_trace_getter()->UponLeavingGTest(); + UnitTest::GetInstance()->UponLeavingGTest(); // Creates the test object. Test* const test = internal::HandleExceptionsInMethodIfSupported( @@ -2855,7 +2887,7 @@ if (test != nullptr) { // Deletes the test object. - impl->os_stack_trace_getter()->UponLeavingGTest(); + UnitTest::GetInstance()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( test, &Test::DeleteSelf_, "the test fixture's destructor"); } @@ -2867,15 +2899,14 @@ // Tells UnitTest to stop associating assertion results to this // test. - impl->set_current_test_info(nullptr); + UnitTest::GetInstance()->set_current_test_info(nullptr); } // Skip and records a skipped test result for this object. void TestInfo::Skip() { if (!should_run_) return; - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_info(this); + UnitTest::GetInstance()->set_current_test_info(this); TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); @@ -2884,12 +2915,13 @@ const TestPartResult test_part_result = TestPartResult(TestPartResult::kSkip, this->file(), this->line(), ""); - impl->GetTestPartResultReporterForCurrentThread()->ReportTestPartResult( - test_part_result); + internal::GetUnitTestImpl() + ->GetTestPartResultReporterForCurrentThread() + ->ReportTestPartResult(test_part_result); // Notifies the unit test event listener that a test has just finished. repeater->OnTestEnd(*this); - impl->set_current_test_info(nullptr); + UnitTest::GetInstance()->set_current_test_info(nullptr); } // class TestSuite @@ -2943,7 +2975,7 @@ // this is not a typed or a type-parameterized test suite. // set_up_tc: pointer to the function that sets up the test suite // tear_down_tc: pointer to the function that tears down the test suite -TestSuite::TestSuite(const char* a_name, const char* a_type_param, +TestSuite::TestSuite(const std::string& a_name, const char* a_type_param, internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc) : name_(a_name), @@ -2985,11 +3017,29 @@ void TestSuite::Run() { if (!should_run_) return; - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_suite(this); + UnitTest::GetInstance()->set_current_test_suite(this); TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + // Ensure our tests are in a deterministic order. + // + // We do this by sorting lexicographically on (file, line number), providing + // an order matching what the user can see in the source code. + // + // In the common case the line number comparison shouldn't be necessary, + // because the registrations made by the TEST macro are executed in order + // within a translation unit. But this is not true of the manual registration + // API, and in more exotic scenarios a single file may be part of multiple + // translation units. + std::stable_sort(test_info_list_.begin(), test_info_list_.end(), + [](const TestInfo* const a, const TestInfo* const b) { + if (const int result = std::strcmp(a->file(), b->file())) { + return result < 0; + } + + return a->line() < b->line(); + }); + // Call both legacy and the new API repeater->OnTestSuiteStart(*this); // Legacy API is deprecated but still available @@ -2997,11 +3047,12 @@ repeater->OnTestCaseStart(*this); #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ - impl->os_stack_trace_getter()->UponLeavingGTest(); + UnitTest::GetInstance()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()"); - const bool skip_all = ad_hoc_test_result().Failed(); + const bool skip_all = + ad_hoc_test_result().Failed() || ad_hoc_test_result().Skipped(); start_timestamp_ = internal::GetTimeInMillis(); internal::Timer timer; @@ -3021,7 +3072,7 @@ } elapsed_time_ = timer.Elapsed(); - impl->os_stack_trace_getter()->UponLeavingGTest(); + UnitTest::GetInstance()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( this, &TestSuite::RunTearDownTestSuite, "TearDownTestSuite()"); @@ -3032,15 +3083,14 @@ repeater->OnTestCaseEnd(*this); #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ - impl->set_current_test_suite(nullptr); + UnitTest::GetInstance()->set_current_test_suite(nullptr); } // Skips all tests under this TestSuite. void TestSuite::Skip() { if (!should_run_) return; - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_suite(this); + UnitTest::GetInstance()->set_current_test_suite(this); TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); @@ -3062,7 +3112,7 @@ repeater->OnTestCaseEnd(*this); #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ - impl->set_current_test_suite(nullptr); + UnitTest::GetInstance()->set_current_test_suite(nullptr); } // Clears the results of all tests in this test suite. @@ -3153,7 +3203,7 @@ // following statements add the test part result message to the Output // window such that the user can double-click on it to jump to the // corresponding source code location; otherwise they do nothing. -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MOBILE) // We don't call OutputDebugString*() on Windows Mobile, as printing // to stdout is done by OutputDebugString() there already - we don't // want the same message printed twice. @@ -3163,8 +3213,9 @@ } // class PrettyUnitTestResultPrinter -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ - !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MOBILE) && \ + !defined(GTEST_OS_WINDOWS_GAMES) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_WINDOWS_MINGW) // Returns the character attribute for the given color. static WORD GetColorAttribute(GTestColor color) { @@ -3224,7 +3275,8 @@ case GTestColor::kYellow: return "3"; default: - return nullptr; + assert(false); + return "9"; } } @@ -3236,7 +3288,7 @@ const char* const gtest_color = c.c_str(); if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW) // On Windows the TERM variable is usually not set, but the // console there does support colors. return stdout_is_tty; @@ -3244,17 +3296,16 @@ // On non-Windows platforms, we rely on the TERM variable. const char* const term = posix::GetEnv("TERM"); const bool term_supports_color = - String::CStringEquals(term, "xterm") || - String::CStringEquals(term, "xterm-color") || - String::CStringEquals(term, "xterm-256color") || - String::CStringEquals(term, "screen") || - String::CStringEquals(term, "screen-256color") || - String::CStringEquals(term, "tmux") || - String::CStringEquals(term, "tmux-256color") || - String::CStringEquals(term, "rxvt-unicode") || - String::CStringEquals(term, "rxvt-unicode-256color") || - String::CStringEquals(term, "linux") || - String::CStringEquals(term, "cygwin"); + term != nullptr && (String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-kitty") || + String::CStringEquals(term, "alacritty") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "tmux") || + String::CStringEquals(term, "rxvt-unicode") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin") || + String::EndsWithCaseInsensitive(term, "-256color")); return stdout_is_tty && term_supports_color; #endif // GTEST_OS_WINDOWS } @@ -3279,7 +3330,10 @@ va_start(args, fmt); static const bool in_color_mode = + // We don't condition this on GTEST_HAS_FILE_SYSTEM because we still need + // to be able to detect terminal I/O regardless. ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != GTestColor::kDefault); if (!use_color) { @@ -3288,8 +3342,9 @@ return; } -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ - !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MOBILE) && \ + !defined(GTEST_OS_WINDOWS_GAMES) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_WINDOWS_MINGW) const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // Gets the current text color. @@ -3343,7 +3398,7 @@ // Class PrettyUnitTestResultPrinter is copyable. class PrettyUnitTestResultPrinter : public TestEventListener { public: - PrettyUnitTestResultPrinter() {} + PrettyUnitTestResultPrinter() = default; static void PrintTestName(const char* test_suite, const char* test) { printf("%s.%s", test_suite, test); } @@ -3651,7 +3706,7 @@ // Class BriefUnitTestResultPrinter is copyable. class BriefUnitTestResultPrinter : public TestEventListener { public: - BriefUnitTestResultPrinter() {} + BriefUnitTestResultPrinter() = default; static void PrintTestName(const char* test_suite, const char* test) { printf("%s.%s", test_suite, test); } @@ -3766,28 +3821,28 @@ bool forwarding_enabled() const { return forwarding_enabled_; } void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } - void OnTestProgramStart(const UnitTest& unit_test) override; + void OnTestProgramStart(const UnitTest& parameter) override; void OnTestIterationStart(const UnitTest& unit_test, int iteration) override; - void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override; - void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) override; + void OnEnvironmentsSetUpStart(const UnitTest& parameter) override; + void OnEnvironmentsSetUpEnd(const UnitTest& parameter) override; // Legacy API is deprecated but still available #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestCaseStart(const TestSuite& parameter) override; #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestSuiteStart(const TestSuite& parameter) override; - void OnTestStart(const TestInfo& test_info) override; - void OnTestDisabled(const TestInfo& test_info) override; - void OnTestPartResult(const TestPartResult& result) override; - void OnTestEnd(const TestInfo& test_info) override; + void OnTestStart(const TestInfo& parameter) override; + void OnTestDisabled(const TestInfo& parameter) override; + void OnTestPartResult(const TestPartResult& parameter) override; + void OnTestEnd(const TestInfo& parameter) override; // Legacy API is deprecated but still available #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestCaseEnd(const TestCase& parameter) override; #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ void OnTestSuiteEnd(const TestSuite& parameter) override; - void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override; - void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) override; + void OnEnvironmentsTearDownStart(const UnitTest& parameter) override; + void OnEnvironmentsTearDownEnd(const UnitTest& parameter) override; void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override; - void OnTestProgramEnd(const UnitTest& unit_test) override; + void OnTestProgramEnd(const UnitTest& parameter) override; private: // Controls whether events will be forwarded to listeners_. Set to false @@ -3884,6 +3939,7 @@ // End TestEventRepeater +#if GTEST_HAS_FILE_SYSTEM // This class generates an XML output file. class XmlUnitTestResultPrinter : public EmptyTestEventListener { public: @@ -3944,6 +4000,12 @@ static void OutputXmlTestSuiteForTestResult(::std::ostream* stream, const TestResult& result); + // Streams a test case XML stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputXmlTestCaseForTestResult(::std::ostream* stream, + const TestResult& result); + // Streams an XML representation of a TestResult object. static void OutputXmlTestResult(::std::ostream* stream, const TestResult& result); @@ -3961,16 +4023,11 @@ static void PrintXmlUnitTest(::std::ostream* stream, const UnitTest& unit_test); - // Produces a string representing the test properties in a result as space - // delimited XML attributes based on the property key="value" pairs. - // When the std::string is not empty, it includes a space at the beginning, - // to delimit this attribute from prior attributes. - static std::string TestPropertiesAsXmlAttributes(const TestResult& result); - // Streams an XML representation of the test properties of a TestResult // object. static void OutputXmlTestProperties(std::ostream* stream, - const TestResult& result); + const TestResult& result, + const std::string& indent); // The output file. const std::string output_file_; @@ -4093,6 +4150,13 @@ // Formats the given time in milliseconds as seconds. std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { ::std::stringstream ss; + // For the exact N seconds, makes sure output has a trailing decimal point. + // Sets precision so that we won't have many trailing zeros (e.g., 300 ms + // will be just 0.3, 410 ms 0.41, and so on) + ss << std::fixed + << std::setprecision( + ms % 1000 == 0 ? 0 : (ms % 100 == 0 ? 1 : (ms % 10 == 0 ? 2 : 3))) + << std::showpoint; ss << (static_cast<double>(ms) * 1e-3); return ss.str(); } @@ -4184,6 +4248,15 @@ FormatEpochTimeInMillisAsIso8601(result.start_timestamp())); *stream << ">"; + OutputXmlTestCaseForTestResult(stream, result); + + // Complete the test suite. + *stream << " </testsuite>\n"; +} + +// Streams a test case XML stanza containing the given test result. +void XmlUnitTestResultPrinter::OutputXmlTestCaseForTestResult( + ::std::ostream* stream, const TestResult& result) { // Output the boilerplate for a minimal test case with a single test. *stream << " <testcase"; OutputXmlAttribute(stream, "testcase", "name", ""); @@ -4198,9 +4271,6 @@ // Output the actual test result. OutputXmlTestResult(stream, result); - - // Complete the test suite. - *stream << " </testsuite>\n"; } // Prints an XML representation of a TestInfo object. @@ -4291,7 +4361,7 @@ if (failures == 0 && skips == 0) { *stream << ">\n"; } - OutputXmlTestProperties(stream, result); + OutputXmlTestProperties(stream, result, /*indent=*/" "); *stream << " </testcase>\n"; } } @@ -4320,13 +4390,18 @@ OutputXmlAttribute( stream, kTestsuite, "timestamp", FormatEpochTimeInMillisAsIso8601(test_suite.start_timestamp())); - *stream << TestPropertiesAsXmlAttributes(test_suite.ad_hoc_test_result()); } *stream << ">\n"; + OutputXmlTestProperties(stream, test_suite.ad_hoc_test_result(), + /*indent=*/" "); for (int i = 0; i < test_suite.total_test_count(); ++i) { if (test_suite.GetTestInfo(i)->is_reportable()) OutputXmlTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); } + if (test_suite.ad_hoc_test_result().Failed()) { + OutputXmlTestCaseForTestResult(stream, test_suite.ad_hoc_test_result()); + } + *stream << " </" << kTestsuite << ">\n"; } @@ -4356,11 +4431,12 @@ OutputXmlAttribute(stream, kTestsuites, "random_seed", StreamableToString(unit_test.random_seed())); } - *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); *stream << ">\n"; + OutputXmlTestProperties(stream, unit_test.ad_hoc_test_result(), + /*indent=*/" "); for (int i = 0; i < unit_test.total_test_suite_count(); ++i) { if (unit_test.GetTestSuite(i)->reportable_test_count() > 0) PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i)); @@ -4397,21 +4473,8 @@ *stream << "</" << kTestsuites << ">\n"; } -// Produces a string representing the test properties in a result as space -// delimited XML attributes based on the property key="value" pairs. -std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( - const TestResult& result) { - Message attributes; - for (int i = 0; i < result.test_property_count(); ++i) { - const TestProperty& property = result.GetTestProperty(i); - attributes << " " << property.key() << "=" - << "\"" << EscapeXmlAttribute(property.value()) << "\""; - } - return attributes.GetString(); -} - void XmlUnitTestResultPrinter::OutputXmlTestProperties( - std::ostream* stream, const TestResult& result) { + std::ostream* stream, const TestResult& result, const std::string& indent) { const std::string kProperties = "properties"; const std::string kProperty = "property"; @@ -4419,19 +4482,21 @@ return; } - *stream << " <" << kProperties << ">\n"; + *stream << indent << "<" << kProperties << ">\n"; for (int i = 0; i < result.test_property_count(); ++i) { const TestProperty& property = result.GetTestProperty(i); - *stream << " <" << kProperty; + *stream << indent << " <" << kProperty; *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\""; *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\""; *stream << "/>\n"; } - *stream << " </" << kProperties << ">\n"; + *stream << indent << "</" << kProperties << ">\n"; } // End XmlUnitTestResultPrinter +#endif // GTEST_HAS_FILE_SYSTEM +#if GTEST_HAS_FILE_SYSTEM // This class generates an JSON output file. class JsonUnitTestResultPrinter : public EmptyTestEventListener { public: @@ -4464,6 +4529,12 @@ static void OutputJsonTestSuiteForTestResult(::std::ostream* stream, const TestResult& result); + // Streams a test case JSON stanza containing the given test result. + // + // Requires: result.Failed() + static void OutputJsonTestCaseForTestResult(::std::ostream* stream, + const TestResult& result); + // Streams a JSON representation of a TestResult object. static void OutputJsonTestResult(::std::ostream* stream, const TestResult& result); @@ -4634,6 +4705,15 @@ } *stream << Indent(6) << "\"testsuite\": [\n"; + OutputJsonTestCaseForTestResult(stream, result); + + // Finish the test suite. + *stream << "\n" << Indent(6) << "]\n" << Indent(4) << "}"; +} + +// Streams a test case JSON stanza containing the given test result. +void JsonUnitTestResultPrinter::OutputJsonTestCaseForTestResult( + ::std::ostream* stream, const TestResult& result) { // Output the boilerplate for a new test case. *stream << Indent(8) << "{\n"; OutputJsonKey(stream, "testcase", "name", "", Indent(10)); @@ -4650,9 +4730,6 @@ // Output the actual test result. OutputJsonTestResult(stream, result); - - // Finish the test suite. - *stream << "\n" << Indent(6) << "]\n" << Indent(4) << "}"; } // Prints a JSON representation of a TestInfo object. @@ -4707,28 +4784,53 @@ const TestResult& result) { const std::string kIndent = Indent(10); - int failures = 0; - for (int i = 0; i < result.total_part_count(); ++i) { - const TestPartResult& part = result.GetTestPartResult(i); - if (part.failed()) { - *stream << ",\n"; - if (++failures == 1) { - *stream << kIndent << "\"" - << "failures" - << "\": [\n"; + { + int failures = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + *stream << ",\n"; + if (++failures == 1) { + *stream << kIndent << "\"" << "failures" << "\": [\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string message = + EscapeJson(location + "\n" + part.message()); + *stream << kIndent << " {\n" + << kIndent << " \"failure\": \"" << message << "\",\n" + << kIndent << " \"type\": \"\"\n" + << kIndent << " }"; } - const std::string location = - internal::FormatCompilerIndependentFileLocation(part.file_name(), - part.line_number()); - const std::string message = EscapeJson(location + "\n" + part.message()); - *stream << kIndent << " {\n" - << kIndent << " \"failure\": \"" << message << "\",\n" - << kIndent << " \"type\": \"\"\n" - << kIndent << " }"; } + + if (failures > 0) *stream << "\n" << kIndent << "]"; } - if (failures > 0) *stream << "\n" << kIndent << "]"; + { + int skipped = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.skipped()) { + *stream << ",\n"; + if (++skipped == 1) { + *stream << kIndent << "\"" << "skipped" << "\": [\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string message = + EscapeJson(location + "\n" + part.message()); + *stream << kIndent << " {\n" + << kIndent << " \"message\": \"" << message << "\"\n" + << kIndent << " }"; + } + } + + if (skipped > 0) *stream << "\n" << kIndent << "]"; + } + *stream << "\n" << Indent(8) << "}"; } @@ -4772,6 +4874,16 @@ OutputJsonTestInfo(stream, test_suite.name(), *test_suite.GetTestInfo(i)); } } + + // If there was a failure in the test suite setup or teardown include that in + // the output. + if (test_suite.ad_hoc_test_result().Failed()) { + if (comma) { + *stream << ",\n"; + } + OutputJsonTestCaseForTestResult(stream, test_suite.ad_hoc_test_result()); + } + *stream << "\n" << kIndent << "]\n" << Indent(4) << "}"; } @@ -4821,6 +4933,9 @@ // If there was a test failure outside of one of the test suites (like in a // test environment) include that in the output. if (unit_test.ad_hoc_test_result().Failed()) { + if (comma) { + *stream << ",\n"; + } OutputJsonTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result()); } @@ -4862,13 +4977,14 @@ for (int i = 0; i < result.test_property_count(); ++i) { const TestProperty& property = result.GetTestProperty(i); attributes << ",\n" - << indent << "\"" << property.key() << "\": " - << "\"" << EscapeJson(property.value()) << "\""; + << indent << "\"" << property.key() << "\": " << "\"" + << EscapeJson(property.value()) << "\""; } return attributes.GetString(); } // End JsonUnitTestResultPrinter +#endif // GTEST_HAS_FILE_SYSTEM #if GTEST_CAN_STREAM_RESULTS_ @@ -4886,7 +5002,8 @@ case '=': case '&': case '\n': - result.append("%" + String::FormatByte(static_cast<unsigned char>(ch))); + result.push_back('%'); + result.append(String::FormatByte(static_cast<unsigned char>(ch))); break; default: result.push_back(ch); @@ -4947,7 +5064,7 @@ std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count) GTEST_LOCK_EXCLUDED_(mutex_) { -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL std::string result; if (max_depth <= 0) { @@ -4996,7 +5113,7 @@ } void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) { -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL void* caller_frame = nullptr; if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) { caller_frame = nullptr; @@ -5007,6 +5124,7 @@ #endif // GTEST_HAS_ABSL } +#ifdef GTEST_HAS_DEATH_TEST // A helper class that creates the premature-exit file in its // constructor and deletes the file in its destructor. class ScopedPrematureExitFile { @@ -5026,7 +5144,7 @@ } ~ScopedPrematureExitFile() { -#if !defined GTEST_OS_ESP8266 +#ifndef GTEST_OS_ESP8266 if (!premature_exit_filepath_.empty()) { int retval = remove(premature_exit_filepath_.c_str()); if (retval) { @@ -5044,6 +5162,7 @@ ScopedPrematureExitFile(const ScopedPrematureExitFile&) = delete; ScopedPrematureExitFile& operator=(const ScopedPrematureExitFile&) = delete; }; +#endif // GTEST_HAS_DEATH_TEST } // namespace internal @@ -5115,8 +5234,8 @@ return repeater_->forwarding_enabled(); } -void TestEventListeners::SuppressEventForwarding() { - repeater_->set_forwarding_enabled(false); +void TestEventListeners::SuppressEventForwarding(bool suppress) { + repeater_->set_forwarding_enabled(!suppress); } // class UnitTest @@ -5257,6 +5376,22 @@ return impl()->GetMutableSuiteCase(i); } +void UnitTest::UponLeavingGTest() { + impl()->os_stack_trace_getter()->UponLeavingGTest(); +} + +// Sets the TestSuite object for the test that's currently running. +void UnitTest::set_current_test_suite(TestSuite* a_current_test_suite) { + internal::MutexLock lock(&mutex_); + impl_->set_current_test_suite(a_current_test_suite); +} + +// Sets the TestInfo object for the test that's currently running. +void UnitTest::set_current_test_info(TestInfo* a_current_test_info) { + internal::MutexLock lock(&mutex_); + impl_->set_current_test_info(a_current_test_info); +} + // Returns the list of event listeners that can be used to track events // inside Google Test. TestEventListeners& UnitTest::listeners() { return *impl()->listeners(); } @@ -5293,7 +5428,7 @@ msg << message; internal::MutexLock lock(&mutex_); - if (impl_->gtest_trace_stack().size() > 0) { + if (!impl_->gtest_trace_stack().empty()) { msg << "\n" << GTEST_NAME_ << " trace:"; for (size_t i = impl_->gtest_trace_stack().size(); i > 0; --i) { @@ -5306,6 +5441,8 @@ if (os_stack_trace.c_str() != nullptr && !os_stack_trace.empty()) { msg << internal::kStackTraceMarker << os_stack_trace; + } else { + msg << "\n"; } const TestPartResult result = TestPartResult( @@ -5321,7 +5458,8 @@ // with another testing framework) and specify the former on the // command line for debugging. if (GTEST_FLAG_GET(break_on_failure)) { -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT +#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) // Using DebugBreak on Windows allows gtest to still break into a debugger // when a failure happens and both the --gtest_break_on_failure and // the --gtest_catch_exceptions flags are specified. @@ -5331,6 +5469,10 @@ (defined(__x86_64__) || defined(__i386__))) // with clang/gcc we can achieve the same effect on x86 by invoking int3 asm("int3"); +#elif GTEST_HAS_BUILTIN(__builtin_trap) + __builtin_trap(); +#elif defined(SIGTRAP) + raise(SIGTRAP); #else // Dereference nullptr through a volatile pointer to prevent the compiler // from removing. We use this rather than abort() or __builtin_trap() for @@ -5365,8 +5507,9 @@ // We don't protect this under mutex_, as we only support calling it // from the main thread. int UnitTest::Run() { +#ifdef GTEST_HAS_DEATH_TEST const bool in_death_test_child_process = - GTEST_FLAG_GET(internal_run_death_test).length() > 0; + !GTEST_FLAG_GET(internal_run_death_test).empty(); // Google Test implements this protocol for catching that a test // program exits before returning control to Google Test: @@ -5393,31 +5536,36 @@ in_death_test_child_process ? nullptr : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); +#else + const bool in_death_test_child_process = false; +#endif // GTEST_HAS_DEATH_TEST // Captures the value of GTEST_FLAG(catch_exceptions). This value will be // used for the duration of the program. impl()->set_catch_exceptions(GTEST_FLAG_GET(catch_exceptions)); -#if GTEST_OS_WINDOWS +#ifdef GTEST_OS_WINDOWS // Either the user wants Google Test to catch exceptions thrown by the // tests or this is executing in the context of death test child // process. In either case the user does not want to see pop-up dialogs // about crashes - they are expected. if (impl()->catch_exceptions() || in_death_test_child_process) { -#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT +#if !defined(GTEST_OS_WINDOWS_MOBILE) && !defined(GTEST_OS_WINDOWS_PHONE) && \ + !defined(GTEST_OS_WINDOWS_RT) && !defined(GTEST_OS_WINDOWS_GAMES) // SetErrorMode doesn't exist on CE. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); #endif // !GTEST_OS_WINDOWS_MOBILE -#if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE +#if (defined(_MSC_VER) || defined(GTEST_OS_WINDOWS_MINGW)) && \ + !defined(GTEST_OS_WINDOWS_MOBILE) // Death test children can be terminated with _abort(). On Windows, // _abort() can show a dialog with a warning message. This forces the // abort message to go to stderr instead. _set_error_mode(_OUT_TO_STDERR); #endif -#if defined(_MSC_VER) && !GTEST_OS_WINDOWS_MOBILE +#if defined(_MSC_VER) && !defined(GTEST_OS_WINDOWS_MOBILE) // In the debug version, Visual Studio pops up a separate dialog // offering a choice to debug the aborted program. We need to suppress // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement @@ -5439,6 +5587,8 @@ } #endif } +#else + (void)in_death_test_child_process; // Needed inside the #if block above #endif // GTEST_OS_WINDOWS return internal::HandleExceptionsInMethodIfSupported( @@ -5448,11 +5598,13 @@ : 1; } +#if GTEST_HAS_FILE_SYSTEM // Returns the working directory when the first TEST() or TEST_F() was // executed. const char* UnitTest::original_working_dir() const { return impl_->original_working_dir_.c_str(); } +#endif // GTEST_HAS_FILE_SYSTEM // Returns the TestSuite object for the test that's currently running, // or NULL if no test is running. @@ -5516,7 +5668,7 @@ GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */) default_global_test_part_result_reporter_(this), default_per_thread_test_part_result_reporter_(this), - GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_repoter_( + GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_reporter_( &default_global_test_part_result_reporter_), per_thread_test_part_result_reporter_( &default_per_thread_test_part_result_reporter_), @@ -5532,7 +5684,7 @@ random_(0), // Will be reseeded before first use. start_timestamp_(0), elapsed_time_(0), -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST death_test_factory_(new DefaultDeathTestFactory), #endif // Will be overridden by the flag before first use. @@ -5572,12 +5724,12 @@ test_result->RecordProperty(xml_element, test_property); } -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST // Disables event forwarding if the control is currently in a death test // subprocess. Must not be called before InitGoogleTest. void UnitTestImpl::SuppressTestEventsIfInSubprocess() { - if (internal_run_death_test_flag_.get() != nullptr) - listeners()->SuppressEventForwarding(); + if (internal_run_death_test_flag_ != nullptr) + listeners()->SuppressEventForwarding(true); } #endif // GTEST_HAS_DEATH_TEST @@ -5585,16 +5737,23 @@ // UnitTestOptions. Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureXmlOutput() { const std::string& output_format = UnitTestOptions::GetOutputFormat(); +#if GTEST_HAS_FILE_SYSTEM if (output_format == "xml") { listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); } else if (output_format == "json") { listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter( UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); - } else if (output_format != "") { + } else if (!output_format.empty()) { GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \"" << output_format << "\" ignored."; } +#else + if (!output_format.empty()) { + GTEST_LOG_(ERROR) << "ERROR: alternative output formats require " + << "GTEST_HAS_FILE_SYSTEM to be enabled"; + } +#endif // GTEST_HAS_FILE_SYSTEM } #if GTEST_CAN_STREAM_RESULTS_ @@ -5630,7 +5789,7 @@ listeners()->Append(new GTEST_CUSTOM_TEST_EVENT_LISTENER_()); #endif // defined(GTEST_CUSTOM_TEST_EVENT_LISTENER_) -#if GTEST_HAS_DEATH_TEST +#ifdef GTEST_HAS_DEATH_TEST InitDeathTestSubprocessControlInfo(); SuppressTestEventsIfInSubprocess(); #endif // GTEST_HAS_DEATH_TEST @@ -5653,7 +5812,7 @@ ConfigureStreamingOutput(); #endif // GTEST_CAN_STREAM_RESULTS_ -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL if (GTEST_FLAG_GET(install_failure_signal_handler)) { absl::FailureSignalHandlerOptions options; absl::InstallFailureSignalHandler(options); @@ -5662,29 +5821,6 @@ } } -// A predicate that checks the name of a TestSuite against a known -// value. -// -// This is used for implementation of the UnitTest class only. We put -// it in the anonymous namespace to prevent polluting the outer -// namespace. -// -// TestSuiteNameIs is copyable. -class TestSuiteNameIs { - public: - // Constructor. - explicit TestSuiteNameIs(const std::string& name) : name_(name) {} - - // Returns true if and only if the name of test_suite matches name_. - bool operator()(const TestSuite* test_suite) const { - return test_suite != nullptr && - strcmp(test_suite->name(), name_.c_str()) == 0; - } - - private: - std::string name_; -}; - // Finds and returns a TestSuite with the given name. If one doesn't // exist, creates one and returns it. It's the CALLER'S // RESPONSIBILITY to ensure that this function is only called WHEN THE @@ -5698,19 +5834,27 @@ // set_up_tc: pointer to the function that sets up the test suite // tear_down_tc: pointer to the function that tears down the test suite TestSuite* UnitTestImpl::GetTestSuite( - const char* test_suite_name, const char* type_param, + const std::string& test_suite_name, const char* type_param, internal::SetUpTestSuiteFunc set_up_tc, internal::TearDownTestSuiteFunc tear_down_tc) { - // Can we find a TestSuite with the given name? - const auto test_suite = - std::find_if(test_suites_.rbegin(), test_suites_.rend(), - TestSuiteNameIs(test_suite_name)); + // During initialization, all TestInfos for a given suite are added in + // sequence. To optimize this case, see if the most recently added suite is + // the one being requested now. + if (!test_suites_.empty() && + (*test_suites_.rbegin())->name_ == test_suite_name) { + return *test_suites_.rbegin(); + } - if (test_suite != test_suites_.rend()) return *test_suite; + // Fall back to searching the collection. + auto item_it = test_suites_by_name_.find(test_suite_name); + if (item_it != test_suites_by_name_.end()) { + return item_it->second; + } - // No. Let's create one. + // Not found. Create a new instance. auto* const new_test_suite = new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc); + test_suites_by_name_.emplace(test_suite_name, new_test_suite); const UnitTestFilter death_test_suite_filter(kDeathTestSuiteFilter); // Is this a death test suite? @@ -5736,6 +5880,23 @@ static void SetUpEnvironment(Environment* env) { env->SetUp(); } static void TearDownEnvironment(Environment* env) { env->TearDown(); } +// If the environment variable TEST_WARNINGS_OUTPUT_FILE was provided, appends +// `str` to the file, creating the file if necessary. +#if GTEST_HAS_FILE_SYSTEM +static void AppendToTestWarningsOutputFile(const std::string& str) { + const char* const filename = posix::GetEnv(kTestWarningsOutputFile); + if (filename == nullptr) { + return; + } + auto* const file = posix::FOpen(filename, "a"); + if (file == nullptr) { + return; + } + GTEST_CHECK_(fwrite(str.data(), 1, str.size(), file) == str.size()); + GTEST_CHECK_(posix::FClose(file) == 0); +} +#endif // GTEST_HAS_FILE_SYSTEM + // Runs all tests in this UnitTest object, prints the result, and // returns true if all tests are successful. If any exception is // thrown during a test, the test is considered to be failed, but the @@ -5757,18 +5918,41 @@ // user didn't call InitGoogleTest. PostFlagParsingInit(); + // Handle the case where the program has no tests linked. + // Sometimes this is a programmer mistake, but sometimes it is intended. + if (total_test_count() == 0) { + constexpr char kNoTestLinkedMessage[] = + "This test program does NOT link in any test case."; + constexpr char kNoTestLinkedFatal[] = + "This is INVALID. Please make sure to link in at least one test case."; + constexpr char kNoTestLinkedWarning[] = + "Please make sure this is intended."; + const bool fail_if_no_test_linked = GTEST_FLAG_GET(fail_if_no_test_linked); + ColoredPrintf( + GTestColor::kRed, "%s %s\n", kNoTestLinkedMessage, + fail_if_no_test_linked ? kNoTestLinkedFatal : kNoTestLinkedWarning); + if (fail_if_no_test_linked) { + return false; + } +#if GTEST_HAS_FILE_SYSTEM + AppendToTestWarningsOutputFile(std::string(kNoTestLinkedMessage) + ' ' + + kNoTestLinkedWarning + '\n'); +#endif // GTEST_HAS_FILE_SYSTEM + } + +#if GTEST_HAS_FILE_SYSTEM // Even if sharding is not on, test runners may want to use the // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding // protocol. internal::WriteToShardStatusFileIfNeeded(); +#endif // GTEST_HAS_FILE_SYSTEM // True if and only if we are in a subprocess for running a thread-safe-style // death test. bool in_subprocess_for_death_test = false; -#if GTEST_HAS_DEATH_TEST - in_subprocess_for_death_test = - (internal_run_death_test_flag_.get() != nullptr); +#ifdef GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_ != nullptr); #if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_) if (in_subprocess_for_death_test) { GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_(); @@ -5922,6 +6106,23 @@ } repeater->OnTestProgramEnd(*parent_); + // Destroy environments in normal code, not in static teardown. + bool delete_environment_on_teardown = true; + if (delete_environment_on_teardown) { + ForEach(environments_, internal::Delete<Environment>); + environments_.clear(); + } + + // Try to warn the user if no tests matched the test filter. + if (ShouldWarnIfNoTestsMatchFilter()) { + const std::string filter_warning = + std::string("filter \"") + GTEST_FLAG_GET(filter) + + "\" did not match any test; no tests were run\n"; + ColoredPrintf(GTestColor::kRed, "WARNING: %s", filter_warning.c_str()); +#if GTEST_HAS_FILE_SYSTEM + AppendToTestWarningsOutputFile(filter_warning); +#endif // GTEST_HAS_FILE_SYSTEM + } if (!gtest_is_initialized_before_run_all_tests) { ColoredPrintf( @@ -5931,15 +6132,12 @@ "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_ " will start to enforce the valid usage. " "Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT -#if GTEST_FOR_GOOGLE_ - ColoredPrintf(GTestColor::kRed, - "For more details, see http://wiki/Main/ValidGUnitMain.\n"); -#endif // GTEST_FOR_GOOGLE_ } return !failed; } +#if GTEST_HAS_FILE_SYSTEM // Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file // if the variable is present. If a file already exists at this location, this // function will write over it. If the variable is present, but the file cannot @@ -5959,6 +6157,7 @@ fclose(file); } } +#endif // GTEST_HAS_FILE_SYSTEM // Checks whether sharding is enabled by examining the relevant // environment variable values. If the variables are present, @@ -6037,7 +6236,7 @@ // each TestSuite and TestInfo object. // If shard_tests == true, further filters tests based on sharding // variables in the environment - see -// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md +// https://github.com/google/googletest/blob/main/docs/advanced.md // . Returns the number of tests that should run. int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { const int32_t total_shards = shard_tests == HONOR_SHARDING_PROTOCOL @@ -6057,12 +6256,11 @@ int num_runnable_tests = 0; int num_selected_tests = 0; for (auto* test_suite : test_suites_) { - const std::string& test_suite_name = test_suite->name(); + const std::string& test_suite_name = test_suite->name_; test_suite->set_should_run(false); - for (size_t j = 0; j < test_suite->test_info_list().size(); j++) { - TestInfo* const test_info = test_suite->test_info_list()[j]; - const std::string test_name(test_info->name()); + for (TestInfo* test_info : test_suite->test_info_list()) { + const std::string& test_name = test_info->name_; // A test is disabled if test suite name or test name matches // kDisableTestFilter. const bool is_disabled = @@ -6094,6 +6292,30 @@ return num_selected_tests; } +// Returns true if a warning should be issued if no tests match the test filter +// flag. We can't simply count the number of tests that ran because, for +// instance, test sharding and death tests might mean no tests are expected to +// run in this process, but will run in another process. +bool UnitTestImpl::ShouldWarnIfNoTestsMatchFilter() const { + if (total_test_count() == 0) { + // No tests were linked in to program. + // This case is handled by a different warning. + return false; + } + const PositiveAndNegativeUnitTestFilter gtest_flag_filter( + GTEST_FLAG_GET(filter)); + for (auto* test_suite : test_suites_) { + const std::string& test_suite_name = test_suite->name_; + for (TestInfo* test_info : test_suite->test_info_list()) { + const std::string& test_name = test_info->name_; + if (gtest_flag_filter.MatchesTest(test_suite_name, test_name)) { + return false; + } + } + } + return true; +} + // Prints the given C-string on a single line by replacing all '\n' // characters with string "\\n". If the output takes more than // max_length characters, only prints the first max_length characters @@ -6150,10 +6372,11 @@ } } fflush(stdout); +#if GTEST_HAS_FILE_SYSTEM const std::string& output_format = UnitTestOptions::GetOutputFormat(); if (output_format == "xml" || output_format == "json") { - FILE* fileout = OpenFileForWriting( - UnitTestOptions::GetAbsolutePathToOutputFile().c_str()); + FILE* fileout = + OpenFileForWriting(UnitTestOptions::GetAbsolutePathToOutputFile()); std::stringstream stream; if (output_format == "xml") { XmlUnitTestResultPrinter( @@ -6167,6 +6390,7 @@ fprintf(fileout, "%s", StringStreamToString(&stream).c_str()); fclose(fileout); } +#endif // GTEST_HAS_FILE_SYSTEM } // Sets the OS stack trace getter. @@ -6245,7 +6469,7 @@ // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. GTEST_NO_INLINE_ GTEST_NO_TAIL_CALL_ std::string -GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, int skip_count) { +GetCurrentOsStackTraceExceptTop(int skip_count) { // We pass skip_count + 1 to skip this wrapper function in addition // to what the user really wants to skip. return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); @@ -6486,7 +6710,7 @@ #endif // GTEST_CAN_STREAM_RESULTS_ "\n" "Assertion Behavior:\n" -#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +#if defined(GTEST_HAS_DEATH_TEST) && !defined(GTEST_OS_WINDOWS) " @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" " Set the default death test style.\n" @@ -6538,6 +6762,7 @@ GTEST_INTERNAL_PARSE_FLAG(death_test_style); GTEST_INTERNAL_PARSE_FLAG(death_test_use_fork); GTEST_INTERNAL_PARSE_FLAG(fail_fast); + GTEST_INTERNAL_PARSE_FLAG(fail_if_no_test_linked); GTEST_INTERNAL_PARSE_FLAG(filter); GTEST_INTERNAL_PARSE_FLAG(internal_run_death_test); GTEST_INTERNAL_PARSE_FLAG(list_tests); @@ -6555,7 +6780,7 @@ return false; } -#if GTEST_USE_OWN_FLAGFILE_FLAG_ +#if GTEST_USE_OWN_FLAGFILE_FLAG_ && GTEST_HAS_FILE_SYSTEM static void LoadFlagsFromFile(const std::string& path) { FILE* flagfile = posix::FOpen(path.c_str(), "r"); if (!flagfile) { @@ -6571,7 +6796,7 @@ if (!ParseGoogleTestFlag(lines[i].c_str())) g_help_flag = true; } } -#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ && GTEST_HAS_FILE_SYSTEM // Parses the command line for Google Test flags, without initializing // other parts of Google Test. The type parameter CharType can be @@ -6588,12 +6813,12 @@ bool remove_flag = false; if (ParseGoogleTestFlag(arg)) { remove_flag = true; -#if GTEST_USE_OWN_FLAGFILE_FLAG_ +#if GTEST_USE_OWN_FLAGFILE_FLAG_ && GTEST_HAS_FILE_SYSTEM } else if (ParseFlag(arg, "flagfile", &flagfile_value)) { GTEST_FLAG_SET(flagfile, flagfile_value); LoadFlagsFromFile(flagfile_value); remove_flag = true; -#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ && GTEST_HAS_FILE_SYSTEM } else if (arg_string == "--help" || HasGoogleTestFlagPrefix(arg)) { // Both help flag and unrecognized Google Test flags (excluding // internal ones) trigger help display. @@ -6601,17 +6826,17 @@ } if (remove_flag) { - // Shift the remainder of the argv list left by one. Note - // that argv has (*argc + 1) elements, the last one always being - // NULL. The following loop moves the trailing NULL element as - // well. - for (int j = i; j != *argc; j++) { - argv[j] = argv[j + 1]; + // Shift the remainder of the argv list left by one. + for (int j = i + 1; j < *argc; ++j) { + argv[j - 1] = argv[j]; } // Decrements the argument count. (*argc)--; + // Terminate the array with nullptr. + argv[*argc] = nullptr; + // We also need to decrement the iterator as we just removed // an element. i--; @@ -6627,26 +6852,60 @@ } // Parses the command line for Google Test flags, without initializing -// other parts of Google Test. +// other parts of Google Test. This function updates argc and argv by removing +// flags that are known to GoogleTest (including other user flags defined using +// ABSL_FLAG if GoogleTest is built with GTEST_USE_ABSL). Other arguments +// remain in place. Unrecognized flags are not reported and do not cause the +// program to exit. void ParseGoogleTestFlagsOnly(int* argc, char** argv) { -#if GTEST_HAS_ABSL - if (*argc > 0) { - // absl::ParseCommandLine() requires *argc > 0. - auto positional_args = absl::flags_internal::ParseCommandLineImpl( - *argc, argv, absl::flags_internal::ArgvListAction::kRemoveParsedArgs, - absl::flags_internal::UsageFlagsAction::kHandleUsage, - absl::flags_internal::OnUndefinedFlag::kReportUndefined); - // Any command-line positional arguments not part of any command-line flag - // (or arguments to a flag) are copied back out to argv, with the program - // invocation name at position 0, and argc is resized. This includes - // positional arguments after the flag-terminating delimiter '--'. - // See https://abseil.io/docs/cpp/guides/flags. - std::copy(positional_args.begin(), positional_args.end(), argv); - if (static_cast<int>(positional_args.size()) < *argc) { - argv[positional_args.size()] = nullptr; - *argc = static_cast<int>(positional_args.size()); +#ifdef GTEST_HAS_ABSL_FLAGS + if (*argc <= 0) return; + + std::vector<char*> positional_args; + std::vector<absl::UnrecognizedFlag> unrecognized_flags; + absl::ParseAbseilFlagsOnly(*argc, argv, positional_args, unrecognized_flags); + absl::flat_hash_set<absl::string_view> unrecognized; + for (const auto& flag : unrecognized_flags) { + unrecognized.insert(flag.flag_name); + } + absl::flat_hash_set<char*> positional; + for (const auto& arg : positional_args) { + positional.insert(arg); + } + + int out_pos = 1; + int in_pos = 1; + for (; in_pos < *argc; ++in_pos) { + char* arg = argv[in_pos]; + absl::string_view arg_str(arg); + if (absl::ConsumePrefix(&arg_str, "--")) { + // Flag-like argument. If the flag was unrecognized, keep it. + // If it was a GoogleTest flag, remove it. + if (unrecognized.contains(arg_str)) { + argv[out_pos++] = argv[in_pos]; + continue; + } + } + + if (arg_str.empty()) { + ++in_pos; + break; // '--' indicates that the rest of the arguments are positional + } + + // Probably a positional argument. If it is in fact positional, keep it. + // If it was a value for the flag argument, remove it. + if (positional.contains(arg)) { + argv[out_pos++] = arg; } } + + // The rest are positional args for sure. + while (in_pos < *argc) { + argv[out_pos++] = argv[in_pos++]; + } + + *argc = out_pos; + argv[out_pos] = nullptr; #else ParseGoogleTestFlagsOnlyImpl(argc, argv); #endif @@ -6654,7 +6913,7 @@ // Fix the value of *_NSGetArgc() on macOS, but if and only if // *_NSGetArgv() == argv // Only applicable to char** version of argv -#if GTEST_OS_MAC +#ifdef GTEST_OS_MAC #ifndef GTEST_OS_IOS if (*_NSGetArgv() == argv) { *_NSGetArgc() = *argc; @@ -6682,14 +6941,16 @@ g_argvs.push_back(StreamableToString(argv[i])); } -#if GTEST_HAS_ABSL +#ifdef GTEST_HAS_ABSL absl::InitializeSymbolizer(g_argvs[0].c_str()); +#ifdef GTEST_HAS_ABSL_FLAGS // When using the Abseil Flags library, set the program usage message to the // help message, but remove the color-encoding from the message first. absl::SetProgramUsageMessage(absl::StrReplaceAll( kColorEncodedHelpMessage, {{"@D", ""}, {"@R", ""}, {"@G", ""}, {"@Y", ""}, {"@@", "@"}})); +#endif // GTEST_HAS_ABSL_FLAGS #endif // GTEST_HAS_ABSL ParseGoogleTestFlagsOnly(argc, argv); @@ -6741,12 +7002,13 @@ #endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) } -#if !defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) -// Return value of first environment variable that is set and contains -// a non-empty string. If there are none, return the "fallback" string. -// Since we like the temporary directory to have a directory separator suffix, -// add it if not provided in the environment variable value. -static std::string GetTempDirFromEnv( +#if !defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) || \ + !defined(GTEST_CUSTOM_SRCDIR_FUNCTION_) +// Returns the value of the first environment variable that is set and contains +// a non-empty string. If there are none, returns the "fallback" string. Adds +// the director-separator character as a suffix if not provided in the +// environment variable value. +static std::string GetDirFromEnv( std::initializer_list<const char*> environment_variables, const char* fallback, char separator) { for (const char* variable_name : environment_variables) { @@ -6765,15 +7027,41 @@ std::string TempDir() { #if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) return GTEST_CUSTOM_TEMPDIR_FUNCTION_(); -#elif GTEST_OS_WINDOWS || GTEST_OS_WINDOWS_MOBILE - return GetTempDirFromEnv({"TEST_TMPDIR", "TEMP"}, "\\temp\\", '\\'); -#elif GTEST_OS_LINUX_ANDROID - return GetTempDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/data/local/tmp/", '/'); +#elif defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_WINDOWS_MOBILE) + return GetDirFromEnv({"TEST_TMPDIR", "TEMP"}, "\\temp\\", '\\'); +#elif defined(GTEST_OS_LINUX_ANDROID) + return GetDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/data/local/tmp/", '/'); #else - return GetTempDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/tmp/", '/'); + return GetDirFromEnv({"TEST_TMPDIR", "TMPDIR"}, "/tmp/", '/'); #endif } +#if GTEST_HAS_FILE_SYSTEM && !defined(GTEST_CUSTOM_SRCDIR_FUNCTION_) +// Returns the directory path (including terminating separator) of the current +// executable as derived from argv[0]. +static std::string GetCurrentExecutableDirectory() { + internal::FilePath argv_0(internal::GetArgvs()[0]); + return argv_0.RemoveFileName().string(); +} +#endif + +#if GTEST_HAS_FILE_SYSTEM +std::string SrcDir() { +#if defined(GTEST_CUSTOM_SRCDIR_FUNCTION_) + return GTEST_CUSTOM_SRCDIR_FUNCTION_(); +#elif defined(GTEST_OS_WINDOWS) || defined(GTEST_OS_WINDOWS_MOBILE) + return GetDirFromEnv({"TEST_SRCDIR"}, GetCurrentExecutableDirectory().c_str(), + '\\'); +#elif defined(GTEST_OS_LINUX_ANDROID) + return GetDirFromEnv({"TEST_SRCDIR"}, GetCurrentExecutableDirectory().c_str(), + '/'); +#else + return GetDirFromEnv({"TEST_SRCDIR"}, GetCurrentExecutableDirectory().c_str(), + '/'); +#endif +} +#endif + // Class ScopedTrace // Pushes the given source file location and message onto a per-thread
diff --git a/third_party/googletest/src/googletest/src/gtest_main.cc b/third_party/googletest/src/googletest/src/gtest_main.cc index 4497637..8141caf 100644 --- a/third_party/googletest/src/googletest/src/gtest_main.cc +++ b/third_party/googletest/src/googletest/src/gtest_main.cc
@@ -31,19 +31,32 @@ #include "gtest/gtest.h" -#if GTEST_OS_ESP8266 || GTEST_OS_ESP32 -#if GTEST_OS_ESP8266 +#if defined(GTEST_OS_ESP8266) || defined(GTEST_OS_ESP32) || \ + (defined(GTEST_OS_NRF52) && defined(ARDUINO)) +// Arduino-like platforms: program entry points are setup/loop instead of main. + +#ifdef GTEST_OS_ESP8266 extern "C" { #endif + void setup() { testing::InitGoogleTest(); } void loop() { RUN_ALL_TESTS(); } -#if GTEST_OS_ESP8266 +#ifdef GTEST_OS_ESP8266 } #endif +#elif defined(GTEST_OS_QURT) +// QuRT: program entry point is main, but argc/argv are unusable. + +GTEST_API_ int main() { + printf("Running main() from %s\n", __FILE__); + testing::InitGoogleTest(); + return RUN_ALL_TESTS(); +} #else +// Normal platforms: program entry point is main, argc/argv are initialized. GTEST_API_ int main(int argc, char **argv) { printf("Running main() from %s\n", __FILE__);
diff --git a/third_party/highway/README.libaom b/third_party/highway/README.libaom index 28fdd58..7fc5db9 100644 --- a/third_party/highway/README.libaom +++ b/third_party/highway/README.libaom
@@ -1,8 +1,13 @@ +Name: Highway: C++ library for SIMD +Short Name: highway URL: https://github.com/google/highway - -Version: e92c12750d18c372867809b882dd3ec6874ecc73 -License: BSD-3-clause clear +Version: N/A +Update Mechanism: Manual +Revision: e92c12750d18c372867809b882dd3ec6874ecc73 +License: BSD-3-Clause License File: LICENSE-BSD3 +Shipped in Chromium: no +Security Critical: yes Description: Highway is a C++ library that provides portable SIMD/vector intrinsics.
diff --git a/third_party/libwebm/README.libaom b/third_party/libwebm/README.libaom index 6e43487..850b9ce 100644 --- a/third_party/libwebm/README.libaom +++ b/third_party/libwebm/README.libaom
@@ -1,7 +1,12 @@ +Name: libwebm URL: https://chromium.googlesource.com/webm/libwebm -Version: 3b630045052e1e4d563207ab9e3be8d137c26067 -License: BSD +Version: N/A +Update Mechanism: Manual +Revision: 3b630045052e1e4d563207ab9e3be8d137c26067 +License: BSD-3-Clause License File: LICENSE.TXT +Shipped in Chromium: no +Security Critical: yes Description: libwebm is used to handle WebM container I/O.
diff --git a/third_party/libyuv/README.libaom b/third_party/libyuv/README.libaom index fcdf921..cdf1652 100644 --- a/third_party/libyuv/README.libaom +++ b/third_party/libyuv/README.libaom
@@ -1,8 +1,12 @@ Name: libyuv URL: https://chromium.googlesource.com/libyuv/libyuv/ -Version: dfaf7534e0e536f7e5ef8ddd7326797bd09b8622 -License: BSD +Version: N/A +Update Mechanism: Manual +Revision: dfaf7534e0e536f7e5ef8ddd7326797bd09b8622 +License: BSD-3-Clause License File: LICENSE +Shipped in Chromium: no +Security Critical: yes Description: libyuv is an open source project that includes YUV conversion and scaling
diff --git a/third_party/vector/README.libaom b/third_party/vector/README.libaom index 4b6e4dd..f8e06b0 100644 --- a/third_party/vector/README.libaom +++ b/third_party/vector/README.libaom
@@ -1,8 +1,12 @@ Name: vector URL: https://github.com/goldsborough/vector -Version: commit-id: 40efe82 +Version: N/A +Update Mechanism: Manual +Revision: 40efe82 License: MIT License File: LICENSE +Shipped in Chromium: yes +Security Critical: yes Description: A feature-complete, generic and customizable resizable
diff --git a/third_party/x86inc/README.libaom b/third_party/x86inc/README.libaom index 6b92358..d6f8901 100644 --- a/third_party/x86inc/README.libaom +++ b/third_party/x86inc/README.libaom
@@ -1,7 +1,12 @@ +Name: x86inc.asm URL: https://git.videolan.org/git/x264.git -Version: 3e5aed95cc470f37e2db3e6506a8deb89b527720 +Version: N/A +Update Mechanism: Manual +Revision: 3e5aed95cc470f37e2db3e6506a8deb89b527720 License: ISC License File: LICENSE +Shipped in Chromium: yes +Security Critical: yes Description: x264/libav's framework for x86 assembly. Contains a variety of macros and