diff --git a/av2/encoder/block.h b/av2/encoder/block.h index 8fac5dee67..b6115eee91 100644 --- a/av2/encoder/block.h +++ b/av2/encoder/block.h @@ -1324,6 +1324,13 @@ typedef struct { } CoeffCosts; /*!\cond */ +typedef enum { + kZeroSad = 0, + kVeryLowSad = 1, + kLowSad = 2, + kMedSad = 3, + kHighSad = 4 +} SOURCE_SAD; #define SINGLE_REF_MODES ((REF_FRAMES - 1) * 4) /*!\endcond */ struct inter_modes_info; @@ -1659,6 +1666,10 @@ typedef struct macroblock { /**@{*/ //! Variance of the source frame. unsigned int source_variance; + //! Superblock source SAD. + unsigned int source_sad; + //! Superblock source SAD level. + SOURCE_SAD source_sad_level; //! SSE of the current predictor. unsigned int pred_sse[SINGLE_REF_FRAMES]; /*! Simple motion search buffers. */ diff --git a/av2/encoder/encodeframe.c b/av2/encoder/encodeframe.c index 8984de2857..c3e8b17b3e 100644 --- a/av2/encoder/encodeframe.c +++ b/av2/encoder/encodeframe.c @@ -179,6 +179,53 @@ static BLOCK_SIZE get_rd_var_based_fixed_partition(AV2_COMP *cpi, MACROBLOCK *x, return BLOCK_8X8; } +static unsigned int get_sb_source_sad(const AV2_COMP *cpi, const MACROBLOCK *x, + int mi_row, int mi_col) { + if (cpi->last_source == NULL || cpi->source == NULL) return UINT_MAX; + if (frame_is_intra_only(&cpi->common)) return UINT_MAX; + if (cpi->last_source->y_width != cpi->source->y_width || + cpi->last_source->y_height != cpi->source->y_height) + return UINT_MAX; + + const BLOCK_SIZE sb_size = cpi->common.seq_params.sb_size; + const int block_width = mi_size_wide[sb_size]; + const int block_height = mi_size_high[sb_size]; + // Avoid border superblocks as sdf reads extended border padding. + if (mi_row + block_height > cpi->common.mi_params.mi_rows || + mi_col + block_width > cpi->common.mi_params.mi_cols) + return UINT_MAX; + + const uint16_t *src_y = x->plane[AVM_PLANE_Y].src.buf; + const int src_stride = x->plane[AVM_PLANE_Y].src.stride; + const int last_src_offset = + (mi_row * MI_SIZE) * cpi->last_source->y_stride + (mi_col * MI_SIZE); + const uint16_t *last_src_y = cpi->last_source->y_buffer + last_src_offset; + const int last_src_stride = cpi->last_source->y_stride; + + return cpi->fn_ptr[sb_size].sdf(src_y, src_stride, last_src_y, + last_src_stride); +} + +static SOURCE_SAD get_source_sad_level(unsigned int sb_source_sad, + BLOCK_SIZE sb_size, int bit_depth) { + if (sb_source_sad == UINT_MAX) return kMedSad; + if (sb_source_sad == 0) return kZeroSad; + + const int num_64x64 = (sb_size == BLOCK_256X256) ? 16 + : (sb_size == BLOCK_128X128) ? 4 + : 1; + unsigned int avg_64x64_sad = (sb_source_sad + (num_64x64 >> 1)) / num_64x64; + + if (bit_depth > 8) { + avg_64x64_sad >>= (bit_depth - 8); + } + + if (avg_64x64_sad < 8000) return kVeryLowSad; + if (avg_64x64_sad < 25000) return kLowSad; + if (avg_64x64_sad > 50000) return kHighSad; + return kMedSad; +} + void av2_setup_src_planes(MACROBLOCK *x, const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col, const int num_planes, const CHROMA_REF_INFO *chroma_ref_info) { @@ -840,7 +887,15 @@ static AVM_INLINE void encode_rd_sb(AV2_COMP *cpi, ThreadData *td, xd->tree_type = SHARED_PART; } else if (sf->part_sf.partition_search_type == VAR_BASED_PARTITION) { av2_set_offsets(cpi, tile_info, x, mi_row, mi_col, sb_size, NULL); - av2_choose_var_based_partitioning(cpi, tile_info, td, x, mi_row, mi_col); + unsigned int source_sad = UINT_MAX; + if (cpi->sf.rt_sf.source_metrics_sb) { + source_sad = get_sb_source_sad(cpi, x, mi_row, mi_col); + } + x->source_sad = source_sad; + x->source_sad_level = get_source_sad_level( + source_sad, sb_size, cpi->common.seq_params.bit_depth); + av2_choose_var_based_partitioning(cpi, tile_info, td, x, mi_row, mi_col, + source_sad); for (int loop_idx = 0; loop_idx < total_loop_num; loop_idx++) { xd->tree_type = (total_loop_num == 1 ? SHARED_PART @@ -1371,6 +1426,8 @@ static AVM_INLINE void encode_sb_row(AV2_COMP *cpi, ThreadData *td, xd->cur_frame_force_integer_mv = cm->features.cur_frame_force_integer_mv; x->source_variance = UINT_MAX; + x->source_sad = UINT_MAX; + x->source_sad_level = kMedSad; td->mb.cb_coef_buff = av2_get_cb_coeff_buffer(cpi, mi_row, mi_col); av2_reset_refmv_bank(cm, xd, tile_info, mi_row, mi_col); diff --git a/av2/encoder/speed_features.c b/av2/encoder/speed_features.c index 9ddc0897ae..8389d412f6 100644 --- a/av2/encoder/speed_features.c +++ b/av2/encoder/speed_features.c @@ -755,6 +755,7 @@ static void set_rt_speed_features_framesize_independent( sf->rd_sf.tx_domain_dist_thres_level = 2; sf->rt_sf.use_nonrd_partition = 1; sf->rt_sf.use_only_dc_intra_interframe = true; + sf->rt_sf.source_metrics_sb = 1; sf->winner_mode_sf.tx_size_search_level = USE_FAST_RD; sf->tx_sf.restrict_tx_partition_type_search = 3; } @@ -1084,6 +1085,12 @@ static AVM_INLINE void init_lc_sf(LC_DEC_SPEED_FEATURES *lc_sf) { lc_sf->enable_partition_size_bias = 0; } +static AVM_INLINE void init_rt_sf(REALTIME_SPEED_FEATURES *rt_sf) { + rt_sf->use_nonrd_partition = 0; + rt_sf->use_only_dc_intra_interframe = false; + rt_sf->source_metrics_sb = 0; +} + static AVM_INLINE void set_erp_speed_features_framesize_dependent( AV2_COMP *cpi) { SPEED_FEATURES *const sf = &cpi->sf; @@ -1297,6 +1304,7 @@ void av2_set_speed_features_framesize_independent(AV2_COMP *cpi, int speed) { init_lpf_sf(&sf->lpf_sf); init_flexmv_sf(&sf->flexmv_sf); init_lc_sf(&sf->lc_sf); + init_rt_sf(&sf->rt_sf); if (oxcf->mode == GOOD) { set_good_speed_features_framesize_independent(cpi, sf, speed); diff --git a/av2/encoder/speed_features.h b/av2/encoder/speed_features.h index 73749265f8..15f51bb148 100644 --- a/av2/encoder/speed_features.h +++ b/av2/encoder/speed_features.h @@ -1105,6 +1105,9 @@ typedef struct REALTIME_SPEED_FEATURES { // Flag to disable all but DC intra mode for inter frame prediction. bool use_only_dc_intra_interframe; + + // Compute source sad metrics for superblock. + int source_metrics_sb; } REALTIME_SPEED_FEATURES; typedef struct LC_DEC_SPEED_FEATURES { diff --git a/av2/encoder/var_based_part.c b/av2/encoder/var_based_part.c index 46b84aa855..762bb8fa85 100644 --- a/av2/encoder/var_based_part.c +++ b/av2/encoder/var_based_part.c @@ -377,16 +377,18 @@ static inline int64_t interpolate_threshold(int64_t low_val, int64_t high_val, (q_high - q_low); } -static inline void tune_thresh_based_on_resolution(AV2_COMP *cpi, - int64_t thresholds[], - int64_t threshold_base, - int current_qindex, - int num_pixels) { +static inline void tune_thresh_based_on_resolution( + AV2_COMP *cpi, int64_t thresholds[], int64_t threshold_base, + int current_qindex, int num_pixels, SOURCE_SAD source_sad_level) { const bool is_screen = (cpi->oxcf.tune_cfg.content == AVM_CONTENT_SCREEN); if (num_pixels >= RESOLUTION_720P) thresholds[4] = thresholds[4] << 1; if (num_pixels <= RESOLUTION_288P) { - const int q_low = 110; - const int q_high = 135; + int q_low = 110; + int q_high = 135; + if (source_sad_level <= kLowSad) { + q_low = 95; + q_high = 120; + } thresholds[2] = threshold_base >> 3; if (num_pixels <= RESOLUTION_180P) { thresholds[3] = interpolate_threshold((3 * threshold_base) >> 3, @@ -510,7 +512,7 @@ static void set_vbp_thresholds_key_frame(int64_t thresholds[], * threshold */ static inline void set_vbp_thresholds(AV2_COMP *cpi, int64_t thresholds[], - int qindex) { + int qindex, SOURCE_SAD source_sad_level) { AV2_COMMON *const cm = &cpi->common; const int is_key_frame = frame_is_intra_only(cm); const int threshold_multiplier = is_key_frame ? 120 : 1; @@ -545,7 +547,21 @@ static inline void set_vbp_thresholds(AV2_COMP *cpi, int64_t thresholds[], thresholds[5] = INT64_MAX; tune_thresh_based_on_resolution(cpi, thresholds, threshold_base, - current_qindex, num_pixels); + current_qindex, num_pixels, source_sad_level); + + if (source_sad_level <= kLowSad) { + thresholds[1] = (5 * thresholds[1]) >> 2; + thresholds[2] = (5 * thresholds[2]) >> 2; + thresholds[3] = (3 * thresholds[3]) >> 1; + thresholds[4] = (3 * thresholds[4]) >> 1; + } + if (source_sad_level <= kVeryLowSad) { + thresholds[4] = thresholds[4] << 1; + } + if (source_sad_level <= kVeryLowSad && current_qindex > 130 && + num_pixels <= RESOLUTION_360P) { + thresholds[4] = INT64_MAX; + } } static inline void force_split_ancestors(PART_EVAL_STATUS *force_split, @@ -792,7 +808,8 @@ static void set_vt_partitioning_64x64(AV2_COMP *cpi, MACROBLOCKD *xd, void av2_choose_var_based_partitioning(AV2_COMP *cpi, const TileInfo *const tile, ThreadData *td, MACROBLOCK *x, - int mi_row, int mi_col) { + int mi_row, int mi_col, + unsigned int source_sad) { AV2_COMMON *const cm = &cpi->common; MACROBLOCKD *xd = &x->e_mbd; // Flat array representing quadtree nodes up to 16x16 level for 256x256 SB @@ -813,6 +830,7 @@ void av2_choose_var_based_partitioning(AV2_COMP *cpi, bool is_key_frame = frame_is_intra_only(cm); bool scaled_ref_last = false; const int is_360p_or_smaller = cm->width * cm->height <= RESOLUTION_360P; + const SOURCE_SAD source_sad_level = x->source_sad_level; assert(cm->seq_params.sb_size == BLOCK_64X64 || cm->seq_params.sb_size == BLOCK_128X128 || @@ -827,7 +845,7 @@ void av2_choose_var_based_partitioning(AV2_COMP *cpi, int64_t thresholds[6]; const int qindex = cm->quant_params.base_qindex; - set_vbp_thresholds(cpi, thresholds, qindex); + set_vbp_thresholds(cpi, thresholds, qindex, source_sad_level); src_buf = x->plane[AVM_PLANE_Y].src.buf; int src_stride = x->plane[AVM_PLANE_Y].src.stride; @@ -844,6 +862,15 @@ void av2_choose_var_based_partitioning(AV2_COMP *cpi, dst_buf = xd->plane[AVM_PLANE_Y].pre[0].buf; dst_stride = xd->plane[AVM_PLANE_Y].pre[0].stride; } + + const int block_width = mi_size_wide[cm->seq_params.sb_size]; + const int block_height = mi_size_high[cm->seq_params.sb_size]; + if (source_sad == 0 && y_sad == 0 && + mi_col + block_width <= tile->mi_col_end && + mi_row + block_height <= tile->mi_row_end) { + set_block_size(cpi, mi_row, mi_col, cm->seq_params.sb_size); + return; + } } else { dst_buf = NULL; dst_stride = 0; @@ -938,6 +965,7 @@ void av2_choose_var_based_partitioning(AV2_COMP *cpi, force_split_ancestors(force_split, offset_64x64, offset_32x32, blk64_idx, lvl1_idx); } else if (!is_key_frame && !is_screen && + (source_sad_level > kVeryLowSad) && ((is_360p_or_smaller && ((max_min_var_16X16_diff > (thresholds[3] >> 1) && maxvar_16x16[blk64_idx][lvl1_idx] > thresholds[3]) || @@ -967,8 +995,8 @@ void av2_choose_var_based_partitioning(AV2_COMP *cpi, const int check_max_var = max_var_32x32[blk64_idx] > (thresholds[2] >> 1); const int64_t set_threshold = 3 * (thresholds[2] >> 3); - if (!is_key_frame && max_min_var_32x32_diff > set_threshold && - check_max_var) { + if (!is_key_frame && (source_sad_level > kVeryLowSad) && + max_min_var_32x32_diff > set_threshold && check_max_var) { force_split[offset_64x64 + blk64_idx] = PART_EVAL_ONLY_SPLIT; force_split[0] = PART_EVAL_ONLY_SPLIT; } diff --git a/av2/encoder/var_based_part.h b/av2/encoder/var_based_part.h index 43dd97ff6c..a87476f203 100644 --- a/av2/encoder/var_based_part.h +++ b/av2/encoder/var_based_part.h @@ -99,7 +99,8 @@ extern "C" { void av2_choose_var_based_partitioning(AV2_COMP *cpi, const TileInfo *const tile, ThreadData *td, MACROBLOCK *x, - int mi_row, int mi_col); + int mi_row, int mi_col, + unsigned int source_sad); #ifdef __cplusplus } // extern "C"