801 lines
34 KiB
C
801 lines
34 KiB
C
/*
|
|
* Copyright (c) 2020, Alliance for Open Media. All rights reserved.
|
|
*
|
|
* This source code is subject to the terms of the BSD 2 Clause License and
|
|
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
|
* was not distributed with this source code in the LICENSE file, you can
|
|
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
|
* Media Patent License 1.0 was not distributed with this source code in the
|
|
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
|
*/
|
|
|
|
#include "av1/common/pred_common.h"
|
|
#include "av1/encoder/interp_search.h"
|
|
#include "av1/encoder/model_rd.h"
|
|
#include "av1/encoder/rdopt_utils.h"
|
|
#include "av1/encoder/reconinter_enc.h"
|
|
|
|
// return mv_diff
|
|
static inline int is_interp_filter_good_match(
|
|
const INTERPOLATION_FILTER_STATS *st, MB_MODE_INFO *const mi,
|
|
int skip_level) {
|
|
const int is_comp = has_second_ref(mi);
|
|
int i;
|
|
|
|
for (i = 0; i < 1 + is_comp; ++i) {
|
|
if (st->ref_frames[i] != mi->ref_frame[i]) return INT_MAX;
|
|
}
|
|
|
|
if (skip_level == 1 && is_comp) {
|
|
if (st->comp_type != mi->interinter_comp.type) return INT_MAX;
|
|
if (st->compound_idx != mi->compound_idx) return INT_MAX;
|
|
}
|
|
|
|
int mv_diff = 0;
|
|
for (i = 0; i < 1 + is_comp; ++i) {
|
|
mv_diff += abs(st->mv[i].as_mv.row - mi->mv[i].as_mv.row) +
|
|
abs(st->mv[i].as_mv.col - mi->mv[i].as_mv.col);
|
|
}
|
|
return mv_diff;
|
|
}
|
|
|
|
static inline int save_interp_filter_search_stat(
|
|
MB_MODE_INFO *const mbmi, int64_t rd, unsigned int pred_sse,
|
|
INTERPOLATION_FILTER_STATS *interp_filter_stats,
|
|
int interp_filter_stats_idx) {
|
|
if (interp_filter_stats_idx < MAX_INTERP_FILTER_STATS) {
|
|
INTERPOLATION_FILTER_STATS stat = { mbmi->interp_filters,
|
|
{ mbmi->mv[0], mbmi->mv[1] },
|
|
{ mbmi->ref_frame[0],
|
|
mbmi->ref_frame[1] },
|
|
mbmi->interinter_comp.type,
|
|
mbmi->compound_idx,
|
|
rd,
|
|
pred_sse };
|
|
interp_filter_stats[interp_filter_stats_idx] = stat;
|
|
interp_filter_stats_idx++;
|
|
}
|
|
return interp_filter_stats_idx;
|
|
}
|
|
|
|
static inline int find_interp_filter_in_stats(
|
|
MB_MODE_INFO *const mbmi, INTERPOLATION_FILTER_STATS *interp_filter_stats,
|
|
int interp_filter_stats_idx, int skip_level) {
|
|
// [skip_levels][single or comp]
|
|
const int thr[2][2] = { { 0, 0 }, { 3, 7 } };
|
|
const int is_comp = has_second_ref(mbmi);
|
|
|
|
// Find good enough match.
|
|
// TODO(yunqing): Separate single-ref mode and comp mode stats for fast
|
|
// search.
|
|
int best = INT_MAX;
|
|
int match = -1;
|
|
for (int j = 0; j < interp_filter_stats_idx; ++j) {
|
|
const INTERPOLATION_FILTER_STATS *st = &interp_filter_stats[j];
|
|
const int mv_diff = is_interp_filter_good_match(st, mbmi, skip_level);
|
|
// Exact match is found.
|
|
if (mv_diff == 0) {
|
|
match = j;
|
|
break;
|
|
} else if (mv_diff < best && mv_diff <= thr[skip_level - 1][is_comp]) {
|
|
best = mv_diff;
|
|
match = j;
|
|
}
|
|
}
|
|
|
|
if (match != -1) {
|
|
mbmi->interp_filters = interp_filter_stats[match].filters;
|
|
return match;
|
|
}
|
|
return -1; // no match result found
|
|
}
|
|
|
|
static int find_interp_filter_match(
|
|
MB_MODE_INFO *const mbmi, const AV1_COMP *const cpi,
|
|
const InterpFilter assign_filter, const int need_search,
|
|
INTERPOLATION_FILTER_STATS *interp_filter_stats,
|
|
int interp_filter_stats_idx) {
|
|
int match_found_idx = -1;
|
|
if (cpi->sf.interp_sf.use_interp_filter && need_search)
|
|
match_found_idx = find_interp_filter_in_stats(
|
|
mbmi, interp_filter_stats, interp_filter_stats_idx,
|
|
cpi->sf.interp_sf.use_interp_filter);
|
|
|
|
if (!need_search || match_found_idx == -1)
|
|
set_default_interp_filters(mbmi, assign_filter);
|
|
return match_found_idx;
|
|
}
|
|
|
|
static inline int get_switchable_rate(MACROBLOCK *const x,
|
|
const int_interpfilters filters,
|
|
const int ctx[2], int dual_filter) {
|
|
const InterpFilter filter0 = filters.as_filters.y_filter;
|
|
int inter_filter_cost =
|
|
x->mode_costs.switchable_interp_costs[ctx[0]][filter0];
|
|
if (dual_filter) {
|
|
const InterpFilter filter1 = filters.as_filters.x_filter;
|
|
inter_filter_cost += x->mode_costs.switchable_interp_costs[ctx[1]][filter1];
|
|
}
|
|
return SWITCHABLE_INTERP_RATE_FACTOR * inter_filter_cost;
|
|
}
|
|
|
|
// Build inter predictor and calculate model rd
|
|
// for a given plane.
|
|
static inline void interp_model_rd_eval(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const orig_dst, int plane_from, int plane_to,
|
|
RD_STATS *rd_stats, int is_skip_build_pred) {
|
|
const AV1_COMMON *cm = &cpi->common;
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
RD_STATS tmp_rd_stats;
|
|
av1_init_rd_stats(&tmp_rd_stats);
|
|
|
|
// Skip inter predictor if the predictor is already available.
|
|
if (!is_skip_build_pred) {
|
|
const int mi_row = xd->mi_row;
|
|
const int mi_col = xd->mi_col;
|
|
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, orig_dst, bsize,
|
|
plane_from, plane_to);
|
|
}
|
|
|
|
model_rd_sb_fn[cpi->sf.rt_sf.use_simple_rd_model
|
|
? MODELRD_LEGACY
|
|
: MODELRD_TYPE_INTERP_FILTER](
|
|
cpi, bsize, x, xd, plane_from, plane_to, &tmp_rd_stats.rate,
|
|
&tmp_rd_stats.dist, &tmp_rd_stats.skip_txfm, &tmp_rd_stats.sse, NULL,
|
|
NULL, NULL);
|
|
|
|
av1_merge_rd_stats(rd_stats, &tmp_rd_stats);
|
|
}
|
|
|
|
// calculate the rdcost of given interpolation_filter
|
|
static inline int64_t interpolation_filter_rd(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi,
|
|
const TileDataEnc *tile_data, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const orig_dst, int64_t *const rd,
|
|
RD_STATS *rd_stats_luma, RD_STATS *rd_stats, int *const switchable_rate,
|
|
const BUFFER_SET *dst_bufs[2], int filter_idx, const int switchable_ctx[2],
|
|
const int skip_pred) {
|
|
const AV1_COMMON *cm = &cpi->common;
|
|
const InterpSearchFlags *interp_search_flags = &cpi->interp_search_flags;
|
|
const int num_planes = av1_num_planes(cm);
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
MB_MODE_INFO *const mbmi = xd->mi[0];
|
|
RD_STATS this_rd_stats_luma, this_rd_stats;
|
|
|
|
// Initialize rd_stats structures to default values.
|
|
av1_init_rd_stats(&this_rd_stats_luma);
|
|
this_rd_stats = *rd_stats_luma;
|
|
const int_interpfilters last_best = mbmi->interp_filters;
|
|
mbmi->interp_filters = filter_sets[filter_idx];
|
|
const int tmp_rs =
|
|
get_switchable_rate(x, mbmi->interp_filters, switchable_ctx,
|
|
cm->seq_params->enable_dual_filter);
|
|
|
|
int64_t min_rd = RDCOST(x->rdmult, tmp_rs, 0);
|
|
if (min_rd > *rd) {
|
|
mbmi->interp_filters = last_best;
|
|
return 0;
|
|
}
|
|
|
|
(void)tile_data;
|
|
|
|
assert(skip_pred != 2);
|
|
assert((rd_stats_luma->rate >= 0) && (rd_stats->rate >= 0));
|
|
assert((rd_stats_luma->dist >= 0) && (rd_stats->dist >= 0));
|
|
assert((rd_stats_luma->sse >= 0) && (rd_stats->sse >= 0));
|
|
assert((rd_stats_luma->skip_txfm == 0) || (rd_stats_luma->skip_txfm == 1));
|
|
assert((rd_stats->skip_txfm == 0) || (rd_stats->skip_txfm == 1));
|
|
assert((skip_pred >= 0) &&
|
|
(skip_pred <= interp_search_flags->default_interp_skip_flags));
|
|
|
|
// When skip_txfm pred is equal to default_interp_skip_flags,
|
|
// skip both luma and chroma MC.
|
|
// For mono-chrome images:
|
|
// num_planes = 1 and cpi->default_interp_skip_flags = 1,
|
|
// skip_pred = 1: skip both luma and chroma
|
|
// skip_pred = 0: Evaluate luma and as num_planes=1,
|
|
// skip chroma evaluation
|
|
int tmp_skip_pred =
|
|
(skip_pred == interp_search_flags->default_interp_skip_flags)
|
|
? INTERP_SKIP_LUMA_SKIP_CHROMA
|
|
: skip_pred;
|
|
|
|
switch (tmp_skip_pred) {
|
|
case INTERP_EVAL_LUMA_EVAL_CHROMA:
|
|
// skip_pred = 0: Evaluate both luma and chroma.
|
|
// Luma MC
|
|
interp_model_rd_eval(x, cpi, bsize, orig_dst, AOM_PLANE_Y, AOM_PLANE_Y,
|
|
&this_rd_stats_luma, 0);
|
|
this_rd_stats = this_rd_stats_luma;
|
|
#if CONFIG_COLLECT_RD_STATS == 3
|
|
RD_STATS rd_stats_y;
|
|
av1_pick_recursive_tx_size_type_yrd(cpi, x, &rd_stats_y, bsize,
|
|
INT64_MAX);
|
|
PrintPredictionUnitStats(cpi, tile_data, x, &rd_stats_y, bsize);
|
|
#endif // CONFIG_COLLECT_RD_STATS == 3
|
|
AOM_FALLTHROUGH_INTENDED;
|
|
case INTERP_SKIP_LUMA_EVAL_CHROMA:
|
|
// skip_pred = 1: skip luma evaluation (retain previous best luma stats)
|
|
// and do chroma evaluation.
|
|
for (int plane = 1; plane < num_planes; ++plane) {
|
|
int64_t tmp_rd =
|
|
RDCOST(x->rdmult, tmp_rs + this_rd_stats.rate, this_rd_stats.dist);
|
|
if (tmp_rd >= *rd) {
|
|
mbmi->interp_filters = last_best;
|
|
return 0;
|
|
}
|
|
interp_model_rd_eval(x, cpi, bsize, orig_dst, plane, plane,
|
|
&this_rd_stats, 0);
|
|
}
|
|
break;
|
|
case INTERP_SKIP_LUMA_SKIP_CHROMA:
|
|
// both luma and chroma evaluation is skipped
|
|
this_rd_stats = *rd_stats;
|
|
break;
|
|
case INTERP_EVAL_INVALID:
|
|
default: assert(0); return 0;
|
|
}
|
|
int64_t tmp_rd =
|
|
RDCOST(x->rdmult, tmp_rs + this_rd_stats.rate, this_rd_stats.dist);
|
|
|
|
if (tmp_rd < *rd) {
|
|
*rd = tmp_rd;
|
|
*switchable_rate = tmp_rs;
|
|
if (skip_pred != interp_search_flags->default_interp_skip_flags) {
|
|
if (skip_pred == INTERP_EVAL_LUMA_EVAL_CHROMA) {
|
|
// Overwrite the data as current filter is the best one
|
|
*rd_stats_luma = this_rd_stats_luma;
|
|
*rd_stats = this_rd_stats;
|
|
// As luma MC data is computed, no need to recompute after the search
|
|
x->recalc_luma_mc_data = 0;
|
|
} else if (skip_pred == INTERP_SKIP_LUMA_EVAL_CHROMA) {
|
|
// As luma MC data is not computed, update of luma data can be skipped
|
|
*rd_stats = this_rd_stats;
|
|
// As luma MC data is not recomputed and current filter is the best,
|
|
// indicate the possibility of recomputing MC data
|
|
// If current buffer contains valid MC data, toggle to indicate that
|
|
// luma MC data needs to be recomputed
|
|
x->recalc_luma_mc_data ^= 1;
|
|
}
|
|
swap_dst_buf(xd, dst_bufs, num_planes);
|
|
}
|
|
return 1;
|
|
}
|
|
mbmi->interp_filters = last_best;
|
|
return 0;
|
|
}
|
|
|
|
static inline INTERP_PRED_TYPE is_pred_filter_search_allowed(
|
|
const AV1_COMP *const cpi, MACROBLOCKD *xd, BLOCK_SIZE bsize,
|
|
int_interpfilters *af, int_interpfilters *lf) {
|
|
const AV1_COMMON *cm = &cpi->common;
|
|
const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
|
|
const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
|
|
const int bsl = mi_size_wide_log2[bsize];
|
|
int is_horiz_eq = 0, is_vert_eq = 0;
|
|
|
|
if (above_mbmi && is_inter_block(above_mbmi))
|
|
*af = above_mbmi->interp_filters;
|
|
|
|
if (left_mbmi && is_inter_block(left_mbmi)) *lf = left_mbmi->interp_filters;
|
|
|
|
if (af->as_filters.x_filter != INTERP_INVALID)
|
|
is_horiz_eq = af->as_filters.x_filter == lf->as_filters.x_filter;
|
|
if (af->as_filters.y_filter != INTERP_INVALID)
|
|
is_vert_eq = af->as_filters.y_filter == lf->as_filters.y_filter;
|
|
|
|
INTERP_PRED_TYPE pred_filter_type = (is_vert_eq << 1) + is_horiz_eq;
|
|
const int mi_row = xd->mi_row;
|
|
const int mi_col = xd->mi_col;
|
|
int pred_filter_enable =
|
|
cpi->sf.interp_sf.cb_pred_filter_search
|
|
? (((mi_row + mi_col) >> bsl) +
|
|
get_chessboard_index(cm->current_frame.frame_number)) &
|
|
0x1
|
|
: 0;
|
|
pred_filter_enable &= is_horiz_eq || is_vert_eq;
|
|
// pred_filter_search = 0: pred_filter is disabled
|
|
// pred_filter_search = 1: pred_filter is enabled and only horz pred matching
|
|
// pred_filter_search = 2: pred_filter is enabled and only vert pred matching
|
|
// pred_filter_search = 3: pred_filter is enabled and
|
|
// both vert, horz pred matching
|
|
return pred_filter_enable * pred_filter_type;
|
|
}
|
|
|
|
static DUAL_FILTER_TYPE find_best_interp_rd_facade(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi,
|
|
const TileDataEnc *tile_data, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const orig_dst, int64_t *const rd, RD_STATS *rd_stats_y,
|
|
RD_STATS *rd_stats, int *const switchable_rate,
|
|
const BUFFER_SET *dst_bufs[2], const int switchable_ctx[2],
|
|
const int skip_pred, uint16_t allow_interp_mask, int is_w4_or_h4) {
|
|
int tmp_skip_pred = skip_pred;
|
|
DUAL_FILTER_TYPE best_filt_type = REG_REG;
|
|
|
|
// If no filter are set to be evaluated, return from function
|
|
if (allow_interp_mask == 0x0) return best_filt_type;
|
|
// For block width or height is 4, skip the pred evaluation of SHARP_SHARP
|
|
tmp_skip_pred = is_w4_or_h4
|
|
? cpi->interp_search_flags.default_interp_skip_flags
|
|
: skip_pred;
|
|
|
|
// Loop over the all filter types and evaluate for only allowed filter types
|
|
for (int filt_type = SHARP_SHARP; filt_type >= REG_REG; --filt_type) {
|
|
const int is_filter_allowed =
|
|
get_interp_filter_allowed_mask(allow_interp_mask, filt_type);
|
|
if (is_filter_allowed)
|
|
if (interpolation_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate,
|
|
dst_bufs, filt_type, switchable_ctx,
|
|
tmp_skip_pred))
|
|
best_filt_type = filt_type;
|
|
tmp_skip_pred = skip_pred;
|
|
}
|
|
return best_filt_type;
|
|
}
|
|
|
|
static inline void pred_dual_interp_filter_rd(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi,
|
|
const TileDataEnc *tile_data, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const orig_dst, int64_t *const rd, RD_STATS *rd_stats_y,
|
|
RD_STATS *rd_stats, int *const switchable_rate,
|
|
const BUFFER_SET *dst_bufs[2], const int switchable_ctx[2],
|
|
const int skip_pred, INTERP_PRED_TYPE pred_filt_type, int_interpfilters *af,
|
|
int_interpfilters *lf) {
|
|
(void)lf;
|
|
assert(pred_filt_type > INTERP_HORZ_NEQ_VERT_NEQ);
|
|
assert(pred_filt_type < INTERP_PRED_TYPE_ALL);
|
|
uint16_t allowed_interp_mask = 0;
|
|
|
|
if (pred_filt_type == INTERP_HORZ_EQ_VERT_NEQ) {
|
|
// pred_filter_search = 1: Only horizontal filter is matching
|
|
allowed_interp_mask =
|
|
av1_interp_dual_filt_mask[pred_filt_type - 1][af->as_filters.x_filter];
|
|
} else if (pred_filt_type == INTERP_HORZ_NEQ_VERT_EQ) {
|
|
// pred_filter_search = 2: Only vertical filter is matching
|
|
allowed_interp_mask =
|
|
av1_interp_dual_filt_mask[pred_filt_type - 1][af->as_filters.y_filter];
|
|
} else {
|
|
// pred_filter_search = 3: Both horizontal and vertical filter are matching
|
|
int filt_type =
|
|
af->as_filters.x_filter + af->as_filters.y_filter * SWITCHABLE_FILTERS;
|
|
set_interp_filter_allowed_mask(&allowed_interp_mask, filt_type);
|
|
}
|
|
// REG_REG is already been evaluated in the beginning
|
|
reset_interp_filter_allowed_mask(&allowed_interp_mask, REG_REG);
|
|
find_best_interp_rd_facade(x, cpi, tile_data, bsize, orig_dst, rd, rd_stats_y,
|
|
rd_stats, switchable_rate, dst_bufs,
|
|
switchable_ctx, skip_pred, allowed_interp_mask, 0);
|
|
}
|
|
// Evaluate dual filter type
|
|
// a) Using above, left block interp filter
|
|
// b) Find the best horizontal filter and
|
|
// then evaluate corresponding vertical filters.
|
|
static inline void fast_dual_interp_filter_rd(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi,
|
|
const TileDataEnc *tile_data, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const orig_dst, int64_t *const rd, RD_STATS *rd_stats_y,
|
|
RD_STATS *rd_stats, int *const switchable_rate,
|
|
const BUFFER_SET *dst_bufs[2], const int switchable_ctx[2],
|
|
const int skip_hor, const int skip_ver) {
|
|
const InterpSearchFlags *interp_search_flags = &cpi->interp_search_flags;
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
MB_MODE_INFO *const mbmi = xd->mi[0];
|
|
INTERP_PRED_TYPE pred_filter_type = INTERP_HORZ_NEQ_VERT_NEQ;
|
|
int_interpfilters af = av1_broadcast_interp_filter(INTERP_INVALID);
|
|
int_interpfilters lf = af;
|
|
|
|
if (!have_newmv_in_inter_mode(mbmi->mode)) {
|
|
pred_filter_type = is_pred_filter_search_allowed(cpi, xd, bsize, &af, &lf);
|
|
}
|
|
|
|
if (pred_filter_type) {
|
|
pred_dual_interp_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate, dst_bufs,
|
|
switchable_ctx, (skip_hor & skip_ver),
|
|
pred_filter_type, &af, &lf);
|
|
} else {
|
|
const int bw = block_size_wide[bsize];
|
|
const int bh = block_size_high[bsize];
|
|
int best_dual_mode = 0;
|
|
int skip_pred =
|
|
bw <= 4 ? interp_search_flags->default_interp_skip_flags : skip_hor;
|
|
// TODO(any): Make use of find_best_interp_rd_facade()
|
|
// if speed impact is negligible
|
|
for (int i = (SWITCHABLE_FILTERS - 1); i >= 1; --i) {
|
|
if (interpolation_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate,
|
|
dst_bufs, i, switchable_ctx, skip_pred)) {
|
|
best_dual_mode = i;
|
|
}
|
|
skip_pred = skip_hor;
|
|
}
|
|
// From best of horizontal EIGHTTAP_REGULAR modes, check vertical modes
|
|
skip_pred =
|
|
bh <= 4 ? interp_search_flags->default_interp_skip_flags : skip_ver;
|
|
for (int i = (best_dual_mode + (SWITCHABLE_FILTERS * 2));
|
|
i >= (best_dual_mode + SWITCHABLE_FILTERS); i -= SWITCHABLE_FILTERS) {
|
|
interpolation_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate, dst_bufs,
|
|
i, switchable_ctx, skip_pred);
|
|
skip_pred = skip_ver;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the best interp filter if dual_interp_filter = 0
|
|
static inline void find_best_non_dual_interp_filter(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi,
|
|
const TileDataEnc *tile_data, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const orig_dst, int64_t *const rd, RD_STATS *rd_stats_y,
|
|
RD_STATS *rd_stats, int *const switchable_rate,
|
|
const BUFFER_SET *dst_bufs[2], const int switchable_ctx[2],
|
|
const int skip_ver, const int skip_hor) {
|
|
const InterpSearchFlags *interp_search_flags = &cpi->interp_search_flags;
|
|
int8_t i;
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
MB_MODE_INFO *const mbmi = xd->mi[0];
|
|
|
|
uint16_t interp_filter_search_mask =
|
|
interp_search_flags->interp_filter_search_mask;
|
|
|
|
if (cpi->sf.interp_sf.adaptive_interp_filter_search == 2) {
|
|
const FRAME_UPDATE_TYPE update_type =
|
|
get_frame_update_type(&cpi->ppi->gf_group, cpi->gf_frame_index);
|
|
const int ctx0 = av1_get_pred_context_switchable_interp(xd, 0);
|
|
const int ctx1 = av1_get_pred_context_switchable_interp(xd, 1);
|
|
int use_actual_frame_probs = 1;
|
|
const int *switchable_interp_p0;
|
|
const int *switchable_interp_p1;
|
|
#if CONFIG_FPMT_TEST
|
|
use_actual_frame_probs =
|
|
(cpi->ppi->fpmt_unit_test_cfg == PARALLEL_SIMULATION_ENCODE) ? 0 : 1;
|
|
if (!use_actual_frame_probs) {
|
|
switchable_interp_p0 = (int *)cpi->ppi->temp_frame_probs
|
|
.switchable_interp_probs[update_type][ctx0];
|
|
switchable_interp_p1 = (int *)cpi->ppi->temp_frame_probs
|
|
.switchable_interp_probs[update_type][ctx1];
|
|
}
|
|
#endif
|
|
if (use_actual_frame_probs) {
|
|
switchable_interp_p0 =
|
|
cpi->ppi->frame_probs.switchable_interp_probs[update_type][ctx0];
|
|
switchable_interp_p1 =
|
|
cpi->ppi->frame_probs.switchable_interp_probs[update_type][ctx1];
|
|
}
|
|
static const int thr[7] = { 0, 8, 8, 8, 8, 0, 8 };
|
|
const int thresh = thr[update_type];
|
|
for (i = 0; i < SWITCHABLE_FILTERS; i++) {
|
|
// For non-dual case, the 2 dir's prob should be identical.
|
|
assert(switchable_interp_p0[i] == switchable_interp_p1[i]);
|
|
if (switchable_interp_p0[i] < thresh &&
|
|
switchable_interp_p1[i] < thresh) {
|
|
DUAL_FILTER_TYPE filt_type = i + SWITCHABLE_FILTERS * i;
|
|
reset_interp_filter_allowed_mask(&interp_filter_search_mask, filt_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Regular filter evaluation should have been done and hence the same should
|
|
// be the winner
|
|
assert(x->e_mbd.mi[0]->interp_filters.as_int == filter_sets[0].as_int);
|
|
if ((skip_hor & skip_ver) != interp_search_flags->default_interp_skip_flags) {
|
|
INTERP_PRED_TYPE pred_filter_type = INTERP_HORZ_NEQ_VERT_NEQ;
|
|
int_interpfilters af = av1_broadcast_interp_filter(INTERP_INVALID);
|
|
int_interpfilters lf = af;
|
|
|
|
pred_filter_type = is_pred_filter_search_allowed(cpi, xd, bsize, &af, &lf);
|
|
if (pred_filter_type) {
|
|
assert(af.as_filters.x_filter != INTERP_INVALID);
|
|
int filter_idx = SWITCHABLE * af.as_filters.x_filter;
|
|
// This assert tells that (filter_x == filter_y) for non-dual filter case
|
|
assert(filter_sets[filter_idx].as_filters.x_filter ==
|
|
filter_sets[filter_idx].as_filters.y_filter);
|
|
if (cpi->sf.interp_sf.adaptive_interp_filter_search &&
|
|
!(get_interp_filter_allowed_mask(interp_filter_search_mask,
|
|
filter_idx))) {
|
|
return;
|
|
}
|
|
if (filter_idx) {
|
|
interpolation_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate, dst_bufs,
|
|
filter_idx, switchable_ctx,
|
|
(skip_hor & skip_ver));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
// Reuse regular filter's modeled rd data for sharp filter for following
|
|
// cases
|
|
// 1) When bsize is 4x4
|
|
// 2) When block width is 4 (i.e. 4x8/4x16 blocks) and MV in vertical
|
|
// direction is full-pel
|
|
// 3) When block height is 4 (i.e. 8x4/16x4 blocks) and MV in horizontal
|
|
// direction is full-pel
|
|
// TODO(any): Optimize cases 2 and 3 further if luma MV in relavant direction
|
|
// alone is full-pel
|
|
|
|
if ((bsize == BLOCK_4X4) ||
|
|
(block_size_wide[bsize] == 4 &&
|
|
skip_ver == interp_search_flags->default_interp_skip_flags) ||
|
|
(block_size_high[bsize] == 4 &&
|
|
skip_hor == interp_search_flags->default_interp_skip_flags)) {
|
|
int skip_pred = skip_hor & skip_ver;
|
|
uint16_t allowed_interp_mask = 0;
|
|
|
|
// REG_REG filter type is evaluated beforehand, hence skip it
|
|
set_interp_filter_allowed_mask(&allowed_interp_mask, SHARP_SHARP);
|
|
set_interp_filter_allowed_mask(&allowed_interp_mask, SMOOTH_SMOOTH);
|
|
if (cpi->sf.interp_sf.adaptive_interp_filter_search)
|
|
allowed_interp_mask &= interp_filter_search_mask;
|
|
|
|
find_best_interp_rd_facade(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate, dst_bufs,
|
|
switchable_ctx, skip_pred, allowed_interp_mask,
|
|
1);
|
|
} else {
|
|
int skip_pred = (skip_hor & skip_ver);
|
|
for (i = (SWITCHABLE_FILTERS + 1); i < DUAL_FILTER_SET_SIZE;
|
|
i += (SWITCHABLE_FILTERS + 1)) {
|
|
// This assert tells that (filter_x == filter_y) for non-dual filter case
|
|
assert(filter_sets[i].as_filters.x_filter ==
|
|
filter_sets[i].as_filters.y_filter);
|
|
if (cpi->sf.interp_sf.adaptive_interp_filter_search &&
|
|
!(get_interp_filter_allowed_mask(interp_filter_search_mask, i))) {
|
|
continue;
|
|
}
|
|
interpolation_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
rd_stats_y, rd_stats, switchable_rate, dst_bufs,
|
|
i, switchable_ctx, skip_pred);
|
|
// In first iteration, smooth filter is evaluated. If smooth filter
|
|
// (which is less sharper) is the winner among regular and smooth filters,
|
|
// sharp filter evaluation is skipped
|
|
// TODO(any): Refine this gating based on modelled rd only (i.e., by not
|
|
// accounting switchable filter rate)
|
|
if (cpi->sf.interp_sf.skip_sharp_interp_filter_search &&
|
|
skip_pred != interp_search_flags->default_interp_skip_flags) {
|
|
if (mbmi->interp_filters.as_int == filter_sets[SMOOTH_SMOOTH].as_int)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void calc_interp_skip_pred_flag(MACROBLOCK *const x,
|
|
const AV1_COMP *const cpi,
|
|
int *skip_hor, int *skip_ver) {
|
|
const AV1_COMMON *cm = &cpi->common;
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
MB_MODE_INFO *const mbmi = xd->mi[0];
|
|
const int num_planes = av1_num_planes(cm);
|
|
const int is_compound = has_second_ref(mbmi);
|
|
assert(is_intrabc_block(mbmi) == 0);
|
|
for (int ref = 0; ref < 1 + is_compound; ++ref) {
|
|
const struct scale_factors *const sf =
|
|
get_ref_scale_factors_const(cm, mbmi->ref_frame[ref]);
|
|
// TODO(any): Refine skip flag calculation considering scaling
|
|
if (av1_is_scaled(sf)) {
|
|
*skip_hor = 0;
|
|
*skip_ver = 0;
|
|
break;
|
|
}
|
|
const MV mv = mbmi->mv[ref].as_mv;
|
|
int skip_hor_plane = 0;
|
|
int skip_ver_plane = 0;
|
|
for (int plane_idx = 0; plane_idx < AOMMAX(1, (num_planes - 1));
|
|
++plane_idx) {
|
|
struct macroblockd_plane *const pd = &xd->plane[plane_idx];
|
|
const int bw = pd->width;
|
|
const int bh = pd->height;
|
|
const MV mv_q4 = clamp_mv_to_umv_border_sb(
|
|
xd, &mv, bw, bh, pd->subsampling_x, pd->subsampling_y);
|
|
const int sub_x = (mv_q4.col & SUBPEL_MASK) << SCALE_EXTRA_BITS;
|
|
const int sub_y = (mv_q4.row & SUBPEL_MASK) << SCALE_EXTRA_BITS;
|
|
skip_hor_plane |= ((sub_x == 0) << plane_idx);
|
|
skip_ver_plane |= ((sub_y == 0) << plane_idx);
|
|
}
|
|
*skip_hor &= skip_hor_plane;
|
|
*skip_ver &= skip_ver_plane;
|
|
// It is not valid that "luma MV is sub-pel, whereas chroma MV is not"
|
|
assert(*skip_hor != 2);
|
|
assert(*skip_ver != 2);
|
|
}
|
|
// When compond prediction type is compound segment wedge, luma MC and chroma
|
|
// MC need to go hand in hand as mask generated during luma MC is reuired for
|
|
// chroma MC. If skip_hor = 0 and skip_ver = 1, mask used for chroma MC during
|
|
// vertical filter decision may be incorrect as temporary MC evaluation
|
|
// overwrites the mask. Make skip_ver as 0 for this case so that mask is
|
|
// populated during luma MC
|
|
if (is_compound && mbmi->compound_idx == 1 &&
|
|
mbmi->interinter_comp.type == COMPOUND_DIFFWTD) {
|
|
assert(mbmi->comp_group_idx == 1);
|
|
if (*skip_hor == 0 && *skip_ver == 1) *skip_ver = 0;
|
|
}
|
|
}
|
|
|
|
/*!\brief AV1 interpolation filter search
|
|
*
|
|
* \ingroup inter_mode_search
|
|
*
|
|
* \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 struc holding all the data for
|
|
* the current macroblock.
|
|
* \param[in] bsize Current block size.
|
|
* \param[in] tmp_dst A temporary prediction buffer to hold a
|
|
* computed prediction.
|
|
* \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] rd The RD cost associated with the selected
|
|
* interpolation filter parameters.
|
|
* \param[in,out] switchable_rate The rate associated with using a SWITCHABLE
|
|
* filter mode.
|
|
* \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.
|
|
* \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.
|
|
*
|
|
* \return Returns INT64_MAX if the filter parameters are invalid and the
|
|
* current motion mode being tested should be skipped. It returns 0 if the
|
|
* parameter search is a success.
|
|
*/
|
|
int64_t av1_interpolation_filter_search(
|
|
MACROBLOCK *const x, const AV1_COMP *const cpi,
|
|
const TileDataEnc *tile_data, BLOCK_SIZE bsize,
|
|
const BUFFER_SET *const tmp_dst, const BUFFER_SET *const orig_dst,
|
|
int64_t *const rd, int *const switchable_rate, int *skip_build_pred,
|
|
HandleInterModeArgs *args, int64_t ref_best_rd) {
|
|
const AV1_COMMON *cm = &cpi->common;
|
|
const InterpSearchFlags *interp_search_flags = &cpi->interp_search_flags;
|
|
const int num_planes = av1_num_planes(cm);
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
MB_MODE_INFO *const mbmi = xd->mi[0];
|
|
const int need_search = av1_is_interp_needed(xd);
|
|
const int ref_frame = xd->mi[0]->ref_frame[0];
|
|
RD_STATS rd_stats_luma, rd_stats;
|
|
|
|
// Initialization of rd_stats structures with default values
|
|
av1_init_rd_stats(&rd_stats_luma);
|
|
av1_init_rd_stats(&rd_stats);
|
|
|
|
int match_found_idx = -1;
|
|
const InterpFilter assign_filter = cm->features.interp_filter;
|
|
|
|
match_found_idx = find_interp_filter_match(
|
|
mbmi, cpi, assign_filter, need_search, args->interp_filter_stats,
|
|
args->interp_filter_stats_idx);
|
|
|
|
if (match_found_idx != -1) {
|
|
*rd = args->interp_filter_stats[match_found_idx].rd;
|
|
x->pred_sse[ref_frame] =
|
|
args->interp_filter_stats[match_found_idx].pred_sse;
|
|
*skip_build_pred = 0;
|
|
return 0;
|
|
}
|
|
|
|
int switchable_ctx[2];
|
|
switchable_ctx[0] = av1_get_pred_context_switchable_interp(xd, 0);
|
|
switchable_ctx[1] = av1_get_pred_context_switchable_interp(xd, 1);
|
|
*switchable_rate =
|
|
get_switchable_rate(x, mbmi->interp_filters, switchable_ctx,
|
|
cm->seq_params->enable_dual_filter);
|
|
|
|
// Do MC evaluation for default filter_type.
|
|
// Luma MC
|
|
interp_model_rd_eval(x, cpi, bsize, orig_dst, AOM_PLANE_Y, AOM_PLANE_Y,
|
|
&rd_stats_luma, *skip_build_pred);
|
|
|
|
#if CONFIG_COLLECT_RD_STATS == 3
|
|
RD_STATS rd_stats_y;
|
|
av1_pick_recursive_tx_size_type_yrd(cpi, x, &rd_stats_y, bsize, INT64_MAX);
|
|
PrintPredictionUnitStats(cpi, tile_data, x, &rd_stats_y, bsize);
|
|
#endif // CONFIG_COLLECT_RD_STATS == 3
|
|
// Chroma MC
|
|
if (num_planes > 1) {
|
|
interp_model_rd_eval(x, cpi, bsize, orig_dst, AOM_PLANE_U, AOM_PLANE_V,
|
|
&rd_stats, *skip_build_pred);
|
|
}
|
|
*skip_build_pred = 1;
|
|
|
|
av1_merge_rd_stats(&rd_stats, &rd_stats_luma);
|
|
|
|
assert(rd_stats.rate >= 0);
|
|
|
|
*rd = RDCOST(x->rdmult, *switchable_rate + rd_stats.rate, rd_stats.dist);
|
|
x->pred_sse[ref_frame] = (unsigned int)(rd_stats_luma.sse >> 4);
|
|
|
|
if (assign_filter != SWITCHABLE || match_found_idx != -1) {
|
|
return 0;
|
|
}
|
|
if (!need_search) {
|
|
int_interpfilters filters = av1_broadcast_interp_filter(EIGHTTAP_REGULAR);
|
|
assert(mbmi->interp_filters.as_int == filters.as_int);
|
|
(void)filters;
|
|
return 0;
|
|
}
|
|
if (args->modelled_rd != NULL) {
|
|
if (has_second_ref(mbmi)) {
|
|
const int ref_mv_idx = mbmi->ref_mv_idx;
|
|
MV_REFERENCE_FRAME *refs = mbmi->ref_frame;
|
|
const int mode0 = compound_ref0_mode(mbmi->mode);
|
|
const int mode1 = compound_ref1_mode(mbmi->mode);
|
|
const int64_t mrd = AOMMIN(args->modelled_rd[mode0][ref_mv_idx][refs[0]],
|
|
args->modelled_rd[mode1][ref_mv_idx][refs[1]]);
|
|
if ((*rd >> 1) > mrd && ref_best_rd < INT64_MAX) {
|
|
return INT64_MAX;
|
|
}
|
|
}
|
|
}
|
|
|
|
x->recalc_luma_mc_data = 0;
|
|
// skip_flag=xx (in binary form)
|
|
// Setting 0th flag corresonds to skipping luma MC and setting 1st bt
|
|
// corresponds to skipping chroma MC skip_flag=0 corresponds to "Don't skip
|
|
// luma and chroma MC" Skip flag=1 corresponds to "Skip Luma MC only"
|
|
// Skip_flag=2 is not a valid case
|
|
// skip_flag=3 corresponds to "Skip both luma and chroma MC"
|
|
int skip_hor = interp_search_flags->default_interp_skip_flags;
|
|
int skip_ver = interp_search_flags->default_interp_skip_flags;
|
|
calc_interp_skip_pred_flag(x, cpi, &skip_hor, &skip_ver);
|
|
|
|
// do interp_filter search
|
|
restore_dst_buf(xd, *tmp_dst, num_planes);
|
|
const BUFFER_SET *dst_bufs[2] = { tmp_dst, orig_dst };
|
|
// Evaluate dual interp filters
|
|
if (cm->seq_params->enable_dual_filter) {
|
|
if (cpi->sf.interp_sf.use_fast_interpolation_filter_search) {
|
|
fast_dual_interp_filter_rd(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
&rd_stats_luma, &rd_stats, switchable_rate,
|
|
dst_bufs, switchable_ctx, skip_hor, skip_ver);
|
|
} else {
|
|
// Use full interpolation filter search
|
|
uint16_t allowed_interp_mask = ALLOW_ALL_INTERP_FILT_MASK;
|
|
// REG_REG filter type is evaluated beforehand, so loop is repeated over
|
|
// REG_SMOOTH to SHARP_SHARP for full interpolation filter search
|
|
reset_interp_filter_allowed_mask(&allowed_interp_mask, REG_REG);
|
|
find_best_interp_rd_facade(x, cpi, tile_data, bsize, orig_dst, rd,
|
|
&rd_stats_luma, &rd_stats, switchable_rate,
|
|
dst_bufs, switchable_ctx,
|
|
(skip_hor & skip_ver), allowed_interp_mask, 0);
|
|
}
|
|
} else {
|
|
// Evaluate non-dual interp filters
|
|
find_best_non_dual_interp_filter(
|
|
x, cpi, tile_data, bsize, orig_dst, rd, &rd_stats_luma, &rd_stats,
|
|
switchable_rate, dst_bufs, switchable_ctx, skip_ver, skip_hor);
|
|
}
|
|
swap_dst_buf(xd, dst_bufs, num_planes);
|
|
// Recompute final MC data if required
|
|
if (x->recalc_luma_mc_data == 1) {
|
|
// Recomputing final luma MC data is required only if the same was skipped
|
|
// in either of the directions Condition below is necessary, but not
|
|
// sufficient
|
|
assert((skip_hor == 1) || (skip_ver == 1));
|
|
const int mi_row = xd->mi_row;
|
|
const int mi_col = xd->mi_col;
|
|
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, orig_dst, bsize,
|
|
AOM_PLANE_Y, AOM_PLANE_Y);
|
|
}
|
|
x->pred_sse[ref_frame] = (unsigned int)(rd_stats_luma.sse >> 4);
|
|
|
|
// save search results
|
|
if (cpi->sf.interp_sf.use_interp_filter) {
|
|
assert(match_found_idx == -1);
|
|
args->interp_filter_stats_idx = save_interp_filter_search_stat(
|
|
mbmi, *rd, x->pred_sse[ref_frame], args->interp_filter_stats,
|
|
args->interp_filter_stats_idx);
|
|
}
|
|
return 0;
|
|
}
|