blob: fabd7478f3b2102567ed26bfd94dd0405b00b88b [file] [log] [blame]
/*
* 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.
*/
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include "./av1_rtcd.h"
#include "./aom_dsp_rtcd.h"
#include "./aom_config.h"
#include "aom_dsp/aom_dsp_common.h"
#include "aom_dsp/binary_codes_writer.h"
#include "aom_ports/mem.h"
#include "aom_ports/aom_timer.h"
#include "aom_ports/system_state.h"
#include "av1/common/common.h"
#include "av1/common/entropy.h"
#include "av1/common/entropymode.h"
#include "av1/common/idct.h"
#include "av1/common/mv.h"
#include "av1/common/mvref_common.h"
#include "av1/common/pred_common.h"
#include "av1/common/quant_common.h"
#include "av1/common/reconintra.h"
#include "av1/common/reconinter.h"
#include "av1/common/seg_common.h"
#include "av1/common/tile_common.h"
#include "av1/encoder/aq_complexity.h"
#include "av1/encoder/aq_cyclicrefresh.h"
#include "av1/encoder/aq_variance.h"
#include "av1/common/warped_motion.h"
#include "av1/encoder/global_motion.h"
#include "av1/encoder/encodeframe.h"
#include "av1/encoder/encodemb.h"
#include "av1/encoder/encodemv.h"
#if CONFIG_LV_MAP
#include "av1/encoder/encodetxb.h"
#endif
#include "av1/encoder/ethread.h"
#include "av1/encoder/extend.h"
#include "av1/encoder/rd.h"
#include "av1/encoder/rdopt.h"
#include "av1/encoder/segmentation.h"
#include "av1/encoder/tokenize.h"
#if CONFIG_HIGHBITDEPTH
#define IF_HBD(...) __VA_ARGS__
#else
#define IF_HBD(...)
#endif // CONFIG_HIGHBITDEPTH
static void encode_superblock(const AV1_COMP *const cpi, ThreadData *td,
TOKENEXTRA **t, RUN_TYPE dry_run, int mi_row,
int mi_col, BLOCK_SIZE bsize, int *rate);
// This is used as a reference when computing the source variance for the
// purposes of activity masking.
// Eventually this should be replaced by custom no-reference routines,
// which will be faster.
static const uint8_t AV1_VAR_OFFS[MAX_SB_SIZE] = {
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
#if CONFIG_EXT_PARTITION
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128
#endif // CONFIG_EXT_PARTITION
};
#if CONFIG_HIGHBITDEPTH
static const uint16_t AV1_HIGH_VAR_OFFS_8[MAX_SB_SIZE] = {
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
#if CONFIG_EXT_PARTITION
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128
#endif // CONFIG_EXT_PARTITION
};
static const uint16_t AV1_HIGH_VAR_OFFS_10[MAX_SB_SIZE] = {
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
#if CONFIG_EXT_PARTITION
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4,
128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4, 128 * 4
#endif // CONFIG_EXT_PARTITION
};
static const uint16_t AV1_HIGH_VAR_OFFS_12[MAX_SB_SIZE] = {
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16,
#if CONFIG_EXT_PARTITION
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16, 128 * 16,
128 * 16
#endif // CONFIG_EXT_PARTITION
};
#endif // CONFIG_HIGHBITDEPTH
unsigned int av1_get_sby_perpixel_variance(const AV1_COMP *cpi,
const struct buf_2d *ref,
BLOCK_SIZE bs) {
unsigned int sse;
const unsigned int var =
cpi->fn_ptr[bs].vf(ref->buf, ref->stride, AV1_VAR_OFFS, 0, &sse);
return ROUND_POWER_OF_TWO(var, num_pels_log2_lookup[bs]);
}
#if CONFIG_HIGHBITDEPTH
unsigned int av1_high_get_sby_perpixel_variance(const AV1_COMP *cpi,
const struct buf_2d *ref,
BLOCK_SIZE bs, int bd) {
unsigned int var, sse;
switch (bd) {
case 10:
var =
cpi->fn_ptr[bs].vf(ref->buf, ref->stride,
CONVERT_TO_BYTEPTR(AV1_HIGH_VAR_OFFS_10), 0, &sse);
break;
case 12:
var =
cpi->fn_ptr[bs].vf(ref->buf, ref->stride,
CONVERT_TO_BYTEPTR(AV1_HIGH_VAR_OFFS_12), 0, &sse);
break;
case 8:
default:
var =
cpi->fn_ptr[bs].vf(ref->buf, ref->stride,
CONVERT_TO_BYTEPTR(AV1_HIGH_VAR_OFFS_8), 0, &sse);
break;
}
return ROUND_POWER_OF_TWO(var, num_pels_log2_lookup[bs]);
}
#endif // CONFIG_HIGHBITDEPTH
static unsigned int get_sby_perpixel_diff_variance(const AV1_COMP *const cpi,
const struct buf_2d *ref,
int mi_row, int mi_col,
BLOCK_SIZE bs) {
unsigned int sse, var;
uint8_t *last_y;
const YV12_BUFFER_CONFIG *last = get_ref_frame_buffer(cpi, LAST_FRAME);
assert(last != NULL);
last_y =
&last->y_buffer[mi_row * MI_SIZE * last->y_stride + mi_col * MI_SIZE];
var = cpi->fn_ptr[bs].vf(ref->buf, ref->stride, last_y, last->y_stride, &sse);
return ROUND_POWER_OF_TWO(var, num_pels_log2_lookup[bs]);
}
static BLOCK_SIZE get_rd_var_based_fixed_partition(AV1_COMP *cpi, MACROBLOCK *x,
int mi_row, int mi_col) {
unsigned int var = get_sby_perpixel_diff_variance(
cpi, &x->plane[0].src, mi_row, mi_col, BLOCK_64X64);
if (var < 8)
return BLOCK_64X64;
else if (var < 128)
return BLOCK_32X32;
else if (var < 2048)
return BLOCK_16X16;
else
return BLOCK_8X8;
}
// Lighter version of set_offsets that only sets the mode info
// pointers.
static void set_mode_info_offsets(const AV1_COMP *const cpi,
MACROBLOCK *const x, MACROBLOCKD *const xd,
int mi_row, int mi_col) {
const AV1_COMMON *const cm = &cpi->common;
const int idx_str = xd->mi_stride * mi_row + mi_col;
xd->mi = cm->mi_grid_visible + idx_str;
xd->mi[0] = cm->mi + idx_str;
x->mbmi_ext = cpi->mbmi_ext_base + (mi_row * cm->mi_cols + mi_col);
}
static void set_offsets_without_segment_id(const AV1_COMP *const cpi,
const TileInfo *const tile,
MACROBLOCK *const x, int mi_row,
int mi_col, BLOCK_SIZE bsize) {
const AV1_COMMON *const cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
const int mi_width = mi_size_wide[bsize];
const int mi_height = mi_size_high[bsize];
set_mode_info_offsets(cpi, x, xd, mi_row, mi_col);
set_skip_context(xd, mi_row, mi_col);
xd->above_txfm_context =
cm->above_txfm_context + (mi_col << TX_UNIT_WIDE_LOG2);
xd->left_txfm_context = xd->left_txfm_context_buffer +
((mi_row & MAX_MIB_MASK) << TX_UNIT_HIGH_LOG2);
// Set up destination pointers.
av1_setup_dst_planes(xd->plane, bsize, get_frame_new_buffer(cm), mi_row,
mi_col);
// Set up limit values for MV components.
// Mv beyond the range do not produce new/different prediction block.
x->mv_limits.row_min =
-(((mi_row + mi_height) * MI_SIZE) + AOM_INTERP_EXTEND);
x->mv_limits.col_min = -(((mi_col + mi_width) * MI_SIZE) + AOM_INTERP_EXTEND);
x->mv_limits.row_max = (cm->mi_rows - mi_row) * MI_SIZE + AOM_INTERP_EXTEND;
x->mv_limits.col_max = (cm->mi_cols - mi_col) * MI_SIZE + AOM_INTERP_EXTEND;
set_plane_n4(xd, mi_width, mi_height);
// Set up distance of MB to edge of frame in 1/8th pel units.
assert(!(mi_col & (mi_width - 1)) && !(mi_row & (mi_height - 1)));
set_mi_row_col(xd, tile, mi_row, mi_height, mi_col, mi_width,
#if CONFIG_DEPENDENT_HORZTILES
cm->dependent_horz_tiles,
#endif // CONFIG_DEPENDENT_HORZTILES
cm->mi_rows, cm->mi_cols);
// Set up source buffers.
av1_setup_src_planes(x, cpi->source, mi_row, mi_col);
// R/D setup.
x->rdmult = cpi->rd.RDMULT;
// required by av1_append_sub8x8_mvs_for_idx() and av1_find_best_ref_mvs()
xd->tile = *tile;
}
static void set_offsets(const AV1_COMP *const cpi, const TileInfo *const tile,
MACROBLOCK *const x, int mi_row, int mi_col,
BLOCK_SIZE bsize) {
const AV1_COMMON *const cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *mbmi;
const struct segmentation *const seg = &cm->seg;
set_offsets_without_segment_id(cpi, tile, x, mi_row, mi_col, bsize);
mbmi = &xd->mi[0]->mbmi;
#if CONFIG_CFL
xd->cfl->mi_row = mi_row;
xd->cfl->mi_col = mi_col;
#endif
// Setup segment ID.
if (seg->enabled) {
if (!cpi->vaq_refresh) {
const uint8_t *const map =
seg->update_map ? cpi->segmentation_map : cm->last_frame_seg_map;
mbmi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
}
av1_init_plane_quantizers(cpi, x, mbmi->segment_id);
} else {
mbmi->segment_id = 0;
}
}
#if CONFIG_DUAL_FILTER
static void reset_intmv_filter_type(const AV1_COMMON *const cm, MACROBLOCKD *xd,
MB_MODE_INFO *mbmi) {
InterpFilter filters[2];
InterpFilter default_filter = av1_unswitchable_filter(cm->interp_filter);
for (int dir = 0; dir < 2; ++dir) {
filters[dir] = ((!has_subpel_mv_component(xd->mi[0], xd, dir) &&
(mbmi->ref_frame[1] == NONE_FRAME ||
!has_subpel_mv_component(xd->mi[0], xd, dir + 2)))
? default_filter
: av1_extract_interp_filter(mbmi->interp_filters, dir));
}
mbmi->interp_filters = av1_make_interp_filters(filters[0], filters[1]);
}
static void update_filter_type_count(FRAME_COUNTS *counts,
const MACROBLOCKD *xd,
const MB_MODE_INFO *mbmi) {
int dir;
for (dir = 0; dir < 2; ++dir) {
if (has_subpel_mv_component(xd->mi[0], xd, dir) ||
(mbmi->ref_frame[1] > INTRA_FRAME &&
has_subpel_mv_component(xd->mi[0], xd, dir + 2))) {
const int ctx = av1_get_pred_context_switchable_interp(xd, dir);
InterpFilter filter =
av1_extract_interp_filter(mbmi->interp_filters, dir);
++counts->switchable_interp[ctx][filter];
update_cdf(xd->tile_ctx->switchable_interp_cdf[ctx], filter,
SWITCHABLE_FILTERS);
}
}
}
#endif
static void update_global_motion_used(PREDICTION_MODE mode, BLOCK_SIZE bsize,
const MB_MODE_INFO *mbmi,
RD_COUNTS *rdc) {
if (mode == ZEROMV || mode == ZERO_ZEROMV) {
const int num_4x4s =
num_4x4_blocks_wide_lookup[bsize] * num_4x4_blocks_high_lookup[bsize];
int ref;
for (ref = 0; ref < 1 + has_second_ref(mbmi); ++ref) {
rdc->global_motion_used[mbmi->ref_frame[ref]] += num_4x4s;
}
}
}
static void reset_tx_size(MACROBLOCKD *xd, MB_MODE_INFO *mbmi,
const TX_MODE tx_mode) {
if (xd->lossless[mbmi->segment_id]) {
mbmi->tx_size = TX_4X4;
} else if (tx_mode != TX_MODE_SELECT) {
mbmi->tx_size =
tx_size_from_tx_mode(mbmi->sb_type, tx_mode, is_inter_block(mbmi));
}
}
static void set_ref_and_pred_mvs(MACROBLOCK *const x, int_mv *const mi_pred_mv,
int8_t rf_type) {
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
const int bw = xd->n8_w << MI_SIZE_LOG2;
const int bh = xd->n8_h << MI_SIZE_LOG2;
int ref_mv_idx = mbmi->ref_mv_idx;
MB_MODE_INFO_EXT *const mbmi_ext = x->mbmi_ext;
CANDIDATE_MV *const curr_ref_mv_stack = mbmi_ext->ref_mv_stack[rf_type];
if (has_second_ref(mbmi)) {
// Special case: NEAR_NEWMV and NEW_NEARMV modes use 1 + mbmi->ref_mv_idx
// (like NEARMV) instead
if (mbmi->mode == NEAR_NEWMV || mbmi->mode == NEW_NEARMV) ref_mv_idx += 1;
if (compound_ref0_mode(mbmi->mode) == NEWMV) {
int_mv this_mv = curr_ref_mv_stack[ref_mv_idx].this_mv;
clamp_mv_ref(&this_mv.as_mv, bw, bh, xd);
mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0] = this_mv;
mbmi->pred_mv[0] = this_mv;
mi_pred_mv[0] = this_mv;
}
if (compound_ref1_mode(mbmi->mode) == NEWMV) {
int_mv this_mv = curr_ref_mv_stack[ref_mv_idx].comp_mv;
clamp_mv_ref(&this_mv.as_mv, bw, bh, xd);
mbmi_ext->ref_mvs[mbmi->ref_frame[1]][0] = this_mv;
mbmi->pred_mv[1] = this_mv;
mi_pred_mv[1] = this_mv;
}
#if CONFIG_COMPOUND_SINGLEREF
} else if (is_inter_singleref_comp_mode(mbmi->mode)) {
// Special case: SR_NEAR_NEWMV uses 1 + mbmi->ref_mv_idx
// (like NEARMV) instead
if (mbmi->mode == SR_NEAR_NEWMV) ref_mv_idx += 1;
if (compound_ref0_mode(mbmi->mode) == NEWMV ||
compound_ref1_mode(mbmi->mode) == NEWMV) {
int_mv this_mv = curr_ref_mv_stack[ref_mv_idx].this_mv;
clamp_mv_ref(&this_mv.as_mv, bw, bh, xd);
mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0] = this_mv;
mbmi->pred_mv[0] = this_mv;
mi_pred_mv[0] = this_mv;
}
#endif // CONFIG_COMPOUND_SINGLEREF
} else {
if (mbmi->mode == NEWMV) {
int i;
for (i = 0; i < 1 + has_second_ref(mbmi); ++i) {
int_mv this_mv = (i == 0) ? curr_ref_mv_stack[ref_mv_idx].this_mv
: curr_ref_mv_stack[ref_mv_idx].comp_mv;
clamp_mv_ref(&this_mv.as_mv, bw, bh, xd);
mbmi_ext->ref_mvs[mbmi->ref_frame[i]][0] = this_mv;
mbmi->pred_mv[i] = this_mv;
mi_pred_mv[i] = this_mv;
}
}
}
}
static void update_state(const AV1_COMP *const cpi, ThreadData *td,
PICK_MODE_CONTEXT *ctx, int mi_row, int mi_col,
BLOCK_SIZE bsize, RUN_TYPE dry_run) {
int i, x_idx, y;
const AV1_COMMON *const cm = &cpi->common;
RD_COUNTS *const rdc = &td->rd_counts;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
struct macroblock_plane *const p = x->plane;
struct macroblockd_plane *const pd = xd->plane;
MODE_INFO *mi = &ctx->mic;
MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
MODE_INFO *mi_addr = xd->mi[0];
const struct segmentation *const seg = &cm->seg;
const int bw = mi_size_wide[mi->mbmi.sb_type];
const int bh = mi_size_high[mi->mbmi.sb_type];
const int mis = cm->mi_stride;
const int mi_width = mi_size_wide[bsize];
const int mi_height = mi_size_high[bsize];
int8_t rf_type;
assert(mi->mbmi.sb_type == bsize);
*mi_addr = *mi;
*x->mbmi_ext = ctx->mbmi_ext;
#if CONFIG_DUAL_FILTER
reset_intmv_filter_type(cm, xd, mbmi);
#endif
rf_type = av1_ref_frame_type(mbmi->ref_frame);
if (x->mbmi_ext->ref_mv_count[rf_type] > 1) {
set_ref_and_pred_mvs(x, mi->mbmi.pred_mv, rf_type);
}
// If segmentation in use
if (seg->enabled) {
// For in frame complexity AQ copy the segment id from the segment map.
if (cpi->oxcf.aq_mode == COMPLEXITY_AQ) {
const uint8_t *const map =
seg->update_map ? cpi->segmentation_map : cm->last_frame_seg_map;
mi_addr->mbmi.segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
reset_tx_size(xd, &mi_addr->mbmi, cm->tx_mode);
}
// Else for cyclic refresh mode update the segment map, set the segment id
// and then update the quantizer.
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) {
av1_cyclic_refresh_update_segment(cpi, &xd->mi[0]->mbmi, mi_row, mi_col,
bsize, ctx->rate, ctx->dist, x->skip);
reset_tx_size(xd, &mi_addr->mbmi, cm->tx_mode);
}
}
for (i = 0; i < MAX_MB_PLANE; ++i) {
p[i].coeff = ctx->coeff[i];
p[i].qcoeff = ctx->qcoeff[i];
pd[i].dqcoeff = ctx->dqcoeff[i];
p[i].eobs = ctx->eobs[i];
#if CONFIG_LV_MAP
p[i].txb_entropy_ctx = ctx->txb_entropy_ctx[i];
#endif // CONFIG_LV_MAP
}
for (i = 0; i < 2; ++i) pd[i].color_index_map = ctx->color_index_map[i];
#if CONFIG_MRC_TX
xd->mrc_mask = ctx->mrc_mask;
#endif // CONFIG_MRC_TX
// Restore the coding context of the MB to that that was in place
// when the mode was picked for it
for (y = 0; y < mi_height; y++)
for (x_idx = 0; x_idx < mi_width; x_idx++)
if ((xd->mb_to_right_edge >> (3 + MI_SIZE_LOG2)) + mi_width > x_idx &&
(xd->mb_to_bottom_edge >> (3 + MI_SIZE_LOG2)) + mi_height > y) {
xd->mi[x_idx + y * mis] = mi_addr;
}
#if !CONFIG_EXT_DELTA_Q
if (cpi->oxcf.aq_mode > NO_AQ && cpi->oxcf.aq_mode < DELTA_AQ)
av1_init_plane_quantizers(cpi, x, xd->mi[0]->mbmi.segment_id);
#else
if (cpi->oxcf.aq_mode)
av1_init_plane_quantizers(cpi, x, xd->mi[0]->mbmi.segment_id);
#endif
x->skip = ctx->skip;
for (i = 0; i < 1; ++i)
memcpy(x->blk_skip[i], ctx->blk_skip[i],
sizeof(uint8_t) * ctx->num_4x4_blk);
if (dry_run) return;
#if CONFIG_INTERNAL_STATS
{
unsigned int *const mode_chosen_counts =
(unsigned int *)cpi->mode_chosen_counts; // Cast const away.
if (frame_is_intra_only(cm)) {
static const int kf_mode_index[] = {
THR_DC /*DC_PRED*/,
THR_V_PRED /*V_PRED*/,
THR_H_PRED /*H_PRED*/,
THR_D45_PRED /*D45_PRED*/,
THR_D135_PRED /*D135_PRED*/,
THR_D117_PRED /*D117_PRED*/,
THR_D153_PRED /*D153_PRED*/,
THR_D207_PRED /*D207_PRED*/,
THR_D63_PRED /*D63_PRED*/,
THR_SMOOTH, /*SMOOTH_PRED*/
#if CONFIG_SMOOTH_HV
THR_SMOOTH_V, /*SMOOTH_V_PRED*/
THR_SMOOTH_H, /*SMOOTH_H_PRED*/
#endif // CONFIG_SMOOTH_HV
THR_PAETH /*PAETH_PRED*/,
};
++mode_chosen_counts[kf_mode_index[mbmi->mode]];
} else {
// Note how often each mode chosen as best
++mode_chosen_counts[ctx->best_mode_index];
}
}
#endif
if (!frame_is_intra_only(cm)) {
if (is_inter_block(mbmi)) {
av1_update_mv_count(td);
if (bsize >= BLOCK_8X8) {
// TODO(sarahparker): global motion stats need to be handled per-tile
// to be compatible with tile-based threading.
update_global_motion_used(mbmi->mode, bsize, mbmi, rdc);
} else {
const int num_4x4_w = num_4x4_blocks_wide_lookup[bsize];
const int num_4x4_h = num_4x4_blocks_high_lookup[bsize];
int idx, idy;
for (idy = 0; idy < 2; idy += num_4x4_h) {
for (idx = 0; idx < 2; idx += num_4x4_w) {
const int j = idy * 2 + idx;
update_global_motion_used(mi->bmi[j].as_mode, bsize, mbmi, rdc);
}
}
}
if (cm->interp_filter == SWITCHABLE
#if CONFIG_WARPED_MOTION
&& mbmi->motion_mode != WARPED_CAUSAL
#endif // CONFIG_WARPED_MOTION
&& !is_nontrans_global_motion(xd)) {
#if CONFIG_DUAL_FILTER
update_filter_type_count(td->counts, xd, mbmi);
#else
const int switchable_ctx = av1_get_pred_context_switchable_interp(xd);
const InterpFilter filter =
av1_extract_interp_filter(mbmi->interp_filters, 0);
++td->counts->switchable_interp[switchable_ctx][filter];
#endif
}
}
rdc->comp_pred_diff[SINGLE_REFERENCE] += ctx->single_pred_diff;
rdc->comp_pred_diff[COMPOUND_REFERENCE] += ctx->comp_pred_diff;
rdc->comp_pred_diff[REFERENCE_MODE_SELECT] += ctx->hybrid_pred_diff;
}
const int x_mis = AOMMIN(bw, cm->mi_cols - mi_col);
const int y_mis = AOMMIN(bh, cm->mi_rows - mi_row);
av1_copy_frame_mvs(cm, mi, mi_row, mi_col, x_mis, y_mis);
#if CONFIG_JNT_COMP
if (has_second_ref(mbmi)) {
const int comp_index_ctx = get_comp_index_context(cm, xd);
++td->counts->compound_index[comp_index_ctx][mbmi->compound_idx];
}
#endif // CONFIG_JNT_COMP
}
#if NC_MODE_INFO
static void set_mode_info_b(const AV1_COMP *const cpi,
const TileInfo *const tile, ThreadData *td,
int mi_row, int mi_col, BLOCK_SIZE bsize,
PICK_MODE_CONTEXT *ctx) {
MACROBLOCK *const x = &td->mb;
set_offsets(cpi, tile, x, mi_row, mi_col, bsize);
update_state(cpi, td, ctx, mi_row, mi_col, bsize, 1);
}
static void set_mode_info_sb(const AV1_COMP *const cpi, ThreadData *td,
const TileInfo *const tile, TOKENEXTRA **tp,
int mi_row, int mi_col, BLOCK_SIZE bsize,
PC_TREE *pc_tree) {
const AV1_COMMON *const cm = &cpi->common;
const int hbs = mi_size_wide[bsize] / 2;
const PARTITION_TYPE partition = pc_tree->partitioning;
BLOCK_SIZE subsize = get_subsize(bsize, partition);
#if CONFIG_EXT_PARTITION_TYPES
const BLOCK_SIZE bsize2 = get_subsize(bsize, PARTITION_SPLIT);
const int quarter_step = mi_size_wide[bsize] / 4;
#endif
if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols) return;
switch (partition) {
case PARTITION_NONE:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, subsize, &pc_tree->none);
break;
case PARTITION_VERT:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, subsize,
&pc_tree->vertical[0]);
if (mi_col + hbs < cm->mi_cols) {
set_mode_info_b(cpi, tile, td, mi_row, mi_col + hbs, subsize,
&pc_tree->vertical[1]);
}
break;
case PARTITION_HORZ:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, subsize,
&pc_tree->horizontal[0]);
if (mi_row + hbs < cm->mi_rows) {
set_mode_info_b(cpi, tile, td, mi_row + hbs, mi_col, subsize,
&pc_tree->horizontal[1]);
}
break;
case PARTITION_SPLIT:
set_mode_info_sb(cpi, td, tile, tp, mi_row, mi_col, subsize,
pc_tree->split[0]);
set_mode_info_sb(cpi, td, tile, tp, mi_row, mi_col + hbs, subsize,
pc_tree->split[1]);
set_mode_info_sb(cpi, td, tile, tp, mi_row + hbs, mi_col, subsize,
pc_tree->split[2]);
set_mode_info_sb(cpi, td, tile, tp, mi_row + hbs, mi_col + hbs, subsize,
pc_tree->split[3]);
break;
#if CONFIG_EXT_PARTITION_TYPES
#if CONFIG_EXT_PARTITION_TYPES_AB
#error NC_MODE_INFO+MOTION_VAR not yet supported for new HORZ/VERT_AB partitions
#endif
case PARTITION_HORZ_A:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, bsize2,
&pc_tree->horizontala[0]);
set_mode_info_b(cpi, tile, td, mi_row, mi_col + hbs, bsize2,
&pc_tree->horizontala[1]);
set_mode_info_b(cpi, tile, td, mi_row + hbs, mi_col, subsize,
&pc_tree->horizontala[2]);
break;
case PARTITION_HORZ_B:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, subsize,
&pc_tree->horizontalb[0]);
set_mode_info_b(cpi, tile, td, mi_row + hbs, mi_col, bsize2,
&pc_tree->horizontalb[1]);
set_mode_info_b(cpi, tile, td, mi_row + hbs, mi_col + hbs, bsize2,
&pc_tree->horizontalb[2]);
break;
case PARTITION_VERT_A:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, bsize2,
&pc_tree->verticala[0]);
set_mode_info_b(cpi, tile, td, mi_row + hbs, mi_col, bsize2,
&pc_tree->verticala[1]);
set_mode_info_b(cpi, tile, td, mi_row, mi_col + hbs, subsize,
&pc_tree->verticala[2]);
break;
case PARTITION_VERT_B:
set_mode_info_b(cpi, tile, td, mi_row, mi_col, subsize,
&pc_tree->verticalb[0]);
set_mode_info_b(cpi, tile, td, mi_row, mi_col + hbs, bsize2,
&pc_tree->verticalb[1]);
set_mode_info_b(cpi, tile, td, mi_row + hbs, mi_col + hbs, bsize2,
&pc_tree->verticalb[2]);
break;
case PARTITION_HORZ_4:
for (int i = 0; i < 4; ++i) {
int this_mi_row = mi_row + i * quarter_step;
if (i > 0 && this_mi_row >= cm->mi_rows) break;
set_mode_info_b(cpi, tile, td, this_mi_row, mi_col, subsize,
&pc_tree->horizontal4[i]);
}
break;
case PARTITION_VERT_4:
for (int i = 0; i < 4; ++i) {
int this_mi_col = mi_col + i * quarter_step;
if (i > 0 && this_mi_col >= cm->mi_cols) break;
set_mode_info_b(cpi, tile, td, mi_row, this_mi_col, subsize,
&pc_tree->vertical4[i]);
}
break;
#endif // CONFIG_EXT_PARTITION_TYPES
default: assert(0 && "Invalid partition type."); break;
}
}
#if CONFIG_NCOBMC_ADAPT_WEIGHT
static void av1_get_ncobmc_mode_rd(const AV1_COMP *const cpi,
MACROBLOCK *const x, MACROBLOCKD *const xd,
int bsize, const int mi_row,
const int mi_col, NCOBMC_MODE *mode) {
const AV1_COMMON *const cm = &cpi->common;
const int mi_width = mi_size_wide[bsize];
const int mi_height = mi_size_high[bsize];
assert(bsize >= BLOCK_8X8);
reset_xd_boundary(xd, mi_row, mi_height, mi_col, mi_width, cm->mi_rows,
cm->mi_cols);
// set up source buffers before calling the mode searching function
av1_setup_src_planes(x, cpi->source, mi_row, mi_col);
*mode = get_ncobmc_mode(cpi, x, xd, mi_row, mi_col, bsize);
}
static void get_ncobmc_intrpl_pred(const AV1_COMP *const cpi, ThreadData *td,
int mi_row, int mi_col, BLOCK_SIZE bsize) {
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
const int mi_width = mi_size_wide[bsize];
const int mi_height = mi_size_high[bsize];
const int hbs = AOMMAX(mi_size_wide[bsize] / 2, mi_size_high[bsize] / 2);
const BLOCK_SIZE sqr_blk = bsize_2_sqr_bsize[bsize];
if (mi_width > mi_height) {
// horizontal partition
av1_get_ncobmc_mode_rd(cpi, x, xd, sqr_blk, mi_row, mi_col,
&mbmi->ncobmc_mode[0]);
xd->mi += hbs;
av1_get_ncobmc_mode_rd(cpi, x, xd, sqr_blk, mi_row, mi_col + hbs,
&mbmi->ncobmc_mode[1]);
} else if (mi_height > mi_width) {
// vertical partition
av1_get_ncobmc_mode_rd(cpi, x, xd, sqr_blk, mi_row, mi_col,
&mbmi->ncobmc_mode[0]);
xd->mi += hbs * xd->mi_stride;
av1_get_ncobmc_mode_rd(cpi, x, xd, sqr_blk, mi_row + hbs, mi_col,
&mbmi->ncobmc_mode[1]);
} else {
av1_get_ncobmc_mode_rd(cpi, x, xd, sqr_blk, mi_row, mi_col,
&mbmi->ncobmc_mode[0]);
}
// restore the info
av1_setup_src_planes(x, cpi->source, mi_row, mi_col);
set_mode_info_offsets(cpi, x, xd, mi_row, mi_col);
}
#endif // CONFIG_NCOBMC_ADAPT_WEIGHT
#endif // (CONFIG_NCOBMC || CONFIG_NCOBMC_ADAPT_WEIGHT)
void av1_setup_src_planes(MACROBLOCK *x, const YV12_BUFFER_CONFIG *src,
int mi_row, int mi_col) {
uint8_t *const buffers[3] = { src->y_buffer, src->u_buffer, src->v_buffer };
const int widths[3] = { src->y_crop_width, src->uv_crop_width,
src->uv_crop_width };
const int heights[3] = { src->y_crop_height, src->uv_crop_height,
src->uv_crop_height };
const int strides[3] = { src->y_stride, src->uv_stride, src->uv_stride };
int i;
// Set current frame pointer.
x->e_mbd.cur_buf = src;
for (i = 0; i < MAX_MB_PLANE; i++)
setup_pred_plane(&x->plane[i].src, x->e_mbd.mi[0]->mbmi.sb_type, buffers[i],
widths[i], heights[i], strides[i], mi_row, mi_col, NULL,
x->e_mbd.plane[i].subsampling_x,
x->e_mbd.plane[i].subsampling_y);
}
static int set_segment_rdmult(const AV1_COMP *const cpi, MACROBLOCK *const x,
int8_t segment_id) {
int segment_qindex;
const AV1_COMMON *const cm = &cpi->common;
av1_init_plane_quantizers(cpi, x, segment_id);
aom_clear_system_state();
segment_qindex = av1_get_qindex(&cm->seg, segment_id, cm->base_qindex);
return av1_compute_rd_mult(cpi, segment_qindex + cm->y_dc_delta_q);
}
#if CONFIG_DIST_8X8
static void dist_8x8_set_sub8x8_dst(MACROBLOCK *const x, uint8_t *dst8x8,
BLOCK_SIZE bsize, int bw, int bh,
int mi_row, int mi_col) {
MACROBLOCKD *const xd = &x->e_mbd;
struct macroblockd_plane *const pd = &xd->plane[0];
const int dst_stride = pd->dst.stride;
uint8_t *dst = pd->dst.buf;
assert(bsize < BLOCK_8X8);
if (bsize < BLOCK_8X8) {
int i, j;
#if CONFIG_HIGHBITDEPTH
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
uint16_t *dst8x8_16 = (uint16_t *)dst8x8;
uint16_t *dst_sub8x8 = &dst8x8_16[((mi_row & 1) * 8 + (mi_col & 1)) << 2];
for (j = 0; j < bh; ++j)
for (i = 0; i < bw; ++i)
dst_sub8x8[j * 8 + i] = CONVERT_TO_SHORTPTR(dst)[j * dst_stride + i];
} else {
#endif
uint8_t *dst_sub8x8 = &dst8x8[((mi_row & 1) * 8 + (mi_col & 1)) << 2];
for (j = 0; j < bh; ++j)
for (i = 0; i < bw; ++i)
dst_sub8x8[j * 8 + i] = dst[j * dst_stride + i];
#if CONFIG_HIGHBITDEPTH
}
#endif
}
}
#endif // CONFIG_DIST_8X8
static void rd_pick_sb_modes(const AV1_COMP *const cpi, TileDataEnc *tile_data,
MACROBLOCK *const x, int mi_row, int mi_col,
RD_STATS *rd_cost,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_TYPE partition,
#endif
BLOCK_SIZE bsize, PICK_MODE_CONTEXT *ctx,
int64_t best_rd) {
const AV1_COMMON *const cm = &cpi->common;
TileInfo *const tile_info = &tile_data->tile_info;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *mbmi;
struct macroblock_plane *const p = x->plane;
struct macroblockd_plane *const pd = xd->plane;
const AQ_MODE aq_mode = cpi->oxcf.aq_mode;
int i, orig_rdmult;
aom_clear_system_state();
set_offsets(cpi, tile_info, x, mi_row, mi_col, bsize);
mbmi = &xd->mi[0]->mbmi;
mbmi->sb_type = bsize;
#if CONFIG_RD_DEBUG
mbmi->mi_row = mi_row;
mbmi->mi_col = mi_col;
#endif
#if CONFIG_EXT_PARTITION_TYPES
mbmi->partition = partition;
#endif
for (i = 0; i < MAX_MB_PLANE; ++i) {
p[i].coeff = ctx->coeff[i];
p[i].qcoeff = ctx->qcoeff[i];
pd[i].dqcoeff = ctx->dqcoeff[i];
p[i].eobs = ctx->eobs[i];
#if CONFIG_LV_MAP
p[i].txb_entropy_ctx = ctx->txb_entropy_ctx[i];
#endif
}
for (i = 0; i < 2; ++i) pd[i].color_index_map = ctx->color_index_map[i];
#if CONFIG_MRC_TX
xd->mrc_mask = ctx->mrc_mask;
#endif // CONFIG_MRC_TX
ctx->skippable = 0;
// Set to zero to make sure we do not use the previous encoded frame stats
mbmi->skip = 0;
x->skip_chroma_rd =
!is_chroma_reference(mi_row, mi_col, bsize, xd->plane[1].subsampling_x,
xd->plane[1].subsampling_y);
#if CONFIG_HIGHBITDEPTH
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
x->source_variance = av1_high_get_sby_perpixel_variance(
cpi, &x->plane[0].src, bsize, xd->bd);
} else {
x->source_variance =
av1_get_sby_perpixel_variance(cpi, &x->plane[0].src, bsize);
}
#else
x->source_variance =
av1_get_sby_perpixel_variance(cpi, &x->plane[0].src, bsize);
#endif // CONFIG_HIGHBITDEPTH
// Save rdmult before it might be changed, so it can be restored later.
orig_rdmult = x->rdmult;
if (aq_mode == VARIANCE_AQ) {
if (cpi->vaq_refresh) {
const int energy =
bsize <= BLOCK_16X16 ? x->mb_energy : av1_block_energy(cpi, x, bsize);
mbmi->segment_id = av1_vaq_segment_id(energy);
// Re-initialise quantiser
av1_init_plane_quantizers(cpi, x, mbmi->segment_id);
}
x->rdmult = set_segment_rdmult(cpi, x, mbmi->segment_id);
} else if (aq_mode == COMPLEXITY_AQ) {
x->rdmult = set_segment_rdmult(cpi, x, mbmi->segment_id);
} else if (aq_mode == CYCLIC_REFRESH_AQ) {
// If segment is boosted, use rdmult for that segment.
if (cyclic_refresh_segment_id_boosted(mbmi->segment_id))
x->rdmult = av1_cyclic_refresh_get_rdmult(cpi->cyclic_refresh);
}
// Find best coding mode & reconstruct the MB so it is available
// as a predictor for MBs that follow in the SB
if (frame_is_intra_only(cm)) {
av1_rd_pick_intra_mode_sb(cpi, x, rd_cost, bsize, ctx, best_rd);
} else {
if (segfeature_active(&cm->seg, mbmi->segment_id, SEG_LVL_SKIP)) {
av1_rd_pick_inter_mode_sb_seg_skip(cpi, tile_data, x, mi_row, mi_col,
rd_cost, bsize, ctx, best_rd);
} else {
av1_rd_pick_inter_mode_sb(cpi, tile_data, x, mi_row, mi_col, rd_cost,
bsize, ctx, best_rd);
}
}
// Examine the resulting rate and for AQ mode 2 make a segment choice.
if ((rd_cost->rate != INT_MAX) && (aq_mode == COMPLEXITY_AQ) &&
(bsize >= BLOCK_16X16) &&
(cm->frame_type == KEY_FRAME || cpi->refresh_alt_ref_frame ||
cpi->refresh_alt2_ref_frame ||
(cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref))) {
av1_caq_select_segment(cpi, x, bsize, mi_row, mi_col, rd_cost->rate);
}
x->rdmult = orig_rdmult;
// TODO(jingning) The rate-distortion optimization flow needs to be
// refactored to provide proper exit/return handle.
if (rd_cost->rate == INT_MAX) rd_cost->rdcost = INT64_MAX;
ctx->rate = rd_cost->rate;
ctx->dist = rd_cost->dist;
}
static void update_inter_mode_stats(FRAME_COUNTS *counts, PREDICTION_MODE mode,
int16_t mode_context) {
int16_t mode_ctx = mode_context & NEWMV_CTX_MASK;
if (mode == NEWMV) {
++counts->newmv_mode[mode_ctx][0];
return;
} else {
++counts->newmv_mode[mode_ctx][1];
if (mode_context & (1 << ALL_ZERO_FLAG_OFFSET)) {
return;
}
mode_ctx = (mode_context >> ZEROMV_OFFSET) & ZEROMV_CTX_MASK;
if (mode == ZEROMV) {
++counts->zeromv_mode[mode_ctx][0];
return;
} else {
++counts->zeromv_mode[mode_ctx][1];
mode_ctx = (mode_context >> REFMV_OFFSET) & REFMV_CTX_MASK;
if (mode_context & (1 << SKIP_NEARESTMV_OFFSET)) mode_ctx = 6;
if (mode_context & (1 << SKIP_NEARMV_OFFSET)) mode_ctx = 7;
if (mode_context & (1 << SKIP_NEARESTMV_SUB8X8_OFFSET)) mode_ctx = 8;
++counts->refmv_mode[mode_ctx][mode != NEARESTMV];
}
}
}
static void update_stats(const AV1_COMMON *const cm, ThreadData *td, int mi_row,
int mi_col) {
MACROBLOCK *x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
const MODE_INFO *const mi = xd->mi[0];
const MB_MODE_INFO *const mbmi = &mi->mbmi;
const MB_MODE_INFO_EXT *const mbmi_ext = x->mbmi_ext;
const BLOCK_SIZE bsize = mbmi->sb_type;
FRAME_CONTEXT *fc = xd->tile_ctx;
// delta quant applies to both intra and inter
int super_block_upper_left =
((mi_row & MAX_MIB_MASK) == 0) && ((mi_col & MAX_MIB_MASK) == 0);
const int seg_ref_active =
segfeature_active(&cm->seg, mbmi->segment_id, SEG_LVL_REF_FRAME);
if (!seg_ref_active) {
const int skip_ctx = av1_get_skip_context(xd);
td->counts->skip[skip_ctx][mbmi->skip]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(fc->skip_cdfs[skip_ctx], mbmi->skip, 2);
#endif // CONFIG_NEW_MULTISYMBOL
}
if (cm->delta_q_present_flag && (bsize != cm->sb_size || !mbmi->skip) &&
super_block_upper_left) {
const int dq = (mbmi->current_q_index - xd->prev_qindex) / cm->delta_q_res;
const int absdq = abs(dq);
int i;
for (i = 0; i < AOMMIN(absdq, DELTA_Q_SMALL); ++i) {
td->counts->delta_q[i][1]++;
}
if (absdq < DELTA_Q_SMALL) td->counts->delta_q[absdq][0]++;
xd->prev_qindex = mbmi->current_q_index;
#if CONFIG_EXT_DELTA_Q
#if CONFIG_LOOPFILTER_LEVEL
if (cm->delta_lf_present_flag) {
if (cm->delta_lf_multi) {
for (int lf_id = 0; lf_id < FRAME_LF_COUNT; ++lf_id) {
const int delta_lf =
(mbmi->curr_delta_lf[lf_id] - xd->prev_delta_lf[lf_id]) /
cm->delta_lf_res;
const int abs_delta_lf = abs(delta_lf);
for (i = 0; i < AOMMIN(abs_delta_lf, DELTA_LF_SMALL); ++i) {
td->counts->delta_lf_multi[lf_id][i][1]++;
}
if (abs_delta_lf < DELTA_LF_SMALL)
td->counts->delta_lf_multi[lf_id][abs_delta_lf][0]++;
xd->prev_delta_lf[lf_id] = mbmi->curr_delta_lf[lf_id];
}
} else {
const int delta_lf =
(mbmi->current_delta_lf_from_base - xd->prev_delta_lf_from_base) /
cm->delta_lf_res;
const int abs_delta_lf = abs(delta_lf);
for (i = 0; i < AOMMIN(abs_delta_lf, DELTA_LF_SMALL); ++i) {
td->counts->delta_lf[i][1]++;
}
if (abs_delta_lf < DELTA_LF_SMALL)
td->counts->delta_lf[abs_delta_lf][0]++;
xd->prev_delta_lf_from_base = mbmi->current_delta_lf_from_base;
}
}
#else
if (cm->delta_lf_present_flag) {
const int dlf =
(mbmi->current_delta_lf_from_base - xd->prev_delta_lf_from_base) /
cm->delta_lf_res;
const int absdlf = abs(dlf);
for (i = 0; i < AOMMIN(absdlf, DELTA_LF_SMALL); ++i) {
td->counts->delta_lf[i][1]++;
}
if (absdlf < DELTA_LF_SMALL) td->counts->delta_lf[absdlf][0]++;
xd->prev_delta_lf_from_base = mbmi->current_delta_lf_from_base;
}
#endif // CONFIG_LOOPFILTER_LEVEL
#endif
}
if (!frame_is_intra_only(cm)) {
FRAME_COUNTS *const counts = td->counts;
RD_COUNTS *rdc = &td->rd_counts;
const int inter_block = is_inter_block(mbmi);
if (!seg_ref_active) {
counts->intra_inter[av1_get_intra_inter_context(xd)][inter_block]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(fc->intra_inter_cdf[av1_get_intra_inter_context(xd)],
inter_block, 2);
#endif
// If the segment reference feature is enabled we have only a single
// reference frame allowed for the segment so exclude it from
// the reference frame counts used to work out probabilities.
if (inter_block) {
const MV_REFERENCE_FRAME ref0 = mbmi->ref_frame[0];
const MV_REFERENCE_FRAME ref1 = mbmi->ref_frame[1];
if (cm->reference_mode == REFERENCE_MODE_SELECT) {
if (has_second_ref(mbmi))
// This flag is also updated for 4x4 blocks
rdc->compound_ref_used_flag = 1;
else
// This flag is also updated for 4x4 blocks
rdc->single_ref_used_flag = 1;
if (is_comp_ref_allowed(mbmi->sb_type)) {
counts->comp_inter[av1_get_reference_mode_context(cm, xd)]
[has_second_ref(mbmi)]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(av1_get_reference_mode_cdf(cm, xd), has_second_ref(mbmi),
2);
#endif // CONFIG_NEW_MULTISYMBOL
}
}
if (has_second_ref(mbmi)) {
#if CONFIG_EXT_COMP_REFS
const COMP_REFERENCE_TYPE comp_ref_type = has_uni_comp_refs(mbmi)
? UNIDIR_COMP_REFERENCE
: BIDIR_COMP_REFERENCE;
#if !USE_UNI_COMP_REFS
// TODO(zoeliu): Temporarily turn off uni-directional comp refs
assert(comp_ref_type == BIDIR_COMP_REFERENCE);
#endif // !USE_UNI_COMP_REFS
counts->comp_ref_type[av1_get_comp_reference_type_context(xd)]
[comp_ref_type]++;
if (comp_ref_type == UNIDIR_COMP_REFERENCE) {
const int bit = (ref0 == BWDREF_FRAME);
counts->uni_comp_ref[av1_get_pred_context_uni_comp_ref_p(xd)][0]
[bit]++;
if (!bit) {
const int bit1 = (ref1 == LAST3_FRAME || ref1 == GOLDEN_FRAME);
counts->uni_comp_ref[av1_get_pred_context_uni_comp_ref_p1(xd)][1]
[bit1]++;
if (bit1) {
counts->uni_comp_ref[av1_get_pred_context_uni_comp_ref_p2(xd)]
[2][ref1 == GOLDEN_FRAME]++;
}
}
} else {
#endif // CONFIG_EXT_COMP_REFS
const int bit = (ref0 == GOLDEN_FRAME || ref0 == LAST3_FRAME);
counts->comp_ref[av1_get_pred_context_comp_ref_p(cm, xd)][0][bit]++;
if (!bit) {
counts->comp_ref[av1_get_pred_context_comp_ref_p1(cm, xd)][1]
[ref0 == LAST_FRAME]++;
} else {
counts->comp_ref[av1_get_pred_context_comp_ref_p2(cm, xd)][2]
[ref0 == GOLDEN_FRAME]++;
}
counts->comp_bwdref[av1_get_pred_context_comp_bwdref_p(cm, xd)][0]
[ref1 == ALTREF_FRAME]++;
if (ref1 != ALTREF_FRAME)
counts->comp_bwdref[av1_get_pred_context_comp_bwdref_p1(cm, xd)]
[1][ref1 == ALTREF2_FRAME]++;
#if CONFIG_EXT_COMP_REFS
}
#endif // CONFIG_EXT_COMP_REFS
} else {
const int bit = (ref0 >= BWDREF_FRAME);
counts->single_ref[av1_get_pred_context_single_ref_p1(xd)][0][bit]++;
if (bit) {
assert(ref0 <= ALTREF_FRAME);
counts->single_ref[av1_get_pred_context_single_ref_p2(xd)][1]
[ref0 == ALTREF_FRAME]++;
if (ref0 != ALTREF_FRAME)
counts->single_ref[av1_get_pred_context_single_ref_p6(xd)][5]
[ref0 == ALTREF2_FRAME]++;
} else {
const int bit1 = !(ref0 == LAST2_FRAME || ref0 == LAST_FRAME);
counts
->single_ref[av1_get_pred_context_single_ref_p3(xd)][2][bit1]++;
if (!bit1) {
counts->single_ref[av1_get_pred_context_single_ref_p4(xd)][3]
[ref0 != LAST_FRAME]++;
} else {
counts->single_ref[av1_get_pred_context_single_ref_p5(xd)][4]
[ref0 != LAST3_FRAME]++;
}
}
}
#if CONFIG_COMPOUND_SINGLEREF
if (!has_second_ref(mbmi))
counts->comp_inter_mode[av1_get_inter_mode_context(xd)]
[is_inter_singleref_comp_mode(mbmi->mode)]++;
#endif // CONFIG_COMPOUND_SINGLEREF
#if CONFIG_INTERINTRA
if (cm->reference_mode != COMPOUND_REFERENCE &&
cm->allow_interintra_compound && is_interintra_allowed(mbmi)) {
const int bsize_group = size_group_lookup[bsize];
if (mbmi->ref_frame[1] == INTRA_FRAME) {
counts->interintra[bsize_group][1]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(fc->interintra_cdf[bsize_group], 1, 2);
#endif
counts->interintra_mode[bsize_group][mbmi->interintra_mode]++;
update_cdf(fc->interintra_mode_cdf[bsize_group],
mbmi->interintra_mode, INTERINTRA_MODES);
if (is_interintra_wedge_used(bsize)) {
counts->wedge_interintra[bsize][mbmi->use_wedge_interintra]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(fc->wedge_interintra_cdf[bsize],
mbmi->use_wedge_interintra, 2);
#endif
}
} else {
counts->interintra[bsize_group][0]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(fc->interintra_cdf[bsize_group], 0, 2);
#endif
}
}
#endif // CONFIG_INTERINTRA
#if CONFIG_WARPED_MOTION
set_ref_ptrs(cm, xd, mbmi->ref_frame[0], mbmi->ref_frame[1]);
#endif
const MOTION_MODE motion_allowed =
motion_mode_allowed(0, xd->global_motion,
#if CONFIG_WARPED_MOTION
xd,
#endif
mi);
if (mbmi->ref_frame[1] != INTRA_FRAME)
#if CONFIG_WARPED_MOTION
{
if (motion_allowed == WARPED_CAUSAL) {
counts->motion_mode[mbmi->sb_type][mbmi->motion_mode]++;
update_cdf(fc->motion_mode_cdf[mbmi->sb_type], mbmi->motion_mode,
MOTION_MODES);
#if CONFIG_NCOBMC_ADAPT_WEIGHT
} else if (motion_allowed == NCOBMC_ADAPT_WEIGHT) {
counts->ncobmc[mbmi->sb_type][mbmi->motion_mode]++;
update_cdf(fc->ncobmc_cdf[mbmi->sb_type], mbmi->motion_mode,
OBMC_FAMILY_MODES);
} else if (motion_allowed == OBMC_CAUSAL) {
counts->obmc[mbmi->sb_type][mbmi->motion_mode == OBMC_CAUSAL]++;
update_cdf(fc->obmc_cdf[mbmi->sb_type], mbmi->motion_mode, 2);
}
#else
} else if (motion_allowed == OBMC_CAUSAL) {
counts->obmc[mbmi->sb_type][mbmi->motion_mode == OBMC_CAUSAL]++;
#if CONFIG_NEW_MULTISYMBOL
update_cdf(fc->obmc_cdf[mbmi->sb_type],
mbmi->motion_mode == OBMC_CAUSAL, 2);
#endif
}
#endif // CONFIG_NCOBMC_ADAPT_WEIGHT
}
#else
if (motion_allowed > SIMPLE_TRANSLATION) {
counts->motion_mode[mbmi->sb_type][mbmi->motion_mode]++;
update_cdf(fc->motion_mode_cdf[mbmi->sb_type], mbmi->motion_mode,
MOTION_MODES);
}
#endif // CONFIG_WARPED_MOTION
#if CONFIG_NCOBMC_ADAPT_WEIGHT
if (mbmi->motion_mode == NCOBMC_ADAPT_WEIGHT) {
ADAPT_OVERLAP_BLOCK ao_block =
adapt_overlap_block_lookup[mbmi->sb_type];
++counts->ncobmc_mode[ao_block][mbmi->ncobmc_mode[0]];
update_cdf(fc->ncobmc_mode_cdf[ao_block], mbmi->ncobmc_mode[0],
MAX_NCOBMC_MODES);
if (mi_size_wide[mbmi->sb_type] != mi_size_high[mbmi->sb_type]) {
++counts->ncobmc_mode[ao_block][mbmi->ncobmc_mode[1]];
update_cdf(fc->ncobmc_mode_cdf[ao_block], mbmi->ncobmc_mode[1],
MAX_NCOBMC_MODES);
}
}
#endif
if (
#if CONFIG_COMPOUND_SINGLEREF
is_inter_anyref_comp_mode(mbmi->mode)
#else // !CONFIG_COMPOUND_SINGLEREF
cm->reference_mode != SINGLE_REFERENCE &&
is_inter_compound_mode(mbmi->mode)
#endif // CONFIG_COMPOUND_SINGLEREF
&& mbmi->motion_mode == SIMPLE_TRANSLATION) {
if (is_interinter_compound_used(COMPOUND_WEDGE, bsize)) {
counts
->compound_interinter[bsize][mbmi->interinter_compound_type]++;
update_cdf(fc->compound_type_cdf[bsize],
mbmi->interinter_compound_type, COMPOUND_TYPES);
}
}
}
}
if (inter_block &&
!segfeature_active(&cm->seg, mbmi->segment_id, SEG_LVL_SKIP)) {
int16_t mode_ctx;
const PREDICTION_MODE mode = mbmi->mode;
if (has_second_ref(mbmi)) {
mode_ctx = mbmi_ext->compound_mode_context[mbmi->ref_frame[0]];
++counts->inter_compound_mode[mode_ctx][INTER_COMPOUND_OFFSET(mode)];
update_cdf(fc->inter_compound_mode_cdf[mode_ctx],
INTER_COMPOUND_OFFSET(mode), INTER_COMPOUND_MODES);
#if CONFIG_COMPOUND_SINGLEREF
} else if (is_inter_singleref_comp_mode(mode)) {
mode_ctx = mbmi_ext->compound_mode_context[mbmi->ref_frame[0]];
++counts->inter_singleref_comp_mode[mode_ctx]
[INTER_SINGLEREF_COMP_OFFSET(mode)];
#endif // CONFIG_COMPOUND_SINGLEREF
} else {
mode_ctx = av1_mode_context_analyzer(mbmi_ext->mode_context,
mbmi->ref_frame, bsize, -1);
update_inter_mode_stats(counts, mode, mode_ctx);
}
int mode_allowed = (mbmi->mode == NEWMV);
mode_allowed |= (mbmi->mode == NEW_NEWMV);
#if CONFIG_COMPOUND_SINGLEREF
mode_allowed |= (mbmi->mode == SR_NEW_NEWMV);
#endif // CONFIG_COMPOUND_SINGLEREF
if (mode_allowed) {
uint8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
int idx;
for (idx = 0; idx < 2; ++idx) {
if (mbmi_ext->ref_mv_count[ref_frame_type] > idx + 1) {
uint8_t drl_ctx =
av1_drl_ctx(mbmi_ext->ref_mv_stack[ref_frame_type], idx);
++counts->drl_mode[drl_ctx][mbmi->ref_mv_idx != idx];
if (mbmi->ref_mv_idx == idx) break;
}
}
}
if (have_nearmv_in_inter_mode(mbmi->mode)) {
uint8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
int idx;
for (idx = 1; idx < 3; ++idx) {
if (mbmi_ext->ref_mv_count[ref_frame_type] > idx + 1) {
uint8_t drl_ctx =
av1_drl_ctx(mbmi_ext->ref_mv_stack[ref_frame_type], idx);
++counts->drl_mode[drl_ctx][mbmi->ref_mv_idx != idx - 1];
if (mbmi->ref_mv_idx == idx - 1) break;
}
}
}
}
#if CONFIG_INTRABC
} else {
if (av1_allow_intrabc(bsize, cm)) {
FRAME_COUNTS *const counts = td->counts;
++counts->intrabc[mbmi->use_intrabc];
} else {
assert(!mbmi->use_intrabc);
}
#endif
}
}
typedef struct {
ENTROPY_CONTEXT a[2 * MAX_MIB_SIZE * MAX_MB_PLANE];
ENTROPY_CONTEXT l[2 * MAX_MIB_SIZE * MAX_MB_PLANE];
PARTITION_CONTEXT sa[MAX_MIB_SIZE];
PARTITION_CONTEXT sl[MAX_MIB_SIZE];
TXFM_CONTEXT *p_ta;
TXFM_CONTEXT *p_tl;
TXFM_CONTEXT ta[2 * MAX_MIB_SIZE];
TXFM_CONTEXT tl[2 * MAX_MIB_SIZE];
} RD_SEARCH_MACROBLOCK_CONTEXT;
static void restore_context(MACROBLOCK *x,
const RD_SEARCH_MACROBLOCK_CONTEXT *ctx, int mi_row,
int mi_col, BLOCK_SIZE bsize) {
MACROBLOCKD *xd = &x->e_mbd;
int p;
const int num_4x4_blocks_wide =
block_size_wide[bsize] >> tx_size_wide_log2[0];
const int num_4x4_blocks_high =
block_size_high[bsize] >> tx_size_high_log2[0];
int mi_width = mi_size_wide[bsize];
int mi_height = mi_size_high[bsize];
for (p = 0; p < MAX_MB_PLANE; p++) {
int tx_col;
int tx_row;
tx_col = mi_col << (MI_SIZE_LOG2 - tx_size_wide_log2[0]);
tx_row = (mi_row & MAX_MIB_MASK) << (MI_SIZE_LOG2 - tx_size_high_log2[0]);
memcpy(xd->above_context[p] + (tx_col >> xd->plane[p].subsampling_x),
ctx->a + num_4x4_blocks_wide * p,
(sizeof(ENTROPY_CONTEXT) * num_4x4_blocks_wide) >>
xd->plane[p].subsampling_x);
memcpy(xd->left_context[p] + (tx_row >> xd->plane[p].subsampling_y),
ctx->l + num_4x4_blocks_high * p,
(sizeof(ENTROPY_CONTEXT) * num_4x4_blocks_high) >>
xd->plane[p].subsampling_y);
}
memcpy(xd->above_seg_context + mi_col, ctx->sa,
sizeof(*xd->above_seg_context) * mi_width);
memcpy(xd->left_seg_context + (mi_row & MAX_MIB_MASK), ctx->sl,
sizeof(xd->left_seg_context[0]) * mi_height);
xd->above_txfm_context = ctx->p_ta;
xd->left_txfm_context = ctx->p_tl;
memcpy(xd->above_txfm_context, ctx->ta,
sizeof(*xd->above_txfm_context) * (mi_width << TX_UNIT_WIDE_LOG2));
memcpy(xd->left_txfm_context, ctx->tl,
sizeof(*xd->left_txfm_context) * (mi_height << TX_UNIT_HIGH_LOG2));
}
static void save_context(const MACROBLOCK *x, RD_SEARCH_MACROBLOCK_CONTEXT *ctx,
int mi_row, int mi_col, BLOCK_SIZE bsize) {
const MACROBLOCKD *xd = &x->e_mbd;
int p;
const int num_4x4_blocks_wide =
block_size_wide[bsize] >> tx_size_wide_log2[0];
const int num_4x4_blocks_high =
block_size_high[bsize] >> tx_size_high_log2[0];
int mi_width = mi_size_wide[bsize];
int mi_height = mi_size_high[bsize];
// buffer the above/left context information of the block in search.
for (p = 0; p < MAX_MB_PLANE; ++p) {
int tx_col;
int tx_row;
tx_col = mi_col << (MI_SIZE_LOG2 - tx_size_wide_log2[0]);
tx_row = (mi_row & MAX_MIB_MASK) << (MI_SIZE_LOG2 - tx_size_high_log2[0]);
memcpy(ctx->a + num_4x4_blocks_wide * p,
xd->above_context[p] + (tx_col >> xd->plane[p].subsampling_x),
(sizeof(ENTROPY_CONTEXT) * num_4x4_blocks_wide) >>
xd->plane[p].subsampling_x);
memcpy(ctx->l + num_4x4_blocks_high * p,
xd->left_context[p] + (tx_row >> xd->plane[p].subsampling_y),
(sizeof(ENTROPY_CONTEXT) * num_4x4_blocks_high) >>
xd->plane[p].subsampling_y);
}
memcpy(ctx->sa, xd->above_seg_context + mi_col,
sizeof(*xd->above_seg_context) * mi_width);
memcpy(ctx->sl, xd->left_seg_context + (mi_row & MAX_MIB_MASK),
sizeof(xd->left_seg_context[0]) * mi_height);
memcpy(ctx->ta, xd->above_txfm_context,
sizeof(*xd->above_txfm_context) * (mi_width << TX_UNIT_WIDE_LOG2));
memcpy(ctx->tl, xd->left_txfm_context,
sizeof(*xd->left_txfm_context) * (mi_height << TX_UNIT_HIGH_LOG2));
ctx->p_ta = xd->above_txfm_context;
ctx->p_tl = xd->left_txfm_context;
}
static void encode_b(const AV1_COMP *const cpi, const TileInfo *const tile,
ThreadData *td, TOKENEXTRA **tp, int mi_row, int mi_col,
RUN_TYPE dry_run, BLOCK_SIZE bsize,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_TYPE partition,
#endif
PICK_MODE_CONTEXT *ctx, int *rate) {
MACROBLOCK *const x = &td->mb;
#if (CONFIG_NCOBMC) | CONFIG_EXT_DELTA_Q | CONFIG_NCOBMC_ADAPT_WEIGHT
MACROBLOCKD *xd = &x->e_mbd;
MB_MODE_INFO *mbmi;
#if CONFIG_NCOBMC
int check_ncobmc;
#endif
#endif
set_offsets(cpi, tile, x, mi_row, mi_col, bsize);
#if CONFIG_EXT_PARTITION_TYPES
x->e_mbd.mi[0]->mbmi.partition = partition;
#endif
update_state(cpi, td, ctx, mi_row, mi_col, bsize, dry_run);
#if (CONFIG_NCOBMC || CONFIG_NCOBMC_ADAPT_WEIGHT)
mbmi = &xd->mi[0]->mbmi;
#if CONFIG_WARPED_MOTION
set_ref_ptrs(&cpi->common, xd, mbmi->ref_frame[0], mbmi->ref_frame[1]);
#endif
#endif
#if (CONFIG_NCOBMC || CONFIG_NCOBMC_ADAPT_WEIGHT)
const MOTION_MODE motion_allowed = motion_mode_allowed(0, xd->global_motion,
#if CONFIG_WARPED_MOTION
xd,
#endif
xd->mi[0]);
#endif // (CONFIG_NCOBMC || CONFIG_NCOBMC_ADAPT_WEIGHT)
#if CONFIG_NCOBMC
check_ncobmc = is_inter_block(mbmi) && motion_allowed >= OBMC_CAUSAL;
if (!dry_run && check_ncobmc) {
av1_check_ncobmc_rd(cpi, x, mi_row, mi_col);
av1_setup_dst_planes(x->e_mbd.plane, bsize,
get_frame_new_buffer(&cpi->common), mi_row, mi_col);
}
#endif
#if CONFIG_LV_MAP
av1_set_coeff_buffer(cpi, x, mi_row, mi_col);
#endif
#if CONFIG_NCOBMC_ADAPT_WEIGHT
if (dry_run == OUTPUT_ENABLED && !frame_is_intra_only(&cpi->common)) {
if (motion_allowed >= NCOBMC_ADAPT_WEIGHT && is_inter_block(mbmi)) {
get_ncobmc_intrpl_pred(cpi, td, mi_row, mi_col, bsize);
av1_check_ncobmc_adapt_weight_rd(cpi, x, mi_row, mi_col);
}
av1_setup_dst_planes(x->e_mbd.plane, bsize,
get_frame_new_buffer(&cpi->common), mi_row, mi_col);
}
#endif // CONFIG_NCOBMC_ADAPT_WEIGHT
encode_superblock(cpi, td, tp, dry_run, mi_row, mi_col, bsize, rate);
#if CONFIG_LV_MAP
if (dry_run == 0)
x->cb_offset += block_size_wide[bsize] * block_size_high[bsize];
#endif
if (!dry_run) {
#if CONFIG_EXT_DELTA_Q
mbmi = &xd->mi[0]->mbmi;
if (bsize == cpi->common.sb_size && mbmi->skip == 1 &&
cpi->common.delta_lf_present_flag) {
#if CONFIG_LOOPFILTER_LEVEL
for (int lf_id = 0; lf_id < FRAME_LF_COUNT; ++lf_id)
mbmi->curr_delta_lf[lf_id] = xd->prev_delta_lf[lf_id];
#endif // CONFIG_LOOPFILTER_LEVEL
mbmi->current_delta_lf_from_base = xd->prev_delta_lf_from_base;
}
#endif
update_stats(&cpi->common, td, mi_row, mi_col);
}
}
static void encode_sb(const AV1_COMP *const cpi, ThreadData *td,
const TileInfo *const tile, TOKENEXTRA **tp, int mi_row,
int mi_col, RUN_TYPE dry_run, BLOCK_SIZE bsize,
PC_TREE *pc_tree, int *rate) {
const AV1_COMMON *const cm = &cpi->common;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
const int hbs = mi_size_wide[bsize] / 2;
#if CONFIG_EXT_PARTITION_TYPES && CONFIG_EXT_PARTITION_TYPES_AB
const int qbs = mi_size_wide[bsize] / 4;
#endif
const int is_partition_root = bsize >= BLOCK_8X8;
const int ctx = is_partition_root
? partition_plane_context(xd, mi_row, mi_col,
#if CONFIG_UNPOISON_PARTITION_CTX
mi_row + hbs < cm->mi_rows,
mi_col + hbs < cm->mi_cols,
#endif
bsize)
: -1;
const PARTITION_TYPE partition = pc_tree->partitioning;
const BLOCK_SIZE subsize = get_subsize(bsize, partition);
#if CONFIG_EXT_PARTITION_TYPES
int quarter_step = mi_size_wide[bsize] / 4;
int i;
#if !CONFIG_EXT_PARTITION_TYPES_AB
BLOCK_SIZE bsize2 = get_subsize(bsize, PARTITION_SPLIT);
#endif // !CONFIG_EXT_PARTITION_TYPES_AB
#endif // CONFIG_EXT_PARTITION_TYPES
if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols) return;
if (!dry_run && ctx >= 0) td->counts->partition[ctx][partition]++;
switch (partition) {
case PARTITION_NONE:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize,
#if CONFIG_EXT_PARTITION_TYPES
partition,
#endif
&pc_tree->none, rate);
break;
case PARTITION_VERT:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize,
#if CONFIG_EXT_PARTITION_TYPES
partition,
#endif
&pc_tree->vertical[0], rate);
if (mi_col + hbs < cm->mi_cols) {
encode_b(cpi, tile, td, tp, mi_row, mi_col + hbs, dry_run, subsize,
#if CONFIG_EXT_PARTITION_TYPES
partition,
#endif
&pc_tree->vertical[1], rate);
}
break;
case PARTITION_HORZ:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize,
#if CONFIG_EXT_PARTITION_TYPES
partition,
#endif
&pc_tree->horizontal[0], rate);
if (mi_row + hbs < cm->mi_rows) {
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col, dry_run, subsize,
#if CONFIG_EXT_PARTITION_TYPES
partition,
#endif
&pc_tree->horizontal[1], rate);
}
break;
case PARTITION_SPLIT:
encode_sb(cpi, td, tile, tp, mi_row, mi_col, dry_run, subsize,
pc_tree->split[0], rate);
encode_sb(cpi, td, tile, tp, mi_row, mi_col + hbs, dry_run, subsize,
pc_tree->split[1], rate);
encode_sb(cpi, td, tile, tp, mi_row + hbs, mi_col, dry_run, subsize,
pc_tree->split[2], rate);
encode_sb(cpi, td, tile, tp, mi_row + hbs, mi_col + hbs, dry_run, subsize,
pc_tree->split[3], rate);
break;
#if CONFIG_EXT_PARTITION_TYPES
#if CONFIG_EXT_PARTITION_TYPES_AB
case PARTITION_HORZ_A:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run,
get_subsize(bsize, PARTITION_HORZ_4), partition,
&pc_tree->horizontala[0], rate);
encode_b(cpi, tile, td, tp, mi_row + qbs, mi_col, dry_run,
get_subsize(bsize, PARTITION_HORZ_4), partition,
&pc_tree->horizontala[1], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col, dry_run, subsize,
partition, &pc_tree->horizontala[2], rate);
break;
case PARTITION_HORZ_B:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize, partition,
&pc_tree->horizontalb[0], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col, dry_run,
get_subsize(bsize, PARTITION_HORZ_4), partition,
&pc_tree->horizontalb[1], rate);
if (mi_row + 3 * qbs < cm->mi_rows)
encode_b(cpi, tile, td, tp, mi_row + 3 * qbs, mi_col, dry_run,
get_subsize(bsize, PARTITION_HORZ_4), partition,
&pc_tree->horizontalb[2], rate);
break;
case PARTITION_VERT_A:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run,
get_subsize(bsize, PARTITION_VERT_4), partition,
&pc_tree->verticala[0], rate);
encode_b(cpi, tile, td, tp, mi_row, mi_col + qbs, dry_run,
get_subsize(bsize, PARTITION_VERT_4), partition,
&pc_tree->verticala[1], rate);
encode_b(cpi, tile, td, tp, mi_row, mi_col + hbs, dry_run, subsize,
partition, &pc_tree->verticala[2], rate);
break;
case PARTITION_VERT_B:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize, partition,
&pc_tree->verticalb[0], rate);
encode_b(cpi, tile, td, tp, mi_row, mi_col + hbs, dry_run,
get_subsize(bsize, PARTITION_VERT_4), partition,
&pc_tree->verticalb[1], rate);
if (mi_col + 3 * qbs < cm->mi_cols)
encode_b(cpi, tile, td, tp, mi_row, mi_col + 3 * qbs, dry_run,
get_subsize(bsize, PARTITION_VERT_4), partition,
&pc_tree->verticalb[2], rate);
break;
#else
case PARTITION_HORZ_A:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, bsize2, partition,
&pc_tree->horizontala[0], rate);
encode_b(cpi, tile, td, tp, mi_row, mi_col + hbs, dry_run, bsize2,
partition, &pc_tree->horizontala[1], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col, dry_run, subsize,
partition, &pc_tree->horizontala[2], rate);
break;
case PARTITION_HORZ_B:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize, partition,
&pc_tree->horizontalb[0], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col, dry_run, bsize2,
partition, &pc_tree->horizontalb[1], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col + hbs, dry_run, bsize2,
partition, &pc_tree->horizontalb[2], rate);
break;
case PARTITION_VERT_A:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, bsize2, partition,
&pc_tree->verticala[0], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col, dry_run, bsize2,
partition, &pc_tree->verticala[1], rate);
encode_b(cpi, tile, td, tp, mi_row, mi_col + hbs, dry_run, subsize,
partition, &pc_tree->verticala[2], rate);
break;
case PARTITION_VERT_B:
encode_b(cpi, tile, td, tp, mi_row, mi_col, dry_run, subsize, partition,
&pc_tree->verticalb[0], rate);
encode_b(cpi, tile, td, tp, mi_row, mi_col + hbs, dry_run, bsize2,
partition, &pc_tree->verticalb[1], rate);
encode_b(cpi, tile, td, tp, mi_row + hbs, mi_col + hbs, dry_run, bsize2,
partition, &pc_tree->verticalb[2], rate);
break;
#endif
case PARTITION_HORZ_4:
for (i = 0; i < 4; ++i) {
int this_mi_row = mi_row + i * quarter_step;
if (i > 0 && this_mi_row >= cm->mi_rows) break;
encode_b(cpi, tile, td, tp, this_mi_row, mi_col, dry_run, subsize,
partition, &pc_tree->horizontal4[i], rate);
}
break;
case PARTITION_VERT_4:
for (i = 0; i < 4; ++i) {
int this_mi_col = mi_col + i * quarter_step;
if (i > 0 && this_mi_col >= cm->mi_cols) break;
encode_b(cpi, tile, td, tp, mi_row, this_mi_col, dry_run, subsize,
partition, &pc_tree->vertical4[i], rate);
}
break;
#endif // CONFIG_EXT_PARTITION_TYPES
default: assert(0 && "Invalid partition type."); break;
}
#if CONFIG_EXT_PARTITION_TYPES
update_ext_partition_context(xd, mi_row, mi_col, subsize, bsize, partition);
#else
if (partition != PARTITION_SPLIT || bsize == BLOCK_8X8)
update_partition_context(xd, mi_row, mi_col, subsize, bsize);
#endif // CONFIG_EXT_PARTITION_TYPES
}
// Check to see if the given partition size is allowed for a specified number
// of mi block rows and columns remaining in the image.
// If not then return the largest allowed partition size
static BLOCK_SIZE find_partition_size(BLOCK_SIZE bsize, int rows_left,
int cols_left, int *bh, int *bw) {
if (rows_left <= 0 || cols_left <= 0) {
return AOMMIN(bsize, BLOCK_8X8);
} else {
for (; bsize > 0; bsize -= 3) {
*bh = mi_size_high[bsize];
*bw = mi_size_wide[bsize];
if ((*bh <= rows_left) && (*bw <= cols_left)) {
break;
}
}
}
return bsize;
}
static void set_partial_sb_partition(const AV1_COMMON *const cm, MODE_INFO *mi,
int bh_in, int bw_in,
int mi_rows_remaining,
int mi_cols_remaining, BLOCK_SIZE bsize,
MODE_INFO **mib) {
int bh = bh_in;
int r, c;
for (r = 0; r < cm->mib_size; r += bh) {
int bw = bw_in;
for (c = 0; c < cm->mib_size; c += bw) {
const int index = r * cm->mi_stride + c;
mib[index] = mi + index;
mib[index]->mbmi.sb_type = find_partition_size(
bsize, mi_rows_remaining - r, mi_cols_remaining - c, &bh, &bw);
}
}
}
// This function attempts to set all mode info entries in a given superblock
// to the same block partition size.
// However, at the bottom and right borders of the image the requested size
// may not be allowed in which case this code attempts to choose the largest
// allowable partition.
static void set_fixed_partitioning(AV1_COMP *cpi, const TileInfo *const tile,
MODE_INFO **mib, int mi_row, int mi_col,
BLOCK_SIZE bsize) {
AV1_COMMON *const cm = &cpi->common;
const int mi_rows_remaining = tile->mi_row_end - mi_row;
const int mi_cols_remaining = tile->mi_col_end - mi_col;
int block_row, block_col;
MODE_INFO *const mi_upper_left = cm->mi + mi_row * cm->mi_stride + mi_col;
int bh = mi_size_high[bsize];
int bw = mi_size_wide[bsize];
assert((mi_rows_remaining > 0) && (mi_cols_remaining > 0));
// Apply the requested partition size to the SB if it is all "in image"
if ((mi_cols_remaining >= cm->mib_size) &&
(mi_rows_remaining >= cm->mib_size)) {
for (block_row = 0; block_row < cm->mib_size; block_row += bh) {
for (block_col = 0; block_col < cm->mib_size; block_col += bw) {
int index = block_row * cm->mi_stride + block_col;
mib[index] = mi_upper_left + index;
mib[index]->mbmi.sb_type = bsize;
}
}
} else {
// Else this is a partial SB.
set_partial_sb_partition(cm, mi_upper_left, bh, bw, mi_rows_remaining,
mi_cols_remaining, bsize, mib);
}
}
static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
TileDataEnc *tile_data, MODE_INFO **mib,
TOKENEXTRA **tp, int mi_row, int mi_col,
BLOCK_SIZE bsize, int *rate, int64_t *dist,
int do_recon, PC_TREE *pc_tree) {
AV1_COMMON *const cm = &cpi->common;
TileInfo *const tile_info = &tile_data->tile_info;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
const int bs = mi_size_wide[bsize];
const int hbs = bs / 2;
int i;
const int pl = (bsize >= BLOCK_8X8)
? partition_plane_context(xd, mi_row, mi_col,
#if CONFIG_UNPOISON_PARTITION_CTX
mi_row + hbs < cm->mi_rows,
mi_col + hbs < cm->mi_cols,
#endif
bsize)
: 0;
const PARTITION_TYPE partition =
(bsize >= BLOCK_8X8) ? get_partition(cm, mi_row, mi_col, bsize)
: PARTITION_NONE;
const BLOCK_SIZE subsize = get_subsize(bsize, partition);
RD_SEARCH_MACROBLOCK_CONTEXT x_ctx;
RD_STATS last_part_rdc, none_rdc, chosen_rdc;
BLOCK_SIZE sub_subsize = BLOCK_4X4;
int splits_below = 0;
BLOCK_SIZE bs_type = mib[0]->mbmi.sb_type;
int do_partition_search = 1;
PICK_MODE_CONTEXT *ctx_none = &pc_tree->none;
if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols) return;
assert(num_4x4_blocks_wide_lookup[bsize] ==
num_4x4_blocks_high_lookup[bsize]);
av1_invalid_rd_stats(&last_part_rdc);
av1_invalid_rd_stats(&none_rdc);
av1_invalid_rd_stats(&chosen_rdc);
pc_tree->partitioning = partition;
xd->above_txfm_context =
cm->above_txfm_context + (mi_col << TX_UNIT_WIDE_LOG2);
xd->left_txfm_context = xd->left_txfm_context_buffer +
((mi_row & MAX_MIB_MASK) << TX_UNIT_HIGH_LOG2);
save_context(x, &x_ctx, mi_row, mi_col, bsize);
if (bsize == BLOCK_16X16 && cpi->vaq_refresh) {
set_offsets(cpi, tile_info, x, mi_row, mi_col, bsize);
x->mb_energy = av1_block_energy(cpi, x, bsize);
}
if (do_partition_search &&
cpi->sf.partition_search_type == SEARCH_PARTITION &&
cpi->sf.adjust_partitioning_from_last_frame) {
// Check if any of the sub blocks are further split.
if (partition == PARTITION_SPLIT && subsize > BLOCK_8X8) {
sub_subsize = get_subsize(subsize, PARTITION_SPLIT);
splits_below = 1;
for (i = 0; i < 4; i++) {
int jj = i >> 1, ii = i & 0x01;
MODE_INFO *this_mi = mib[jj * hbs * cm->mi_stride + ii * hbs];
if (this_mi && this_mi->mbmi.sb_type >= sub_subsize) {
splits_below = 0;
}
}
}
// If partition is not none try none unless each of the 4 splits are split
// even further..
if (partition != PARTITION_NONE && !splits_below &&
mi_row + hbs < cm->mi_rows && mi_col + hbs < cm->mi_cols) {
pc_tree->partitioning = PARTITION_NONE;
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &none_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_NONE,
#endif
bsize, ctx_none, INT64_MAX);
if (none_rdc.rate < INT_MAX) {
none_rdc.rate += x->partition_cost[pl][PARTITION_NONE];
none_rdc.rdcost = RDCOST(x->rdmult, none_rdc.rate, none_rdc.dist);
}
restore_context(x, &x_ctx, mi_row, mi_col, bsize);
mib[0]->mbmi.sb_type = bs_type;
pc_tree->partitioning = partition;
}
}
switch (partition) {
case PARTITION_NONE:
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &last_part_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_NONE,
#endif
bsize, ctx_none, INT64_MAX);
break;
case PARTITION_HORZ:
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &last_part_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_HORZ,
#endif
subsize, &pc_tree->horizontal[0], INT64_MAX);
if (last_part_rdc.rate != INT_MAX && bsize >= BLOCK_8X8 &&
mi_row + hbs < cm->mi_rows) {
RD_STATS tmp_rdc;
PICK_MODE_CONTEXT *ctx_h = &pc_tree->horizontal[0];
av1_init_rd_stats(&tmp_rdc);
update_state(cpi, td, ctx_h, mi_row, mi_col, subsize, 1);
encode_superblock(cpi, td, tp, DRY_RUN_NORMAL, mi_row, mi_col, subsize,
NULL);
rd_pick_sb_modes(cpi, tile_data, x, mi_row + hbs, mi_col, &tmp_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_HORZ,
#endif
subsize, &pc_tree->horizontal[1], INT64_MAX);
if (tmp_rdc.rate == INT_MAX || tmp_rdc.dist == INT64_MAX) {
av1_invalid_rd_stats(&last_part_rdc);
break;
}
last_part_rdc.rate += tmp_rdc.rate;
last_part_rdc.dist += tmp_rdc.dist;
last_part_rdc.rdcost += tmp_rdc.rdcost;
}
break;
case PARTITION_VERT:
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &last_part_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_VERT,
#endif
subsize, &pc_tree->vertical[0], INT64_MAX);
if (last_part_rdc.rate != INT_MAX && bsize >= BLOCK_8X8 &&
mi_col + hbs < cm->mi_cols) {
RD_STATS tmp_rdc;
PICK_MODE_CONTEXT *ctx_v = &pc_tree->vertical[0];
av1_init_rd_stats(&tmp_rdc);
update_state(cpi, td, ctx_v, mi_row, mi_col, subsize, 1);
encode_superblock(cpi, td, tp, DRY_RUN_NORMAL, mi_row, mi_col, subsize,
NULL);
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col + hbs, &tmp_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_VERT,
#endif
subsize, &pc_tree->vertical[bsize > BLOCK_8X8],
INT64_MAX);
if (tmp_rdc.rate == INT_MAX || tmp_rdc.dist == INT64_MAX) {
av1_invalid_rd_stats(&last_part_rdc);
break;
}
last_part_rdc.rate += tmp_rdc.rate;
last_part_rdc.dist += tmp_rdc.dist;
last_part_rdc.rdcost += tmp_rdc.rdcost;
}
break;
case PARTITION_SPLIT:
last_part_rdc.rate = 0;
last_part_rdc.dist = 0;
last_part_rdc.rdcost = 0;
for (i = 0; i < 4; i++) {
int x_idx = (i & 1) * hbs;
int y_idx = (i >> 1) * hbs;
int jj = i >> 1, ii = i & 0x01;
RD_STATS tmp_rdc;
if ((mi_row + y_idx >= cm->mi_rows) || (mi_col + x_idx >= cm->mi_cols))
continue;
av1_init_rd_stats(&tmp_rdc);
rd_use_partition(cpi, td, tile_data,
mib + jj * hbs * cm->mi_stride + ii * hbs, tp,
mi_row + y_idx, mi_col + x_idx, subsize, &tmp_rdc.rate,
&tmp_rdc.dist, i != 3, pc_tree->split[i]);
if (tmp_rdc.rate == INT_MAX || tmp_rdc.dist == INT64_MAX) {
av1_invalid_rd_stats(&last_part_rdc);
break;
}
last_part_rdc.rate += tmp_rdc.rate;
last_part_rdc.dist += tmp_rdc.dist;
}
break;
#if CONFIG_EXT_PARTITION_TYPES
case PARTITION_VERT_A:
case PARTITION_VERT_B:
case PARTITION_HORZ_A:
case PARTITION_HORZ_B:
case PARTITION_HORZ_4:
case PARTITION_VERT_4: assert(0 && "Cannot handle extended partiton types");
#endif // CONFIG_EXT_PARTITION_TYPES
default: assert(0); break;
}
if (last_part_rdc.rate < INT_MAX) {
last_part_rdc.rate += x->partition_cost[pl][partition];
last_part_rdc.rdcost =
RDCOST(x->rdmult, last_part_rdc.rate, last_part_rdc.dist);
}
if (do_partition_search && cpi->sf.adjust_partitioning_from_last_frame &&
cpi->sf.partition_search_type == SEARCH_PARTITION &&
partition != PARTITION_SPLIT && bsize > BLOCK_8X8 &&
(mi_row + bs < cm->mi_rows || mi_row + hbs == cm->mi_rows) &&
(mi_col + bs < cm->mi_cols || mi_col + hbs == cm->mi_cols)) {
BLOCK_SIZE split_subsize = get_subsize(bsize, PARTITION_SPLIT);
chosen_rdc.rate = 0;
chosen_rdc.dist = 0;
restore_context(x, &x_ctx, mi_row, mi_col, bsize);
pc_tree->partitioning = PARTITION_SPLIT;
// Split partition.
for (i = 0; i < 4; i++) {
int x_idx = (i & 1) * hbs;
int y_idx = (i >> 1) * hbs;
RD_STATS tmp_rdc;
if ((mi_row + y_idx >= cm->mi_rows) || (mi_col + x_idx >= cm->mi_cols))
continue;
save_context(x, &x_ctx, mi_row, mi_col, bsize);
pc_tree->split[i]->partitioning = PARTITION_NONE;
rd_pick_sb_modes(cpi, tile_data, x, mi_row + y_idx, mi_col + x_idx,
&tmp_rdc,
#if CONFIG_EXT_PARTITION_TYPES
PARTITION_SPLIT,
#endif
split_subsize, &pc_tree->split[i]->none, INT64_MAX);
restore_context(x, &x_ctx, mi_row, mi_col, bsize);
if (tmp_rdc.rate == INT_MAX || tmp_rdc.dist == INT64_MAX) {
av1_invalid_rd_stats(&chosen_rdc);
break;
}
chosen_rdc.rate += tmp_rdc.rate;
chosen_rdc.dist += tmp_rdc.dist;
if (i != 3)
encode_sb(cpi, td, tile_info, tp, mi_row + y_idx, mi_col + x_idx,
OUTPUT_ENABLED, split_subsize, pc_tree->split[i], NULL);
chosen_rdc.rate += x->partition_cost[pl][PARTITION_NONE];
}
if (chosen_rdc.rate < INT_MAX) {
chosen_rdc.rate += x->partition_cost[pl][PARTITION_SPLIT];
chosen_rdc.rdcost = RDCOST(x->rdmult, chosen_rdc.rate, chosen_rdc.dist);
}
}
// If last_part is better set the partitioning to that.
if (last_part_rdc.rdcost < chosen_rdc.rdcost) {
mib[0]->mbmi.sb_type = bsize;
if (bsize >= BLOCK_8X8) pc_tree->partitioning = partition;
chosen_rdc = last_part_rdc;
}
// If none was better set the partitioning to that.
if (none_rdc.rdcost < chosen_rdc.rdcost) {
if (bsize >= BLOCK_8X8) pc_tree->partitioning = PARTITION_NONE;
chosen_rdc = none_rdc;
}
restore_context(x, &x_ctx, mi_row, mi_col, bsize);
// We must have chosen a partitioning and encoding or we'll fail later on.
// No other opportunities for success.
if (bsize == cm->sb_size)
assert(chosen_rdc.rate < INT_MAX && chosen_rdc.dist < INT64_MAX);
if (do_recon) {
if (bsize == cm->sb_size) {
// NOTE: To get estimate for rate due to the tokens, use:
// int rate_coeffs = 0;
// encode_sb(cpi, td, tile_info, tp, mi_row, mi_col, DRY_RUN_COSTCOEFFS,
// bsize, pc_tree, &rate_coeffs);
encode_sb(cpi, td, tile_info, tp, mi_row, mi_col, OUTPUT_ENABLED, bsize,
pc_tree, NULL);
} else {
encode_sb(cpi, td, tile_info, tp, mi_row, mi_col, DRY_RUN_NORMAL, bsize,
pc_tree, NULL);
}
}
*rate = chosen_rdc.rate;
*dist = chosen_rdc.dist;
}
/* clang-format off */
static const BLOCK_SIZE min_partition_size[BLOCK_SIZES_ALL] = {
BLOCK_2X2, BLOCK_2X2, BLOCK_2X2, // 2x2, 2x4, 4x2
BLOCK_4X4, // 4x4
BLOCK_4X4, BLOCK_4X4, BLOCK_4X4, // 4x8, 8x4, 8x8
BLOCK_4X4, BLOCK_4X4, BLOCK_8X8, // 8x16, 16x8, 16x16
BLOCK_8X8, BLOCK_8X8, BLOCK_16X16, // 16x32, 32x16, 32x32
BLOCK_16X16, BLOCK_16X16, BLOCK_16X16, // 32x64, 64x32, 64x64
#if CONFIG_EXT_PARTITION
BLOCK_16X16, BLOCK_16X16, BLOCK_16X16, // 64x128, 128x64, 128x128
#endif // CONFIG_EXT_PARTITION
BLOCK_4X4, BLOCK_4X4, BLOCK_8X8, // 4x16, 16x4, 8x32
BLOCK_8X8, BLOCK_16X16, BLOCK_16X16, // 32x8, 16x64, 64x16
#if CONFIG_EXT_PARTITION
BLOCK_16X16, BLOCK_16X16 // 32x128, 128x32
#endif // CONFIG_EXT_PARTITION
};
static const BLOCK_SIZE max_partition_size[BLOCK_SIZES_ALL] = {
BLOCK_4X4, BLOCK_4X4, BLOCK_4X4, // 2x2, 2x4, 4x2
BLOCK_8X8, // 4x4
BLOCK_16X16, BLOCK_16X16, BLOCK_16X16, // 4x8, 8x4, 8x8
BLOCK_32X32, BLOCK_32X32, BLOCK_32X32, // 8x16, 16x8, 16x16
BLOCK_64X64, BLOCK_64X64, BLOCK_64X64, // 16x32, 32x16, 32x32
BLOCK_LARGEST, BLOCK_LARGEST, BLOCK_LARGEST, // 32x64, 64x32, 64x64
#if CONFIG_EXT_PARTITION
BLOCK_LARGEST, BLOCK_LARGEST, BLOCK_LARGEST, // 64x128, 128x64, 128x128
#endif // CONFIG_EXT_PARTITION
BLOCK_16X16, BLOCK_16X16, BLOCK_32X32, // 4x16, 16x4, 8x32
BLOCK_32X32, BLOCK_LARGEST, BLOCK_LARGEST, // 32x8, 16x64, 64x16
#if CONFIG_EXT_PARTITION
BLOCK_LARGEST, BLOCK_LARGEST // 32x128, 128x32
#endif // CONFIG_EXT_PARTITION
};
// Next square block size less or equal than current block size.
static const BLOCK_SIZE next_square_size[BLOCK_SIZES_ALL] = {
BLOCK_2X2, BLOCK_2X2, BLOCK_2X2, // 2x2, 2x4, 4x2
BLOCK_4X4, // 4x4
BLOCK_4X4, BLOCK_4X4, BLOCK_8X8, // 4x8, 8x4, 8x8
BLOCK_8X8, BLOCK_8X8, BLOCK_16X16, // 8x16, 16x8, 16x16
BLOCK_16X16, BLOCK_16X16, BLOCK_32X32, // 16x32, 32x16, 32x32
BLOCK_32X32, BLOCK_32X32, BLOCK_64X64, // 32x64, 64x32, 64x64
#if CONFIG_EXT_PARTITION
BLOCK_64X64, BLOCK_64X64, BLOCK_128X128, // 64x128, 128x64, 128x128
#endif // CONFIG_EXT_PARTITION
BLOCK_4X4, BLOCK_4X4, BLOCK_8X8, // 4x16, 16x4, 8x32
BLOCK_8X8, BLOCK_16X16, BLOCK_16X16, // 32x8, 16x64, 64x16
#if CONFIG_EXT_PARTITION
BLOCK_32X32, BLOCK_32X32 // 32x128, 128x32
#endif // CONFIG_EXT_PARTITION
};
/* clang-format on */
// Look at all the mode_info entries for blocks that are part of this
// partition and find the min and max values for sb_type.
// At the moment this is designed to work on a superblock but could be
// adjusted to use a size parameter.
//
// The min and max are assumed to have been initialized prior to calling this
// function so repeat calls can accumulate a min and max of more than one
// superblock.
static void get_sb_partition_size_range(const AV1_COMMON *const cm,
MACROBLOCKD *xd, MODE_INFO **mib,
BLOCK_SIZE *min_block_size,
BLOCK_SIZE *max_block_size) {
int i, j;
int index = 0;
// Check the sb_type for each block that belongs to this region.
for (i = 0; i < cm->mib_size; ++i) {
for (j = 0; j < cm->mib_size; ++j) {
MODE_INFO *mi = mib[index + j];
BLOCK_SIZE sb_type = mi ? mi->mbmi.sb_type : BLOCK_4X4;
*min_block_size = AOMMIN(*min_block_size, sb_type);
*max_block_size = AOMMAX(*max_block_size, sb_type);
}
index += xd->mi_stride;
}
}
// Look at neighboring blocks and set a min and max partition size based on
// what they chose.
static void rd_auto_partition_range(AV1_COMP *cpi, const TileInfo *const tile,
MACROBLOCKD *const xd, int mi_row,
int mi_col, BLOCK_SIZE *min_block_size,
BLOCK_SIZE *max_block_size) {
AV1_COMMON *const cm = &cpi->common;
MODE_INFO **mi = xd->mi;
const int left_in_image = xd->left_available && mi[-1];
const int above_in_image = xd->up_available && mi[-xd->mi_stride];
const int mi_rows_remaining = tile->mi_row_end - mi_row;
const int mi_cols_remaining = tile->mi_col_end - mi_col;
int bh, bw;
BLOCK_SIZE min_size = BLOCK_4X4;
BLOCK_SIZE max_size = BLOCK_LARGEST;
// Trap case where we do not have a prediction.
if (left_in_image || above_in_image || cm->frame_type != KEY_FRAME) {
// Default "min to max" and "max to min"
min_size = BLOCK_LARGEST;
max_size = BLOCK_4X4;
// NOTE: each call to get_sb_partition_size_range() uses the previous
// passed in values for min and max as a starting point.
// Find the min and max partition used in previous frame at this location
if (cm->frame_type != KEY_FRAME) {
MODE_INFO **prev_mi =
&cm->prev_mi_grid_visible[mi_row * xd->mi_stride + mi_col];
get_sb_partition_size_range(cm, xd, prev_mi, &min_size, &max_size);
}
// Find the min and max partition sizes used in the left superblock
if (left_in_image) {
MODE_INFO **left_sb_mi = &mi[-cm->mib_size];
get_sb_partition_size_range(cm, xd, left_sb_mi, &min_size, &max_size);
}
// Find the min and max partition sizes used in the above suprblock.
if (above_in_image) {
MODE_INFO **above_sb_mi = &mi[-xd->mi_stride * cm->mib_size];
get_sb_partition_size_range(cm, xd, above_sb_mi, &min_size, &max_size);
}
// Adjust observed min and max for "relaxed" auto partition case.
if (cpi->sf.auto_min_max_partition_size == RELAXED_NEIGHBORING_MIN_MAX) {
min_size = min_partition_size[min_size];
max_size = max_partition_size[max_size];
}
}
// Check border cases where max and min from neighbors may not be legal.
max_size = find_partition_size(max_size, mi_rows_remaining, mi_cols_remaining,
&bh, &bw);
min_size = AOMMIN(min_size, max_size);
// Test for blocks at the edge of the active image.
// This may be the actual edge of the image or where there are formatting
// bars.
if (av1_active_edge_sb(cpi, mi_row, mi_col)) {
min_size = BLOCK_4X4;
} else {
min_size = AOMMIN(cpi->sf.rd_auto_partition_min_limit, min_size);
}
// When use_square_partition_only is true, make sure at least one square
// partition is allowed by selecting the next smaller square size as
// *min_block_size.
if (cpi->sf.use_square_partition_only) {
min_size = AOMMIN(min_size, next_square_size[max_size]);
}
*min_block_size = AOMMIN(min_size, cm->sb_size);
*max_block_size = AOMMIN(max_size, cm->sb_size);
}
// TODO(jingning) refactor functions setting partition search range
static void set_partition_range(const AV1_COMMON *const cm,
const MACROBLOCKD *const xd, int mi_row,
int mi_col, BLOCK_SIZE bsize,
BLOCK_SIZE *const min_bs,
BLOCK_SIZE *const max_bs) {
const int mi_width = mi_size_wide[bsize];
const int mi_height = mi_size_high[bsize];
int idx, idy;
const int idx_str = cm->mi_stride * mi_row + mi_col;
MODE_INFO **const prev_mi = &cm->prev_mi_grid_visible[idx_str];
BLOCK_SIZE min_size = cm->sb_size; // default values
BLOCK_SIZE max_size = BLOCK_4X4;
if (prev_mi) {
for (idy = 0; idy < mi_height; ++idy) {
for (idx = 0; idx < mi_width; ++idx) {
const MODE_INFO *const mi = prev_mi[idy * cm->mi_stride + idx];
const BLOCK_SIZE bs = mi ? mi->mbmi.sb_type : bsize;
min_size = AOMMIN(min_size, bs);
max_size = AOMMAX(max_size, bs);
}
}
}
if (xd->left_available) {
for (idy = 0; idy < mi_height; ++idy) {
const MODE_INFO *const mi = xd->mi[idy * cm->mi_stride - 1];
const BLOCK_SIZE bs = mi ? mi->mbmi.sb_type : bsize;
min_size = AOMMIN(min_size, bs);
max_size = AOMMAX(max_size, bs);
}
}
if (xd->up_available) {
for (idx = 0; idx < mi_width; ++idx) {
const MODE_INFO *const mi = xd->mi[idx - cm->mi_stride];
const BLOCK_SIZE bs = mi ? mi->mbmi.sb_type : bsize;
min_size = AOMMIN(min_size, bs);
max_size = AOMMAX(max_size, bs);
}
}
if (min_size == max_size) {
min_size = min_partition_size[min_size];
max_size = max_partition_size[max_size];
}
*min_bs = AOMMIN(min_size, cm->sb_size);
*max_bs = AOMMIN(max_size, cm->sb_size);
}
static INLINE void store_pred_mv(MACROBLOCK *x, PICK_MODE_CONTEXT *ctx) {
memcpy(ctx->pred_mv, x->pred_mv, sizeof(x->pred_mv));
}
static INLINE void load_pred_mv(MACROBLOCK *x, PICK_MODE_CONTEXT *ctx) {
memcpy(x->pred_mv, ctx->pred_mv, sizeof(x->pred_mv));
}
#if CONFIG_FP_MB_STATS
const int qindex_skip_threshold_lookup[BLOCK_SIZES] = {
0, 10, 10, 30, 40, 40, 60, 80, 80, 90, 100, 100, 120,
#if CONFIG_EXT_PARTITION
// TODO(debargha): What are the correct numbers here?
130, 130, 150
#endif // CONFIG_EXT_PARTITION
};
const int qindex_split_threshold_lookup[BLOCK_SIZES] = {
0, 3, 3, 7, 15, 15, 30, 40, 40, 60, 80, 80, 120,
#if CONFIG_EXT_PARTITION
// TODO(debargha): What are the correct numbers here?
160, 160, 240
#endif // CONFIG_EXT_PARTITION
};
const int complexity_16x16_blocks_threshold[BLOCK_SIZES] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 6,
#if CONFIG_EXT_PARTITION
// TODO(debargha): What are the correct numbers here?
8, 8, 10
#endif // CONFIG_EXT_PARTITION
};
typedef enum {
MV_ZERO = 0,
MV_LEFT = 1,
MV_UP = 2,
MV_RIGHT = 3,
MV_DOWN = 4,
MV_INVALID
} MOTION_DIRECTION;
static INLINE MOTION_DIRECTION get_motion_direction_fp(uint8_t fp_byte) {
if (fp_byte & FPMB_MOTION_ZERO_MASK) {
return MV_ZERO;
} else if (fp_byte & FPMB_MOTION_LEFT_MASK) {
return MV_LEFT;
} else if (fp_byte & FPMB_MOTION_RIGHT_MASK) {
return MV_RIGHT;
} else if (fp_byte & FPMB_MOTION_UP_MASK) {
return MV_UP;
} else {
return MV_DOWN;
}
}
static INLINE int get_motion_inconsistency(MOTION_DIRECTION this_mv,
MOTION_DIRECTION that_mv) {
if (this_mv == that_mv) {
return 0;
} else {
return abs(this_mv - that_mv) == 2 ? 2 : 1;
}
}
#endif
#if CONFIG_EXT_PARTITION_TYPES
// Try searching for an encoding for the given subblock. Returns zero if the
// rdcost is already too high (to tell the caller not to bother searching for
// encodings of further subblocks)
static int rd_try_subblock(const AV1_COMP *const cpi, ThreadData *td,
TileDataEnc *tile_data, TOKENEXTRA **tp,
int is_first, int is_last, int mi_row, int mi_col,
BLOCK_SIZE subsize, RD_STATS *best_rdc,
RD_STATS *sum_rdc, RD_STATS *this_rdc,
PARTITION_TYPE partition,
PICK_MODE_CONTEXT *prev_ctx,
PICK_MODE_CONTEXT *this_ctx) {
#define RTS_X_RATE_NOCOEF_ARG
#define RTS_MAX_RDCOST best_rdc->rdcost
MACROBLOCK *const x = &td->mb;
if (cpi->sf.adaptive_motion_search) load_pred_mv(x, prev_ctx);
// On the first time around, write the rd stats straight to sum_rdc. Also, we
// should treat sum_rdc as containing zeros (even if it doesn't) to avoid
// having to zero it at the start.
if (is_first) this_rdc = sum_rdc;
const int64_t spent_rdcost = is_first ? 0 : sum_rdc->rdcost;
const int64_t rdcost_remaining = best_rdc->rdcost - spent_rdcost;
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, this_rdc,
RTS_X_RATE_NOCOEF_ARG partition, subsize, this_ctx,
rdcost_remaining);
if (!is_first) {
if (this_rdc->rate == INT_MAX) {
sum_rdc->rdcost = INT64_MAX;
} else {
sum_rdc->rate += this_rdc->rate;
sum_rdc->dist += this_rdc->dist;
sum_rdc->rdcost += this_rdc->rdcost;
}
}
if (sum_rdc->rdcost >= RTS_MAX_RDCOST) return 0;
if (!is_last) {
update_state(cpi, td, this_ctx, mi_row, mi_col, subsize, 1);
encode_superblock(cpi, td, tp, DRY_RUN_NORMAL, mi_row, mi_col, subsize,
NULL);
}
return 1;
#undef RTS_X_RATE_NOCOEF_ARG
#undef RTS_MAX_RDCOST
}
static void rd_test_partition3(const AV1_COMP *const cpi, ThreadData *td,
TileDataEnc *tile_data, TOKENEXTRA **tp,
PC_TREE *pc_tree, RD_STATS *best_rdc,
PICK_MODE_CONTEXT ctxs[3],
PICK_MODE_CONTEXT *ctx, int mi_row, int mi_col,
BLOCK_SIZE bsize, PARTITION_TYPE partition,
int mi_row0, int mi_col0, BLOCK_SIZE subsize0,
int mi_row1, int mi_col1, BLOCK_SIZE subsize1,
int mi_row2, int mi_col2, BLOCK_SIZE subsize2) {
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
RD_STATS sum_rdc, this_rdc;
#if CONFIG_UNPOISON_PARTITION_CTX
const AV1_COMMON *const cm = &cpi->common;
const int hbs = mi_size_wide[bsize] / 2;
const int has_rows = mi_row + hbs < cm->mi_rows;
const int has_cols = mi_col + hbs < cm->mi_cols;
#endif // CONFIG_UNPOISON_PARTITION_CTX
#if CONFIG_EXT_PARTITION_TYPES_AB
const AV1_COMMON *const cm = &cpi->common;
#endif
#define RTP_STX_TRY_ARGS
if (!rd_try_subblock(cpi, td, tile_data, tp, 1, 0, mi_row0, mi_col0, subsize0,
best_rdc, &sum_rdc, &this_rdc,
RTP_STX_TRY_ARGS partition, ctx, &ctxs[0]))
return;
if (!rd_try_subblock(cpi, td, tile_data, tp, 0, 0, mi_row1, mi_col1, subsize1,
best_rdc, &sum_rdc, &this_rdc,
RTP_STX_TRY_ARGS partition, &ctxs[0], &ctxs[1]))
return;