/* * 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.
*/
typedefstruct InterModeSearchState {
int64_t best_rd;
int64_t best_skip_rd[2];
MB_MODE_INFO best_mbmode; int best_rate_y; int best_rate_uv; int best_mode_skippable; int best_skip2;
THR_MODES best_mode_index; int num_available_refs;
int64_t dist_refs[REF_FRAMES]; int dist_order_refs[REF_FRAMES];
int64_t mode_threshold[MAX_MODES];
int64_t best_intra_rd; unsignedint best_pred_sse;
/*! * \brief Keep track of best intra rd for use in compound mode.
*/
int64_t best_pred_rd[REFERENCE_MODES]; // Save a set of single_newmv for each checked ref_mv.
int_mv single_newmv[MAX_REF_MV_SEARCH][REF_FRAMES]; int single_newmv_rate[MAX_REF_MV_SEARCH][REF_FRAMES]; int single_newmv_valid[MAX_REF_MV_SEARCH][REF_FRAMES];
int64_t modelled_rd[MB_MODE_COUNT][MAX_REF_MV_SEARCH][REF_FRAMES]; // The rd of simple translation in single inter modes
int64_t simple_rd[MB_MODE_COUNT][MAX_REF_MV_SEARCH][REF_FRAMES];
int64_t best_single_rd[REF_FRAMES];
PREDICTION_MODE best_single_mode[REF_FRAMES];
// Single search results by [directions][modes][reference frames]
SingleInterModeState single_state[2][SINGLE_INTER_MODE_NUM][FWD_REFS]; int single_state_cnt[2][SINGLE_INTER_MODE_NUM];
SingleInterModeState single_state_modelled[2][SINGLE_INTER_MODE_NUM]
[FWD_REFS]; int single_state_modelled_cnt[2][SINGLE_INTER_MODE_NUM];
MV_REFERENCE_FRAME single_rd_order[2][SINGLE_INTER_MODE_NUM][FWD_REFS];
IntraModeSearchState intra_search_state;
RD_STATS best_y_rdcost;
} InterModeSearchState; /*!\endcond */
staticint compare_rd_idx_pair(constvoid *a, constvoid *b) { if (((RdIdxPair *)a)->rd == ((RdIdxPair *)b)->rd) { // To avoid inconsistency in qsort() ordering when two elements are equal, // using idx as tie breaker. Refer aomedia:2928 if (((RdIdxPair *)a)->idx == ((RdIdxPair *)b)->idx) return 0; elseif (((RdIdxPair *)a)->idx > ((RdIdxPair *)b)->idx) return 1; else return -1;
} elseif (((const RdIdxPair *)a)->rd > ((const RdIdxPair *)b)->rd) { return 1;
} else { return -1;
}
}
staticinlinevoid inter_modes_info_sort(const InterModesInfo *inter_modes_info,
RdIdxPair *rd_idx_pair_arr) { if (inter_modes_info->num == 0) { return;
} for (int i = 0; i < inter_modes_info->num; ++i) {
rd_idx_pair_arr[i].idx = i;
rd_idx_pair_arr[i].rd = inter_modes_info->est_rd_arr[i];
}
qsort(rd_idx_pair_arr, inter_modes_info->num, sizeof(rd_idx_pair_arr[0]),
compare_rd_idx_pair);
}
// 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, int width, int height, float *hcorr, float *vcorr) { // The following notation is used: // x - current pixel // y - left neighbor pixel // z - top neighbor pixel
int64_t x_sum = 0, x2_sum = 0, xy_sum = 0, xz_sum = 0;
int64_t x_firstrow = 0, x_finalrow = 0, x_firstcol = 0, x_finalcol = 0;
int64_t x2_firstrow = 0, x2_finalrow = 0, x2_firstcol = 0, x2_finalcol = 0;
// First, process horizontal correlation on just the first row
x_sum += diff[0];
x2_sum += diff[0] * diff[0];
x_firstrow += diff[0];
x2_firstrow += diff[0] * diff[0]; for (int j = 1; j < width; ++j) { const int16_t x = diff[j]; const int16_t y = diff[j - 1];
x_sum += x;
x_firstrow += x;
x2_sum += x * x;
x2_firstrow += x * x;
xy_sum += x * y;
}
// Process vertical correlation in the first column
x_firstcol += diff[0];
x2_firstcol += diff[0] * diff[0]; for (int i = 1; i < height; ++i) { const int16_t x = diff[i * stride]; const int16_t z = diff[(i - 1) * stride];
x_sum += x;
x_firstcol += x;
x2_sum += x * x;
x2_firstcol += x * x;
xz_sum += x * z;
}
// Now process horiz and vert correlation through the rest unit for (int i = 1; i < height; ++i) { for (int j = 1; j < width; ++j) { const int16_t x = diff[i * stride + j]; const int16_t y = diff[i * stride + j - 1]; const int16_t z = diff[(i - 1) * stride + j];
x_sum += x;
x2_sum += x * x;
xy_sum += x * y;
xz_sum += x * z;
}
}
// Determine cost of a single ref frame, where frame types are represented // by a tree: // Level 0: add cost whether this ref is a forward or backward ref
ref_costs_single[LAST_FRAME] += mode_costs->single_ref_cost[ctx_p1][0][0];
ref_costs_single[LAST2_FRAME] += mode_costs->single_ref_cost[ctx_p1][0][0];
ref_costs_single[LAST3_FRAME] += mode_costs->single_ref_cost[ctx_p1][0][0];
ref_costs_single[GOLDEN_FRAME] += mode_costs->single_ref_cost[ctx_p1][0][0];
ref_costs_single[BWDREF_FRAME] += mode_costs->single_ref_cost[ctx_p1][0][1];
ref_costs_single[ALTREF2_FRAME] +=
mode_costs->single_ref_cost[ctx_p1][0][1];
ref_costs_single[ALTREF_FRAME] += mode_costs->single_ref_cost[ctx_p1][0][1];
// Level 1: if this ref is forward ref, // add cost whether it is last/last2 or last3/golden
ref_costs_single[LAST_FRAME] += mode_costs->single_ref_cost[ctx_p3][2][0];
ref_costs_single[LAST2_FRAME] += mode_costs->single_ref_cost[ctx_p3][2][0];
ref_costs_single[LAST3_FRAME] += mode_costs->single_ref_cost[ctx_p3][2][1];
ref_costs_single[GOLDEN_FRAME] += mode_costs->single_ref_cost[ctx_p3][2][1];
// Level 1: if this ref is backward ref // then add cost whether this ref is altref or backward ref
ref_costs_single[BWDREF_FRAME] += mode_costs->single_ref_cost[ctx_p2][1][0];
ref_costs_single[ALTREF2_FRAME] +=
mode_costs->single_ref_cost[ctx_p2][1][0];
ref_costs_single[ALTREF_FRAME] += mode_costs->single_ref_cost[ctx_p2][1][1];
// Level 2: further add cost whether this ref is last or last2
ref_costs_single[LAST_FRAME] += mode_costs->single_ref_cost[ctx_p4][3][0];
ref_costs_single[LAST2_FRAME] += mode_costs->single_ref_cost[ctx_p4][3][1];
// Level 2: last3 or golden
ref_costs_single[LAST3_FRAME] += mode_costs->single_ref_cost[ctx_p5][4][0];
ref_costs_single[GOLDEN_FRAME] += mode_costs->single_ref_cost[ctx_p5][4][1];
// cost: if one ref frame is forward ref, the other ref is backward ref int ref0, ref1; for (ref0 = LAST_FRAME; ref0 <= GOLDEN_FRAME; ++ref0) { for (ref1 = BWDREF_FRAME; ref1 <= ALTREF_FRAME; ++ref1) {
ref_costs_comp[ref0][ref1] =
ref_bicomp_costs[ref0] + ref_bicomp_costs[ref1];
}
}
// Take a snapshot of the coding context so it can be // restored if we decide to encode this way
ctx->rd_stats.skip_txfm = x->txfm_search_info.skip_txfm;
ctx->skippable = skippable; #if CONFIG_INTERNAL_STATS
ctx->best_mode_index = mode_index; #endif// CONFIG_INTERNAL_STATS
ctx->mic = *xd->mi[0];
av1_copy_mbmi_ext_to_mbmi_ext_frame(&ctx->mbmi_ext_best, &x->mbmi_ext,
av1_ref_frame_type(xd->mi[0]->ref_frame));
}
if (scaled_ref_frame) { // Setup pred block based on scaled reference, because av1_mv_pred() doesn't // support scaling.
av1_setup_pred_block(xd, yv12_mb[ref_frame], scaled_ref_frame, NULL, NULL,
num_planes);
} else {
av1_setup_pred_block(xd, yv12_mb[ref_frame], yv12, sf, sf, num_planes);
}
// Gets an initial list of candidate vectors from neighbours and orders them
av1_find_mv_refs(cm, xd, mbmi, ref_frame, mbmi_ext->ref_mv_count,
xd->ref_mv_stack, xd->weight, NULL, mbmi_ext->global_mvs,
mbmi_ext->mode_context); // TODO(Ravi): Populate mbmi_ext->ref_mv_stack[ref_frame][4] and // mbmi_ext->weight[ref_frame][4] inside av1_find_mv_refs.
av1_copy_usable_ref_mv_stack_and_weight(xd, mbmi_ext, ref_frame); // Further refinement that is encode side only to test the top few candidates // in full and choose the best as the center point for subsequent searches. // The current implementation doesn't support scaling.
av1_mv_pred(cpi, x, yv12_mb[ref_frame][0].buf, yv12_mb[ref_frame][0].stride,
ref_frame, block_size);
// Go back to unscaled reference. if (scaled_ref_frame) { // We had temporarily setup pred block based on scaled reference above. Go // back to unscaled reference now, for subsequent use.
av1_setup_pred_block(xd, yv12_mb[ref_frame], yv12, sf, sf, num_planes);
}
}
// TODO(jingning): this mv clamping function should be block size dependent. staticinlinevoid clamp_mv2(MV *mv, const MACROBLOCKD *xd) { const SubpelMvLimits mv_limits = { xd->mb_to_left_edge - LEFT_TOP_MARGIN,
xd->mb_to_right_edge + RIGHT_BOTTOM_MARGIN,
xd->mb_to_top_edge - LEFT_TOP_MARGIN,
xd->mb_to_bottom_edge +
RIGHT_BOTTOM_MARGIN };
clamp_mv(mv, &mv_limits);
}
/* If the current mode shares the same mv with other modes with higher cost,
* skip this mode. */ staticint skip_repeated_mv(const AV1_COMMON *const cm, const MACROBLOCK *const x,
PREDICTION_MODE this_mode, const MV_REFERENCE_FRAME ref_frames[2],
InterModeSearchState *search_state) { constint is_comp_pred = ref_frames[1] > INTRA_FRAME; const uint8_t ref_frame_type = av1_ref_frame_type(ref_frames); const MB_MODE_INFO_EXT *const mbmi_ext = &x->mbmi_ext; constint ref_mv_count = mbmi_ext->ref_mv_count[ref_frame_type];
PREDICTION_MODE compare_mode = MB_MODE_COUNT; if (!is_comp_pred) { if (this_mode == NEARMV) { if (ref_mv_count == 0) { // NEARMV has the same motion vector as NEARESTMV
compare_mode = NEARESTMV;
} if (ref_mv_count == 1 &&
cm->global_motion[ref_frames[0]].wmtype <= TRANSLATION) { // NEARMV has the same motion vector as GLOBALMV
compare_mode = GLOBALMV;
}
} if (this_mode == GLOBALMV) { if (ref_mv_count == 0 &&
cm->global_motion[ref_frames[0]].wmtype <= TRANSLATION) { // GLOBALMV has the same motion vector as NEARESTMV
compare_mode = NEARESTMV;
} if (ref_mv_count == 1) { // GLOBALMV has the same motion vector as NEARMV
compare_mode = NEARMV;
}
}
if (compare_mode != MB_MODE_COUNT) { // Use modelled_rd to check whether compare mode was searched if (search_state->modelled_rd[compare_mode][0][ref_frames[0]] !=
INT64_MAX) { const int16_t mode_ctx =
av1_mode_context_analyzer(mbmi_ext->mode_context, ref_frames); constint compare_cost =
cost_mv_ref(&x->mode_costs, compare_mode, mode_ctx); constint this_cost = cost_mv_ref(&x->mode_costs, this_mode, mode_ctx);
// Only skip if the mode cost is larger than compare mode cost if (this_cost > compare_cost) {
search_state->modelled_rd[this_mode][0][ref_frames[0]] =
search_state->modelled_rd[compare_mode][0][ref_frames[0]]; return 1;
}
}
}
} return 0;
}
// To use single newmv directly for compound modes, need to clamp the mv to the // valid mv range. Without this, encoder would generate out of range mv, and // this is seen in 8k encoding. staticinlinevoid clamp_mv_in_range(MACROBLOCK *const x, int_mv *mv, int ref_idx) { const int_mv ref_mv = av1_get_ref_mv(x, ref_idx);
SubpelMvLimits mv_limits;
// Return after single_newmv is set. if (mode_info[mbmi->ref_mv_idx].skip) return INT64_MAX;
}
return 0;
}
staticinlinevoid update_mode_start_end_index( const AV1_COMP *const cpi, const MB_MODE_INFO *const mbmi, int *mode_index_start, int *mode_index_end, int last_motion_mode_allowed, int interintra_allowed, int eval_motion_mode) {
*mode_index_start = (int)SIMPLE_TRANSLATION;
*mode_index_end = (int)last_motion_mode_allowed + interintra_allowed; if (cpi->sf.winner_mode_sf.motion_mode_for_winner_cand) { if (!eval_motion_mode) {
*mode_index_end = (int)SIMPLE_TRANSLATION;
} else { // Set the start index appropriately to process motion modes other than // simple translation
*mode_index_start = 1;
}
} if (cpi->sf.inter_sf.extra_prune_warped && mbmi->bsize > BLOCK_16X16)
*mode_index_end = SIMPLE_TRANSLATION;
}
/*!\brief AV1 motion mode search * * \ingroup inter_mode_search * Function to search over and determine the motion mode. It will update * mbmi->motion_mode to one of SIMPLE_TRANSLATION, OBMC_CAUSAL, or * WARPED_CAUSAL and determine any necessary side information for the selected * motion mode. It will also perform the full transform search, unless the * input parameter do_tx_search indicates to do an estimation of the RD rather * than an RD corresponding to a full transform search. It will return the * RD for the final motion_mode. * Do the RD search for a given inter mode and compute all information relevant * to the input mode. It will compute the best MV, * compound parameters (if the mode is a compound mode) and interpolation filter * parameters. * * \param[in] cpi Top-level encoder structure. * \param[in] tile_data Pointer to struct holding adaptive * data/contexts/models for the tile during * encoding. * \param[in] x Pointer to struct holding all the data for * the current macroblock. * \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 * for only the Y plane. * \param[in,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 * search. See the documentation for this * struct for a description of each member. * \param[in] ref_best_rd Best RD found so far for this block. * It is used for early termination of this * search if the RD exceeds this value. * \param[in,out] ref_skip_rd A length 2 array, where skip_rd[0] is the * best total RD for a skip mode so far, and * skip_rd[1] is the best RD for a skip mode so * far in luma. This is used as a speed feature * to skip the transform search if the computed * skip RD for the current mode is not better * than the best skip_rd so far. * \param[in,out] rate_mv The rate associated with the motion vectors. * This will be modified if a motion search is * done in the motion mode search. * \param[in,out] orig_dst A prediction buffer to hold a computed * prediction. This will eventually hold the * final prediction, and the tmp_dst info will * be copied here. * \param[in,out] best_est_rd Estimated RD for motion mode search if * do_tx_search (see below) is 0. * \param[in] do_tx_search Parameter to indicate whether or not to do * a full transform search. This will compute * an estimated RD for the modes without the * transform search and later perform the full * transform search on the best candidates. * \param[in] inter_modes_info InterModesInfo struct to hold inter mode * information to perform a full transform * search only on winning candidates searched * with an estimate for transform coding RD. * \param[in] eval_motion_mode Boolean whether or not to evaluate motion * motion modes other than SIMPLE_TRANSLATION. * \param[out] yrd Stores the rdcost corresponding to encoding * the luma plane. * \return Returns INT64_MAX if the determined motion mode is invalid and the * current motion mode being tested should be skipped. It returns 0 if the * motion mode search is a success.
*/ static int64_t motion_mode_rd( const AV1_COMP *const cpi, TileDataEnc *tile_data, MACROBLOCK *const x,
BLOCK_SIZE bsize, RD_STATS *rd_stats, RD_STATS *rd_stats_y,
RD_STATS *rd_stats_uv, HandleInterModeArgs *const args, int64_t ref_best_rd,
int64_t *ref_skip_rd, int *rate_mv, const BUFFER_SET *orig_dst,
int64_t *best_est_rd, int do_tx_search, InterModesInfo *inter_modes_info, int eval_motion_mode, int64_t *yrd) { const AV1_COMMON *const cm = &cpi->common; const FeatureFlags *const features = &cm->features;
TxfmSearchInfo *txfm_info = &x->txfm_search_info; constint num_planes = av1_num_planes(cm);
MACROBLOCKD *xd = &x->e_mbd;
MB_MODE_INFO *mbmi = xd->mi[0]; constint is_comp_pred = has_second_ref(mbmi); const PREDICTION_MODE this_mode = mbmi->mode; constint 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]; constint rate_mv0 = *rate_mv; constint interintra_allowed = cm->seq_params->enable_interintra_compound &&
is_interintra_allowed(mbmi) &&
mbmi->compound_idx;
WARP_SAMPLE_INFO *const warp_sample_info =
&x->warp_sample_info[mbmi->ref_frame[0]]; int *pts0 = warp_sample_info->pts; int *pts_inref0 = warp_sample_info->pts_inref;
assert(mbmi->ref_frame[1] != INTRA_FRAME); const MV_REFERENCE_FRAME ref_frame_1 = mbmi->ref_frame[1];
av1_invalid_rd_stats(&best_rd_stats);
mbmi->num_proj_ref = 1; // assume num_proj_ref >=1
MOTION_MODE last_motion_mode_allowed = SIMPLE_TRANSLATION;
*yrd = INT64_MAX; if (features->switchable_motion_mode) { // Determine which motion modes to search if more than SIMPLE_TRANSLATION // is allowed.
last_motion_mode_allowed = motion_mode_allowed(
xd->global_motion, xd, mbmi, features->allow_warped_motion);
}
if (last_motion_mode_allowed == WARPED_CAUSAL) { // Collect projection samples used in least squares approximation of // the warped motion parameters if WARPED_CAUSAL is going to be searched. if (warp_sample_info->num < 0) {
warp_sample_info->num = av1_findSamples(cm, xd, pts0, pts_inref0);
}
mbmi->num_proj_ref = warp_sample_info->num;
} constint total_samples = mbmi->num_proj_ref; if (total_samples == 0) { // Do not search WARPED_CAUSAL if there are no samples to use to determine // warped parameters.
last_motion_mode_allowed = OBMC_CAUSAL;
}
// Modify the start and end index according to speed features. For example, // if SIMPLE_TRANSLATION has already been searched according to // the motion_mode_for_winner_cand speed feature, update the mode_index_start // to avoid searching it again.
update_mode_start_end_index(cpi, mbmi, &mode_index_start, &mode_index_end,
last_motion_mode_allowed, interintra_allowed,
eval_motion_mode); // Main function loop. This loops over all of the possible motion modes and // computes RD to determine the best one. This process includes computing // any necessary side information for the motion mode and performing the // transform search. for (int mode_index = mode_index_start; mode_index <= mode_index_end;
mode_index++) { if (args->skip_motion_mode && mode_index) continue; int tmp_rate2 = rate2_nocoeff; constint is_interintra_mode = mode_index > (int)last_motion_mode_allowed; int tmp_rate_mv = rate_mv0;
*mbmi = base_mbmi; if (is_interintra_mode) { // Only use SIMPLE_TRANSLATION for interintra
mbmi->motion_mode = SIMPLE_TRANSLATION;
} else {
mbmi->motion_mode = (MOTION_MODE)mode_index;
assert(mbmi->ref_frame[1] != INTRA_FRAME);
}
// Do not search OBMC if the probability of selecting it is below a // predetermined threshold for this update_type and block size. const FRAME_UPDATE_TYPE update_type =
get_frame_update_type(&cpi->ppi->gf_group, cpi->gf_frame_index); int use_actual_frame_probs = 1; int prune_obmc; #if CONFIG_FPMT_TEST
use_actual_frame_probs =
(cpi->ppi->fpmt_unit_test_cfg == PARALLEL_SIMULATION_ENCODE) ? 0 : 1; if (!use_actual_frame_probs) {
prune_obmc = cpi->ppi->temp_frame_probs.obmc_probs[update_type][bsize] <
cpi->sf.inter_sf.prune_obmc_prob_thresh;
} #endif if (use_actual_frame_probs) {
prune_obmc = cpi->ppi->frame_probs.obmc_probs[update_type][bsize] <
cpi->sf.inter_sf.prune_obmc_prob_thresh;
} if ((!cpi->oxcf.motion_mode_cfg.enable_obmc || prune_obmc) &&
mbmi->motion_mode == OBMC_CAUSAL) continue;
if (mbmi->motion_mode == SIMPLE_TRANSLATION && !is_interintra_mode) { // SIMPLE_TRANSLATION mode: no need to recalculate. // The prediction is calculated before motion_mode_rd() is called in // handle_inter_mode()
} elseif (mbmi->motion_mode == OBMC_CAUSAL) { const uint32_t cur_mv = mbmi->mv[0].as_int; // OBMC_CAUSAL not allowed for compound prediction
assert(!is_comp_pred); if (have_newmv_in_inter_mode(this_mode)) {
av1_single_motion_search(cpi, x, bsize, 0, &tmp_rate_mv, INT_MAX, NULL,
&mbmi->mv[0], NULL);
tmp_rate2 = rate2_nocoeff - rate_mv0 + tmp_rate_mv;
} if ((mbmi->mv[0].as_int != cur_mv) || eval_motion_mode) { // Build the predictor according to the current motion vector if it has // not already been built
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, orig_dst, bsize,
0, av1_num_planes(cm) - 1);
} // Build the inter predictor by blending the predictor corresponding to // this MV, and the neighboring blocks using the OBMC model
av1_build_obmc_inter_prediction(
cm, xd, args->above_pred_buf, args->above_pred_stride,
args->left_pred_buf, args->left_pred_stride); #if !CONFIG_REALTIME_ONLY
} elseif (mbmi->motion_mode == WARPED_CAUSAL) { int pts[SAMPLES_ARRAY_SIZE], pts_inref[SAMPLES_ARRAY_SIZE];
mbmi->motion_mode = WARPED_CAUSAL;
mbmi->wm_params.wmtype = DEFAULT_WMTYPE;
mbmi->interp_filters =
av1_broadcast_interp_filter(av1_unswitchable_filter(interp_filter));
memcpy(pts, pts0, total_samples * 2 * sizeof(*pts0));
memcpy(pts_inref, pts_inref0, total_samples * 2 * sizeof(*pts_inref0)); // Select the samples according to motion vector difference if (mbmi->num_proj_ref > 1) {
mbmi->num_proj_ref = av1_selectSamples(
&mbmi->mv[0].as_mv, pts, pts_inref, mbmi->num_proj_ref, bsize);
}
// Compute the warped motion parameters with a least squares fit // using the collected samples if (!av1_find_projection(mbmi->num_proj_ref, pts, pts_inref, bsize,
mbmi->mv[0].as_mv.row, mbmi->mv[0].as_mv.col,
&mbmi->wm_params, mi_row, mi_col)) {
assert(!is_comp_pred); if (have_newmv_in_inter_mode(this_mode)) { // Refine MV for NEWMV mode const int_mv mv0 = mbmi->mv[0]; const WarpedMotionParams wm_params0 = mbmi->wm_params; constint num_proj_ref0 = mbmi->num_proj_ref;
// Refine MV in a small range.
av1_refine_warped_mv(xd, cm, &ms_params, bsize, pts0, pts_inref0,
total_samples, cpi->sf.mv_sf.warp_search_method,
cpi->sf.mv_sf.warp_search_iters);
if (mv0.as_int != mbmi->mv[0].as_int) { // Keep the refined MV and WM parameters.
tmp_rate_mv = av1_mv_bit_cost(
&mbmi->mv[0].as_mv, &ref_mv.as_mv, x->mv_costs->nmv_joint_cost,
x->mv_costs->mv_cost_stack, MV_COST_WEIGHT);
tmp_rate2 = rate2_nocoeff - rate_mv0 + tmp_rate_mv;
} else { // Restore the old MV and WM parameters.
mbmi->mv[0] = mv0;
mbmi->wm_params = wm_params0;
mbmi->num_proj_ref = num_proj_ref0;
}
}
// Check NEARESTMV, NEARMV, GLOBALMV ref mvs for duplicate and skip the relevant // mode // Note(rachelbarker): This speed feature currently does not interact correctly // with global motion. The issue is that, when global motion is used, GLOBALMV // produces a different prediction to NEARESTMV/NEARMV even if the motion // vectors are the same. Thus GLOBALMV should not be pruned in this case. staticinlineint check_repeat_ref_mv(const MB_MODE_INFO_EXT *mbmi_ext, int ref_idx, const MV_REFERENCE_FRAME *ref_frame,
PREDICTION_MODE single_mode) { const uint8_t ref_frame_type = av1_ref_frame_type(ref_frame); constint ref_mv_count = mbmi_ext->ref_mv_count[ref_frame_type];
assert(single_mode != NEWMV); if (single_mode == NEARESTMV) { return 0;
} elseif (single_mode == NEARMV) { // when ref_mv_count = 0, NEARESTMV and NEARMV are same as GLOBALMV // when ref_mv_count = 1, NEARMV is same as GLOBALMV if (ref_mv_count < 2) return 1;
} elseif (single_mode == GLOBALMV) { // when ref_mv_count == 0, GLOBALMV is same as NEARESTMV if (ref_mv_count == 0) return 1; // when ref_mv_count == 1, NEARMV is same as GLOBALMV elseif (ref_mv_count == 1) return 0;
int stack_size = AOMMIN(USABLE_REF_MV_STACK_SIZE, ref_mv_count); // Check GLOBALMV is matching with any mv in ref_mv_stack for (int ref_mv_idx = 0; ref_mv_idx < stack_size; ref_mv_idx++) {
int_mv this_mv;
// Skip NEARESTMV and NEARMV modes based on refmv weight computed in ref mv list // population staticinlineint skip_nearest_near_mv_using_refmv_weight( const MACROBLOCK *const x, const PREDICTION_MODE this_mode, const int8_t ref_frame_type, PREDICTION_MODE best_mode) { if (this_mode != NEARESTMV && this_mode != NEARMV) return 0; // Do not skip the mode if the current block has not yet obtained a valid // inter mode. if (!is_inter_mode(best_mode)) return 0;
const MACROBLOCKD *xd = &x->e_mbd; // Do not skip the mode if both the top and left neighboring blocks are not // available. if (!xd->left_available || !xd->up_available) return 0; const MB_MODE_INFO_EXT *const mbmi_ext = &x->mbmi_ext; const uint16_t *const ref_mv_weight = mbmi_ext->weight[ref_frame_type]; constint ref_mv_count =
AOMMIN(MAX_REF_MV_SEARCH, mbmi_ext->ref_mv_count[ref_frame_type]);
if (ref_mv_count == 0) return 0; // If ref mv list has at least one nearest candidate do not prune NEARESTMV if (this_mode == NEARESTMV && ref_mv_weight[0] >= REF_CAT_LEVEL) return 0;
// Count number of ref mvs populated from nearest candidates int nearest_refmv_count = 0; for (int ref_mv_idx = 0; ref_mv_idx < ref_mv_count; ref_mv_idx++) { if (ref_mv_weight[ref_mv_idx] >= REF_CAT_LEVEL) nearest_refmv_count++;
}
// nearest_refmv_count indicates the closeness of block motion characteristics // with respect to its spatial neighbor. Smaller value of nearest_refmv_count // w.r.t to ref_mv_count means less correlation with its spatial neighbors. // Hence less possibility for NEARESTMV and NEARMV modes becoming the best // mode since these modes work well for blocks that shares similar motion // characteristics with its neighbor. Thus, NEARMV mode is pruned when // nearest_refmv_count is relatively smaller than ref_mv_count and NEARESTMV // mode is pruned if none of the ref mvs are populated from nearest candidate. constint prune_thresh = 1 + (ref_mv_count >= 2); if (nearest_refmv_count < prune_thresh) return 1; return 0;
}
// This function update the non-new mv for the current prediction mode staticinlineint build_cur_mv(int_mv *cur_mv, PREDICTION_MODE this_mode, const AV1_COMMON *cm, const MACROBLOCK *x, int skip_repeated_ref_mv) { const MACROBLOCKD *xd = &x->e_mbd; const MB_MODE_INFO *mbmi = xd->mi[0]; constint is_comp_pred = has_second_ref(mbmi);
int ret = 1; for (int i = 0; i < is_comp_pred + 1; ++i) {
int_mv this_mv;
this_mv.as_int = INVALID_MV;
ret = get_this_mv(&this_mv, this_mode, i, mbmi->ref_mv_idx,
skip_repeated_ref_mv, mbmi->ref_frame, &x->mbmi_ext); if (!ret) return 0; const PREDICTION_MODE single_mode = get_single_mode(this_mode, i); if (single_mode == NEWMV) { const uint8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
cur_mv[i] =
(i == 0) ? x->mbmi_ext.ref_mv_stack[ref_frame_type][mbmi->ref_mv_idx]
.this_mv
: x->mbmi_ext.ref_mv_stack[ref_frame_type][mbmi->ref_mv_idx]
.comp_mv;
} else {
ret &= clamp_and_check_mv(cur_mv + i, this_mv, cm, x);
}
} return ret;
}
// Checks if particular ref_mv_idx should be pruned. staticint prune_ref_mv_idx_using_qindex(constint reduce_inter_modes, constint qindex, constint ref_mv_idx) { if (reduce_inter_modes >= 3) return 1; // Q-index logic based pruning is enabled only for // reduce_inter_modes = 2.
assert(reduce_inter_modes == 2); // When reduce_inter_modes=2, pruning happens as below based on q index. // For q index range between 0 and 85: prune if ref_mv_idx >= 1. // For q index range between 86 and 170: prune if ref_mv_idx == 2. // For q index range between 171 and 255: no pruning. constint min_prune_ref_mv_idx = (qindex * 3 / QINDEX_RANGE) + 1; return (ref_mv_idx >= min_prune_ref_mv_idx);
}
// Represents a set of integers, from 0 to sizeof(int) * 8, as bits in // an integer. 0 for the i-th bit means that integer is excluded, 1 means // it is included. staticinlinevoid mask_set_bit(int *mask, int index) { *mask |= (1 << index); }
// Before performing the full MV search in handle_inter_mode, do a simple // translation search and see if we can eliminate any motion vectors. // 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. staticint 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, constint ref_set) { // If the number of ref mv count is equal to 1, do not prune the same. It // is better to evaluate the same than to prune it. if (ref_set == 1) return 1;
AV1_COMMON *const cm = &cpi->common; const MACROBLOCKD *const xd = &x->e_mbd; const MB_MODE_INFO *const mbmi = xd->mi[0]; const PREDICTION_MODE this_mode = mbmi->mode;
// Only search indices if they have some chance of being good. int good_indices = 0; for (int i = 0; i < ref_set; ++i) { if (ref_mv_idx_early_breakout(&cpi->sf, &cpi->ref_frame_dist_info, x, args,
ref_best_rd, i)) { continue;
}
mask_set_bit(&good_indices, i);
}
// Only prune in NEARMV mode, if the speed feature is set, and the block size // is large enough. If these conditions are not met, return all good indices // found so far. if (!cpi->sf.inter_sf.prune_mode_search_simple_translation) return good_indices; if (!have_nearmv_in_inter_mode(this_mode)) return good_indices; if (num_pels_log2_lookup[bsize] <= 6) return good_indices; // Do not prune when there is internal resizing. TODO(elliottk) fix this // so b/2384 can be resolved. if (av1_is_scaled(get_ref_scale_factors(cm, mbmi->ref_frame[0])) ||
(mbmi->ref_frame[1] > 0 &&
av1_is_scaled(get_ref_scale_factors(cm, mbmi->ref_frame[1])))) { return good_indices;
}
// Calculate the RD cost for the motion vectors using simple translation.
int64_t idx_rdcost[] = { INT64_MAX, INT64_MAX, INT64_MAX }; for (int ref_mv_idx = 0; ref_mv_idx < ref_set; ++ref_mv_idx) { // If this index is bad, ignore it. if (!mask_check_bit(good_indices, ref_mv_idx)) { continue;
}
idx_rdcost[ref_mv_idx] = simple_translation_pred_rd(
cpi, x, rd_stats, args, ref_mv_idx, ref_best_rd, bsize);
} // Find the index with the best RD cost. int best_idx = 0; for (int i = 1; i < MAX_REF_MV_SEARCH; ++i) { if (idx_rdcost[i] < idx_rdcost[best_idx]) {
best_idx = i;
}
} // Only include indices that are good and within a % of the best. constdouble dth = has_second_ref(mbmi) ? 1.05 : 1.001; // If the simple translation cost is not within this multiple of the // best RD, skip it. Note that the cutoff is derived experimentally. constdouble ref_dth = 5; int result = 0; for (int i = 0; i < ref_set; ++i) { if (mask_check_bit(good_indices, i) &&
(1.0 * idx_rdcost[i]) / idx_rdcost[best_idx] < dth &&
(1.0 * idx_rdcost[i]) / ref_best_rd < ref_dth) {
mask_set_bit(&result, i);
}
} return result;
}
/*!\brief Motion mode information for inter mode search speedup. * * Used in a speed feature to search motion modes other than * SIMPLE_TRANSLATION only on winning candidates.
*/ typedefstruct motion_mode_candidate { /*! * Mode info for the motion mode candidate.
*/
MB_MODE_INFO mbmi; /*! * Rate describing the cost of the motion vectors for this candidate.
*/ int rate_mv; /*! * Rate before motion mode search and transform coding is applied.
*/ int rate2_nocoeff; /*! * An integer value 0 or 1 which indicates whether or not to skip the motion * mode search and default to SIMPLE_TRANSLATION as a speed feature for this * candidate.
*/ int skip_motion_mode; /*! * Total RD cost for this candidate.
*/
int64_t rd_cost;
} motion_mode_candidate;
// Checks if the current reference frame matches with neighbouring block's // (top/left) reference frames staticinlineint ref_match_found_in_nb_blocks(MB_MODE_INFO *cur_mbmi,
MB_MODE_INFO *nb_mbmi) {
MV_REFERENCE_FRAME nb_ref_frames[2] = { nb_mbmi->ref_frame[0],
nb_mbmi->ref_frame[1] };
MV_REFERENCE_FRAME cur_ref_frames[2] = { cur_mbmi->ref_frame[0],
cur_mbmi->ref_frame[1] }; constint is_cur_comp_pred = has_second_ref(cur_mbmi); int match_found = 0;
for (int i = 0; i < (is_cur_comp_pred + 1); i++) { if ((cur_ref_frames[i] == nb_ref_frames[0]) ||
(cur_ref_frames[i] == nb_ref_frames[1]))
match_found = 1;
} return match_found;
}
staticinlineint find_ref_match_in_above_nbs(constint total_mi_cols,
MACROBLOCKD *xd) { if (!xd->up_available) return 1; constint mi_col = xd->mi_col;
MB_MODE_INFO **cur_mbmi = xd->mi; // prev_row_mi points into the mi array, starting at the beginning of the // previous row.
MB_MODE_INFO **prev_row_mi = xd->mi - mi_col - 1 * xd->mi_stride; constint end_col = AOMMIN(mi_col + xd->width, total_mi_cols);
uint8_t mi_step; for (int above_mi_col = mi_col; above_mi_col < end_col;
above_mi_col += mi_step) {
MB_MODE_INFO **above_mi = prev_row_mi + above_mi_col;
mi_step = mi_size_wide[above_mi[0]->bsize]; int match_found = 0; if (is_inter_block(*above_mi))
match_found = ref_match_found_in_nb_blocks(*cur_mbmi, *above_mi); if (match_found) return 1;
} return 0;
}
staticinlineint find_ref_match_in_left_nbs(constint total_mi_rows,
MACROBLOCKD *xd) { if (!xd->left_available) return 1; constint mi_row = xd->mi_row;
MB_MODE_INFO **cur_mbmi = xd->mi; // prev_col_mi points into the mi array, starting at the top of the // previous column
MB_MODE_INFO **prev_col_mi = xd->mi - 1 - mi_row * xd->mi_stride; constint end_row = AOMMIN(mi_row + xd->height, total_mi_rows);
uint8_t mi_step; for (int left_mi_row = mi_row; left_mi_row < end_row;
left_mi_row += mi_step) {
MB_MODE_INFO **left_mi = prev_col_mi + left_mi_row * xd->mi_stride;
mi_step = mi_size_high[left_mi[0]->bsize]; int match_found = 0; if (is_inter_block(*left_mi))
match_found = ref_match_found_in_nb_blocks(*cur_mbmi, *left_mi); if (match_found) return 1;
} return 0;
} /*!\endcond */
/*! \brief Struct used to hold TPL data to * narrow down parts of the inter mode search.
*/ typedefstruct { /*! * The best inter cost out of all of the reference frames.
*/
int64_t best_inter_cost; /*! * The inter cost for each reference frame.
*/
int64_t ref_inter_cost[INTER_REFS_PER_FRAME];
} PruneInfoFromTpl;
#if !CONFIG_REALTIME_ONLY // TODO(Remya): Check if get_tpl_stats_b() can be reused staticinlinevoid get_block_level_tpl_stats(
AV1_COMP *cpi, BLOCK_SIZE bsize, int mi_row, int mi_col, int *valid_refs,
PruneInfoFromTpl *inter_cost_info_from_tpl) {
AV1_COMMON *const cm = &cpi->common;
// 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 staticconstint tpl_inter_mode_prune_mul_factor[3][MAX_REF_MV_SEARCH + 1] = {
{ 6, 6, 6, 4 }, { 6, 4, 4, 4 }, { 5, 4, 4, 4 }
};
constint 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];
} else { const int64_t inter_cost_ref0 =
inter_cost_info_from_tpl->ref_inter_cost[refs[0] - 1]; const int64_t inter_cost_ref1 =
inter_cost_info_from_tpl->ref_inter_cost[refs[1] - 1]; // Choose maximum inter_cost among inter_cost_ref0 and inter_cost_ref1 for // more aggressive pruning
cur_inter_cost = AOMMAX(inter_cost_ref0, inter_cost_ref1);
}
// Prune the mode if cur_inter_cost is greater than threshold times // best_inter_cost if (cur_inter_cost >
((tpl_inter_mode_prune_mul_factor[prune_level][prune_index] *
best_inter_cost) >>
2)) return 1; return 0;
}
/*!\brief High level function to select parameters for compound mode. * * \ingroup inter_mode_search * The main search functionality is done in the call to av1_compound_type_rd(). * * \param[in] cpi Top-level encoder structure. * \param[in] x Pointer to struct holding all the data for * the current macroblock. * \param[in] args HandleInterModeArgs struct holding * miscellaneous arguments for inter mode * search. See the documentation for this * struct for a description of each member. * \param[in] ref_best_rd Best RD found so far for this block. * It is used for early termination of this * search if the RD exceeds this value. * \param[in,out] cur_mv Current motion vector. * \param[in] bsize Current block size. * \param[in,out] compmode_interinter_cost RD of the selected interinter compound mode. * \param[in,out] rd_buffers CompoundTypeRdBuffers struct to hold all * allocated buffers for the compound * predictors and masks in the compound type * search. * \param[in,out] orig_dst A prediction buffer to hold a computed * prediction. This will eventually hold the * final prediction, and the tmp_dst info will * be copied here. * \param[in] tmp_dst A temporary prediction buffer to hold a * computed prediction. * \param[in,out] rate_mv The rate associated with the motion vectors. * This will be modified if a motion search is * done in the motion mode search. * \param[in,out] rd_stats Struct to keep track of the overall RD * information. * \param[in,out] skip_rd An array of length 2 where skip_rd[0] is the * best total RD for a skip mode so far, and * skip_rd[1] is the best RD for a skip mode so * far in luma. This is used as a speed feature * to skip the transform search if the computed * skip RD for the current mode is not better * than the best skip_rd so far. * \param[in,out] skip_build_pred Indicates whether or not to build the inter * predictor. If this is 0, the inter predictor * has already been built and thus we can avoid * repeating computation. * \return Returns 1 if this mode is worse than one already seen and 0 if it is * a viable candidate.
*/ staticint process_compound_inter_mode(
AV1_COMP *const cpi, MACROBLOCK *x, HandleInterModeArgs *args,
int64_t ref_best_rd, int_mv *cur_mv, BLOCK_SIZE bsize, int *compmode_interinter_cost, const CompoundTypeRdBuffers *rd_buffers, const BUFFER_SET *orig_dst, const BUFFER_SET *tmp_dst, int *rate_mv,
RD_STATS *rd_stats, int64_t *skip_rd, int *skip_build_pred) {
MACROBLOCKD *xd = &x->e_mbd;
MB_MODE_INFO *mbmi = xd->mi[0]; const AV1_COMMON *cm = &cpi->common; constint masked_compound_used = is_any_masked_compound_used(bsize) &&
cm->seq_params->enable_masked_compound; int mode_search_mask = (1 << COMPOUND_AVERAGE) | (1 << COMPOUND_DISTWTD) |
(1 << COMPOUND_WEDGE) | (1 << COMPOUND_DIFFWTD);
int64_t best_rd_compound;
int64_t rd_thresh; constint comp_type_rd_shift = COMP_TYPE_RD_THRESH_SHIFT; constint comp_type_rd_scale = COMP_TYPE_RD_THRESH_SCALE;
rd_thresh = get_rd_thresh_from_best_rd(ref_best_rd, (1 << comp_type_rd_shift),
comp_type_rd_scale); // Select compound type and any parameters related to that type // (for example, the mask parameters if it is a masked mode) and compute // the RD
*compmode_interinter_cost = av1_compound_type_rd(
cpi, x, args, bsize, cur_mv, mode_search_mask, masked_compound_used,
orig_dst, tmp_dst, rd_buffers, rate_mv, &best_rd_compound, rd_stats,
ref_best_rd, skip_rd[1], &is_luma_interp_done, rd_thresh); if (ref_best_rd < INT64_MAX &&
(best_rd_compound >> comp_type_rd_shift) * comp_type_rd_scale >
ref_best_rd) {
restore_dst_buf(xd, *orig_dst, num_planes); return 1;
}
// Build only uv predictor for COMPOUND_AVERAGE. // Note there is no need to call av1_enc_build_inter_predictor // for luma if COMPOUND_AVERAGE is selected because it is the first // candidate in av1_compound_type_rd, which means it used the dst_buf // rather than the tmp_buf. if (mbmi->interinter_comp.type == COMPOUND_AVERAGE && is_luma_interp_done) { if (num_planes > 1) {
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, orig_dst, bsize,
AOM_PLANE_U, num_planes - 1);
}
*skip_build_pred = 1;
} return 0;
}
// Speed feature to prune out MVs that are similar to previous MVs if they // don't achieve the best RD advantage. staticint prune_ref_mv_idx_search(int ref_mv_idx, int best_ref_mv_idx,
int_mv save_mv[MAX_REF_MV_SEARCH - 1][2],
MB_MODE_INFO *mbmi, int pruning_factor) { int i; constint is_comp_pred = has_second_ref(mbmi); constint thr = (1 + is_comp_pred) << (pruning_factor + 1);
// Skip the evaluation if an MV match is found. if (ref_mv_idx > 0) { for (int idx = 0; idx < ref_mv_idx; ++idx) { if (save_mv[idx][0].as_int == INVALID_MV) continue;
int mv_diff = 0; for (i = 0; i < 1 + is_comp_pred; ++i) {
mv_diff += abs(save_mv[idx][i].as_mv.row - mbmi->mv[i].as_mv.row) +
abs(save_mv[idx][i].as_mv.col - mbmi->mv[i].as_mv.col);
}
// If this mode is not the best one, and current MV is similar to // previous stored MV, terminate this ref_mv_idx evaluation. if (best_ref_mv_idx == -1 && mv_diff <= thr) return 1;
}
}
if (ref_mv_idx < MAX_REF_MV_SEARCH - 1) { for (i = 0; i < is_comp_pred + 1; ++i)
save_mv[ref_mv_idx][i].as_int = mbmi->mv[i].as_int;
}
return 0;
}
/*!\brief Prunes ZeroMV Search Using Best NEWMV's SSE * * \ingroup inter_mode_search * * Compares the sse of zero mv and the best sse found in single new_mv. If the * sse of the zero_mv is higher, returns 1 to signal zero_mv can be skipped. * Else returns 0. * * Note that the sse of here comes from single_motion_search. So it is * interpolated with the filter in motion search, not the actual interpolation * filter used in encoding. * * \param[in] fn_ptr A table of function pointers to compute SSE. * \param[in] x Pointer to struct holding all the data for * the current macroblock. * \param[in] bsize The current block_size. * \param[in] args The args to handle_inter_mode, used to track * the best SSE. * \param[in] prune_zero_mv_with_sse The argument holds speed feature * prune_zero_mv_with_sse value * \return Returns 1 if zero_mv is pruned, 0 otherwise.
*/ staticinlineint prune_zero_mv_with_sse(const aom_variance_fn_ptr_t *fn_ptr, const MACROBLOCK *x, BLOCK_SIZE bsize, const HandleInterModeArgs *args, int prune_zero_mv_with_sse) { const MACROBLOCKD *xd = &x->e_mbd; const MB_MODE_INFO *mbmi = xd->mi[0];
for (int idx = 0; idx < 1 + is_comp_pred; idx++) { if (xd->global_motion[refs[idx]].wmtype != IDENTITY) { // Pruning logic only works for IDENTITY type models // Note: In theory we could apply similar logic for TRANSLATION // type models, but we do not code these due to a spec bug // (see comments in gm_get_motion_vector() in av1/common/mv.h)
assert(xd->global_motion[refs[idx]].wmtype != TRANSLATION); return 0;
}
// Don't prune if we have invalid data
assert(mbmi->mv[idx].as_int == 0); if (args->best_single_sse_in_refs[refs[idx]] == INT32_MAX) { return 0;
}
}
// Sum up the sse of ZEROMV and best NEWMV unsignedint this_sse_sum = 0; unsignedint best_sse_sum = 0; for (int idx = 0; idx < 1 + is_comp_pred; idx++) { conststruct macroblock_plane *const p = &x->plane[AOM_PLANE_Y]; conststruct macroblockd_plane *pd = xd->plane; conststruct buf_2d *src_buf = &p->src; conststruct buf_2d *ref_buf = &pd->pre[idx]; const uint8_t *src = src_buf->buf; const uint8_t *ref = ref_buf->buf; constint src_stride = src_buf->stride; constint ref_stride = ref_buf->stride;
constdouble mul = prune_zero_mv_with_sse > 1 ? 1.00 : 1.25; if ((double)this_sse_sum > (mul * (double)best_sse_sum)) { return 1;
}
return 0;
}
/*!\brief Searches for interpolation filter in realtime mode during winner eval * * \ingroup inter_mode_search * * Does a simple interpolation filter search during winner mode evaluation. This * is currently only used by realtime mode as \ref * av1_interpolation_filter_search is not called during realtime encoding. * * This function only searches over two possible filters. EIGHTTAP_REGULAR is * always search. For lowres clips (<= 240p), MULTITAP_SHARP is also search. For * higher res slips (>240p), EIGHTTAP_SMOOTH is also searched. * * * \param[in] cpi Pointer to the compressor. Used for feature * flags. * \param[in,out] x Pointer to macroblock. This is primarily * used to access the buffers. * \param[in] mi_row The current row in mi unit (4X4 pixels). * \param[in] mi_col The current col in mi unit (4X4 pixels). * \param[in] bsize The current block_size. * \return Returns true if a predictor is built in xd->dst, false otherwise.
*/ staticinlinebool fast_interp_search(const AV1_COMP *cpi, MACROBLOCK *x, int mi_row, int mi_col,
BLOCK_SIZE bsize) { staticconst InterpFilters filters_ref_set[3] = {
{ EIGHTTAP_REGULAR, EIGHTTAP_REGULAR },
{ EIGHTTAP_SMOOTH, EIGHTTAP_SMOOTH },
{ MULTITAP_SHARP, MULTITAP_SHARP }
};
const AV1_COMMON *const cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mi = xd->mi[0];
int64_t best_cost = INT64_MAX; int best_filter_index = -1; // dst_bufs[0] sores the new predictor, and dist_bifs[1] stores the best constint num_planes = av1_num_planes(cm); constint is_240p_or_lesser = AOMMIN(cm->width, cm->height) <= 240;
assert(is_inter_mode(mi->mode));
assert(mi->motion_mode == SIMPLE_TRANSLATION);
assert(!is_inter_compound_mode(mi->mode));
if (is_best_pred_in_orig) {
swap_dst_buf(xd, dst_bufs, num_planes);
} else { // Note that xd->pd's bufers are kept in sync with dst_bufs[0]. So if // is_best_pred_in_orig is false, that means the current buffer is the // original one.
assert(&orig_dst == dst_bufs[0]);
assert(xd->plane[AOM_PLANE_Y].dst.buf == orig_dst.plane[AOM_PLANE_Y]); constint width = block_size_wide[bsize]; constint height = block_size_high[bsize]; #if CONFIG_AV1_HIGHBITDEPTH constbool is_hbd = is_cur_buf_hbd(xd); if (is_hbd) {
aom_highbd_convolve_copy(CONVERT_TO_SHORTPTR(tmp_dst.plane[AOM_PLANE_Y]),
tmp_dst.stride[AOM_PLANE_Y],
CONVERT_TO_SHORTPTR(orig_dst.plane[AOM_PLANE_Y]),
orig_dst.stride[AOM_PLANE_Y], width, height);
} else {
aom_convolve_copy(tmp_dst.plane[AOM_PLANE_Y], tmp_dst.stride[AOM_PLANE_Y],
orig_dst.plane[AOM_PLANE_Y],
orig_dst.stride[AOM_PLANE_Y], width, height);
} #else
aom_convolve_copy(tmp_dst.plane[AOM_PLANE_Y], tmp_dst.stride[AOM_PLANE_Y],
orig_dst.plane[AOM_PLANE_Y], orig_dst.stride[AOM_PLANE_Y],
width, height); #endif
}
// Build the YUV predictor. if (num_planes > 1) {
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, NULL, bsize,
AOM_PLANE_U, AOM_PLANE_V);
}
returntrue;
}
/*!\brief AV1 inter mode RD computation * * \ingroup inter_mode_search * Do the RD search for a given inter mode and compute all information relevant * to the input mode. It will compute the best MV, * compound parameters (if the mode is a compound mode) and interpolation filter * parameters. * * \param[in] cpi Top-level encoder structure. * \param[in] tile_data Pointer to struct holding adaptive * data/contexts/models for the tile during * encoding. * \param[in] x Pointer to structure holding all the data * for the current macroblock. * \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 * for only the Y plane. * \param[in,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 * search. See the documentation for this * struct for a description of each member. * \param[in] ref_best_rd Best RD found so far for this block. * It is used for early termination of this * search if the RD exceeds this value. * \param[in] tmp_buf Temporary buffer used to hold predictors * built in this search. * \param[in,out] rd_buffers CompoundTypeRdBuffers struct to hold all * allocated buffers for the compound * predictors and masks in the compound type * search. * \param[in,out] best_est_rd Estimated RD for motion mode search if * do_tx_search (see below) is 0. * \param[in] do_tx_search Parameter to indicate whether or not to do * a full transform search. This will compute * an estimated RD for the modes without the * transform search and later perform the full * transform search on the best candidates. * \param[in,out] inter_modes_info InterModesInfo struct to hold inter mode * information to perform a full transform * search only on winning candidates searched * with an estimate for transform coding RD. * \param[in,out] motion_mode_cand A motion_mode_candidate struct to store * motion mode information used in a speed * feature to search motion modes other than * SIMPLE_TRANSLATION only on winning * candidates. * \param[in,out] skip_rd A length 2 array, where skip_rd[0] is the * best total RD for a skip mode so far, and * skip_rd[1] is the best RD for a skip mode so * far in luma. This is used as a speed feature * to skip the transform search if the computed * skip RD for the current mode is not better * than the best skip_rd so far. * \param[in] inter_cost_info_from_tpl A PruneInfoFromTpl struct used to * narrow down the search based on data * collected in the TPL model. * \param[out] yrd Stores the rdcost corresponding to encoding * the luma plane. * * \return The RD cost for the mode being searched.
*/ static int64_t handle_inter_mode(
AV1_COMP *const cpi, TileDataEnc *tile_data, MACROBLOCK *x,
BLOCK_SIZE bsize, RD_STATS *rd_stats, RD_STATS *rd_stats_y,
RD_STATS *rd_stats_uv, HandleInterModeArgs *args, int64_t ref_best_rd,
uint8_t *const tmp_buf, const CompoundTypeRdBuffers *rd_buffers,
int64_t *best_est_rd, constint do_tx_search,
InterModesInfo *inter_modes_info, motion_mode_candidate *motion_mode_cand,
int64_t *skip_rd, PruneInfoFromTpl *inter_cost_info_from_tpl,
int64_t *yrd) { const AV1_COMMON *cm = &cpi->common; constint num_planes = av1_num_planes(cm);
MACROBLOCKD *xd = &x->e_mbd;
MB_MODE_INFO *mbmi = xd->mi[0];
MB_MODE_INFO_EXT *const mbmi_ext = &x->mbmi_ext;
TxfmSearchInfo *txfm_info = &x->txfm_search_info; constint is_comp_pred = has_second_ref(mbmi); const PREDICTION_MODE this_mode = mbmi->mode;
#if CONFIG_REALTIME_ONLY constint prune_modes_based_on_tpl = 0; #else// CONFIG_REALTIME_ONLY const TplParams *const tpl_data = &cpi->ppi->tpl_data; constint prune_modes_based_on_tpl =
cpi->sf.inter_sf.prune_inter_modes_based_on_tpl &&
av1_tpl_stats_ready(tpl_data, cpi->gf_frame_index); #endif// CONFIG_REALTIME_ONLY int i; // Reference frames for this mode constint refs[2] = { mbmi->ref_frame[0],
(mbmi->ref_frame[1] < 0 ? 0 : mbmi->ref_frame[1]) }; int rate_mv = 0;
int64_t rd = INT64_MAX; // Do first prediction into the destination buffer. Do the next // prediction into a temporary buffer. Then keep track of which one // of these currently holds the best predictor, and use the other // one for future predictions. In the end, copy from tmp_buf to // dst if necessary. struct macroblockd_plane *pd = xd->plane; const BUFFER_SET orig_dst = {
{ pd[0].dst.buf, pd[1].dst.buf, pd[2].dst.buf },
{ pd[0].dst.stride, pd[1].dst.stride, pd[2].dst.stride },
}; const BUFFER_SET tmp_dst = { { tmp_buf, tmp_buf + 1 * MAX_SB_SQUARE,
tmp_buf + 2 * MAX_SB_SQUARE },
{ MAX_SB_SIZE, MAX_SB_SIZE, MAX_SB_SIZE } };
// Do not prune the mode based on inter cost from tpl if the current ref frame // is the winner ref in neighbouring blocks. int ref_match_found_in_above_nb = 0; int ref_match_found_in_left_nb = 0; if (prune_modes_based_on_tpl) {
ref_match_found_in_above_nb =
find_ref_match_in_above_nbs(cm->mi_params.mi_cols, xd);
ref_match_found_in_left_nb =
find_ref_match_in_left_nbs(cm->mi_params.mi_rows, xd);
}
// First, perform a simple translation search for each of the indices. If // an index performs well, it will be fully searched in the main loop // of this function. constint ref_set = get_drl_refmv_count(x, mbmi->ref_frame, this_mode); // Save MV results from first 2 ref_mv_idx.
int_mv save_mv[MAX_REF_MV_SEARCH - 1][2]; int best_ref_mv_idx = -1; constint idx_mask =
ref_mv_idx_to_search(cpi, x, rd_stats, 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; constint ref_mv_cost = cost_mv_ref(mode_costs, this_mode, mode_ctx); constint base_rate =
args->ref_frame_cost + args->single_comp_cost + ref_mv_cost;
for (i = 0; i < MAX_REF_MV_SEARCH - 1; ++i) {
save_mv[i][0].as_int = INVALID_MV;
save_mv[i][1].as_int = INVALID_MV;
}
args->start_mv_cnt = 0;
// Main loop of this function. This will iterate over all of the ref mvs // in the dynamic reference list and do the following: // 1.) Get the current MV. Create newmv MV if necessary // 2.) Search compound type and parameters if applicable // 3.) Do interpolation filter search // 4.) Build the inter predictor // 5.) Pick the motion mode (SIMPLE_TRANSLATION, OBMC_CAUSAL, // WARPED_CAUSAL) // 6.) Update stats if best so far for (int ref_mv_idx = 0; ref_mv_idx < ref_set; ++ref_mv_idx) {
mbmi->ref_mv_idx = ref_mv_idx;
if (!mask_check_bit(idx_mask, ref_mv_idx)) { // MV did not perform well in simple translation search. Skip it. continue;
} if (prune_modes_based_on_tpl && !ref_match_found_in_above_nb &&
!ref_match_found_in_left_nb && (ref_best_rd != INT64_MAX)) { // Skip mode if TPL model indicates it will not be beneficial. if (prune_modes_based_on_tpl_stats(
inter_cost_info_from_tpl, refs, ref_mv_idx, this_mode,
cpi->sf.inter_sf.prune_inter_modes_based_on_tpl)) continue;
}
av1_init_rd_stats(rd_stats);
// Compute cost for signalling this DRL index
rd_stats->rate = base_rate;
rd_stats->rate += drl_cost;
int rs = 0; int compmode_interinter_cost = 0;
int_mv cur_mv[2];
// TODO(Cherma): Extend this speed feature to support compound mode int skip_repeated_ref_mv =
is_comp_pred ? 0 : cpi->sf.inter_sf.skip_repeated_ref_mv; // Generate the current mv according to the prediction mode if (!build_cur_mv(cur_mv, this_mode, cm, x, skip_repeated_ref_mv)) { continue;
}
// The above call to build_cur_mv does not handle NEWMV modes. Build // the mv here if we have NEWMV for any predictors. if (have_newmv_in_inter_mode(this_mode)) { #if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, handle_newmv_time); #endif
newmv_ret_val =
handle_newmv(cpi, x, bsize, cur_mv, &rate_mv, args, mode_info); #if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, handle_newmv_time); #endif
rd_stats->rate += rate_mv;
} // Copy the motion vector for this mode into mbmi struct for (i = 0; i < is_comp_pred + 1; ++i) {
mbmi->mv[i].as_int = cur_mv[i].as_int;
}
// Skip the rest of the search if prune_ref_mv_idx_search speed feature // is enabled, and the current MV is similar to a previous one. if (cpi->sf.inter_sf.prune_ref_mv_idx_search && is_comp_pred &&
prune_ref_mv_idx_search(ref_mv_idx, best_ref_mv_idx, save_mv, mbmi,
cpi->sf.inter_sf.prune_ref_mv_idx_search)) continue;
rd_stats->rate += compmode_interinter_cost; if (skip_build_pred != 1) { // Build this inter predictor if it has not been previously built
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, &orig_dst, bsize, 0,
av1_num_planes(cm) - 1);
}
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, motion_mode_rd_time); #endif int rate2_nocoeff = rd_stats->rate; // Determine the motion mode. This will be one of SIMPLE_TRANSLATION, // OBMC_CAUSAL or WARPED_CAUSAL
int64_t this_yrd;
ret_val = motion_mode_rd(cpi, tile_data, x, bsize, rd_stats, rd_stats_y,
rd_stats_uv, args, ref_best_rd, skip_rd, &rate_mv,
&orig_dst, best_est_rd, do_tx_search,
inter_modes_info, 0, &this_yrd); #if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, motion_mode_rd_time); #endif
assert(
IMPLIES(!av1_check_newmv_joint_nonzero(cm, x), ret_val == INT64_MAX));
if (ret_val != INT64_MAX) {
int64_t tmp_rd = RDCOST(x->rdmult, rd_stats->rate, rd_stats->dist); const THR_MODES mode_enum = get_prediction_mode_idx(
mbmi->mode, mbmi->ref_frame[0], mbmi->ref_frame[1]); // Collect mode stats for multiwinner mode processing
store_winner_mode_stats(&cpi->common, x, mbmi, rd_stats, rd_stats_y,
rd_stats_uv, mode_enum, NULL, bsize, tmp_rd,
cpi->sf.winner_mode_sf.multi_winner_mode_type,
do_tx_search); if (tmp_rd < best_rd) {
best_yrd = this_yrd; // Update the best rd stats if we found the best mode so far
best_rd_stats = *rd_stats;
best_rd_stats_y = *rd_stats_y;
best_rd_stats_uv = *rd_stats_uv;
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;
motion_mode_cand->rate2_nocoeff = rate2_nocoeff;
}
/*!\brief Search for the best intrabc predictor * * \ingroup intra_mode_search * \callergraph * This function performs a motion search to find the best intrabc predictor. * * \returns Returns the best overall rdcost (including the non-intrabc modes * search before this function).
*/ static int64_t rd_pick_intrabc_mode_sb(const AV1_COMP *cpi, MACROBLOCK *x,
PICK_MODE_CONTEXT *ctx,
RD_STATS *rd_stats, BLOCK_SIZE bsize,
int64_t best_rd) { const AV1_COMMON *const cm = &cpi->common; if (!av1_allow_intrabc(cm) || !cpi->oxcf.kf_cfg.enable_intrabc ||
!cpi->sf.mv_sf.use_intrabc || cpi->sf.rt_sf.use_nonrd_pick_mode) return INT64_MAX; constint num_planes = av1_num_planes(cm);
if (bestsme == INT_MAX) continue; const MV dv = get_mv_from_fullmv(&best_mv.as_fullmv); if (!av1_is_fullmv_in_range(&fullms_params.mv_limits,
get_fullmv_from_mv(&dv))) continue; if (!av1_is_dv_valid(dv, cm, xd, mi_row, mi_col, bsize,
cm->seq_params->mib_size_log2)) continue;
// TODO(chiyotsai@google.com): We are using struct $struct_name instead of their // typedef here because Doxygen doesn't know about the typedefs yet. So using // the typedef will prevent doxygen from finding this function and generating // the callgraph. Once documents for AV1_COMP and MACROBLOCK are added to // doxygen, we can revert back to using the typedefs. void av1_rd_pick_intra_mode_sb(conststruct AV1_COMP *cpi, struct macroblock *x, struct RD_STATS *rd_cost, BLOCK_SIZE bsize,
PICK_MODE_CONTEXT *ctx, int64_t best_rd) { const AV1_COMMON *const cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0]; constint 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;
if (intra_yrd < best_rd) { // Search intra modes for uv planes if needed if (num_planes > 1) { // 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);
av1_rd_pick_intra_sbuv_mode(cpi, x, &rate_uv, &rate_uv_tokenonly,
&dist_uv, &uv_skip_txfm, bsize,
max_uv_tx_size);
}
// speed feature: fast intra/inter transform type search // Used for speed >= 2 // When this speed feature is on, in rd mode search, only DCT is used. // After the mode is determined, this function is called, to select // transform types and get accurate rdcost. staticinlinevoid refine_winner_mode_tx( const AV1_COMP *cpi, MACROBLOCK *x, RD_STATS *rd_cost, BLOCK_SIZE bsize,
PICK_MODE_CONTEXT *ctx, THR_MODES *best_mode_index,
MB_MODE_INFO *best_mbmode, struct buf_2d yv12_mb[REF_FRAMES][MAX_MB_PLANE], int best_rate_y, int best_rate_uv, int *best_skip2, int winner_mode_count) { const AV1_COMMON *const cm = &cpi->common;
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; constint num_planes = av1_num_planes(cm);
if (!is_winner_mode_processing_enabled(cpi, x, best_mbmode,
rd_cost->skip_txfm)) return;
// Set params for winner mode evaluation
set_mode_eval_params(cpi, x, WINNER_MODE_EVAL);
// No best mode identified so far if (*best_mode_index == THR_INVALID) return;
/*!\cond */ typedefstruct { // Mask for each reference frame, specifying which prediction modes to NOT try // during search.
uint32_t pred_modes[REF_FRAMES]; // If ref_combo[i][j + 1] is true, do NOT try prediction using combination of // reference frames (i, j). // Note: indexing with 'j + 1' is due to the fact that 2nd reference can be -1 // (NONE_FRAME). bool ref_combo[REF_FRAMES][REF_FRAMES + 1];
} mode_skip_mask_t; /*!\endcond */
// Update 'ref_combo' mask to disable given 'ref' in single and compound modes. staticinlinevoid disable_reference(
MV_REFERENCE_FRAME ref, bool ref_combo[REF_FRAMES][REF_FRAMES + 1]) { for (MV_REFERENCE_FRAME ref2 = NONE_FRAME; ref2 < REF_FRAMES; ++ref2) {
ref_combo[ref][ref2 + 1] = true;
}
}
if (sf->rt_sf.use_real_time_ref_set)
ref_set = REF_SET_REALTIME; elseif (cpi->oxcf.ref_frm_cfg.enable_reduced_reference_set)
ref_set = REF_SET_REDUCED;
default_skip_mask(mask, ref_set);
int min_pred_mv_sad = INT_MAX;
MV_REFERENCE_FRAME ref_frame; if (ref_set == REF_SET_REALTIME) { // For real-time encoding, we only look at a subset of ref frames. So the // threshold for pruning should be computed from this subset as well. constint num_rt_refs = sizeof(real_time_ref_combos) / sizeof(*real_time_ref_combos); for (int r_idx = 0; r_idx < num_rt_refs; r_idx++) { const MV_REFERENCE_FRAME ref = real_time_ref_combos[r_idx][0]; if (ref != INTRA_FRAME) {
min_pred_mv_sad = AOMMIN(min_pred_mv_sad, x->pred_mv_sad[ref]);
}
}
} else { for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame)
min_pred_mv_sad = AOMMIN(min_pred_mv_sad, x->pred_mv_sad[ref_frame]);
}
for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) { if (!(cpi->ref_frame_flags & av1_ref_frame_flag_list[ref_frame])) { // Skip checking missing reference in both single and compound reference // modes.
disable_reference(ref_frame, mask->ref_combo);
} else { // Skip fixed mv modes for poor references if ((x->pred_mv_sad[ref_frame] >> 2) > min_pred_mv_sad) {
mask->pred_modes[ref_frame] |= INTER_NEAREST_NEAR_ZERO;
}
} if (segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME) &&
get_segdata(seg, segment_id, SEG_LVL_REF_FRAME) != (int)ref_frame) { // Reference not used for the segment.
disable_reference(ref_frame, mask->ref_combo);
}
} // Note: We use the following drop-out only if the SEG_LVL_REF_FRAME feature // is disabled for this segment. This is to prevent the possibility that we // end up unable to pick any mode. if (!segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME)) { // Only consider GLOBALMV/ALTREF_FRAME for alt ref frame, // unless ARNR filtering is enabled in which case we want // an unfiltered alternative. We allow near/nearest as well // because they may result in zero-zero MVs but be cheaper. if (cpi->rc.is_src_frame_alt_ref &&
(cpi->oxcf.algo_cfg.arnr_max_frames == 0)) {
disable_inter_references_except_altref(mask->ref_combo);
if (near_mv.as_int != global_mv.as_int)
mask->pred_modes[ALTREF_FRAME] |= (1 << NEARMV); if (nearest_mv.as_int != global_mv.as_int)
mask->pred_modes[ALTREF_FRAME] |= (1 << NEARESTMV);
}
}
if (cpi->rc.is_src_frame_alt_ref) { if (inter_sf->alt_ref_search_fp &&
(cpi->ref_frame_flags & av1_ref_frame_flag_list[ALTREF_FRAME])) {
mask->pred_modes[ALTREF_FRAME] = 0;
disable_inter_references_except_altref(mask->ref_combo);
disable_reference(INTRA_FRAME, mask->ref_combo);
}
}
if (inter_sf->alt_ref_search_fp) { if (!cm->show_frame && x->best_pred_mv_sad[0] < INT_MAX) { int sad_thresh = x->best_pred_mv_sad[0] + (x->best_pred_mv_sad[0] >> 3); // Conservatively skip the modes w.r.t. BWDREF, ALTREF2 and ALTREF, if // those are past frames
MV_REFERENCE_FRAME start_frame =
inter_sf->alt_ref_search_fp == 1 ? ALTREF2_FRAME : BWDREF_FRAME; for (ref_frame = start_frame; ref_frame <= ALTREF_FRAME; ref_frame++) { if (cpi->ref_frame_dist_info.ref_relative_dist[ref_frame - LAST_FRAME] <
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]))) { continue;
} if (x->pred_mv_sad[ref_frame] > sad_thresh)
mask->pred_modes[ref_frame] |= INTER_ALL;
}
}
}
}
if (sf->rt_sf.prune_inter_modes_wrt_gf_arf_based_on_sad) { if (x->best_pred_mv_sad[0] < INT_MAX) { int sad_thresh = x->best_pred_mv_sad[0] + (x->best_pred_mv_sad[0] >> 1); constint prune_ref_list[2] = { GOLDEN_FRAME, ALTREF_FRAME };
// Conservatively skip the modes w.r.t. GOLDEN and ALTREF references for (int ref_idx = 0; ref_idx < 2; ref_idx++) {
ref_frame = prune_ref_list[ref_idx]; if (x->pred_mv_sad[ref_frame] > sad_thresh)
mask->pred_modes[ref_frame] |= INTER_NEAREST_NEAR_ZERO;
}
}
}
if (bsize > sf->part_sf.max_intra_bsize) {
disable_reference(INTRA_FRAME, mask->ref_combo);
}
// 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); constdouble prune_threshes[2] = { 1.20, 1.05 };
// if we are here, then the current mode is compound.
MV_REFERENCE_FRAME cached_ref_type = av1_ref_frame_type(mi_cache->ref_frame); return ref_frame == cached_ref_type;
}
// Please add/modify parameter setting in this function, making it consistent // and easy to read and maintain. staticinlinevoid set_params_rd_pick_inter_mode( const AV1_COMP *cpi, MACROBLOCK *x, HandleInterModeArgs *args,
BLOCK_SIZE bsize, mode_skip_mask_t *mode_skip_mask, int skip_ref_frame_mask, unsignedint *ref_costs_single, unsignedint (*ref_costs_comp)[REF_FRAMES], struct buf_2d (*yv12_mb)[MAX_MB_PLANE]) { const AV1_COMMON *const cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];
MB_MODE_INFO_EXT *const mbmi_ext = &x->mbmi_ext; unsignedchar segment_id = mbmi->segment_id;
for (MV_REFERENCE_FRAME ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME;
++ref_frame) {
x->pred_mv_sad[ref_frame] = INT_MAX;
mbmi_ext->mode_context[ref_frame] = 0;
mbmi_ext->ref_mv_count[ref_frame] = UINT8_MAX; if (cpi->ref_frame_flags & av1_ref_frame_flag_list[ref_frame]) { // Skip the ref frame if the mask says skip and the ref is not used by // compound ref. if (skip_ref_frame_mask & (1 << ref_frame) &&
!is_ref_frame_used_by_compound_ref(ref_frame, skip_ref_frame_mask) &&
!is_ref_frame_used_in_cache(ref_frame, x->mb_mode_cache)) { continue;
}
assert(get_ref_frame_yv12_buf(cm, ref_frame) != NULL);
setup_buffer_ref_mvs_inter(cpi, x, ref_frame, bsize, yv12_mb);
} if (cpi->sf.inter_sf.alt_ref_search_fp ||
cpi->sf.inter_sf.prune_single_ref ||
cpi->sf.rt_sf.prune_inter_modes_wrt_gf_arf_based_on_sad) { // Store the best pred_mv_sad across all past frames if (cpi->ref_frame_dist_info.ref_relative_dist[ref_frame - LAST_FRAME] <
0)
x->best_pred_mv_sad[0] =
AOMMIN(x->best_pred_mv_sad[0], x->pred_mv_sad[ref_frame]); else // Store the best pred_mv_sad across all future frames
x->best_pred_mv_sad[1] =
AOMMIN(x->best_pred_mv_sad[1], x->pred_mv_sad[ref_frame]);
}
}
if (!cpi->sf.rt_sf.use_real_time_ref_set && is_comp_ref_allowed(bsize)) { // No second reference on RT ref set, so no need to initialize for (MV_REFERENCE_FRAME ref_frame = EXTREF_FRAME;
ref_frame < MODE_CTX_REF_FRAMES; ++ref_frame) {
mbmi_ext->mode_context[ref_frame] = 0;
mbmi_ext->ref_mv_count[ref_frame] = UINT8_MAX; const MV_REFERENCE_FRAME *rf = ref_frame_map[ref_frame - REF_FRAMES]; if (!((cpi->ref_frame_flags & av1_ref_frame_flag_list[rf[0]]) &&
(cpi->ref_frame_flags & av1_ref_frame_flag_list[rf[1]]))) { continue;
}
if (skip_ref_frame_mask & (1 << ref_frame) &&
!is_ref_frame_used_in_cache(ref_frame, x->mb_mode_cache)) { continue;
} // Ref mv list population is not required, when compound references are // pruned. if (prune_ref_frame(cpi, x, ref_frame)) continue;
conststruct segmentation *const seg = &cm->seg; constunsignedchar segment_id = x->e_mbd.mi[0]->segment_id; // Do not allow compound prediction if the segment level reference frame // feature is in use as in this case there can only be one reference. if (segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME)) return 1;
}
if (ref_frames[0] > INTRA_FRAME && ref_frames[1] == INTRA_FRAME) { // Mode must be compatible if (!is_interintra_allowed_bsize(bsize)) return 1; if (!is_interintra_allowed_mode(curr_mode)) return 1;
}
// Check if reference frame pair of the current block matches with the given // block. staticinlineint match_ref_frame_pair(const MB_MODE_INFO *mbmi, const MV_REFERENCE_FRAME *ref_frames) { return ((ref_frames[0] == mbmi->ref_frame[0]) &&
(ref_frames[1] == mbmi->ref_frame[1]));
}
// Case 1: return 0, means don't skip this mode // Case 2: return 1, means skip this mode completely // Case 3: return 2, means skip compound only, but still try single motion modes staticint inter_mode_search_order_independent_skip( const AV1_COMP *cpi, const MACROBLOCK *x, mode_skip_mask_t *mode_skip_mask,
InterModeSearchState *search_state, int skip_ref_frame_mask,
PREDICTION_MODE mode, const MV_REFERENCE_FRAME *ref_frame) { if (mask_says_skip(mode_skip_mask, ref_frame, mode)) { return 1;
}
constint ref_type = av1_ref_frame_type(ref_frame); if (!cpi->sf.rt_sf.use_real_time_ref_set) if (prune_ref_frame(cpi, x, ref_type)) return 1;
// This is only used in motion vector unit test. if (cpi->oxcf.unit_test_cfg.motion_vector_unit_test &&
ref_frame[0] == INTRA_FRAME) return 1;
const AV1_COMMON *const cm = &cpi->common; if (skip_repeated_mv(cm, x, mode, ref_frame, search_state)) { return 1;
}
// If the cached mode is intra, then we just need to match the mode. if (is_mode_intra(cached_mode) && mode != cached_mode) { return 1;
}
// If the cached mode is single inter mode, then we match the mode and // reference frame. if (cached_mode_is_single) { if (mode != cached_mode || ref_frame[0] != cached_frame[0]) { return 1;
}
} else { // If the cached mode is compound, then we need to consider several cases. constint mode_is_single = ref_frame[1] <= INTRA_FRAME; if (mode_is_single) { // If the mode is single, we know the modes can't match. But we might // still want to search it if compound mode depends on the current mode. int skip_motion_mode_only = 0; if (cached_mode == NEW_NEARMV || cached_mode == NEW_NEARESTMV) {
skip_motion_mode_only = (ref_frame[0] == cached_frame[0]);
} elseif (cached_mode == NEAR_NEWMV || cached_mode == NEAREST_NEWMV) {
skip_motion_mode_only = (ref_frame[0] == cached_frame[1]);
} elseif (cached_mode == NEW_NEWMV) {
skip_motion_mode_only = (ref_frame[0] == cached_frame[0] ||
ref_frame[0] == cached_frame[1]);
}
return 1 + skip_motion_mode_only;
} else { // If both modes are compound, then everything must match. if (mode != cached_mode || ref_frame[0] != cached_frame[0] ||
ref_frame[1] != cached_frame[1]) { return 1;
}
}
}
}
const MB_MODE_INFO *const mbmi = x->e_mbd.mi[0]; // If no valid mode has been found so far in PARTITION_NONE when finding a // valid partition is required, do not skip mode. if (search_state->best_rd == INT64_MAX && mbmi->partition == PARTITION_NONE &&
x->must_find_valid_partition) return 0;
const SPEED_FEATURES *const sf = &cpi->sf; // Prune NEARMV and NEAR_NEARMV based on q index and neighbor's reference // frames if (sf->inter_sf.prune_nearmv_using_neighbors &&
(mode == NEAR_NEARMV || mode == NEARMV)) { const MACROBLOCKD *const xd = &x->e_mbd; if (search_state->best_rd != INT64_MAX && xd->left_available &&
xd->up_available) { constint thresholds[PRUNE_NEARMV_MAX][3] = { { 1, 0, 0 },
{ 1, 1, 0 },
{ 2, 1, 0 } }; constint qindex_sub_range = x->qindex * 3 / QINDEX_RANGE;
// Pruning based on ref frame pair match with neighbors. if (num_ref_frame_pair_match < num_ref_frame_pair_match_thresh) return 1;
}
}
int skip_motion_mode = 0; if (mbmi->partition != PARTITION_NONE) { int skip_ref = skip_ref_frame_mask & (1 << ref_type); if (ref_type <= ALTREF_FRAME && skip_ref) { // Since the compound ref modes depends on the motion estimation result of // two single ref modes (best mv of single ref modes as the start point), // if current single ref mode is marked skip, we need to check if it will // be used in compound ref modes. if (is_ref_frame_used_by_compound_ref(ref_type, skip_ref_frame_mask)) { // Found a not skipped compound ref mode which contains current // single ref. So this single ref can't be skipped completely // Just skip its motion mode search, still try its simple // transition mode.
skip_motion_mode = 1;
skip_ref = 0;
}
} // If we are reusing the prediction from cache, and the current frame is // required by the cache, then we cannot prune it. if (is_ref_frame_used_in_cache(ref_type, x->mb_mode_cache)) {
skip_ref = 0; // If the cache only needs the current reference type for compound // prediction, then we can skip motion mode search.
skip_motion_mode = (ref_type <= ALTREF_FRAME &&
x->mb_mode_cache->ref_frame[1] > INTRA_FRAME);
} if (skip_ref) return 1;
}
if (ref_frame[0] == INTRA_FRAME) { if (mode != DC_PRED) { // Disable intra modes other than DC_PRED for blocks with low variance // Threshold for intra skipping based on source variance // TODO(debargha): Specialize the threshold for super block sizes constunsignedint skip_intra_var_thresh = 64; if ((sf->rt_sf.mode_search_skip_flags & FLAG_SKIP_INTRA_LOWVAR) &&
x->source_variance < skip_intra_var_thresh) return 1;
}
}
// Use the best rd of GLOBALMV or NEWMV to prune the unlikely // reference frames for all the modes (NEARESTMV and NEARMV may not // have same motion vectors). Always keep the best of each mode // because it might form the best possible combination with other mode.
state = search_state->single_state[dir];
best_rd = AOMMIN(state[INTER_OFFSET(NEWMV)][0].rd,
state[INTER_OFFSET(GLOBALMV)][0].rd); for (mode = 0; mode < SINGLE_INTER_MODE_NUM; ++mode) { for (i = 1; i < search_state->single_state_cnt[dir][mode]; ++i) { if (state[mode][i].rd != INT64_MAX &&
(state[mode][i].rd >> 3) * prune_factor > best_rd) {
state[mode][i].valid = 0;
}
}
}
state = search_state->single_state_modelled[dir];
best_rd = AOMMIN(state[INTER_OFFSET(NEWMV)][0].rd,
state[INTER_OFFSET(GLOBALMV)][0].rd); for (mode = 0; mode < SINGLE_INTER_MODE_NUM; ++mode) { for (i = 1; i < search_state->single_state_modelled_cnt[dir][mode]; ++i) { if (state[mode][i].rd != INT64_MAX &&
(state[mode][i].rd >> 3) * prune_factor > best_rd) {
state[mode][i].valid = 0;
}
}
}
}
// Ordering by simple rd first, then by modelled rd for (dir = 0; dir < 2; ++dir) { for (mode = 0; mode < SINGLE_INTER_MODE_NUM; ++mode) { constint state_cnt_s = search_state->single_state_cnt[dir][mode]; constint state_cnt_m =
search_state->single_state_modelled_cnt[dir][mode];
SingleInterModeState *state_s = search_state->single_state[dir][mode];
SingleInterModeState *state_m =
search_state->single_state_modelled[dir][mode]; int count = 0; constint max_candidates = AOMMAX(state_cnt_s, state_cnt_m); for (i = 0; i < state_cnt_s; ++i) { if (state_s[i].rd == INT64_MAX) break; if (state_s[i].valid) {
search_state->single_rd_order[dir][mode][count++] =
state_s[i].ref_frame;
}
} if (count >= max_candidates) continue;
for (i = 0; i < state_cnt_m && count < max_candidates; ++i) { if (state_m[i].rd == INT64_MAX) break; if (!state_m[i].valid) continue; constint ref_frame = state_m[i].ref_frame; int match = 0; // Check if existing already for (j = 0; j < count; ++j) { if (search_state->single_rd_order[dir][mode][j] == ref_frame) {
match = 1; break;
}
} if (match) continue; // Check if this ref_frame is removed in simple rd int valid = 1; for (j = 0; j < state_cnt_s; ++j) { if (ref_frame == state_s[j].ref_frame) {
valid = state_s[j].valid; break;
}
} if (valid) {
search_state->single_rd_order[dir][mode][count++] = ref_frame;
}
}
}
}
}
int max_candidates = 0; for (int i = 0; i < FWD_REFS; ++i) { if (search_state->single_rd_order[dir][mode_offset][i] == NONE_FRAME) break;
max_candidates++;
}
int candidates = max_candidates; if (cpi->sf.inter_sf.prune_comp_search_by_single_result >= 2) {
candidates = AOMMIN(2, max_candidates);
} if (cpi->sf.inter_sf.prune_comp_search_by_single_result >= 3) { if (state[0].rd != INT64_MAX && state_modelled[0].rd != INT64_MAX &&
state[0].ref_frame == state_modelled[0].ref_frame)
candidates = 1; if (mode == NEARMV || mode == GLOBALMV) candidates = 1;
}
if (cpi->sf.inter_sf.prune_comp_search_by_single_result >= 4) { // Limit the number of candidates to 1 in each direction for compound // prediction
candidates = AOMMIN(1, candidates);
} return candidates;
}
for (i = 0; i < 2; ++i) { if (!ref_searched[i] || !ref_mv_match[i]) continue; constint candidates =
compound_skip_get_candidates(cpi, search_state, mode_dir[i], mode[i]); const MV_REFERENCE_FRAME *ref_order =
search_state->single_rd_order[mode_dir[i]][mode_offset[i]]; int match = 0; for (j = 0; j < candidates; ++j) { if (refs[i] == ref_order[j]) {
match = 1; break;
}
} if (!match) return 1;
}
return 0;
}
// Check if ref frames of current block matches with given block. staticinlinevoid match_ref_frame(const MB_MODE_INFO *const mbmi, const MV_REFERENCE_FRAME *ref_frames, int *const is_ref_match) { if (is_inter_block(mbmi)) {
is_ref_match[0] |= ref_frames[0] == mbmi->ref_frame[0];
is_ref_match[1] |= ref_frames[1] == mbmi->ref_frame[0]; if (has_second_ref(mbmi)) {
is_ref_match[0] |= ref_frames[0] == mbmi->ref_frame[1];
is_ref_match[1] |= ref_frames[1] == mbmi->ref_frame[1];
}
}
}
// Prune compound mode using ref frames of neighbor blocks. staticinlineint compound_skip_using_neighbor_refs(
MACROBLOCKD *const xd, const PREDICTION_MODE this_mode, const MV_REFERENCE_FRAME *ref_frames, int prune_ext_comp_using_neighbors) { // Exclude non-extended compound modes from pruning if (this_mode == NEAREST_NEARESTMV || this_mode == NEAR_NEARMV ||
this_mode == NEW_NEWMV || this_mode == GLOBAL_GLOBALMV) return 0;
if (prune_ext_comp_using_neighbors >= 3) return 1;
int is_ref_match[2] = { 0 }; // 0 - match for forward refs // 1 - match for backward refs // Check if ref frames of this block matches with left neighbor. if (xd->left_available)
match_ref_frame(xd->left_mbmi, ref_frames, is_ref_match);
// Check if ref frames of this block matches with above neighbor. if (xd->up_available)
match_ref_frame(xd->above_mbmi, ref_frames, is_ref_match);
// Combine ref frame match with neighbors in forward and backward refs. constint track_ref_match = is_ref_match[0] + is_ref_match[1];
// Pruning based on ref frame match with neighbors. if (track_ref_match >= prune_ext_comp_using_neighbors) return 0; return 1;
}
// Update best single mode for the given reference frame based on simple rd. staticinlinevoid update_best_single_mode(InterModeSearchState *search_state, const PREDICTION_MODE this_mode, const MV_REFERENCE_FRAME ref_frame,
int64_t this_rd) { if (this_rd < search_state->best_single_rd[ref_frame]) {
search_state->best_single_rd[ref_frame] = this_rd;
search_state->best_single_mode[ref_frame] = this_mode;
}
}
// Prune compound mode using best single mode for the same reference. staticinlineint skip_compound_using_best_single_mode_ref( const PREDICTION_MODE this_mode, const MV_REFERENCE_FRAME *ref_frames, const PREDICTION_MODE *best_single_mode, int prune_comp_using_best_single_mode_ref) { // Exclude non-extended compound modes from pruning if (this_mode == NEAREST_NEARESTMV || this_mode == NEAR_NEARMV ||
this_mode == NEW_NEWMV || this_mode == GLOBAL_GLOBALMV) return 0;
assert(this_mode >= NEAREST_NEWMV && this_mode <= NEW_NEARMV); const PREDICTION_MODE comp_mode_ref0 = compound_ref0_mode(this_mode); // Get ref frame direction corresponding to NEWMV // 0 - NEWMV corresponding to forward direction // 1 - NEWMV corresponding to backward direction constint newmv_dir = comp_mode_ref0 != NEWMV;
// Avoid pruning the compound mode when ref frame corresponding to NEWMV // have NEWMV as single mode winner. // Example: For an extended-compound mode, // {mode, {fwd_frame, bwd_frame}} = {NEAR_NEWMV, {LAST_FRAME, ALTREF_FRAME}} // - Ref frame corresponding to NEWMV is ALTREF_FRAME // - Avoid pruning this mode, if best single mode corresponding to ref frame // ALTREF_FRAME is NEWMV const PREDICTION_MODE single_mode = best_single_mode[ref_frames[newmv_dir]]; if (single_mode == NEWMV) return 0;
// Avoid pruning the compound mode when best single mode is not available if (prune_comp_using_best_single_mode_ref == 1) if (single_mode == MB_MODE_COUNT) return 0; return 1;
}
search_state->best_rd = new_best_rd_stats->rdcost;
search_state->best_mode_index = new_best_mode;
*best_rd_stats_dst = *new_best_rd_stats;
search_state->best_mbmode = *mbmi;
search_state->best_skip2 = skip_txfm;
search_state->best_mode_skippable = new_best_rd_stats->skip_txfm; // When !txfm_search_done, new_best_rd_stats won't provide correct rate_y and // rate_uv because av1_txfm_search process is replaced by rd estimation. // Therefore, we should avoid updating best_rate_y and best_rate_uv here. // These two values will be updated when av1_txfm_search is called. if (txfm_search_done) {
search_state->best_rate_y =
new_best_rd_stats_y->rate +
x->mode_costs.skip_txfm_cost[skip_ctx]
[new_best_rd_stats->skip_txfm || skip_txfm];
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);
}
// Find the best RD for a reference frame (among single reference modes) // and store +10% of it in the 0-th element in ref_frame_rd. staticinlinevoid find_top_ref(int64_t ref_frame_rd[REF_FRAMES]) {
assert(ref_frame_rd[0] == INT64_MAX);
int64_t ref_copy[REF_FRAMES - 1];
memcpy(ref_copy, ref_frame_rd + 1, sizeof(ref_frame_rd[0]) * (REF_FRAMES - 1));
qsort(ref_copy, REF_FRAMES - 1, sizeof(int64_t), compare_int64);
int64_t cutoff = ref_copy[0]; // The cut-off is within 10% of the best. if (cutoff != INT64_MAX) {
assert(cutoff < INT64_MAX / 200);
cutoff = (110 * cutoff) / 100;
}
ref_frame_rd[0] = cutoff;
}
// Check if either frame is within the cutoff. staticinlinebool in_single_ref_cutoff(int64_t ref_frame_rd[REF_FRAMES],
MV_REFERENCE_FRAME frame1,
MV_REFERENCE_FRAME frame2) {
assert(frame2 > 0); return ref_frame_rd[frame1] <= ref_frame_rd[0] ||
ref_frame_rd[frame2] <= ref_frame_rd[0];
}
/*!\cond */ // Arguments for speed feature pruning of inter mode search typedefstruct { int *skip_motion_mode;
mode_skip_mask_t *mode_skip_mask;
InterModeSearchState *search_state; int skip_ref_frame_mask; int reach_first_comp_mode; int mode_thresh_mul_fact; int num_single_modes_processed; int prune_cpd_using_sr_stats_ready;
} InterModeSFArgs; /*!\endcond */
staticint skip_inter_mode(AV1_COMP *cpi, MACROBLOCK *x, const BLOCK_SIZE bsize,
int64_t *ref_frame_rd, int midx,
InterModeSFArgs *args, int is_low_temp_var) { const SPEED_FEATURES *const sf = &cpi->sf;
MACROBLOCKD *const xd = &x->e_mbd; // Get the actual prediction mode we are trying in this iteration const THR_MODES mode_enum = av1_default_mode_order[midx]; const MODE_DEFINITION *mode_def = &av1_mode_defs[mode_enum]; const PREDICTION_MODE this_mode = mode_def->mode; const MV_REFERENCE_FRAME *ref_frames = mode_def->ref_frame; const MV_REFERENCE_FRAME ref_frame = ref_frames[0]; const MV_REFERENCE_FRAME second_ref_frame = ref_frames[1]; constint comp_pred = second_ref_frame > INTRA_FRAME;
// This is for real time encoding. if (is_low_temp_var && !comp_pred && ref_frame != LAST_FRAME &&
this_mode != NEARESTMV) return 1;
// Check if this mode should be skipped because it is incompatible with the // current frame if (inter_mode_compatible_skip(cpi, x, bsize, this_mode, ref_frames)) return 1; constint ret = inter_mode_search_order_independent_skip(
cpi, x, args->mode_skip_mask, args->search_state,
args->skip_ref_frame_mask, this_mode, mode_def->ref_frame); if (ret == 1) return 1;
*(args->skip_motion_mode) = (ret == 2);
// We've reached the first compound prediction mode, get stats from the // single reference predictors to help with pruning. // Disable this pruning logic if interpolation filter search was skipped for // single prediction modes as it can result in aggressive pruning of compound // prediction modes due to the absence of modelled_rd populated by // av1_interpolation_filter_search(). // TODO(Remya): Check the impact of the sf // 'prune_comp_search_by_single_result' if compound prediction modes are // enabled in future for REALTIME encode. if (!sf->interp_sf.skip_interp_filter_search &&
sf->inter_sf.prune_comp_search_by_single_result > 0 && comp_pred &&
args->reach_first_comp_mode == 0) {
analyze_single_states(cpi, args->search_state);
args->reach_first_comp_mode = 1;
}
// Prune aggressively when best mode is skippable. int mul_fact = args->search_state->best_mode_skippable
? args->mode_thresh_mul_fact
: (1 << MODE_THRESH_QBITS);
int64_t mode_threshold =
(args->search_state->mode_threshold[mode_enum] * mul_fact) >>
MODE_THRESH_QBITS;
if (args->search_state->best_rd < mode_threshold) return 1;
// Skip this compound mode based on the RD results from the single prediction // modes if (!sf->interp_sf.skip_interp_filter_search &&
sf->inter_sf.prune_comp_search_by_single_result > 0 && comp_pred) { if (compound_skip_by_single_states(cpi, args->search_state, this_mode,
ref_frame, second_ref_frame, x)) return 1;
}
if (sf->inter_sf.prune_compound_using_single_ref && comp_pred) { // After we done with single reference modes, find the 2nd best RD // for a reference frame. Only search compound modes that have a reference // frame at least as good as the 2nd best. if (!args->prune_cpd_using_sr_stats_ready &&
args->num_single_modes_processed == NUM_SINGLE_REF_MODES) {
find_top_ref(ref_frame_rd);
args->prune_cpd_using_sr_stats_ready = 1;
} if (args->prune_cpd_using_sr_stats_ready &&
!in_single_ref_cutoff(ref_frame_rd, ref_frame, second_ref_frame)) return 1;
}
if (sf->inter_sf.prune_ext_comp_using_neighbors && comp_pred) { if (compound_skip_using_neighbor_refs(
xd, this_mode, ref_frames,
sf->inter_sf.prune_ext_comp_using_neighbors)) return 1;
}
if (sf->inter_sf.prune_comp_using_best_single_mode_ref && comp_pred) { if (skip_compound_using_best_single_mode_ref(
this_mode, ref_frames, args->search_state->best_single_mode,
sf->inter_sf.prune_comp_using_best_single_mode_ref)) return 1;
}
if (sf->inter_sf.prune_nearest_near_mv_using_refmv_weight && !comp_pred) { const int8_t ref_frame_type = av1_ref_frame_type(ref_frames); if (skip_nearest_near_mv_using_refmv_weight(
x, this_mode, ref_frame_type,
args->search_state->best_mbmode.mode)) { // Ensure the mode is pruned only when the current block has obtained a // valid inter mode.
assert(is_inter_mode(args->search_state->best_mbmode.mode)); return 1;
}
}
if (rd_stats.rdcost < search_state->best_rd) {
update_search_state(search_state, rd_cost, ctx, &rd_stats, &rd_stats_y,
&rd_stats_uv, mode_enum, x, txfm_search_done);
search_state->best_skip_rd[0] = skip_rd; // Limit the total number of modes to be evaluated if the first is valid // and transform skip or compound if (cpi->sf.inter_sf.inter_mode_txfm_breakout) { if (!j && (search_state->best_mbmode.skip_txfm || rd_stats.skip_txfm)) { // Evaluate more candidates at high quantizers where occurrence of // transform skip is high. constint max_cands_cap[5] = { 2, 3, 5, 7, 9 }; constint qindex_band = (5 * x->qindex) >> QINDEX_BITS;
num_inter_mode_cands =
AOMMIN(max_cands_cap[qindex_band], inter_modes_info->num);
} elseif (!j && has_second_ref(&search_state->best_mbmode)) { constint aggr = cpi->sf.inter_sf.inter_mode_txfm_breakout - 1; // Evaluate more candidates at low quantizers where occurrence of // single reference mode is high. constint max_cands_cap_cmp[2][4] = { { 10, 7, 5, 4 },
{ 10, 7, 5, 3 } }; constint qindex_band_cmp = (4 * x->qindex) >> QINDEX_BITS;
num_inter_mode_cands = AOMMIN(
max_cands_cap_cmp[aggr][qindex_band_cmp], inter_modes_info->num);
}
}
} // If the number of candidates evaluated exceeds max_allowed_cands, break if // a newmv mode was evaluated already. if ((num_tx_cands > max_allowed_cands) && newmv_mode_evaled) break;
}
}
// Indicates number of winner simple translation modes to be used staticconstunsignedint num_winner_motion_modes[3] = { 0, 10, 3 };
// Adds a motion mode to the candidate list for motion_mode_for_winner_cand // speed feature. This list consists of modes that have only searched // SIMPLE_TRANSLATION. The final list will be used to search other motion // modes after the initial RD search. staticvoid handle_winner_cand(
MB_MODE_INFO *const mbmi,
motion_mode_best_st_candidate *best_motion_mode_cands, int max_winner_motion_mode_cand, int64_t this_rd,
motion_mode_candidate *motion_mode_cand, int skip_motion_mode) { // Number of current motion mode candidates in list constint num_motion_mode_cand = best_motion_mode_cands->num_motion_mode_cand; int valid_motion_mode_cand_loc = num_motion_mode_cand;
// find the best location to insert new motion mode candidate for (int j = 0; j < num_motion_mode_cand; j++) { if (this_rd < best_motion_mode_cands->motion_mode_cand[j].rd_cost) {
valid_motion_mode_cand_loc = j; break;
}
}
/*!\brief Search intra modes in interframes * * \ingroup intra_mode_search * * This function searches for the best intra mode when the current frame is an * interframe. This function however does *not* handle luma palette mode. * Palette mode is currently handled by \ref av1_search_palette_mode. * * This function will first iterate through the luma mode candidates to find the * best luma intra mode. Once the best luma mode it's found, it will then search * for the best chroma mode. Because palette mode is currently not handled by * here, a cache of uv mode is stored in * InterModeSearchState::intra_search_state so it can be reused later by \ref * av1_search_palette_mode. * * \param[in,out] search_state Struct keep track of the prediction mode * search state in interframe. * * \param[in] cpi Top-level encoder structure. * \param[in,out] x Pointer to struct holding all the data for * the current prediction block. * \param[out] rd_cost Stores the best rd_cost among all the * prediction modes searched. * \param[in] bsize Current block size. * \param[in,out] ctx Structure to hold the number of 4x4 blks to * copy the tx_type and txfm_skip arrays. * for only the Y plane. * \param[in] sf_args Stores the list of intra mode candidates * to be searched. * \param[in] intra_ref_frame_cost The entropy cost for signaling that the * current ref frame is an intra frame. * \param[in] yrd_threshold The rdcost threshold for luma intra mode to * terminate chroma intra mode search. * * \remark If a new best mode is found, search_state and rd_costs are updated * correspondingly. While x is also modified, it is only used as a temporary * buffer, and the final decisions are stored in search_state.
*/ staticinlinevoid search_intra_modes_in_interframe(
InterModeSearchState *search_state, const AV1_COMP *cpi, MACROBLOCK *x,
RD_STATS *rd_cost, BLOCK_SIZE bsize, PICK_MODE_CONTEXT *ctx, const InterModeSFArgs *sf_args, unsignedint intra_ref_frame_cost,
int64_t yrd_threshold) { const AV1_COMMON *const cm = &cpi->common; const SPEED_FEATURES *const sf = &cpi->sf; const IntraModeCfg *const intra_mode_cfg = &cpi->oxcf.intra_mode_cfg;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];
IntraModeSearchState *intra_search_state = &search_state->intra_search_state;
// Performs luma search
int64_t best_model_rd = INT64_MAX;
int64_t top_intra_model_rd[TOP_INTRA_MODEL_COUNT]; for (int i = 0; i < TOP_INTRA_MODEL_COUNT; i++) {
top_intra_model_rd[i] = INT64_MAX;
} 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) break;
set_y_mode_and_delta_angle(
mode_idx, mbmi, sf->intra_sf.prune_luma_odd_delta_angles_in_intra);
assert(mbmi->mode < INTRA_MODE_END);
// Use intra_y_mode_mask speed feature to skip intra mode evaluation. if (sf_args->mode_skip_mask->pred_modes[INTRA_FRAME] & (1 << mbmi->mode)) continue;
if (this_mode != DC_PRED) { // Only search the oblique modes if the best so far is // one of the neighboring directional modes if ((sf->rt_sf.mode_search_skip_flags & FLAG_SKIP_INTRA_BESTINTER) &&
(this_mode >= D45_PRED && this_mode <= PAETH_PRED)) { if (search_state->best_mode_index != THR_INVALID &&
search_state->best_mbmode.ref_frame[0] > INTRA_FRAME) continue;
} if (sf->rt_sf.mode_search_skip_flags & FLAG_SKIP_INTRA_DIRMISMATCH) { if (conditional_skipintra(
this_mode, search_state->intra_search_state.best_intra_mode)) continue;
}
}
// Merge the luma and chroma rd stats
assert(best_mode_cost_y >= 0);
intra_rd_stats.rate = best_intra_rd_stats_y.rate + best_mode_cost_y; if (!xd->lossless[mbmi->segment_id] && block_signals_txsize(bsize)) { // av1_pick_uniform_tx_size_type_yrd above includes the cost of the tx_size // in the tokenonly rate, but for intra blocks, tx_size is always coded // (prediction granularity), so we account for it in the full rate, // not the tokenonly rate.
best_intra_rd_stats_y.rate -= tx_size_cost(x, bsize, mbmi->tx_size);
}
// Intra block is always coded as non-skip
intra_rd_stats.skip_txfm = 0;
intra_rd_stats.dist = best_intra_rd_stats_y.dist + intra_rd_stats_uv.dist; // Add in the cost of the no skip flag. constint skip_ctx = av1_get_skip_txfm_context(xd);
intra_rd_stats.rate += mode_costs->skip_txfm_cost[skip_ctx][0]; // Calculate the final RD estimate for this mode. const int64_t this_rd =
RDCOST(x->rdmult, intra_rd_stats.rate, intra_rd_stats.dist); // Keep record of best intra rd if (this_rd < search_state->best_intra_rd) {
search_state->best_intra_rd = this_rd;
intra_search_state->best_intra_mode = mode;
}
for (int i = 0; i < REFERENCE_MODES; ++i) {
search_state->best_pred_rd[i] =
AOMMIN(search_state->best_pred_rd[i], this_rd);
}
// For two parameters, the max prob returned from av1_nn_softmax equals // 1.0 / (1.0 + e^(-|diff_score|)). Here use scores directly to avoid the // calling of av1_nn_softmax. constfloat thresh[5] = { 1.4f, 1.4f, 1.4f, 1.4f, 1.4f };
assert(skip_intra_in_interframe <= 5); if (scores[1] > scores[0] + thresh[skip_intra_in_interframe - 1]) {
search_state->intra_search_state.skip_intra_modes = 1;
}
}
}
for (i = 0; i < MODE_CTX_REF_FRAMES; ++i) args.cmp_mode[i] = -1; // Indicates the appropriate number of simple translation winner modes for // exhaustive motion mode evaluation constint max_winner_motion_mode_cand =
num_winner_motion_modes[sf->winner_mode_sf.motion_mode_for_winner_cand];
assert(max_winner_motion_mode_cand <= MAX_WINNER_MOTION_MODES);
motion_mode_candidate motion_mode_cand;
motion_mode_best_st_candidate best_motion_mode_cands; // Initializing the number of motion mode candidates to zero.
best_motion_mode_cands.num_motion_mode_cand = 0; for (i = 0; i < MAX_WINNER_MOTION_MODES; ++i)
best_motion_mode_cands.motion_mode_cand[i].rd_cost = INT64_MAX;
for (i = 0; i < REF_FRAMES; ++i) x->pred_sse[i] = INT_MAX;
av1_invalid_rd_stats(rd_cost);
for (i = 0; i < REF_FRAMES; ++i) {
x->warp_sample_info[i].num = -1;
}
// Ref frames that are selected by square partition blocks. int picked_ref_frames_mask = 0; if (sf->inter_sf.prune_ref_frame_for_rect_partitions &&
mbmi->partition != PARTITION_NONE) { // prune_ref_frame_for_rect_partitions = 1 implies prune only extended // partition blocks. prune_ref_frame_for_rect_partitions >=2 // implies prune for vert, horiz and extended partition blocks. if ((mbmi->partition != PARTITION_VERT &&
mbmi->partition != PARTITION_HORZ) ||
sf->inter_sf.prune_ref_frame_for_rect_partitions >= 2) {
picked_ref_frames_mask =
fetch_picked_ref_frames_mask(x, bsize, cm->seq_params->mib_size);
}
}
int64_t best_est_rd = INT64_MAX; const InterModeRdModel *md = &tile_data->inter_mode_rd_models[bsize]; // If do_tx_search is 0, only estimated RD should be computed. // If do_tx_search is 1, all modes have TX search performed. constint do_tx_search =
!((sf->inter_sf.inter_mode_rd_model_estimation == 1 && md->ready) ||
(sf->inter_sf.inter_mode_rd_model_estimation == 2 &&
num_pels_log2_lookup[bsize] > 8));
InterModesInfo *inter_modes_info = x->inter_modes_info;
inter_modes_info->num = 0;
// Temporary buffers used by handle_inter_mode().
uint8_t *const tmp_buf = get_buf_by_bd(xd, x->tmp_pred_bufs[0]);
// The best RD found for the reference frame, among single reference modes. // Note that the 0-th element will contain a cut-off that is later used // to determine if we should skip a compound mode.
int64_t ref_frame_rd[REF_FRAMES] = { INT64_MAX, INT64_MAX, INT64_MAX,
INT64_MAX, INT64_MAX, INT64_MAX,
INT64_MAX, INT64_MAX };
// Prepared stats used later to check if we could skip intra mode eval.
int64_t inter_cost = -1;
int64_t intra_cost = -1; // Need to tweak the threshold for hdres speed 0 & 1. constint mi_row = xd->mi_row; constint mi_col = xd->mi_col;
// Obtain the relevant tpl stats for pruning inter modes
PruneInfoFromTpl inter_cost_info_from_tpl; #if !CONFIG_REALTIME_ONLY if (sf->inter_sf.prune_inter_modes_based_on_tpl) { // x->tpl_keep_ref_frame[id] = 1 => no pruning in // prune_ref_by_selective_ref_frame() // x->tpl_keep_ref_frame[id] = 0 => ref frame can be pruned in // prune_ref_by_selective_ref_frame() // Populating valid_refs[idx] = 1 ensures that // 'inter_cost_info_from_tpl.best_inter_cost' does not correspond to a // pruned ref frame. int valid_refs[INTER_REFS_PER_FRAME]; for (MV_REFERENCE_FRAME frame = LAST_FRAME; frame < REF_FRAMES; frame++) { const MV_REFERENCE_FRAME refs[2] = { frame, NONE_FRAME };
valid_refs[frame - 1] =
x->tpl_keep_ref_frame[frame] ||
!prune_ref_by_selective_ref_frame(
cpi, x, refs, cm->cur_frame->ref_display_order_hint);
}
av1_zero(inter_cost_info_from_tpl);
get_block_level_tpl_stats(cpi, bsize, mi_row, mi_col, valid_refs,
&inter_cost_info_from_tpl);
}
// This is the main loop of this function. It loops over all possible inter // modes and calls handle_inter_mode() to compute the RD for each. // Here midx is just an iterator index that should not be used by itself // except to keep track of the number of modes searched. It should be used // with av1_default_mode_order to get the enum that defines the mode, which // can be used with av1_mode_defs to get the prediction mode and the ref // frames. // TODO(yunqing, any): Setting mode_start and mode_end outside for-loop brings // good speedup for real time case. If we decide to use compound mode in real // time, maybe we can modify av1_default_mode_order table.
THR_MODES mode_start = THR_INTER_MODE_START;
THR_MODES mode_end = THR_INTER_MODE_END; const CurrentFrame *const current_frame = &cm->current_frame; if (current_frame->reference_mode == SINGLE_REFERENCE) {
mode_start = SINGLE_REF_MODE_START;
mode_end = SINGLE_REF_MODE_END;
}
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]; const MODE_DEFINITION *mode_def = &av1_mode_defs[mode_enum]; const PREDICTION_MODE this_mode = mode_def->mode; const MV_REFERENCE_FRAME *ref_frames = mode_def->ref_frame;
// Did this mode help, i.e., is it the new best mode if (this_rd < search_state.best_rd) {
assert(IMPLIES(comp_pred,
cm->current_frame.reference_mode != SINGLE_REFERENCE));
search_state.best_pred_sse = x->pred_sse[ref_frame];
best_inter_yrd = this_yrd;
update_search_state(&search_state, rd_cost, ctx, &rd_stats, &rd_stats_y,
&rd_stats_uv, mode_enum, x, do_tx_search); if (do_tx_search) search_state.best_skip_rd[0] = skip_rd[0]; // skip_rd[0] is the best total rd for a skip mode so far. // skip_rd[1] is the best total rd for a skip mode so far in luma. // When do_tx_search = 1, both skip_rd[0] and skip_rd[1] are updated. // When do_tx_search = 0, skip_rd[1] is updated.
search_state.best_skip_rd[1] = skip_rd[1];
} if (sf->winner_mode_sf.motion_mode_for_winner_cand) { // Add this mode to motion mode candidate list for motion mode search // if using motion_mode_for_winner_cand speed feature
handle_winner_cand(mbmi, &best_motion_mode_cands,
max_winner_motion_mode_cand, this_rd,
&motion_mode_cand, args.skip_motion_mode);
}
/* keep record of best compound/single-only prediction */
record_best_compound(cm->current_frame.reference_mode, &rd_stats, comp_pred,
x->rdmult, &search_state, compmode_cost);
}
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, evaluate_motion_mode_for_winner_candidates_time); #endif if (sf->winner_mode_sf.motion_mode_for_winner_cand) { // For the single ref winner candidates, evaluate other motion modes (non // simple translation).
evaluate_motion_mode_for_winner_candidates(
cpi, x, rd_cost, &args, tile_data, ctx, yv12_mb,
&best_motion_mode_cands, do_tx_search, bsize, &best_est_rd,
&search_state, &best_inter_yrd);
} #if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, evaluate_motion_mode_for_winner_candidates_time); #endif
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, do_tx_search_time); #endif if (do_tx_search != 1) { // A full tx search has not yet been done, do tx search for // top mode candidates
tx_search_best_inter_candidates(cpi, tile_data, x, best_rd_so_far, bsize,
yv12_mb, mi_row, mi_col, &search_state,
rd_cost, ctx, &best_inter_yrd);
} #if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, do_tx_search_time); #endif
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, handle_intra_mode_time); #endif // Gate intra mode evaluation if best of inter is skip except when source // variance is extremely low and also based on max intra bsize.
skip_intra_modes_in_interframe(cm, x, bsize, &search_state, sf, inter_cost,
intra_cost);
// Make sure that the ref_mv_idx is only nonzero when we're // using a mode which can support ref_mv_idx if (search_state.best_mbmode.ref_mv_idx != 0 &&
!(search_state.best_mbmode.mode == NEWMV ||
search_state.best_mbmode.mode == NEW_NEWMV ||
have_nearmv_in_inter_mode(search_state.best_mbmode.mode))) {
search_state.best_mbmode.ref_mv_idx = 0;
}
// Note: this section is needed since the mode may have been forced to // GLOBALMV by the all-zero mode handling of ref-mv. if (mbmi->mode == GLOBALMV || mbmi->mode == GLOBAL_GLOBALMV) { // Correct the interp filters for GLOBALMV if (is_nontrans_global_motion(xd, xd->mi[0])) {
int_interpfilters filters =
av1_broadcast_interp_filter(av1_unswitchable_filter(interp_filter));
assert(mbmi->interp_filters.as_int == filters.as_int);
(void)filters;
}
}
if (interp_filter != SWITCHABLE) {
best_filter = interp_filter;
} else {
best_filter = EIGHTTAP_REGULAR; if (av1_is_interp_needed(xd)) { int rs; int best_rs = INT_MAX; for (i = 0; i < SWITCHABLE_FILTERS; ++i) {
mbmi->interp_filters = av1_broadcast_interp_filter(i);
rs = av1_get_switchable_rate(x, xd, interp_filter,
cm->seq_params->enable_dual_filter); if (rs < best_rs) {
best_rs = rs;
best_filter = mbmi->interp_filters.as_filters.y_filter;
}
}
}
} // Set the appropriate filter
mbmi->interp_filters = av1_broadcast_interp_filter(best_filter);
rate2 += av1_get_switchable_rate(x, xd, interp_filter,
cm->seq_params->enable_dual_filter);
if (cm->current_frame.reference_mode == REFERENCE_MODE_SELECT)
rate2 += comp_inter_cost[comp_pred];
// Estimate the reference frame signaling cost and add it // to the rolling cost variable.
rate2 += ref_costs_single[LAST_FRAME];
this_rd = RDCOST(x->rdmult, rate2, distortion2);
for (int row = 0; row < bh; ++row) { for (int col = 0; col < bw; ++col) {
wsrc_buf[col] = src[col] * src_scale - wsrc_buf[col];
}
wsrc_buf += bw;
src += x->plane[0].src.stride;
}
}
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.167Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.