aom: update libaom to 0ec86ac7ae1e32a7e70410fa4972a655ec3670a4

This commit is contained in:
Roy Tam 2019-02-22 21:52:01 +08:00
commit eb361970c5
438 changed files with 52661 additions and 21905 deletions

View file

@ -93,7 +93,7 @@ void av1_setup_in_frame_q_adj(AV1_COMP *cpi) {
if (segment == DEFAULT_AQ2_SEG) continue;
qindex_delta = av1_compute_qdelta_by_rate(
&cpi->rc, cm->frame_type, cm->base_qindex,
&cpi->rc, cm->current_frame.frame_type, cm->base_qindex,
aq_c_q_adj_factor[aq_strength][segment], cm->seq_params.bit_depth);
// For AQ complexity mode, we dont allow Q0 in a segment if the base
@ -148,7 +148,7 @@ void av1_caq_select_segment(const AV1_COMP *cpi, MACROBLOCK *mb, BLOCK_SIZE bs,
? AOMMAX(exp(cpi->twopass.mb_av_energy), MIN_DEFAULT_LV_THRESH)
: DEFAULT_LV_THRESH;
av1_setup_src_planes(mb, cpi->source, mi_row, mi_col, num_planes);
av1_setup_src_planes(mb, cpi->source, mi_row, mi_col, num_planes, bs);
logvar = av1_log_block_var(cpi, mb, bs);
segment = AQ_C_SEGMENTS - 1; // Just in case no break out below.

View file

@ -140,8 +140,8 @@ static int compute_deltaq(const AV1_COMP *cpi, int q, double rate_factor) {
const CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
const RATE_CONTROL *const rc = &cpi->rc;
int deltaq =
av1_compute_qdelta_by_rate(rc, cpi->common.frame_type, q, rate_factor,
cpi->common.seq_params.bit_depth);
av1_compute_qdelta_by_rate(rc, cpi->common.current_frame.frame_type, q,
rate_factor, cpi->common.seq_params.bit_depth);
if ((-deltaq) > cr->max_qdelta_perc * q / 100) {
deltaq = -cr->max_qdelta_perc * q / 100;
}
@ -166,15 +166,15 @@ int av1_cyclic_refresh_estimate_bits_at_q(const AV1_COMP *cpi,
// Take segment weighted average for estimated bits.
estimated_bits =
(int)((1.0 - weight_segment1 - weight_segment2) *
av1_estimate_bits_at_q(cm->frame_type, cm->base_qindex, mbs,
correction_factor,
av1_estimate_bits_at_q(cm->current_frame.frame_type,
cm->base_qindex, mbs, correction_factor,
cm->seq_params.bit_depth) +
weight_segment1 * av1_estimate_bits_at_q(
cm->frame_type,
cm->current_frame.frame_type,
cm->base_qindex + cr->qindex_delta[1], mbs,
correction_factor, cm->seq_params.bit_depth) +
weight_segment2 * av1_estimate_bits_at_q(
cm->frame_type,
cm->current_frame.frame_type,
cm->base_qindex + cr->qindex_delta[2], mbs,
correction_factor, cm->seq_params.bit_depth));
return estimated_bits;
@ -203,10 +203,11 @@ int av1_cyclic_refresh_rc_bits_per_mb(const AV1_COMP *cpi, int i,
// Take segment weighted average for bits per mb.
bits_per_mb =
(int)((1.0 - weight_segment) *
av1_rc_bits_per_mb(cm->frame_type, i, correction_factor,
av1_rc_bits_per_mb(cm->current_frame.frame_type, i,
correction_factor,
cm->seq_params.bit_depth) +
weight_segment * av1_rc_bits_per_mb(cm->frame_type, i + deltaq,
correction_factor,
weight_segment * av1_rc_bits_per_mb(cm->current_frame.frame_type,
i + deltaq, correction_factor,
cm->seq_params.bit_depth));
return bits_per_mb;
}
@ -496,14 +497,14 @@ void av1_cyclic_refresh_setup(AV1_COMP *const cpi) {
av1_disable_segmentation(seg);
return;
}
if (cm->current_video_frame == 0) cr->low_content_avg = 0.0;
if (cm->current_frame.frame_number == 0) cr->low_content_avg = 0.0;
// Don't apply refresh on key frame or enhancement layer frames.
if (!apply_cyclic_refresh || cm->frame_type == KEY_FRAME) {
if (!apply_cyclic_refresh || cm->current_frame.frame_type == KEY_FRAME) {
// Set segmentation map to 0 and disable.
unsigned char *const seg_map = cpi->segmentation_map;
memset(seg_map, 0, cm->mi_rows * cm->mi_cols);
av1_disable_segmentation(&cm->seg);
if (cm->frame_type == KEY_FRAME) {
if (cm->current_frame.frame_type == KEY_FRAME) {
memset(cr->last_coded_q_map, MAXQ,
cm->mi_rows * cm->mi_cols * sizeof(*cr->last_coded_q_map));
cr->sb_index = 0;

View file

@ -77,8 +77,8 @@ void av1_vaq_frame_setup(AV1_COMP *cpi) {
// Set up avg segment id to be 1.0 and adjust the other segments around
// it.
int qindex_delta = av1_compute_qdelta_by_rate(
&cpi->rc, cm->frame_type, cm->base_qindex, rate_ratio[i] / avg_ratio,
cm->seq_params.bit_depth);
&cpi->rc, cm->current_frame.frame_type, cm->base_qindex,
rate_ratio[i] / avg_ratio, cm->seq_params.bit_depth);
// We don't allow qindex 0 in a segment if the base value is not 0.
// Q index 0 (lossless) implies 4x4 encoding only and in AQ mode a segment
@ -121,7 +121,7 @@ int av1_log_block_var(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bs) {
for (i = 0; i < bh; i += 4) {
for (j = 0; j < bw; j += 4) {
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
var +=
log(1.0 + cpi->fn_ptr[BLOCK_4X4].vf(
x->plane[0].src.buf + i * x->plane[0].src.stride + j,
@ -147,13 +147,13 @@ int av1_log_block_var(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bs) {
#define DEFAULT_E_MIDPOINT 10.0
unsigned int haar_ac_energy(MACROBLOCK *x, BLOCK_SIZE bs) {
static unsigned int haar_ac_energy(MACROBLOCK *x, BLOCK_SIZE bs) {
MACROBLOCKD *xd = &x->e_mbd;
int stride = x->plane[0].src.stride;
uint8_t *buf = x->plane[0].src.buf;
const int bw = MI_SIZE * mi_size_wide[bs];
const int bh = MI_SIZE * mi_size_high[bs];
int hbd = xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH;
const int hbd = is_cur_buf_hbd(xd);
int var = 0;
for (int r = 0; r < bh; r += 8)
@ -192,8 +192,8 @@ int av1_compute_deltaq_from_energy_level(const AV1_COMP *const cpi,
rate_level = block_var_level;
}
int qindex_delta = av1_compute_qdelta_by_rate(
&cpi->rc, cm->frame_type, cm->base_qindex, deltaq_rate_ratio[rate_level],
cm->seq_params.bit_depth);
&cpi->rc, cm->current_frame.frame_type, cm->base_qindex,
deltaq_rate_ratio[rate_level], cm->seq_params.bit_depth);
if ((cm->base_qindex != 0) && ((cm->base_qindex + qindex_delta) == 0)) {
qindex_delta = -cm->base_qindex + 1;

View file

@ -364,8 +364,6 @@ static const int8_t fadst4_range_mult2[7] = { 0, 2, 4, 3, 3, 3, 3 };
static const int8_t fadst8_range_mult2[8] = { 0, 0, 1, 3, 3, 5, 5, 5 };
static const int8_t fadst16_range_mult2[10] = { 0, 0, 1, 3, 3, 5, 5, 7, 7, 7 };
static const int8_t max_fwd_range_mult2_col[5] = { 3, 5, 7, 9, 11 };
static const int8_t fidtx4_range_mult2[1] = { 1 };
static const int8_t fidtx8_range_mult2[1] = { 2 };
static const int8_t fidtx16_range_mult2[1] = { 3 };
@ -389,14 +387,12 @@ const int8_t *fwd_txfm_range_mult2_list[TXFM_TYPES] = {
};
static INLINE void set_fwd_txfm_non_scale_range(TXFM_2D_FLIP_CFG *cfg) {
const int txh_idx = get_txh_idx(cfg->tx_size);
av1_zero(cfg->stage_range_col);
av1_zero(cfg->stage_range_row);
const int8_t *range_mult2_col = fwd_txfm_range_mult2_list[cfg->txfm_type_col];
if (cfg->txfm_type_col != TXFM_TYPE_INVALID) {
int stage_num_col = cfg->stage_num_col;
const int8_t *range_mult2_col =
fwd_txfm_range_mult2_list[cfg->txfm_type_col];
for (int i = 0; i < stage_num_col; ++i)
cfg->stage_range_col[i] = (range_mult2_col[i] + 1) >> 1;
}
@ -405,9 +401,11 @@ static INLINE void set_fwd_txfm_non_scale_range(TXFM_2D_FLIP_CFG *cfg) {
int stage_num_row = cfg->stage_num_row;
const int8_t *range_mult2_row =
fwd_txfm_range_mult2_list[cfg->txfm_type_row];
for (int i = 0; i < stage_num_row; ++i)
for (int i = 0; i < stage_num_row; ++i) {
cfg->stage_range_row[i] =
(max_fwd_range_mult2_col[txh_idx] + range_mult2_row[i] + 1) >> 1;
(range_mult2_col[cfg->stage_num_col - 1] + range_mult2_row[i] + 1) >>
1;
}
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <assert.h>
#include "av1/encoder/encoder.h"
#include "av1/encoder/ethread.h"
#include "av1/encoder/av1_multi_thread.h"
void av1_row_mt_mem_alloc(AV1_COMP *cpi, int max_sb_rows) {
struct AV1Common *cm = &cpi->common;
MultiThreadHandle *multi_thread_ctxt = &cpi->multi_thread_ctxt;
int tile_row, tile_col;
const int tile_cols = cm->tile_cols;
const int tile_rows = cm->tile_rows;
multi_thread_ctxt->allocated_tile_cols = tile_cols;
multi_thread_ctxt->allocated_tile_rows = tile_rows;
multi_thread_ctxt->allocated_sb_rows = max_sb_rows;
// Allocate memory for row based multi-threading
for (tile_row = 0; tile_row < multi_thread_ctxt->allocated_tile_rows;
tile_row++) {
for (tile_col = 0; tile_col < multi_thread_ctxt->allocated_tile_cols;
tile_col++) {
TileDataEnc *this_tile =
&cpi->tile_data[tile_row * multi_thread_ctxt->allocated_tile_cols +
tile_col];
av1_row_mt_sync_mem_alloc(&this_tile->row_mt_sync, cm, max_sb_rows);
if (cpi->oxcf.cdf_update_mode)
CHECK_MEM_ERROR(
cm, this_tile->row_ctx,
(FRAME_CONTEXT *)aom_memalign(
16,
AOMMAX(1, (av1_get_sb_cols_in_tile(cm, this_tile->tile_info) -
1)) *
sizeof(*this_tile->row_ctx)));
}
}
}
void av1_row_mt_mem_dealloc(AV1_COMP *cpi) {
MultiThreadHandle *multi_thread_ctxt = &cpi->multi_thread_ctxt;
int tile_col;
int tile_row;
// Free row based multi-threading sync memory
for (tile_row = 0; tile_row < multi_thread_ctxt->allocated_tile_rows;
tile_row++) {
for (tile_col = 0; tile_col < multi_thread_ctxt->allocated_tile_cols;
tile_col++) {
TileDataEnc *this_tile =
&cpi->tile_data[tile_row * multi_thread_ctxt->allocated_tile_cols +
tile_col];
av1_row_mt_sync_mem_dealloc(&this_tile->row_mt_sync);
if (cpi->oxcf.cdf_update_mode) aom_free(this_tile->row_ctx);
}
}
multi_thread_ctxt->allocated_sb_rows = 0;
multi_thread_ctxt->allocated_tile_cols = 0;
multi_thread_ctxt->allocated_tile_rows = 0;
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2018, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#ifndef AV1_ENCODER_AV1_MULTI_THREAD_H
#define AV1_ENCODER_AV1_MULTI_THREAD_H
#include "av1/encoder/encoder.h"
void av1_row_mt_mem_alloc(AV1_COMP *cpi, int max_sb_rows);
void av1_row_mt_mem_dealloc(AV1_COMP *cpi);
#endif // AV1_ENCODER_AV1_MULTI_THREAD_H

View file

@ -41,47 +41,37 @@ static void quantize_fp_helper_c(
const int16_t *scan, const int16_t *iscan, const qm_val_t *qm_ptr,
const qm_val_t *iqm_ptr, int log_scale) {
int i, eob = -1;
const int rounding[2] = { ROUND_POWER_OF_TWO(round_ptr[0], log_scale),
ROUND_POWER_OF_TWO(round_ptr[1], log_scale) };
// TODO(jingning) Decide the need of these arguments after the
// quantization process is completed.
(void)zbin_ptr;
(void)quant_shift_ptr;
(void)iscan;
memset(qcoeff_ptr, 0, n_coeffs * sizeof(*qcoeff_ptr));
memset(dqcoeff_ptr, 0, n_coeffs * sizeof(*dqcoeff_ptr));
if (qm_ptr == NULL && iqm_ptr == NULL) {
const int rounding0 = ROUND_POWER_OF_TWO(round_ptr[0], log_scale);
{ // rc == 0
const int coeff = coeff_ptr[0];
for (i = 0; i < n_coeffs; i++) {
const int rc = scan[i];
const int32_t thresh = (int32_t)(dequant_ptr[rc != 0]);
const int coeff = coeff_ptr[rc];
const int coeff_sign = (coeff >> 31);
int64_t abs_coeff = (coeff ^ coeff_sign) - coeff_sign;
if ((abs_coeff << (1 + log_scale)) >= (int32_t)(dequant_ptr[0])) {
abs_coeff = clamp64(abs_coeff + rounding0, INT16_MIN, INT16_MAX);
const int tmp32 = (int)((abs_coeff * quant_ptr[0]) >> (16 - log_scale));
int tmp32 = 0;
if ((abs_coeff << (1 + log_scale)) >= thresh) {
abs_coeff =
clamp64(abs_coeff + rounding[rc != 0], INT16_MIN, INT16_MAX);
tmp32 = (int)((abs_coeff * quant_ptr[rc != 0]) >> (16 - log_scale));
if (tmp32) {
qcoeff_ptr[0] = (tmp32 ^ coeff_sign) - coeff_sign;
const tran_low_t abs_dqcoeff = (tmp32 * dequant_ptr[0]) >> log_scale;
dqcoeff_ptr[0] = (abs_dqcoeff ^ coeff_sign) - coeff_sign;
eob = 0;
}
}
}
const int rounding1 = ROUND_POWER_OF_TWO(round_ptr[1], log_scale);
const int32_t thresh1 = (int32_t)(dequant_ptr[1]);
for (i = 1; i < n_coeffs; i++) {
const int coeff = coeff_ptr[i];
const int coeff_sign = (coeff >> 31);
int64_t abs_coeff = (coeff ^ coeff_sign) - coeff_sign;
if ((abs_coeff << (1 + log_scale)) >= thresh1) {
abs_coeff = clamp64(abs_coeff + rounding1, INT16_MIN, INT16_MAX);
const int tmp32 = (int)((abs_coeff * quant_ptr[1]) >> (16 - log_scale));
if (tmp32) {
qcoeff_ptr[i] = (tmp32 ^ coeff_sign) - coeff_sign;
const tran_low_t abs_dqcoeff = (tmp32 * dequant_ptr[1]) >> log_scale;
dqcoeff_ptr[i] = (abs_dqcoeff ^ coeff_sign) - coeff_sign;
eob = AOMMAX(iscan[i], eob);
qcoeff_ptr[rc] = (tmp32 ^ coeff_sign) - coeff_sign;
const tran_low_t abs_dqcoeff =
(tmp32 * dequant_ptr[rc != 0]) >> log_scale;
dqcoeff_ptr[rc] = (abs_dqcoeff ^ coeff_sign) - coeff_sign;
}
}
if (tmp32) eob = i;
}
} else {
// Quantization pass: All coefficients with index >= zero_flag are
@ -99,7 +89,7 @@ static void quantize_fp_helper_c(
int tmp32 = 0;
if (abs_coeff * wt >=
(dequant_ptr[rc != 0] << (AOM_QM_BITS - (1 + log_scale)))) {
abs_coeff += ROUND_POWER_OF_TWO(round_ptr[rc != 0], log_scale);
abs_coeff += rounding[rc != 0];
abs_coeff = clamp64(abs_coeff, INT16_MIN, INT16_MAX);
tmp32 = (int)((abs_coeff * wt * quant_ptr[rc != 0]) >>
(16 - log_scale + AOM_QM_BITS));
@ -275,32 +265,65 @@ void av1_quantize_b_facade(const tran_low_t *coeff_ptr, intptr_t n_coeffs,
const SCAN_ORDER *sc, const QUANT_PARAM *qparam) {
const qm_val_t *qm_ptr = qparam->qmatrix;
const qm_val_t *iqm_ptr = qparam->iqmatrix;
if (qm_ptr != NULL && iqm_ptr != NULL) {
quantize_b_helper_c(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan, qm_ptr, iqm_ptr, qparam->log_scale);
if (qparam->use_quant_b_adapt) {
// TODO(sarahparker) These quantize_b optimizations need SIMD
// implementations
if (qm_ptr != NULL && iqm_ptr != NULL) {
quantize_b_adaptive_helper_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX, eob_ptr,
sc->scan, sc->iscan, qm_ptr, iqm_ptr, qparam->log_scale);
} else {
switch (qparam->log_scale) {
case 0:
aom_quantize_b_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
case 1:
aom_quantize_b_32x32_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
case 2:
aom_quantize_b_64x64_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
default: assert(0);
}
}
} else {
switch (qparam->log_scale) {
case 0:
aom_quantize_b(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
break;
case 1:
aom_quantize_b_32x32(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
break;
case 2:
aom_quantize_b_64x64(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
break;
default: assert(0);
if (qm_ptr != NULL && iqm_ptr != NULL) {
quantize_b_helper_c(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan, qm_ptr, iqm_ptr, qparam->log_scale);
} else {
switch (qparam->log_scale) {
case 0:
aom_quantize_b(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
break;
case 1:
aom_quantize_b_32x32(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
break;
case 2:
aom_quantize_b_64x64(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
break;
default: assert(0);
}
}
}
}
@ -391,41 +414,81 @@ void av1_highbd_quantize_b_facade(const tran_low_t *coeff_ptr,
const QUANT_PARAM *qparam) {
const qm_val_t *qm_ptr = qparam->qmatrix;
const qm_val_t *iqm_ptr = qparam->iqmatrix;
if (qm_ptr != NULL && iqm_ptr != NULL) {
highbd_quantize_b_helper_c(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan, qm_ptr, iqm_ptr, qparam->log_scale);
if (qparam->use_quant_b_adapt) {
if (qm_ptr != NULL && iqm_ptr != NULL) {
highbd_quantize_b_adaptive_helper_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX, eob_ptr,
sc->scan, sc->iscan, qm_ptr, iqm_ptr, qparam->log_scale);
} else {
switch (qparam->log_scale) {
case 0:
if (LIKELY(n_coeffs >= 8)) {
aom_highbd_quantize_b_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
} else {
// TODO(luoyi): Need SIMD (e.g. sse2) for smaller block size
// quantization
aom_highbd_quantize_b_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
}
break;
case 1:
aom_highbd_quantize_b_32x32_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
case 2:
aom_highbd_quantize_b_64x64_adaptive_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
default: assert(0);
}
}
} else {
switch (qparam->log_scale) {
case 0:
if (LIKELY(n_coeffs >= 8)) {
aom_highbd_quantize_b(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan);
} else {
// TODO(luoyi): Need SIMD (e.g. sse2) for smaller block size
// quantization
aom_highbd_quantize_b_c(coeff_ptr, n_coeffs, p->zbin_QTX,
if (qm_ptr != NULL && iqm_ptr != NULL) {
highbd_quantize_b_helper_c(coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX,
p->quant_QTX, p->quant_shift_QTX, qcoeff_ptr,
dqcoeff_ptr, p->dequant_QTX, eob_ptr, sc->scan,
sc->iscan, qm_ptr, iqm_ptr, qparam->log_scale);
} else {
switch (qparam->log_scale) {
case 0:
if (LIKELY(n_coeffs >= 8)) {
aom_highbd_quantize_b(coeff_ptr, n_coeffs, p->zbin_QTX,
p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr,
p->dequant_QTX, eob_ptr, sc->scan, sc->iscan);
}
break;
case 1:
aom_highbd_quantize_b_32x32(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
case 2:
aom_highbd_quantize_b_64x64(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
default: assert(0);
} else {
// TODO(luoyi): Need SIMD (e.g. sse2) for smaller block size
// quantization
aom_highbd_quantize_b_c(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
}
break;
case 1:
aom_highbd_quantize_b_32x32(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
case 2:
aom_highbd_quantize_b_64x64(
coeff_ptr, n_coeffs, p->zbin_QTX, p->round_QTX, p->quant_QTX,
p->quant_shift_QTX, qcoeff_ptr, dqcoeff_ptr, p->dequant_QTX,
eob_ptr, sc->scan, sc->iscan);
break;
default: assert(0);
}
}
}
}
@ -697,7 +760,7 @@ void av1_frame_init_quantizer(AV1_COMP *cpi) {
void av1_set_quantizer(AV1_COMMON *cm, int q) {
// quantizer has to be reinitialized with av1_init_quantizer() if any
// delta_q changes.
cm->base_qindex = AOMMAX(cm->delta_q_present_flag, q);
cm->base_qindex = AOMMAX(cm->delta_q_info.delta_q_present_flag, q);
cm->y_dc_delta_q = 0;
cm->u_dc_delta_q = 0;
cm->u_ac_delta_q = 0;

View file

@ -27,6 +27,7 @@ typedef struct QUANT_PARAM {
TX_SIZE tx_size;
const qm_val_t *qmatrix;
const qm_val_t *iqmatrix;
int use_quant_b_adapt;
} QUANT_PARAM;
typedef void (*AV1_QUANT_FACADE)(const tran_low_t *coeff_ptr, intptr_t n_coeffs,
@ -67,7 +68,7 @@ typedef struct {
// The Dequants structure is used only for internal quantizer setup in
// av1_quantize.c.
// Fields are sufffixed according to whether or not they're expressed in
// Fields are suffixed according to whether or not they're expressed in
// the same coefficient shift/precision as TX or a fixed Q3 format.
typedef struct {
DECLARE_ALIGNED(16, int16_t,

File diff suppressed because it is too large Load diff

View file

@ -33,9 +33,10 @@ uint32_t write_obu_header(OBU_TYPE obu_type, int obu_extension,
int write_uleb_obu_size(uint32_t obu_header_size, uint32_t obu_payload_size,
uint8_t *dest);
int av1_pack_bitstream(AV1_COMP *const cpi, uint8_t *dest, size_t *size);
int av1_pack_bitstream(AV1_COMP *const cpi, uint8_t *dst, size_t *size,
int *const largest_tile_id);
static INLINE int av1_preserve_existing_gf(AV1_COMP *cpi) {
static INLINE int av1_preserve_existing_gf(const AV1_COMP *const cpi) {
// Do not swap gf and arf indices for internal overlay frames
return cpi->rc.is_src_frame_alt_ref && !cpi->rc.is_src_frame_ext_arf;
}

View file

@ -54,10 +54,10 @@ typedef struct macroblock_plane {
typedef struct {
int txb_skip_cost[TXB_SKIP_CONTEXTS][2];
int base_eob_cost[SIG_COEF_CONTEXTS_EOB][3];
int base_cost[SIG_COEF_CONTEXTS][4];
int base_cost[SIG_COEF_CONTEXTS][8];
int eob_extra_cost[EOB_COEF_CONTEXTS][2];
int dc_sign_cost[DC_SIGN_CONTEXTS][2];
int lps_cost[LEVEL_CONTEXTS][COEFF_BASE_RANGE + 1];
int lps_cost[LEVEL_CONTEXTS][COEFF_BASE_RANGE + 1 + COEFF_BASE_RANGE + 1];
} LV_MAP_COEFF_COST;
typedef struct {
@ -74,16 +74,16 @@ typedef struct {
} CB_COEFF_BUFFER;
typedef struct {
int16_t mode_context[MODE_CTX_REF_FRAMES];
// TODO(angiebird): Reduce the buffer size according to sb_type
tran_low_t *tcoeff[MAX_MB_PLANE];
uint16_t *eobs[MAX_MB_PLANE];
uint8_t *txb_skip_ctx[MAX_MB_PLANE];
int *dc_sign_ctx[MAX_MB_PLANE];
uint8_t ref_mv_count[MODE_CTX_REF_FRAMES];
CANDIDATE_MV ref_mv_stack[MODE_CTX_REF_FRAMES][MAX_REF_MV_STACK_SIZE];
int_mv global_mvs[REF_FRAMES];
int16_t compound_mode_context[MODE_CTX_REF_FRAMES];
int16_t mode_context[MODE_CTX_REF_FRAMES];
uint8_t ref_mv_count[MODE_CTX_REF_FRAMES];
} MB_MODE_INFO_EXT;
typedef struct {
@ -140,6 +140,20 @@ typedef struct tx_size_rd_info_node {
struct tx_size_rd_info_node *children[4];
} TXB_RD_INFO_NODE;
// Simple translation rd state for prune_comp_search_by_single_result
typedef struct {
RD_STATS rd_stats;
RD_STATS rd_stats_y;
RD_STATS rd_stats_uv;
uint8_t blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE];
uint8_t skip;
uint8_t disable_skip;
uint8_t early_skipped;
} SimpleRDState;
// 4: NEAREST, NEW, NEAR, GLOBAL
#define SINGLE_REF_MODES ((REF_FRAMES - 1) * 4)
// Region size for mode decision sampling in the first pass of partition
// search(two_pass_partition_search speed feature), in units of mi size(4).
// Used by the mode_pruning_based_on_two_pass_partition_search speed feature.
@ -173,6 +187,19 @@ typedef struct {
COMPOUND_TYPE comp_type;
} INTERPOLATION_FILTER_STATS;
#define MAX_COMP_RD_STATS 64
typedef struct {
int32_t rate[COMPOUND_TYPES];
int64_t dist[COMPOUND_TYPES];
int_mv mv[2];
MV_REFERENCE_FRAME ref_frames[2];
PREDICTION_MODE mode;
InterpFilters filter;
int ref_mv_idx;
int is_global[2];
} COMP_RD_STATS;
struct inter_modes_info;
typedef struct macroblock MACROBLOCK;
struct macroblock {
struct macroblock_plane plane[MAX_MB_PLANE];
@ -194,6 +221,9 @@ struct macroblock {
INTERPOLATION_FILTER_STATS interp_filter_stats[2][MAX_INTERP_FILTER_STATS];
int interp_filter_stats_idx[2];
// prune_comp_search_by_single_result (3:MAX_REF_MV_SERCH)
SimpleRDState simple_rd_state[SINGLE_REF_MODES][3];
// Activate constrained coding block partition search range.
int use_cb_search_range;
@ -224,12 +254,16 @@ struct macroblock {
// for sub-8x8 blocks.
int sadperbit4;
int rdmult;
int cb_rdmult;
int mb_energy;
int sb_energy_level;
int *m_search_count_ptr;
int *ex_search_count_ptr;
unsigned int txb_split_count;
#if CONFIG_SPEED_STATS
unsigned int tx_search_count;
#endif // CONFIG_SPEED_STATS
// These are set to their default values at the beginning, and then adjusted
// further in the encoding process.
@ -241,12 +275,10 @@ struct macroblock {
unsigned int pred_sse[REF_FRAMES];
int pred_mv_sad[REF_FRAMES];
int *nmvjointcost;
int nmv_vec_cost[MV_JOINTS];
int *nmvcost[2];
int *nmvcost_hp[2];
int **mv_cost_stack;
int **mvcost;
int32_t *wsrc_buf;
int32_t *mask_buf;
@ -258,6 +290,16 @@ struct macroblock {
CONV_BUF_TYPE *tmp_conv_dst;
uint8_t *tmp_obmc_bufs[2];
FRAME_CONTEXT *row_ctx;
// This context will be used to update color_map_cdf pointer which would be
// used during pack bitstream. For single thread and tile-multithreading case
// this ponter will be same as xd->tile_ctx, but for the case of row-mt:
// xd->tile_ctx will point to a temporary context while tile_pb_ctx will point
// to the accurate tile context.
FRAME_CONTEXT *tile_pb_ctx;
struct inter_modes_info *inter_modes_info;
// buffer for hash value calculation of a block
// used only in av1_get_block_hash_value()
// [first hash/second hash]
@ -351,6 +393,9 @@ struct macroblock {
// Store the second best motion vector during full-pixel motion search
int_mv second_best_mv;
// Store the fractional best motion vector during sub/Qpel-pixel motion search
int_mv fractional_best_mv[3];
// use default transform and skip transform type search for intra modes
int use_default_intra_tx_type;
// use default transform and skip transform type search for inter modes
@ -367,6 +412,14 @@ struct macroblock {
int tx_split_prune_flag; // Flag to skip tx split RD search.
int recalc_luma_mc_data; // Flag to indicate recalculation of MC data during
// interpolation filter search
// The likelihood of an edge existing in the block (using partial Canny edge
// detection). For reference, 556 is the value returned for a solid
// vertical black/white edge.
uint16_t edge_strength;
// [Saved stat index]
COMP_RD_STATS comp_rd_stats[MAX_COMP_RD_STATS];
int comp_rd_stats_idx;
};
static INLINE int is_rect_tx_allowed_bsize(BLOCK_SIZE bsize) {

View file

@ -16,8 +16,15 @@ static const BLOCK_SIZE square[MAX_SB_SIZE_LOG2 - 1] = {
BLOCK_4X4, BLOCK_8X8, BLOCK_16X16, BLOCK_32X32, BLOCK_64X64, BLOCK_128X128,
};
typedef struct {
tran_low_t *coeff_buf[MAX_MB_PLANE];
tran_low_t *qcoeff_buf[MAX_MB_PLANE];
tran_low_t *dqcoeff_buf[MAX_MB_PLANE];
} PC_TREE_SHARED_BUFFERS;
static void alloc_mode_context(AV1_COMMON *cm, int num_pix,
PICK_MODE_CONTEXT *ctx) {
PICK_MODE_CONTEXT *ctx,
PC_TREE_SHARED_BUFFERS *shared_bufs) {
const int num_planes = av1_num_planes(cm);
int i;
const int num_blk = num_pix / 16;
@ -25,12 +32,9 @@ static void alloc_mode_context(AV1_COMMON *cm, int num_pix,
CHECK_MEM_ERROR(cm, ctx->blk_skip, aom_calloc(num_blk, sizeof(uint8_t)));
for (i = 0; i < num_planes; ++i) {
CHECK_MEM_ERROR(cm, ctx->coeff[i],
aom_memalign(32, num_pix * sizeof(*ctx->coeff[i])));
CHECK_MEM_ERROR(cm, ctx->qcoeff[i],
aom_memalign(32, num_pix * sizeof(*ctx->qcoeff[i])));
CHECK_MEM_ERROR(cm, ctx->dqcoeff[i],
aom_memalign(32, num_pix * sizeof(*ctx->dqcoeff[i])));
ctx->coeff[i] = shared_bufs->coeff_buf[i];
ctx->qcoeff[i] = shared_bufs->qcoeff_buf[i];
ctx->dqcoeff[i] = shared_bufs->dqcoeff_buf[i];
CHECK_MEM_ERROR(cm, ctx->eobs[i],
aom_memalign(32, num_blk * sizeof(*ctx->eobs[i])));
CHECK_MEM_ERROR(
@ -52,11 +56,8 @@ static void free_mode_context(PICK_MODE_CONTEXT *ctx, const int num_planes) {
aom_free(ctx->blk_skip);
ctx->blk_skip = 0;
for (i = 0; i < num_planes; ++i) {
aom_free(ctx->coeff[i]);
ctx->coeff[i] = 0;
aom_free(ctx->qcoeff[i]);
ctx->qcoeff[i] = 0;
aom_free(ctx->dqcoeff[i]);
ctx->dqcoeff[i] = 0;
aom_free(ctx->eobs[i]);
ctx->eobs[i] = 0;
@ -71,36 +72,37 @@ static void free_mode_context(PICK_MODE_CONTEXT *ctx, const int num_planes) {
}
static void alloc_tree_contexts(AV1_COMMON *cm, PC_TREE *tree, int num_pix,
int is_leaf) {
alloc_mode_context(cm, num_pix, &tree->none);
int is_leaf,
PC_TREE_SHARED_BUFFERS *shared_bufs) {
alloc_mode_context(cm, num_pix, &tree->none, shared_bufs);
if (is_leaf) return;
alloc_mode_context(cm, num_pix / 2, &tree->horizontal[0]);
alloc_mode_context(cm, num_pix / 2, &tree->vertical[0]);
alloc_mode_context(cm, num_pix / 2, &tree->horizontal[0], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->vertical[0], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->horizontal[1]);
alloc_mode_context(cm, num_pix / 2, &tree->vertical[1]);
alloc_mode_context(cm, num_pix / 2, &tree->horizontal[1], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->vertical[1], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->horizontala[0]);
alloc_mode_context(cm, num_pix / 4, &tree->horizontala[1]);
alloc_mode_context(cm, num_pix / 2, &tree->horizontala[2]);
alloc_mode_context(cm, num_pix / 4, &tree->horizontala[0], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->horizontala[1], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->horizontala[2], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->horizontalb[0]);
alloc_mode_context(cm, num_pix / 4, &tree->horizontalb[1]);
alloc_mode_context(cm, num_pix / 4, &tree->horizontalb[2]);
alloc_mode_context(cm, num_pix / 2, &tree->horizontalb[0], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->horizontalb[1], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->horizontalb[2], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->verticala[0]);
alloc_mode_context(cm, num_pix / 4, &tree->verticala[1]);
alloc_mode_context(cm, num_pix / 2, &tree->verticala[2]);
alloc_mode_context(cm, num_pix / 4, &tree->verticala[0], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->verticala[1], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->verticala[2], shared_bufs);
alloc_mode_context(cm, num_pix / 2, &tree->verticalb[0]);
alloc_mode_context(cm, num_pix / 4, &tree->verticalb[1]);
alloc_mode_context(cm, num_pix / 4, &tree->verticalb[2]);
alloc_mode_context(cm, num_pix / 2, &tree->verticalb[0], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->verticalb[1], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->verticalb[2], shared_bufs);
for (int i = 0; i < 4; ++i) {
alloc_mode_context(cm, num_pix / 4, &tree->horizontal4[i]);
alloc_mode_context(cm, num_pix / 4, &tree->vertical4[i]);
alloc_mode_context(cm, num_pix / 4, &tree->horizontal4[i], shared_bufs);
alloc_mode_context(cm, num_pix / 4, &tree->vertical4[i], shared_bufs);
}
}
@ -135,6 +137,7 @@ void av1_setup_pc_tree(AV1_COMMON *cm, ThreadData *td) {
const int tree_nodes = tree_nodes_inc + 256 + 64 + 16 + 4 + 1;
int pc_tree_index = 0;
PC_TREE *this_pc;
PC_TREE_SHARED_BUFFERS shared_bufs;
int square_index = 1;
int nodes;
@ -143,11 +146,24 @@ void av1_setup_pc_tree(AV1_COMMON *cm, ThreadData *td) {
aom_calloc(tree_nodes, sizeof(*td->pc_tree)));
this_pc = &td->pc_tree[0];
for (i = 0; i < 3; i++) {
const int max_num_pix = MAX_SB_SIZE * MAX_SB_SIZE;
CHECK_MEM_ERROR(cm, td->tree_coeff_buf[i],
aom_memalign(32, max_num_pix * sizeof(tran_low_t)));
CHECK_MEM_ERROR(cm, td->tree_qcoeff_buf[i],
aom_memalign(32, max_num_pix * sizeof(tran_low_t)));
CHECK_MEM_ERROR(cm, td->tree_dqcoeff_buf[i],
aom_memalign(32, max_num_pix * sizeof(tran_low_t)));
shared_bufs.coeff_buf[i] = td->tree_coeff_buf[i];
shared_bufs.qcoeff_buf[i] = td->tree_qcoeff_buf[i];
shared_bufs.dqcoeff_buf[i] = td->tree_dqcoeff_buf[i];
}
// Sets up all the leaf nodes in the tree.
for (pc_tree_index = 0; pc_tree_index < leaf_nodes; ++pc_tree_index) {
PC_TREE *const tree = &td->pc_tree[pc_tree_index];
tree->block_size = square[0];
alloc_tree_contexts(cm, tree, 16, 1);
alloc_tree_contexts(cm, tree, 16, 1, &shared_bufs);
}
// Each node has 4 leaf nodes, fill each block_size level of the tree
@ -155,7 +171,7 @@ void av1_setup_pc_tree(AV1_COMMON *cm, ThreadData *td) {
for (nodes = leaf_nodes >> 2; nodes > 0; nodes >>= 2) {
for (i = 0; i < nodes; ++i) {
PC_TREE *const tree = &td->pc_tree[pc_tree_index];
alloc_tree_contexts(cm, tree, 16 << (2 * square_index), 0);
alloc_tree_contexts(cm, tree, 16 << (2 * square_index), 0, &shared_bufs);
tree->block_size = square[square_index];
for (j = 0; j < 4; j++) tree->split[j] = this_pc++;
++pc_tree_index;
@ -181,6 +197,14 @@ void av1_free_pc_tree(ThreadData *td, const int num_planes) {
for (int i = 0; i < tree_nodes; ++i) {
free_tree_contexts(&td->pc_tree[i], num_planes);
}
for (int i = 0; i < 3; ++i) {
aom_free(td->tree_coeff_buf[i]);
aom_free(td->tree_qcoeff_buf[i]);
aom_free(td->tree_dqcoeff_buf[i]);
td->tree_coeff_buf[i] = NULL;
td->tree_qcoeff_buf[i] = NULL;
td->tree_dqcoeff_buf[i] = NULL;
}
aom_free(td->pc_tree);
td->pc_tree = NULL;
}

View file

@ -23,7 +23,7 @@ struct AV1_COMP;
struct AV1Common;
struct ThreadData;
typedef enum {
enum {
// Search all the partition types in this plane.
SEARCH_FULL_PLANE = 0,
// Only search none_partition coding block.
@ -32,12 +32,14 @@ typedef enum {
SEARCH_SAME_PLANE = 2,
// Skip search partition on this plane. Go split directly.
SPLIT_PLANE = 3,
} CB_TREE_SEARCH;
} UENUM1BYTE(CB_TREE_SEARCH);
// Structure to hold snapshot of coding context during the mode picking process
typedef struct {
MB_MODE_INFO mic;
MB_MODE_INFO_EXT mbmi_ext;
int64_t dist;
int64_t rdcost;
uint8_t *color_index_map[2];
uint8_t *blk_skip;
@ -62,30 +64,42 @@ typedef struct {
// TODO(jingning) Use RD_COST struct here instead. This involves a boarder
// scope of refactoring.
int rate;
int64_t dist;
int64_t rdcost;
int rd_mode_is_ready; // Flag to indicate whether rd pick mode decision has
// been made.
int mode_selected;
#if CONFIG_ONE_PASS_SVM
// Features for one pass svm early term
int seg_feat;
#endif
// motion vector cache for adaptive motion search control in partition
// search loop
MV pred_mv[REF_FRAMES];
InterpFilter pred_interp_filter;
PARTITION_TYPE partition;
// Reference and prediction mode cache for ref/mode speedup
// TODO(zoeliu@gmail.com): The values of ref_selected and mode_selected will
// be explored for further encoder speedup, to differentiate this approach for
// setting skip_ref_frame_mask from others. For instance, it is possible that
// the underlying square block(s) share the same SIMPLE_TRANSLATION motion
// mode as well as the mode of GLOBALMV, more ref/mode combos could be
// skipped.
MV_REFERENCE_FRAME ref_selected[2];
} PICK_MODE_CONTEXT;
typedef struct {
int64_t rdcost;
int64_t sub_block_rdcost[4];
int valid;
int split;
int skip;
int64_t rdcost;
int sub_block_split[4];
int sub_block_skip[4];
int64_t sub_block_rdcost[4];
int skip;
} PC_TREE_STATS;
typedef struct PC_TREE {
int index;
PARTITION_TYPE partitioning;
BLOCK_SIZE block_size;
PICK_MODE_CONTEXT none;
@ -97,9 +111,11 @@ typedef struct PC_TREE {
PICK_MODE_CONTEXT verticalb[3];
PICK_MODE_CONTEXT horizontal4[4];
PICK_MODE_CONTEXT vertical4[4];
CB_TREE_SEARCH cb_search_range;
struct PC_TREE *split[4];
PC_TREE_STATS pc_tree_stats;
CB_TREE_SEARCH cb_search_range;
int index;
MV mv_ref_fulls[REF_FRAMES];
} PC_TREE;
void av1_setup_pc_tree(struct AV1Common *cm, struct ThreadData *td);

View file

@ -30,6 +30,10 @@ extern const uint16_t av1_prob_cost[128];
// Calculate the cost of a symbol with probability p15 / 2^15
static INLINE int av1_cost_symbol(aom_cdf_prob p15) {
// p15 can be out of range [1, CDF_PROB_TOP - 1]. Clamping it, so that the
// following cost calculation works correctly. Otherwise, if p15 =
// CDF_PROB_TOP, shift would be -1, and "p15 << shift" would be wrong.
p15 = (aom_cdf_prob)clamp(p15, 1, CDF_PROB_TOP - 1);
assert(0 < p15 && p15 < CDF_PROB_TOP);
const int shift = CDF_PROB_BITS - 1 - get_msb(p15);
const int prob = get_prob(p15 << shift, CDF_PROB_TOP);

View file

@ -0,0 +1,918 @@
/*
* Copyright (c) 2019, 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 <stdint.h>
#include "config/aom_config.h"
#include "config/aom_scale_rtcd.h"
#include "aom/aom_codec.h"
#include "aom/aom_encoder.h"
#include "aom_ports/system_state.h"
#if CONFIG_MISMATCH_DEBUG
#include "aom_util/debug_util.h"
#endif // CONFIG_MISMATCH_DEBUG
#include "av1/common/onyxc_int.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/encode_strategy.h"
#include "av1/encoder/firstpass.h"
#include "av1/encoder/temporal_filter.h"
#include "av1/encoder/tpl_model.h"
// Define the reference buffers that will be updated post encode.
void av1_configure_buffer_updates(AV1_COMP *cpi, const FRAME_UPDATE_TYPE type) {
// NOTE(weitinglin): Should we define another function to take care of
// cpi->rc.is_$Source_Type to make this function as it is in the comment?
// show_existing_frame is a flag left set from the end of encoding the
// previous frame. Alongside it, is_src_frame_alt_ref may also be left
// set so shouldn't be cleared in this case.
if (!cpi->common.show_existing_frame) cpi->rc.is_src_frame_alt_ref = 0;
cpi->rc.is_bwd_ref_frame = 0;
cpi->rc.is_last_bipred_frame = 0;
cpi->rc.is_bipred_frame = 0;
cpi->rc.is_src_frame_ext_arf = 0;
switch (type) {
case KF_UPDATE:
cpi->refresh_last_frame = 1;
cpi->refresh_golden_frame = 1;
cpi->refresh_bwd_ref_frame = 1;
cpi->refresh_alt2_ref_frame = 1;
cpi->refresh_alt_ref_frame = 1;
break;
case LF_UPDATE:
cpi->refresh_last_frame = 1;
cpi->refresh_golden_frame = 0;
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
break;
case GF_UPDATE:
// TODO(zoeliu): To further investigate whether 'refresh_last_frame' is
// needed.
cpi->refresh_last_frame = 1;
cpi->refresh_golden_frame = 1;
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
break;
case OVERLAY_UPDATE:
cpi->refresh_last_frame = 0;
cpi->refresh_golden_frame = 1;
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
cpi->rc.is_src_frame_alt_ref = 1;
break;
case ARF_UPDATE:
cpi->refresh_last_frame = 0;
cpi->refresh_golden_frame = 0;
// NOTE: BWDREF does not get updated along with ALTREF_FRAME.
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 1;
break;
case BRF_UPDATE:
cpi->refresh_last_frame = 0;
cpi->refresh_golden_frame = 0;
cpi->refresh_bwd_ref_frame = 1;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
cpi->rc.is_bwd_ref_frame = 1;
break;
case LAST_BIPRED_UPDATE:
cpi->refresh_last_frame = 1;
cpi->refresh_golden_frame = 0;
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
cpi->rc.is_last_bipred_frame = 1;
break;
case BIPRED_UPDATE:
cpi->refresh_last_frame = 1;
cpi->refresh_golden_frame = 0;
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
cpi->rc.is_bipred_frame = 1;
break;
case INTNL_OVERLAY_UPDATE:
cpi->refresh_last_frame = 1;
cpi->refresh_golden_frame = 0;
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 0;
cpi->refresh_alt_ref_frame = 0;
cpi->rc.is_src_frame_alt_ref = 1;
cpi->rc.is_src_frame_ext_arf = 1;
break;
case INTNL_ARF_UPDATE:
cpi->refresh_last_frame = 0;
cpi->refresh_golden_frame = 0;
if (cpi->new_bwdref_update_rule == 1 && cpi->oxcf.pass == 2) {
cpi->refresh_bwd_ref_frame = 1;
cpi->refresh_alt2_ref_frame = 0;
} else {
cpi->refresh_bwd_ref_frame = 0;
cpi->refresh_alt2_ref_frame = 1;
}
cpi->refresh_alt_ref_frame = 0;
break;
default: assert(0); break;
}
}
static void set_additional_frame_flags(const AV1_COMMON *const cm,
unsigned int *const frame_flags) {
if (frame_is_intra_only(cm)) *frame_flags |= FRAMEFLAGS_INTRAONLY;
if (frame_is_sframe(cm)) *frame_flags |= FRAMEFLAGS_SWITCH;
if (cm->error_resilient_mode) *frame_flags |= FRAMEFLAGS_ERROR_RESILIENT;
}
static INLINE void update_keyframe_counters(AV1_COMP *cpi) {
// TODO(zoeliu): To investigate whether we should treat BWDREF_FRAME
// differently here for rc->avg_frame_bandwidth.
if (cpi->common.show_frame || cpi->rc.is_bwd_ref_frame) {
if (!cpi->common.show_existing_frame || cpi->rc.is_src_frame_alt_ref ||
cpi->common.current_frame.frame_type == KEY_FRAME) {
// If this is a show_existing_frame with a source other than altref,
// or if it is not a displayed forward keyframe, the keyframe update
// counters were incremented when it was originally encoded.
cpi->rc.frames_since_key++;
cpi->rc.frames_to_key--;
}
}
}
static INLINE int is_frame_droppable(const AV1_COMP *const cpi) {
return !(cpi->refresh_alt_ref_frame || cpi->refresh_alt2_ref_frame ||
cpi->refresh_bwd_ref_frame || cpi->refresh_golden_frame ||
cpi->refresh_last_frame);
}
static INLINE void update_frames_till_gf_update(AV1_COMP *cpi) {
// TODO(weitinglin): Updating this counter for is_frame_droppable
// is a work-around to handle the condition when a frame is drop.
// We should fix the cpi->common.show_frame flag
// instead of checking the other condition to update the counter properly.
if (cpi->common.show_frame || is_frame_droppable(cpi)) {
// Decrement count down till next gf
if (cpi->rc.frames_till_gf_update_due > 0)
cpi->rc.frames_till_gf_update_due--;
}
}
static INLINE void update_twopass_gf_group_index(AV1_COMP *cpi) {
// Increment the gf group index ready for the next frame. If this is
// a show_existing_frame with a source other than altref, or if it is not
// a displayed forward keyframe, the index was incremented when it was
// originally encoded.
if (!cpi->common.show_existing_frame || cpi->rc.is_src_frame_alt_ref ||
cpi->common.current_frame.frame_type == KEY_FRAME) {
++cpi->twopass.gf_group.index;
}
}
static void update_rc_counts(AV1_COMP *cpi) {
update_keyframe_counters(cpi);
update_frames_till_gf_update(cpi);
if (cpi->oxcf.pass == 2) update_twopass_gf_group_index(cpi);
}
static void check_show_existing_frame(AV1_COMP *cpi) {
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
AV1_COMMON *const cm = &cpi->common;
const FRAME_UPDATE_TYPE next_frame_update_type =
gf_group->update_type[gf_group->index];
const int which_arf = (cpi->new_bwdref_update_rule == 1)
? gf_group->arf_update_idx[gf_group->index] > 0
: gf_group->arf_update_idx[gf_group->index];
if (cm->show_existing_frame == 1) {
cm->show_existing_frame = 0;
} else if (cpi->rc.is_last_bipred_frame) {
// NOTE: When new structure is used, every bwdref will have one overlay
// frame. Therefore, there is no need to find out which frame to
// show in advance.
if (cpi->new_bwdref_update_rule == 0) {
// NOTE: If the current frame is a last bi-predictive frame, it is
// needed next to show the BWDREF_FRAME, which is pointed by
// the last_fb_idxes[0] after reference frame buffer update
cpi->rc.is_last_bipred_frame = 0;
cm->show_existing_frame = 1;
cpi->existing_fb_idx_to_show = cm->remapped_ref_idx[0];
}
} else if (cpi->is_arf_filter_off[which_arf] &&
(next_frame_update_type == OVERLAY_UPDATE ||
next_frame_update_type == INTNL_OVERLAY_UPDATE)) {
const int bwdref_to_show =
(cpi->new_bwdref_update_rule == 1) ? BWDREF_FRAME : ALTREF2_FRAME;
// Other parameters related to OVERLAY_UPDATE will be taken care of
// in av1_rc_get_second_pass_params(cpi)
cm->show_existing_frame = 1;
cpi->rc.is_src_frame_alt_ref = 1;
cpi->existing_fb_idx_to_show =
(next_frame_update_type == OVERLAY_UPDATE)
? get_ref_frame_map_idx(cm, ALTREF_FRAME)
: get_ref_frame_map_idx(cm, bwdref_to_show);
if (cpi->new_bwdref_update_rule == 0) {
cpi->is_arf_filter_off[which_arf] = 0;
}
}
cpi->rc.is_src_frame_ext_arf = 0;
}
static void set_ext_overrides(AV1_COMP *const cpi,
EncodeFrameParams *const frame_params) {
// Overrides the defaults with the externally supplied values with
// av1_update_reference() and av1_update_entropy() calls
// Note: The overrides are valid only for the next frame passed
// to av1_encode_lowlevel()
AV1_COMMON *const cm = &cpi->common;
if (cpi->ext_use_s_frame) {
frame_params->frame_type = S_FRAME;
}
if (cpi->ext_refresh_frame_context_pending) {
cm->refresh_frame_context = cpi->ext_refresh_frame_context;
cpi->ext_refresh_frame_context_pending = 0;
}
if (cpi->ext_refresh_frame_flags_pending) {
cpi->refresh_last_frame = cpi->ext_refresh_last_frame;
cpi->refresh_golden_frame = cpi->ext_refresh_golden_frame;
cpi->refresh_alt_ref_frame = cpi->ext_refresh_alt_ref_frame;
cpi->refresh_bwd_ref_frame = cpi->ext_refresh_bwd_ref_frame;
cpi->refresh_alt2_ref_frame = cpi->ext_refresh_alt2_ref_frame;
cpi->ext_refresh_frame_flags_pending = 0;
}
cm->allow_ref_frame_mvs = cpi->ext_use_ref_frame_mvs;
frame_params->error_resilient_mode = cpi->ext_use_error_resilient;
// A keyframe is already error resilient and keyframes with
// error_resilient_mode interferes with the use of show_existing_frame
// when forward reference keyframes are enabled.
frame_params->error_resilient_mode &= frame_params->frame_type != KEY_FRAME;
// For bitstream conformance, s-frames must be error-resilient
frame_params->error_resilient_mode |= frame_params->frame_type == S_FRAME;
}
static int get_ref_frame_flags(const AV1_COMP *const cpi) {
const AV1_COMMON *const cm = &cpi->common;
const RefCntBuffer *last_buf = get_ref_frame_buf(cm, LAST_FRAME);
const RefCntBuffer *last2_buf = get_ref_frame_buf(cm, LAST2_FRAME);
const RefCntBuffer *last3_buf = get_ref_frame_buf(cm, LAST3_FRAME);
const RefCntBuffer *golden_buf = get_ref_frame_buf(cm, GOLDEN_FRAME);
const RefCntBuffer *bwd_buf = get_ref_frame_buf(cm, BWDREF_FRAME);
const RefCntBuffer *alt2_buf = get_ref_frame_buf(cm, ALTREF2_FRAME);
const RefCntBuffer *alt_buf = get_ref_frame_buf(cm, ALTREF_FRAME);
// No.1 Priority: LAST_FRAME
const int last2_is_last = (last2_buf == last_buf);
const int last3_is_last = (last3_buf == last_buf);
const int gld_is_last = (golden_buf == last_buf);
const int bwd_is_last = (bwd_buf == last_buf);
const int alt2_is_last = (alt2_buf == last_buf);
const int alt_is_last = (alt_buf == last_buf);
// No.2 Priority: ALTREF_FRAME
const int last2_is_alt = (last2_buf == alt_buf);
const int last3_is_alt = (last3_buf == alt_buf);
const int gld_is_alt = (golden_buf == alt_buf);
const int bwd_is_alt = (bwd_buf == alt_buf);
const int alt2_is_alt = (alt2_buf == alt_buf);
// No.3 Priority: LAST2_FRAME
const int last3_is_last2 = (last3_buf == last2_buf);
const int gld_is_last2 = (golden_buf == last2_buf);
const int bwd_is_last2 = (bwd_buf == last2_buf);
const int alt2_is_last2 = (alt2_buf == last2_buf);
// No.4 Priority: LAST3_FRAME
const int gld_is_last3 = (golden_buf == last3_buf);
const int bwd_is_last3 = (bwd_buf == last3_buf);
const int alt2_is_last3 = (alt2_buf == last3_buf);
// No.5 Priority: GOLDEN_FRAME
const int bwd_is_gld = (bwd_buf == golden_buf);
const int alt2_is_gld = (alt2_buf == golden_buf);
// No.6 Priority: BWDREF_FRAME
const int alt2_is_bwd = (alt2_buf == bwd_buf);
// No.7 Priority: ALTREF2_FRAME
// cpi->ext_ref_frame_flags allows certain reference types to be disabled
// by the external interface. These are set by av1_apply_encoding_flags().
// Start with what the external interface allows, then suppress any reference
// types which we have found to be duplicates.
int flags = cpi->ext_ref_frame_flags;
if (cpi->rc.frames_till_gf_update_due == INT_MAX) flags &= ~AOM_GOLD_FLAG;
if (alt_is_last) flags &= ~AOM_ALT_FLAG;
if (last2_is_last || last2_is_alt) flags &= ~AOM_LAST2_FLAG;
if (last3_is_last || last3_is_alt || last3_is_last2) flags &= ~AOM_LAST3_FLAG;
if (gld_is_last || gld_is_alt || gld_is_last2 || gld_is_last3)
flags &= ~AOM_GOLD_FLAG;
if ((bwd_is_last || bwd_is_alt || bwd_is_last2 || bwd_is_last3 || bwd_is_gld))
flags &= ~AOM_BWD_FLAG;
if ((alt2_is_last || alt2_is_alt || alt2_is_last2 || alt2_is_last3 ||
alt2_is_gld || alt2_is_bwd))
flags &= ~AOM_ALT2_FLAG;
return flags;
}
static int get_current_frame_ref_type(
const AV1_COMP *const cpi, const EncodeFrameParams *const frame_params) {
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
// We choose the reference "type" of this frame from the flags which indicate
// which reference frames will be refreshed by it. More than one of these
// flags may be set, so the order here implies an order of precedence.
// This is just used to choose the primary_ref_frame (as the most recent
// reference buffer of the same reference-type as the current frame)
const int intra_only = frame_params->frame_type == KEY_FRAME ||
frame_params->frame_type == INTRA_ONLY_FRAME;
if (intra_only || frame_params->error_resilient_mode ||
cpi->ext_use_primary_ref_none)
return REGULAR_FRAME;
else if (gf_group->update_type[gf_group->index] == INTNL_ARF_UPDATE)
return EXT_ARF_FRAME;
else if (cpi->refresh_alt_ref_frame)
return ARF_FRAME;
else if (cpi->rc.is_src_frame_alt_ref)
return OVERLAY_FRAME;
else if (cpi->refresh_golden_frame)
return GLD_FRAME;
else if (cpi->refresh_bwd_ref_frame)
return BRF_FRAME;
else
return REGULAR_FRAME;
}
static int choose_primary_ref_frame(
const AV1_COMP *const cpi, const EncodeFrameParams *const frame_params) {
const AV1_COMMON *const cm = &cpi->common;
const int intra_only = frame_params->frame_type == KEY_FRAME ||
frame_params->frame_type == INTRA_ONLY_FRAME;
if (intra_only || frame_params->error_resilient_mode ||
cpi->ext_use_primary_ref_none) {
return PRIMARY_REF_NONE;
}
// Find the most recent reference frame with the same reference type as the
// current frame
const FRAME_CONTEXT_INDEX current_ref_type =
get_current_frame_ref_type(cpi, frame_params);
int wanted_fb = cpi->fb_of_context_type[current_ref_type];
int primary_ref_frame = PRIMARY_REF_NONE;
for (int ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ref_frame++) {
if (get_ref_frame_map_idx(cm, ref_frame) == wanted_fb) {
primary_ref_frame = ref_frame - LAST_FRAME;
}
}
return primary_ref_frame;
}
static void update_fb_of_context_type(
const AV1_COMP *const cpi, const EncodeFrameParams *const frame_params,
int *const fb_of_context_type) {
const AV1_COMMON *const cm = &cpi->common;
if (frame_is_intra_only(cm) || cm->error_resilient_mode ||
cpi->ext_use_primary_ref_none) {
for (int i = 0; i < REF_FRAMES; i++) {
fb_of_context_type[i] = -1;
}
fb_of_context_type[REGULAR_FRAME] =
cm->show_frame ? get_ref_frame_map_idx(cm, GOLDEN_FRAME)
: get_ref_frame_map_idx(cm, ALTREF_FRAME);
}
if (!encode_show_existing_frame(cm)) {
// Refresh fb_of_context_type[]: see encoder.h for explanation
// Note that we want the value of refresh_frame_flags for the frame that
// just happened. If we call get_refresh_frame_flags now we will get a
// different answer, because update_reference_frames() has happened.
if (cm->current_frame.frame_type == KEY_FRAME) {
// All ref frames are refreshed, pick one that will live long enough
fb_of_context_type[REGULAR_FRAME] = 0;
} else {
// If more than one frame is refreshed, it doesn't matter which one we
// pick so pick the first. LST sometimes doesn't refresh any: this is ok
const int current_frame_ref_type =
get_current_frame_ref_type(cpi, frame_params);
for (int i = 0; i < REF_FRAMES; i++) {
if (cm->current_frame.refresh_frame_flags & (1 << i)) {
fb_of_context_type[current_frame_ref_type] = i;
break;
}
}
}
}
}
static int get_order_offset(const AV1_COMP *const cpi,
const EncodeFrameParams *const frame_params) {
// shown frame by definition has order offset 0
// show_existing_frame ignores order_offset and simply takes the order_hint
// from the reference frame being shown.
if (frame_params->show_frame || cpi->common.show_existing_frame) return 0;
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
const int arf_offset =
AOMMIN((MAX_GF_INTERVAL - 1), gf_group->arf_src_offset[gf_group->index]);
const int brf_offset = gf_group->brf_src_offset[gf_group->index];
return AOMMIN((MAX_GF_INTERVAL - 1), arf_offset + brf_offset);
}
static void adjust_frame_rate(AV1_COMP *cpi,
const struct lookahead_entry *source) {
int64_t this_duration;
int step = 0;
// Clear down mmx registers
aom_clear_system_state();
if (source->ts_start == cpi->first_time_stamp_ever) {
this_duration = source->ts_end - source->ts_start;
step = 1;
} else {
int64_t last_duration =
cpi->last_end_time_stamp_seen - cpi->last_time_stamp_seen;
this_duration = source->ts_end - cpi->last_end_time_stamp_seen;
// do a step update if the duration changes by 10%
if (last_duration)
step = (int)((this_duration - last_duration) * 10 / last_duration);
}
if (this_duration) {
if (step) {
av1_new_framerate(cpi, 10000000.0 / this_duration);
} else {
// Average this frame's rate into the last second's average
// frame rate. If we haven't seen 1 second yet, then average
// over the whole interval seen.
const double interval = AOMMIN(
(double)(source->ts_end - cpi->first_time_stamp_ever), 10000000.0);
double avg_duration = 10000000.0 / cpi->framerate;
avg_duration *= (interval - avg_duration + this_duration);
avg_duration /= interval;
av1_new_framerate(cpi, 10000000.0 / avg_duration);
}
}
cpi->last_time_stamp_seen = source->ts_start;
cpi->last_end_time_stamp_seen = source->ts_end;
}
static void check_src_altref(AV1_COMP *cpi,
const struct lookahead_entry *source) {
RATE_CONTROL *const rc = &cpi->rc;
// If pass == 2, the parameters set here will be reset in
// av1_rc_get_second_pass_params()
if (cpi->oxcf.pass == 2) {
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
rc->is_src_frame_alt_ref =
(gf_group->update_type[gf_group->index] == INTNL_OVERLAY_UPDATE) ||
(gf_group->update_type[gf_group->index] == OVERLAY_UPDATE);
rc->is_src_frame_ext_arf =
gf_group->update_type[gf_group->index] == INTNL_OVERLAY_UPDATE;
} else {
rc->is_src_frame_alt_ref =
cpi->alt_ref_source && (source == cpi->alt_ref_source);
}
if (rc->is_src_frame_alt_ref) {
// Current frame is an ARF overlay frame.
cpi->alt_ref_source = NULL;
if (rc->is_src_frame_ext_arf && !cpi->common.show_existing_frame) {
// For INTNL_OVERLAY, when show_existing_frame == 0, they do need to
// refresh the LAST_FRAME, i.e. LAST3 gets retired, LAST2 becomes LAST3,
// LAST becomes LAST2, and INTNL_OVERLAY becomes LAST.
cpi->refresh_last_frame = 1;
} else {
// Don't refresh the last buffer for an ARF overlay frame. It will
// become the GF so preserve last as an alternative prediction option.
cpi->refresh_last_frame = 0;
}
}
}
// Returns 0 if this is not an alt ref else the offset of the source frame
// used as the arf midpoint.
static int get_arf_src_index(AV1_COMP *cpi) {
RATE_CONTROL *const rc = &cpi->rc;
int arf_src_index = 0;
if (is_altref_enabled(cpi)) {
if (cpi->oxcf.pass == 2) {
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
if (gf_group->update_type[gf_group->index] == ARF_UPDATE) {
arf_src_index = gf_group->arf_src_offset[gf_group->index];
}
} else if (rc->source_alt_ref_pending) {
arf_src_index = rc->frames_till_gf_update_due;
}
}
return arf_src_index;
}
static int get_brf_src_index(AV1_COMP *cpi) {
int brf_src_index = 0;
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
// TODO(zoeliu): We need to add the check on the -bwd_ref command line setup
// flag.
if (gf_group->bidir_pred_enabled[gf_group->index]) {
if (cpi->oxcf.pass == 2) {
if (gf_group->update_type[gf_group->index] == BRF_UPDATE)
brf_src_index = gf_group->brf_src_offset[gf_group->index];
} else {
// TODO(zoeliu): To re-visit the setup for this scenario
brf_src_index = cpi->rc.bipred_group_interval - 1;
}
}
return brf_src_index;
}
// Returns 0 if this is not an alt ref else the offset of the source frame
// used as the arf midpoint.
static int get_arf2_src_index(AV1_COMP *cpi) {
int arf2_src_index = 0;
if (is_altref_enabled(cpi) && cpi->num_extra_arfs) {
if (cpi->oxcf.pass == 2) {
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
if (gf_group->update_type[gf_group->index] == INTNL_ARF_UPDATE) {
arf2_src_index = gf_group->arf_src_offset[gf_group->index];
}
}
}
return arf2_src_index;
}
// Called if this frame is an ARF or ARF2. Also handles forward-keyframes
// For an ARF set arf2=0, for ARF2 set arf2=1
// temporal_filtered is set to 1 if we temporally filter the ARF frame, so that
// the correct post-filter buffer can be used.
static struct lookahead_entry *setup_arf_or_arf2(
AV1_COMP *const cpi, const int arf_src_index, const int arf2,
int *temporal_filtered, EncodeFrameParams *const frame_params) {
AV1_COMMON *const cm = &cpi->common;
RATE_CONTROL *const rc = &cpi->rc;
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
assert(arf_src_index <= rc->frames_to_key);
*temporal_filtered = 0;
struct lookahead_entry *source =
av1_lookahead_peek(cpi->lookahead, arf_src_index);
if (source != NULL) {
cm->showable_frame = 1;
cpi->alt_ref_source = source;
// When arf_src_index == rc->frames_to_key, it indicates a fwd_kf
if (!arf2 && arf_src_index == rc->frames_to_key) {
// Skip temporal filtering and mark as intra_only if we have a fwd_kf
const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
int which_arf = gf_group->arf_update_idx[gf_group->index];
cpi->is_arf_filter_off[which_arf] = 1;
cpi->no_show_kf = 1;
} else {
if (oxcf->arnr_max_frames > 0) {
// Produce the filtered ARF frame.
av1_temporal_filter(cpi, arf_src_index);
aom_extend_frame_borders(&cpi->alt_ref_buffer, av1_num_planes(cm));
*temporal_filtered = 1;
}
}
frame_params->show_frame = 0;
if (oxcf->pass < 2) {
// In second pass, the buffer updates configure will be set
// in the function av1_rc_get_second_pass_params
av1_configure_buffer_updates(cpi, arf2 ? INTNL_ARF_UPDATE : ARF_UPDATE);
}
}
rc->source_alt_ref_pending = 0;
return source;
}
// Determine whether there is a forced keyframe pending in the lookahead buffer
static int is_forced_keyframe_pending(struct lookahead_ctx *lookahead,
const int up_to_index) {
for (int i = 0; i <= up_to_index; i++) {
const struct lookahead_entry *e = av1_lookahead_peek(lookahead, i);
if (e == NULL) {
// We have reached the end of the lookahead buffer and not early-returned
// so there isn't a forced key-frame pending.
return 0;
} else if (e->flags == AOM_EFLAG_FORCE_KF) {
return 1;
} else {
continue;
}
}
return 0; // Never reached
}
// Check if we should encode an ARF, ARF2 or BRF. If not, try a LAST
// Do some setup associated with the chosen source
// Return the frame source, or NULL if we couldn't find one
struct lookahead_entry *choose_frame_source(
AV1_COMP *const cpi, int *const temporal_filtered, int *const flush,
struct lookahead_entry **last_source,
EncodeFrameParams *const frame_params) {
AV1_COMMON *const cm = &cpi->common;
struct lookahead_entry *source = NULL;
*temporal_filtered = 0;
// Should we encode an alt-ref frame.
int arf_src_index = get_arf_src_index(cpi);
if (arf_src_index &&
is_forced_keyframe_pending(cpi->lookahead, arf_src_index)) {
arf_src_index = 0;
*flush = 1;
}
if (arf_src_index) {
source = setup_arf_or_arf2(cpi, arf_src_index, 0, temporal_filtered,
frame_params);
}
// Should we encode an arf2 frame (mutually exclusive to ARF)
arf_src_index = get_arf2_src_index(cpi);
if (arf_src_index &&
is_forced_keyframe_pending(cpi->lookahead, arf_src_index)) {
arf_src_index = 0;
*flush = 1;
}
if (arf_src_index) {
source = setup_arf_or_arf2(cpi, arf_src_index, 1, temporal_filtered,
frame_params);
}
cpi->rc.is_bwd_ref_frame = 0;
int brf_src_index = get_brf_src_index(cpi);
if (brf_src_index) {
assert(brf_src_index <= cpi->rc.frames_to_key);
if ((source = av1_lookahead_peek(cpi->lookahead, brf_src_index)) != NULL) {
cm->showable_frame = 1;
frame_params->show_frame = 0;
if (cpi->oxcf.pass < 2) {
// In second pass, the buffer updates configure will be set
// in the function av1_rc_get_second_pass_params
av1_configure_buffer_updates(cpi, BRF_UPDATE);
}
}
}
if (!source) {
// Get last frame source.
if (cm->current_frame.frame_number > 0) {
*last_source = av1_lookahead_peek(cpi->lookahead, -1);
}
// Read in the source frame.
source = av1_lookahead_pop(cpi->lookahead, *flush);
if (source != NULL) {
frame_params->show_frame = 1;
// Check to see if the frame should be encoded as an arf overlay.
check_src_altref(cpi, source);
}
}
return source;
}
int av1_encode_strategy(AV1_COMP *const cpi, size_t *const size,
uint8_t *const dest, unsigned int *frame_flags,
int64_t *const time_stamp, int64_t *const time_end,
const aom_rational_t *const timebase, int flush) {
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
AV1_COMMON *const cm = &cpi->common;
EncodeFrameInput frame_input;
EncodeFrameParams frame_params;
EncodeFrameResults frame_results;
memset(&frame_input, 0, sizeof(frame_input));
memset(&frame_params, 0, sizeof(frame_params));
memset(&frame_results, 0, sizeof(frame_results));
int temporal_filtered = 0;
struct lookahead_entry *source = NULL;
struct lookahead_entry *last_source = NULL;
if (cm->show_existing_frame) {
source = av1_lookahead_pop(cpi->lookahead, flush);
} else {
source = choose_frame_source(cpi, &temporal_filtered, &flush, &last_source,
&frame_params);
}
if (source == NULL) { // If no source was found, we can't encode a frame.
if (flush && oxcf->pass == 1 && !cpi->twopass.first_pass_done) {
av1_end_first_pass(cpi); /* get last stats packet */
cpi->twopass.first_pass_done = 1;
}
return -1;
}
frame_input.source = temporal_filtered ? &cpi->alt_ref_buffer : &source->img;
frame_input.last_source = last_source != NULL ? &last_source->img : NULL;
frame_input.ts_duration = source->ts_end - source->ts_start;
*time_stamp = source->ts_start;
*time_end = source->ts_end;
if (source->ts_start < cpi->first_time_stamp_ever) {
cpi->first_time_stamp_ever = source->ts_start;
cpi->last_end_time_stamp_seen = source->ts_start;
}
av1_apply_encoding_flags(cpi, source->flags);
if (!cm->show_existing_frame)
*frame_flags = (source->flags & AOM_EFLAG_FORCE_KF) ? FRAMEFLAGS_KEY : 0;
cpi->frame_flags = *frame_flags;
if (frame_params.show_frame ||
(cm->show_existing_frame && cpi->rc.is_src_frame_alt_ref)) {
// Shown frames and arf-overlay frames need frame-rate considering
adjust_frame_rate(cpi, source);
}
if (cm->show_existing_frame) {
// show_existing_frame implies this frame is shown!
frame_params.show_frame = 1;
} else {
// Retain the RF_LEVEL for the current newly coded frame.
cm->cur_frame->frame_rf_level =
cpi->twopass.gf_group.rf_level[cpi->twopass.gf_group.index];
if (cpi->film_grain_table) {
cm->seq_params.film_grain_params_present = aom_film_grain_table_lookup(
cpi->film_grain_table, *time_stamp, *time_end, 0 /* =erase */,
&cm->film_grain_params);
}
cm->cur_frame->film_grain_params_present =
cm->seq_params.film_grain_params_present;
// only one operating point supported now
const int64_t pts64 = ticks_to_timebase_units(timebase, *time_stamp);
if (pts64 < 0 || pts64 > UINT32_MAX) return AOM_CODEC_ERROR;
cpi->common.frame_presentation_time = (uint32_t)pts64;
}
if (oxcf->pass == 2 &&
(!cm->show_existing_frame || cpi->rc.is_src_frame_alt_ref)) {
// GF_GROUP needs updating for arf overlays as well as non-show-existing
av1_rc_get_second_pass_params(cpi, &frame_params);
}
if (cm->show_existing_frame && frame_params.frame_type != KEY_FRAME) {
// Force show-existing frames to be INTER, except forward keyframes
frame_params.frame_type = INTER_FRAME;
}
if (!cm->show_existing_frame) {
cm->using_qmatrix = cpi->oxcf.using_qm;
cm->min_qmlevel = cpi->oxcf.qm_minlevel;
cm->max_qmlevel = cpi->oxcf.qm_maxlevel;
if (cpi->twopass.gf_group.index == 1 && cpi->oxcf.enable_tpl_model) {
av1_set_frame_size(cpi, cm->width, cm->height);
av1_tpl_setup_stats(cpi, &frame_input);
}
}
frame_params.frame_flags = frame_flags;
// TODO(david.turner@argondesign.com): Move all the encode strategy
// (largely near av1_get_compressed_data) in here
// TODO(david.turner@argondesign.com): Change all the encode strategy to
// modify frame_params instead of cm or cpi.
// Per-frame encode speed. In theory this can vary, but things may have been
// written assuming speed-level will not change within a sequence, so this
// parameter should be used with caution.
frame_params.speed = oxcf->speed;
// Work out some encoding parameters specific to the pass:
if (oxcf->pass == 0) {
if (cpi->oxcf.rc_mode == AOM_CBR) {
av1_rc_get_one_pass_cbr_params(cpi, &frame_params);
} else {
av1_rc_get_one_pass_vbr_params(cpi, &frame_params);
}
} else if (oxcf->pass == 1) {
av1_setup_frame_size(cpi);
cpi->td.mb.e_mbd.lossless[0] = is_lossless_requested(&cpi->oxcf);
if (!cpi->refresh_alt_ref_frame && (cm->current_frame.frame_number == 0 ||
(cpi->frame_flags & FRAMEFLAGS_KEY))) {
frame_params.frame_type = KEY_FRAME;
} else {
frame_params.frame_type = INTER_FRAME;
}
} else if (oxcf->pass == 2) {
#if CONFIG_MISMATCH_DEBUG
mismatch_move_frame_idx_w();
#endif
#if TXCOEFF_COST_TIMER
cm->txcoeff_cost_timer = 0;
cm->txcoeff_cost_count = 0;
#endif
}
if (oxcf->pass == 0 || oxcf->pass == 2) {
// Apply external override flags
set_ext_overrides(cpi, &frame_params);
// Work out which reference frame slots may be used.
frame_params.ref_frame_flags = get_ref_frame_flags(cpi);
}
if (oxcf->pass == 0 || oxcf->pass == 2) {
frame_params.primary_ref_frame =
choose_primary_ref_frame(cpi, &frame_params);
frame_params.order_offset = get_order_offset(cpi, &frame_params);
}
if (av1_encode(cpi, dest, &frame_input, &frame_params, &frame_results) !=
AOM_CODEC_OK) {
return AOM_CODEC_ERROR;
}
if (oxcf->pass == 2) {
#if TXCOEFF_COST_TIMER
cm->cum_txcoeff_cost_timer += cm->txcoeff_cost_timer;
fprintf(stderr,
"\ntxb coeff cost block number: %ld, frame time: %ld, cum time %ld "
"in us\n",
cm->txcoeff_cost_count, cm->txcoeff_cost_timer,
cm->cum_txcoeff_cost_timer);
#endif
av1_twopass_postencode_update(cpi);
}
if (oxcf->pass == 0 || oxcf->pass == 2) {
update_fb_of_context_type(cpi, &frame_params, cpi->fb_of_context_type);
set_additional_frame_flags(cm, frame_params.frame_flags);
update_rc_counts(cpi);
check_show_existing_frame(cpi); // Is next frame a show_existing frame?
}
// Unpack frame_results:
*size = frame_results.size;
// Leave a signal for a higher level caller about if this frame is droppable
if (*size > 0) {
cpi->droppable = is_frame_droppable(cpi);
}
return AOM_CODEC_OK;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#ifndef AOM_AV1_ENCODER_ENCODE_STRATEGY_H_
#define AOM_AV1_ENCODER_ENCODE_STRATEGY_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "aom/aom_encoder.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/firstpass.h"
// This function will implement high-level encode strategy, choosing frame type,
// frame placement, etc. It populates an EncodeFrameParams struct with the
// results of these decisions and then calls av1_encode()
int av1_encode_strategy(AV1_COMP *const cpi, size_t *const size,
uint8_t *const dest, unsigned int *frame_flags,
int64_t *const time_stamp, int64_t *const time_end,
const aom_rational_t *const timebase, int flush);
// Set individual buffer update flags based on frame reference type
void av1_configure_buffer_updates(AV1_COMP *const cpi,
const FRAME_UPDATE_TYPE type);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // AOM_AV1_ENCODER_ENCODE_STRATEGY_H_

File diff suppressed because it is too large Load diff

View file

@ -29,7 +29,7 @@ struct ThreadData;
void av1_setup_src_planes(struct macroblock *x,
const struct yv12_buffer_config *src, int mi_row,
int mi_col, const int num_planes);
int mi_col, const int num_planes, BLOCK_SIZE bsize);
void av1_encode_frame(struct AV1_COMP *cpi);

View file

@ -43,7 +43,7 @@ static void subtract_block(const MACROBLOCKD *xd, int rows, int cols,
const uint8_t *src8, ptrdiff_t src_stride,
const uint8_t *pred8, ptrdiff_t pred_stride) {
if (check_subtract_block_size(rows, cols)) {
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
aom_highbd_subtract_block_c(rows, cols, diff, diff_stride, src8,
src_stride, pred8, pred_stride, xd->bd);
return;
@ -54,7 +54,7 @@ static void subtract_block(const MACROBLOCKD *xd, int rows, int cols,
return;
}
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
aom_highbd_subtract_block(rows, cols, diff, diff_stride, src8, src_stride,
pred8, pred_stride, xd->bd);
return;
@ -116,11 +116,11 @@ int av1_optimize_b(const struct AV1_COMP *cpi, MACROBLOCK *mb, int plane,
rate_cost, cpi->oxcf.sharpness);
}
typedef enum QUANT_FUNC {
enum {
QUANT_FUNC_LOWBD = 0,
QUANT_FUNC_HIGHBD = 1,
QUANT_FUNC_TYPES = 2
} QUANT_FUNC;
} UENUM1BYTE(QUANT_FUNC);
static AV1_QUANT_FACADE
quant_func_list[AV1_XFORM_QUANT_TYPES][QUANT_FUNC_TYPES] = {
@ -163,6 +163,7 @@ void av1_xform_quant(const AV1_COMMON *cm, MACROBLOCK *x, int plane, int block,
qparam.tx_size = tx_size;
qparam.qmatrix = qmatrix;
qparam.iqmatrix = iqmatrix;
qparam.use_quant_b_adapt = cm->use_quant_b_adapt;
TxfmParam txfm_param;
txfm_param.tx_type = tx_type;
txfm_param.tx_size = tx_size;
@ -171,7 +172,7 @@ void av1_xform_quant(const AV1_COMMON *cm, MACROBLOCK *x, int plane, int block,
txfm_param.tx_size, is_inter_block(mbmi), cm->reduced_tx_set_used);
txfm_param.bd = xd->bd;
txfm_param.is_hbd = get_bitdepth_data_path_index(xd);
txfm_param.is_hbd = is_cur_buf_hbd(xd);
av1_fwd_txfm(src_diff, coeff, diff_stride, &txfm_param);
@ -226,7 +227,7 @@ static void encode_block(int plane, int block, int blk_row, int blk_col,
if (!is_blk_skip(x, plane, blk_row * bw + blk_col) && !mbmi->skip_mode) {
TX_TYPE tx_type = av1_get_tx_type(pd->plane_type, xd, blk_row, blk_col,
tx_size, cm->reduced_tx_set_used);
if (args->enable_optimize_b) {
if (args->enable_optimize_b != NO_TRELLIS_OPT) {
av1_xform_quant(cm, x, plane, block, blk_row, blk_col, plane_bsize,
tx_size, tx_type, AV1_XFORM_QUANT_FP);
TXB_CTX txb_ctx;
@ -256,11 +257,11 @@ static void encode_block(int plane, int block, int blk_row, int blk_col,
}
if (p->eobs[block] == 0 && plane == 0) {
// TODO(debargha, jingning): Temporarily disable txk_type check for eob=0
// case. It is possible that certain collision in hash index would cause
// the assertion failure. To further optimize the rate-distortion
// performance, we need to re-visit this part and enable this assert
// again.
// TODO(debargha, jingning): Temporarily disable txk_type check for eob=0
// case. It is possible that certain collision in hash index would cause
// the assertion failure. To further optimize the rate-distortion
// performance, we need to re-visit this part and enable this assert
// again.
#if 0
if (args->cpi->oxcf.aq_mode == NO_AQ &&
args->cpi->oxcf.deltaq_mode == NO_DELTA_Q) {
@ -285,8 +286,8 @@ static void encode_block(int plane, int block, int blk_row, int blk_col,
int blk_h = block_size_high[bsize];
mi_to_pixel_loc(&pixel_c, &pixel_r, mi_col, mi_row, blk_col, blk_row,
pd->subsampling_x, pd->subsampling_y);
mismatch_record_block_tx(dst, pd->dst.stride, cm->frame_offset, plane,
pixel_c, pixel_r, blk_w, blk_h,
mismatch_record_block_tx(dst, pd->dst.stride, cm->current_frame.order_hint,
plane, pixel_c, pixel_r, blk_w, blk_h,
xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH);
}
#endif
@ -431,7 +432,7 @@ static void encode_block_pass1(int plane, int block, int blk_row, int blk_col,
if (p->eobs[block] > 0) {
txfm_param.bd = xd->bd;
txfm_param.is_hbd = get_bitdepth_data_path_index(xd);
txfm_param.is_hbd = is_cur_buf_hbd(xd);
txfm_param.tx_type = DCT_DCT;
txfm_param.tx_size = tx_size;
txfm_param.eob = p->eobs[block];
@ -578,7 +579,7 @@ void av1_encode_block_intra(int plane, int block, int blk_row, int blk_col,
const ENTROPY_CONTEXT *a = &args->ta[blk_col];
const ENTROPY_CONTEXT *l = &args->tl[blk_row];
if (args->enable_optimize_b) {
if (args->enable_optimize_b != NO_TRELLIS_OPT) {
av1_xform_quant(cm, x, plane, block, blk_row, blk_col, plane_bsize,
tx_size, tx_type, AV1_XFORM_QUANT_FP);
TXB_CTX txb_ctx;
@ -598,11 +599,11 @@ void av1_encode_block_intra(int plane, int block, int blk_row, int blk_col,
}
if (*eob == 0 && plane == 0) {
// TODO(jingning): Temporarily disable txk_type check for eob=0 case.
// It is possible that certain collision in hash index would cause
// the assertion failure. To further optimize the rate-distortion
// performance, we need to re-visit this part and enable this assert
// again.
// TODO(jingning): Temporarily disable txk_type check for eob=0 case.
// It is possible that certain collision in hash index would cause
// the assertion failure. To further optimize the rate-distortion
// performance, we need to re-visit this part and enable this assert
// again.
#if 0
if (args->cpi->oxcf.aq_mode == NO_AQ
&& args->cpi->oxcf.deltaq_mode == NO_DELTA_Q) {

View file

@ -37,13 +37,13 @@ struct encode_b_args {
int8_t enable_optimize_b;
};
typedef enum AV1_XFORM_QUANT {
enum {
AV1_XFORM_QUANT_FP = 0,
AV1_XFORM_QUANT_B = 1,
AV1_XFORM_QUANT_DC = 2,
AV1_XFORM_QUANT_SKIP_QUANT,
AV1_XFORM_QUANT_TYPES,
} AV1_XFORM_QUANT;
} UENUM1BYTE(AV1_XFORM_QUANT);
void av1_encode_sb(const struct AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
int mi_row, int mi_col, RUN_TYPE dry_run);

File diff suppressed because it is too large Load diff

View file

@ -59,7 +59,7 @@ typedef struct {
FRAME_CONTEXT fc;
} CODING_CONTEXT;
typedef enum {
enum {
// regular inter frame
REGULAR_FRAME = 0,
// alternate reference frame
@ -73,51 +73,54 @@ typedef enum {
// extra alternate reference frame
EXT_ARF_FRAME = 5,
FRAME_CONTEXT_INDEXES
} FRAME_CONTEXT_INDEX;
} UENUM1BYTE(FRAME_CONTEXT_INDEX);
typedef enum {
enum {
NORMAL = 0,
FOURFIVE = 1,
THREEFIVE = 2,
ONETWO = 3
} AOM_SCALING;
} UENUM1BYTE(AOM_SCALING);
typedef enum {
enum {
// Good Quality Fast Encoding. The encoder balances quality with the amount of
// time it takes to encode the output. Speed setting controls how fast.
GOOD
} MODE;
} UENUM1BYTE(MODE);
typedef enum {
enum {
FRAMEFLAGS_KEY = 1 << 0,
FRAMEFLAGS_GOLDEN = 1 << 1,
FRAMEFLAGS_BWDREF = 1 << 2,
// TODO(zoeliu): To determine whether a frame flag is needed for ALTREF2_FRAME
FRAMEFLAGS_ALTREF = 1 << 3,
} FRAMETYPE_FLAGS;
FRAMEFLAGS_INTRAONLY = 1 << 4,
FRAMEFLAGS_SWITCH = 1 << 5,
FRAMEFLAGS_ERROR_RESILIENT = 1 << 6,
} UENUM1BYTE(FRAMETYPE_FLAGS);
typedef enum {
enum {
NO_AQ = 0,
VARIANCE_AQ = 1,
COMPLEXITY_AQ = 2,
CYCLIC_REFRESH_AQ = 3,
AQ_MODE_COUNT // This should always be the last member of the enum
} AQ_MODE;
typedef enum {
} UENUM1BYTE(AQ_MODE);
enum {
NO_DELTA_Q = 0,
DELTA_Q_ONLY = 1,
DELTA_Q_LF = 2,
DELTAQ_MODE_COUNT // This should always be the last member of the enum
} DELTAQ_MODE;
} UENUM1BYTE(DELTAQ_MODE);
typedef enum {
enum {
RESIZE_NONE = 0, // No frame resizing allowed.
RESIZE_FIXED = 1, // All frames are coded at the specified scale.
RESIZE_RANDOM = 2, // All frames are coded at a random scale.
RESIZE_MODES
} RESIZE_MODE;
} UENUM1BYTE(RESIZE_MODE);
typedef enum {
enum {
SUPERRES_NONE = 0, // No frame superres allowed
SUPERRES_FIXED = 1, // All frames are coded at the specified scale,
// and super-resolved.
@ -126,7 +129,31 @@ typedef enum {
SUPERRES_QTHRESH = 3, // Superres scale for a frame is determined based on
// q_index
SUPERRES_MODES
} SUPERRES_MODE;
} UENUM1BYTE(SUPERRES_MODE);
typedef struct TplDepStats {
int64_t intra_cost;
int64_t inter_cost;
int64_t mc_flow;
int64_t mc_dep_cost;
int64_t mc_ref_cost;
int ref_frame_index;
int_mv mv;
} TplDepStats;
typedef struct TplDepFrame {
uint8_t is_valid;
TplDepStats *tpl_stats_ptr;
int stride;
int width;
int height;
int mi_rows;
int mi_cols;
int base_qindex;
} TplDepFrame;
#define TPL_DEP_COST_SCALE_LOG2 4
typedef struct AV1EncoderConfig {
BITSTREAM_PROFILE profile;
@ -188,6 +215,7 @@ typedef struct AV1EncoderConfig {
DELTAQ_MODE deltaq_mode;
int enable_cdef;
int enable_restoration;
int enable_obmc;
int disable_trellis_quant;
int using_qm;
int qm_y;
@ -247,6 +275,7 @@ typedef struct AV1EncoderConfig {
int min_gf_interval;
int max_gf_interval;
int gf_max_pyr_height;
int row_mt;
int tile_columns;
@ -256,10 +285,11 @@ typedef struct AV1EncoderConfig {
int tile_widths[MAX_TILE_COLS];
int tile_heights[MAX_TILE_ROWS];
int enable_tpl_model;
int max_threads;
aom_fixed_buf_t two_pass_stats_in;
struct aom_codec_pkt_list *output_pkt_list;
#if CONFIG_FP_MB_STATS
aom_fixed_buf_t firstpass_mb_stats_in;
@ -275,15 +305,12 @@ typedef struct AV1EncoderConfig {
int color_range;
int render_width;
int render_height;
aom_timing_info_type_t timing_info_type;
int timing_info_present;
aom_timing_info_t timing_info;
int decoder_model_info_present_flag;
int display_model_info_present_flag;
int buffer_removal_time_present;
aom_dec_model_info_t buffer_model;
aom_dec_model_op_parameters_t op_params[MAX_NUM_OPERATING_POINTS + 1];
aom_op_timing_info_t op_frame_timing[MAX_NUM_OPERATING_POINTS + 1];
int film_grain_test_vector;
const char *film_grain_table_filename;
@ -291,18 +318,36 @@ typedef struct AV1EncoderConfig {
aom_superblock_size_t superblock_size;
unsigned int large_scale_tile;
unsigned int single_tile_decoding;
int monochrome;
uint8_t monochrome;
unsigned int full_still_picture_hdr;
int enable_dual_filter;
unsigned int motion_vector_unit_test;
const cfg_options_t *cfg;
int enable_rect_partitions;
int enable_intra_edge_filter;
int enable_tx64;
int enable_order_hint;
int enable_jnt_comp;
int enable_dist_wtd_comp;
int enable_ref_frame_mvs;
unsigned int max_reference_frames;
unsigned int allow_ref_frame_mvs;
int enable_masked_comp;
int enable_interintra_comp;
int enable_smooth_interintra;
int enable_diff_wtd_comp;
int enable_interinter_wedge;
int enable_interintra_wedge;
int enable_global_motion;
int enable_warped_motion;
int allow_warped_motion;
int enable_filter_intra;
int enable_smooth_intra;
int enable_paeth_intra;
int enable_cfl_intra;
int enable_superres;
int enable_palette;
int enable_intrabc;
int enable_angle_delta;
unsigned int save_as_annexb;
#if CONFIG_DENOISE
@ -312,6 +357,11 @@ typedef struct AV1EncoderConfig {
unsigned int chroma_subsampling_x;
unsigned int chroma_subsampling_y;
int reduced_tx_type_set;
int use_intra_dct_only;
int use_inter_dct_only;
int quant_b_adapt;
int border_in_pixels;
} AV1EncoderConfig;
static INLINE int is_lossless_requested(const AV1EncoderConfig *cfg) {
@ -404,7 +454,6 @@ typedef struct FRAME_COUNTS {
[SWITCHABLE_FILTERS];
} FRAME_COUNTS;
#if CONFIG_COLLECT_INTER_MODE_RD_STATS
#define INTER_MODE_RD_DATA_OVERALL_SIZE 6400
typedef struct {
@ -439,22 +488,37 @@ typedef struct inter_modes_info {
int64_t est_rd_arr[MAX_INTER_MODES];
RdIdxPair rd_idx_pair_arr[MAX_INTER_MODES];
} InterModesInfo;
// Encoder row synchronization
typedef struct AV1RowMTSyncData {
#if CONFIG_MULTITHREAD
pthread_mutex_t *mutex_;
pthread_cond_t *cond_;
#endif
// Allocate memory to store the sb/mb block index in each row.
int *cur_col;
int sync_range;
int rows;
} AV1RowMTSync;
typedef struct AV1RowMTInfo {
int current_mi_row;
int num_threads_working;
} AV1RowMTInfo;
// TODO(jingning) All spatially adaptive variables should go to TileDataEnc.
typedef struct TileDataEnc {
TileInfo tile_info;
int thresh_freq_fact[BLOCK_SIZES_ALL][MAX_MODES];
int mode_map[BLOCK_SIZES_ALL][MAX_MODES];
int m_search_count;
int ex_search_count;
CFL_CTX cfl;
DECLARE_ALIGNED(16, FRAME_CONTEXT, tctx);
FRAME_CONTEXT *row_ctx;
uint8_t allow_update_cdf;
#if CONFIG_COLLECT_INTER_MODE_RD_STATS
InterModeRdModel inter_mode_rd_models[BLOCK_SIZES_ALL];
InterModesInfo inter_modes_info;
#endif
AV1RowMTSync row_mt_sync;
AV1RowMTInfo row_mt_info;
} TileDataEnc;
typedef struct {
@ -463,6 +527,13 @@ typedef struct {
unsigned int count;
} TOKENLIST;
typedef struct MultiThreadHandle {
int allocated_tile_rows;
int allocated_tile_cols;
int allocated_sb_rows;
int thread_id_to_tile_id[MAX_NUM_THREADS]; // Mapping of threads to tiles
} MultiThreadHandle;
typedef struct RD_COUNTS {
int64_t comp_pred_diff[REFERENCE_MODES];
// Stores number of 4x4 blocks using global motion per reference frame.
@ -477,6 +548,10 @@ typedef struct ThreadData {
FRAME_COUNTS *counts;
PC_TREE *pc_tree;
PC_TREE *pc_root[MAX_MIB_SIZE_LOG2 - MIN_MIB_SIZE_LOG2 + 1];
tran_low_t *tree_coeff_buf[MAX_MB_PLANE];
tran_low_t *tree_qcoeff_buf[MAX_MB_PLANE];
tran_low_t *tree_dqcoeff_buf[MAX_MB_PLANE];
InterModesInfo *inter_modes_info;
uint32_t *hash_value_buffer[2][2];
int32_t *wsrc_buf;
int32_t *mask_buf;
@ -485,7 +560,8 @@ typedef struct ThreadData {
PALETTE_BUFFER *palette_buffer;
CONV_BUF_TYPE *tmp_conv_dst;
uint8_t *tmp_obmc_bufs[2];
int intrabc_used_this_tile;
int intrabc_used;
FRAME_CONTEXT *tctx;
} ThreadData;
struct EncWorkerData;
@ -498,13 +574,13 @@ typedef struct ActiveMap {
#if CONFIG_INTERNAL_STATS
// types of stats
typedef enum {
enum {
STAT_Y,
STAT_U,
STAT_V,
STAT_ALL,
NUM_STAT_TYPES // This should always be the last member of the enum
} StatType;
} UENUM1BYTE(StatType);
typedef struct IMAGE_STAT {
double stat[NUM_STAT_TYPES];
@ -517,10 +593,14 @@ typedef struct {
YV12_BUFFER_CONFIG buf;
} EncRefCntBuffer;
typedef struct TileBufferEnc {
uint8_t *data;
size_t size;
} TileBufferEnc;
#if CONFIG_COLLECT_PARTITION_STATS
typedef struct PartitionStats {
int partition_decisions[6][EXT_PARTITION_TYPES];
int partition_attempts[6][EXT_PARTITION_TYPES];
int partition_redo;
} PartitionStats;
#endif
typedef struct AV1_COMP {
QUANTS quants;
@ -545,6 +625,9 @@ typedef struct AV1_COMP {
YV12_BUFFER_CONFIG *unscaled_last_source;
YV12_BUFFER_CONFIG scaled_last_source;
TplDepFrame tpl_stats[MAX_LAG_BUFFERS];
YV12_BUFFER_CONFIG *tpl_recon_frames[INTER_REFS_PER_FRAME + 1];
// For a still frame, this flag is set to 1 to skip partition search.
int partition_search_skippable_frame;
double csm_rate_array[32];
@ -553,23 +636,41 @@ typedef struct AV1_COMP {
int rate_index;
hash_table *previous_hash_table;
int previous_index;
int cur_poc; // DebugInfo
unsigned int row_mt;
int scaled_ref_idx[REF_FRAMES];
int ref_fb_idx[REF_FRAMES];
int refresh_fb_idx; // ref frame buffer index to refresh
RefCntBuffer *scaled_ref_buf[INTER_REFS_PER_FRAME];
int last_show_frame_buf_idx; // last show frame buffer index
RefCntBuffer *last_show_frame_buf; // last show frame buffer
// refresh_*_frame are boolean flags. If 'refresh_xyz_frame' is true, then
// after the current frame is encoded, the XYZ reference frame gets refreshed
// (updated) to be the current frame.
//
// Special case: 'refresh_last_frame' specifies that:
// - LAST_FRAME reference should be updated to be the current frame (as usual)
// - Also, LAST2_FRAME and LAST3_FRAME references are implicitly updated to be
// the two past reference frames just before LAST_FRAME that are available.
//
// Note: Usually at most one of these refresh flags is true at a time.
// But a key-frame is special, for which all the flags are true at once.
int refresh_last_frame;
int refresh_golden_frame;
int refresh_bwd_ref_frame;
int refresh_alt2_ref_frame;
int refresh_alt_ref_frame;
#if USE_SYMM_MULTI_LAYER
// For each type of reference frame, this contains the index of a reference
// frame buffer for a reference frame of the same type. We use this to
// choose our primary reference frame (which is the most recent reference
// frame of the same type as the current frame).
int fb_of_context_type[REF_FRAMES];
// When true, a new rule for backward (future) reference frames is in effect:
// - BWDREF_FRAME is always the closest future frame available
// - ALTREF2_FRAME is always the 2nd closest future frame available
// - 'refresh_bwd_ref_frame' flag is used for updating both the BWDREF_FRAME
// and ALTREF2_FRAME. ('refresh_alt2_ref_frame' flag is irrelevant).
int new_bwdref_update_rule;
#endif
int ext_refresh_frame_flags_pending;
int ext_refresh_last_frame;
@ -608,9 +709,10 @@ typedef struct AV1_COMP {
RATE_CONTROL rc;
double framerate;
// NOTE(zoeliu): Any inter frame allows maximum of REF_FRAMES inter
// references; Plus the currently coded frame itself, it is needed to allocate
// sufficient space to the size of the maximum possible number of frames.
// Relevant for an inter frame.
// - Index '0' corresponds to the values for the currently coded frame.
// - Indices LAST_FRAME ... EXTREF_FRAMES are used to store values for all the
// possible inter reference frames.
int interp_filter_selected[REF_FRAMES + 1][SWITCHABLE];
struct aom_codec_pkt_list *output_pkt_list;
@ -620,14 +722,15 @@ typedef struct AV1_COMP {
int static_mb_pct; // % forced skip mbs by segmentation
int ref_frame_flags;
int ext_ref_frame_flags;
RATE_FACTOR_LEVEL frame_rf_level[FRAME_BUFFERS];
// speed is passed as a per-frame parameter into the encoder
int speed;
// sf contains fine-grained config set internally based on speed
SPEED_FEATURES sf;
unsigned int max_mv_magnitude;
int mv_step_param;
int allow_comp_inter_inter;
int all_one_sided_refs;
uint8_t *segmentation_map;
@ -641,7 +744,6 @@ typedef struct AV1_COMP {
uint64_t time_receive_data;
uint64_t time_compress_data;
uint64_t time_pick_lpf;
uint64_t time_encode_sb_row;
#if CONFIG_FP_MB_STATS
int use_fp_mb_stats;
@ -680,6 +782,9 @@ typedef struct AV1_COMP {
Metrics metrics;
#endif
int b_calculate_psnr;
#if CONFIG_SPEED_STATS
unsigned int tx_search_count;
#endif // CONFIG_SPEED_STATS
int droppable;
@ -705,15 +810,11 @@ typedef struct AV1_COMP {
int allocated_tiles; // Keep track of memory allocated for tiles.
TOKENEXTRA *tile_tok[MAX_TILE_ROWS][MAX_TILE_COLS];
unsigned int tok_count[MAX_TILE_ROWS][MAX_TILE_COLS];
TOKENLIST *tplist[MAX_TILE_ROWS][MAX_TILE_COLS];
TileBufferEnc tile_buffers[MAX_TILE_ROWS][MAX_TILE_COLS];
int resize_state;
int resize_avg_qp;
int resize_buffer_underflow;
int resize_count;
// Sequence parameters have been transmitted already and locked
// or not. Once locked av1_change_config cannot change the seq
@ -727,14 +828,12 @@ typedef struct AV1_COMP {
int num_workers;
AVxWorker *workers;
struct EncWorkerData *tile_thr_data;
int refresh_frame_mask;
int existing_fb_idx_to_show;
int is_arf_filter_off[MAX_EXT_ARFS + 1];
int num_extra_arfs;
int arf_pos_in_gf[MAX_EXT_ARFS + 1];
int arf_pos_for_ovrly[MAX_EXT_ARFS + 1];
int global_motion_search_done;
tran_low_t *tcoeff_buf[MAX_MB_PLANE];
int extra_arf_allowed;
// A flag to indicate if intrabc is ever used in current frame.
int intrabc_used;
@ -743,10 +842,16 @@ typedef struct AV1_COMP {
int dv_joint_cost[MV_JOINTS];
int has_lossless_segment;
// For frame refs short signaling:
// A mapping of each reference frame from its encoder side value to the
// decoder side value obtained following the short signaling procedure.
int ref_conv[REF_FRAMES];
// Factors to control gating of compound type selection based on best
// approximate rd so far
int max_comp_type_rd_threshold_mul;
int max_comp_type_rd_threshold_div;
unsigned int tx_domain_dist_threshold;
// Factor to control R-D optimization of coeffs based on block
// mse.
unsigned int coeff_opt_dist_threshold;
AV1LfSync lf_row_sync;
AV1LrSync lr_row_sync;
@ -760,8 +865,50 @@ typedef struct AV1_COMP {
// Set as 1 for monochrome and 3 for other color formats
int default_interp_skip_flags;
int preserve_arf_as_gld;
MultiThreadHandle multi_thread_ctxt;
void (*row_mt_sync_read_ptr)(AV1RowMTSync *const, int, int);
void (*row_mt_sync_write_ptr)(AV1RowMTSync *const, int, int, const int);
#if CONFIG_MULTITHREAD
pthread_mutex_t *row_mt_mutex_;
#endif
// Set if screen content is set or relevant tools are enabled
int is_screen_content_type;
#if CONFIG_COLLECT_PARTITION_STATS
PartitionStats partition_stats;
#endif
} AV1_COMP;
typedef struct {
YV12_BUFFER_CONFIG *source;
YV12_BUFFER_CONFIG *last_source;
int64_t ts_duration;
} EncodeFrameInput;
// EncodeFrameParams contains per-frame encoding parameters decided upon by
// av1_encode_strategy() and passed down to av1_encode()
struct EncodeFrameParams {
int error_resilient_mode;
FRAME_TYPE frame_type;
int primary_ref_frame;
int order_offset;
int show_frame;
// This is a bitmask of which reference slots can be used in this frame
int ref_frame_flags;
// Speed level to use for this frame: Bigger number means faster.
int speed;
unsigned int *frame_flags;
};
typedef struct EncodeFrameParams EncodeFrameParams;
// EncodeFrameResults contains information about the result of encoding a
// single frame
typedef struct {
size_t size; // Size of resulting bitstream
} EncodeFrameResults;
// Must not be called more than once.
void av1_initialize_enc(void);
@ -782,6 +929,11 @@ int av1_get_compressed_data(AV1_COMP *cpi, unsigned int *frame_flags,
int64_t *time_end, int flush,
const aom_rational_t *timebase);
int av1_encode(AV1_COMP *const cpi, uint8_t *const dest,
const EncodeFrameInput *const frame_input,
const EncodeFrameParams *const frame_params,
EncodeFrameResults *const frame_results);
int av1_get_preview_raw_frame(AV1_COMP *cpi, YV12_BUFFER_CONFIG *dest);
int av1_get_last_show_frame(AV1_COMP *cpi, YV12_BUFFER_CONFIG *frame);
@ -792,12 +944,12 @@ aom_codec_err_t av1_copy_new_frame_enc(AV1_COMMON *cm,
int av1_use_as_reference(AV1_COMP *cpi, int ref_frame_flags);
void av1_update_reference(AV1_COMP *cpi, int ref_frame_flags);
int av1_copy_reference_enc(AV1_COMP *cpi, int idx, YV12_BUFFER_CONFIG *sd);
int av1_set_reference_enc(AV1_COMP *cpi, int idx, YV12_BUFFER_CONFIG *sd);
void av1_set_frame_size(AV1_COMP *cpi, int width, int height);
int av1_update_entropy(AV1_COMP *cpi, int update);
int av1_set_active_map(AV1_COMP *cpi, unsigned char *map, int rows, int cols);
@ -811,59 +963,62 @@ int av1_get_quantizer(struct AV1_COMP *cpi);
int av1_convert_sect5obus_to_annexb(uint8_t *buffer, size_t *input_size);
int64_t timebase_units_to_ticks(const aom_rational_t *timebase, int64_t n);
int64_t ticks_to_timebase_units(const aom_rational_t *timebase, int64_t n);
// av1 uses 10,000,000 ticks/second as time stamp
#define TICKS_PER_SEC 10000000LL
static INLINE int64_t timebase_units_to_ticks(const aom_rational_t *timebase,
int64_t n) {
return n * TICKS_PER_SEC * timebase->num / timebase->den;
}
static INLINE int64_t ticks_to_timebase_units(const aom_rational_t *timebase,
int64_t n) {
const int64_t round = TICKS_PER_SEC * timebase->num / 2 - 1;
return (n * timebase->den + round) / timebase->num / TICKS_PER_SEC;
}
static INLINE int frame_is_kf_gf_arf(const AV1_COMP *cpi) {
return frame_is_intra_only(&cpi->common) || cpi->refresh_alt_ref_frame ||
(cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref);
}
static INLINE int get_ref_frame_map_idx(const AV1_COMP *cpi,
MV_REFERENCE_FRAME ref_frame) {
return (ref_frame >= 1) ? cpi->ref_fb_idx[ref_frame - 1] : INVALID_IDX;
}
static INLINE int get_ref_frame_buf_idx(const AV1_COMP *cpi,
MV_REFERENCE_FRAME ref_frame) {
const AV1_COMMON *const cm = &cpi->common;
const int map_idx = get_ref_frame_map_idx(cpi, ref_frame);
return (map_idx != INVALID_IDX) ? cm->ref_frame_map[map_idx] : INVALID_IDX;
}
// TODO(huisu@google.com, youzhou@microsoft.com): enable hash-me for HBD.
static INLINE int av1_use_hash_me(const AV1_COMMON *const cm) {
return cm->allow_screen_content_tools;
}
static INLINE hash_table *av1_get_ref_frame_hash_map(
const AV1_COMP *cpi, MV_REFERENCE_FRAME ref_frame) {
const AV1_COMMON *const cm = &cpi->common;
const int buf_idx = get_ref_frame_buf_idx(cpi, ref_frame);
return buf_idx != INVALID_IDX
? &cm->buffer_pool->frame_bufs[buf_idx].hash_table
: NULL;
const AV1_COMMON *cm, MV_REFERENCE_FRAME ref_frame) {
const int map_idx = get_ref_frame_map_idx(cm, ref_frame);
RefCntBuffer *buf =
(map_idx != INVALID_IDX) ? cm->ref_frame_map[map_idx] : NULL;
return buf ? &buf->hash_table : NULL;
}
static INLINE YV12_BUFFER_CONFIG *get_ref_frame_buffer(
const AV1_COMP *cpi, MV_REFERENCE_FRAME ref_frame) {
const AV1_COMMON *const cm = &cpi->common;
const int buf_idx = get_ref_frame_buf_idx(cpi, ref_frame);
return buf_idx != INVALID_IDX ? &cm->buffer_pool->frame_bufs[buf_idx].buf
: NULL;
static INLINE const YV12_BUFFER_CONFIG *get_ref_frame_yv12_buf(
const AV1_COMMON *const cm, MV_REFERENCE_FRAME ref_frame) {
const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame);
return buf != NULL ? &buf->buf : NULL;
}
static INLINE int enc_is_ref_frame_buf(AV1_COMP *cpi, RefCntBuffer *frame_buf) {
static INLINE int enc_is_ref_frame_buf(const AV1_COMMON *const cm,
const RefCntBuffer *const frame_buf) {
MV_REFERENCE_FRAME ref_frame;
AV1_COMMON *const cm = &cpi->common;
for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
const int buf_idx = get_ref_frame_buf_idx(cpi, ref_frame);
if (buf_idx == INVALID_IDX) continue;
if (frame_buf == &cm->buffer_pool->frame_bufs[buf_idx]) break;
const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame);
if (buf == NULL) continue;
if (frame_buf == buf) break;
}
return (ref_frame <= ALTREF_FRAME);
}
static INLINE void alloc_frame_mvs(AV1_COMMON *const cm, RefCntBuffer *buf) {
assert(buf != NULL);
ensure_mv_buffer(buf, cm);
buf->width = cm->width;
buf->height = cm->height;
}
// Token buffer is only used for palette tokens.
static INLINE unsigned int get_token_alloc(int mb_rows, int mb_cols,
int sb_size_log2,
@ -919,10 +1074,10 @@ static INLINE int is_altref_enabled(const AV1_COMP *const cpi) {
static INLINE void set_ref_ptrs(const AV1_COMMON *cm, MACROBLOCKD *xd,
MV_REFERENCE_FRAME ref0,
MV_REFERENCE_FRAME ref1) {
xd->block_refs[0] =
&cm->frame_refs[ref0 >= LAST_FRAME ? ref0 - LAST_FRAME : 0];
xd->block_refs[1] =
&cm->frame_refs[ref1 >= LAST_FRAME ? ref1 - LAST_FRAME : 0];
xd->block_ref_scale_factors[0] =
get_ref_scale_factors_const(cm, ref0 >= LAST_FRAME ? ref0 : 1);
xd->block_ref_scale_factors[1] =
get_ref_scale_factors_const(cm, ref1 >= LAST_FRAME ? ref1 : 1);
}
static INLINE int get_chessboard_index(int frame_index) {
@ -935,20 +1090,10 @@ static INLINE int *cond_cost_list(const struct AV1_COMP *cpi, int *cost_list) {
void av1_new_framerate(AV1_COMP *cpi, double framerate);
void av1_setup_frame_size(AV1_COMP *cpi);
#define LAYER_IDS_TO_IDX(sl, tl, num_tl) ((sl) * (num_tl) + (tl))
// Update up-sampled reference frame index.
static INLINE void uref_cnt_fb(EncRefCntBuffer *ubufs, int *uidx,
int new_uidx) {
const int ref_index = *uidx;
if (ref_index >= 0 && ubufs[ref_index].ref_count > 0)
ubufs[ref_index].ref_count--;
*uidx = new_uidx;
ubufs[new_uidx].ref_count++;
}
// Returns 1 if a frame is scaled and 0 otherwise.
static INLINE int av1_resize_scaled(const AV1_COMMON *cm) {
return !(cm->superres_upscaled_width == cm->render_width &&
@ -963,8 +1108,8 @@ static INLINE int av1_frame_scaled(const AV1_COMMON *cm) {
// frame. An exception can be made for a forward keyframe since it has no
// previous dependencies.
static INLINE int encode_show_existing_frame(const AV1_COMMON *cm) {
return cm->show_existing_frame &&
(!cm->error_resilient_mode || cm->frame_type == KEY_FRAME);
return cm->show_existing_frame && (!cm->error_resilient_mode ||
cm->current_frame.frame_type == KEY_FRAME);
}
// Returns a Sequence Header OBU stored in an aom_fixed_buf_t, or NULL upon
@ -978,6 +1123,52 @@ static INLINE int encode_show_existing_frame(const AV1_COMMON *cm) {
// field.
aom_fixed_buf_t *av1_get_global_headers(AV1_COMP *cpi);
#if CONFIG_COLLECT_PARTITION_STATS
static INLINE void av1_print_partition_stats(PartitionStats *part_stats) {
FILE *f = fopen("partition_stats.csv", "w");
if (!f) {
return;
}
fprintf(f, "bsize,redo,");
for (int part = 0; part < EXT_PARTITION_TYPES; part++) {
fprintf(f, "decision_%d,", part);
}
for (int part = 0; part < EXT_PARTITION_TYPES; part++) {
fprintf(f, "attempt_%d,", part);
}
fprintf(f, "\n");
const int bsizes[6] = { 128, 64, 32, 16, 8, 4 };
for (int bsize_idx = 0; bsize_idx < 6; bsize_idx++) {
fprintf(f, "%d,%d,", bsizes[bsize_idx], part_stats->partition_redo);
for (int part = 0; part < EXT_PARTITION_TYPES; part++) {
fprintf(f, "%d,", part_stats->partition_decisions[bsize_idx][part]);
}
for (int part = 0; part < EXT_PARTITION_TYPES; part++) {
fprintf(f, "%d,", part_stats->partition_attempts[bsize_idx][part]);
}
fprintf(f, "\n");
}
fclose(f);
}
static INLINE int av1_get_bsize_idx_for_part_stats(BLOCK_SIZE bsize) {
assert(bsize == BLOCK_128X128 || bsize == BLOCK_64X64 ||
bsize == BLOCK_32X32 || bsize == BLOCK_16X16 || bsize == BLOCK_8X8);
switch (bsize) {
case BLOCK_128X128: return 0;
case BLOCK_64X64: return 1;
case BLOCK_32X32: return 2;
case BLOCK_16X16: return 3;
case BLOCK_8X8: return 4;
case BLOCK_4X4: return 5;
default: assert(0 && "Invalid bsize for partition_stats."); return -1;
}
}
#endif
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -284,20 +284,16 @@ static INLINE int get_sign_bit_cost(tran_low_t qc, int coeff_idx,
return av1_cost_literal(1);
}
static INLINE int get_br_cost(tran_low_t abs_qc, int ctx,
const int *coeff_lps) {
const tran_low_t min_level = 1 + NUM_BASE_LEVELS;
const tran_low_t max_level = 1 + NUM_BASE_LEVELS + COEFF_BASE_RANGE;
(void)ctx;
if (abs_qc >= min_level) {
if (abs_qc >= max_level) {
return coeff_lps[COEFF_BASE_RANGE]; // COEFF_BASE_RANGE * cost0;
} else {
return coeff_lps[(abs_qc - min_level)]; // * cost0 + cost1;
}
}
return 0;
}
static const int golomb_bits_cost[32] = {
0, 512, 512 * 3, 512 * 3, 512 * 5, 512 * 5, 512 * 5, 512 * 5,
512 * 7, 512 * 7, 512 * 7, 512 * 7, 512 * 7, 512 * 7, 512 * 7, 512 * 7,
512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9,
512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9, 512 * 9
};
static const int golomb_cost_diff[32] = {
0, 512, 512 * 2, 0, 512 * 2, 0, 0, 0, 512 * 2, 0, 0, 0, 0, 0, 0, 0,
512 * 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static INLINE int get_golomb_cost(int abs_qc) {
if (abs_qc >= 1 + NUM_BASE_LEVELS + COEFF_BASE_RANGE) {
@ -308,6 +304,32 @@ static INLINE int get_golomb_cost(int abs_qc) {
return 0;
}
static INLINE int get_br_cost_with_diff(tran_low_t level, const int *coeff_lps,
int *diff) {
const int base_range = AOMMIN(level - 1 - NUM_BASE_LEVELS, COEFF_BASE_RANGE);
int golomb_bits = 0;
if (level <= COEFF_BASE_RANGE + 1 + NUM_BASE_LEVELS)
*diff += coeff_lps[base_range + COEFF_BASE_RANGE + 1];
if (level >= COEFF_BASE_RANGE + 1 + NUM_BASE_LEVELS) {
int r = level - COEFF_BASE_RANGE - NUM_BASE_LEVELS;
if (r < 32) {
golomb_bits = golomb_bits_cost[r];
*diff += golomb_cost_diff[r];
} else {
golomb_bits = get_golomb_cost(level);
*diff += (r & (r - 1)) == 0 ? 1024 : 0;
}
}
return coeff_lps[base_range] + golomb_bits;
}
static INLINE int get_br_cost(tran_low_t level, const int *coeff_lps) {
const int base_range = AOMMIN(level - 1 - NUM_BASE_LEVELS, COEFF_BASE_RANGE);
return coeff_lps[base_range] + get_golomb_cost(level);
}
static int get_coeff_cost(const tran_low_t qc, const int scan_idx,
const int is_eob, const TxbInfo *const txb_info,
const LV_MAP_COEFF_COST *const txb_costs,
@ -331,8 +353,7 @@ static int get_coeff_cost(const tran_low_t qc, const int scan_idx,
if (abs_qc > NUM_BASE_LEVELS) {
const int ctx =
get_br_ctx(txb_info->levels, pos, txb_info->bwl, tx_class);
cost += get_br_cost(abs_qc, ctx, txb_costs->lps_cost[ctx]);
cost += get_golomb_cost(abs_qc);
cost += get_br_cost(abs_qc, txb_costs->lps_cost[ctx]);
}
}
return cost;
@ -464,8 +485,6 @@ void av1_txb_init_levels_c(const tran_low_t *const coeff, const int width,
const int stride = width + TX_PAD_HOR;
uint8_t *ls = levels;
memset(levels - TX_PAD_TOP * stride, 0,
sizeof(*levels) * TX_PAD_TOP * stride);
memset(levels + stride * height, 0,
sizeof(*levels) * (TX_PAD_BOTTOM * stride + TX_PAD_END));
@ -554,14 +573,15 @@ void av1_write_coeffs_txb(const AV1_COMMON *const cm, MACROBLOCKD *xd,
break;
}
if (k_eob_offset_bits[eob_pt] > 0) {
const int eob_offset_bits = k_eob_offset_bits[eob_pt];
if (eob_offset_bits > 0) {
const int eob_ctx = eob_pt - 3;
int eob_shift = k_eob_offset_bits[eob_pt] - 1;
int eob_shift = eob_offset_bits - 1;
int bit = (eob_extra & (1 << eob_shift)) ? 1 : 0;
aom_write_symbol(w, bit,
ec_ctx->eob_extra_cdf[txs_ctx][plane_type][eob_ctx], 2);
for (int i = 1; i < k_eob_offset_bits[eob_pt]; i++) {
eob_shift = k_eob_offset_bits[eob_pt] - 1 - i;
for (int i = 1; i < eob_offset_bits; i++) {
eob_shift = eob_offset_bits - 1 - i;
bit = (eob_extra & (1 << eob_shift)) ? 1 : 0;
aom_write_bit(w, bit);
}
@ -588,12 +608,11 @@ void av1_write_coeffs_txb(const AV1_COMMON *const cm, MACROBLOCKD *xd,
// level is above 1.
const int base_range = level - 1 - NUM_BASE_LEVELS;
const int br_ctx = get_br_ctx(levels, pos, bwl, tx_class);
aom_cdf_prob *cdf =
ec_ctx->coeff_br_cdf[AOMMIN(txs_ctx, TX_32X32)][plane_type][br_ctx];
for (int idx = 0; idx < COEFF_BASE_RANGE; idx += BR_CDF_SIZE - 1) {
const int k = AOMMIN(base_range - idx, BR_CDF_SIZE - 1);
aom_write_symbol(
w, k,
ec_ctx->coeff_br_cdf[AOMMIN(txs_ctx, TX_32X32)][plane_type][br_ctx],
BR_CDF_SIZE);
aom_write_symbol(w, k, cdf, BR_CDF_SIZE);
if (k < BR_CDF_SIZE - 1) break;
}
}
@ -745,7 +764,8 @@ static AOM_FORCE_INLINE int warehouse_efficients_txb(
av1_get_nz_map_contexts(levels, scan, eob, tx_size, tx_class, coeff_contexts);
const int(*lps_cost)[COEFF_BASE_RANGE + 1] = coeff_costs->lps_cost;
const int(*lps_cost)[COEFF_BASE_RANGE + 1 + COEFF_BASE_RANGE + 1] =
coeff_costs->lps_cost;
int c = eob - 1;
{
const int pos = scan[c];
@ -758,11 +778,8 @@ static AOM_FORCE_INLINE int warehouse_efficients_txb(
if (v) {
// sign bit cost
if (level > NUM_BASE_LEVELS) {
const int ctx = get_br_ctx(levels, pos, bwl, tx_class);
const int base_range =
AOMMIN(level - 1 - NUM_BASE_LEVELS, COEFF_BASE_RANGE);
cost += lps_cost[ctx][base_range];
cost += get_golomb_cost(level);
const int ctx = get_br_ctx_eob(pos, bwl, tx_class);
cost += get_br_cost(level, lps_cost[ctx]);
}
if (c) {
cost += av1_cost_literal(1);
@ -774,7 +791,7 @@ static AOM_FORCE_INLINE int warehouse_efficients_txb(
}
}
}
const int(*base_cost)[4] = coeff_costs->base_cost;
const int(*base_cost)[8] = coeff_costs->base_cost;
for (c = eob - 2; c >= 1; --c) {
const int pos = scan[c];
const int coeff_ctx = coeff_contexts[pos];
@ -786,10 +803,7 @@ static AOM_FORCE_INLINE int warehouse_efficients_txb(
cost += av1_cost_literal(1);
if (level > NUM_BASE_LEVELS) {
const int ctx = get_br_ctx(levels, pos, bwl, tx_class);
const int base_range =
AOMMIN(level - 1 - NUM_BASE_LEVELS, COEFF_BASE_RANGE);
cost += lps_cost[ctx][base_range];
cost += get_golomb_cost(level);
cost += get_br_cost(level, lps_cost[ctx]);
}
}
cost += cost0;
@ -809,10 +823,7 @@ static AOM_FORCE_INLINE int warehouse_efficients_txb(
cost += coeff_costs->dc_sign_cost[dc_sign_ctx][sign01];
if (level > NUM_BASE_LEVELS) {
const int ctx = get_br_ctx(levels, pos, bwl, tx_class);
const int base_range =
AOMMIN(level - 1 - NUM_BASE_LEVELS, COEFF_BASE_RANGE);
cost += lps_cost[ctx][base_range];
cost += get_golomb_cost(level);
cost += get_br_cost(level, lps_cost[ctx]);
}
}
}
@ -1014,13 +1025,7 @@ static int optimize_txb(TxbInfo *txb_info, const LV_MAP_COEFF_COST *txb_costs,
return update;
}
// These numbers are empirically obtained.
static const int plane_rd_mult[REF_TYPES][PLANE_TYPES] = {
{ 17, 13 },
{ 16, 10 },
};
void hbt_init() {
static void hbt_init() {
hbt_hash_table =
aom_malloc(sizeof(OptTxbQcoeff) * HBT_TABLE_SIZE * HBT_ARRAY_LENGTH);
memset(hbt_hash_table, 0,
@ -1032,11 +1037,11 @@ void hbt_init() {
void hbt_destroy() { aom_free(hbt_hash_table); }
int hbt_hash_miss(uint32_t hbt_ctx_hash, uint32_t hbt_qc_hash,
TxbInfo *txb_info, const LV_MAP_COEFF_COST *txb_costs,
const LV_MAP_EOB_COST *txb_eob_costs,
const struct macroblock_plane *p, int block, int fast_mode,
int *rate_cost) {
static int hbt_hash_miss(uint32_t hbt_ctx_hash, uint32_t hbt_qc_hash,
TxbInfo *txb_info, const LV_MAP_COEFF_COST *txb_costs,
const LV_MAP_EOB_COST *txb_eob_costs,
const struct macroblock_plane *p, int block,
int fast_mode, int *rate_cost) {
(void)fast_mode;
const int16_t *scan = txb_info->scan_order->scan;
int prev_eob = txb_info->eob;
@ -1098,9 +1103,9 @@ int hbt_hash_miss(uint32_t hbt_ctx_hash, uint32_t hbt_qc_hash,
return txb_info->eob;
}
int hbt_hash_hit(uint32_t hbt_table_index, int hbt_array_index,
TxbInfo *txb_info, const struct macroblock_plane *p, int block,
int *rate_cost) {
static int hbt_hash_hit(uint32_t hbt_table_index, int hbt_array_index,
TxbInfo *txb_info, const struct macroblock_plane *p,
int block, int *rate_cost) {
const int16_t *scan = txb_info->scan_order->scan;
int new_eob = 0;
int update = 0;
@ -1140,11 +1145,12 @@ int hbt_hash_hit(uint32_t hbt_table_index, int hbt_array_index,
return txb_info->eob;
}
int hbt_search_match(uint32_t hbt_ctx_hash, uint32_t hbt_qc_hash,
TxbInfo *txb_info, const LV_MAP_COEFF_COST *txb_costs,
const LV_MAP_EOB_COST *txb_eob_costs,
const struct macroblock_plane *p, int block, int fast_mode,
int *rate_cost) {
static int hbt_search_match(uint32_t hbt_ctx_hash, uint32_t hbt_qc_hash,
TxbInfo *txb_info,
const LV_MAP_COEFF_COST *txb_costs,
const LV_MAP_EOB_COST *txb_eob_costs,
const struct macroblock_plane *p, int block,
int fast_mode, int *rate_cost) {
// Check for qcoeff match
int hbt_array_index = hbt_qc_hash % HBT_ARRAY_LENGTH;
int hbt_table_index = hbt_ctx_hash % HBT_TABLE_SIZE;
@ -1163,10 +1169,11 @@ int hbt_search_match(uint32_t hbt_ctx_hash, uint32_t hbt_qc_hash,
}
}
int hbt_create_hashes(TxbInfo *txb_info, const LV_MAP_COEFF_COST *txb_costs,
const LV_MAP_EOB_COST *txb_eob_costs,
const struct macroblock_plane *p, int block,
int fast_mode, int *rate_cost) {
static int hbt_create_hashes(TxbInfo *txb_info,
const LV_MAP_COEFF_COST *txb_costs,
const LV_MAP_EOB_COST *txb_eob_costs,
const struct macroblock_plane *p, int block,
int fast_mode, int *rate_cost) {
// Initialize hash table if needed.
if (hbt_needs_init) {
hbt_init();
@ -1288,20 +1295,47 @@ int hbt_create_hashes(TxbInfo *txb_info, const LV_MAP_COEFF_COST *txb_costs,
txb_eob_costs, p, block, fast_mode, rate_cost);
}
static AOM_FORCE_INLINE int get_coeff_cost_simple(
static AOM_FORCE_INLINE int get_two_coeff_cost_simple(
int ci, tran_low_t abs_qc, int coeff_ctx,
const LV_MAP_COEFF_COST *txb_costs, int bwl, TX_CLASS tx_class,
const uint8_t *levels) {
const uint8_t *levels, int *cost_low) {
// this simple version assumes the coeff's scan_idx is not DC (scan_idx != 0)
// and not the last (scan_idx != eob - 1)
assert(ci > 0);
int cost = txb_costs->base_cost[coeff_ctx][AOMMIN(abs_qc, 3)];
int diff = 0;
if (abs_qc <= 3) diff = txb_costs->base_cost[coeff_ctx][abs_qc + 4];
if (abs_qc) {
cost += av1_cost_literal(1);
if (abs_qc > NUM_BASE_LEVELS) {
const int br_ctx = get_br_ctx(levels, ci, bwl, tx_class);
cost += get_br_cost(abs_qc, br_ctx, txb_costs->lps_cost[br_ctx]);
cost += get_golomb_cost(abs_qc);
int brcost_diff = 0;
cost += get_br_cost_with_diff(abs_qc, txb_costs->lps_cost[br_ctx],
&brcost_diff);
diff += brcost_diff;
}
}
*cost_low = cost - diff;
return cost;
}
static INLINE int get_coeff_cost_eob(int ci, tran_low_t abs_qc, int sign,
int coeff_ctx, int dc_sign_ctx,
const LV_MAP_COEFF_COST *txb_costs,
int bwl, TX_CLASS tx_class) {
int cost = 0;
cost += txb_costs->base_eob_cost[coeff_ctx][AOMMIN(abs_qc, 3) - 1];
if (abs_qc != 0) {
if (ci == 0) {
cost += txb_costs->dc_sign_cost[dc_sign_ctx][sign];
} else {
cost += av1_cost_literal(1);
}
if (abs_qc > NUM_BASE_LEVELS) {
int br_ctx;
br_ctx = get_br_ctx_eob(ci, bwl, tx_class);
cost += get_br_cost(abs_qc, txb_costs->lps_cost[br_ctx]);
}
}
return cost;
@ -1326,9 +1360,12 @@ static INLINE int get_coeff_cost_general(int is_last, int ci, tran_low_t abs_qc,
cost += av1_cost_literal(1);
}
if (abs_qc > NUM_BASE_LEVELS) {
const int br_ctx = get_br_ctx(levels, ci, bwl, tx_class);
cost += get_br_cost(abs_qc, br_ctx, txb_costs->lps_cost[br_ctx]);
cost += get_golomb_cost(abs_qc);
int br_ctx;
if (is_last)
br_ctx = get_br_ctx_eob(ci, bwl, tx_class);
else
br_ctx = get_br_ctx(levels, ci, bwl, tx_class);
cost += get_br_cost(abs_qc, txb_costs->lps_cost[br_ctx]);
}
}
return cost;
@ -1372,13 +1409,23 @@ static INLINE void update_coeff_general(
const int64_t rd = RDCOST(rdmult, rate, dist);
tran_low_t qc_low, dqc_low;
get_qc_dqc_low(abs_qc, sign, dqv, shift, &qc_low, &dqc_low);
const tran_low_t abs_qc_low = abs_qc - 1;
const int64_t dist_low = get_coeff_dist(tqc, dqc_low, shift);
const int rate_low =
get_coeff_cost_general(is_last, ci, abs_qc_low, sign, coeff_ctx,
dc_sign_ctx, txb_costs, bwl, tx_class, levels);
const int64_t rd_low = RDCOST(rdmult, rate_low, dist_low);
tran_low_t abs_qc_low;
int64_t dist_low, rd_low;
int rate_low;
if (abs_qc == 1) {
abs_qc_low = qc_low = dqc_low = 0;
dist_low = dist0;
rate_low = txb_costs->base_cost[coeff_ctx][0];
} else {
get_qc_dqc_low(abs_qc, sign, dqv, shift, &qc_low, &dqc_low);
abs_qc_low = abs_qc - 1;
dist_low = get_coeff_dist(tqc, dqc_low, shift);
rate_low =
get_coeff_cost_general(is_last, ci, abs_qc_low, sign, coeff_ctx,
dc_sign_ctx, txb_costs, bwl, tx_class, levels);
}
rd_low = RDCOST(rdmult, rate_low, dist_low);
if (rd_low < rd) {
qcoeff[ci] = qc_low;
dqcoeff[ci] = dqc_low;
@ -1412,28 +1459,28 @@ static AOM_FORCE_INLINE void update_coeff_simple(
*accu_rate += txb_costs->base_cost[coeff_ctx][0];
} else {
const tran_low_t abs_qc = abs(qc);
const tran_low_t tqc = tcoeff[ci];
const tran_low_t dqc = dqcoeff[ci];
const int rate = get_coeff_cost_simple(ci, abs_qc, coeff_ctx, txb_costs,
bwl, tx_class, levels);
if (abs(dqc) < abs(tqc)) {
const tran_low_t abs_tqc = abs(tcoeff[ci]);
const tran_low_t abs_dqc = abs(dqcoeff[ci]);
int rate_low = 0;
const int rate = get_two_coeff_cost_simple(
ci, abs_qc, coeff_ctx, txb_costs, bwl, tx_class, levels, &rate_low);
if (abs_dqc < abs_tqc) {
*accu_rate += rate;
return;
}
const int64_t dist = get_coeff_dist(tqc, dqc, shift);
const int64_t dist = get_coeff_dist(abs_tqc, abs_dqc, shift);
const int64_t rd = RDCOST(rdmult, rate, dist);
const int sign = (qc < 0) ? 1 : 0;
tran_low_t qc_low, dqc_low;
get_qc_dqc_low(abs_qc, sign, dqv, shift, &qc_low, &dqc_low);
const tran_low_t abs_qc_low = abs_qc - 1;
const int64_t dist_low = get_coeff_dist(tqc, dqc_low, shift);
const int rate_low = get_coeff_cost_simple(
ci, abs_qc_low, coeff_ctx, txb_costs, bwl, tx_class, levels);
const tran_low_t abs_dqc_low = (abs_qc_low * dqv) >> shift;
const int64_t dist_low = get_coeff_dist(abs_tqc, abs_dqc_low, shift);
const int64_t rd_low = RDCOST(rdmult, rate_low, dist_low);
if (rd_low < rd) {
qcoeff[ci] = qc_low;
dqcoeff[ci] = dqc_low;
const int sign = (qc < 0) ? 1 : 0;
qcoeff[ci] = (-sign ^ abs_qc_low) + sign;
dqcoeff[ci] = (-sign ^ abs_dqc_low) + sign;
levels[get_padded_idx(ci, bwl)] = AOMMIN(abs_qc_low, INT8_MAX);
*accu_rate += rate_low;
} else {
@ -1471,40 +1518,42 @@ static AOM_FORCE_INLINE void update_coeff_eob(
int64_t rd = RDCOST(rdmult, *accu_rate + rate, *accu_dist + dist);
tran_low_t qc_low, dqc_low;
get_qc_dqc_low(abs_qc, sign, dqv, shift, &qc_low, &dqc_low);
const tran_low_t abs_qc_low = abs_qc - 1;
const int64_t dist_low = get_coeff_dist(tqc, dqc_low, shift) - dist0;
const int rate_low =
get_coeff_cost_general(0, ci, abs_qc_low, sign, coeff_ctx, dc_sign_ctx,
txb_costs, bwl, tx_class, levels);
const int64_t rd_low =
RDCOST(rdmult, *accu_rate + rate_low, *accu_dist + dist_low);
tran_low_t abs_qc_low;
int64_t dist_low, rd_low;
int rate_low;
if (abs_qc == 1) {
abs_qc_low = 0;
dqc_low = qc_low = 0;
dist_low = 0;
rate_low = txb_costs->base_cost[coeff_ctx][0];
rd_low = RDCOST(rdmult, *accu_rate + rate_low, *accu_dist);
} else {
get_qc_dqc_low(abs_qc, sign, dqv, shift, &qc_low, &dqc_low);
abs_qc_low = abs_qc - 1;
dist_low = get_coeff_dist(tqc, dqc_low, shift) - dist0;
rate_low =
get_coeff_cost_general(0, ci, abs_qc_low, sign, coeff_ctx,
dc_sign_ctx, txb_costs, bwl, tx_class, levels);
rd_low = RDCOST(rdmult, *accu_rate + rate_low, *accu_dist + dist_low);
}
int lower_level_new_eob = 0;
const int new_eob = si + 1;
uint8_t tmp_levels[3];
for (int ni = 0; ni < *nz_num; ++ni) {
const int last_ci = nz_ci[ni];
tmp_levels[ni] = levels[get_padded_idx(last_ci, bwl)];
levels[get_padded_idx(last_ci, bwl)] = 0;
}
const int coeff_ctx_new_eob = get_lower_levels_ctx_general(
1, si, bwl, height, levels, ci, tx_size, tx_class);
const int coeff_ctx_new_eob = get_lower_levels_ctx_eob(bwl, height, si);
const int new_eob_cost =
get_eob_cost(new_eob, txb_eob_costs, txb_costs, tx_class);
int rate_coeff_eob =
new_eob_cost + get_coeff_cost_general(1, ci, abs_qc, sign,
coeff_ctx_new_eob, dc_sign_ctx,
txb_costs, bwl, tx_class, levels);
new_eob_cost + get_coeff_cost_eob(ci, abs_qc, sign, coeff_ctx_new_eob,
dc_sign_ctx, txb_costs, bwl,
tx_class);
int64_t dist_new_eob = dist;
int64_t rd_new_eob = RDCOST(rdmult, rate_coeff_eob, dist_new_eob);
if (abs_qc_low > 0) {
const int rate_coeff_eob_low =
new_eob_cost +
get_coeff_cost_general(1, ci, abs_qc_low, sign, coeff_ctx_new_eob,
dc_sign_ctx, txb_costs, bwl, tx_class, levels);
new_eob_cost + get_coeff_cost_eob(ci, abs_qc_low, sign,
coeff_ctx_new_eob, dc_sign_ctx,
txb_costs, bwl, tx_class);
const int64_t dist_new_eob_low = dist_low;
const int64_t rd_new_eob_low =
RDCOST(rdmult, rate_coeff_eob_low, dist_new_eob_low);
@ -1526,7 +1575,7 @@ static AOM_FORCE_INLINE void update_coeff_eob(
if (sharpness == 0 && rd_new_eob < rd) {
for (int ni = 0; ni < *nz_num; ++ni) {
int last_ci = nz_ci[ni];
// levels[get_padded_idx(last_ci, bwl)] = 0;
levels[get_padded_idx(last_ci, bwl)] = 0;
qcoeff[last_ci] = 0;
dqcoeff[last_ci] = 0;
}
@ -1536,10 +1585,6 @@ static AOM_FORCE_INLINE void update_coeff_eob(
*accu_dist = dist_new_eob;
lower_level = lower_level_new_eob;
} else {
for (int ni = 0; ni < *nz_num; ++ni) {
const int last_ci = nz_ci[ni];
levels[get_padded_idx(last_ci, bwl)] = tmp_levels[ni];
}
*accu_rate += rate;
*accu_dist += dist;
}
@ -1619,14 +1664,14 @@ int av1_optimize_txb_new(const struct AV1_COMP *cpi, MACROBLOCK *x, int plane,
uint8_t levels_buf[TX_PAD_2D];
uint8_t *const levels = set_levels(levels_buf, width);
int eob = p->eobs[block];
av1_txb_init_levels(qcoeff, width, height, levels);
if (eob > 1) av1_txb_init_levels(qcoeff, width, height, levels);
// TODO(angirbird): check iqmatrix
const int non_skip_cost = txb_costs->txb_skip_cost[txb_ctx->txb_skip_ctx][0];
const int skip_cost = txb_costs->txb_skip_cost[txb_ctx->txb_skip_ctx][1];
int eob = p->eobs[block];
const int eob_cost = get_eob_cost(eob, txb_eob_costs, txb_costs, tx_class);
int accu_rate = eob_cost;
int64_t accu_dist = 0;
@ -1646,11 +1691,10 @@ int av1_optimize_txb_new(const struct AV1_COMP *cpi, MACROBLOCK *x, int plane,
--si;
} else {
assert(abs_qc == 1);
const int coeff_ctx = get_lower_levels_ctx_general(
1, si, bwl, height, levels, ci, tx_size, tx_class);
accu_rate += get_coeff_cost_general(1, ci, abs_qc, sign, coeff_ctx,
txb_ctx->dc_sign_ctx, txb_costs, bwl,
tx_class, levels);
const int coeff_ctx = get_lower_levels_ctx_eob(bwl, height, si);
accu_rate +=
get_coeff_cost_eob(ci, abs_qc, sign, coeff_ctx, txb_ctx->dc_sign_ctx,
txb_costs, bwl, tx_class);
const tran_low_t tqc = tcoeff[ci];
const tran_low_t dqc = dqcoeff[ci];
const int64_t dist = get_coeff_dist(tqc, dqc, shift);
@ -1767,10 +1811,9 @@ int av1_optimize_txb(const struct AV1_COMP *cpi, MACROBLOCK *x, int plane,
assert(width == (1 << bwl));
const int tx_type_cost = get_tx_type_cost(cm, x, xd, plane, tx_size, tx_type);
TxbInfo txb_info = {
qcoeff, levels, dqcoeff, tcoeff, dequant, shift,
tx_size, txs_ctx, tx_type, bwl, width, height,
eob, seg_eob, scan_order, txb_ctx, rdmult, &cm->coeff_ctx_table,
iqmatrix, tx_type_cost,
qcoeff, levels, dqcoeff, tcoeff, dequant, shift, tx_size,
txs_ctx, tx_type, bwl, width, height, eob, seg_eob,
scan_order, txb_ctx, rdmult, iqmatrix, tx_type_cost,
};
// Hash based trellis (hbt) speed feature: avoid expensive optimize_txb calls

View file

@ -42,7 +42,6 @@ typedef struct TxbInfo {
const SCAN_ORDER *scan_order;
TXB_CTX *txb_ctx;
int64_t rdmult;
const LV_MAP_CTX_TABLE *coeff_ctx_table;
const qm_val_t *iqmatrix;
int tx_type_cost;
} TxbInfo;
@ -80,6 +79,13 @@ int av1_optimize_txb_new(const struct AV1_COMP *cpi, MACROBLOCK *x, int plane,
int block, TX_SIZE tx_size, TX_TYPE tx_type,
const TXB_CTX *const txb_ctx, int *rate_cost,
int sharpness);
// These numbers are empirically obtained.
static const int plane_rd_mult[REF_TYPES][PLANE_TYPES] = {
{ 17, 13 },
{ 16, 10 },
};
#ifdef __cplusplus
}
#endif

View file

@ -9,9 +9,11 @@
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include "av1/encoder/av1_multi_thread.h"
#include "av1/encoder/encodeframe.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/ethread.h"
#include "av1/encoder/rdopt.h"
#include "aom_dsp/aom_dsp_common.h"
static void accumulate_rd_opt(ThreadData *td, ThreadData *td_t) {
@ -27,6 +29,318 @@ static void accumulate_rd_opt(ThreadData *td, ThreadData *td_t) {
td->rd_counts.skip_mode_used_flag |= td_t->rd_counts.skip_mode_used_flag;
}
static void update_delta_lf_for_row_mt(AV1_COMP *cpi) {
AV1_COMMON *cm = &cpi->common;
MACROBLOCKD *xd = &cpi->td.mb.e_mbd;
const int mib_size = cm->seq_params.mib_size;
const int frame_lf_count =
av1_num_planes(cm) > 1 ? FRAME_LF_COUNT : FRAME_LF_COUNT - 2;
for (int row = 0; row < cm->tile_rows; row++) {
for (int col = 0; col < cm->tile_cols; col++) {
TileDataEnc *tile_data = &cpi->tile_data[row * cm->tile_cols + col];
const TileInfo *const tile_info = &tile_data->tile_info;
for (int mi_row = tile_info->mi_row_start; mi_row < tile_info->mi_row_end;
mi_row += mib_size) {
if (mi_row == tile_info->mi_row_start)
av1_reset_loop_filter_delta(xd, av1_num_planes(cm));
for (int mi_col = tile_info->mi_col_start;
mi_col < tile_info->mi_col_end; mi_col += mib_size) {
const int idx_str = cm->mi_stride * mi_row + mi_col;
MB_MODE_INFO **mi = cm->mi_grid_visible + idx_str;
MB_MODE_INFO *mbmi = mi[0];
if (mbmi->skip == 1 && (mbmi->sb_type == cm->seq_params.sb_size)) {
for (int lf_id = 0; lf_id < frame_lf_count; ++lf_id)
mbmi->delta_lf[lf_id] = xd->delta_lf[lf_id];
mbmi->delta_lf_from_base = xd->delta_lf_from_base;
} else {
if (cm->delta_q_info.delta_lf_multi) {
for (int lf_id = 0; lf_id < frame_lf_count; ++lf_id)
xd->delta_lf[lf_id] = mbmi->delta_lf[lf_id];
} else {
xd->delta_lf_from_base = mbmi->delta_lf_from_base;
}
}
}
}
}
}
}
void av1_row_mt_sync_read_dummy(struct AV1RowMTSyncData *const row_mt_sync,
int r, int c) {
(void)row_mt_sync;
(void)r;
(void)c;
return;
}
void av1_row_mt_sync_write_dummy(struct AV1RowMTSyncData *const row_mt_sync,
int r, int c, const int cols) {
(void)row_mt_sync;
(void)r;
(void)c;
(void)cols;
return;
}
void av1_row_mt_sync_read(AV1RowMTSync *const row_mt_sync, int r, int c) {
#if CONFIG_MULTITHREAD
const int nsync = row_mt_sync->sync_range;
if (r) {
pthread_mutex_t *const mutex = &row_mt_sync->mutex_[r - 1];
pthread_mutex_lock(mutex);
while (c > row_mt_sync->cur_col[r - 1] - nsync) {
pthread_cond_wait(&row_mt_sync->cond_[r - 1], mutex);
}
pthread_mutex_unlock(mutex);
}
#else
(void)row_mt_sync;
(void)r;
(void)c;
#endif // CONFIG_MULTITHREAD
}
void av1_row_mt_sync_write(AV1RowMTSync *const row_mt_sync, int r, int c,
const int cols) {
#if CONFIG_MULTITHREAD
const int nsync = row_mt_sync->sync_range;
int cur;
// Only signal when there are enough encoded blocks for next row to run.
int sig = 1;
if (c < cols - 1) {
cur = c;
if (c % nsync) sig = 0;
} else {
cur = cols + nsync;
}
if (sig) {
pthread_mutex_lock(&row_mt_sync->mutex_[r]);
row_mt_sync->cur_col[r] = cur;
pthread_cond_signal(&row_mt_sync->cond_[r]);
pthread_mutex_unlock(&row_mt_sync->mutex_[r]);
}
#else
(void)row_mt_sync;
(void)r;
(void)c;
(void)cols;
#endif // CONFIG_MULTITHREAD
}
// Allocate memory for row synchronization
void av1_row_mt_sync_mem_alloc(AV1RowMTSync *row_mt_sync, AV1_COMMON *cm,
int rows) {
row_mt_sync->rows = rows;
#if CONFIG_MULTITHREAD
{
int i;
CHECK_MEM_ERROR(cm, row_mt_sync->mutex_,
aom_malloc(sizeof(*row_mt_sync->mutex_) * rows));
if (row_mt_sync->mutex_) {
for (i = 0; i < rows; ++i) {
pthread_mutex_init(&row_mt_sync->mutex_[i], NULL);
}
}
CHECK_MEM_ERROR(cm, row_mt_sync->cond_,
aom_malloc(sizeof(*row_mt_sync->cond_) * rows));
if (row_mt_sync->cond_) {
for (i = 0; i < rows; ++i) {
pthread_cond_init(&row_mt_sync->cond_[i], NULL);
}
}
}
#endif // CONFIG_MULTITHREAD
CHECK_MEM_ERROR(cm, row_mt_sync->cur_col,
aom_malloc(sizeof(*row_mt_sync->cur_col) * rows));
// Set up nsync.
row_mt_sync->sync_range = 1;
}
// Deallocate row based multi-threading synchronization related mutex and data
void av1_row_mt_sync_mem_dealloc(AV1RowMTSync *row_mt_sync) {
if (row_mt_sync != NULL) {
#if CONFIG_MULTITHREAD
int i;
if (row_mt_sync->mutex_ != NULL) {
for (i = 0; i < row_mt_sync->rows; ++i) {
pthread_mutex_destroy(&row_mt_sync->mutex_[i]);
}
aom_free(row_mt_sync->mutex_);
}
if (row_mt_sync->cond_ != NULL) {
for (i = 0; i < row_mt_sync->rows; ++i) {
pthread_cond_destroy(&row_mt_sync->cond_[i]);
}
aom_free(row_mt_sync->cond_);
}
#endif // CONFIG_MULTITHREAD
aom_free(row_mt_sync->cur_col);
// clear the structure as the source of this call may be dynamic change
// in tiles in which case this call will be followed by an _alloc()
// which may fail.
av1_zero(*row_mt_sync);
}
}
static void assign_tile_to_thread(MultiThreadHandle *multi_thread_ctxt,
int num_tiles, int num_workers) {
int tile_id = 0;
int i;
for (i = 0; i < num_workers; i++) {
multi_thread_ctxt->thread_id_to_tile_id[i] = tile_id++;
if (tile_id == num_tiles) tile_id = 0;
}
}
static int get_next_job(AV1_COMP *const cpi, int *current_mi_row,
int cur_tile_id) {
AV1_COMMON *const cm = &cpi->common;
TileDataEnc *const this_tile = &cpi->tile_data[cur_tile_id];
AV1RowMTInfo *row_mt_info = &this_tile->row_mt_info;
if (row_mt_info->current_mi_row < this_tile->tile_info.mi_row_end) {
*current_mi_row = row_mt_info->current_mi_row;
row_mt_info->num_threads_working++;
row_mt_info->current_mi_row += cm->seq_params.mib_size;
return 1;
}
return 0;
}
static void switch_tile_and_get_next_job(AV1_COMP *const cpi, int *cur_tile_id,
int *current_mi_row,
int *end_of_frame) {
AV1_COMMON *const cm = &cpi->common;
const int tile_cols = cm->tile_cols;
const int tile_rows = cm->tile_rows;
int tile_id = -1; // Stores the tile ID with minimum proc done
int max_mis_to_encode = 0;
int min_num_threads_working = INT_MAX;
for (int tile_row = 0; tile_row < tile_rows; tile_row++) {
for (int tile_col = 0; tile_col < tile_cols; tile_col++) {
int tile_index = tile_row * tile_cols + tile_col;
TileDataEnc *this_tile = &cpi->tile_data[tile_index];
AV1RowMTInfo *row_mt_info = &this_tile->row_mt_info;
int num_mis_to_encode =
this_tile->tile_info.mi_row_end - row_mt_info->current_mi_row;
// Tile to be processed by this thread is selected on the basis of
// availability of jobs:
// 1) If jobs are available, tile to be processed is chosen on the
// basis of minimum number of threads working for that tile. If two or
// more tiles have same number of threads working for them, then the tile
// with maximum number of jobs available will be chosen.
// 2) If no jobs are available, then end_of_frame is reached.
if (num_mis_to_encode > 0) {
int num_threads_working = row_mt_info->num_threads_working;
if (num_threads_working < min_num_threads_working) {
min_num_threads_working = num_threads_working;
max_mis_to_encode = 0;
}
if (num_threads_working == min_num_threads_working &&
num_mis_to_encode > max_mis_to_encode) {
tile_id = tile_index;
max_mis_to_encode = num_mis_to_encode;
}
}
}
}
if (tile_id == -1) {
*end_of_frame = 1;
} else {
// Update the cur ID to the next tile ID that will be processed,
// which will be the least processed tile
*cur_tile_id = tile_id;
get_next_job(cpi, current_mi_row, *cur_tile_id);
}
}
static int enc_row_mt_worker_hook(void *arg1, void *unused) {
EncWorkerData *const thread_data = (EncWorkerData *)arg1;
AV1_COMP *const cpi = thread_data->cpi;
AV1_COMMON *const cm = &cpi->common;
MultiThreadHandle *multi_thread_ctxt = &cpi->multi_thread_ctxt;
int thread_id = thread_data->thread_id;
int cur_tile_id = multi_thread_ctxt->thread_id_to_tile_id[thread_id];
(void)unused;
assert(cur_tile_id != -1);
int end_of_frame = 0;
while (1) {
int current_mi_row = -1;
#if CONFIG_MULTITHREAD
pthread_mutex_lock(cpi->row_mt_mutex_);
#endif
if (!get_next_job(cpi, &current_mi_row, cur_tile_id)) {
// No jobs are available for the current tile. Query for the status of
// other tiles and get the next job if available
switch_tile_and_get_next_job(cpi, &cur_tile_id, &current_mi_row,
&end_of_frame);
}
#if CONFIG_MULTITHREAD
pthread_mutex_unlock(cpi->row_mt_mutex_);
#endif
if (end_of_frame == 1) break;
TileDataEnc *const this_tile = &cpi->tile_data[cur_tile_id];
int tile_row = this_tile->tile_info.tile_row;
int tile_col = this_tile->tile_info.tile_col;
assert(current_mi_row != -1 &&
current_mi_row <= this_tile->tile_info.mi_row_end);
ThreadData *td = thread_data->td;
td->mb.e_mbd.tile_ctx = td->tctx;
td->mb.tile_pb_ctx = &this_tile->tctx;
if (this_tile->allow_update_cdf) {
td->mb.row_ctx = this_tile->row_ctx;
if (current_mi_row == this_tile->tile_info.mi_row_start)
memcpy(td->mb.e_mbd.tile_ctx, &this_tile->tctx, sizeof(FRAME_CONTEXT));
} else {
memcpy(td->mb.e_mbd.tile_ctx, &this_tile->tctx, sizeof(FRAME_CONTEXT));
}
av1_init_above_context(cm, &td->mb.e_mbd, tile_row);
// Disable exhaustive search speed features for row based multi-threading of
// encoder.
td->mb.m_search_count_ptr = NULL;
td->mb.ex_search_count_ptr = NULL;
cfl_init(&td->mb.e_mbd.cfl, &cm->seq_params);
av1_crc32c_calculator_init(&td->mb.mb_rd_record.crc_calculator);
av1_encode_sb_row(cpi, td, tile_row, tile_col, current_mi_row);
#if CONFIG_MULTITHREAD
pthread_mutex_lock(cpi->row_mt_mutex_);
#endif
this_tile->row_mt_info.num_threads_working--;
#if CONFIG_MULTITHREAD
pthread_mutex_unlock(cpi->row_mt_mutex_);
#endif
}
return 1;
}
static int enc_worker_hook(void *arg1, void *unused) {
EncWorkerData *const thread_data = (EncWorkerData *)arg1;
AV1_COMP *const cpi = thread_data->cpi;
@ -42,6 +356,10 @@ static int enc_worker_hook(void *arg1, void *unused) {
int tile_row = t / tile_cols;
int tile_col = t % tile_cols;
TileDataEnc *const this_tile =
&cpi->tile_data[tile_row * cm->tile_cols + tile_col];
thread_data->td->mb.e_mbd.tile_ctx = &this_tile->tctx;
thread_data->td->mb.tile_pb_ctx = &this_tile->tctx;
av1_encode_tile(cpi, thread_data->td, tile_row, tile_col);
}
@ -58,16 +376,28 @@ static void create_enc_workers(AV1_COMP *cpi, int num_workers) {
CHECK_MEM_ERROR(cm, cpi->tile_thr_data,
aom_calloc(num_workers, sizeof(*cpi->tile_thr_data)));
for (int i = 0; i < num_workers; i++) {
#if CONFIG_MULTITHREAD
if (cpi->row_mt == 1) {
if (cpi->row_mt_mutex_ == NULL) {
CHECK_MEM_ERROR(cm, cpi->row_mt_mutex_,
aom_malloc(sizeof(*(cpi->row_mt_mutex_))));
if (cpi->row_mt_mutex_) pthread_mutex_init(cpi->row_mt_mutex_, NULL);
}
}
#endif
for (int i = num_workers - 1; i >= 0; i--) {
AVxWorker *const worker = &cpi->workers[i];
EncWorkerData *const thread_data = &cpi->tile_thr_data[i];
++cpi->num_workers;
winterface->init(worker);
worker->thread_name = "aom enc worker";
thread_data->cpi = cpi;
thread_data->thread_id = i;
if (i < num_workers - 1) {
if (i > 0) {
// Allocate thread data.
CHECK_MEM_ERROR(cm, thread_data->td,
aom_memalign(32, sizeof(*thread_data->td)));
@ -91,6 +421,10 @@ static void create_enc_workers(AV1_COMP *cpi, int num_workers) {
(int32_t *)aom_memalign(
16, MAX_SB_SQUARE * sizeof(*thread_data->td->wsrc_buf)));
CHECK_MEM_ERROR(cm, thread_data->td->inter_modes_info,
(InterModesInfo *)aom_malloc(
sizeof(*thread_data->td->inter_modes_info)));
for (int x = 0; x < 2; x++)
for (int y = 0; y < 2; y++)
CHECK_MEM_ERROR(
@ -119,7 +453,7 @@ static void create_enc_workers(AV1_COMP *cpi, int num_workers) {
for (int j = 0; j < 2; ++j) {
CHECK_MEM_ERROR(
cm, thread_data->td->tmp_obmc_bufs[j],
aom_memalign(16, 2 * MAX_MB_PLANE * MAX_SB_SQUARE *
aom_memalign(32, 2 * MAX_MB_PLANE * MAX_SB_SQUARE *
sizeof(*thread_data->td->tmp_obmc_bufs[j])));
}
@ -131,6 +465,10 @@ static void create_enc_workers(AV1_COMP *cpi, int num_workers) {
// Main thread acts as a worker and uses the thread data in cpi.
thread_data->td = &cpi->td;
}
if (cpi->row_mt == 1)
CHECK_MEM_ERROR(
cm, thread_data->td->tctx,
(FRAME_CONTEXT *)aom_memalign(16, sizeof(*thread_data->td->tctx)));
winterface->sync(worker);
}
}
@ -138,14 +476,14 @@ static void create_enc_workers(AV1_COMP *cpi, int num_workers) {
static void launch_enc_workers(AV1_COMP *cpi, int num_workers) {
const AVxWorkerInterface *const winterface = aom_get_worker_interface();
// Encode a frame
for (int i = 0; i < num_workers; i++) {
for (int i = num_workers - 1; i >= 0; i--) {
AVxWorker *const worker = &cpi->workers[i];
EncWorkerData *const thread_data = (EncWorkerData *)worker->data1;
// Set the starting tile for each thread.
thread_data->start = i;
if (i == cpi->num_workers - 1)
if (i == 0)
winterface->execute(worker);
else
winterface->launch(worker);
@ -154,31 +492,39 @@ static void launch_enc_workers(AV1_COMP *cpi, int num_workers) {
static void sync_enc_workers(AV1_COMP *cpi, int num_workers) {
const AVxWorkerInterface *const winterface = aom_get_worker_interface();
int had_error = 0;
// Encoding ends.
for (int i = 0; i < num_workers; i++) {
for (int i = num_workers - 1; i >= 0; i--) {
AVxWorker *const worker = &cpi->workers[i];
winterface->sync(worker);
had_error |= !winterface->sync(worker);
}
if (had_error)
aom_internal_error(&cpi->common.error, AOM_CODEC_ERROR,
"Failed to encode tile data");
}
static void accumulate_counters_enc_workers(AV1_COMP *cpi, int num_workers) {
for (int i = 0; i < num_workers; i++) {
for (int i = num_workers - 1; i >= 0; i--) {
AVxWorker *const worker = &cpi->workers[i];
EncWorkerData *const thread_data = (EncWorkerData *)worker->data1;
cpi->intrabc_used |= thread_data->td->intrabc_used_this_tile;
cpi->intrabc_used |= thread_data->td->intrabc_used;
// Accumulate counters.
if (i < cpi->num_workers - 1) {
if (i > 0) {
av1_accumulate_frame_counts(&cpi->counts, thread_data->td->counts);
accumulate_rd_opt(&cpi->td, thread_data->td);
cpi->td.mb.txb_split_count += thread_data->td->mb.txb_split_count;
#if CONFIG_SPEED_STATS
cpi->td.mb.tx_search_count += thread_data->td->mb.tx_search_count;
#endif // CONFIG_SPEED_STATS
}
}
}
static void prepare_enc_workers(AV1_COMP *cpi, AVxWorkerHook hook,
int num_workers) {
for (int i = 0; i < num_workers; i++) {
for (int i = num_workers - 1; i >= 0; i--) {
AVxWorker *const worker = &cpi->workers[i];
EncWorkerData *const thread_data = &cpi->tile_thr_data[i];
@ -186,6 +532,8 @@ static void prepare_enc_workers(AV1_COMP *cpi, AVxWorkerHook hook,
worker->data1 = thread_data;
worker->data2 = NULL;
thread_data->td->intrabc_used = 0;
// Before encoding a frame, copy the thread data from cpi.
if (thread_data->td != &cpi->td) {
thread_data->td->mb = cpi->td.mb;
@ -193,6 +541,8 @@ static void prepare_enc_workers(AV1_COMP *cpi, AVxWorkerHook hook,
thread_data->td->mb.above_pred_buf = thread_data->td->above_pred_buf;
thread_data->td->mb.left_pred_buf = thread_data->td->left_pred_buf;
thread_data->td->mb.wsrc_buf = thread_data->td->wsrc_buf;
thread_data->td->mb.inter_modes_info = thread_data->td->inter_modes_info;
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
memcpy(thread_data->td->hash_value_buffer[x][y],
@ -209,7 +559,7 @@ static void prepare_enc_workers(AV1_COMP *cpi, AVxWorkerHook hook,
memcpy(thread_data->td->counts, &cpi->counts, sizeof(cpi->counts));
}
if (i < num_workers - 1) {
if (i > 0) {
thread_data->td->mb.palette_buffer = thread_data->td->palette_buffer;
thread_data->td->mb.tmp_conv_dst = thread_data->td->tmp_conv_dst;
for (int j = 0; j < 2; ++j) {
@ -259,3 +609,74 @@ void av1_accumulate_frame_counts(FRAME_COUNTS *acc_counts,
for (unsigned int i = 0; i < n_counts; i++) acc[i] += cnt[i];
}
void av1_encode_tiles_row_mt(AV1_COMP *cpi) {
AV1_COMMON *const cm = &cpi->common;
const int tile_cols = cm->tile_cols;
const int tile_rows = cm->tile_rows;
MultiThreadHandle *multi_thread_ctxt = &cpi->multi_thread_ctxt;
int num_workers = 0;
int total_num_sb_rows = 0;
int max_sb_rows = 0;
if (cpi->tile_data == NULL || cpi->allocated_tiles < tile_cols * tile_rows) {
av1_row_mt_mem_dealloc(cpi);
av1_alloc_tile_data(cpi);
}
av1_init_tile_data(cpi);
for (int row = 0; row < tile_rows; row++) {
for (int col = 0; col < tile_cols; col++) {
TileDataEnc *tile_data = &cpi->tile_data[row * cm->tile_cols + col];
int num_sb_rows_in_tile =
av1_get_sb_rows_in_tile(cm, tile_data->tile_info);
total_num_sb_rows += num_sb_rows_in_tile;
max_sb_rows = AOMMAX(max_sb_rows, num_sb_rows_in_tile);
}
}
num_workers = AOMMIN(cpi->oxcf.max_threads, total_num_sb_rows);
if (multi_thread_ctxt->allocated_tile_cols != tile_cols ||
multi_thread_ctxt->allocated_tile_rows != tile_rows ||
multi_thread_ctxt->allocated_sb_rows != max_sb_rows) {
av1_row_mt_mem_dealloc(cpi);
av1_row_mt_mem_alloc(cpi, max_sb_rows);
}
memset(multi_thread_ctxt->thread_id_to_tile_id, -1,
sizeof(*multi_thread_ctxt->thread_id_to_tile_id) * MAX_NUM_THREADS);
for (int tile_row = 0; tile_row < tile_rows; tile_row++) {
for (int tile_col = 0; tile_col < tile_cols; tile_col++) {
int tile_id = tile_row * tile_cols + tile_col;
TileDataEnc *this_tile = &cpi->tile_data[tile_id];
// Initialize cur_col to -1 for all rows.
memset(this_tile->row_mt_sync.cur_col, -1,
sizeof(*this_tile->row_mt_sync.cur_col) * max_sb_rows);
this_tile->row_mt_info.current_mi_row = this_tile->tile_info.mi_row_start;
this_tile->row_mt_info.num_threads_working = 0;
av1_inter_mode_data_init(this_tile);
av1_zero_above_context(cm, &cpi->td.mb.e_mbd,
this_tile->tile_info.mi_col_start,
this_tile->tile_info.mi_col_end, tile_row);
this_tile->m_search_count = 0; // Count of motion search hits.
this_tile->ex_search_count = 0; // Exhaustive mesh search hits.
}
}
// Only run once to create threads and allocate thread data.
if (cpi->num_workers == 0) {
create_enc_workers(cpi, num_workers);
} else {
num_workers = AOMMIN(num_workers, cpi->num_workers);
}
assign_tile_to_thread(multi_thread_ctxt, tile_cols * tile_rows, num_workers);
prepare_enc_workers(cpi, enc_row_mt_worker_hook, num_workers);
launch_enc_workers(cpi, num_workers);
sync_enc_workers(cpi, num_workers);
if (cm->delta_q_info.delta_lf_present_flag) update_delta_lf_for_row_mt(cpi);
accumulate_counters_enc_workers(cpi, num_workers);
}

View file

@ -18,14 +18,31 @@ extern "C" {
struct AV1_COMP;
struct ThreadData;
struct AV1RowMTSyncData;
typedef struct EncWorkerData {
struct AV1_COMP *cpi;
struct ThreadData *td;
int start;
int thread_id;
} EncWorkerData;
void av1_row_mt_sync_read(AV1RowMTSync *const row_mt_sync, int r, int c);
void av1_row_mt_sync_write(AV1RowMTSync *const row_mt_sync, int r, int c,
const int cols);
void av1_row_mt_sync_read_dummy(struct AV1RowMTSyncData *const row_mt_sync,
int r, int c);
void av1_row_mt_sync_write_dummy(struct AV1RowMTSyncData *const row_mt_sync,
int r, int c, const int cols);
void av1_row_mt_sync_mem_dealloc(AV1RowMTSync *row_mt_sync);
// Allocate memory for row based multi-threading synchronization.
void av1_row_mt_sync_mem_alloc(AV1RowMTSync *row_mt_sync, struct AV1Common *cm,
int rows);
void av1_encode_tiles_mt(struct AV1_COMP *cpi);
void av1_encode_tiles_row_mt(struct AV1_COMP *cpi);
void av1_accumulate_frame_counts(struct FRAME_COUNTS *acc_counts,
const struct FRAME_COUNTS *counts);

File diff suppressed because it is too large Load diff

View file

@ -86,7 +86,7 @@ typedef struct {
double raw_error_stdev;
} FIRSTPASS_STATS;
typedef enum {
enum {
KF_UPDATE = 0,
LF_UPDATE = 1,
GF_UPDATE = 2,
@ -98,34 +98,29 @@ typedef enum {
INTNL_OVERLAY_UPDATE = 8, // Internal Overlay Frame
INTNL_ARF_UPDATE = 9, // Internal Altref Frame (candidate for ALTREF2)
FRAME_UPDATE_TYPES = 10
} FRAME_UPDATE_TYPE;
} UENUM1BYTE(FRAME_UPDATE_TYPE);
#define FC_ANIMATION_THRESH 0.15
typedef enum {
enum {
FC_NORMAL = 0,
FC_GRAPHICS_ANIMATION = 1,
FRAME_CONTENT_TYPES = 2
} FRAME_CONTENT_TYPE;
} UENUM1BYTE(FRAME_CONTENT_TYPE);
typedef struct {
unsigned char index;
RATE_FACTOR_LEVEL rf_level[(MAX_LAG_BUFFERS * 2) + 1];
FRAME_UPDATE_TYPE update_type[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char arf_src_offset[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char arf_update_idx[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char arf_ref_idx[(MAX_LAG_BUFFERS * 2) + 1];
#if USE_SYMM_MULTI_LAYER
unsigned char arf_pos_in_gf[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char pyramid_level[(MAX_LAG_BUFFERS * 2) + 1];
RATE_FACTOR_LEVEL rf_level[MAX_STATIC_GF_GROUP_LENGTH + 1];
FRAME_UPDATE_TYPE update_type[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char arf_src_offset[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char arf_update_idx[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char arf_ref_idx[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char arf_pos_in_gf[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char pyramid_level[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char pyramid_height;
unsigned char pyramid_lvl_nodes[MAX_PYRAMID_LVL];
#endif
unsigned char brf_src_offset[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char bidir_pred_enabled[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char ref_fb_idx_map[(MAX_LAG_BUFFERS * 2) + 1][REF_FRAMES];
unsigned char refresh_idx[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char refresh_flag[(MAX_LAG_BUFFERS * 2) + 1];
int bit_allocation[(MAX_LAG_BUFFERS * 2) + 1];
unsigned char brf_src_offset[MAX_STATIC_GF_GROUP_LENGTH + 1];
unsigned char bidir_pred_enabled[MAX_STATIC_GF_GROUP_LENGTH + 1];
int bit_allocation[MAX_STATIC_GF_GROUP_LENGTH + 1];
} GF_GROUP;
typedef struct {
@ -176,29 +171,37 @@ typedef struct {
} TWO_PASS;
struct AV1_COMP;
struct EncodeFrameParams;
void av1_init_first_pass(struct AV1_COMP *cpi);
void av1_rc_get_first_pass_params(struct AV1_COMP *cpi);
void av1_first_pass(struct AV1_COMP *cpi, const struct lookahead_entry *source);
void av1_first_pass(struct AV1_COMP *cpi, const int64_t ts_duration);
void av1_end_first_pass(struct AV1_COMP *cpi);
void av1_init_second_pass(struct AV1_COMP *cpi);
void av1_rc_get_second_pass_params(struct AV1_COMP *cpi);
void av1_configure_buffer_updates_firstpass(struct AV1_COMP *cpi,
FRAME_UPDATE_TYPE update_type);
void av1_rc_get_second_pass_params(
struct AV1_COMP *cpi, struct EncodeFrameParams *const frame_params);
// Post encode update of the rate control parameters for 2-pass
void av1_twopass_postencode_update(struct AV1_COMP *cpi);
static INLINE int get_number_of_extra_arfs(int interval, int arf_pending) {
if (arf_pending && MAX_EXT_ARFS > 0)
return interval >= MIN_EXT_ARF_INTERVAL * (MAX_EXT_ARFS + 1)
? MAX_EXT_ARFS
: interval >= MIN_EXT_ARF_INTERVAL * MAX_EXT_ARFS
? MAX_EXT_ARFS - 1
: 0;
else
return 0;
static INLINE int get_number_of_extra_arfs(int interval, int arf_pending,
int max_pyr_height) {
// Max extra (internal) alt-refs allowed based on interval.
int extra_arfs_from_interval = 0;
if (arf_pending && MAX_EXT_ARFS > 0) {
extra_arfs_from_interval =
(interval >= MIN_EXT_ARF_INTERVAL * (MAX_EXT_ARFS + 1))
? MAX_EXT_ARFS
: (interval >= MIN_EXT_ARF_INTERVAL * MAX_EXT_ARFS)
? MAX_EXT_ARFS - 1
: 0;
}
// Max extra (internal) alt-refs allowed based on max pyramid height.
assert(max_pyr_height >= 1);
const int ext_arfs_from_max_height = max_pyr_height - 1;
// Finally, min of the two above is our actual max allowance.
return AOMMIN(extra_arfs_from_interval, ext_arfs_from_max_height);
}
#ifdef __cplusplus

View file

@ -15,8 +15,12 @@
#include <math.h>
#include <assert.h>
#include "config/aom_dsp_rtcd.h"
#include "av1/encoder/global_motion.h"
#include "av1/common/convolve.h"
#include "av1/common/resize.h"
#include "av1/common/warped_motion.h"
#include "av1/encoder/segmentation.h"
@ -28,15 +32,45 @@
#define MIN_INLIER_PROB 0.1
#define MIN_TRANS_THRESH (1 * GM_TRANS_DECODE_FACTOR)
#define USE_GM_FEATURE_BASED 1
// Border over which to compute the global motion
#define ERRORADV_BORDER 0
// Number of pyramid levels in disflow computation
#define N_LEVELS 2
// Size of square patches in the disflow dense grid
#define PATCH_SIZE 8
// Center point of square patch
#define PATCH_CENTER ((PATCH_SIZE + 1) >> 1)
// Step size between patches, lower value means greater patch overlap
#define PATCH_STEP 1
// Minimum size of border padding for disflow
#define MIN_PAD 7
// Warp error convergence threshold for disflow
#define DISFLOW_ERROR_TR 0.01
// Max number of iterations if warp convergence is not found
#define DISFLOW_MAX_ITR 10
// Struct for an image pyramid
typedef struct {
int n_levels;
int pad_size;
int has_gradient;
int widths[N_LEVELS];
int heights[N_LEVELS];
int strides[N_LEVELS];
int level_loc[N_LEVELS];
unsigned char *level_buffer;
double *level_dx_buffer;
double *level_dy_buffer;
} ImagePyramid;
static const double erroradv_tr[] = { 0.65, 0.60, 0.55 };
static const double erroradv_prod_tr[] = { 20000, 18000, 16000 };
int is_enough_erroradvantage(double best_erroradvantage, int params_cost,
int erroradv_type) {
int av1_is_enough_erroradvantage(double best_erroradvantage, int params_cost,
int erroradv_type) {
assert(erroradv_type < GM_ERRORADV_TR_TYPES);
return best_erroradvantage < erroradv_tr[erroradv_type] &&
best_erroradvantage * params_cost < erroradv_prod_tr[erroradv_type];
@ -75,9 +109,10 @@ static void convert_to_params(const double *params, int32_t *model) {
}
}
void convert_model_to_params(const double *params, WarpedMotionParams *model) {
void av1_convert_model_to_params(const double *params,
WarpedMotionParams *model) {
convert_to_params(params, model->wmmat);
model->wmtype = get_gmtype(model);
model->wmtype = get_wmtype(model);
model->invalid = 0;
}
@ -131,12 +166,13 @@ static void force_wmtype(WarpedMotionParams *wm, TransformationType wmtype) {
wm->wmtype = wmtype;
}
int64_t refine_integerized_param(WarpedMotionParams *wm,
TransformationType wmtype, int use_hbd, int bd,
uint8_t *ref, int r_width, int r_height,
int r_stride, uint8_t *dst, int d_width,
int d_height, int d_stride, int n_refinements,
int64_t best_frame_error) {
int64_t av1_refine_integerized_param(WarpedMotionParams *wm,
TransformationType wmtype, int use_hbd,
int bd, uint8_t *ref, int r_width,
int r_height, int r_stride, uint8_t *dst,
int d_width, int d_height, int d_stride,
int n_refinements,
int64_t best_frame_error) {
static const int max_trans_model_params[TRANS_TYPES] = { 0, 2, 4, 6 };
const int border = ERRORADV_BORDER;
int i = 0, p;
@ -209,7 +245,7 @@ int64_t refine_integerized_param(WarpedMotionParams *wm,
}
}
force_wmtype(wm, wmtype);
wm->wmtype = get_gmtype(wm);
wm->wmtype = get_wmtype(wm);
return best_error;
}
@ -240,12 +276,11 @@ static unsigned char *downconvert_frame(YV12_BUFFER_CONFIG *frm,
return buf_8bit;
}
int compute_global_motion_feature_based(TransformationType type,
YV12_BUFFER_CONFIG *frm,
YV12_BUFFER_CONFIG *ref, int bit_depth,
int *num_inliers_by_motion,
double *params_by_motion,
int num_motions) {
#if USE_GM_FEATURE_BASED
static int compute_global_motion_feature_based(
TransformationType type, YV12_BUFFER_CONFIG *frm, YV12_BUFFER_CONFIG *ref,
int bit_depth, int *num_inliers_by_motion, double *params_by_motion,
int num_motions) {
int i;
int num_frm_corners, num_ref_corners;
int num_correspondences;
@ -296,3 +331,557 @@ int compute_global_motion_feature_based(TransformationType type,
}
return 0;
}
#else
static INLINE RansacFuncDouble
get_ransac_double_prec_type(TransformationType type) {
switch (type) {
case AFFINE: return ransac_affine_double_prec;
case ROTZOOM: return ransac_rotzoom_double_prec;
case TRANSLATION: return ransac_translation_double_prec;
default: assert(0); return NULL;
}
}
// Don't use points around the frame border since they are less reliable
static INLINE int valid_point(int x, int y, int width, int height) {
return (x > (PATCH_SIZE + PATCH_CENTER)) &&
(x < (width - PATCH_SIZE - PATCH_CENTER)) &&
(y > (PATCH_SIZE + PATCH_CENTER)) &&
(y < (height - PATCH_SIZE - PATCH_CENTER));
}
static int determine_disflow_correspondence(int *frm_corners,
int num_frm_corners, double *flow_u,
double *flow_v, int width,
int height, int stride,
double *correspondences) {
int num_correspondences = 0;
int x, y;
for (int i = 0; i < num_frm_corners; ++i) {
x = frm_corners[2 * i];
y = frm_corners[2 * i + 1];
if (valid_point(x, y, width, height)) {
correspondences[4 * num_correspondences] = x;
correspondences[4 * num_correspondences + 1] = y;
correspondences[4 * num_correspondences + 2] = x + flow_u[y * stride + x];
correspondences[4 * num_correspondences + 3] = y + flow_v[y * stride + x];
num_correspondences++;
}
}
return num_correspondences;
}
double getCubicValue(double p[4], double x) {
return p[1] + 0.5 * x *
(p[2] - p[0] +
x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] +
x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));
}
void get_subcolumn(unsigned char *ref, double col[4], int stride, int x,
int y_start) {
int i;
for (i = 0; i < 4; ++i) {
col[i] = ref[(i + y_start) * stride + x];
}
}
double bicubic(unsigned char *ref, double x, double y, int stride) {
double arr[4];
int k;
int i = (int)x;
int j = (int)y;
for (k = 0; k < 4; ++k) {
double arr_temp[4];
get_subcolumn(ref, arr_temp, stride, i + k - 1, j - 1);
arr[k] = getCubicValue(arr_temp, y - j);
}
return getCubicValue(arr, x - i);
}
// Interpolate a warped block using bicubic interpolation when possible
unsigned char interpolate(unsigned char *ref, double x, double y, int width,
int height, int stride) {
if (x < 0 && y < 0)
return ref[0];
else if (x < 0 && y > height - 1)
return ref[(height - 1) * stride];
else if (x > width - 1 && y < 0)
return ref[width - 1];
else if (x > width - 1 && y > height - 1)
return ref[(height - 1) * stride + (width - 1)];
else if (x < 0) {
int v;
int i = (int)y;
double a = y - i;
if (y > 1 && y < height - 2) {
double arr[4];
get_subcolumn(ref, arr, stride, 0, i - 1);
return clamp((int)(getCubicValue(arr, a) + 0.5), 0, 255);
}
v = (int)(ref[i * stride] * (1 - a) + ref[(i + 1) * stride] * a + 0.5);
return clamp(v, 0, 255);
} else if (y < 0) {
int v;
int j = (int)x;
double b = x - j;
if (x > 1 && x < width - 2) {
double arr[4] = { ref[j - 1], ref[j], ref[j + 1], ref[j + 2] };
return clamp((int)(getCubicValue(arr, b) + 0.5), 0, 255);
}
v = (int)(ref[j] * (1 - b) + ref[j + 1] * b + 0.5);
return clamp(v, 0, 255);
} else if (x > width - 1) {
int v;
int i = (int)y;
double a = y - i;
if (y > 1 && y < height - 2) {
double arr[4];
get_subcolumn(ref, arr, stride, width - 1, i - 1);
return clamp((int)(getCubicValue(arr, a) + 0.5), 0, 255);
}
v = (int)(ref[i * stride + width - 1] * (1 - a) +
ref[(i + 1) * stride + width - 1] * a + 0.5);
return clamp(v, 0, 255);
} else if (y > height - 1) {
int v;
int j = (int)x;
double b = x - j;
if (x > 1 && x < width - 2) {
int row = (height - 1) * stride;
double arr[4] = { ref[row + j - 1], ref[row + j], ref[row + j + 1],
ref[row + j + 2] };
return clamp((int)(getCubicValue(arr, b) + 0.5), 0, 255);
}
v = (int)(ref[(height - 1) * stride + j] * (1 - b) +
ref[(height - 1) * stride + j + 1] * b + 0.5);
return clamp(v, 0, 255);
} else if (x > 1 && y > 1 && x < width - 2 && y < height - 2) {
return clamp((int)(bicubic(ref, x, y, stride) + 0.5), 0, 255);
} else {
int i = (int)y;
int j = (int)x;
double a = y - i;
double b = x - j;
int v = (int)(ref[i * stride + j] * (1 - a) * (1 - b) +
ref[i * stride + j + 1] * (1 - a) * b +
ref[(i + 1) * stride + j] * a * (1 - b) +
ref[(i + 1) * stride + j + 1] * a * b);
return clamp(v, 0, 255);
}
}
// Warps a block using flow vector [u, v] and computes the mse
double compute_warp_and_error(unsigned char *ref, unsigned char *frm, int width,
int height, int stride, int x, int y, double u,
double v, int16_t *dt) {
int i, j;
unsigned char warped;
double x_w, y_w;
double mse = 0;
int16_t err = 0;
for (i = y; i < y + PATCH_SIZE; ++i)
for (j = x; j < x + PATCH_SIZE; ++j) {
x_w = (double)j + u;
y_w = (double)i + v;
warped = interpolate(ref, x_w, y_w, width, height, stride);
err = warped - frm[j + i * stride];
mse += err * err;
dt[(i - y) * PATCH_SIZE + (j - x)] = err;
}
mse /= (PATCH_SIZE * PATCH_SIZE);
return mse;
}
// Computes the components of the system of equations used to solve for
// a flow vector. This includes:
// 1.) The hessian matrix for optical flow. This matrix is in the
// form of:
//
// M = |sum(dx * dx) sum(dx * dy)|
// |sum(dx * dy) sum(dy * dy)|
//
// 2.) b = |sum(dx * dt)|
// |sum(dy * dt)|
// Where the sums are computed over a square window of PATCH_SIZE.
static INLINE void compute_flow_system(const double *dx, int dx_stride,
const double *dy, int dy_stride,
const int16_t *dt, int dt_stride,
double *M, double *b) {
for (int i = 0; i < PATCH_SIZE; i++) {
for (int j = 0; j < PATCH_SIZE; j++) {
M[0] += dx[i * dx_stride + j] * dx[i * dx_stride + j];
M[1] += dx[i * dx_stride + j] * dy[i * dy_stride + j];
M[3] += dy[i * dy_stride + j] * dy[i * dy_stride + j];
b[0] += dx[i * dx_stride + j] * dt[i * dt_stride + j];
b[1] += dy[i * dy_stride + j] * dt[i * dt_stride + j];
}
}
M[2] = M[1];
}
// Solves a general Mx = b where M is a 2x2 matrix and b is a 2x1 matrix
static INLINE void solve_2x2_system(const double *M, const double *b,
double *output_vec) {
double M_0 = M[0];
double M_3 = M[3];
double det = (M_0 * M_3) - (M[1] * M[2]);
if (det < 1e-5) {
// Handle singular matrix
// TODO(sarahparker) compare results using pseudo inverse instead
M_0 += 1e-10;
M_3 += 1e-10;
det = (M_0 * M_3) - (M[1] * M[2]);
}
const double det_inv = 1 / det;
const double mult_b0 = det_inv * b[0];
const double mult_b1 = det_inv * b[1];
output_vec[0] = M_3 * mult_b0 - M[1] * mult_b1;
output_vec[1] = -M[2] * mult_b0 + M_0 * mult_b1;
}
static INLINE void image_difference(const uint8_t *src, int src_stride,
const uint8_t *ref, int ref_stride,
int16_t *dst, int dst_stride, int height,
int width) {
const int block_unit = 8;
// Take difference in 8x8 blocks to make use of optimized diff function
for (int i = 0; i < height; i += block_unit) {
for (int j = 0; j < width; j += block_unit) {
aom_subtract_block(block_unit, block_unit, dst + i * dst_stride + j,
dst_stride, src + i * src_stride + j, src_stride,
ref + i * ref_stride + j, ref_stride);
}
}
}
// Compute an image gradient using a sobel filter.
// If dir == 1, compute the x gradient. If dir == 0, compute y. This function
// assumes the images have been padded so that they can be processed in units
// of 8.
static INLINE void sobel_xy_image_gradient(const uint8_t *src, int src_stride,
double *dst, int dst_stride,
int height, int width, int dir) {
double norm = 1.0;
// TODO(sarahparker) experiment with doing this over larger block sizes
const int block_unit = 8;
// Filter in 8x8 blocks to eventually make use of optimized convolve function
for (int i = 0; i < height; i += block_unit) {
for (int j = 0; j < width; j += block_unit) {
av1_convolve_2d_sobel_y_c(src + i * src_stride + j, src_stride,
dst + i * dst_stride + j, dst_stride,
block_unit, block_unit, dir, norm);
}
}
}
static ImagePyramid *alloc_pyramid(int width, int height, int pad_size,
int compute_gradient) {
ImagePyramid *pyr = aom_malloc(sizeof(*pyr));
pyr->has_gradient = compute_gradient;
// 2 * width * height is the upper bound for a buffer that fits
// all pyramid levels + padding for each level
const int buffer_size = sizeof(*pyr->level_buffer) * 2 * width * height +
(width + 2 * pad_size) * 2 * pad_size * N_LEVELS;
pyr->level_buffer = aom_malloc(buffer_size);
memset(pyr->level_buffer, 0, buffer_size);
if (compute_gradient) {
const int gradient_size =
sizeof(*pyr->level_dx_buffer) * 2 * width * height +
(width + 2 * pad_size) * 2 * pad_size * N_LEVELS;
pyr->level_dx_buffer = aom_malloc(gradient_size);
pyr->level_dy_buffer = aom_malloc(gradient_size);
memset(pyr->level_dx_buffer, 0, gradient_size);
memset(pyr->level_dy_buffer, 0, gradient_size);
}
return pyr;
}
static void free_pyramid(ImagePyramid *pyr) {
aom_free(pyr->level_buffer);
if (pyr->has_gradient) {
aom_free(pyr->level_dx_buffer);
aom_free(pyr->level_dy_buffer);
}
aom_free(pyr);
}
static INLINE void update_level_dims(ImagePyramid *frm_pyr, int level) {
frm_pyr->widths[level] = frm_pyr->widths[level - 1] >> 1;
frm_pyr->heights[level] = frm_pyr->heights[level - 1] >> 1;
frm_pyr->strides[level] = frm_pyr->widths[level] + 2 * frm_pyr->pad_size;
// Point the beginning of the next level buffer to the correct location inside
// the padded border
frm_pyr->level_loc[level] =
frm_pyr->level_loc[level - 1] +
frm_pyr->strides[level - 1] *
(2 * frm_pyr->pad_size + frm_pyr->heights[level - 1]);
}
// Compute coarse to fine pyramids for a frame
static void compute_flow_pyramids(unsigned char *frm, const int frm_width,
const int frm_height, const int frm_stride,
int n_levels, int pad_size, int compute_grad,
ImagePyramid *frm_pyr) {
int cur_width, cur_height, cur_stride, cur_loc;
assert((frm_width >> n_levels) > 0);
assert((frm_height >> n_levels) > 0);
// Initialize first level
frm_pyr->n_levels = n_levels;
frm_pyr->pad_size = pad_size;
frm_pyr->widths[0] = frm_width;
frm_pyr->heights[0] = frm_height;
frm_pyr->strides[0] = frm_width + 2 * frm_pyr->pad_size;
// Point the beginning of the level buffer to the location inside
// the padded border
frm_pyr->level_loc[0] =
frm_pyr->strides[0] * frm_pyr->pad_size + frm_pyr->pad_size;
// This essentially copies the original buffer into the pyramid buffer
// without the original padding
av1_resize_plane(frm, frm_height, frm_width, frm_stride,
frm_pyr->level_buffer + frm_pyr->level_loc[0],
frm_pyr->heights[0], frm_pyr->widths[0],
frm_pyr->strides[0]);
if (compute_grad) {
cur_width = frm_pyr->widths[0];
cur_height = frm_pyr->heights[0];
cur_stride = frm_pyr->strides[0];
cur_loc = frm_pyr->level_loc[0];
assert(frm_pyr->has_gradient && frm_pyr->level_dx_buffer != NULL &&
frm_pyr->level_dy_buffer != NULL);
// Computation x gradient
sobel_xy_image_gradient(frm_pyr->level_buffer + cur_loc, cur_stride,
frm_pyr->level_dx_buffer + cur_loc, cur_stride,
cur_height, cur_width, 1);
// Computation y gradient
sobel_xy_image_gradient(frm_pyr->level_buffer + cur_loc, cur_stride,
frm_pyr->level_dy_buffer + cur_loc, cur_stride,
cur_height, cur_width, 0);
}
// Start at the finest level and resize down to the coarsest level
for (int level = 1; level < n_levels; ++level) {
update_level_dims(frm_pyr, level);
cur_width = frm_pyr->widths[level];
cur_height = frm_pyr->heights[level];
cur_stride = frm_pyr->strides[level];
cur_loc = frm_pyr->level_loc[level];
av1_resize_plane(frm_pyr->level_buffer + frm_pyr->level_loc[level - 1],
frm_pyr->heights[level - 1], frm_pyr->widths[level - 1],
frm_pyr->strides[level - 1],
frm_pyr->level_buffer + cur_loc, cur_height, cur_width,
cur_stride);
if (compute_grad) {
assert(frm_pyr->has_gradient && frm_pyr->level_dx_buffer != NULL &&
frm_pyr->level_dy_buffer != NULL);
// Computation x gradient
sobel_xy_image_gradient(frm_pyr->level_buffer + cur_loc, cur_stride,
frm_pyr->level_dx_buffer + cur_loc, cur_stride,
cur_height, cur_width, 1);
// Computation y gradient
sobel_xy_image_gradient(frm_pyr->level_buffer + cur_loc, cur_stride,
frm_pyr->level_dy_buffer + cur_loc, cur_stride,
cur_height, cur_width, 0);
}
}
}
static INLINE void compute_flow_at_point(unsigned char *frm, unsigned char *ref,
double *dx, double *dy, int x, int y,
int width, int height, int stride,
double *u, double *v) {
double M[4] = { 0 };
double b[2] = { 0 };
double tmp_output_vec[2] = { 0 };
double error = 0;
int16_t dt[PATCH_SIZE * PATCH_SIZE];
double o_u = *u;
double o_v = *v;
for (int itr = 0; itr < DISFLOW_MAX_ITR; itr++) {
error = compute_warp_and_error(ref, frm, width, height, stride, x, y, *u,
*v, dt);
if (error <= DISFLOW_ERROR_TR) break;
compute_flow_system(dx, stride, dy, stride, dt, PATCH_SIZE, M, b);
solve_2x2_system(M, b, tmp_output_vec);
*u += tmp_output_vec[0];
*v += tmp_output_vec[1];
}
if (fabs(*u - o_u) > PATCH_SIZE || fabs(*v - o_u) > PATCH_SIZE) {
*u = o_u;
*v = o_v;
}
}
// make sure flow_u and flow_v start at 0
static void compute_flow_field(ImagePyramid *frm_pyr, ImagePyramid *ref_pyr,
double *flow_u, double *flow_v) {
int cur_width, cur_height, cur_stride, cur_loc, patch_loc, patch_center;
double *u_upscale =
aom_malloc(frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_u));
double *v_upscale =
aom_malloc(frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_v));
assert(frm_pyr->n_levels == ref_pyr->n_levels);
// Compute flow field from coarsest to finest level of the pyramid
for (int level = frm_pyr->n_levels - 1; level >= 0; --level) {
cur_width = frm_pyr->widths[level];
cur_height = frm_pyr->heights[level];
cur_stride = frm_pyr->strides[level];
cur_loc = frm_pyr->level_loc[level];
for (int i = PATCH_SIZE; i < cur_height - PATCH_SIZE; i += PATCH_STEP) {
for (int j = PATCH_SIZE; j < cur_width - PATCH_SIZE; j += PATCH_STEP) {
patch_loc = i * cur_stride + j;
patch_center = patch_loc + PATCH_CENTER * cur_stride + PATCH_CENTER;
compute_flow_at_point(frm_pyr->level_buffer + cur_loc,
ref_pyr->level_buffer + cur_loc,
frm_pyr->level_dx_buffer + cur_loc + patch_loc,
frm_pyr->level_dy_buffer + cur_loc + patch_loc, j,
i, cur_width, cur_height, cur_stride,
flow_u + patch_center, flow_v + patch_center);
}
}
// TODO(sarahparker) Replace this with upscale function in resize.c
if (level > 0) {
int h_upscale = frm_pyr->heights[level - 1];
int w_upscale = frm_pyr->widths[level - 1];
int s_upscale = frm_pyr->strides[level - 1];
for (int i = 0; i < h_upscale; ++i) {
for (int j = 0; j < w_upscale; ++j) {
u_upscale[j + i * s_upscale] =
flow_u[(int)(j >> 1) + (int)(i >> 1) * cur_stride];
v_upscale[j + i * s_upscale] =
flow_v[(int)(j >> 1) + (int)(i >> 1) * cur_stride];
}
}
memcpy(flow_u, u_upscale,
frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_u));
memcpy(flow_v, v_upscale,
frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_v));
}
}
aom_free(u_upscale);
aom_free(v_upscale);
}
static int compute_global_motion_disflow_based(
TransformationType type, YV12_BUFFER_CONFIG *frm, YV12_BUFFER_CONFIG *ref,
int bit_depth, int *num_inliers_by_motion, double *params_by_motion,
int num_motions) {
unsigned char *frm_buffer = frm->y_buffer;
unsigned char *ref_buffer = ref->y_buffer;
const int frm_width = frm->y_width;
const int frm_height = frm->y_height;
const int ref_width = ref->y_width;
const int ref_height = ref->y_height;
const int pad_size = AOMMAX(PATCH_SIZE, MIN_PAD);
int num_frm_corners;
int num_correspondences;
double *correspondences;
int frm_corners[2 * MAX_CORNERS];
RansacFuncDouble ransac = get_ransac_double_prec_type(type);
assert(frm_width == ref_width);
assert(frm_height == ref_height);
// Ensure the number of pyramid levels will work with the frame resolution
const int msb =
frm_width < frm_height ? get_msb(frm_width) : get_msb(frm_height);
const int n_levels = AOMMIN(msb, N_LEVELS);
if (frm->flags & YV12_FLAG_HIGHBITDEPTH) {
// The frame buffer is 16-bit, so we need to convert to 8 bits for the
// following code. We cache the result until the frame is released.
frm_buffer = downconvert_frame(frm, bit_depth);
}
if (ref->flags & YV12_FLAG_HIGHBITDEPTH) {
ref_buffer = downconvert_frame(ref, bit_depth);
}
// TODO(sarahparker) We will want to do the source pyramid computation
// outside of this function so it doesn't get recomputed for every
// reference. We also don't need to compute every pyramid level for the
// reference in advance, since lower levels can be overwritten once their
// flow field is computed and upscaled. I'll add these optimizations
// once the full implementation is working.
// Allocate frm image pyramids
int compute_gradient = 1;
ImagePyramid *frm_pyr =
alloc_pyramid(frm_width, frm_height, pad_size, compute_gradient);
compute_flow_pyramids(frm_buffer, frm_width, frm_height, frm->y_stride,
n_levels, pad_size, compute_gradient, frm_pyr);
// Allocate ref image pyramids
compute_gradient = 0;
ImagePyramid *ref_pyr =
alloc_pyramid(ref_width, ref_height, pad_size, compute_gradient);
compute_flow_pyramids(ref_buffer, ref_width, ref_height, ref->y_stride,
n_levels, pad_size, compute_gradient, ref_pyr);
double *flow_u =
aom_malloc(frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_u));
double *flow_v =
aom_malloc(frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_v));
memset(flow_u, 0,
frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_u));
memset(flow_v, 0,
frm_pyr->strides[0] * frm_pyr->heights[0] * sizeof(*flow_v));
compute_flow_field(frm_pyr, ref_pyr, flow_u, flow_v);
// compute interest points in images using FAST features
num_frm_corners = fast_corner_detect(frm_buffer, frm_width, frm_height,
frm->y_stride, frm_corners, MAX_CORNERS);
// find correspondences between the two images using the flow field
correspondences = aom_malloc(num_frm_corners * 4 * sizeof(*correspondences));
num_correspondences = determine_disflow_correspondence(
frm_corners, num_frm_corners, flow_u, flow_v, frm_width, frm_height,
frm_pyr->strides[0], correspondences);
ransac(correspondences, num_correspondences, num_inliers_by_motion,
params_by_motion, num_motions);
free_pyramid(frm_pyr);
free_pyramid(ref_pyr);
aom_free(correspondences);
aom_free(flow_u);
aom_free(flow_v);
// Set num_inliers = 0 for motions with too few inliers so they are ignored.
for (int i = 0; i < num_motions; ++i) {
if (num_inliers_by_motion[i] < MIN_INLIER_PROB * num_correspondences) {
num_inliers_by_motion[i] = 0;
}
}
// Return true if any one of the motions has inliers.
for (int i = 0; i < num_motions; ++i) {
if (num_inliers_by_motion[i] > 0) return 1;
}
return 0;
}
#endif
int av1_compute_global_motion(TransformationType type, YV12_BUFFER_CONFIG *frm,
YV12_BUFFER_CONFIG *ref, int bit_depth,
int *num_inliers_by_motion,
double *params_by_motion, int num_motions) {
#if USE_GM_FEATURE_BASED
return compute_global_motion_feature_based(type, frm, ref, bit_depth,
num_inliers_by_motion,
params_by_motion, num_motions);
#else
return compute_global_motion_disflow_based(type, frm, ref, bit_depth,
num_inliers_by_motion,
params_by_motion, num_motions);
#endif
}

View file

@ -22,20 +22,22 @@ extern "C" {
#define RANSAC_NUM_MOTIONS 1
void convert_model_to_params(const double *params, WarpedMotionParams *model);
void av1_convert_model_to_params(const double *params,
WarpedMotionParams *model);
int is_enough_erroradvantage(double best_erroradvantage, int params_cost,
int erroradv_type);
int av1_is_enough_erroradvantage(double best_erroradvantage, int params_cost,
int erroradv_type);
// Returns the av1_warp_error between "dst" and the result of applying the
// motion params that result from fine-tuning "wm" to "ref". Note that "wm" is
// modified in place.
int64_t refine_integerized_param(WarpedMotionParams *wm,
TransformationType wmtype, int use_hbd, int bd,
uint8_t *ref, int r_width, int r_height,
int r_stride, uint8_t *dst, int d_width,
int d_height, int d_stride, int n_refinements,
int64_t best_frame_error);
int64_t av1_refine_integerized_param(WarpedMotionParams *wm,
TransformationType wmtype, int use_hbd,
int bd, uint8_t *ref, int r_width,
int r_height, int r_stride, uint8_t *dst,
int d_width, int d_height, int d_stride,
int n_refinements,
int64_t best_frame_error);
/*
Computes "num_motions" candidate global motion parameters between two frames.
@ -52,12 +54,10 @@ int64_t refine_integerized_param(WarpedMotionParams *wm,
number of inlier feature points for each motion. Params for which the
num_inliers entry is 0 should be ignored by the caller.
*/
int compute_global_motion_feature_based(TransformationType type,
YV12_BUFFER_CONFIG *frm,
YV12_BUFFER_CONFIG *ref, int bit_depth,
int *num_inliers_by_motion,
double *params_by_motion,
int num_motions);
int av1_compute_global_motion(TransformationType type, YV12_BUFFER_CONFIG *frm,
YV12_BUFFER_CONFIG *ref, int bit_depth,
int *num_inliers_by_motion,
double *params_by_motion, int num_motions);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -147,7 +147,8 @@ static void hash_table_add_to_table(hash_table *p_hash_table,
}
}
int32_t av1_hash_table_count(hash_table *p_hash_table, uint32_t hash_value) {
int32_t av1_hash_table_count(const hash_table *p_hash_table,
uint32_t hash_value) {
if (p_hash_table->p_lookup_table[hash_value] == NULL) {
return 0;
} else {

View file

@ -37,7 +37,8 @@ typedef struct _hash_table {
void av1_hash_table_init(hash_table *p_hash_table, struct macroblock *x);
void av1_hash_table_destroy(hash_table *p_hash_table);
void av1_hash_table_create(hash_table *p_hash_table);
int32_t av1_hash_table_count(hash_table *p_hash_table, uint32_t hash_value);
int32_t av1_hash_table_count(const hash_table *p_hash_table,
uint32_t hash_value);
Iterator av1_hash_get_first_iterator(hash_table *p_hash_table,
uint32_t hash_value);
int32_t av1_has_exact_match(hash_table *p_hash_table, uint32_t hash_value1,

View file

@ -87,35 +87,21 @@ static void highbd_fwd_txfm_4x4(const int16_t *src_diff, tran_low_t *coeff,
av1_highbd_fwht4x4(src_diff, coeff, diff_stride);
return;
}
switch (tx_type) {
// use the c version for anything including identity for now
case V_DCT:
case H_DCT:
case V_ADST:
case H_ADST:
case V_FLIPADST:
case H_FLIPADST:
case IDTX:
av1_fwd_txfm2d_4x4_c(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
default:
av1_fwd_txfm2d_4x4(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
}
av1_fwd_txfm2d_4x4(src_diff, dst_coeff, diff_stride, tx_type, bd);
}
static void highbd_fwd_txfm_4x8(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_4x8_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_4x8(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_8x4(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_8x4_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_8x4(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_8x16(const int16_t *src_diff, tran_low_t *coeff,
@ -123,21 +109,7 @@ static void highbd_fwd_txfm_8x16(const int16_t *src_diff, tran_low_t *coeff,
int32_t *dst_coeff = (int32_t *)coeff;
const TX_TYPE tx_type = txfm_param->tx_type;
const int bd = txfm_param->bd;
switch (tx_type) {
// use the c version for anything including identity for now
case V_DCT:
case H_DCT:
case V_ADST:
case H_ADST:
case V_FLIPADST:
case H_FLIPADST:
case IDTX:
av1_fwd_txfm2d_8x16_c(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
default:
av1_fwd_txfm2d_8x16(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
}
av1_fwd_txfm2d_8x16(src_diff, dst_coeff, diff_stride, tx_type, bd);
}
static void highbd_fwd_txfm_16x8(const int16_t *src_diff, tran_low_t *coeff,
@ -145,63 +117,49 @@ static void highbd_fwd_txfm_16x8(const int16_t *src_diff, tran_low_t *coeff,
int32_t *dst_coeff = (int32_t *)coeff;
const TX_TYPE tx_type = txfm_param->tx_type;
const int bd = txfm_param->bd;
switch (tx_type) {
// use the c version for anything including identity for now
case V_DCT:
case H_DCT:
case V_ADST:
case H_ADST:
case V_FLIPADST:
case H_FLIPADST:
case IDTX:
av1_fwd_txfm2d_16x8_c(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
default:
av1_fwd_txfm2d_16x8(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
}
av1_fwd_txfm2d_16x8(src_diff, dst_coeff, diff_stride, tx_type, bd);
}
static void highbd_fwd_txfm_16x32(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_16x32_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_16x32(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_32x16(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_32x16_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_32x16(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_16x4(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_16x4_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_16x4(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_4x16(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_4x16_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_4x16(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_32x8(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_32x8_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_32x8(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_8x32(const int16_t *src_diff, tran_low_t *coeff,
int diff_stride, TxfmParam *txfm_param) {
int32_t *dst_coeff = (int32_t *)coeff;
av1_fwd_txfm2d_8x32_c(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
av1_fwd_txfm2d_8x32(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
txfm_param->bd);
}
static void highbd_fwd_txfm_8x8(const int16_t *src_diff, tran_low_t *coeff,
@ -209,21 +167,7 @@ static void highbd_fwd_txfm_8x8(const int16_t *src_diff, tran_low_t *coeff,
int32_t *dst_coeff = (int32_t *)coeff;
const TX_TYPE tx_type = txfm_param->tx_type;
const int bd = txfm_param->bd;
switch (tx_type) {
// use the c version for anything including identity for now
case V_DCT:
case H_DCT:
case V_ADST:
case H_ADST:
case V_FLIPADST:
case H_FLIPADST:
case IDTX:
av1_fwd_txfm2d_8x8_c(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
default:
av1_fwd_txfm2d_8x8(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
}
av1_fwd_txfm2d_8x8(src_diff, dst_coeff, diff_stride, tx_type, bd);
}
static void highbd_fwd_txfm_16x16(const int16_t *src_diff, tran_low_t *coeff,
@ -231,21 +175,7 @@ static void highbd_fwd_txfm_16x16(const int16_t *src_diff, tran_low_t *coeff,
int32_t *dst_coeff = (int32_t *)coeff;
const TX_TYPE tx_type = txfm_param->tx_type;
const int bd = txfm_param->bd;
switch (tx_type) {
// use the c version for anything including identity for now
case V_DCT:
case H_DCT:
case V_ADST:
case H_ADST:
case V_FLIPADST:
case H_FLIPADST:
case IDTX:
av1_fwd_txfm2d_16x16_c(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
default:
av1_fwd_txfm2d_16x16(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
}
av1_fwd_txfm2d_16x16(src_diff, dst_coeff, diff_stride, tx_type, bd);
}
static void highbd_fwd_txfm_32x32(const int16_t *src_diff, tran_low_t *coeff,
@ -253,21 +183,7 @@ static void highbd_fwd_txfm_32x32(const int16_t *src_diff, tran_low_t *coeff,
int32_t *dst_coeff = (int32_t *)coeff;
const TX_TYPE tx_type = txfm_param->tx_type;
const int bd = txfm_param->bd;
switch (tx_type) {
// use the c version for anything including identity for now
case V_DCT:
case H_DCT:
case V_ADST:
case H_ADST:
case V_FLIPADST:
case H_FLIPADST:
case IDTX:
av1_fwd_txfm2d_32x32_c(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
default:
av1_fwd_txfm2d_32x32(src_diff, dst_coeff, diff_stride, tx_type, bd);
break;
}
av1_fwd_txfm2d_32x32(src_diff, dst_coeff, diff_stride, tx_type, bd);
}
static void highbd_fwd_txfm_32x64(const int16_t *src_diff, tran_low_t *coeff,
@ -275,7 +191,8 @@ static void highbd_fwd_txfm_32x64(const int16_t *src_diff, tran_low_t *coeff,
assert(txfm_param->tx_type == DCT_DCT);
int32_t *dst_coeff = (int32_t *)coeff;
const int bd = txfm_param->bd;
av1_fwd_txfm2d_32x64_c(src_diff, dst_coeff, diff_stride, DCT_DCT, bd);
av1_fwd_txfm2d_32x64(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
bd);
}
static void highbd_fwd_txfm_64x32(const int16_t *src_diff, tran_low_t *coeff,
@ -283,7 +200,8 @@ static void highbd_fwd_txfm_64x32(const int16_t *src_diff, tran_low_t *coeff,
assert(txfm_param->tx_type == DCT_DCT);
int32_t *dst_coeff = (int32_t *)coeff;
const int bd = txfm_param->bd;
av1_fwd_txfm2d_64x32_c(src_diff, dst_coeff, diff_stride, DCT_DCT, bd);
av1_fwd_txfm2d_64x32(src_diff, dst_coeff, diff_stride, txfm_param->tx_type,
bd);
}
static void highbd_fwd_txfm_16x64(const int16_t *src_diff, tran_low_t *coeff,
@ -291,7 +209,7 @@ static void highbd_fwd_txfm_16x64(const int16_t *src_diff, tran_low_t *coeff,
assert(txfm_param->tx_type == DCT_DCT);
int32_t *dst_coeff = (int32_t *)coeff;
const int bd = txfm_param->bd;
av1_fwd_txfm2d_16x64_c(src_diff, dst_coeff, diff_stride, DCT_DCT, bd);
av1_fwd_txfm2d_16x64(src_diff, dst_coeff, diff_stride, DCT_DCT, bd);
}
static void highbd_fwd_txfm_64x16(const int16_t *src_diff, tran_low_t *coeff,
@ -299,7 +217,7 @@ static void highbd_fwd_txfm_64x16(const int16_t *src_diff, tran_low_t *coeff,
assert(txfm_param->tx_type == DCT_DCT);
int32_t *dst_coeff = (int32_t *)coeff;
const int bd = txfm_param->bd;
av1_fwd_txfm2d_64x16_c(src_diff, dst_coeff, diff_stride, DCT_DCT, bd);
av1_fwd_txfm2d_64x16(src_diff, dst_coeff, diff_stride, DCT_DCT, bd);
}
static void highbd_fwd_txfm_64x64(const int16_t *src_diff, tran_low_t *coeff,

View file

@ -43,7 +43,8 @@ void av1_lookahead_destroy(struct lookahead_ctx *ctx) {
struct lookahead_ctx *av1_lookahead_init(
unsigned int width, unsigned int height, unsigned int subsampling_x,
unsigned int subsampling_y, int use_highbitdepth, unsigned int depth) {
unsigned int subsampling_y, int use_highbitdepth, unsigned int depth,
const int border_in_pixels) {
struct lookahead_ctx *ctx = NULL;
// Clamp the lookahead queue depth
@ -63,7 +64,7 @@ struct lookahead_ctx *av1_lookahead_init(
for (i = 0; i < depth; i++)
if (aom_alloc_frame_buffer(&ctx->buf[i].img, width, height, subsampling_x,
subsampling_y, use_highbitdepth,
AOM_BORDER_IN_PIXELS, legacy_byte_alignment))
border_in_pixels, legacy_byte_alignment))
goto bail;
}
return ctx;

View file

@ -46,7 +46,8 @@ struct lookahead_ctx {
*/
struct lookahead_ctx *av1_lookahead_init(
unsigned int width, unsigned int height, unsigned int subsampling_x,
unsigned int subsampling_y, int use_highbitdepth, unsigned int depth);
unsigned int subsampling_y, int use_highbitdepth, unsigned int depth,
const int border_in_pixels);
/**\brief Destroys the lookahead stage
*/

View file

@ -60,7 +60,7 @@ static unsigned int do_16x16_motion_iteration(AV1_COMP *cpi, const MV *ref_mv,
x, &cpi->common, mb_row, mb_col, ref_mv,
cpi->common.allow_high_precision_mv, x->errorperbit, &v_fn_ptr, 0,
mv_sf->subpel_iters_per_step, cond_cost_list(cpi, cost_list), NULL,
NULL, &distortion, &sse, NULL, NULL, 0, 0, 0, 0, 0);
NULL, &distortion, &sse, NULL, NULL, 0, 0, 0, 0, 0, 1);
}
if (has_second_ref(xd->mi[0]))
@ -71,8 +71,8 @@ static unsigned int do_16x16_motion_iteration(AV1_COMP *cpi, const MV *ref_mv,
xd->mi[0]->mv[0] = x->best_mv;
xd->mi[0]->ref_frame[1] = NONE_FRAME;
av1_build_inter_predictors_sby(&cpi->common, xd, mb_row, mb_col, NULL,
BLOCK_16X16);
av1_enc_build_inter_predictor(&cpi->common, xd, mb_row, mb_col, NULL,
BLOCK_16X16, AOM_PLANE_Y, AOM_PLANE_Y);
/* restore UMV window */
x->mv_limits = tmp_mv_limits;
@ -179,8 +179,8 @@ static void update_mbgraph_mb_stats(AV1_COMP *cpi, MBGRAPH_MB_STATS *stats,
x->plane[0].src.buf = buf->y_buffer + mb_y_offset;
x->plane[0].src.stride = buf->y_stride;
xd->plane[0].dst.buf = get_frame_new_buffer(cm)->y_buffer + mb_y_offset;
xd->plane[0].dst.stride = get_frame_new_buffer(cm)->y_stride;
xd->plane[0].dst.buf = cm->cur_frame->buf.y_buffer + mb_y_offset;
xd->plane[0].dst.stride = cm->cur_frame->buf.y_stride;
// do intra 16x16 prediction
intra_error = find_best_16x16_intra(cpi, &stats->ref[INTRA_FRAME].m.mode);
@ -364,7 +364,7 @@ static void separate_arf_mbs(AV1_COMP *cpi) {
void av1_update_mbgraph_stats(AV1_COMP *cpi) {
AV1_COMMON *const cm = &cpi->common;
int i, n_frames = av1_lookahead_depth(cpi->lookahead);
YV12_BUFFER_CONFIG *golden_ref = get_ref_frame_buffer(cpi, GOLDEN_FRAME);
YV12_BUFFER_CONFIG *golden_ref = &get_ref_frame_buf(cm, GOLDEN_FRAME)->buf;
assert(golden_ref != NULL);

View file

@ -112,7 +112,7 @@ static int mvsad_err_cost(const MACROBLOCK *x, const MV *mv, const MV *ref,
int sad_per_bit) {
const MV diff = { (mv->row - ref->row) * 8, (mv->col - ref->col) * 8 };
return ROUND_POWER_OF_TWO(
(unsigned)mv_cost(&diff, x->nmvjointcost, x->mvcost) * sad_per_bit,
(unsigned)mv_cost(&diff, x->nmv_vec_cost, x->mv_cost_stack) * sad_per_bit,
AV1_PROB_COST_SHIFT);
}
@ -179,36 +179,31 @@ static INLINE const uint8_t *pre(const uint8_t *buf, int stride, int r, int c) {
}
/* checks if (r, c) has better score than previous best */
#define CHECK_BETTER(v, r, c) \
if (c >= minc && c <= maxc && r >= minr && r <= maxr) { \
MV this_mv = { r, c }; \
v = mv_err_cost(&this_mv, ref_mv, mvjcost, mvcost, error_per_bit); \
if (second_pred == NULL) { \
thismse = vfp->svf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, &sse); \
} else if (mask) { \
thismse = vfp->msvf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, second_pred, mask, \
mask_stride, invert_mask, &sse); \
} else { \
if (xd->jcp_param.use_jnt_comp_avg) \
thismse = vfp->jsvaf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, &sse, second_pred, \
&xd->jcp_param); \
else \
thismse = vfp->svaf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, &sse, second_pred); \
} \
v += thismse; \
if (v < besterr) { \
besterr = v; \
br = r; \
bc = c; \
*distortion = thismse; \
*sse1 = sse; \
} \
} else { \
v = INT_MAX; \
#define CHECK_BETTER(v, r, c) \
if (c >= minc && c <= maxc && r >= minr && r <= maxr) { \
MV this_mv = { r, c }; \
v = mv_err_cost(&this_mv, ref_mv, mvjcost, mvcost, error_per_bit); \
if (second_pred == NULL) { \
thismse = vfp->svf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, &sse); \
} else if (mask) { \
thismse = vfp->msvf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, second_pred, mask, \
mask_stride, invert_mask, &sse); \
} else { \
thismse = vfp->svaf(pre(y, y_stride, r, c), y_stride, sp(c), sp(r), \
src_address, src_stride, &sse, second_pred); \
} \
v += thismse; \
if (v < besterr) { \
besterr = v; \
br = r; \
bc = c; \
*distortion = thismse; \
*sse1 = sse; \
} \
} else { \
v = INT_MAX; \
}
#define CHECK_BETTER0(v, r, c) CHECK_BETTER(v, r, c)
@ -341,19 +336,15 @@ static unsigned int setup_center_error(
int *mvcost[2], unsigned int *sse1, int *distortion) {
unsigned int besterr;
if (second_pred != NULL) {
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
DECLARE_ALIGNED(16, uint16_t, comp_pred16[MAX_SB_SQUARE]);
uint8_t *comp_pred = CONVERT_TO_BYTEPTR(comp_pred16);
if (mask) {
aom_highbd_comp_mask_pred(comp_pred, second_pred, w, h, y + offset,
y_stride, mask, mask_stride, invert_mask);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
aom_highbd_jnt_comp_avg_pred(comp_pred, second_pred, w, h, y + offset,
y_stride, &xd->jcp_param);
else
aom_highbd_comp_avg_pred(comp_pred, second_pred, w, h, y + offset,
y_stride);
aom_highbd_comp_avg_pred(comp_pred, second_pred, w, h, y + offset,
y_stride);
}
besterr = vfp->vf(comp_pred, w, src, src_stride, sse1);
} else {
@ -362,11 +353,7 @@ static unsigned int setup_center_error(
aom_comp_mask_pred(comp_pred, second_pred, w, h, y + offset, y_stride,
mask, mask_stride, invert_mask);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
aom_jnt_comp_avg_pred(comp_pred, second_pred, w, h, y + offset,
y_stride, &xd->jcp_param);
else
aom_comp_avg_pred(comp_pred, second_pred, w, h, y + offset, y_stride);
aom_comp_avg_pred(comp_pred, second_pred, w, h, y + offset, y_stride);
}
besterr = vfp->vf(comp_pred, w, src, src_stride, sse1);
}
@ -409,7 +396,7 @@ int av1_find_best_sub_pixel_tree_pruned_evenmore(
int *cost_list, int *mvjcost, int *mvcost[2], int *distortion,
unsigned int *sse1, const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search) {
int use_accurate_subpel_search, const int do_reset_fractional_mv) {
SETUP_SUBPEL_SEARCH;
besterr = setup_center_error(xd, bestmv, ref_mv, error_per_bit, vfp,
src_address, src_stride, y, y_stride,
@ -426,6 +413,7 @@ int av1_find_best_sub_pixel_tree_pruned_evenmore(
(void)cm;
(void)mi_row;
(void)mi_col;
(void)do_reset_fractional_mv;
if (cost_list && cost_list[0] != INT_MAX && cost_list[1] != INT_MAX &&
cost_list[2] != INT_MAX && cost_list[3] != INT_MAX &&
@ -481,12 +469,13 @@ int av1_find_best_sub_pixel_tree_pruned_more(
int *cost_list, int *mvjcost, int *mvcost[2], int *distortion,
unsigned int *sse1, const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search) {
int use_accurate_subpel_search, const int do_reset_fractional_mv) {
SETUP_SUBPEL_SEARCH;
(void)use_accurate_subpel_search;
(void)cm;
(void)mi_row;
(void)mi_col;
(void)do_reset_fractional_mv;
besterr = setup_center_error(xd, bestmv, ref_mv, error_per_bit, vfp,
src_address, src_stride, y, y_stride,
@ -549,12 +538,13 @@ int av1_find_best_sub_pixel_tree_pruned(
int *cost_list, int *mvjcost, int *mvcost[2], int *distortion,
unsigned int *sse1, const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search) {
int use_accurate_subpel_search, const int do_reset_fractional_mv) {
SETUP_SUBPEL_SEARCH;
(void)use_accurate_subpel_search;
(void)cm;
(void)mi_row;
(void)mi_col;
(void)do_reset_fractional_mv;
besterr = setup_center_error(xd, bestmv, ref_mv, error_per_bit, vfp,
src_address, src_stride, y, y_stride,
@ -651,7 +641,7 @@ static int upsampled_pref_error(MACROBLOCKD *xd, const AV1_COMMON *const cm,
int mask_stride, int invert_mask, int w, int h,
unsigned int *sse, int subpel_search) {
unsigned int besterr;
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
DECLARE_ALIGNED(16, uint16_t, pred16[MAX_SB_SQUARE]);
uint8_t *pred8 = CONVERT_TO_BYTEPTR(pred16);
if (second_pred != NULL) {
@ -661,14 +651,9 @@ static int upsampled_pref_error(MACROBLOCKD *xd, const AV1_COMMON *const cm,
subpel_y_q3, y, y_stride, mask, mask_stride, invert_mask, xd->bd,
subpel_search);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
aom_highbd_jnt_comp_avg_upsampled_pred(
xd, cm, mi_row, mi_col, mv, pred8, second_pred, w, h, subpel_x_q3,
subpel_y_q3, y, y_stride, xd->bd, &xd->jcp_param, subpel_search);
else
aom_highbd_comp_avg_upsampled_pred(
xd, cm, mi_row, mi_col, mv, pred8, second_pred, w, h, subpel_x_q3,
subpel_y_q3, y, y_stride, xd->bd, subpel_search);
aom_highbd_comp_avg_upsampled_pred(
xd, cm, mi_row, mi_col, mv, pred8, second_pred, w, h, subpel_x_q3,
subpel_y_q3, y, y_stride, xd->bd, subpel_search);
}
} else {
aom_highbd_upsampled_pred(xd, cm, mi_row, mi_col, mv, pred8, w, h,
@ -685,14 +670,9 @@ static int upsampled_pref_error(MACROBLOCKD *xd, const AV1_COMMON *const cm,
subpel_y_q3, y, y_stride, mask,
mask_stride, invert_mask, subpel_search);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
aom_jnt_comp_avg_upsampled_pred(
xd, cm, mi_row, mi_col, mv, pred, second_pred, w, h, subpel_x_q3,
subpel_y_q3, y, y_stride, &xd->jcp_param, subpel_search);
else
aom_comp_avg_upsampled_pred(xd, cm, mi_row, mi_col, mv, pred,
second_pred, w, h, subpel_x_q3,
subpel_y_q3, y, y_stride, subpel_search);
aom_comp_avg_upsampled_pred(xd, cm, mi_row, mi_col, mv, pred,
second_pred, w, h, subpel_x_q3, subpel_y_q3,
y, y_stride, subpel_search);
}
} else {
aom_upsampled_pred(xd, cm, mi_row, mi_col, mv, pred, w, h, subpel_x_q3,
@ -723,7 +703,7 @@ static unsigned int upsampled_setup_center_error(
// when use_accurate_subpel_search == 0
static INLINE unsigned int estimate_upsampled_pref_error(
MACROBLOCKD *xd, const aom_variance_fn_ptr_t *vfp, const uint8_t *const src,
const aom_variance_fn_ptr_t *vfp, const uint8_t *const src,
const int src_stride, const uint8_t *const pre, int y_stride,
int subpel_x_q3, int subpel_y_q3, const uint8_t *second_pred,
const uint8_t *mask, int mask_stride, int invert_mask, unsigned int *sse) {
@ -734,12 +714,8 @@ static INLINE unsigned int estimate_upsampled_pref_error(
return vfp->msvf(pre, y_stride, subpel_x_q3, subpel_y_q3, src, src_stride,
second_pred, mask, mask_stride, invert_mask, sse);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
return vfp->jsvaf(pre, y_stride, subpel_x_q3, subpel_y_q3, src,
src_stride, sse, second_pred, &xd->jcp_param);
else
return vfp->svaf(pre, y_stride, subpel_x_q3, subpel_y_q3, src, src_stride,
sse, second_pred);
return vfp->svaf(pre, y_stride, subpel_x_q3, subpel_y_q3, src, src_stride,
sse, second_pred);
}
}
@ -750,7 +726,7 @@ int av1_find_best_sub_pixel_tree(
int *cost_list, int *mvjcost, int *mvcost[2], int *distortion,
unsigned int *sse1, const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search) {
int use_accurate_subpel_search, const int do_reset_fractional_mv) {
const uint8_t *const src_address = x->plane[0].src.buf;
const int src_stride = x->plane[0].src.stride;
MACROBLOCKD *xd = &x->e_mbd;
@ -796,7 +772,16 @@ int av1_find_best_sub_pixel_tree(
(void)cost_list; // to silence compiler warning
if (do_reset_fractional_mv) {
av1_set_fractional_mv(x->fractional_best_mv);
}
for (iter = 0; iter < round; ++iter) {
if ((x->fractional_best_mv[iter].as_mv.row == br) &&
(x->fractional_best_mv[iter].as_mv.col == bc))
return INT_MAX;
x->fractional_best_mv[iter].as_mv.row = br;
x->fractional_best_mv[iter].as_mv.col = bc;
// Check vertical and horizontal sub-pixel positions.
for (idx = 0; idx < 4; ++idx) {
tr = br + search_step[idx].row;
@ -812,9 +797,9 @@ int av1_find_best_sub_pixel_tree(
use_accurate_subpel_search);
} else {
thismse = estimate_upsampled_pref_error(
xd, vfp, src_address, src_stride, pre(y, y_stride, tr, tc),
y_stride, sp(tc), sp(tr), second_pred, mask, mask_stride,
invert_mask, &sse);
vfp, src_address, src_stride, pre(y, y_stride, tr, tc), y_stride,
sp(tc), sp(tr), second_pred, mask, mask_stride, invert_mask,
&sse);
}
cost_array[idx] = thismse + mv_err_cost(&this_mv, ref_mv, mvjcost,
@ -848,9 +833,8 @@ int av1_find_best_sub_pixel_tree(
use_accurate_subpel_search);
} else {
thismse = estimate_upsampled_pref_error(
xd, vfp, src_address, src_stride, pre(y, y_stride, tr, tc),
y_stride, sp(tc), sp(tr), second_pred, mask, mask_stride,
invert_mask, &sse);
vfp, src_address, src_stride, pre(y, y_stride, tr, tc), y_stride,
sp(tc), sp(tr), second_pred, mask, mask_stride, invert_mask, &sse);
}
cost_array[4] = thismse + mv_err_cost(&this_mv, ref_mv, mvjcost, mvcost,
@ -915,9 +899,10 @@ unsigned int av1_compute_motion_cost(const AV1_COMP *cpi, MACROBLOCK *const x,
unsigned int mse;
unsigned int sse;
av1_build_inter_predictors_sby(cm, xd, mi_row, mi_col, NULL, bsize);
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, NULL, bsize,
AOM_PLANE_Y, AOM_PLANE_Y);
mse = vfp->vf(dst, dst_stride, src, src_stride, &sse);
mse += mv_err_cost(this_mv, &ref_mv.as_mv, x->nmvjointcost, x->mvcost,
mse += mv_err_cost(this_mv, &ref_mv.as_mv, x->nmv_vec_cost, x->mv_cost_stack,
x->errorperbit);
return mse;
}
@ -1057,8 +1042,8 @@ static INLINE void calc_int_cost_list(const MACROBLOCK *x,
cost_list[i + 1] = fn_ptr->vf(what->buf, what->stride,
get_buf_from_mv(in_what, &neighbor_mv),
in_what->stride, &sse) +
mv_err_cost(&neighbor_mv, &fcenter_mv, x->nmvjointcost,
x->mvcost, x->errorperbit);
mv_err_cost(&neighbor_mv, &fcenter_mv, x->nmv_vec_cost,
x->mv_cost_stack, x->errorperbit);
}
} else {
for (i = 0; i < 4; i++) {
@ -1070,8 +1055,8 @@ static INLINE void calc_int_cost_list(const MACROBLOCK *x,
fn_ptr->vf(what->buf, what->stride,
get_buf_from_mv(in_what, &neighbor_mv), in_what->stride,
&sse) +
mv_err_cost(&neighbor_mv, &fcenter_mv, x->nmvjointcost, x->mvcost,
x->errorperbit);
mv_err_cost(&neighbor_mv, &fcenter_mv, x->nmv_vec_cost,
x->mv_cost_stack, x->errorperbit);
}
}
}
@ -1393,8 +1378,8 @@ int av1_get_mvpred_var(const MACROBLOCK *x, const MV *best_mv,
return vfp->vf(what->buf, what->stride, get_buf_from_mv(in_what, best_mv),
in_what->stride, &unused) +
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost,
x->errorperbit)
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmv_vec_cost,
x->mv_cost_stack, x->errorperbit)
: 0);
}
@ -1407,19 +1392,11 @@ int av1_get_mvpred_av_var(const MACROBLOCK *x, const MV *best_mv,
const MV mv = { best_mv->row * 8, best_mv->col * 8 };
unsigned int unused;
if (xd->jcp_param.use_jnt_comp_avg)
return vfp->jsvaf(get_buf_from_mv(in_what, best_mv), in_what->stride, 0, 0,
what->buf, what->stride, &unused, second_pred,
&xd->jcp_param) +
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost,
x->errorperbit)
: 0);
else
return vfp->svaf(get_buf_from_mv(in_what, best_mv), in_what->stride, 0, 0,
what->buf, what->stride, &unused, second_pred) +
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost,
x->errorperbit)
: 0);
return vfp->svaf(get_buf_from_mv(in_what, best_mv), in_what->stride, 0, 0,
what->buf, what->stride, &unused, second_pred) +
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmv_vec_cost,
x->mv_cost_stack, x->errorperbit)
: 0);
}
int av1_get_mvpred_mask_var(const MACROBLOCK *x, const MV *best_mv,
@ -1436,8 +1413,8 @@ int av1_get_mvpred_mask_var(const MACROBLOCK *x, const MV *best_mv,
return vfp->msvf(what->buf, what->stride, 0, 0,
get_buf_from_mv(in_what, best_mv), in_what->stride,
second_pred, mask, mask_stride, invert_mask, &unused) +
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost,
x->errorperbit)
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmv_vec_cost,
x->mv_cost_stack, x->errorperbit)
: 0);
}
@ -1897,7 +1874,7 @@ static int full_pixel_exhaustive(const AV1_COMP *const cpi, MACROBLOCK *x,
int baseline_interval_divisor;
// Keep track of number of exhaustive calls (this frame in this thread).
++(*x->ex_search_count_ptr);
if (x->ex_search_count_ptr != NULL) ++(*x->ex_search_count_ptr);
// Trap illegal values for interval and range for this function.
if ((range < MIN_RANGE) || (range > MAX_RANGE) || (interval < MIN_INTERVAL) ||
@ -2051,16 +2028,10 @@ int av1_refining_search_8p_c(MACROBLOCK *x, int error_per_bit, int search_range,
second_pred, mask, mask_stride, invert_mask) +
mvsad_err_cost(x, best_mv, &fcenter_mv, error_per_bit);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
best_sad = fn_ptr->jsdaf(what->buf, what->stride,
get_buf_from_mv(in_what, best_mv),
in_what->stride, second_pred, &xd->jcp_param) +
mvsad_err_cost(x, best_mv, &fcenter_mv, error_per_bit);
else
best_sad = fn_ptr->sdaf(what->buf, what->stride,
get_buf_from_mv(in_what, best_mv),
in_what->stride, second_pred) +
mvsad_err_cost(x, best_mv, &fcenter_mv, error_per_bit);
best_sad =
fn_ptr->sdaf(what->buf, what->stride, get_buf_from_mv(in_what, best_mv),
in_what->stride, second_pred) +
mvsad_err_cost(x, best_mv, &fcenter_mv, error_per_bit);
}
do_refine_search_grid[grid_coord] = 1;
@ -2084,14 +2055,9 @@ int av1_refining_search_8p_c(MACROBLOCK *x, int error_per_bit, int search_range,
get_buf_from_mv(in_what, &mv), in_what->stride,
second_pred, mask, mask_stride, invert_mask);
} else {
if (xd->jcp_param.use_jnt_comp_avg)
sad = fn_ptr->jsdaf(what->buf, what->stride,
get_buf_from_mv(in_what, &mv), in_what->stride,
second_pred, &xd->jcp_param);
else
sad = fn_ptr->sdaf(what->buf, what->stride,
get_buf_from_mv(in_what, &mv), in_what->stride,
second_pred);
sad = fn_ptr->sdaf(what->buf, what->stride,
get_buf_from_mv(in_what, &mv), in_what->stride,
second_pred);
}
if (sad < best_sad) {
sad += mvsad_err_cost(x, &mv, &fcenter_mv, error_per_bit);
@ -2117,13 +2083,16 @@ int av1_refining_search_8p_c(MACROBLOCK *x, int error_per_bit, int search_range,
#define MIN_EX_SEARCH_LIMIT 128
static int is_exhaustive_allowed(const AV1_COMP *const cpi, MACROBLOCK *x) {
const SPEED_FEATURES *const sf = &cpi->sf;
const int max_ex =
AOMMAX(MIN_EX_SEARCH_LIMIT,
(*x->m_search_count_ptr * sf->max_exaustive_pct) / 100);
return sf->allow_exhaustive_searches &&
(sf->exhaustive_searches_thresh < INT_MAX) &&
(*x->ex_search_count_ptr <= max_ex) && !cpi->rc.is_src_frame_alt_ref;
int is_allowed = sf->allow_exhaustive_searches &&
(sf->exhaustive_searches_thresh < INT_MAX) &&
!cpi->rc.is_src_frame_alt_ref;
if (x->m_search_count_ptr != NULL && x->ex_search_count_ptr != NULL) {
const int max_ex =
AOMMAX(MIN_EX_SEARCH_LIMIT,
(*x->m_search_count_ptr * sf->max_exaustive_pct) / 100);
is_allowed = *x->ex_search_count_ptr <= max_ex && is_allowed;
}
return is_allowed;
}
int av1_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
@ -2144,7 +2113,7 @@ int av1_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
}
// Keep track of number of searches (this frame in this thread).
++(*x->m_search_count_ptr);
if (x->m_search_count_ptr != NULL) ++(*x->m_search_count_ptr);
switch (method) {
case FAST_DIAMOND:
@ -2241,13 +2210,12 @@ int av1_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
// for the hashMap
hash_table *ref_frame_hash =
intra
? &cpi->common.cur_frame->hash_table
: av1_get_ref_frame_hash_map(cpi, x->e_mbd.mi[0]->ref_frame[0]);
intra ? &cpi->common.cur_frame->hash_table
: av1_get_ref_frame_hash_map(&cpi->common,
x->e_mbd.mi[0]->ref_frame[0]);
av1_get_block_hash_value(
what, what_stride, block_width, &hash_value1, &hash_value2,
x->e_mbd.cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, x);
av1_get_block_hash_value(what, what_stride, block_width, &hash_value1,
&hash_value2, is_cur_buf_hbd(&x->e_mbd), x);
const int count = av1_hash_table_count(ref_frame_hash, hash_value1);
// for intra, at lest one matching can be found, itself.
@ -2333,8 +2301,9 @@ int av1_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
mask, vfp, z, pre(y, y_stride, r, c), \
y_stride, sp(c), sp(r), w, h, &sse, \
use_accurate_subpel_search); \
if ((v = MVC(r, c) + thismse) < besterr) { \
besterr = v; \
v = mv_err_cost(&this_mv, ref_mv, mvjcost, mvcost, error_per_bit); \
if ((v + thismse) < besterr) { \
besterr = v + thismse; \
br = r; \
bc = c; \
*distortion = thismse; \
@ -2365,7 +2334,7 @@ static int upsampled_obmc_pref_error(
unsigned int besterr;
DECLARE_ALIGNED(16, uint8_t, pred[2 * MAX_SB_SQUARE]);
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
uint8_t *pred8 = CONVERT_TO_BYTEPTR(pred);
aom_highbd_upsampled_pred(xd, cm, mi_row, mi_col, mv, pred8, w, h,
subpel_x_q3, subpel_y_q3, y, y_stride, xd->bd,
@ -2568,16 +2537,16 @@ static int get_obmc_mvpred_var(const MACROBLOCK *x, const int32_t *wsrc,
return vfp->ovf(get_buf_from_mv(in_what, best_mv), in_what->stride, wsrc,
mask, &unused) +
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost,
x->errorperbit)
(use_mvcost ? mv_err_cost(&mv, center_mv, x->nmv_vec_cost,
x->mv_cost_stack, x->errorperbit)
: 0);
}
int obmc_refining_search_sad(const MACROBLOCK *x, const int32_t *wsrc,
const int32_t *mask, MV *ref_mv, int error_per_bit,
int search_range,
const aom_variance_fn_ptr_t *fn_ptr,
const MV *center_mv, int is_second) {
static int obmc_refining_search_sad(const MACROBLOCK *x, const int32_t *wsrc,
const int32_t *mask, MV *ref_mv,
int error_per_bit, int search_range,
const aom_variance_fn_ptr_t *fn_ptr,
const MV *center_mv, int is_second) {
const MV neighbors[4] = { { -1, 0 }, { 0, -1 }, { 0, 1 }, { 1, 0 } };
const MACROBLOCKD *const xd = &x->e_mbd;
const struct buf_2d *const in_what = &xd->plane[0].pre[is_second];
@ -2616,12 +2585,13 @@ int obmc_refining_search_sad(const MACROBLOCK *x, const int32_t *wsrc,
return best_sad;
}
int obmc_diamond_search_sad(const MACROBLOCK *x, const search_site_config *cfg,
const int32_t *wsrc, const int32_t *mask,
MV *ref_mv, MV *best_mv, int search_param,
int sad_per_bit, int *num00,
const aom_variance_fn_ptr_t *fn_ptr,
const MV *center_mv, int is_second) {
static int obmc_diamond_search_sad(const MACROBLOCK *x,
const search_site_config *cfg,
const int32_t *wsrc, const int32_t *mask,
MV *ref_mv, MV *best_mv, int search_param,
int sad_per_bit, int *num00,
const aom_variance_fn_ptr_t *fn_ptr,
const MV *center_mv, int is_second) {
const MACROBLOCKD *const xd = &x->e_mbd;
const struct buf_2d *const in_what = &xd->plane[0].pre[is_second];
// search_param determines the length of the initial step and hence the number
@ -2824,16 +2794,14 @@ int av1_obmc_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, MV *mvp_full,
(void)thismse; \
(void)cost_list;
// Return the maximum MV.
int av1_return_max_sub_pixel_mv(MACROBLOCK *x, const AV1_COMMON *const cm,
int mi_row, int mi_col, const MV *ref_mv,
int allow_hp, int error_per_bit,
const aom_variance_fn_ptr_t *vfp,
int forced_stop, int iters_per_step,
int *cost_list, int *mvjcost, int *mvcost[2],
int *distortion, unsigned int *sse1,
const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search) {
int av1_return_max_sub_pixel_mv(
MACROBLOCK *x, const AV1_COMMON *const cm, int mi_row, int mi_col,
const MV *ref_mv, int allow_hp, int error_per_bit,
const aom_variance_fn_ptr_t *vfp, int forced_stop, int iters_per_step,
int *cost_list, int *mvjcost, int *mvcost[2], int *distortion,
unsigned int *sse1, const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search, const int do_reset_fractional_mv) {
COMMON_MV_TEST;
(void)mask;
(void)mask_stride;
@ -2844,6 +2812,7 @@ int av1_return_max_sub_pixel_mv(MACROBLOCK *x, const AV1_COMMON *const cm,
(void)cm;
(void)mi_row;
(void)mi_col;
(void)do_reset_fractional_mv;
bestmv->row = maxr;
bestmv->col = maxc;
@ -2854,16 +2823,14 @@ int av1_return_max_sub_pixel_mv(MACROBLOCK *x, const AV1_COMMON *const cm,
return besterr;
}
// Return the minimum MV.
int av1_return_min_sub_pixel_mv(MACROBLOCK *x, const AV1_COMMON *const cm,
int mi_row, int mi_col, const MV *ref_mv,
int allow_hp, int error_per_bit,
const aom_variance_fn_ptr_t *vfp,
int forced_stop, int iters_per_step,
int *cost_list, int *mvjcost, int *mvcost[2],
int *distortion, unsigned int *sse1,
const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search) {
int av1_return_min_sub_pixel_mv(
MACROBLOCK *x, const AV1_COMMON *const cm, int mi_row, int mi_col,
const MV *ref_mv, int allow_hp, int error_per_bit,
const aom_variance_fn_ptr_t *vfp, int forced_stop, int iters_per_step,
int *cost_list, int *mvjcost, int *mvcost[2], int *distortion,
unsigned int *sse1, const uint8_t *second_pred, const uint8_t *mask,
int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search, const int do_reset_fractional_mv) {
COMMON_MV_TEST;
(void)maxr;
(void)maxc;
@ -2874,6 +2841,7 @@ int av1_return_min_sub_pixel_mv(MACROBLOCK *x, const AV1_COMMON *const cm,
(void)cm;
(void)mi_row;
(void)mi_col;
(void)do_reset_fractional_mv;
bestmv->row = minr;
bestmv->col = minc;

View file

@ -103,7 +103,7 @@ typedef int(fractional_mv_step_fp)(
int iters_per_step, int *cost_list, int *mvjcost, int *mvcost[2],
int *distortion, unsigned int *sse1, const uint8_t *second_pred,
const uint8_t *mask, int mask_stride, int invert_mask, int w, int h,
int use_accurate_subpel_search);
int use_accurate_subpel_search, const int do_reset_fractional_mv);
extern fractional_mv_step_fp av1_find_best_sub_pixel_tree;
extern fractional_mv_step_fp av1_find_best_sub_pixel_tree_pruned;
@ -154,6 +154,12 @@ unsigned int av1_refine_warped_mv(const struct AV1_COMP *cpi,
int mi_row, int mi_col, int *pts0,
int *pts_inref0, int total_samples);
static INLINE void av1_set_fractional_mv(int_mv *fractional_best_mv) {
for (int z = 0; z < 3; z++) {
fractional_best_mv[z].as_int = INVALID_MV;
}
}
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -267,6 +267,7 @@ static void temporal_filter_apply_16size_msa(uint8_t *frm1_ptr, uint32_t stride,
}
}
// TODO(yunqing) The following optimization is not used since c code changes.
void av1_temporal_filter_apply_msa(uint8_t *frame1_ptr, uint32_t stride,
uint8_t *frame2_ptr, uint32_t blk_w,
uint32_t blk_h, int32_t strength,

View file

@ -15,31 +15,31 @@
#include "aom_dsp/aom_dsp_common.h"
#include "av1/encoder/ml.h"
void av1_nn_predict(const float *features, const NN_CONFIG *nn_config,
float *output) {
// Calculate prediction based on the given input features and neural net config.
// Assume there are no more than NN_MAX_NODES_PER_LAYER nodes in each hidden
// layer.
void av1_nn_predict_c(const float *input_nodes,
const NN_CONFIG *const nn_config, float *const output) {
int num_input_nodes = nn_config->num_inputs;
int buf_index = 0;
float buf[2][NN_MAX_NODES_PER_LAYER];
const float *input_nodes = features;
// Propagate hidden layers.
const int num_layers = nn_config->num_hidden_layers;
assert(num_layers <= NN_MAX_HIDDEN_LAYERS);
for (int layer = 0; layer < num_layers; ++layer) {
const float *weights = nn_config->weights[layer];
const float *bias = nn_config->bias[layer];
const float *layer_weights = nn_config->weights[layer];
const float *layer_bias = nn_config->bias[layer];
float *output_nodes = buf[buf_index];
const int num_output_nodes = nn_config->num_hidden_nodes[layer];
assert(num_output_nodes < NN_MAX_NODES_PER_LAYER);
for (int node = 0; node < num_output_nodes; ++node) {
float val = 0.0f;
float val = layer_bias[node];
for (int i = 0; i < num_input_nodes; ++i)
val += weights[i] * input_nodes[i];
val += bias[node];
val += layer_weights[node * num_input_nodes + i] * input_nodes[i];
// ReLU as activation function.
val = val > 0.0f ? val : 0.0f; // Could use AOMMAX().
output_nodes[node] = val;
weights += num_input_nodes;
}
num_input_nodes = num_output_nodes;
input_nodes = output_nodes;
@ -47,14 +47,13 @@ void av1_nn_predict(const float *features, const NN_CONFIG *nn_config,
}
// Final output layer.
const float *weights = nn_config->weights[num_layers];
const float *layer_weights = nn_config->weights[num_layers];
const float *layer_bias = nn_config->bias[num_layers];
for (int node = 0; node < nn_config->num_outputs; ++node) {
const float *bias = nn_config->bias[num_layers];
float val = 0.0f;
float val = layer_bias[node];
for (int i = 0; i < num_input_nodes; ++i)
val += weights[i] * input_nodes[i];
output[node] = val + bias[node];
weights += num_input_nodes;
val += layer_weights[node * num_input_nodes + i] * input_nodes[i];
output[node] = val;
}
}
@ -66,7 +65,9 @@ void av1_nn_softmax(const float *input, float *output, int n) {
for (int i = 1; i < n; i++) max_inp = AOMMAX(max_inp, input[i]);
float sum_out = 0.0f;
for (int i = 0; i < n; i++) {
output[i] = (float)exp(input[i] - max_inp);
// Clamp to range [-10.0, 0.0] to prevent FE_UNDERFLOW errors.
const float normalized_input = AOMMAX(input[i] - max_inp, -10.0f);
output[i] = (float)exp(normalized_input);
sum_out += output[i];
}
for (int i = 0; i < n; i++) output[i] /= sum_out;

View file

@ -16,10 +16,12 @@
extern "C" {
#endif
#include "config/av1_rtcd.h"
#define NN_MAX_HIDDEN_LAYERS 10
#define NN_MAX_NODES_PER_LAYER 128
typedef struct {
struct NN_CONFIG {
int num_inputs; // Number of input nodes, i.e. features.
int num_outputs; // Number of output nodes.
int num_hidden_layers; // Number of hidden layers, maximum 10.
@ -29,13 +31,8 @@ typedef struct {
const float *weights[NN_MAX_HIDDEN_LAYERS + 1];
// Bias parameters, indexed by layer.
const float *bias[NN_MAX_HIDDEN_LAYERS + 1];
} NN_CONFIG;
// Calculate prediction based on the given input features and neural net config.
// Assume there are no more than NN_MAX_NODES_PER_LAYER nodes in each hidden
// layer.
void av1_nn_predict(const float *features, const NN_CONFIG *nn_config,
float *output);
};
// Typedef from struct NN_CONFIG to NN_CONFIG is in rtcd_defs
// Applies the softmax normalization function to the input
// to get a valid probability distribution in the output:

File diff suppressed because it is too large Load diff

View file

@ -281,6 +281,7 @@ uint64_t compute_cdef_dist(uint16_t *dst, int dstride, uint16_t *src,
void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
AV1_COMMON *cm, MACROBLOCKD *xd, int fast) {
CdefInfo *const cdef_info = &cm->cdef_info;
int r, c;
int fbr, fbc;
uint16_t *src[3];
@ -475,23 +476,23 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
best_tot_mse = tot_mse;
nb_strength_bits = i;
for (j = 0; j < 1 << nb_strength_bits; j++) {
cm->cdef_strengths[j] = best_lev0[j];
cm->cdef_uv_strengths[j] = best_lev1[j];
cdef_info->cdef_strengths[j] = best_lev0[j];
cdef_info->cdef_uv_strengths[j] = best_lev1[j];
}
}
}
nb_strengths = 1 << nb_strength_bits;
cm->cdef_bits = nb_strength_bits;
cm->nb_cdef_strengths = nb_strengths;
cdef_info->cdef_bits = nb_strength_bits;
cdef_info->nb_cdef_strengths = nb_strengths;
for (i = 0; i < sb_count; i++) {
int gi;
int best_gi;
uint64_t best_mse = (uint64_t)1 << 63;
best_gi = 0;
for (gi = 0; gi < cm->nb_cdef_strengths; gi++) {
uint64_t curr = mse[0][i][cm->cdef_strengths[gi]];
if (num_planes >= 3) curr += mse[1][i][cm->cdef_uv_strengths[gi]];
for (gi = 0; gi < cdef_info->nb_cdef_strengths; gi++) {
uint64_t curr = mse[0][i][cdef_info->cdef_strengths[gi]];
if (num_planes >= 3) curr += mse[1][i][cdef_info->cdef_uv_strengths[gi]];
if (curr < best_mse) {
best_gi = gi;
best_mse = curr;
@ -503,18 +504,18 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
if (fast) {
for (int j = 0; j < nb_strengths; j++) {
cm->cdef_strengths[j] =
priconv[cm->cdef_strengths[j] / CDEF_SEC_STRENGTHS] *
cdef_info->cdef_strengths[j] =
priconv[cm->cdef_info.cdef_strengths[j] / CDEF_SEC_STRENGTHS] *
CDEF_SEC_STRENGTHS +
(cm->cdef_strengths[j] % CDEF_SEC_STRENGTHS);
cm->cdef_uv_strengths[j] =
priconv[cm->cdef_uv_strengths[j] / CDEF_SEC_STRENGTHS] *
(cdef_info->cdef_strengths[j] % CDEF_SEC_STRENGTHS);
cdef_info->cdef_uv_strengths[j] =
priconv[cdef_info->cdef_uv_strengths[j] / CDEF_SEC_STRENGTHS] *
CDEF_SEC_STRENGTHS +
(cm->cdef_uv_strengths[j] % CDEF_SEC_STRENGTHS);
(cdef_info->cdef_uv_strengths[j] % CDEF_SEC_STRENGTHS);
}
}
cm->cdef_pri_damping = pri_damping;
cm->cdef_sec_damping = sec_damping;
cdef_info->cdef_pri_damping = pri_damping;
cdef_info->cdef_sec_damping = sec_damping;
aom_free(mse[0]);
aom_free(mse[1]);
for (pli = 0; pli < num_planes; pli++) {

View file

@ -67,26 +67,27 @@ static int64_t try_filter_frame(const YV12_BUFFER_CONFIG *sd,
case 2: cm->lf.filter_level_v = filter_level[0]; break;
}
// TODO(any): please enable multi-thread and remove the flag when loop
// filter mask is compatible with multi-thread.
#if LOOP_FILTER_BITMASK
av1_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, 0, plane,
plane + 1, partial_frame);
#else
// TODO(any): please enable multi-thread and remove the flag when loop
// filter mask is compatible with multi-thread.
if (cpi->num_workers > 1)
av1_loop_filter_frame_mt(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, plane,
plane + 1, partial_frame, cpi->workers,
cpi->num_workers, &cpi->lf_row_sync);
else
av1_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, plane,
plane + 1, partial_frame);
av1_loop_filter_frame_mt(&cm->cur_frame->buf, cm, &cpi->td.mb.e_mbd, plane,
plane + 1, partial_frame,
#if LOOP_FILTER_BITMASK
0,
#endif
cpi->workers, cpi->num_workers, &cpi->lf_row_sync);
else
av1_loop_filter_frame(&cm->cur_frame->buf, cm, &cpi->td.mb.e_mbd,
#if LOOP_FILTER_BITMASK
0,
#endif
plane, plane + 1, partial_frame);
filt_err = aom_get_sse_plane(sd, cm->frame_to_show, plane,
filt_err = aom_get_sse_plane(sd, &cm->cur_frame->buf, plane,
cm->seq_params.use_highbitdepth);
// Re-instate the unfiltered frame
yv12_copy_plane(&cpi->last_frame_uf, cm->frame_to_show, plane);
yv12_copy_plane(&cpi->last_frame_uf, &cm->cur_frame->buf, plane);
return filt_err;
}
@ -107,7 +108,17 @@ static int search_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
// range.
int lvl;
switch (plane) {
case 0: lvl = last_frame_filter_level[dir]; break;
case 0:
switch (dir) {
case 2:
lvl = (last_frame_filter_level[0] + last_frame_filter_level[1] + 1) >>
1;
break;
case 0:
case 1: lvl = last_frame_filter_level[dir]; break;
default: assert(dir >= 0 && dir <= 2); return 0;
}
break;
case 1: lvl = last_frame_filter_level[2]; break;
case 2: lvl = last_frame_filter_level[3]; break;
default: assert(plane >= 0 && plane <= 2); return 0;
@ -119,7 +130,7 @@ static int search_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
// Set each entry to -1
memset(ss_err, 0xFF, sizeof(ss_err));
yv12_copy_plane(cm->frame_to_show, &cpi->last_frame_uf, plane);
yv12_copy_plane(&cm->cur_frame->buf, &cpi->last_frame_uf, plane);
best_err = try_filter_frame(sd, cpi, filt_mid, partial_frame, plane, dir);
filt_best = filt_mid;
ss_err[filt_mid] = best_err;
@ -212,7 +223,7 @@ void av1_pick_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
int filt_guess;
switch (cm->seq_params.bit_depth) {
case AOM_BITS_8:
filt_guess = (cm->frame_type == KEY_FRAME)
filt_guess = (cm->current_frame.frame_type == KEY_FRAME)
? ROUND_POWER_OF_TWO(q * 17563 - 421574, 18)
: ROUND_POWER_OF_TWO(q * 6017 + 650707, 18);
break;
@ -228,7 +239,8 @@ void av1_pick_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
"or AOM_BITS_12");
return;
}
if (cm->seq_params.bit_depth != AOM_BITS_8 && cm->frame_type == KEY_FRAME)
if (cm->seq_params.bit_depth != AOM_BITS_8 &&
cm->current_frame.frame_type == KEY_FRAME)
filt_guess -= 4;
// TODO(chengchen): retrain the model for Y, U, V filter levels
lf->filter_level[0] = clamp(filt_guess, min_filter_level, max_filter_level);

View file

@ -43,6 +43,9 @@ static const RestorationType force_restore_type = RESTORE_TYPES;
// Penalty factor for use of dual sgr
#define DUAL_SGR_PENALTY_MULT 0.01
// Working precision for Wiener filter coefficients
#define WIENER_TAP_SCALE_FACTOR ((int64_t)1 << 16)
const int frame_level_restore_bits[RESTORE_TYPES] = { 2, 2, 2, 2 };
typedef int64_t (*sse_extractor_type)(const YV12_BUFFER_CONFIG *a,
@ -113,15 +116,11 @@ typedef struct {
AV1PixelRect tile_rect;
} RestSearchCtxt;
static void rsc_on_tile(int tile_row, int tile_col, void *priv) {
(void)tile_col;
static void rsc_on_tile(void *priv) {
RestSearchCtxt *rsc = (RestSearchCtxt *)priv;
set_default_sgrproj(&rsc->sgrproj);
set_default_wiener(&rsc->wiener);
rsc->tile_stripe0 =
(tile_row == 0) ? 0 : rsc->cm->rst_end_stripe[tile_row - 1];
rsc->tile_stripe0 = 0;
}
static void reset_rsc(RestSearchCtxt *rsc) {
@ -141,7 +140,7 @@ static void init_rsc(const YV12_BUFFER_CONFIG *src, const AV1_COMMON *cm,
rsc->rusi = rusi;
rsc->sf = sf;
const YV12_BUFFER_CONFIG *dgd = cm->frame_to_show;
const YV12_BUFFER_CONFIG *dgd = &cm->cur_frame->buf;
const int is_uv = plane != AOM_PLANE_Y;
rsc->plane_width = src->crop_widths[is_uv];
rsc->plane_height = src->crop_heights[is_uv];
@ -166,7 +165,7 @@ static int64_t try_restoration_unit(const RestSearchCtxt *rsc,
const int bit_depth = cm->seq_params.bit_depth;
const int highbd = cm->seq_params.use_highbitdepth;
const YV12_BUFFER_CONFIG *fts = cm->frame_to_show;
const YV12_BUFFER_CONFIG *fts = &cm->cur_frame->buf;
// TODO(yunqing): For now, only use optimized LR filter in decoder. Can be
// also used in encoder.
const int optimized_lr = 0;
@ -201,7 +200,7 @@ int64_t av1_lowbd_pixel_proj_error_c(const uint8_t *src8, int width, int height,
v += xq[0] * (flt0[j] - u) + xq[1] * (flt1[j] - u);
const int32_t e =
ROUND_POWER_OF_TWO(v, SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS) - src[j];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -217,7 +216,7 @@ int64_t av1_lowbd_pixel_proj_error_c(const uint8_t *src8, int width, int height,
v += xq[0] * (flt0[j] - u);
const int32_t e =
ROUND_POWER_OF_TWO(v, SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS) - src[j];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -232,7 +231,7 @@ int64_t av1_lowbd_pixel_proj_error_c(const uint8_t *src8, int width, int height,
v += xq[1] * (flt1[j] - u);
const int32_t e =
ROUND_POWER_OF_TWO(v, SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS) - src[j];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -242,7 +241,7 @@ int64_t av1_lowbd_pixel_proj_error_c(const uint8_t *src8, int width, int height,
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t e = (int32_t)(dat[j]) - src[j];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -252,88 +251,97 @@ int64_t av1_lowbd_pixel_proj_error_c(const uint8_t *src8, int width, int height,
return err;
}
int64_t av1_highbd_pixel_proj_error_c(const uint8_t *src8, int width,
int height, int src_stride,
const uint8_t *dat8, int dat_stride,
int32_t *flt0, int flt0_stride,
int32_t *flt1, int flt1_stride, int xq[2],
const sgr_params_type *params) {
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
int i, j;
int64_t err = 0;
const int32_t half = 1 << (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS - 1);
if (params->r[0] > 0 && params->r[1] > 0) {
int xq0 = xq[0];
int xq1 = xq[1];
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t d = dat[j];
const int32_t s = src[j];
const int32_t u = (int32_t)(d << SGRPROJ_RST_BITS);
int32_t v0 = flt0[j] - u;
int32_t v1 = flt1[j] - u;
int32_t v = half;
v += xq0 * v0;
v += xq1 * v1;
const int32_t e = (v >> (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS)) + d - s;
err += ((int64_t)e * e);
}
dat += dat_stride;
flt0 += flt0_stride;
flt1 += flt1_stride;
src += src_stride;
}
} else if (params->r[0] > 0 || params->r[1] > 0) {
int exq;
int32_t *flt;
int flt_stride;
if (params->r[0] > 0) {
exq = xq[0];
flt = flt0;
flt_stride = flt0_stride;
} else {
exq = xq[1];
flt = flt1;
flt_stride = flt1_stride;
}
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t d = dat[j];
const int32_t s = src[j];
const int32_t u = (int32_t)(d << SGRPROJ_RST_BITS);
int32_t v = half;
v += exq * (flt[j] - u);
const int32_t e = (v >> (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS)) + d - s;
err += ((int64_t)e * e);
}
dat += dat_stride;
flt += flt_stride;
src += src_stride;
}
} else {
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t d = dat[j];
const int32_t s = src[j];
const int32_t e = d - s;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
}
}
return err;
}
static int64_t get_pixel_proj_error(const uint8_t *src8, int width, int height,
int src_stride, const uint8_t *dat8,
int dat_stride, int use_highbitdepth,
int32_t *flt0, int flt0_stride,
int32_t *flt1, int flt1_stride, int *xqd,
const sgr_params_type *params) {
int i, j;
int64_t err = 0;
int xq[2];
decode_xq(xqd, xq, params);
if (!use_highbitdepth) {
err = av1_lowbd_pixel_proj_error(src8, width, height, src_stride, dat8,
dat_stride, flt0, flt0_stride, flt1,
flt1_stride, xq, params);
return av1_lowbd_pixel_proj_error(src8, width, height, src_stride, dat8,
dat_stride, flt0, flt0_stride, flt1,
flt1_stride, xq, params);
} else {
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
const int32_t half = 1 << (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS - 1);
if (params->r[0] > 0 && params->r[1] > 0) {
int xq0 = xq[0];
int xq1 = xq[1];
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t d = dat[j];
const int32_t s = src[j];
const int32_t u = (int32_t)(d << SGRPROJ_RST_BITS);
int32_t v0 = flt0[j] - u;
int32_t v1 = flt1[j] - u;
int32_t v = half;
v += xq0 * v0;
v += xq1 * v1;
const int32_t e =
(v >> (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS)) + d - s;
err += e * e;
}
dat += dat_stride;
flt0 += flt0_stride;
flt1 += flt1_stride;
src += src_stride;
}
} else if (params->r[0] > 0 || params->r[1] > 0) {
int exq;
int32_t *flt;
int flt_stride;
if (params->r[0] > 0) {
exq = xq[0];
flt = flt0;
flt_stride = flt0_stride;
} else {
exq = xq[1];
flt = flt1;
flt_stride = flt1_stride;
}
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t d = dat[j];
const int32_t s = src[j];
const int32_t u = (int32_t)(d << SGRPROJ_RST_BITS);
int32_t v = half;
v += exq * (flt[j] - u);
const int32_t e =
(v >> (SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS)) + d - s;
err += e * e;
}
dat += dat_stride;
flt += flt_stride;
src += src_stride;
}
} else {
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const int32_t d = dat[j];
const int32_t s = src[j];
const int32_t e = d - s;
err += e * e;
}
dat += dat_stride;
src += src_stride;
}
}
return av1_highbd_pixel_proj_error(src8, width, height, src_stride, dat8,
dat_stride, flt0, flt0_stride, flt1,
flt1_stride, xq, params);
}
return err;
}
#define USE_SGRPROJ_REFINEMENT_SEARCH 1
@ -398,6 +406,13 @@ static int64_t finer_search_pixel_proj_error(
return err;
}
static int64_t signed_rounded_divide(int64_t dividend, int64_t divisor) {
if (dividend < 0)
return (dividend - divisor / 2) / divisor;
else
return (dividend + divisor / 2) / divisor;
}
static void get_proj_subspace(const uint8_t *src8, int width, int height,
int src_stride, const uint8_t *dat8,
int dat_stride, int use_highbitdepth,
@ -405,34 +420,32 @@ static void get_proj_subspace(const uint8_t *src8, int width, int height,
int flt1_stride, int *xq,
const sgr_params_type *params) {
int i, j;
double H[2][2] = { { 0, 0 }, { 0, 0 } };
double C[2] = { 0, 0 };
double Det;
double x[2];
int64_t H[2][2] = { { 0, 0 }, { 0, 0 } };
int64_t C[2] = { 0, 0 };
const int size = width * height;
aom_clear_system_state();
// Default
// Default values to be returned if the problem becomes ill-posed
xq[0] = 0;
xq[1] = 0;
if (!use_highbitdepth) {
const uint8_t *src = src8;
const uint8_t *dat = dat8;
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const double u = (double)(dat[i * dat_stride + j] << SGRPROJ_RST_BITS);
const double s =
(double)(src[i * src_stride + j] << SGRPROJ_RST_BITS) - u;
const double f1 =
(params->r[0] > 0) ? (double)flt0[i * flt0_stride + j] - u : 0;
const double f2 =
(params->r[1] > 0) ? (double)flt1[i * flt1_stride + j] - u : 0;
H[0][0] += f1 * f1;
H[1][1] += f2 * f2;
H[0][1] += f1 * f2;
C[0] += f1 * s;
C[1] += f2 * s;
const int32_t u =
(int32_t)(dat[i * dat_stride + j] << SGRPROJ_RST_BITS);
const int32_t s =
(int32_t)(src[i * src_stride + j] << SGRPROJ_RST_BITS) - u;
const int32_t f1 =
(params->r[0] > 0) ? (int32_t)flt0[i * flt0_stride + j] - u : 0;
const int32_t f2 =
(params->r[1] > 0) ? (int32_t)flt1[i * flt1_stride + j] - u : 0;
H[0][0] += (int64_t)f1 * f1;
H[1][1] += (int64_t)f2 * f2;
H[0][1] += (int64_t)f1 * f2;
C[0] += (int64_t)f1 * s;
C[1] += (int64_t)f2 * s;
}
}
} else {
@ -440,18 +453,19 @@ static void get_proj_subspace(const uint8_t *src8, int width, int height,
const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
const double u = (double)(dat[i * dat_stride + j] << SGRPROJ_RST_BITS);
const double s =
(double)(src[i * src_stride + j] << SGRPROJ_RST_BITS) - u;
const double f1 =
(params->r[0] > 0) ? (double)flt0[i * flt0_stride + j] - u : 0;
const double f2 =
(params->r[1] > 0) ? (double)flt1[i * flt1_stride + j] - u : 0;
H[0][0] += f1 * f1;
H[1][1] += f2 * f2;
H[0][1] += f1 * f2;
C[0] += f1 * s;
C[1] += f2 * s;
const int32_t u =
(int32_t)(dat[i * dat_stride + j] << SGRPROJ_RST_BITS);
const int32_t s =
(int32_t)(src[i * src_stride + j] << SGRPROJ_RST_BITS) - u;
const int32_t f1 =
(params->r[0] > 0) ? (int32_t)flt0[i * flt0_stride + j] - u : 0;
const int32_t f2 =
(params->r[1] > 0) ? (int32_t)flt1[i * flt1_stride + j] - u : 0;
H[0][0] += (int64_t)f1 * f1;
H[1][1] += (int64_t)f2 * f2;
H[0][1] += (int64_t)f1 * f2;
C[0] += (int64_t)f1 * s;
C[1] += (int64_t)f2 * s;
}
}
}
@ -464,35 +478,39 @@ static void get_proj_subspace(const uint8_t *src8, int width, int height,
if (params->r[0] == 0) {
// H matrix is now only the scalar H[1][1]
// C vector is now only the scalar C[1]
Det = H[1][1];
if (Det < 1e-8) return; // ill-posed, return default values
x[0] = 0;
x[1] = C[1] / Det;
const int64_t Det = H[1][1];
if (Det == 0) return; // ill-posed, return default values
xq[0] = 0;
xq[1] = (int)rint(x[1] * (1 << SGRPROJ_PRJ_BITS));
xq[1] = (int)signed_rounded_divide(C[1] * (1 << SGRPROJ_PRJ_BITS), Det);
} else if (params->r[1] == 0) {
// H matrix is now only the scalar H[0][0]
// C vector is now only the scalar C[0]
Det = H[0][0];
if (Det < 1e-8) return; // ill-posed, return default values
x[0] = C[0] / Det;
x[1] = 0;
xq[0] = (int)rint(x[0] * (1 << SGRPROJ_PRJ_BITS));
const int64_t Det = H[0][0];
if (Det == 0) return; // ill-posed, return default values
xq[0] = (int)signed_rounded_divide(C[0] * (1 << SGRPROJ_PRJ_BITS), Det);
xq[1] = 0;
} else {
Det = (H[0][0] * H[1][1] - H[0][1] * H[1][0]);
if (Det < 1e-8) return; // ill-posed, return default values
x[0] = (H[1][1] * C[0] - H[0][1] * C[1]) / Det;
x[1] = (H[0][0] * C[1] - H[1][0] * C[0]) / Det;
const int64_t Det = H[0][0] * H[1][1] - H[0][1] * H[1][0];
if (Det == 0) return; // ill-posed, return default values
xq[0] = (int)rint(x[0] * (1 << SGRPROJ_PRJ_BITS));
xq[1] = (int)rint(x[1] * (1 << SGRPROJ_PRJ_BITS));
// If scaling up dividend would overflow, instead scale down the divisor
const int64_t div1 = H[1][1] * C[0] - H[0][1] * C[1];
if ((div1 > 0 && INT64_MAX / (1 << SGRPROJ_PRJ_BITS) < div1) ||
(div1 < 0 && INT64_MIN / (1 << SGRPROJ_PRJ_BITS) > div1))
xq[0] = (int)signed_rounded_divide(div1, Det / (1 << SGRPROJ_PRJ_BITS));
else
xq[0] = (int)signed_rounded_divide(div1 * (1 << SGRPROJ_PRJ_BITS), Det);
const int64_t div2 = H[0][0] * C[1] - H[1][0] * C[0];
if ((div2 > 0 && INT64_MAX / (1 << SGRPROJ_PRJ_BITS) < div2) ||
(div2 < 0 && INT64_MIN / (1 << SGRPROJ_PRJ_BITS) > div2))
xq[1] = (int)signed_rounded_divide(div2, Det / (1 << SGRPROJ_PRJ_BITS));
else
xq[1] = (int)signed_rounded_divide(div2 * (1 << SGRPROJ_PRJ_BITS), Det);
}
}
void encode_xq(int *xq, int *xqd, const sgr_params_type *params) {
static void encode_xq(int *xq, int *xqd, const sgr_params_type *params) {
if (params->r[0] == 0) {
xqd[0] = 0;
xqd[1] = clamp((1 << SGRPROJ_PRJ_BITS) - xq[1], SGRPROJ_PRJ_MIN1,
@ -651,34 +669,34 @@ static void search_sgrproj(const RestorationTileLimits *limits,
void av1_compute_stats_c(int wiener_win, const uint8_t *dgd, const uint8_t *src,
int h_start, int h_end, int v_start, int v_end,
int dgd_stride, int src_stride, double *M, double *H) {
int dgd_stride, int src_stride, int64_t *M,
int64_t *H) {
int i, j, k, l;
double Y[WIENER_WIN2];
int16_t Y[WIENER_WIN2];
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const double avg =
find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
uint8_t avg = find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
memset(M, 0, sizeof(*M) * wiener_win2);
memset(H, 0, sizeof(*H) * wiener_win2 * wiener_win2);
for (i = v_start; i < v_end; i++) {
for (j = h_start; j < h_end; j++) {
const double X = (double)src[i * src_stride + j] - avg;
const int16_t X = (int16_t)src[i * src_stride + j] - (int16_t)avg;
int idx = 0;
for (k = -wiener_halfwin; k <= wiener_halfwin; k++) {
for (l = -wiener_halfwin; l <= wiener_halfwin; l++) {
Y[idx] = (double)dgd[(i + l) * dgd_stride + (j + k)] - avg;
Y[idx] = (int16_t)dgd[(i + l) * dgd_stride + (j + k)] - (int16_t)avg;
idx++;
}
}
assert(idx == wiener_win2);
for (k = 0; k < wiener_win2; ++k) {
M[k] += Y[k] * X;
M[k] += (int32_t)Y[k] * X;
for (l = k; l < wiener_win2; ++l) {
// H is a symmetric matrix, so we only need to fill out the upper
// triangle here. We can copy it down to the lower triangle outside
// the (i, j) loops.
H[k * wiener_win2 + l] += Y[k] * Y[l];
H[k * wiener_win2 + l] += (int32_t)Y[k] * Y[l];
}
}
}
@ -690,60 +708,55 @@ void av1_compute_stats_c(int wiener_win, const uint8_t *dgd, const uint8_t *src,
}
}
static double find_average_highbd(const uint16_t *src, int h_start, int h_end,
int v_start, int v_end, int stride) {
uint64_t sum = 0;
double avg = 0;
int i, j;
aom_clear_system_state();
for (i = v_start; i < v_end; i++)
for (j = h_start; j < h_end; j++) sum += src[i * stride + j];
avg = (double)sum / ((v_end - v_start) * (h_end - h_start));
return avg;
}
static AOM_FORCE_INLINE void compute_stats_highbd(
int wiener_win, const uint8_t *dgd8, const uint8_t *src8, int h_start,
int h_end, int v_start, int v_end, int dgd_stride, int src_stride,
double *M, double *H) {
void av1_compute_stats_highbd_c(int wiener_win, const uint8_t *dgd8,
const uint8_t *src8, int h_start, int h_end,
int v_start, int v_end, int dgd_stride,
int src_stride, int64_t *M, int64_t *H,
aom_bit_depth_t bit_depth) {
int i, j, k, l;
double Y[WIENER_WIN2];
int32_t Y[WIENER_WIN2];
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
const double avg =
uint16_t avg =
find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
uint8_t bit_depth_divider = 1;
if (bit_depth == AOM_BITS_12)
bit_depth_divider = 16;
else if (bit_depth == AOM_BITS_10)
bit_depth_divider = 4;
memset(M, 0, sizeof(*M) * wiener_win2);
memset(H, 0, sizeof(*H) * wiener_win2 * wiener_win2);
for (i = v_start; i < v_end; i++) {
for (j = h_start; j < h_end; j++) {
const double X = (double)src[i * src_stride + j] - avg;
const int32_t X = (int32_t)src[i * src_stride + j] - (int32_t)avg;
int idx = 0;
for (k = -wiener_halfwin; k <= wiener_halfwin; k++) {
for (l = -wiener_halfwin; l <= wiener_halfwin; l++) {
Y[idx] = (double)dgd[(i + l) * dgd_stride + (j + k)] - avg;
Y[idx] = (int32_t)dgd[(i + l) * dgd_stride + (j + k)] - (int32_t)avg;
idx++;
}
}
assert(idx == wiener_win2);
for (k = 0; k < wiener_win2; ++k) {
double Yk = Y[k];
M[k] += Yk * X;
double *H2 = &H[k * wiener_win2];
H2[k] += Yk * Yk;
for (l = k + 1; l < wiener_win2; ++l) {
M[k] += (int64_t)Y[k] * X;
for (l = k; l < wiener_win2; ++l) {
// H is a symmetric matrix, so we only need to fill out the upper
// triangle here. We can copy it down to the lower triangle outside
// the (i, j) loops.
H2[l] += Yk * Y[l];
H[k * wiener_win2 + l] += (int64_t)Y[k] * Y[l];
}
}
}
}
for (k = 0; k < wiener_win2; ++k) {
M[k] /= bit_depth_divider;
H[k * wiener_win2 + k] /= bit_depth_divider;
for (l = k + 1; l < wiener_win2; ++l) {
H[k * wiener_win2 + l] /= bit_depth_divider;
H[l * wiener_win2 + k] = H[k * wiener_win2 + l];
}
}
@ -754,12 +767,56 @@ static INLINE int wrap_index(int i, int wiener_win) {
return (i >= wiener_halfwin1 ? wiener_win - 1 - i : i);
}
// Solve linear equations to find Wiener filter tap values
// Taps are output scaled by WIENER_FILT_STEP
static int linsolve_wiener(int n, int64_t *A, int stride, int64_t *b,
int32_t *x) {
for (int k = 0; k < n - 1; k++) {
// Partial pivoting: bring the row with the largest pivot to the top
for (int i = n - 1; i > k; i--) {
// If row i has a better (bigger) pivot than row (i-1), swap them
if (llabs(A[(i - 1) * stride + k]) < llabs(A[i * stride + k])) {
for (int j = 0; j < n; j++) {
const int64_t c = A[i * stride + j];
A[i * stride + j] = A[(i - 1) * stride + j];
A[(i - 1) * stride + j] = c;
}
const int64_t c = b[i];
b[i] = b[i - 1];
b[i - 1] = c;
}
}
// Forward elimination (convert A to row-echelon form)
for (int i = k; i < n - 1; i++) {
if (A[k * stride + k] == 0) return 0;
const int64_t c = A[(i + 1) * stride + k];
const int64_t cd = A[k * stride + k];
for (int j = 0; j < n; j++) {
A[(i + 1) * stride + j] -= c / 256 * A[k * stride + j] / cd * 256;
}
b[i + 1] -= c * b[k] / cd;
}
}
// Back-substitution
for (int i = n - 1; i >= 0; i--) {
if (A[i * stride + i] == 0) return 0;
int64_t c = 0;
for (int j = i + 1; j <= n - 1; j++) {
c += A[i * stride + j] * x[j] / WIENER_TAP_SCALE_FACTOR;
}
// Store filter taps x in scaled form.
x[i] = (int32_t)(WIENER_TAP_SCALE_FACTOR * (b[i] - c) / A[i * stride + i]);
}
return 1;
}
// Fix vector b, update vector a
static void update_a_sep_sym(int wiener_win, double **Mc, double **Hc,
double *a, double *b) {
static void update_a_sep_sym(int wiener_win, int64_t **Mc, int64_t **Hc,
int32_t *a, int32_t *b) {
int i, j;
double S[WIENER_WIN];
double A[WIENER_HALFWIN1], B[WIENER_HALFWIN1 * WIENER_HALFWIN1];
int32_t S[WIENER_WIN];
int64_t A[WIENER_HALFWIN1], B[WIENER_HALFWIN1 * WIENER_HALFWIN1];
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin1 = (wiener_win >> 1) + 1;
memset(A, 0, sizeof(A));
@ -767,36 +824,41 @@ static void update_a_sep_sym(int wiener_win, double **Mc, double **Hc,
for (i = 0; i < wiener_win; i++) {
for (j = 0; j < wiener_win; ++j) {
const int jj = wrap_index(j, wiener_win);
A[jj] += Mc[i][j] * b[i];
A[jj] += Mc[i][j] * b[i] / WIENER_TAP_SCALE_FACTOR;
}
}
for (i = 0; i < wiener_win; i++) {
for (j = 0; j < wiener_win; j++) {
int k, l;
for (k = 0; k < wiener_win; ++k)
for (k = 0; k < wiener_win; ++k) {
for (l = 0; l < wiener_win; ++l) {
const int kk = wrap_index(k, wiener_win);
const int ll = wrap_index(l, wiener_win);
B[ll * wiener_halfwin1 + kk] +=
Hc[j * wiener_win + i][k * wiener_win2 + l] * b[i] * b[j];
Hc[j * wiener_win + i][k * wiener_win2 + l] * b[i] /
WIENER_TAP_SCALE_FACTOR * b[j] / WIENER_TAP_SCALE_FACTOR;
}
}
}
}
// Normalization enforcement in the system of equations itself
for (i = 0; i < wiener_halfwin1 - 1; ++i)
for (i = 0; i < wiener_halfwin1 - 1; ++i) {
A[i] -=
A[wiener_halfwin1 - 1] * 2 +
B[i * wiener_halfwin1 + wiener_halfwin1 - 1] -
2 * B[(wiener_halfwin1 - 1) * wiener_halfwin1 + (wiener_halfwin1 - 1)];
for (i = 0; i < wiener_halfwin1 - 1; ++i)
for (j = 0; j < wiener_halfwin1 - 1; ++j)
}
for (i = 0; i < wiener_halfwin1 - 1; ++i) {
for (j = 0; j < wiener_halfwin1 - 1; ++j) {
B[i * wiener_halfwin1 + j] -=
2 * (B[i * wiener_halfwin1 + (wiener_halfwin1 - 1)] +
B[(wiener_halfwin1 - 1) * wiener_halfwin1 + j] -
2 * B[(wiener_halfwin1 - 1) * wiener_halfwin1 +
(wiener_halfwin1 - 1)]);
if (linsolve(wiener_halfwin1 - 1, B, wiener_halfwin1, A, S)) {
S[wiener_halfwin1 - 1] = 1.0;
}
}
if (linsolve_wiener(wiener_halfwin1 - 1, B, wiener_halfwin1, A, S)) {
S[wiener_halfwin1 - 1] = WIENER_TAP_SCALE_FACTOR;
for (i = wiener_halfwin1; i < wiener_win; ++i) {
S[i] = S[wiener_win - 1 - i];
S[wiener_halfwin1 - 1] -= 2 * S[i];
@ -806,18 +868,20 @@ static void update_a_sep_sym(int wiener_win, double **Mc, double **Hc,
}
// Fix vector a, update vector b
static void update_b_sep_sym(int wiener_win, double **Mc, double **Hc,
double *a, double *b) {
static void update_b_sep_sym(int wiener_win, int64_t **Mc, int64_t **Hc,
int32_t *a, int32_t *b) {
int i, j;
double S[WIENER_WIN];
double A[WIENER_HALFWIN1], B[WIENER_HALFWIN1 * WIENER_HALFWIN1];
int32_t S[WIENER_WIN];
int64_t A[WIENER_HALFWIN1], B[WIENER_HALFWIN1 * WIENER_HALFWIN1];
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin1 = (wiener_win >> 1) + 1;
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
for (i = 0; i < wiener_win; i++) {
const int ii = wrap_index(i, wiener_win);
for (j = 0; j < wiener_win; j++) A[ii] += Mc[i][j] * a[j];
for (j = 0; j < wiener_win; j++) {
A[ii] += Mc[i][j] * a[j] / WIENER_TAP_SCALE_FACTOR;
}
}
for (i = 0; i < wiener_win; i++) {
@ -825,27 +889,33 @@ static void update_b_sep_sym(int wiener_win, double **Mc, double **Hc,
const int ii = wrap_index(i, wiener_win);
const int jj = wrap_index(j, wiener_win);
int k, l;
for (k = 0; k < wiener_win; ++k)
for (l = 0; l < wiener_win; ++l)
for (k = 0; k < wiener_win; ++k) {
for (l = 0; l < wiener_win; ++l) {
B[jj * wiener_halfwin1 + ii] +=
Hc[i * wiener_win + j][k * wiener_win2 + l] * a[k] * a[l];
Hc[i * wiener_win + j][k * wiener_win2 + l] * a[k] /
WIENER_TAP_SCALE_FACTOR * a[l] / WIENER_TAP_SCALE_FACTOR;
}
}
}
}
// Normalization enforcement in the system of equations itself
for (i = 0; i < wiener_halfwin1 - 1; ++i)
for (i = 0; i < wiener_halfwin1 - 1; ++i) {
A[i] -=
A[wiener_halfwin1 - 1] * 2 +
B[i * wiener_halfwin1 + wiener_halfwin1 - 1] -
2 * B[(wiener_halfwin1 - 1) * wiener_halfwin1 + (wiener_halfwin1 - 1)];
for (i = 0; i < wiener_halfwin1 - 1; ++i)
for (j = 0; j < wiener_halfwin1 - 1; ++j)
}
for (i = 0; i < wiener_halfwin1 - 1; ++i) {
for (j = 0; j < wiener_halfwin1 - 1; ++j) {
B[i * wiener_halfwin1 + j] -=
2 * (B[i * wiener_halfwin1 + (wiener_halfwin1 - 1)] +
B[(wiener_halfwin1 - 1) * wiener_halfwin1 + j] -
2 * B[(wiener_halfwin1 - 1) * wiener_halfwin1 +
(wiener_halfwin1 - 1)]);
if (linsolve(wiener_halfwin1 - 1, B, wiener_halfwin1, A, S)) {
S[wiener_halfwin1 - 1] = 1.0;
}
}
if (linsolve_wiener(wiener_halfwin1 - 1, B, wiener_halfwin1, A, S)) {
S[wiener_halfwin1 - 1] = WIENER_TAP_SCALE_FACTOR;
for (i = wiener_halfwin1; i < wiener_win; ++i) {
S[i] = S[wiener_win - 1 - i];
S[wiener_halfwin1 - 1] -= 2 * S[i];
@ -854,20 +924,21 @@ static void update_b_sep_sym(int wiener_win, double **Mc, double **Hc,
}
}
static int wiener_decompose_sep_sym(int wiener_win, double *M, double *H,
double *a, double *b) {
static const int init_filt[WIENER_WIN] = {
static int wiener_decompose_sep_sym(int wiener_win, int64_t *M, int64_t *H,
int32_t *a, int32_t *b) {
static const int32_t init_filt[WIENER_WIN] = {
WIENER_FILT_TAP0_MIDV, WIENER_FILT_TAP1_MIDV, WIENER_FILT_TAP2_MIDV,
WIENER_FILT_TAP3_MIDV, WIENER_FILT_TAP2_MIDV, WIENER_FILT_TAP1_MIDV,
WIENER_FILT_TAP0_MIDV,
};
double *Hc[WIENER_WIN2];
double *Mc[WIENER_WIN];
int64_t *Hc[WIENER_WIN2];
int64_t *Mc[WIENER_WIN];
int i, j, iter;
const int plane_off = (WIENER_WIN - wiener_win) >> 1;
const int wiener_win2 = wiener_win * wiener_win;
for (i = 0; i < wiener_win; i++) {
a[i] = b[i] = (double)init_filt[i + plane_off] / WIENER_FILT_STEP;
a[i] = b[i] =
WIENER_TAP_SCALE_FACTOR / WIENER_FILT_STEP * init_filt[i + plane_off];
}
for (i = 0; i < wiener_win; i++) {
Mc[i] = M + i * wiener_win;
@ -889,23 +960,23 @@ static int wiener_decompose_sep_sym(int wiener_win, double *M, double *H,
// Computes the function x'*H*x - x'*M for the learned 2D filter x, and compares
// against identity filters; Final score is defined as the difference between
// the function values
static double compute_score(int wiener_win, double *M, double *H,
InterpKernel vfilt, InterpKernel hfilt) {
double ab[WIENER_WIN * WIENER_WIN];
static int64_t compute_score(int wiener_win, int64_t *M, int64_t *H,
InterpKernel vfilt, InterpKernel hfilt) {
int32_t ab[WIENER_WIN * WIENER_WIN];
int16_t a[WIENER_WIN], b[WIENER_WIN];
int64_t P = 0, Q = 0;
int64_t iP = 0, iQ = 0;
int64_t Score, iScore;
int i, k, l;
double P = 0, Q = 0;
double iP = 0, iQ = 0;
double Score, iScore;
double a[WIENER_WIN], b[WIENER_WIN];
const int plane_off = (WIENER_WIN - wiener_win) >> 1;
const int wiener_win2 = wiener_win * wiener_win;
aom_clear_system_state();
a[WIENER_HALFWIN] = b[WIENER_HALFWIN] = 1.0;
a[WIENER_HALFWIN] = b[WIENER_HALFWIN] = WIENER_FILT_STEP;
for (i = 0; i < WIENER_HALFWIN; ++i) {
a[i] = a[WIENER_WIN - i - 1] = (double)vfilt[i] / WIENER_FILT_STEP;
b[i] = b[WIENER_WIN - i - 1] = (double)hfilt[i] / WIENER_FILT_STEP;
a[i] = a[WIENER_WIN - i - 1] = vfilt[i];
b[i] = b[WIENER_WIN - i - 1] = hfilt[i];
a[WIENER_HALFWIN] -= 2 * a[i];
b[WIENER_HALFWIN] -= 2 * b[i];
}
@ -915,9 +986,11 @@ static double compute_score(int wiener_win, double *M, double *H,
ab[k * wiener_win + l] = a[l + plane_off] * b[k + plane_off];
}
for (k = 0; k < wiener_win2; ++k) {
P += ab[k] * M[k];
for (l = 0; l < wiener_win2; ++l)
Q += ab[k] * H[k * wiener_win2 + l] * ab[l];
P += ab[k] * M[k] / WIENER_FILT_STEP / WIENER_FILT_STEP;
for (l = 0; l < wiener_win2; ++l) {
Q += ab[k] * H[k * wiener_win2 + l] * ab[l] / WIENER_FILT_STEP /
WIENER_FILT_STEP / WIENER_FILT_STEP / WIENER_FILT_STEP;
}
}
Score = Q - 2 * P;
@ -928,11 +1001,19 @@ static double compute_score(int wiener_win, double *M, double *H,
return Score - iScore;
}
static void quantize_sym_filter(int wiener_win, double *f, InterpKernel fi) {
static void finalize_sym_filter(int wiener_win, int32_t *f, InterpKernel fi) {
int i;
const int wiener_halfwin = (wiener_win >> 1);
for (i = 0; i < wiener_halfwin; ++i) {
fi[i] = RINT(f[i] * WIENER_FILT_STEP);
const int64_t dividend = f[i] * WIENER_FILT_STEP;
const int64_t divisor = WIENER_TAP_SCALE_FACTOR;
// Perform this division with proper rounding rather than truncation
if (dividend < 0) {
fi[i] = (int16_t)((dividend - (divisor / 2)) / divisor);
} else {
fi[i] = (int16_t)((dividend + (divisor / 2)) / divisor);
}
}
// Specialize for 7-tap filter
if (wiener_win == WIENER_WIN) {
@ -1110,15 +1191,16 @@ static void search_wiener(const RestorationTileLimits *limits,
const int wiener_win =
(rsc->plane == AOM_PLANE_Y) ? WIENER_WIN : WIENER_WIN_CHROMA;
double M[WIENER_WIN2];
double H[WIENER_WIN2 * WIENER_WIN2];
double vfilterd[WIENER_WIN], hfilterd[WIENER_WIN];
int64_t M[WIENER_WIN2];
int64_t H[WIENER_WIN2 * WIENER_WIN2];
int32_t vfilter[WIENER_WIN], hfilter[WIENER_WIN];
const AV1_COMMON *const cm = rsc->cm;
if (cm->seq_params.use_highbitdepth) {
compute_stats_highbd(wiener_win, rsc->dgd_buffer, rsc->src_buffer,
limits->h_start, limits->h_end, limits->v_start,
limits->v_end, rsc->dgd_stride, rsc->src_stride, M, H);
av1_compute_stats_highbd(wiener_win, rsc->dgd_buffer, rsc->src_buffer,
limits->h_start, limits->h_end, limits->v_start,
limits->v_end, rsc->dgd_stride, rsc->src_stride, M,
H, cm->seq_params.bit_depth);
} else {
av1_compute_stats(wiener_win, rsc->dgd_buffer, rsc->src_buffer,
limits->h_start, limits->h_end, limits->v_start,
@ -1128,7 +1210,7 @@ static void search_wiener(const RestorationTileLimits *limits,
const MACROBLOCK *const x = rsc->x;
const int64_t bits_none = x->wiener_restore_cost[0];
if (!wiener_decompose_sep_sym(wiener_win, M, H, vfilterd, hfilterd)) {
if (!wiener_decompose_sep_sym(wiener_win, M, H, vfilter, hfilter)) {
rsc->bits += bits_none;
rsc->sse += rusi->sse[RESTORE_NONE];
rusi->best_rtype[RESTORE_WIENER - 1] = RESTORE_NONE;
@ -1139,8 +1221,8 @@ static void search_wiener(const RestorationTileLimits *limits,
RestorationUnitInfo rui;
memset(&rui, 0, sizeof(rui));
rui.restoration_type = RESTORE_WIENER;
quantize_sym_filter(wiener_win, vfilterd, rui.wiener_info.vfilter);
quantize_sym_filter(wiener_win, hfilterd, rui.wiener_info.hfilter);
finalize_sym_filter(wiener_win, vfilter, rui.wiener_info.vfilter);
finalize_sym_filter(wiener_win, hfilter, rui.wiener_info.hfilter);
// Filter score computes the value of the function x'*A*x - x'*b for the
// learned filter and compares it against identity filer. If there is no
@ -1199,7 +1281,7 @@ static void search_norestore(const RestorationTileLimits *limits,
const int highbd = rsc->cm->seq_params.use_highbitdepth;
rusi->sse[RESTORE_NONE] = sse_restoration_unit(
limits, rsc->src, rsc->cm->frame_to_show, rsc->plane, highbd);
limits, rsc->src, &rsc->cm->cur_frame->buf, rsc->plane, highbd);
rsc->sse += rusi->sse[RESTORE_NONE];
}
@ -1283,7 +1365,8 @@ static double search_rest_type(RestSearchCtxt *rsc, RestorationType rtype) {
};
reset_rsc(rsc);
rsc_on_tile(LR_TILE_ROW, LR_TILE_COL, rsc);
rsc_on_tile(rsc);
av1_foreach_rest_unit_in_plane(rsc->cm, rsc->plane, funs[rtype], rsc,
&rsc->tile_rect, rsc->cm->rst_tmpbuf, NULL);
return RDCOST_DBL(rsc->x->rdmult, rsc->bits >> 4, rsc->sse);

View file

@ -25,16 +25,34 @@ static const uint8_t g_shuffle_stats_data[16] = {
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
};
static INLINE double find_average(const uint8_t *src, int h_start, int h_end,
int v_start, int v_end, int stride) {
static const uint8_t g_shuffle_stats_highbd_data[32] = {
0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9,
0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9,
};
static INLINE uint8_t find_average(const uint8_t *src, int h_start, int h_end,
int v_start, int v_end, int stride) {
uint64_t sum = 0;
double avg = 0;
int i, j;
aom_clear_system_state();
for (i = v_start; i < v_end; i++)
for (j = h_start; j < h_end; j++) sum += src[i * stride + j];
avg = (double)sum / ((v_end - v_start) * (h_end - h_start));
return avg;
for (int i = v_start; i < v_end; i++) {
for (int j = h_start; j < h_end; j++) {
sum += src[i * stride + j];
}
}
uint64_t avg = sum / ((v_end - v_start) * (h_end - h_start));
return (uint8_t)avg;
}
static INLINE uint16_t find_average_highbd(const uint16_t *src, int h_start,
int h_end, int v_start, int v_end,
int stride) {
uint64_t sum = 0;
for (int i = v_start; i < v_end; i++) {
for (int j = h_start; j < h_end; j++) {
sum += src[i * stride + j];
}
}
uint64_t avg = sum / ((v_end - v_start) * (h_end - h_start));
return (uint16_t)avg;
}
void av1_pick_filter_restoration(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi);

View file

@ -23,7 +23,7 @@
#define MAX_DEGENERATE_ITER 10
#define MINPTS_MULTIPLIER 5
#define INLIER_THRESHOLD 1.0
#define INLIER_THRESHOLD 1.25
#define MIN_TRIALS 20
////////////////////////////////////////////////////////////////////////////////
@ -376,10 +376,7 @@ static int ransac(const int *matched_points, int npoints,
IsDegenerateFunc is_degenerate,
FindTransformationFunc find_transformation,
ProjectPointsDoubleFunc projectpoints) {
static const double PROBABILITY_REQUIRED = 0.9;
static const double EPS = 1e-12;
int N = 10000, trial_count = 0;
int trial_count = 0;
int i = 0;
int ret_val = 0;
@ -444,7 +441,7 @@ static int ransac(const int *matched_points, int npoints,
*(cnp2++) = *(matched_points++);
}
while (N > trial_count) {
while (MIN_TRIALS > trial_count) {
double sum_distance = 0.0;
double sum_distance_squared = 0.0;
@ -491,8 +488,7 @@ static int ransac(const int *matched_points, int npoints,
if (current_motion.num_inliers >= worst_kept_motion->num_inliers &&
current_motion.num_inliers > 1) {
int temp;
double fracinliers, pNoOutliers, mean_distance, dtemp;
double mean_distance;
mean_distance = sum_distance / ((double)current_motion.num_inliers);
current_motion.variance =
sum_distance_squared / ((double)current_motion.num_inliers - 1.0) -
@ -506,21 +502,183 @@ static int ransac(const int *matched_points, int npoints,
worst_kept_motion->variance = current_motion.variance;
memcpy(worst_kept_motion->inlier_indices, current_motion.inlier_indices,
sizeof(*current_motion.inlier_indices) * npoints);
assert(npoints > 0);
fracinliers = (double)current_motion.num_inliers / (double)npoints;
pNoOutliers = 1 - pow(fracinliers, minpts);
pNoOutliers = fmax(EPS, pNoOutliers);
pNoOutliers = fmin(1 - EPS, pNoOutliers);
dtemp = log(1.0 - PROBABILITY_REQUIRED) / log(pNoOutliers);
temp = (dtemp > (double)INT32_MAX)
? INT32_MAX
: dtemp < (double)INT32_MIN ? INT32_MIN : (int)dtemp;
if (temp > 0 && temp < N) {
N = AOMMAX(temp, MIN_TRIALS);
// Determine the new worst kept motion and its num_inliers and variance.
for (i = 0; i < num_desired_motions; ++i) {
if (is_better_motion(worst_kept_motion, &motions[i])) {
worst_kept_motion = &motions[i];
}
}
}
}
trial_count++;
}
// Sort the motions, best first.
qsort(motions, num_desired_motions, sizeof(RANSAC_MOTION), compare_motions);
// Recompute the motions using only the inliers.
for (i = 0; i < num_desired_motions; ++i) {
if (motions[i].num_inliers >= minpts) {
copy_points_at_indices(points1, corners1, motions[i].inlier_indices,
motions[i].num_inliers);
copy_points_at_indices(points2, corners2, motions[i].inlier_indices,
motions[i].num_inliers);
find_transformation(motions[i].num_inliers, points1, points2,
params_by_motion + (MAX_PARAMDIM - 1) * i);
}
num_inliers_by_motion[i] = motions[i].num_inliers;
}
finish_ransac:
aom_free(points1);
aom_free(points2);
aom_free(corners1);
aom_free(corners2);
aom_free(image1_coord);
aom_free(current_motion.inlier_indices);
for (i = 0; i < num_desired_motions; ++i) {
aom_free(motions[i].inlier_indices);
}
aom_free(motions);
return ret_val;
}
static int ransac_double_prec(const double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion, int num_desired_motions,
const int minpts, IsDegenerateFunc is_degenerate,
FindTransformationFunc find_transformation,
ProjectPointsDoubleFunc projectpoints) {
int trial_count = 0;
int i = 0;
int ret_val = 0;
unsigned int seed = (unsigned int)npoints;
int indices[MAX_MINPTS] = { 0 };
double *points1, *points2;
double *corners1, *corners2;
double *image1_coord;
// Store information for the num_desired_motions best transformations found
// and the worst motion among them, as well as the motion currently under
// consideration.
RANSAC_MOTION *motions, *worst_kept_motion = NULL;
RANSAC_MOTION current_motion;
// Store the parameters and the indices of the inlier points for the motion
// currently under consideration.
double params_this_motion[MAX_PARAMDIM];
double *cnp1, *cnp2;
for (i = 0; i < num_desired_motions; ++i) {
num_inliers_by_motion[i] = 0;
}
if (npoints < minpts * MINPTS_MULTIPLIER || npoints == 0) {
return 1;
}
points1 = (double *)aom_malloc(sizeof(*points1) * npoints * 2);
points2 = (double *)aom_malloc(sizeof(*points2) * npoints * 2);
corners1 = (double *)aom_malloc(sizeof(*corners1) * npoints * 2);
corners2 = (double *)aom_malloc(sizeof(*corners2) * npoints * 2);
image1_coord = (double *)aom_malloc(sizeof(*image1_coord) * npoints * 2);
motions =
(RANSAC_MOTION *)aom_malloc(sizeof(RANSAC_MOTION) * num_desired_motions);
for (i = 0; i < num_desired_motions; ++i) {
motions[i].inlier_indices =
(int *)aom_malloc(sizeof(*motions->inlier_indices) * npoints);
clear_motion(motions + i, npoints);
}
current_motion.inlier_indices =
(int *)aom_malloc(sizeof(*current_motion.inlier_indices) * npoints);
clear_motion(&current_motion, npoints);
worst_kept_motion = motions;
if (!(points1 && points2 && corners1 && corners2 && image1_coord && motions &&
current_motion.inlier_indices)) {
ret_val = 1;
goto finish_ransac;
}
cnp1 = corners1;
cnp2 = corners2;
for (i = 0; i < npoints; ++i) {
*(cnp1++) = *(matched_points++);
*(cnp1++) = *(matched_points++);
*(cnp2++) = *(matched_points++);
*(cnp2++) = *(matched_points++);
}
while (MIN_TRIALS > trial_count) {
double sum_distance = 0.0;
double sum_distance_squared = 0.0;
clear_motion(&current_motion, npoints);
int degenerate = 1;
int num_degenerate_iter = 0;
while (degenerate) {
num_degenerate_iter++;
if (!get_rand_indices(npoints, minpts, indices, &seed)) {
ret_val = 1;
goto finish_ransac;
}
copy_points_at_indices(points1, corners1, indices, minpts);
copy_points_at_indices(points2, corners2, indices, minpts);
degenerate = is_degenerate(points1);
if (num_degenerate_iter > MAX_DEGENERATE_ITER) {
ret_val = 1;
goto finish_ransac;
}
}
if (find_transformation(minpts, points1, points2, params_this_motion)) {
trial_count++;
continue;
}
projectpoints(params_this_motion, corners1, image1_coord, npoints, 2, 2);
for (i = 0; i < npoints; ++i) {
double dx = image1_coord[i * 2] - corners2[i * 2];
double dy = image1_coord[i * 2 + 1] - corners2[i * 2 + 1];
double distance = sqrt(dx * dx + dy * dy);
if (distance < INLIER_THRESHOLD) {
current_motion.inlier_indices[current_motion.num_inliers++] = i;
sum_distance += distance;
sum_distance_squared += distance * distance;
}
}
if (current_motion.num_inliers >= worst_kept_motion->num_inliers &&
current_motion.num_inliers > 1) {
double mean_distance;
mean_distance = sum_distance / ((double)current_motion.num_inliers);
current_motion.variance =
sum_distance_squared / ((double)current_motion.num_inliers - 1.0) -
mean_distance * mean_distance * ((double)current_motion.num_inliers) /
((double)current_motion.num_inliers - 1.0);
if (is_better_motion(&current_motion, worst_kept_motion)) {
// This motion is better than the worst currently kept motion. Remember
// the inlier points and variance. The parameters for each kept motion
// will be recomputed later using only the inliers.
worst_kept_motion->num_inliers = current_motion.num_inliers;
worst_kept_motion->variance = current_motion.variance;
memcpy(worst_kept_motion->inlier_indices, current_motion.inlier_indices,
sizeof(*current_motion.inlier_indices) * npoints);
assert(npoints > 0);
// Determine the new worst kept motion and its num_inliers and variance.
for (i = 0; i < num_desired_motions; ++i) {
if (is_better_motion(worst_kept_motion, &motions[i])) {
@ -601,3 +759,33 @@ int ransac_affine(int *matched_points, int npoints, int *num_inliers_by_motion,
params_by_motion, num_desired_motions, 3, is_degenerate_affine,
find_affine, project_points_double_affine);
}
int ransac_translation_double_prec(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion,
int num_desired_motions) {
return ransac_double_prec(matched_points, npoints, num_inliers_by_motion,
params_by_motion, num_desired_motions, 3,
is_degenerate_translation, find_translation,
project_points_double_translation);
}
int ransac_rotzoom_double_prec(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion,
int num_desired_motions) {
return ransac_double_prec(matched_points, npoints, num_inliers_by_motion,
params_by_motion, num_desired_motions, 3,
is_degenerate_affine, find_rotzoom,
project_points_double_rotzoom);
}
int ransac_affine_double_prec(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion,
int num_desired_motions) {
return ransac_double_prec(matched_points, npoints, num_inliers_by_motion,
params_by_motion, num_desired_motions, 3,
is_degenerate_affine, find_affine,
project_points_double_affine);
}

View file

@ -22,6 +22,9 @@
typedef int (*RansacFunc)(int *matched_points, int npoints,
int *num_inliers_by_motion, double *params_by_motion,
int num_motions);
typedef int (*RansacFuncDouble)(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion, int num_motions);
/* Each of these functions fits a motion model from a set of
corresponding points in 2 frames using RANSAC. */
@ -32,4 +35,16 @@ int ransac_rotzoom(int *matched_points, int npoints, int *num_inliers_by_motion,
int ransac_translation(int *matched_points, int npoints,
int *num_inliers_by_motion, double *params_by_motion,
int num_motions);
int ransac_translation_double_prec(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion,
int num_desired_motions);
int ransac_rotzoom_double_prec(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion,
int num_desired_motions);
int ransac_affine_double_prec(double *matched_points, int npoints,
int *num_inliers_by_motion,
double *params_by_motion,
int num_desired_motions);
#endif // AOM_AV1_ENCODER_RANSAC_H_

View file

@ -29,6 +29,7 @@
#include "av1/common/seg_common.h"
#include "av1/encoder/encodemv.h"
#include "av1/encoder/encode_strategy.h"
#include "av1/encoder/random.h"
#include "av1/encoder/ratectrl.h"
@ -250,12 +251,10 @@ int av1_rc_get_default_min_gf_interval(int width, int height,
// 4K60: 12
}
int av1_rc_get_default_max_gf_interval(double framerate, int min_gf_interval) {
int av1_rc_get_default_max_gf_interval(double framerate, int min_gf_interval,
int max_pyr_height) {
int interval = AOMMIN(MAX_GF_INTERVAL, (int)(framerate * 0.75));
interval += (interval & 0x01); // Round to even value
#if CONFIG_FIX_GF_LENGTH
interval = AOMMAX(FIXED_GF_LENGTH, interval);
#endif
interval = AOMMAX(av1_rc_get_fixed_gf_length(max_pyr_height), interval);
return AOMMAX(interval, min_gf_interval);
}
@ -312,7 +311,7 @@ void av1_rc_init(const AV1EncoderConfig *oxcf, int pass, RATE_CONTROL *rc) {
oxcf->width, oxcf->height, oxcf->init_framerate);
if (rc->max_gf_interval == 0)
rc->max_gf_interval = av1_rc_get_default_max_gf_interval(
oxcf->init_framerate, rc->min_gf_interval);
oxcf->init_framerate, rc->min_gf_interval, oxcf->gf_max_pyr_height);
rc->baseline_gf_interval = (rc->min_gf_interval + rc->max_gf_interval) / 2;
}
@ -357,7 +356,7 @@ static double get_rate_correction_factor(const AV1_COMP *cpi, int width,
const RATE_CONTROL *const rc = &cpi->rc;
double rcf;
if (cpi->common.frame_type == KEY_FRAME) {
if (cpi->common.current_frame.frame_type == KEY_FRAME) {
rcf = rc->rate_correction_factors[KF_STD];
} else if (cpi->oxcf.pass == 2) {
RATE_FACTOR_LEVEL rf_lvl =
@ -384,7 +383,7 @@ static void set_rate_correction_factor(AV1_COMP *cpi, double factor, int width,
factor = fclamp(factor, MIN_BPB_FACTOR, MAX_BPB_FACTOR);
if (cpi->common.frame_type == KEY_FRAME) {
if (cpi->common.current_frame.frame_type == KEY_FRAME) {
rc->rate_correction_factors[KF_STD] = factor;
} else if (cpi->oxcf.pass == 2) {
RATE_FACTOR_LEVEL rf_lvl =
@ -425,8 +424,8 @@ void av1_rc_update_rate_correction_factors(AV1_COMP *cpi, int width,
av1_cyclic_refresh_estimate_bits_at_q(cpi, rate_correction_factor);
} else {
projected_size_based_on_q = av1_estimate_bits_at_q(
cpi->common.frame_type, cm->base_qindex, MBs, rate_correction_factor,
cm->seq_params.bit_depth);
cpi->common.current_frame.frame_type, cm->base_qindex, MBs,
rate_correction_factor, cm->seq_params.bit_depth);
}
// Work out a size correction factor.
if (projected_size_based_on_q > FRAME_OVERHEAD_BITS)
@ -497,8 +496,9 @@ int av1_rc_regulate_q(const AV1_COMP *cpi, int target_bits_per_frame,
bits_per_mb_at_this_q =
(int)av1_cyclic_refresh_rc_bits_per_mb(cpi, i, correction_factor);
} else {
bits_per_mb_at_this_q = (int)av1_rc_bits_per_mb(
cm->frame_type, i, correction_factor, cm->seq_params.bit_depth);
bits_per_mb_at_this_q =
(int)av1_rc_bits_per_mb(cm->current_frame.frame_type, i,
correction_factor, cm->seq_params.bit_depth);
}
if (bits_per_mb_at_this_q <= target_bits_per_mb) {
@ -559,20 +559,18 @@ static int get_gf_active_quality(const RATE_CONTROL *const rc, int q,
arfgf_low_motion_minq, arfgf_high_motion_minq);
}
#if REDUCE_LAST_ALT_BOOST
static int get_gf_high_motion_quality(int q, aom_bit_depth_t bit_depth) {
int *arfgf_high_motion_minq;
ASSIGN_MINQ_TABLE(bit_depth, arfgf_high_motion_minq);
return arfgf_high_motion_minq[q];
}
#endif
static int calc_active_worst_quality_one_pass_vbr(const AV1_COMP *cpi) {
const RATE_CONTROL *const rc = &cpi->rc;
const unsigned int curr_frame = cpi->common.current_video_frame;
const unsigned int curr_frame = cpi->common.current_frame.frame_number;
int active_worst_quality;
if (cpi->common.frame_type == KEY_FRAME) {
if (cpi->common.current_frame.frame_type == KEY_FRAME) {
active_worst_quality =
curr_frame == 0 ? rc->worst_quality : rc->last_q[KEY_FRAME] * 2;
} else {
@ -604,13 +602,13 @@ static int calc_active_worst_quality_one_pass_cbr(const AV1_COMP *cpi) {
int adjustment = 0;
int active_worst_quality;
int ambient_qp;
if (cm->frame_type == KEY_FRAME) return rc->worst_quality;
if (cm->current_frame.frame_type == KEY_FRAME) return rc->worst_quality;
// For ambient_qp we use minimum of avg_frame_qindex[KEY_FRAME/INTER_FRAME]
// for the first few frames following key frame. These are both initialized
// to worst_quality and updated with (3/4, 1/4) average in postencode_update.
// So for first few frames following key, the qp of that key frame is weighted
// into the active_worst_quality setting.
ambient_qp = (cm->current_video_frame < 5)
ambient_qp = (cm->current_frame.frame_number < 5)
? AOMMIN(rc->avg_frame_qindex[INTER_FRAME],
rc->avg_frame_qindex[KEY_FRAME])
: rc->avg_frame_qindex[INTER_FRAME];
@ -650,6 +648,7 @@ static int rc_pick_q_and_bounds_one_pass_cbr(const AV1_COMP *cpi, int width,
int *top_index) {
const AV1_COMMON *const cm = &cpi->common;
const RATE_CONTROL *const rc = &cpi->rc;
const CurrentFrame *const current_frame = &cm->current_frame;
int active_best_quality;
int active_worst_quality = calc_active_worst_quality_one_pass_cbr(cpi);
int q;
@ -668,7 +667,7 @@ static int rc_pick_q_and_bounds_one_pass_cbr(const AV1_COMP *cpi, int width,
int delta_qindex = av1_compute_qdelta(rc, last_boosted_q,
(last_boosted_q * 0.75), bit_depth);
active_best_quality = AOMMAX(qindex + delta_qindex, rc->best_quality);
} else if (cm->current_video_frame > 0) {
} else if (current_frame->frame_number > 0) {
// not first frame of one pass and kf_boost is set
double q_adj_factor = 1.0;
double q_val;
@ -701,7 +700,7 @@ static int rc_pick_q_and_bounds_one_pass_cbr(const AV1_COMP *cpi, int width,
active_best_quality = get_gf_active_quality(rc, q, bit_depth);
} else {
// Use the lower of active_worst_quality and recent/average Q.
if (cm->current_video_frame > 1) {
if (current_frame->frame_number > 1) {
if (rc->avg_frame_qindex[INTER_FRAME] < active_worst_quality)
active_best_quality = rtc_minq[rc->avg_frame_qindex[INTER_FRAME]];
else
@ -724,18 +723,18 @@ static int rc_pick_q_and_bounds_one_pass_cbr(const AV1_COMP *cpi, int width,
*bottom_index = active_best_quality;
// Limit Q range for the adaptive loop.
if (cm->frame_type == KEY_FRAME && !rc->this_key_frame_forced &&
!(cm->current_video_frame == 0)) {
if (current_frame->frame_type == KEY_FRAME && !rc->this_key_frame_forced &&
!(current_frame->frame_number == 0)) {
int qdelta = 0;
aom_clear_system_state();
qdelta = av1_compute_qdelta_by_rate(&cpi->rc, cm->frame_type,
qdelta = av1_compute_qdelta_by_rate(&cpi->rc, current_frame->frame_type,
active_worst_quality, 2.0, bit_depth);
*top_index = active_worst_quality + qdelta;
*top_index = AOMMAX(*top_index, *bottom_index);
}
// Special case code to try and match quality with forced key frames
if (cm->frame_type == KEY_FRAME && rc->this_key_frame_forced) {
if (current_frame->frame_type == KEY_FRAME && rc->this_key_frame_forced) {
q = rc->last_boosted_qindex;
} else {
q = av1_rc_regulate_q(cpi, rc->this_frame_target, active_best_quality,
@ -756,10 +755,28 @@ static int rc_pick_q_and_bounds_one_pass_cbr(const AV1_COMP *cpi, int width,
return q;
}
static int gf_group_pyramid_level(const AV1_COMP *cpi) {
const GF_GROUP *gf_group = &cpi->twopass.gf_group;
int this_height = gf_group->pyramid_level[gf_group->index];
return this_height;
}
static int get_active_cq_level(const RATE_CONTROL *rc,
const AV1EncoderConfig *const oxcf) {
const AV1EncoderConfig *const oxcf,
int intra_only, int superres_denom) {
static const double cq_adjust_threshold = 0.1;
int active_cq_level = oxcf->cq_level;
(void)intra_only;
if (oxcf->rc_mode == AOM_CQ || oxcf->rc_mode == AOM_Q) {
// printf("Superres %d %d %d = %d\n", superres_denom, intra_only,
// rc->frames_to_key, !(intra_only && rc->frames_to_key <= 1));
if (oxcf->superres_mode == SUPERRES_QTHRESH &&
superres_denom != SCALE_NUMERATOR &&
!(intra_only && rc->frames_to_key <= 1)) {
active_cq_level =
AOMMAX(active_cq_level - ((superres_denom - SCALE_NUMERATOR) * 4), 0);
}
}
if (oxcf->rc_mode == AOM_CQ && rc->total_target_bits > 0) {
const double x = (double)rc->total_actual_bits / rc->total_target_bits;
if (x < cq_adjust_threshold) {
@ -774,8 +791,10 @@ static int rc_pick_q_and_bounds_one_pass_vbr(const AV1_COMP *cpi, int width,
int *top_index) {
const AV1_COMMON *const cm = &cpi->common;
const RATE_CONTROL *const rc = &cpi->rc;
const CurrentFrame *const current_frame = &cm->current_frame;
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
const int cq_level = get_active_cq_level(rc, oxcf);
const int cq_level = get_active_cq_level(rc, oxcf, frame_is_intra_only(cm),
cm->superres_scale_denominator);
int active_best_quality;
int active_worst_quality = calc_active_worst_quality_one_pass_vbr(cpi);
int q;
@ -849,12 +868,12 @@ static int rc_pick_q_and_bounds_one_pass_vbr(const AV1_COMP *cpi, int width,
0.70, 1.0, 0.85, 1.0 };
const int delta_qindex = av1_compute_qdelta(
rc, q_val,
q_val * delta_rate[cm->current_video_frame % FIXED_GF_INTERVAL],
q_val * delta_rate[current_frame->frame_number % FIXED_GF_INTERVAL],
bit_depth);
active_best_quality = AOMMAX(qindex + delta_qindex, rc->best_quality);
} else {
// Use the lower of active_worst_quality and recent/average Q.
active_best_quality = (cm->current_video_frame > 1)
active_best_quality = (current_frame->frame_number > 1)
? inter_minq[rc->avg_frame_qindex[INTER_FRAME]]
: inter_minq[rc->avg_frame_qindex[KEY_FRAME]];
// For the constrained quality mode we don't want
@ -878,14 +897,15 @@ static int rc_pick_q_and_bounds_one_pass_vbr(const AV1_COMP *cpi, int width,
{
int qdelta = 0;
aom_clear_system_state();
if (cm->frame_type == KEY_FRAME && !rc->this_key_frame_forced &&
!(cm->current_video_frame == 0)) {
qdelta = av1_compute_qdelta_by_rate(&cpi->rc, cm->frame_type,
if (current_frame->frame_type == KEY_FRAME && !rc->this_key_frame_forced &&
!(current_frame->frame_number == 0)) {
qdelta = av1_compute_qdelta_by_rate(&cpi->rc, current_frame->frame_type,
active_worst_quality, 2.0, bit_depth);
} else if (!rc->is_src_frame_alt_ref &&
(cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame)) {
qdelta = av1_compute_qdelta_by_rate(
&cpi->rc, cm->frame_type, active_worst_quality, 1.75, bit_depth);
qdelta =
av1_compute_qdelta_by_rate(&cpi->rc, current_frame->frame_type,
active_worst_quality, 1.75, bit_depth);
}
*top_index = active_worst_quality + qdelta;
*top_index = AOMMAX(*top_index, *bottom_index);
@ -894,7 +914,8 @@ static int rc_pick_q_and_bounds_one_pass_vbr(const AV1_COMP *cpi, int width,
if (oxcf->rc_mode == AOM_Q) {
q = active_best_quality;
// Special case code to try and match quality with forced key frames
} else if ((cm->frame_type == KEY_FRAME) && rc->this_key_frame_forced) {
} else if ((current_frame->frame_type == KEY_FRAME) &&
rc->this_key_frame_forced) {
q = rc->last_boosted_qindex;
} else {
q = av1_rc_regulate_q(cpi, rc->this_frame_target, active_best_quality,
@ -934,7 +955,8 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
const RATE_CONTROL *const rc = &cpi->rc;
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
const GF_GROUP *gf_group = &cpi->twopass.gf_group;
const int cq_level = get_active_cq_level(rc, oxcf);
const int cq_level = get_active_cq_level(rc, oxcf, frame_is_intra_only(cm),
cm->superres_scale_denominator);
int active_best_quality;
int active_worst_quality = cpi->twopass.active_worst_quality;
int q;
@ -942,18 +964,20 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
const int bit_depth = cm->seq_params.bit_depth;
ASSIGN_MINQ_TABLE(bit_depth, inter_minq);
#if CUSTOMIZED_GF
const int is_intrl_arf_boost =
gf_group->update_type[gf_group->index] == INTNL_ARF_UPDATE;
#else
const int is_intrl_arf_boost = cpi->refresh_alt2_ref_frame;
#endif // CUSTOMIZED_GF
if (frame_is_intra_only(cm)) {
// Handle the special case for key frames forced when we have reached
// the maximum key frame interval. Here force the Q to a range
// based on the ambient Q to reduce the risk of popping.
if (rc->this_key_frame_forced) {
if (rc->frames_to_key == 1 && oxcf->rc_mode == AOM_Q) {
// If the next frame is also a key frame or the current frame is the
// only frame in the sequence in AOM_Q mode, just use the cq_level
// as q.
active_best_quality = cq_level;
active_worst_quality = cq_level;
} else if (rc->this_key_frame_forced) {
// Handle the special case for key frames forced when we have reached
// the maximum key frame interval. Here force the Q to a range
// based on the ambient Q to reduce the risk of popping.
double last_boosted_q;
int delta_qindex;
int qindex;
@ -967,10 +991,13 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
active_worst_quality =
AOMMIN(qindex + delta_qindex, active_worst_quality);
} else {
// Increase the boost if the forced keyframe is a forward reference.
// These numbers were derived empirically.
const double boost_factor = cpi->oxcf.fwd_kf_enabled ? 0.25 : 0.50;
qindex = rc->last_boosted_qindex;
last_boosted_q = av1_convert_qindex_to_q(qindex, bit_depth);
delta_qindex = av1_compute_qdelta(rc, last_boosted_q,
last_boosted_q * 0.5, bit_depth);
delta_qindex = av1_compute_qdelta(
rc, last_boosted_q, last_boosted_q * boost_factor, bit_depth);
active_best_quality = AOMMAX(qindex + delta_qindex, rc->best_quality);
}
} else {
@ -982,6 +1009,10 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
active_best_quality =
get_kf_active_quality(rc, active_worst_quality, bit_depth);
if (cpi->twopass.kf_zeromotion_pct >= STATIC_KF_GROUP_THRESH) {
active_best_quality /= 3;
}
// Allow somewhat lower kf minq with small image formats.
if ((width * height) <= (352 * 288)) {
q_adj_factor -= 0.25;
@ -1011,33 +1042,30 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
// For constrained quality dont allow Q less than the cq level
if (oxcf->rc_mode == AOM_CQ) {
if (q < cq_level) q = cq_level;
#if USE_SYMM_MULTI_LAYER && MULTI_LVL_BOOST_VBR_CQ
active_best_quality = get_gf_active_quality(rc, q, bit_depth);
// Constrained quality use slightly lower active best.
active_best_quality = active_best_quality * 15 / 16;
if (gf_group->update_type[gf_group->index] == ARF_UPDATE ||
(is_intrl_arf_boost && !cpi->new_bwdref_update_rule)) {
#endif // USE_SYMM_MULTI_LAYER && MULTI_LVL_BOOST_VBR_CQ
active_best_quality = get_gf_active_quality(rc, q, bit_depth);
// Constrained quality use slightly lower active best.
active_best_quality = active_best_quality * 15 / 16;
#if REDUCE_LAST_ALT_BOOST
if (gf_group->update_type[gf_group->index] == ARF_UPDATE) {
const int min_boost = get_gf_high_motion_quality(q, bit_depth);
const int boost = min_boost - active_best_quality;
active_best_quality = min_boost - (int)(boost * rc->arf_boost_factor);
}
#endif
*arf_q = active_best_quality;
#if USE_SYMM_MULTI_LAYER && MULTI_LVL_BOOST_VBR_CQ
} else {
} else if (cpi->new_bwdref_update_rule && is_intrl_arf_boost) {
assert(rc->arf_q >= 0); // Ensure it is set to a valid value.
active_best_quality = rc->arf_q;
int this_height = gf_group->pyramid_level[gf_group->index];
int this_height = gf_group_pyramid_level(cpi);
while (this_height < gf_group->pyramid_height) {
active_best_quality = (active_best_quality + cq_level + 1) / 2;
++this_height;
}
}
#endif // USE_SYMM_MULTI_LAYER && MULTI_LVL_BOOST_VBR_CQ
} else if (oxcf->rc_mode == AOM_Q) {
if (!cpi->refresh_alt_ref_frame && !is_intrl_arf_boost) {
active_best_quality = cq_level;
@ -1045,50 +1073,41 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
if (gf_group->update_type[gf_group->index] == ARF_UPDATE) {
active_best_quality = get_gf_active_quality(rc, q, bit_depth);
*arf_q = active_best_quality;
#if REDUCE_LAST_ALT_BOOST
const int min_boost = get_gf_high_motion_quality(q, bit_depth);
const int boost = min_boost - active_best_quality;
active_best_quality = min_boost - (int)(boost * rc->arf_boost_factor);
#endif
} else {
assert(rc->arf_q >= 0); // Ensure it is set to a valid value.
active_best_quality = rc->arf_q;
}
#if USE_SYMM_MULTI_LAYER
if (cpi->new_bwdref_update_rule && is_intrl_arf_boost) {
int this_height = gf_group->pyramid_level[gf_group->index];
int this_height = gf_group_pyramid_level(cpi);
while (this_height < gf_group->pyramid_height) {
active_best_quality = (active_best_quality + cq_level + 1) / 2;
++this_height;
}
} else {
#endif
// Modify best quality for second level arfs. For mode AOM_Q this
// becomes the baseline frame q.
if (gf_group->rf_level[gf_group->index] == GF_ARF_LOW)
active_best_quality = (active_best_quality + cq_level + 1) / 2;
#if USE_SYMM_MULTI_LAYER
}
#endif
}
} else {
active_best_quality = get_gf_active_quality(rc, q, bit_depth);
#if REDUCE_LAST_ALT_BOOST
const int min_boost = get_gf_high_motion_quality(q, bit_depth);
const int boost = min_boost - active_best_quality;
active_best_quality = min_boost - (int)(boost * rc->arf_boost_factor);
#endif
#if USE_SYMM_MULTI_LAYER
if (cpi->new_bwdref_update_rule && is_intrl_arf_boost) {
int this_height = gf_group->pyramid_level[gf_group->index];
int this_height = gf_group_pyramid_level(cpi);
while (this_height < gf_group->pyramid_height) {
active_best_quality =
(active_best_quality + active_worst_quality + 1) / 2;
++this_height;
}
}
#endif
}
} else {
if (oxcf->rc_mode == AOM_Q) {
@ -1135,7 +1154,7 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
// Modify active_best_quality for downscaled normal frames.
if (av1_frame_scaled(cm) && !frame_is_kf_gf_arf(cpi)) {
int qdelta = av1_compute_qdelta_by_rate(
rc, cm->frame_type, active_best_quality, 2.0, bit_depth);
rc, cm->current_frame.frame_type, active_best_quality, 2.0, bit_depth);
active_best_quality =
AOMMAX(active_best_quality + qdelta, rc->best_quality);
}
@ -1145,7 +1164,9 @@ static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width,
active_worst_quality =
clamp(active_worst_quality, active_best_quality, rc->worst_quality);
if (oxcf->rc_mode == AOM_Q) {
if (oxcf->rc_mode == AOM_Q ||
(frame_is_intra_only(cm) && !rc->this_key_frame_forced &&
cpi->twopass.kf_zeromotion_pct >= STATIC_KF_GROUP_THRESH)) {
q = active_best_quality;
// Special case code to try and match quality with forced key frames.
} else if (frame_is_intra_only(cm) && rc->this_key_frame_forced) {
@ -1193,7 +1214,7 @@ int av1_rc_pick_q_and_bounds(AV1_COMP *cpi, int width, int height,
assert(cpi->oxcf.pass == 2 && "invalid encode pass");
GF_GROUP *gf_group = &cpi->twopass.gf_group;
int arf_q = 0;
int arf_q = -1; // Initialize to invalid value, for sanity check later.
q = rc_pick_q_and_bounds_two_pass(cpi, width, height, bottom_index,
top_index, &arf_q);
@ -1253,16 +1274,12 @@ static void update_alt_ref_frame_stats(AV1_COMP *cpi) {
static void update_golden_frame_stats(AV1_COMP *cpi) {
RATE_CONTROL *const rc = &cpi->rc;
#if CUSTOMIZED_GF
const TWO_PASS *const twopass = &cpi->twopass;
const GF_GROUP *const gf_group = &twopass->gf_group;
const int is_intrnl_arf =
cpi->oxcf.pass == 2
? gf_group->update_type[gf_group->index] == INTNL_ARF_UPDATE
: cpi->refresh_alt2_ref_frame;
#else
const int is_intnl_arf = cpi->refresh_alt2_ref_frame;
#endif
// Update the Golden frame usage counts.
// NOTE(weitinglin): If we use show_existing_frame for an OVERLAY frame,
@ -1288,19 +1305,42 @@ static void update_golden_frame_stats(AV1_COMP *cpi) {
}
}
void av1_estimate_qp_gop(AV1_COMP *cpi) {
AV1_COMMON *const cm = &cpi->common;
int gop_length = cpi->rc.baseline_gf_interval;
int bottom_index, top_index;
int idx;
const int gf_index = cpi->twopass.gf_group.index;
for (idx = 1; idx <= gop_length + 1 && idx < MAX_LAG_BUFFERS; ++idx) {
TplDepFrame *tpl_frame = &cpi->tpl_stats[idx];
int target_rate = cpi->twopass.gf_group.bit_allocation[idx];
int arf_q = 0;
cpi->twopass.gf_group.index = idx;
rc_set_frame_target(cpi, target_rate, cm->width, cm->height);
av1_configure_buffer_updates(
cpi, cpi->twopass.gf_group.update_type[cpi->twopass.gf_group.index]);
tpl_frame->base_qindex = rc_pick_q_and_bounds_two_pass(
cpi, cm->width, cm->height, &bottom_index, &top_index, &arf_q);
tpl_frame->base_qindex = AOMMAX(tpl_frame->base_qindex, 1);
}
// Reset the actual index and frame update
cpi->twopass.gf_group.index = gf_index;
av1_configure_buffer_updates(
cpi, cpi->twopass.gf_group.update_type[cpi->twopass.gf_group.index]);
}
void av1_rc_postencode_update(AV1_COMP *cpi, uint64_t bytes_used) {
const AV1_COMMON *const cm = &cpi->common;
const CurrentFrame *const current_frame = &cm->current_frame;
RATE_CONTROL *const rc = &cpi->rc;
#if CUSTOMIZED_GF
const TWO_PASS *const twopass = &cpi->twopass;
const GF_GROUP *const gf_group = &twopass->gf_group;
const int is_intrnl_arf =
cpi->oxcf.pass == 2
? gf_group->update_type[gf_group->index] == INTNL_ARF_UPDATE
: cpi->refresh_alt2_ref_frame;
#else
const int is_intrnl_arf = cpi->refresh_alt2_ref_frame;
#endif
const int qindex = cm->base_qindex;
@ -1315,7 +1355,7 @@ void av1_rc_postencode_update(AV1_COMP *cpi, uint64_t bytes_used) {
av1_rc_update_rate_correction_factors(cpi, cm->width, cm->height);
// Keep a record of last Q and ambient average Q.
if (cm->frame_type == KEY_FRAME) {
if (current_frame->frame_type == KEY_FRAME) {
rc->last_q[KEY_FRAME] = qindex;
rc->avg_frame_qindex[KEY_FRAME] =
ROUND_POWER_OF_TWO(3 * rc->avg_frame_qindex[KEY_FRAME] + qindex, 2);
@ -1341,13 +1381,14 @@ void av1_rc_postencode_update(AV1_COMP *cpi, uint64_t bytes_used) {
// If all mbs in this group are skipped only update if the Q value is
// better than that already stored.
// This is used to help set quality in forced key frames to reduce popping
if ((qindex < rc->last_boosted_qindex) || (cm->frame_type == KEY_FRAME) ||
if ((qindex < rc->last_boosted_qindex) ||
(current_frame->frame_type == KEY_FRAME) ||
(!rc->constrained_gf_group &&
(cpi->refresh_alt_ref_frame || is_intrnl_arf ||
(cpi->refresh_golden_frame && !rc->is_src_frame_alt_ref)))) {
rc->last_boosted_qindex = qindex;
}
if (cm->frame_type == KEY_FRAME) rc->last_kf_qindex = qindex;
if (current_frame->frame_type == KEY_FRAME) rc->last_kf_qindex = qindex;
update_buffer_level(cpi, rc->projected_frame_size);
@ -1357,7 +1398,7 @@ void av1_rc_postencode_update(AV1_COMP *cpi, uint64_t bytes_used) {
rc->this_frame_target =
(int)(rc->this_frame_target /
resize_rate_factor(cpi, cm->width, cm->height));
if (cm->frame_type != KEY_FRAME) {
if (current_frame->frame_type != KEY_FRAME) {
rc->rolling_target_bits = ROUND_POWER_OF_TWO(
rc->rolling_target_bits * 3 + rc->this_frame_target, 2);
rc->rolling_actual_bits = ROUND_POWER_OF_TWO(
@ -1378,15 +1419,15 @@ void av1_rc_postencode_update(AV1_COMP *cpi, uint64_t bytes_used) {
rc->total_target_vs_actual = rc->total_actual_bits - rc->total_target_bits;
if (is_altref_enabled(cpi) && cpi->refresh_alt_ref_frame &&
(cm->frame_type != KEY_FRAME))
(current_frame->frame_type != KEY_FRAME))
// Update the alternate reference frame stats as appropriate.
update_alt_ref_frame_stats(cpi);
else
// Update the Golden frame stats as appropriate.
update_golden_frame_stats(cpi);
if (cm->frame_type == KEY_FRAME) rc->frames_since_key = 0;
// if (cm->current_video_frame == 1 && cm->show_frame)
if (current_frame->frame_type == KEY_FRAME) rc->frames_since_key = 0;
// if (current_frame->frame_number == 1 && cm->show_frame)
/*
rc->this_frame_target =
(int)(rc->this_frame_target / resize_rate_factor(cpi, cm->width,
@ -1431,9 +1472,11 @@ static int calc_iframe_target_size_one_pass_vbr(const AV1_COMP *const cpi) {
return av1_rc_clamp_iframe_target_size(cpi, target);
}
void av1_rc_get_one_pass_vbr_params(AV1_COMP *cpi) {
void av1_rc_get_one_pass_vbr_params(AV1_COMP *cpi,
EncodeFrameParams *const frame_params) {
AV1_COMMON *const cm = &cpi->common;
RATE_CONTROL *const rc = &cpi->rc;
CurrentFrame *const current_frame = &cm->current_frame;
int target;
int altref_enabled = is_altref_enabled(cpi);
int sframe_dist = cpi->oxcf.sframe_dist;
@ -1441,44 +1484,44 @@ void av1_rc_get_one_pass_vbr_params(AV1_COMP *cpi) {
int sframe_enabled = cpi->oxcf.sframe_enabled;
// TODO(yaowu): replace the "auto_key && 0" below with proper decision logic.
if (!cpi->refresh_alt_ref_frame &&
(cm->current_video_frame == 0 || (cpi->frame_flags & FRAMEFLAGS_KEY) ||
rc->frames_to_key == 0 || (cpi->oxcf.auto_key && 0))) {
cm->frame_type = KEY_FRAME;
(current_frame->frame_number == 0 ||
(cpi->frame_flags & FRAMEFLAGS_KEY) || rc->frames_to_key == 0 ||
(cpi->oxcf.auto_key && 0))) {
frame_params->frame_type = KEY_FRAME;
rc->this_key_frame_forced =
cm->current_video_frame != 0 && rc->frames_to_key == 0;
current_frame->frame_number != 0 && rc->frames_to_key == 0;
rc->frames_to_key = cpi->oxcf.key_freq;
rc->kf_boost = DEFAULT_KF_BOOST;
rc->source_alt_ref_active = 0;
} else {
cm->frame_type = INTER_FRAME;
frame_params->frame_type = INTER_FRAME;
if (sframe_enabled) {
if (altref_enabled) {
if (sframe_mode == 1) {
// sframe_mode == 1: insert sframe if it matches altref frame.
if (cm->current_video_frame % sframe_dist == 0 &&
cm->frame_type != KEY_FRAME && cm->current_video_frame != 0 &&
cpi->refresh_alt_ref_frame) {
cm->frame_type = S_FRAME;
if (current_frame->frame_number % sframe_dist == 0 &&
current_frame->frame_number != 0 && cpi->refresh_alt_ref_frame) {
frame_params->frame_type = S_FRAME;
}
} else {
// sframe_mode != 1: if sframe will be inserted at the next available
// altref frame
if (cm->current_video_frame % sframe_dist == 0 &&
cm->frame_type != KEY_FRAME && cm->current_video_frame != 0) {
if (current_frame->frame_number % sframe_dist == 0 &&
current_frame->frame_number != 0) {
rc->sframe_due = 1;
}
if (rc->sframe_due && cpi->refresh_alt_ref_frame) {
cm->frame_type = S_FRAME;
frame_params->frame_type = S_FRAME;
rc->sframe_due = 0;
}
}
} else {
if (cm->current_video_frame % sframe_dist == 0 &&
cm->frame_type != KEY_FRAME && cm->current_video_frame != 0) {
cm->frame_type = S_FRAME;
if (current_frame->frame_number % sframe_dist == 0 &&
current_frame->frame_number != 0) {
frame_params->frame_type = S_FRAME;
}
}
}
@ -1501,7 +1544,7 @@ void av1_rc_get_one_pass_vbr_params(AV1_COMP *cpi) {
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ)
av1_cyclic_refresh_update_parameters(cpi);
if (cm->frame_type == KEY_FRAME)
if (frame_params->frame_type == KEY_FRAME)
target = calc_iframe_target_size_one_pass_vbr(cpi);
else
target = calc_pframe_target_size_one_pass_vbr(cpi);
@ -1550,7 +1593,7 @@ static int calc_pframe_target_size_one_pass_cbr(const AV1_COMP *cpi) {
static int calc_iframe_target_size_one_pass_cbr(const AV1_COMP *cpi) {
const RATE_CONTROL *rc = &cpi->rc;
int target;
if (cpi->common.current_video_frame == 0) {
if (cpi->common.current_frame.frame_number == 0) {
target = ((rc->starting_buffer_level / 2) > INT_MAX)
? INT_MAX
: (int)(rc->starting_buffer_level / 2);
@ -1567,21 +1610,24 @@ static int calc_iframe_target_size_one_pass_cbr(const AV1_COMP *cpi) {
return av1_rc_clamp_iframe_target_size(cpi, target);
}
void av1_rc_get_one_pass_cbr_params(AV1_COMP *cpi) {
void av1_rc_get_one_pass_cbr_params(AV1_COMP *cpi,
EncodeFrameParams *const frame_params) {
AV1_COMMON *const cm = &cpi->common;
RATE_CONTROL *const rc = &cpi->rc;
CurrentFrame *const current_frame = &cm->current_frame;
int target;
// TODO(yaowu): replace the "auto_key && 0" below with proper decision logic.
if ((cm->current_video_frame == 0 || (cpi->frame_flags & FRAMEFLAGS_KEY) ||
rc->frames_to_key == 0 || (cpi->oxcf.auto_key && 0))) {
cm->frame_type = KEY_FRAME;
if ((current_frame->frame_number == 0 ||
(cpi->frame_flags & FRAMEFLAGS_KEY) || rc->frames_to_key == 0 ||
(cpi->oxcf.auto_key && 0))) {
frame_params->frame_type = KEY_FRAME;
rc->this_key_frame_forced =
cm->current_video_frame != 0 && rc->frames_to_key == 0;
current_frame->frame_number != 0 && rc->frames_to_key == 0;
rc->frames_to_key = cpi->oxcf.key_freq;
rc->kf_boost = DEFAULT_KF_BOOST;
rc->source_alt_ref_active = 0;
} else {
cm->frame_type = INTER_FRAME;
frame_params->frame_type = INTER_FRAME;
}
if (rc->frames_till_gf_update_due == 0) {
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ)
@ -1602,7 +1648,7 @@ void av1_rc_get_one_pass_cbr_params(AV1_COMP *cpi) {
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ)
av1_cyclic_refresh_update_parameters(cpi);
if (cm->frame_type == KEY_FRAME)
if (frame_params->frame_type == KEY_FRAME)
target = calc_iframe_target_size_one_pass_cbr(cpi);
else
target = calc_pframe_target_size_one_pass_cbr(cpi);
@ -1674,15 +1720,10 @@ void av1_rc_set_gf_interval_range(const AV1_COMP *const cpi,
oxcf->width, oxcf->height, cpi->framerate);
if (rc->max_gf_interval == 0)
rc->max_gf_interval = av1_rc_get_default_max_gf_interval(
cpi->framerate, rc->min_gf_interval);
cpi->framerate, rc->min_gf_interval, oxcf->gf_max_pyr_height);
// Extended interval for genuinely static scenes
rc->static_scene_max_gf_interval = MAX_LAG_BUFFERS * 2;
if (is_altref_enabled(cpi)) {
if (rc->static_scene_max_gf_interval > oxcf->lag_in_frames - 1)
rc->static_scene_max_gf_interval = oxcf->lag_in_frames - 1;
}
// Extended max interval for genuinely static scenes like slide shows.
rc->static_scene_max_gf_interval = MAX_STATIC_GF_GROUP_LENGTH;
if (rc->max_gf_interval > rc->static_scene_max_gf_interval)
rc->max_gf_interval = rc->static_scene_max_gf_interval;
@ -1733,7 +1774,7 @@ static void vbr_rate_correction(AV1_COMP *cpi, int *this_frame_target) {
// This number is used to damp the per frame rate correction.
// Range 0 - 1.0
if (cpi->twopass.total_stats.count != 0.) {
position_factor = sqrt((double)cpi->common.current_video_frame /
position_factor = sqrt((double)cpi->common.current_frame.frame_number /
cpi->twopass.total_stats.count);
}
max_delta = (int)(position_factor *

View file

@ -16,6 +16,7 @@
#include "aom/aom_integer.h"
#include "av1/common/blockd.h"
#include "av1/common/onyxc_int.h"
#ifdef __cplusplus
extern "C" {
@ -24,48 +25,24 @@ extern "C" {
// Bits Per MB at different Q (Multiplied by 512)
#define BPER_MB_NORMBITS 9
#define CUSTOMIZED_GF 1
// Threshold used to define if a KF group is static (e.g. a slide show).
// Essentially, this means that no frame in the group has more than 1% of MBs
// that are not marked as coded with 0,0 motion in the first pass.
#define STATIC_KF_GROUP_THRESH 99
#define STATIC_KF_GROUP_FLOAT_THRESH 0.99
#if CONFIG_FIX_GF_LENGTH
#define FIXED_GF_LENGTH 16
// The maximum duration of a GF group that is static (e.g. a slide show).
#define MAX_STATIC_GF_GROUP_LENGTH 250
// Minimum and maximum height for the new pyramid structure.
// (Old structure supports height = 1, but does NOT support height = 4).
#define MIN_PYRAMID_LVL 2
#define MAX_PYRAMID_LVL 4
// We allow a frame to have at most two left/right descendants before changing
// them into to a subtree, i.e., we allow the following structure:
/* OUT_OF_ORDER_FRAME
/ / \ \
(two left children) F F F F (two right children) */
// Therefore the max gf size supported by 4 layer structure is
// 1 (KEY/OVERLAY) + 1 + 2 + 4 + 16 (two children on both side of their parent)
#define MAX_PYRAMID_SIZE 24
#define USE_SYMM_MULTI_LAYER 1
#define REDUCE_LAST_ALT_BOOST 1
#define REDUCE_LAST_GF_LENGTH 1
#define MULTI_LVL_BOOST_VBR_CQ 1
#else
#define USE_SYMM_MULTI_LAYER 0
#define REDUCE_LAST_ALT_BOOST 0
#define REDUCE_LAST_GF_LENGTH 0
#define MULTI_LVL_BOOST_VBR_CQ 0
#endif
#if USE_SYMM_MULTI_LAYER
#define USE_MANUAL_GF4_STRUCT 0
#endif
#define MIN_GF_INTERVAL 4
#define MAX_GF_INTERVAL 16
#define FIXED_GF_INTERVAL 8 // Used in some testing modes only
typedef enum {
INTER_NORMAL = 0,
INTER_LOW = 1,
INTER_HIGH = 2,
GF_ARF_LOW = 3,
GF_ARF_STD = 4,
KF_STD = 5,
RATE_FACTOR_LEVELS = 6
} RATE_FACTOR_LEVEL;
static const double rate_factor_deltas[RATE_FACTOR_LEVELS] = {
1.00, // INTER_NORMAL
0.80, // INTER_LOW
@ -93,7 +70,6 @@ typedef struct {
int last_kf_qindex; // Q index of the last key frame coded.
int gfu_boost;
int last_boost;
int kf_boost;
double rate_correction_factors[RATE_FACTOR_LEVELS];
@ -195,7 +171,10 @@ int av1_rc_get_default_min_gf_interval(int width, int height, double framerate);
// Note av1_rc_get_default_max_gf_interval() requires the min_gf_interval to
// be passed in to ensure that the max_gf_interval returned is at least as bis
// as that.
int av1_rc_get_default_max_gf_interval(double framerate, int min_frame_rate);
int av1_rc_get_default_max_gf_interval(double framerate, int min_frame_rate,
int max_pyr_height);
int av1_rc_get_fixed_gf_length(int max_pyr_height);
// Generally at the high level, the following flow is expected
// to be enforced for rate control:
@ -220,8 +199,11 @@ int av1_rc_get_default_max_gf_interval(double framerate, int min_frame_rate);
// Functions to set parameters for encoding before the actual
// encode_frame_to_data_rate() function.
void av1_rc_get_one_pass_vbr_params(struct AV1_COMP *cpi);
void av1_rc_get_one_pass_cbr_params(struct AV1_COMP *cpi);
struct EncodeFrameParams;
void av1_rc_get_one_pass_vbr_params(
struct AV1_COMP *cpi, struct EncodeFrameParams *const frame_params);
void av1_rc_get_one_pass_cbr_params(
struct AV1_COMP *cpi, struct EncodeFrameParams *const frame_params);
// Post encode update of the rate control parameters based
// on bytes used
@ -262,9 +244,6 @@ int av1_rc_clamp_iframe_target_size(const struct AV1_COMP *const cpi,
int target);
int av1_rc_clamp_pframe_target_size(const struct AV1_COMP *const cpi,
int target);
// Utility to set frame_target into the RATE_CONTROL structure
// This function is called only from the av1_rc_get_..._params() functions.
void av1_rc_set_frame_target(struct AV1_COMP *cpi, int target);
// Computes a q delta (in "q index" terms) to get from a starting q value
// to a target q value
@ -288,6 +267,8 @@ void av1_set_target_rate(struct AV1_COMP *cpi, int width, int height);
int av1_resize_one_pass_cbr(struct AV1_COMP *cpi);
void av1_estimate_qp_gop(struct AV1_COMP *cpi);
#ifdef __cplusplus
} // extern "C"
#endif

File diff suppressed because it is too large Load diff

View file

@ -48,7 +48,7 @@ extern "C" {
// This enumerator type needs to be kept aligned with the mode order in
// const MODE_DEFINITION av1_mode_order[MAX_MODES] used in the rd code.
typedef enum {
enum {
THR_NEARESTMV,
THR_NEARESTL2,
THR_NEARESTL3,
@ -246,9 +246,9 @@ typedef enum {
MAX_SINGLE_REF_MODES = LAST_SINGLE_REF_MODES + 1,
LAST_COMP_REF_MODES = THR_COMP_GLOBAL_GLOBALBA,
MAX_COMP_REF_MODES = LAST_COMP_REF_MODES + 1
} THR_MODES;
} UENUM1BYTE(THR_MODES);
typedef enum {
enum {
THR_LAST,
THR_LAST2,
THR_LAST3,
@ -275,7 +275,7 @@ typedef enum {
THR_INTRA,
MAX_REFS
} THR_MODES_SUB8X8;
} UENUM1BYTE(THR_MODES_SUB8X8);
typedef struct RD_OPT {
// Thresh_mult is used to set a threshold for the rd score. A higher value
@ -283,13 +283,14 @@ typedef struct RD_OPT {
// is used in combination with the current block size, and thresh_freq_fact
// to pick a threshold.
int thresh_mult[MAX_MODES];
int thresh_mult_sub8x8[MAX_REFS];
int threshes[MAX_SEGMENTS][BLOCK_SIZES_ALL][MAX_MODES];
int64_t prediction_type_threshes[REF_FRAMES][REFERENCE_MODES];
int RDMULT;
double r0;
} RD_OPT;
static INLINE void av1_init_rd_stats(RD_STATS *rd_stats) {
@ -317,6 +318,25 @@ static INLINE void av1_init_rd_stats(RD_STATS *rd_stats) {
}
}
#endif
#if CONFIG_ONE_PASS_SVM
rd_stats->eob = 0;
rd_stats->eob_0 = 0;
rd_stats->eob_1 = 0;
rd_stats->eob_2 = 0;
rd_stats->eob_3 = 0;
rd_stats->rd = 0;
rd_stats->rd_0 = 0;
rd_stats->rd_1 = 0;
rd_stats->rd_2 = 0;
rd_stats->rd_3 = 0;
rd_stats->y_sse = 0;
rd_stats->sse_0 = 0;
rd_stats->sse_1 = 0;
rd_stats->sse_2 = 0;
rd_stats->sse_3 = 0;
#endif
}
static INLINE void av1_invalid_rd_stats(RD_STATS *rd_stats) {
@ -344,6 +364,30 @@ static INLINE void av1_invalid_rd_stats(RD_STATS *rd_stats) {
}
}
#endif
#if CONFIG_ONE_PASS_SVM
// TODO(chiyotsai@google.com): Change invalid values to INT_MAX and
// INT64_MAX. Currently there are some code paths where rd_stats's properties
// are set directly without calling av1_init_rd_stats, so changing it now will
// break this speed feature. Need to hunt down all places where rd_stats is
// used without initialized.
rd_stats->eob = 0;
rd_stats->eob_0 = 0;
rd_stats->eob_1 = 0;
rd_stats->eob_2 = 0;
rd_stats->eob_3 = 0;
rd_stats->rd = 0;
rd_stats->rd_0 = 0;
rd_stats->rd_1 = 0;
rd_stats->rd_2 = 0;
rd_stats->rd_3 = 0;
rd_stats->y_sse = 0;
rd_stats->sse_0 = 0;
rd_stats->sse_1 = 0;
rd_stats->sse_2 = 0;
rd_stats->sse_3 = 0;
#endif
}
static INLINE void av1_merge_rd_stats(RD_STATS *rd_stats_dst,
@ -377,26 +421,245 @@ static INLINE void av1_merge_rd_stats(RD_STATS *rd_stats_dst,
}
}
#endif
#if CONFIG_ONE_PASS_SVM
rd_stats_dst->eob += rd_stats_src->eob;
rd_stats_dst->eob_0 += rd_stats_src->eob_0;
rd_stats_dst->eob_1 += rd_stats_src->eob_1;
rd_stats_dst->eob_2 += rd_stats_src->eob_2;
rd_stats_dst->eob_3 += rd_stats_src->eob_3;
rd_stats_dst->rd += rd_stats_src->rd;
rd_stats_dst->rd_0 += rd_stats_src->rd_0;
rd_stats_dst->rd_1 += rd_stats_src->rd_1;
rd_stats_dst->rd_2 += rd_stats_src->rd_2;
rd_stats_dst->rd_3 += rd_stats_src->rd_3;
rd_stats_dst->y_sse += rd_stats_src->y_sse;
rd_stats_dst->sse_0 += rd_stats_src->sse_0;
rd_stats_dst->sse_1 += rd_stats_src->sse_1;
rd_stats_dst->sse_2 += rd_stats_src->sse_2;
rd_stats_dst->sse_3 += rd_stats_src->sse_3;
#endif
}
#if CONFIG_ONE_PASS_SVM
static INLINE void av1_add_reg_stat(RD_STATS *rd_stats, int eob, int64_t rd,
int64_t sse, int blk_row, int blk_col,
BLOCK_SIZE bsize, BLOCK_SIZE crop_bsize) {
// NOTE: Currently the calculation of regional features works by assuming
// bsize is square so that each transform block of size crop_bsize either
// 1. locates completely within a quadrant or
// 2. is exactly half of bsize or
// 3. is the entire prediction block
// Size of TX block and SB
const int block_width_mi = mi_size_wide[bsize];
const int block_height_mi = mi_size_high[bsize];
const int crop_width_mi = mi_size_wide[crop_bsize];
const int crop_height_mi = mi_size_high[crop_bsize];
// Increment the eob proportionally to how much the tx_block overlaps with
// each quadrant. We will scale it by MAX_MIB_SIZE * MAX_MIB_SIZE to avoid
// being truncated.
const int max_scaling_factor = MAX_MIB_SIZE * MAX_MIB_SIZE;
// Update the stats
rd_stats->eob = eob;
rd_stats->rd = rd;
rd_stats->y_sse = sse;
if (crop_width_mi <= block_width_mi / 2 &&
crop_height_mi <= block_width_mi / 2) {
// The transform block lies completely in a quadrant.
const int scaling_factor = max_scaling_factor;
const int r_eob = eob * scaling_factor, r_rd = rd * scaling_factor,
r_sse = sse * scaling_factor;
if (blk_row < block_height_mi / 2 && blk_col < block_width_mi / 2) {
rd_stats->eob_0 = r_eob;
rd_stats->rd_0 = r_rd;
rd_stats->sse_0 = r_sse;
} else if (blk_row < block_height_mi / 2 && blk_col >= block_width_mi / 2) {
rd_stats->eob_1 = r_eob;
rd_stats->rd_1 = r_rd;
rd_stats->sse_1 = r_sse;
} else if (blk_row >= block_height_mi / 2 && blk_col < block_width_mi / 2) {
rd_stats->eob_2 = r_eob;
rd_stats->rd_2 = r_rd;
rd_stats->sse_2 = r_sse;
} else {
rd_stats->eob_3 = r_eob;
rd_stats->rd_3 = r_rd;
rd_stats->sse_3 = r_sse;
}
} else if (crop_height_mi == block_height_mi &&
crop_width_mi == block_width_mi) {
// The transform block is the whole prediction block
const int scaling_factor = max_scaling_factor;
const int r_eob = eob * scaling_factor, r_rd = rd * scaling_factor,
r_sse = sse * scaling_factor;
rd_stats->eob_0 = r_eob;
rd_stats->rd_0 = r_rd;
rd_stats->sse_0 = r_sse;
rd_stats->eob_1 = r_eob;
rd_stats->rd_1 = r_rd;
rd_stats->sse_1 = r_sse;
rd_stats->eob_2 = r_eob;
rd_stats->rd_2 = r_rd;
rd_stats->sse_2 = r_sse;
rd_stats->eob_3 = r_eob;
rd_stats->rd_3 = r_rd;
rd_stats->sse_3 = r_sse;
} else if (crop_height_mi == block_height_mi) {
// The tranform block is a vertical block
const int scaling_factor = max_scaling_factor / 2;
const int r_eob = eob * scaling_factor, r_rd = rd * scaling_factor,
r_sse = sse * scaling_factor;
if (blk_col < block_width_mi / 2) {
rd_stats->eob_0 = r_eob;
rd_stats->rd_0 = r_rd;
rd_stats->sse_0 = r_sse;
rd_stats->eob_2 = r_eob;
rd_stats->rd_2 = r_rd;
rd_stats->sse_2 = r_sse;
} else {
rd_stats->eob_1 = r_eob;
rd_stats->rd_1 = r_rd;
rd_stats->sse_1 = r_sse;
rd_stats->eob_3 = r_eob;
rd_stats->rd_3 = r_rd;
rd_stats->sse_3 = r_sse;
}
} else if (crop_width_mi == block_width_mi) {
// The tranform block is a horizontal block half the size of predition block
const int scaling_factor = max_scaling_factor / 2;
const int r_eob = eob * scaling_factor, r_rd = rd * scaling_factor,
r_sse = sse * scaling_factor;
if (blk_row < block_height_mi / 2) {
rd_stats->eob_0 = r_eob;
rd_stats->rd_0 = r_rd;
rd_stats->sse_0 = r_sse;
rd_stats->eob_1 = r_eob;
rd_stats->rd_1 = r_rd;
rd_stats->sse_1 = r_sse;
} else {
rd_stats->eob_2 = r_eob;
rd_stats->rd_2 = r_rd;
rd_stats->sse_2 = r_sse;
rd_stats->eob_3 = r_eob;
rd_stats->rd_3 = r_rd;
rd_stats->sse_3 = r_sse;
}
} else {
assert(0 && "Unexpected transform size");
}
}
static INLINE void av1_reg_stat_skipmode_update(RD_STATS *rd_stats,
int rdmult) {
// Update the stats
rd_stats->eob = 0;
rd_stats->eob_0 = 0;
rd_stats->eob_1 = 0;
rd_stats->eob_2 = 0;
rd_stats->eob_3 = 0;
rd_stats->rd = RDCOST(rdmult, 0, rd_stats->sse);
rd_stats->rd_0 = RDCOST(rdmult, 0, rd_stats->sse_0);
rd_stats->rd_1 = RDCOST(rdmult, 0, rd_stats->sse_1);
rd_stats->rd_2 = RDCOST(rdmult, 0, rd_stats->sse_2);
rd_stats->rd_3 = RDCOST(rdmult, 0, rd_stats->sse_3);
}
static INLINE void av1_copy_reg_stat(RD_STATS *rd_stats_dst,
RD_STATS *rd_stats_src) {
rd_stats_dst->eob = rd_stats_src->eob;
rd_stats_dst->eob_0 = rd_stats_src->eob_0;
rd_stats_dst->eob_1 = rd_stats_src->eob_1;
rd_stats_dst->eob_2 = rd_stats_src->eob_2;
rd_stats_dst->eob_3 = rd_stats_src->eob_3;
rd_stats_dst->rd = rd_stats_src->rd;
rd_stats_dst->rd_0 = rd_stats_src->rd_0;
rd_stats_dst->rd_1 = rd_stats_src->rd_1;
rd_stats_dst->rd_2 = rd_stats_src->rd_2;
rd_stats_dst->rd_3 = rd_stats_src->rd_3;
rd_stats_dst->y_sse = rd_stats_src->y_sse;
rd_stats_dst->sse_0 = rd_stats_src->sse_0;
rd_stats_dst->sse_1 = rd_stats_src->sse_1;
rd_stats_dst->sse_2 = rd_stats_src->sse_2;
rd_stats_dst->sse_3 = rd_stats_src->sse_3;
}
static INLINE void av1_unpack_reg_stat(RD_STATS *rd_stats, int *eob, int *eob_0,
int *eob_1, int *eob_2, int *eob_3,
int64_t *rd, int64_t *rd_0,
int64_t *rd_1, int64_t *rd_2,
int64_t *rd_3) {
*rd = rd_stats->rd;
*rd_0 = rd_stats->rd_0;
*rd_1 = rd_stats->rd_1;
*rd_2 = rd_stats->rd_2;
*rd_3 = rd_stats->rd_3;
*eob = rd_stats->eob;
*eob_0 = rd_stats->eob_0;
*eob_1 = rd_stats->eob_1;
*eob_2 = rd_stats->eob_2;
*eob_3 = rd_stats->eob_3;
}
static INLINE void av1_set_reg_stat(RD_STATS *rd_stats, int eob, int eob_0,
int eob_1, int eob_2, int eob_3, int64_t rd,
int64_t rd_0, int64_t rd_1, int64_t rd_2,
int64_t rd_3) {
rd_stats->rd = rd;
rd_stats->rd_0 = rd_0;
rd_stats->rd_1 = rd_1;
rd_stats->rd_2 = rd_2;
rd_stats->rd_3 = rd_3;
rd_stats->eob = eob;
rd_stats->eob_0 = eob_0;
rd_stats->eob_1 = eob_1;
rd_stats->eob_2 = eob_2;
rd_stats->eob_3 = eob_3;
}
#endif
struct TileInfo;
struct TileDataEnc;
struct AV1_COMP;
struct macroblock;
int av1_compute_rd_mult_based_on_qindex(const struct AV1_COMP *cpi, int qindex);
int av1_compute_rd_mult(const struct AV1_COMP *cpi, int qindex);
void av1_initialize_rd_consts(struct AV1_COMP *cpi);
void av1_initialize_cost_tables(const AV1_COMMON *const cm, MACROBLOCK *x);
void av1_initialize_me_consts(const struct AV1_COMP *cpi, MACROBLOCK *x,
int qindex);
void av1_model_rd_from_var_lapndz(int64_t var, unsigned int n,
unsigned int qstep, int *rate, int64_t *dist);
void av1_model_rd_curvfit(double xqr, double *rate_f, double *distbysse_f);
void av1_model_rd_surffit(double xm, double yl, double *rate_f,
double *distbysse_f);
void av1_model_rd_curvfit(BLOCK_SIZE bsize, double sse_norm, double xqr,
double *rate_f, double *distbysse_f);
void av1_model_rd_surffit(BLOCK_SIZE bsize, double sse_norm, double xm,
double yl, double *rate_f, double *distbysse_f);
int av1_get_switchable_rate(const AV1_COMMON *const cm, MACROBLOCK *x,
const MACROBLOCKD *xd);
@ -421,8 +684,6 @@ void av1_get_entropy_contexts(BLOCK_SIZE bsize,
void av1_set_rd_speed_thresholds(struct AV1_COMP *cpi);
void av1_set_rd_speed_thresholds_sub8x8(struct AV1_COMP *cpi);
void av1_update_rd_thresh_fact(const AV1_COMMON *const cm,
int (*fact)[MAX_MODES], int rd_thresh, int bsize,
int best_mode_index);
@ -457,6 +718,8 @@ void av1_fill_mode_rates(AV1_COMMON *const cm, MACROBLOCK *x,
void av1_fill_coeff_costs(MACROBLOCK *x, FRAME_CONTEXT *fc,
const int num_planes);
int av1_get_adaptive_rdmult(const struct AV1_COMP *cpi, double beta);
#ifdef __cplusplus
} // extern "C"
#endif

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,8 @@
#ifndef AOM_AV1_ENCODER_RDOPT_H_
#define AOM_AV1_ENCODER_RDOPT_H_
#include <stdbool.h>
#include "av1/common/blockd.h"
#include "av1/common/txb_common.h"
@ -126,10 +128,31 @@ void av1_rd_pick_inter_mode_sb_seg_skip(
struct macroblock *x, int mi_row, int mi_col, struct RD_STATS *rd_cost,
BLOCK_SIZE bsize, PICK_MODE_CONTEXT *ctx, int64_t best_rd_so_far);
#if CONFIG_COLLECT_INTER_MODE_RD_STATS
/** Returns an integer indicating the strength of the edge.
* 0 means no edge found, 556 is the strength of a solid black/white edge,
* and the number may range higher if the signal is even stronger (e.g., on a
* corner). high_bd is a bool indicating the source should be treated
* as a 16-bit array. bd is the bit depth.
*/
uint16_t av1_edge_exists(const uint8_t *src, int src_stride, int w, int h,
bool high_bd, int bd);
/** Applies a Gaussian blur with sigma = 1.3. Used by av1_edge_exists and
* tests.
*/
void gaussian_blur(const uint8_t *src, int src_stride, int w, int h,
uint8_t *dst, bool high_bd, int bd);
/* Applies standard 3x3 Sobel matrix. */
typedef struct {
int16_t x;
int16_t y;
} sobel_xy;
sobel_xy sobel(const uint8_t *input, int stride, int i, int j, bool high_bd);
void av1_inter_mode_data_init(struct TileDataEnc *tile_data);
void av1_inter_mode_data_fit(TileDataEnc *tile_data, int rdmult);
#endif
#ifdef __cplusplus
} // extern "C"

View file

@ -138,26 +138,28 @@ static INLINE void build_inter_predictors(const AV1_COMMON *cm, MACROBLOCKD *xd,
assert(bw < 8 || bh < 8);
ConvolveParams conv_params = get_conv_params_no_round(
0, plane, xd->tmp_conv_dst, tmp_dst_stride, is_compound, xd->bd);
conv_params.use_jnt_comp_avg = 0;
conv_params.use_dist_wtd_comp_avg = 0;
struct buf_2d *const dst_buf = &pd->dst;
uint8_t *dst = dst_buf->buf + dst_buf->stride * y + x;
ref = 0;
const RefBuffer *ref_buf =
&cm->frame_refs[this_mbmi->ref_frame[ref] - LAST_FRAME];
const RefCntBuffer *ref_buf =
get_ref_frame_buf(cm, this_mbmi->ref_frame[ref]);
const struct scale_factors *ref_scale_factors =
get_ref_scale_factors_const(cm, this_mbmi->ref_frame[ref]);
pd->pre[ref].buf0 =
(plane == 1) ? ref_buf->buf->u_buffer : ref_buf->buf->v_buffer;
(plane == 1) ? ref_buf->buf.u_buffer : ref_buf->buf.v_buffer;
pd->pre[ref].buf =
pd->pre[ref].buf0 + scaled_buffer_offset(pre_x, pre_y,
ref_buf->buf->uv_stride,
&ref_buf->sf);
pd->pre[ref].width = ref_buf->buf->uv_crop_width;
pd->pre[ref].height = ref_buf->buf->uv_crop_height;
pd->pre[ref].stride = ref_buf->buf->uv_stride;
ref_buf->buf.uv_stride,
ref_scale_factors);
pd->pre[ref].width = ref_buf->buf.uv_crop_width;
pd->pre[ref].height = ref_buf->buf.uv_crop_height;
pd->pre[ref].stride = ref_buf->buf.uv_stride;
const struct scale_factors *const sf =
is_intrabc ? &cm->sf_identity : &ref_buf->sf;
is_intrabc ? &cm->sf_identity : ref_scale_factors;
struct buf_2d *const pre_buf = is_intrabc ? dst_buf : &pd->pre[ref];
const MV mv = this_mbmi->mv[ref].as_mv;
@ -194,15 +196,15 @@ static INLINE void build_inter_predictors(const AV1_COMMON *cm, MACROBLOCKD *xd,
{
ConvolveParams conv_params = get_conv_params_no_round(
0, plane, xd->tmp_conv_dst, MAX_SB_SIZE, is_compound, xd->bd);
av1_jnt_comp_weight_assign(cm, mi, 0, &conv_params.fwd_offset,
&conv_params.bck_offset,
&conv_params.use_jnt_comp_avg, is_compound);
av1_dist_wtd_comp_weight_assign(
cm, mi, 0, &conv_params.fwd_offset, &conv_params.bck_offset,
&conv_params.use_dist_wtd_comp_avg, is_compound);
struct buf_2d *const dst_buf = &pd->dst;
uint8_t *const dst = dst_buf->buf;
for (ref = 0; ref < 1 + is_compound; ++ref) {
const struct scale_factors *const sf =
is_intrabc ? &cm->sf_identity : &xd->block_refs[ref]->sf;
is_intrabc ? &cm->sf_identity : xd->block_ref_scale_factors[ref];
struct buf_2d *const pre_buf = is_intrabc ? dst_buf : &pd->pre[ref];
const MV mv = mi->mv[ref].as_mv;
@ -235,46 +237,19 @@ static INLINE void build_inter_predictors(const AV1_COMMON *cm, MACROBLOCKD *xd,
}
}
static void build_inter_predictors_for_planes(const AV1_COMMON *cm,
MACROBLOCKD *xd, BLOCK_SIZE bsize,
int mi_row, int mi_col,
int plane_from, int plane_to) {
int plane;
static void build_inter_predictors_for_plane(const AV1_COMMON *cm,
MACROBLOCKD *xd, int mi_row,
int mi_col, const BUFFER_SET *ctx,
BLOCK_SIZE bsize, int plane_idx) {
const struct macroblockd_plane *pd = &xd->plane[plane_idx];
if (!is_chroma_reference(mi_row, mi_col, bsize, pd->subsampling_x,
pd->subsampling_y))
return;
const int mi_x = mi_col * MI_SIZE;
const int mi_y = mi_row * MI_SIZE;
for (plane = plane_from; plane <= plane_to; ++plane) {
const struct macroblockd_plane *pd = &xd->plane[plane];
const int bw = pd->width;
const int bh = pd->height;
if (!is_chroma_reference(mi_row, mi_col, bsize, pd->subsampling_x,
pd->subsampling_y))
continue;
build_inter_predictors(cm, xd, plane, xd->mi[0], 0, bw, bh, mi_x, mi_y);
}
}
void av1_build_inter_predictors_sby(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize) {
av1_build_inter_predictors_sbp(cm, xd, mi_row, mi_col, ctx, bsize, 0);
}
void av1_build_inter_predictors_sbuv(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize) {
for (int plane_idx = 1; plane_idx < MAX_MB_PLANE; plane_idx++) {
av1_build_inter_predictors_sbp(cm, xd, mi_row, mi_col, ctx, bsize,
plane_idx);
}
}
void av1_build_inter_predictors_sbp(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize, int plane_idx) {
build_inter_predictors_for_planes(cm, xd, bsize, mi_row, mi_col, plane_idx,
plane_idx);
build_inter_predictors(cm, xd, plane_idx, xd->mi[0], 0, pd->width, pd->height,
mi_x, mi_y);
if (is_interintra_pred(xd->mi[0])) {
BUFFER_SET default_ctx = { { NULL, NULL, NULL }, { 0, 0, 0 } };
@ -289,13 +264,14 @@ void av1_build_inter_predictors_sbp(const AV1_COMMON *cm, MACROBLOCKD *xd,
}
}
void av1_build_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize) {
const int num_planes = av1_num_planes(cm);
av1_build_inter_predictors_sby(cm, xd, mi_row, mi_col, ctx, bsize);
if (num_planes > 1)
av1_build_inter_predictors_sbuv(cm, xd, mi_row, mi_col, ctx, bsize);
void av1_enc_build_inter_predictor(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col,
const BUFFER_SET *ctx, BLOCK_SIZE bsize,
int plane_from, int plane_to) {
for (int plane_idx = plane_from; plane_idx <= plane_to; ++plane_idx) {
build_inter_predictors_for_plane(cm, xd, mi_row, mi_col, ctx, bsize,
plane_idx);
}
}
// TODO(sarahparker):
@ -308,7 +284,7 @@ void av1_build_inter_predictor(const uint8_t *src, int src_stride, uint8_t *dst,
InterpFilters interp_filters,
const WarpTypesAllowed *warp_types, int p_col,
int p_row, int plane, int ref,
enum mv_precision precision, int x, int y,
mv_precision precision, int x, int y,
const MACROBLOCKD *xd, int can_use_previous) {
const int is_q4 = precision == MV_PRECISION_Q4;
const MV mv_q4 = { is_q4 ? src_mv->row : src_mv->row * 2,
@ -338,7 +314,7 @@ static INLINE void build_prediction_by_above_pred(
MB_MODE_INFO backup_mbmi = *above_mbmi;
av1_setup_build_prediction_by_above_pred(xd, rel_mi_col, above_mi_width,
above_mbmi, ctxt, num_planes);
&backup_mbmi, ctxt, num_planes);
mi_x = above_mi_col << MI_SIZE_LOG2;
mi_y = ctxt->mi_row << MI_SIZE_LOG2;
@ -351,9 +327,9 @@ static INLINE void build_prediction_by_above_pred(
block_size_high[BLOCK_64X64] >> (pd->subsampling_y + 1));
if (av1_skip_u4x4_pred_in_obmc(bsize, pd, 0)) continue;
build_inter_predictors(ctxt->cm, xd, j, above_mbmi, 1, bw, bh, mi_x, mi_y);
build_inter_predictors(ctxt->cm, xd, j, &backup_mbmi, 1, bw, bh, mi_x,
mi_y);
}
*above_mbmi = backup_mbmi;
}
void av1_build_prediction_by_above_preds(const AV1_COMMON *cm, MACROBLOCKD *xd,
@ -394,7 +370,7 @@ static INLINE void build_prediction_by_left_pred(
MB_MODE_INFO backup_mbmi = *left_mbmi;
av1_setup_build_prediction_by_left_pred(xd, rel_mi_row, left_mi_height,
left_mbmi, ctxt, num_planes);
&backup_mbmi, ctxt, num_planes);
mi_x = ctxt->mi_col << MI_SIZE_LOG2;
mi_y = left_mi_row << MI_SIZE_LOG2;
const BLOCK_SIZE bsize = xd->mi[0]->sb_type;
@ -406,9 +382,9 @@ static INLINE void build_prediction_by_left_pred(
int bh = (left_mi_height << MI_SIZE_LOG2) >> pd->subsampling_y;
if (av1_skip_u4x4_pred_in_obmc(bsize, pd, 1)) continue;
build_inter_predictors(ctxt->cm, xd, j, left_mbmi, 1, bw, bh, mi_x, mi_y);
build_inter_predictors(ctxt->cm, xd, j, &backup_mbmi, 1, bw, bh, mi_x,
mi_y);
}
*left_mbmi = backup_mbmi;
}
void av1_build_prediction_by_left_preds(const AV1_COMMON *cm, MACROBLOCKD *xd,
@ -451,7 +427,7 @@ void av1_build_obmc_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
int dst_height1[MAX_MB_PLANE] = { MAX_SB_SIZE, MAX_SB_SIZE, MAX_SB_SIZE };
int dst_height2[MAX_MB_PLANE] = { MAX_SB_SIZE, MAX_SB_SIZE, MAX_SB_SIZE };
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
if (is_cur_buf_hbd(xd)) {
int len = sizeof(uint16_t);
dst_buf1[0] = CONVERT_TO_BYTEPTR(xd->tmp_obmc_bufs[0]);
dst_buf1[1] =
@ -475,7 +451,7 @@ void av1_build_obmc_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
dst_width1, dst_height1, dst_stride1);
av1_build_prediction_by_left_preds(cm, xd, mi_row, mi_col, dst_buf2,
dst_width2, dst_height2, dst_stride2);
av1_setup_dst_planes(xd->plane, xd->mi[0]->sb_type, get_frame_new_buffer(cm),
av1_setup_dst_planes(xd->plane, xd->mi[0]->sb_type, &cm->cur_frame->buf,
mi_row, mi_col, 0, num_planes);
av1_build_obmc_inter_prediction(cm, xd, mi_row, mi_col, dst_buf1, dst_stride1,
dst_buf2, dst_stride2);
@ -492,7 +468,7 @@ static void build_inter_predictors_single_buf(MACROBLOCKD *xd, int plane,
struct macroblockd_plane *const pd = &xd->plane[plane];
const MB_MODE_INFO *mi = xd->mi[0];
const struct scale_factors *const sf = &xd->block_refs[ref]->sf;
const struct scale_factors *const sf = xd->block_ref_scale_factors[ref];
struct buf_2d *const pre_buf = &pd->pre[ref];
uint8_t *const dst = get_buf_by_bd(xd, ext_dst) + ext_dst_stride * y + x;
const MV mv = mi->mv[ref].as_mv;
@ -574,37 +550,41 @@ static void build_wedge_inter_predictor_from_buf(
uint8_t *const dst = dst_buf->buf + dst_buf->stride * y + x;
mbmi->interinter_comp.seg_mask = xd->seg_mask;
const INTERINTER_COMPOUND_DATA *comp_data = &mbmi->interinter_comp;
const int is_hbd = is_cur_buf_hbd(xd);
if (is_compound && is_masked_compound_type(comp_data->type)) {
if (!plane && comp_data->type == COMPOUND_DIFFWTD) {
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH)
if (is_hbd) {
av1_build_compound_diffwtd_mask_highbd(
comp_data->seg_mask, comp_data->mask_type,
CONVERT_TO_BYTEPTR(ext_dst0), ext_dst_stride0,
CONVERT_TO_BYTEPTR(ext_dst1), ext_dst_stride1, h, w, xd->bd);
else
} else {
av1_build_compound_diffwtd_mask(
comp_data->seg_mask, comp_data->mask_type, ext_dst0,
ext_dst_stride0, ext_dst1, ext_dst_stride1, h, w);
}
}
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH)
if (is_hbd) {
build_masked_compound_highbd(
dst, dst_buf->stride, CONVERT_TO_BYTEPTR(ext_dst0), ext_dst_stride0,
CONVERT_TO_BYTEPTR(ext_dst1), ext_dst_stride1, comp_data,
mbmi->sb_type, h, w, xd->bd);
else
} else {
build_masked_compound(dst, dst_buf->stride, ext_dst0, ext_dst_stride0,
ext_dst1, ext_dst_stride1, comp_data, mbmi->sb_type,
h, w);
}
} else {
if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH)
if (is_hbd) {
aom_highbd_convolve_copy(CONVERT_TO_BYTEPTR(ext_dst0), ext_dst_stride0,
dst, dst_buf->stride, NULL, 0, NULL, 0, w, h,
xd->bd);
else
} else {
aom_convolve_copy(ext_dst0, ext_dst_stride0, dst, dst_buf->stride, NULL,
0, NULL, 0, w, h);
}
}
}

View file

@ -23,21 +23,10 @@
extern "C" {
#endif
void av1_build_inter_predictors_sby(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize);
void av1_build_inter_predictors_sbuv(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize);
void av1_build_inter_predictors_sbp(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize, int plane_idx);
void av1_build_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col, BUFFER_SET *ctx,
BLOCK_SIZE bsize);
void av1_enc_build_inter_predictor(const AV1_COMMON *cm, MACROBLOCKD *xd,
int mi_row, int mi_col,
const BUFFER_SET *ctx, BLOCK_SIZE bsize,
int plane_from, int plane_to);
void av1_build_inter_predictor(const uint8_t *src, int src_stride, uint8_t *dst,
int dst_stride, const MV *src_mv,
@ -46,7 +35,7 @@ void av1_build_inter_predictor(const uint8_t *src, int src_stride, uint8_t *dst,
InterpFilters interp_filters,
const WarpTypesAllowed *warp_types, int p_col,
int p_row, int plane, int ref,
enum mv_precision precision, int x, int y,
mv_precision precision, int x, int y,
const MACROBLOCKD *xd, int can_use_previous);
// Detect if the block have sub-pixel level motion vectors

View file

@ -62,7 +62,7 @@ static void count_segs(const AV1_COMMON *cm, MACROBLOCKD *xd,
no_pred_segcounts[segment_id]++;
// Temporal prediction not allowed on key frames
if (cm->frame_type != KEY_FRAME) {
if (cm->current_frame.frame_type != KEY_FRAME) {
const BLOCK_SIZE bsize = xd->mi[0]->sb_type;
// Test to see if the segment id matches the predicted value.
const int pred_segment_id =

View file

@ -24,6 +24,8 @@
#define DISABLE_TRELLISQ_SEARCH 0
#define MAX_MESH_SPEED 5 // Max speed setting for mesh motion method
// Max speed setting for tx domain evaluation
#define MAX_TX_DOMAIN_EVAL_SPEED 5
static MESH_PATTERN
good_quality_mesh_patterns[MAX_MESH_SPEED + 1][MAX_MESH_STEP] = {
{ { 64, 8 }, { 28, 4 }, { 15, 1 }, { 7, 1 } },
@ -50,6 +52,22 @@ static MESH_PATTERN intrabc_mesh_patterns[MAX_MESH_SPEED + 1][MAX_MESH_STEP] = {
static uint8_t intrabc_max_mesh_pct[MAX_MESH_SPEED + 1] = { 100, 100, 100,
25, 25, 10 };
// Threshold values to be used for pruning the txfm_domain_distortion
// based on block MSE
// TODO(any): Experiment the threshold logic based on variance metric
static unsigned int tx_domain_dist_thresholds[MAX_TX_DOMAIN_EVAL_SPEED + 1] = {
UINT_MAX, 162754, 22026, 22026, 22026, 0
};
// Threshold values to be used for disabling coeff RD-optimization
// based on block MSE
// TODO(any): Experiment the threshold logic based on variance metric
static unsigned int coeff_opt_dist_thresholds[5] = { UINT_MAX, 162754, 162754,
22026, 22026 };
// scaling values to be used for gating wedge/compound segment based on best
// approximate rd
static int comp_type_rd_threshold_mul[3] = { 1, 11, 12 };
static int comp_type_rd_threshold_div[3] = { 3, 16, 16 };
// Intra only frames, golden frames (except alt ref overlays) and
// alt ref frames tend to be coded at a higher than ambient quality
static int frame_is_boosted(const AV1_COMP *cpi) {
@ -62,7 +80,7 @@ static int frame_is_boosted(const AV1_COMP *cpi) {
// partly on the screen area that over which they propogate. Propogation is
// limited by transform block size but the screen area take up by a given block
// size will be larger for a small image format stretched to full screen.
static BLOCK_SIZE set_partition_min_limit(AV1_COMMON *const cm) {
static BLOCK_SIZE set_partition_min_limit(const AV1_COMMON *const cm) {
unsigned int screen_area = (cm->width * cm->height);
// Select block size based on image format size.
@ -85,10 +103,9 @@ static int has_internal_image_edge(const AV1_COMP *cpi) {
(cpi->twopass.this_frame_stats.inactive_zone_cols > 0));
}
static void set_good_speed_feature_framesize_dependent(AV1_COMP *cpi,
SPEED_FEATURES *sf,
int speed) {
AV1_COMMON *const cm = &cpi->common;
static void set_good_speed_feature_framesize_dependent(
const AV1_COMP *const cpi, SPEED_FEATURES *const sf, int speed) {
const AV1_COMMON *const cm = &cpi->common;
const int is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720;
const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480;
@ -126,6 +143,17 @@ static void set_good_speed_feature_framesize_dependent(AV1_COMP *cpi,
}
if (speed >= 2) {
if (is_720p_or_larger) {
sf->use_square_partition_only_threshold = BLOCK_64X64;
} else if (is_480p_or_larger) {
sf->use_square_partition_only_threshold = BLOCK_32X32;
} else {
// TODO(chiyotsai@google.com): Setting the threshold to BLOCK_16X16 incurs
// a large loss (about 0.584%). Try increasing the threshold on boosted
// frame and see if it improves the performance.
sf->use_square_partition_only_threshold = BLOCK_32X32;
}
if (is_720p_or_larger) {
sf->disable_split_mask =
cm->show_frame ? DISABLE_ALL_SPLIT : DISABLE_ALL_INTER_SPLIT;
@ -172,11 +200,12 @@ static void set_good_speed_feature_framesize_dependent(AV1_COMP *cpi,
}
}
static void set_good_speed_features_framesize_independent(AV1_COMP *cpi,
SPEED_FEATURES *sf,
int speed) {
AV1_COMMON *const cm = &cpi->common;
static void set_good_speed_features_framesize_independent(
const AV1_COMP *const cpi, SPEED_FEATURES *const sf, int speed) {
const AV1_COMMON *const cm = &cpi->common;
const int boosted = frame_is_boosted(cpi);
const int is_boosted_arf2_bwd_type =
boosted || cpi->refresh_bwd_ref_frame || cpi->refresh_alt2_ref_frame;
// Speed 0 for all speed features that give neutral coding performance change.
sf->reduce_inter_modes = 1;
@ -184,54 +213,79 @@ static void set_good_speed_features_framesize_independent(AV1_COMP *cpi,
sf->ml_prune_rect_partition = 1;
sf->ml_prune_ab_partition = 1;
sf->ml_prune_4_partition = 1;
sf->simple_motion_search_prune_rect = 1;
sf->adaptive_txb_search_level = 1;
sf->jnt_comp_skip_mv_search = 1;
sf->use_dist_wtd_comp_flag = DIST_WTD_COMP_SKIP_MV_SEARCH;
sf->model_based_prune_tx_search_level = 1;
sf->model_based_post_interp_filter_breakout = 1;
sf->model_based_motion_mode_rd_breakout = 1;
// TODO(debargha): Test, tweak and turn on either 1 or 2
sf->inter_mode_rd_model_estimation = 1;
sf->prune_ref_frame_for_rect_partitions =
!(boosted || cpi->refresh_bwd_ref_frame || cpi->refresh_alt2_ref_frame);
sf->prune_ref_frame_for_rect_partitions = !is_boosted_arf2_bwd_type;
sf->prune_ref_mode_for_partitions = sf->prune_ref_frame_for_rect_partitions;
sf->less_rectangular_check_level = 1;
sf->gm_search_type = GM_REDUCED_REF_SEARCH;
sf->gm_search_type = GM_REDUCED_REF_SEARCH_SKIP_L2_L3;
sf->gm_disable_recode = 1;
sf->use_fast_interpolation_filter_search = 1;
sf->intra_tx_size_search_init_depth_sqr = 1;
sf->intra_angle_estimation = 1;
sf->selective_ref_frame = 1;
sf->prune_wedge_pred_diff_based = 1;
sf->disable_wedge_search_var_thresh = 0;
sf->disable_wedge_search_edge_thresh = 0;
sf->prune_motion_mode_level = 1;
sf->cb_pred_filter_search = 0;
if (speed >= 1) {
sf->gm_erroradv_type = GM_ERRORADV_TR_1;
sf->selective_ref_frame = 1;
sf->selective_ref_frame = 2;
sf->inter_tx_size_search_init_depth_rect = 1;
sf->inter_tx_size_search_init_depth_sqr = 1;
sf->intra_tx_size_search_init_depth_rect = 1;
sf->intra_tx_size_search_init_depth_sqr = 1;
sf->tx_size_search_lgr_block = 1;
if (speed >= CONFIG_2PASS_PARTITION_SEARCH_LVL) {
sf->two_pass_partition_search = 1;
sf->mode_pruning_based_on_two_pass_partition_search = 1;
}
sf->prune_ext_partition_types_search_level = 2;
sf->use_fast_interpolation_filter_search = 1;
sf->skip_repeat_interpolation_filter_search = 1;
sf->tx_type_search.skip_tx_search = 1;
sf->tx_type_search.ml_tx_split_thresh = 40;
sf->model_based_prune_tx_search_level = 0;
sf->model_based_post_interp_filter_breakout = 0;
// TODO(angiebird): Re-evaluate the impact of inter_mode_rd_model_estimation
// on speed 1
sf->inter_mode_rd_model_estimation = 0;
sf->adaptive_txb_search_level = 2;
sf->use_intra_txb_hash = 1;
sf->optimize_b_precheck = 1;
sf->dual_sgr_penalty_level = 1;
sf->use_accurate_subpel_search = 1;
sf->use_accurate_subpel_search = USE_4_TAPS;
sf->reuse_inter_intra_mode = 1;
sf->prune_comp_search_by_single_result = 1;
sf->skip_repeated_newmv = 1;
sf->obmc_full_pixel_search_level = 1;
// TODO(anyone): Following speed feature will be further explored to
// identify the appropriate tradeoff between encoder performance and its
// speed.
sf->prune_single_motion_modes_by_simple_trans = 1;
sf->simple_motion_search_split_only = 1;
sf->disable_wedge_search_var_thresh = 0;
sf->disable_wedge_search_edge_thresh = 0;
sf->disable_interinter_wedge_newmv_search = boosted ? 0 : 1;
sf->prune_comp_type_by_comp_avg = 1;
sf->prune_motion_mode_level = 2;
sf->gm_search_type = GM_REDUCED_REF_SEARCH_SKIP_L2_L3_ARF2;
sf->cb_pred_filter_search = 1;
sf->use_transform_domain_distortion = boosted ? 0 : 1;
sf->perform_coeff_opt = boosted ? 0 : 1;
}
if (speed >= 2) {
sf->gm_erroradv_type = GM_ERRORADV_TR_2;
sf->selective_ref_frame = 2;
sf->selective_ref_frame = 3;
sf->fast_cdef_search = 1;
sf->adaptive_rd_thresh = 1;
@ -246,10 +300,19 @@ static void set_good_speed_features_framesize_independent(AV1_COMP *cpi,
// sf->auto_min_max_partition_size = RELAXED_NEIGHBORING_MIN_MAX;
sf->allow_partition_search_skip = 1;
sf->disable_wedge_search_var_thresh = 100;
sf->disable_wedge_search_edge_thresh = 0;
sf->disable_interinter_wedge_newmv_search = 1;
sf->fast_wedge_sign_estimate = 1;
sf->disable_dual_filter = 1;
sf->use_dist_wtd_comp_flag = DIST_WTD_COMP_DISABLED;
sf->prune_comp_type_by_comp_avg = 2;
sf->cb_pred_filter_search = 0;
sf->adaptive_interp_filter_search = 1;
sf->perform_coeff_opt = boosted ? 0 : 2;
}
if (speed >= 3) {
sf->selective_ref_frame = 4;
sf->tx_size_search_method = boosted ? USE_FULL_RD : USE_LARGESTALL;
sf->less_rectangular_check_level = 2;
sf->adaptive_pred_interp_filter = 1;
@ -258,26 +321,33 @@ static void set_good_speed_features_framesize_independent(AV1_COMP *cpi,
// See aomedia:1778.
// sf->adaptive_motion_search = 1;
sf->recode_loop = ALLOW_RECODE_KFARFGF;
sf->use_transform_domain_distortion = 1;
sf->use_accurate_subpel_search = 0;
sf->use_transform_domain_distortion = boosted ? 1 : 2;
sf->use_accurate_subpel_search = USE_2_TAPS;
sf->adaptive_rd_thresh = 2;
sf->tx_type_search.prune_mode = PRUNE_2D_FAST;
sf->gm_search_type = GM_DISABLE_SEARCH;
sf->prune_comp_search_by_single_result = 2;
sf->prune_motion_mode_level = boosted ? 2 : 3;
sf->prune_warp_using_wmtype = 1;
// TODO(yunqing): evaluate this speed feature for speed 1 & 2, and combine
// it with cpi->sf.disable_wedge_search_var_thresh.
sf->disable_wedge_interintra_search = 1;
sf->perform_coeff_opt = is_boosted_arf2_bwd_type ? 2 : 3;
}
if (speed >= 4) {
sf->use_intra_txb_hash = 0;
sf->tx_type_search.fast_intra_tx_type_search = 1;
sf->tx_type_search.fast_inter_tx_type_search = 1;
sf->use_square_partition_only_threshold =
boosted ? BLOCK_128X128 : BLOCK_4X4;
sf->tx_size_search_method =
frame_is_intra_only(cm) ? USE_FULL_RD : USE_LARGESTALL;
sf->mv.subpel_search_method = SUBPEL_TREE_PRUNED;
sf->adaptive_pred_interp_filter = 0;
sf->adaptive_mode_search = 1;
sf->cb_partition_search = !boosted;
sf->alt_ref_search_fp = 1;
sf->skip_sharp_interp_filter_search = 1;
sf->perform_coeff_opt = is_boosted_arf2_bwd_type ? 2 : 4;
sf->adaptive_txb_search_level = boosted ? 2 : 3;
}
if (speed >= 5) {
@ -294,7 +364,7 @@ static void set_good_speed_features_framesize_independent(AV1_COMP *cpi,
sf->mv.subpel_search_method = SUBPEL_TREE_PRUNED_MORE;
sf->adaptive_rd_thresh = 4;
sf->mode_search_skip_flags =
(cm->frame_type == KEY_FRAME)
(cm->current_frame.frame_type == KEY_FRAME)
? 0
: FLAG_SKIP_INTRA_DIRMISMATCH | FLAG_SKIP_INTRA_BESTINTER |
FLAG_SKIP_COMP_BESTINTRA | FLAG_SKIP_INTRA_LOWVAR |
@ -327,35 +397,28 @@ static void set_good_speed_features_framesize_independent(AV1_COMP *cpi,
sf->mv.search_method = FAST_HEX;
sf->partition_search_type = REFERENCE_PARTITION;
sf->mode_search_skip_flags |= FLAG_SKIP_INTRA_DIRMISMATCH;
// TODO(any): evaluate adaptive_mode_search=1 for speed 7 & 8
sf->adaptive_mode_search = 2;
}
if (speed >= 8) {
sf->mv.search_method = FAST_DIAMOND;
sf->mv.subpel_force_stop = 2;
sf->mv.subpel_force_stop = HALF_PEL;
sf->lpf_pick = LPF_PICK_MINIMAL_LPF;
}
}
void av1_set_speed_features_framesize_dependent(AV1_COMP *cpi) {
void av1_set_speed_features_framesize_dependent(AV1_COMP *cpi, int speed) {
SPEED_FEATURES *const sf = &cpi->sf;
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
RD_OPT *const rd = &cpi->rd;
int i;
if (oxcf->mode == GOOD) {
set_good_speed_feature_framesize_dependent(cpi, sf, oxcf->speed);
set_good_speed_feature_framesize_dependent(cpi, sf, speed);
}
if (sf->disable_split_mask == DISABLE_ALL_SPLIT) {
sf->adaptive_pred_interp_filter = 0;
}
// Check for masked out split cases.
for (i = 0; i < MAX_REFS; ++i) {
if (sf->disable_split_mask & (1 << i)) {
rd->thresh_mult_sub8x8[i] = INT_MAX;
}
}
// This is only used in motion vector unit test.
if (cpi->oxcf.motion_vector_unit_test == 1)
cpi->find_fractional_mv_step = av1_return_max_sub_pixel_mv;
@ -363,7 +426,7 @@ void av1_set_speed_features_framesize_dependent(AV1_COMP *cpi) {
cpi->find_fractional_mv_step = av1_return_min_sub_pixel_mv;
}
void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
void av1_set_speed_features_framesize_independent(AV1_COMP *cpi, int speed) {
AV1_COMMON *const cm = &cpi->common;
SPEED_FEATURES *const sf = &cpi->sf;
MACROBLOCK *const x = &cpi->td.mb;
@ -376,7 +439,7 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
sf->recode_loop = ALLOW_RECODE;
sf->mv.subpel_search_method = SUBPEL_TREE;
sf->mv.subpel_iters_per_step = 2;
sf->mv.subpel_force_stop = 0;
sf->mv.subpel_force_stop = EIGHTH_PEL;
#if DISABLE_TRELLISQ_SEARCH == 2
sf->optimize_coefficients = !is_lossless_requested(&cpi->oxcf)
? FINAL_PASS_TRELLIS_OPT
@ -402,6 +465,7 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
sf->tx_size_search_lgr_block = 0;
sf->model_based_prune_tx_search_level = 0;
sf->model_based_post_interp_filter_breakout = 0;
sf->model_based_motion_mode_rd_breakout = 0;
sf->reduce_inter_modes = 0;
sf->selective_ref_gm = 1;
sf->adaptive_motion_search = 0;
@ -420,6 +484,7 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
sf->less_rectangular_check_level = 0;
sf->use_square_partition_only_threshold = BLOCK_128X128;
sf->prune_ref_frame_for_rect_partitions = 0;
sf->prune_ref_mode_for_partitions = 0;
sf->auto_min_max_partition_size = NOT_IN_USE;
sf->rd_auto_partition_min_limit = BLOCK_4X4;
sf->default_max_partition_size = BLOCK_LARGEST;
@ -429,9 +494,11 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
sf->mode_search_skip_flags = 0;
sf->disable_filter_search_var_thresh = 0;
sf->allow_partition_search_skip = 0;
sf->use_accurate_subpel_search = 2;
sf->use_accurate_subpel_search = USE_8_TAPS;
sf->disable_wedge_search_edge_thresh = 0;
sf->disable_wedge_search_var_thresh = 0;
sf->fast_wedge_sign_estimate = 0;
sf->prune_wedge_pred_diff_based = 0;
sf->drop_ref = 0;
sf->skip_intra_in_interframe = 1;
sf->txb_split_cap = 1;
@ -439,12 +506,17 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
sf->two_pass_partition_search = 0;
sf->mode_pruning_based_on_two_pass_partition_search = 0;
sf->use_intra_txb_hash = 0;
sf->use_inter_txb_hash = 1;
// TODO(any) : clean use_inter_txb_hash code
sf->use_inter_txb_hash = 0;
sf->use_mb_rd_hash = 1;
sf->optimize_b_precheck = 0;
sf->jnt_comp_fast_tx_search = 0;
sf->jnt_comp_skip_mv_search = 0;
sf->dist_wtd_comp_fast_tx_search = 0;
sf->use_dist_wtd_comp_flag = DIST_WTD_COMP_ENABLED;
sf->reuse_inter_intra_mode = 0;
sf->intra_angle_estimation = 0;
sf->skip_obmc_in_uniform_mv_field = 0;
sf->skip_wm_in_uniform_mv_field = 0;
sf->adaptive_interp_filter_search = 0;
for (i = 0; i < TX_SIZES; i++) {
sf->intra_y_mode_mask[i] = INTRA_ALL;
@ -466,27 +538,48 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
sf->ml_prune_ab_partition = 0;
sf->ml_prune_4_partition = 0;
sf->fast_cdef_search = 0;
for (i = 0; i < PARTITION_BLOCK_SIZES; ++i)
for (i = 0; i < PARTITION_BLOCK_SIZES; ++i) {
sf->ml_partition_search_breakout_thresh[i] = -1; // -1 means not enabled.
}
sf->simple_motion_search_split_only = 0;
sf->simple_motion_search_prune_rect = 0;
// Set this at the appropriate speed levels
sf->use_transform_domain_distortion = 0;
sf->gm_search_type = GM_FULL_SEARCH;
sf->gm_disable_recode = 0;
sf->use_fast_interpolation_filter_search = 0;
sf->disable_dual_filter = 0;
sf->skip_repeat_interpolation_filter_search = 0;
sf->use_hash_based_trellis = 0;
sf->prune_comp_search_by_single_result = 0;
sf->skip_repeated_newmv = 0;
sf->prune_single_motion_modes_by_simple_trans = 0;
// Set decoder side speed feature to use less dual sgr modes
sf->dual_sgr_penalty_level = 0;
// TODO(angiebird, debargha): Re-evaluate the impact of
// inter_mode_rd_model_estimation in conjunction with
// model_based_motion_mode_rd_breakout
sf->inter_mode_rd_model_estimation = 0;
sf->obmc_full_pixel_search_level = 0;
sf->skip_sharp_interp_filter_search = 0;
sf->prune_comp_type_by_comp_avg = 0;
sf->disable_interinter_wedge_newmv_search = 0;
sf->prune_motion_mode_level = 0;
sf->prune_warp_using_wmtype = 0;
sf->disable_wedge_interintra_search = 0;
sf->perform_coeff_opt = 0;
if (oxcf->mode == GOOD)
set_good_speed_features_framesize_independent(cpi, sf, oxcf->speed);
set_good_speed_features_framesize_independent(cpi, sf, speed);
if (!cpi->seq_params_locked) {
cpi->common.seq_params.enable_dual_filter &= !sf->disable_dual_filter;
}
// sf->partition_search_breakout_dist_thr is set assuming max 64x64
// blocks. Normalise this if the blocks are bigger.
@ -497,28 +590,31 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
cpi->diamond_search_sad = av1_diamond_search_sad;
sf->allow_exhaustive_searches = 1;
int speed = (oxcf->speed > MAX_MESH_SPEED) ? MAX_MESH_SPEED : oxcf->speed;
const int mesh_speed = AOMMIN(speed, MAX_MESH_SPEED);
if (cpi->twopass.fr_content_type == FC_GRAPHICS_ANIMATION)
sf->exhaustive_searches_thresh = (1 << 24);
else
sf->exhaustive_searches_thresh = (1 << 25);
sf->max_exaustive_pct = good_quality_max_mesh_pct[speed];
if (speed > 0)
sf->max_exaustive_pct = good_quality_max_mesh_pct[mesh_speed];
if (mesh_speed > 0)
sf->exhaustive_searches_thresh = sf->exhaustive_searches_thresh << 1;
for (i = 0; i < MAX_MESH_STEP; ++i) {
sf->mesh_patterns[i].range = good_quality_mesh_patterns[speed][i].range;
sf->mesh_patterns[i].range =
good_quality_mesh_patterns[mesh_speed][i].range;
sf->mesh_patterns[i].interval =
good_quality_mesh_patterns[speed][i].interval;
good_quality_mesh_patterns[mesh_speed][i].interval;
}
if ((frame_is_intra_only(cm) && cm->allow_screen_content_tools) &&
(cpi->twopass.fr_content_type == FC_GRAPHICS_ANIMATION ||
cpi->oxcf.content == AOM_CONTENT_SCREEN)) {
for (i = 0; i < MAX_MESH_STEP; ++i) {
sf->mesh_patterns[i].range = intrabc_mesh_patterns[speed][i].range;
sf->mesh_patterns[i].interval = intrabc_mesh_patterns[speed][i].interval;
sf->mesh_patterns[i].range = intrabc_mesh_patterns[mesh_speed][i].range;
sf->mesh_patterns[i].interval =
intrabc_mesh_patterns[mesh_speed][i].interval;
}
sf->max_exaustive_pct = intrabc_max_mesh_pct[speed];
sf->max_exaustive_pct = intrabc_max_mesh_pct[mesh_speed];
}
// Slow quant, dct and trellis not worthwhile for first pass
@ -544,7 +640,7 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
cpi->optimize_speed_feature =
oxcf->pass != 1 ? sf->optimize_coefficients : NO_TRELLIS_OPT;
// FIXME: trellis not very efficient for quantisation matrices
if (cm->using_qmatrix) cpi->optimize_speed_feature = NO_TRELLIS_OPT;
if (oxcf->using_qm) cpi->optimize_speed_feature = NO_TRELLIS_OPT;
if (oxcf->disable_trellis_quant) cpi->optimize_speed_feature = NO_TRELLIS_OPT;
x->min_partition_size = sf->default_min_partition_size;
@ -555,10 +651,26 @@ void av1_set_speed_features_framesize_independent(AV1_COMP *cpi) {
cpi->find_fractional_mv_step = av1_return_max_sub_pixel_mv;
else if (cpi->oxcf.motion_vector_unit_test == 2)
cpi->find_fractional_mv_step = av1_return_min_sub_pixel_mv;
cpi->max_comp_type_rd_threshold_mul =
comp_type_rd_threshold_mul[sf->prune_comp_type_by_comp_avg];
cpi->max_comp_type_rd_threshold_div =
comp_type_rd_threshold_div[sf->prune_comp_type_by_comp_avg];
const int tx_domain_speed = AOMMIN(speed, MAX_TX_DOMAIN_EVAL_SPEED);
cpi->tx_domain_dist_threshold = tx_domain_dist_thresholds[tx_domain_speed];
// assert ensures that coeff_opt_dist_thresholds is accessed correctly
assert(cpi->sf.perform_coeff_opt >= 0 && cpi->sf.perform_coeff_opt < 5);
cpi->coeff_opt_dist_threshold =
coeff_opt_dist_thresholds[cpi->sf.perform_coeff_opt];
#if CONFIG_DIST_8X8
if (sf->use_transform_domain_distortion > 0) cpi->oxcf.using_dist_8x8 = 0;
if (cpi->oxcf.using_dist_8x8) x->min_partition_size = BLOCK_8X8;
#endif // CONFIG_DIST_8X8
if (cpi->oxcf.row_mt == 1 && (cpi->oxcf.max_threads > 1)) {
sf->adaptive_rd_thresh = 0;
if (sf->inter_mode_rd_model_estimation == 1)
sf->inter_mode_rd_model_estimation = 0;
}
}

View file

@ -73,7 +73,7 @@ enum {
(1 << THR_ALTR) | (1 << THR_GOLD)
};
typedef enum {
enum {
TXFM_CODING_SF = 1,
INTER_PRED_SF = 2,
INTRA_PRED_SF = 4,
@ -82,9 +82,9 @@ typedef enum {
RD_SKIP_SF = 32,
RESERVE_2_SF = 64,
RESERVE_3_SF = 128,
} DEV_SPEED_FEATURES;
} UENUM1BYTE(DEV_SPEED_FEATURES);
typedef enum {
enum {
DIAMOND = 0,
NSTEP = 1,
HEX = 2,
@ -92,9 +92,9 @@ typedef enum {
SQUARE = 4,
FAST_HEX = 5,
FAST_DIAMOND = 6
} SEARCH_METHODS;
} UENUM1BYTE(SEARCH_METHODS);
typedef enum {
enum {
// No recode.
DISALLOW_RECODE = 0,
// Allow recode for KF and exceeding maximum frame bandwidth.
@ -103,28 +103,28 @@ typedef enum {
ALLOW_RECODE_KFARFGF = 2,
// Allow recode for all frames based on bitrate constraints.
ALLOW_RECODE = 3,
} RECODE_LOOP_TYPE;
} UENUM1BYTE(RECODE_LOOP_TYPE);
typedef enum {
enum {
SUBPEL_TREE = 0,
SUBPEL_TREE_PRUNED = 1, // Prunes 1/2-pel searches
SUBPEL_TREE_PRUNED_MORE = 2, // Prunes 1/2-pel searches more aggressively
SUBPEL_TREE_PRUNED_EVENMORE = 3, // Prunes 1/2- and 1/4-pel searches
// Other methods to come
} SUBPEL_SEARCH_METHODS;
} UENUM1BYTE(SUBPEL_SEARCH_METHODS);
typedef enum {
enum {
USE_FULL_RD = 0,
USE_FAST_RD,
USE_LARGESTALL,
} TX_SIZE_SEARCH_METHOD;
} UENUM1BYTE(TX_SIZE_SEARCH_METHOD);
typedef enum {
enum {
NOT_IN_USE = 0,
RELAXED_NEIGHBORING_MIN_MAX = 1
} AUTO_MIN_MAX_MODE;
} UENUM1BYTE(AUTO_MIN_MAX_MODE);
typedef enum {
enum {
// Try the full image with different values.
LPF_PICK_FROM_FULL_IMAGE,
// Try a small portion of the image with different values.
@ -133,9 +133,9 @@ typedef enum {
LPF_PICK_FROM_Q,
// Pick 0 to disable LPF if LPF was enabled last frame
LPF_PICK_MINIMAL_LPF
} LPF_PICK_METHOD;
} UENUM1BYTE(LPF_PICK_METHOD);
typedef enum {
enum {
// Terminate search early based on distortion so far compared to
// qp step, distortion in the neighborhood of the frame, etc.
FLAG_EARLY_TERMINATE = 1 << 0,
@ -152,9 +152,9 @@ typedef enum {
// Skips intra modes other than DC_PRED if the source variance is small
FLAG_SKIP_INTRA_LOWVAR = 1 << 5,
} MODE_SEARCH_SKIP_LOGIC;
} UENUM1BYTE(MODE_SEARCH_SKIP_LOGIC);
typedef enum {
enum {
NO_PRUNE = 0,
// eliminates one tx type in vertical and horizontal direction
PRUNE_ONE = 1,
@ -165,7 +165,7 @@ typedef enum {
PRUNE_2D_ACCURATE = 3,
// similar, but applies much more aggressive pruning to get better speed-up
PRUNE_2D_FAST = 4,
} TX_TYPE_PRUNE_MODE;
} UENUM1BYTE(TX_TYPE_PRUNE_MODE);
typedef struct {
TX_TYPE_PRUNE_MODE prune_mode;
@ -184,7 +184,7 @@ typedef struct {
int skip_tx_search;
} TX_TYPE_SEARCH;
typedef enum {
enum {
// Search partitions using RD criterion
SEARCH_PARTITION,
@ -192,7 +192,14 @@ typedef enum {
FIXED_PARTITION,
REFERENCE_PARTITION
} PARTITION_SEARCH_TYPE;
} UENUM1BYTE(PARTITION_SEARCH_TYPE);
enum {
EIGHTH_PEL,
QUARTER_PEL,
HALF_PEL,
FULL_PEL
} UENUM1BYTE(SUBPEL_FORCE_STOP);
typedef struct MV_SPEED_FEATURES {
// Motion search method (Diamond, NSTEP, Hex, Big Diamond, Square, etc).
@ -215,8 +222,8 @@ typedef struct MV_SPEED_FEATURES {
// Maximum number of steps in logarithmic subpel search before giving up.
int subpel_iters_per_step;
// Control when to stop subpel search
int subpel_force_stop;
// When to stop subpel search.
SUBPEL_FORCE_STOP subpel_force_stop;
} MV_SPEED_FEATURES;
#define MAX_MESH_STEP 4
@ -226,29 +233,42 @@ typedef struct MESH_PATTERN {
int interval;
} MESH_PATTERN;
typedef enum {
enum {
GM_FULL_SEARCH,
GM_REDUCED_REF_SEARCH,
GM_REDUCED_REF_SEARCH_SKIP_L2_L3,
GM_REDUCED_REF_SEARCH_SKIP_L2_L3_ARF2,
GM_DISABLE_SEARCH
} GM_SEARCH_TYPE;
} UENUM1BYTE(GM_SEARCH_TYPE);
typedef enum {
enum {
GM_ERRORADV_TR_0,
GM_ERRORADV_TR_1,
GM_ERRORADV_TR_2,
GM_ERRORADV_TR_TYPES,
} GM_ERRORADV_TYPE;
} UENUM1BYTE(GM_ERRORADV_TYPE);
typedef enum {
enum {
NO_TRELLIS_OPT, // No trellis optimization
FULL_TRELLIS_OPT, // Trellis optimization in all stages
FINAL_PASS_TRELLIS_OPT // Trellis optimization in only the final encode pass
} TRELLIS_OPT_TYPE;
} UENUM1BYTE(TRELLIS_OPT_TYPE);
typedef enum {
enum {
FULL_TXFM_RD,
LOW_TXFM_RD,
} TXFM_RD_MODEL;
} UENUM1BYTE(TXFM_RD_MODEL);
enum {
DIST_WTD_COMP_ENABLED,
DIST_WTD_COMP_SKIP_MV_SEARCH,
DIST_WTD_COMP_DISABLED,
} UENUM1BYTE(DIST_WTD_COMP_FLAG);
typedef enum {
FLAG_SKIP_EIGHTTAP = 1 << EIGHTTAP_REGULAR,
FLAG_SKIP_EIGHTTAP_SMOOTH = 1 << EIGHTTAP_SMOOTH,
FLAG_SKIP_EIGHTTAP_SHARP = 1 << MULTITAP_SHARP,
} INTERP_FILTER_MASK;
typedef struct SPEED_FEATURES {
MV_SPEED_FEATURES mv;
@ -329,11 +349,16 @@ typedef struct SPEED_FEATURES {
// 1: use model based rd breakout
int model_based_post_interp_filter_breakout;
// Model based breakout in motion_mode_rd
// 0: no breakout
// 1: use model based rd breakout
int model_based_motion_mode_rd_breakout;
// Used if partition_search_type = FIXED_SIZE_PARTITION
BLOCK_SIZE always_this_block_size;
// Drop less likely to be picked reference frames in the RD search.
// Has three levels for now: 0, 1 and 2, where higher levels prune more
// Has five levels for now: 0, 1, 2, 3 and 4, where higher levels prune more
// aggressively than lower ones. (0 means no pruning).
int selective_ref_frame;
@ -371,6 +396,9 @@ typedef struct SPEED_FEATURES {
// Prune reference frames for rectangular partitions.
int prune_ref_frame_for_rect_partitions;
// Prune ref/mode choices for partitions.
int prune_ref_mode_for_partitions;
// Sets min and max partition sizes for this superblock based on the
// same superblock in last encoded frame, and the left and above neighbor.
AUTO_MIN_MAX_MODE auto_min_max_partition_size;
@ -431,12 +459,19 @@ typedef struct SPEED_FEATURES {
// Choose a very large value (UINT_MAX) to use 8-tap always
unsigned int disable_filter_search_var_thresh;
// A source variance threshold below which wedge search is disabled
// Only enable wedge search if the edge strength is greater than
// this threshold. A value of 0 signals that this check is disabled.
unsigned int disable_wedge_search_edge_thresh;
// Only enable wedge search if the variance is above this threshold.
unsigned int disable_wedge_search_var_thresh;
// Whether fast wedge sign estimate is used
int fast_wedge_sign_estimate;
// Whether to prune wedge search based on predictor difference
int prune_wedge_pred_diff_based;
// These bit masks allow you to enable or disable intra modes for each
// transform size separately.
int intra_y_mode_mask[TX_SIZES];
@ -473,7 +508,7 @@ typedef struct SPEED_FEATURES {
// If true, sub-pixel search uses the exact convolve function used for final
// encoding and decoding; otherwise, it uses bilinear interpolation.
int use_accurate_subpel_search;
SUBPEL_SEARCH_TYPE use_accurate_subpel_search;
// Whether to compute distortion in the image domain (slower but
// more accurate), or in the transform domain (faster but less acurate).
@ -492,6 +527,9 @@ typedef struct SPEED_FEATURES {
// usually includes EIGHTTAP_REGULAR.
int use_fast_interpolation_filter_search;
// Disable dual filter
int disable_dual_filter;
// Save results of interpolation_filter_search for a block
// Check mv and ref_frames before search, if they are same with previous
// saved results, it can be skipped.
@ -522,18 +560,23 @@ typedef struct SPEED_FEATURES {
// Calculate RD cost before doing optimize_b, and skip if the cost is large.
int optimize_b_precheck;
// Use model rd instead of transform search in jnt_comp
int jnt_comp_fast_tx_search;
// Use model rd instead of transform search in dist_wtd_comp
int dist_wtd_comp_fast_tx_search;
// Skip mv search in jnt_comp
int jnt_comp_skip_mv_search;
// Decide when and how to use joint_comp.
DIST_WTD_COMP_FLAG use_dist_wtd_comp_flag;
// Decoder side speed feature to add penalty for use of dual-sgr filters.
// Takes values 0 - 10, 0 indicating no penalty and each additional level
// adding a penalty of 1%
int dual_sgr_penalty_level;
// Dynamically estimate final rd from prediction error and mode cost
// 2-pass inter mode model estimation where the preliminary pass skips
// transform search and uses a model to estimate rd, while the final pass
// computes the full transform search. two types of models are supported:
// 0: not used
// 1: used with online dynamic rd model
// 2: used with static rd model
int inter_mode_rd_model_estimation;
// Skip some ref frames in compound motion search by single motion search
@ -543,6 +586,10 @@ typedef struct SPEED_FEATURES {
// single inter mode as a group.
int prune_comp_search_by_single_result;
// Skip certain motion modes (OBMC, warped, interintra) for single reference
// motion search, using the results of single ref SIMPLE_TRANSLATION
int prune_single_motion_modes_by_simple_trans;
// Reuse the inter_intra_mode search result from NEARESTMV mode to other
// single ref modes
int reuse_inter_intra_mode;
@ -554,12 +601,65 @@ typedef struct SPEED_FEATURES {
// flag to skip NEWMV mode in drl if the motion search result is the same
int skip_repeated_newmv;
// Prune intra mode candidates based on source block gradient stats.
int intra_angle_estimation;
// Skip obmc or warped motion mode when neighborhood motion field is
// identical
int skip_obmc_in_uniform_mv_field;
int skip_wm_in_uniform_mv_field;
// Enable/disable ME for interinter wedge search.
int disable_interinter_wedge_newmv_search;
// skip sharp_filter evaluation based on regular and smooth filter rd for
// dual_filter=0 case
int skip_sharp_interp_filter_search;
// prune wedge and compound segment approximate rd evaluation based on
// compound average rd/ref_best_rd
int prune_comp_type_by_comp_avg;
// Prune/gate motion mode evaluation based on token based rd
// during transform search for inter blocks
// Values are 0 (not used) , 1 - 3 with progressively increasing
// aggressiveness
int prune_motion_mode_level;
// Gate warp evaluation for motions of type IDENTITY,
// TRANSLATION and AFFINE(based on number of warp neighbors)
int prune_warp_using_wmtype;
// Perform simple_motion_search on each possible subblock and use it to prune
// PARTITION_HORZ and PARTITION_VERT.
int simple_motion_search_prune_rect;
// Perform simple motion search before none_partition to decide if we
// want to split directly without trying other partition types.
int simple_motion_search_split_only;
int cb_pred_filter_search;
// adaptive interp_filter search to allow skip of certain filter types.
int adaptive_interp_filter_search;
// mask for skip evaluation of certain interp_filter type.
INTERP_FILTER_MASK interp_filter_search_mask;
// Enable/disable interintra wedge search.
int disable_wedge_interintra_search;
// Flag used to control the extent of coeff R-D optimization
int perform_coeff_opt;
} SPEED_FEATURES;
struct AV1_COMP;
void av1_set_speed_features_framesize_independent(struct AV1_COMP *cpi);
void av1_set_speed_features_framesize_dependent(struct AV1_COMP *cpi);
void av1_set_speed_features_framesize_independent(struct AV1_COMP *cpi,
int speed);
void av1_set_speed_features_framesize_dependent(struct AV1_COMP *cpi,
int speed);
#ifdef __cplusplus
} // extern "C"

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,20 @@
extern "C" {
#endif
#define ARNR_FILT_QINDEX 128
// Block size used in temporal filtering
#define TF_BLOCK BLOCK_32X32
#define BH 32
#define BH_LOG2 5
#define BW 32
#define BW_LOG2 5
#define BLK_PELS 1024 // Pixels in the block
#define THR_SHIFT 2
#define TF_SUB_BLOCK BLOCK_16X16
#define SUB_BH 16
#define SUB_BW 16
void av1_temporal_filter(AV1_COMP *cpi, int distance);
#ifdef __cplusplus

View file

@ -29,7 +29,7 @@
static int cost_and_tokenize_map(Av1ColorMapParam *param, TOKENEXTRA **t,
int plane, int calc_rate, int allow_update_cdf,
FRAME_COUNTS *counts) {
FRAME_COUNTS *counts, MapCdf map_pb_cdf) {
const uint8_t *const color_map = param->color_map;
MapCdf map_cdf = param->map_cdf;
ColorCost color_cost = param->color_cost;
@ -55,7 +55,7 @@ static int cost_and_tokenize_map(Av1ColorMapParam *param, TOKENEXTRA **t,
this_rate += (*color_cost)[palette_size_idx][color_ctx][color_new_idx];
} else {
(*t)->token = color_new_idx;
(*t)->color_map_cdf = map_cdf[palette_size_idx][color_ctx];
(*t)->color_map_cdf = map_pb_cdf[palette_size_idx][color_ctx];
++(*t);
if (allow_update_cdf)
update_cdf(map_cdf[palette_size_idx][color_ctx], color_new_idx, n);
@ -107,7 +107,10 @@ int av1_cost_color_map(const MACROBLOCK *const x, int plane, BLOCK_SIZE bsize,
assert(plane == 0 || plane == 1);
Av1ColorMapParam color_map_params;
get_color_map_params(x, plane, bsize, tx_size, type, &color_map_params);
return cost_and_tokenize_map(&color_map_params, NULL, plane, 1, 0, NULL);
MapCdf map_pb_cdf = plane ? x->tile_pb_ctx->palette_uv_color_index_cdf
: x->tile_pb_ctx->palette_y_color_index_cdf;
return cost_and_tokenize_map(&color_map_params, NULL, plane, 1, 0, NULL,
map_pb_cdf);
}
void av1_tokenize_color_map(const MACROBLOCK *const x, int plane,
@ -121,13 +124,15 @@ void av1_tokenize_color_map(const MACROBLOCK *const x, int plane,
(*t)->token = color_map_params.color_map[0];
(*t)->color_map_cdf = NULL;
++(*t);
MapCdf map_pb_cdf = plane ? x->tile_pb_ctx->palette_uv_color_index_cdf
: x->tile_pb_ctx->palette_y_color_index_cdf;
cost_and_tokenize_map(&color_map_params, t, plane, 0, allow_update_cdf,
counts);
counts, map_pb_cdf);
}
void tokenize_vartx(ThreadData *td, TOKENEXTRA **t, RUN_TYPE dry_run,
TX_SIZE tx_size, BLOCK_SIZE plane_bsize, int blk_row,
int blk_col, int block, int plane, void *arg) {
static void tokenize_vartx(ThreadData *td, TOKENEXTRA **t, RUN_TYPE dry_run,
TX_SIZE tx_size, BLOCK_SIZE plane_bsize, int blk_row,
int blk_col, int block, int plane, void *arg) {
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];

View file

@ -38,11 +38,11 @@ struct tokenize_b_args {
uint8_t allow_update_cdf;
};
typedef enum {
enum {
OUTPUT_ENABLED = 0,
DRY_RUN_NORMAL,
DRY_RUN_COSTCOEFFS,
} RUN_TYPE;
} UENUM1BYTE(RUN_TYPE);
// Note in all the tokenize functions rate if non NULL is incremented
// with the coefficient token cost only if dry_run = DRY_RUN_COSTCOEFS,

595
third_party/aom/av1/encoder/tpl_model.c vendored Normal file
View file

@ -0,0 +1,595 @@
/*
* Copyright (c) 2019, 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 <stdint.h>
#include "config/aom_config.h"
#include "config/aom_dsp_rtcd.h"
#include "aom/aom_codec.h"
#include "av1/common/onyxc_int.h"
#include "av1/common/reconintra.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/reconinter_enc.h"
typedef struct GF_PICTURE {
YV12_BUFFER_CONFIG *frame;
int ref_frame[7];
} GF_PICTURE;
static void get_quantize_error(MACROBLOCK *x, int plane, tran_low_t *coeff,
tran_low_t *qcoeff, tran_low_t *dqcoeff,
TX_SIZE tx_size, int64_t *recon_error,
int64_t *sse) {
const struct macroblock_plane *const p = &x->plane[plane];
const SCAN_ORDER *const scan_order = &av1_default_scan_orders[tx_size];
uint16_t eob;
int pix_num = 1 << num_pels_log2_lookup[txsize_to_bsize[tx_size]];
const int shift = tx_size == TX_32X32 ? 0 : 2;
av1_quantize_fp_32x32(coeff, pix_num, p->zbin_QTX, p->round_fp_QTX,
p->quant_fp_QTX, p->quant_shift_QTX, qcoeff, dqcoeff,
p->dequant_QTX, &eob, scan_order->scan,
scan_order->iscan);
*recon_error = av1_block_error(coeff, dqcoeff, pix_num, sse) >> shift;
*recon_error = AOMMAX(*recon_error, 1);
*sse = (*sse) >> shift;
*sse = AOMMAX(*sse, 1);
}
static void wht_fwd_txfm(int16_t *src_diff, int bw, tran_low_t *coeff,
TX_SIZE tx_size) {
switch (tx_size) {
case TX_8X8: aom_hadamard_8x8(src_diff, bw, coeff); break;
case TX_16X16: aom_hadamard_16x16(src_diff, bw, coeff); break;
case TX_32X32: aom_hadamard_32x32(src_diff, bw, coeff); break;
default: assert(0);
}
}
static uint32_t motion_compensated_prediction(AV1_COMP *cpi, ThreadData *td,
uint8_t *cur_frame_buf,
uint8_t *ref_frame_buf,
int stride, BLOCK_SIZE bsize,
int mi_row, int mi_col) {
AV1_COMMON *cm = &cpi->common;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
MV_SPEED_FEATURES *const mv_sf = &cpi->sf.mv;
const SEARCH_METHODS search_method = NSTEP;
int step_param;
int sadpb = x->sadperbit16;
uint32_t bestsme = UINT_MAX;
int distortion;
uint32_t sse;
int cost_list[5];
const MvLimits tmp_mv_limits = x->mv_limits;
MV best_ref_mv1 = { 0, 0 };
MV best_ref_mv1_full; /* full-pixel value of best_ref_mv1 */
best_ref_mv1_full.col = best_ref_mv1.col >> 3;
best_ref_mv1_full.row = best_ref_mv1.row >> 3;
// Setup frame pointers
x->plane[0].src.buf = cur_frame_buf;
x->plane[0].src.stride = stride;
xd->plane[0].pre[0].buf = ref_frame_buf;
xd->plane[0].pre[0].stride = stride;
step_param = mv_sf->reduce_first_step_size;
step_param = AOMMIN(step_param, MAX_MVSEARCH_STEPS - 2);
av1_set_mv_search_range(&x->mv_limits, &best_ref_mv1);
av1_full_pixel_search(cpi, x, bsize, &best_ref_mv1_full, step_param,
search_method, 0, sadpb, cond_cost_list(cpi, cost_list),
&best_ref_mv1, INT_MAX, 0, (MI_SIZE * mi_col),
(MI_SIZE * mi_row), 0);
/* restore UMV window */
x->mv_limits = tmp_mv_limits;
const int pw = block_size_wide[bsize];
const int ph = block_size_high[bsize];
bestsme = cpi->find_fractional_mv_step(
x, cm, mi_row, mi_col, &best_ref_mv1, cpi->common.allow_high_precision_mv,
x->errorperbit, &cpi->fn_ptr[bsize], 0, mv_sf->subpel_iters_per_step,
cond_cost_list(cpi, cost_list), NULL, NULL, &distortion, &sse, NULL, NULL,
0, 0, pw, ph, 1, 1);
return bestsme;
}
static void mode_estimation(AV1_COMP *cpi, MACROBLOCK *x, MACROBLOCKD *xd,
struct scale_factors *sf, GF_PICTURE *gf_picture,
int frame_idx, int16_t *src_diff, tran_low_t *coeff,
tran_low_t *qcoeff, tran_low_t *dqcoeff, int mi_row,
int mi_col, BLOCK_SIZE bsize, TX_SIZE tx_size,
YV12_BUFFER_CONFIG *ref_frame[], uint8_t *predictor,
int64_t *recon_error, int64_t *sse,
TplDepStats *tpl_stats) {
AV1_COMMON *cm = &cpi->common;
ThreadData *td = &cpi->td;
const int bw = 4 << mi_size_wide_log2[bsize];
const int bh = 4 << mi_size_high_log2[bsize];
const int pix_num = bw * bh;
int best_rf_idx = -1;
int_mv best_mv;
int64_t best_inter_cost = INT64_MAX;
int64_t inter_cost;
int rf_idx;
const InterpFilters kernel =
av1_make_interp_filters(EIGHTTAP_REGULAR, EIGHTTAP_REGULAR);
int64_t best_intra_cost = INT64_MAX;
int64_t intra_cost;
PREDICTION_MODE mode;
int mb_y_offset = mi_row * MI_SIZE * xd->cur_buf->y_stride + mi_col * MI_SIZE;
MB_MODE_INFO mi_above, mi_left;
memset(tpl_stats, 0, sizeof(*tpl_stats));
xd->mb_to_top_edge = -((mi_row * MI_SIZE) * 8);
xd->mb_to_bottom_edge = ((cm->mi_rows - 1 - mi_row) * MI_SIZE) * 8;
xd->mb_to_left_edge = -((mi_col * MI_SIZE) * 8);
xd->mb_to_right_edge = ((cm->mi_cols - 1 - mi_col) * MI_SIZE) * 8;
xd->above_mbmi = (mi_row > 0) ? &mi_above : NULL;
xd->left_mbmi = (mi_col > 0) ? &mi_left : NULL;
// Intra prediction search
for (mode = DC_PRED; mode <= PAETH_PRED; ++mode) {
uint8_t *src, *dst;
int src_stride, dst_stride;
src = xd->cur_buf->y_buffer + mb_y_offset;
src_stride = xd->cur_buf->y_stride;
dst = &predictor[0];
dst_stride = bw;
xd->mi[0]->sb_type = bsize;
xd->mi[0]->ref_frame[0] = INTRA_FRAME;
av1_predict_intra_block(
cm, xd, block_size_wide[bsize], block_size_high[bsize], tx_size, mode,
0, 0, FILTER_INTRA_MODES, src, src_stride, dst, dst_stride, 0, 0, 0);
if (is_cur_buf_hbd(xd)) {
aom_highbd_subtract_block(bh, bw, src_diff, bw, src, src_stride, dst,
dst_stride, xd->bd);
} else {
aom_subtract_block(bh, bw, src_diff, bw, src, src_stride, dst,
dst_stride);
}
wht_fwd_txfm(src_diff, bw, coeff, tx_size);
intra_cost = aom_satd(coeff, pix_num);
if (intra_cost < best_intra_cost) best_intra_cost = intra_cost;
}
// Motion compensated prediction
best_mv.as_int = 0;
(void)mb_y_offset;
// Motion estimation column boundary
x->mv_limits.col_min = -((mi_col * MI_SIZE) + (17 - 2 * AOM_INTERP_EXTEND));
x->mv_limits.col_max =
((cm->mi_cols - 1 - mi_col) * MI_SIZE) + (17 - 2 * AOM_INTERP_EXTEND);
for (rf_idx = 0; rf_idx < 7; ++rf_idx) {
if (ref_frame[rf_idx] == NULL) continue;
motion_compensated_prediction(cpi, td, xd->cur_buf->y_buffer + mb_y_offset,
ref_frame[rf_idx]->y_buffer + mb_y_offset,
xd->cur_buf->y_stride, bsize, mi_row, mi_col);
// TODO(jingning): Not yet support high bit-depth in the next three
// steps.
ConvolveParams conv_params = get_conv_params(0, 0, xd->bd);
WarpTypesAllowed warp_types;
memset(&warp_types, 0, sizeof(WarpTypesAllowed));
av1_build_inter_predictor(
ref_frame[rf_idx]->y_buffer + mb_y_offset, ref_frame[rf_idx]->y_stride,
&predictor[0], bw, &x->best_mv.as_mv, sf, bw, bh, &conv_params, kernel,
&warp_types, mi_col * MI_SIZE, mi_row * MI_SIZE, 0, 0, MV_PRECISION_Q3,
mi_col * MI_SIZE, mi_row * MI_SIZE, xd, 0);
if (is_cur_buf_hbd(xd)) {
aom_highbd_subtract_block(
bh, bw, src_diff, bw, xd->cur_buf->y_buffer + mb_y_offset,
xd->cur_buf->y_stride, &predictor[0], bw, xd->bd);
} else {
aom_subtract_block(bh, bw, src_diff, bw,
xd->cur_buf->y_buffer + mb_y_offset,
xd->cur_buf->y_stride, &predictor[0], bw);
}
wht_fwd_txfm(src_diff, bw, coeff, tx_size);
inter_cost = aom_satd(coeff, pix_num);
if (inter_cost < best_inter_cost) {
best_rf_idx = rf_idx;
best_inter_cost = inter_cost;
best_mv.as_int = x->best_mv.as_int;
get_quantize_error(x, 0, coeff, qcoeff, dqcoeff, tx_size, recon_error,
sse);
}
}
best_intra_cost = AOMMAX(best_intra_cost, 1);
best_inter_cost = AOMMIN(best_intra_cost, best_inter_cost);
tpl_stats->inter_cost = best_inter_cost << TPL_DEP_COST_SCALE_LOG2;
tpl_stats->intra_cost = best_intra_cost << TPL_DEP_COST_SCALE_LOG2;
tpl_stats->mc_dep_cost = tpl_stats->intra_cost + tpl_stats->mc_flow;
tpl_stats->ref_frame_index = gf_picture[frame_idx].ref_frame[best_rf_idx];
tpl_stats->mv.as_int = best_mv.as_int;
}
static int round_floor(int ref_pos, int bsize_pix) {
int round;
if (ref_pos < 0)
round = -(1 + (-ref_pos - 1) / bsize_pix);
else
round = ref_pos / bsize_pix;
return round;
}
static int get_overlap_area(int grid_pos_row, int grid_pos_col, int ref_pos_row,
int ref_pos_col, int block, BLOCK_SIZE bsize) {
int width = 0, height = 0;
int bw = 4 << mi_size_wide_log2[bsize];
int bh = 4 << mi_size_high_log2[bsize];
switch (block) {
case 0:
width = grid_pos_col + bw - ref_pos_col;
height = grid_pos_row + bh - ref_pos_row;
break;
case 1:
width = ref_pos_col + bw - grid_pos_col;
height = grid_pos_row + bh - ref_pos_row;
break;
case 2:
width = grid_pos_col + bw - ref_pos_col;
height = ref_pos_row + bh - grid_pos_row;
break;
case 3:
width = ref_pos_col + bw - grid_pos_col;
height = ref_pos_row + bh - grid_pos_row;
break;
default: assert(0);
}
return width * height;
}
static void tpl_model_update_b(TplDepFrame *tpl_frame, TplDepStats *tpl_stats,
int mi_row, int mi_col, const BLOCK_SIZE bsize) {
TplDepFrame *ref_tpl_frame = &tpl_frame[tpl_stats->ref_frame_index];
TplDepStats *ref_stats = ref_tpl_frame->tpl_stats_ptr;
MV mv = tpl_stats->mv.as_mv;
int mv_row = mv.row >> 3;
int mv_col = mv.col >> 3;
int ref_pos_row = mi_row * MI_SIZE + mv_row;
int ref_pos_col = mi_col * MI_SIZE + mv_col;
const int bw = 4 << mi_size_wide_log2[bsize];
const int bh = 4 << mi_size_high_log2[bsize];
const int mi_height = mi_size_high[bsize];
const int mi_width = mi_size_wide[bsize];
const int pix_num = bw * bh;
// top-left on grid block location in pixel
int grid_pos_row_base = round_floor(ref_pos_row, bh) * bh;
int grid_pos_col_base = round_floor(ref_pos_col, bw) * bw;
int block;
for (block = 0; block < 4; ++block) {
int grid_pos_row = grid_pos_row_base + bh * (block >> 1);
int grid_pos_col = grid_pos_col_base + bw * (block & 0x01);
if (grid_pos_row >= 0 && grid_pos_row < ref_tpl_frame->mi_rows * MI_SIZE &&
grid_pos_col >= 0 && grid_pos_col < ref_tpl_frame->mi_cols * MI_SIZE) {
int overlap_area = get_overlap_area(
grid_pos_row, grid_pos_col, ref_pos_row, ref_pos_col, block, bsize);
int ref_mi_row = round_floor(grid_pos_row, bh) * mi_height;
int ref_mi_col = round_floor(grid_pos_col, bw) * mi_width;
int64_t mc_flow = tpl_stats->mc_dep_cost -
(tpl_stats->mc_dep_cost * tpl_stats->inter_cost) /
tpl_stats->intra_cost;
int idx, idy;
for (idy = 0; idy < mi_height; ++idy) {
for (idx = 0; idx < mi_width; ++idx) {
TplDepStats *des_stats =
&ref_stats[(ref_mi_row + idy) * ref_tpl_frame->stride +
(ref_mi_col + idx)];
des_stats->mc_flow += (mc_flow * overlap_area) / pix_num;
des_stats->mc_ref_cost +=
((tpl_stats->intra_cost - tpl_stats->inter_cost) * overlap_area) /
pix_num;
assert(overlap_area >= 0);
}
}
}
}
}
static void tpl_model_update(TplDepFrame *tpl_frame, TplDepStats *tpl_stats,
int mi_row, int mi_col, const BLOCK_SIZE bsize) {
int idx, idy;
const int mi_height = mi_size_high[bsize];
const int mi_width = mi_size_wide[bsize];
for (idy = 0; idy < mi_height; ++idy) {
for (idx = 0; idx < mi_width; ++idx) {
TplDepStats *tpl_ptr =
&tpl_stats[(mi_row + idy) * tpl_frame->stride + (mi_col + idx)];
tpl_model_update_b(tpl_frame, tpl_ptr, mi_row + idy, mi_col + idx,
BLOCK_4X4);
}
}
}
static void tpl_model_store(TplDepStats *tpl_stats, int mi_row, int mi_col,
BLOCK_SIZE bsize, int stride,
const TplDepStats *src_stats) {
const int mi_height = mi_size_high[bsize];
const int mi_width = mi_size_wide[bsize];
int idx, idy;
int64_t intra_cost = src_stats->intra_cost / (mi_height * mi_width);
int64_t inter_cost = src_stats->inter_cost / (mi_height * mi_width);
TplDepStats *tpl_ptr;
intra_cost = AOMMAX(1, intra_cost);
inter_cost = AOMMAX(1, inter_cost);
for (idy = 0; idy < mi_height; ++idy) {
tpl_ptr = &tpl_stats[(mi_row + idy) * stride + mi_col];
for (idx = 0; idx < mi_width; ++idx) {
tpl_ptr->intra_cost = intra_cost;
tpl_ptr->inter_cost = inter_cost;
tpl_ptr->mc_dep_cost = tpl_ptr->intra_cost + tpl_ptr->mc_flow;
tpl_ptr->ref_frame_index = src_stats->ref_frame_index;
tpl_ptr->mv.as_int = src_stats->mv.as_int;
++tpl_ptr;
}
}
}
static void mc_flow_dispenser(AV1_COMP *cpi, GF_PICTURE *gf_picture,
int frame_idx) {
TplDepFrame *tpl_frame = &cpi->tpl_stats[frame_idx];
YV12_BUFFER_CONFIG *this_frame = gf_picture[frame_idx].frame;
YV12_BUFFER_CONFIG *ref_frame[7] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
AV1_COMMON *cm = &cpi->common;
struct scale_factors sf;
int rdmult, idx;
ThreadData *td = &cpi->td;
MACROBLOCK *x = &td->mb;
MACROBLOCKD *xd = &x->e_mbd;
int mi_row, mi_col;
DECLARE_ALIGNED(16, uint16_t, predictor16[32 * 32 * 3]);
DECLARE_ALIGNED(16, uint8_t, predictor8[32 * 32 * 3]);
uint8_t *predictor;
DECLARE_ALIGNED(16, int16_t, src_diff[32 * 32]);
DECLARE_ALIGNED(16, tran_low_t, coeff[32 * 32]);
DECLARE_ALIGNED(16, tran_low_t, qcoeff[32 * 32]);
DECLARE_ALIGNED(16, tran_low_t, dqcoeff[32 * 32]);
const BLOCK_SIZE bsize = BLOCK_32X32;
const TX_SIZE tx_size = max_txsize_lookup[bsize];
const int mi_height = mi_size_high[bsize];
const int mi_width = mi_size_wide[bsize];
int64_t recon_error, sse;
// Setup scaling factor
av1_setup_scale_factors_for_frame(
&sf, this_frame->y_crop_width, this_frame->y_crop_height,
this_frame->y_crop_width, this_frame->y_crop_height);
if (is_cur_buf_hbd(xd))
predictor = CONVERT_TO_BYTEPTR(predictor16);
else
predictor = predictor8;
// Prepare reference frame pointers. If any reference frame slot is
// unavailable, the pointer will be set to Null.
for (idx = 0; idx < 7; ++idx) {
int rf_idx = gf_picture[frame_idx].ref_frame[idx];
if (rf_idx != -1) ref_frame[idx] = gf_picture[rf_idx].frame;
}
xd->mi = cm->mi_grid_visible;
xd->mi[0] = cm->mi;
xd->cur_buf = this_frame;
// Get rd multiplier set up.
rdmult = (int)av1_compute_rd_mult(cpi, tpl_frame->base_qindex);
if (rdmult < 1) rdmult = 1;
set_error_per_bit(&cpi->td.mb, rdmult);
av1_initialize_me_consts(cpi, &cpi->td.mb, tpl_frame->base_qindex);
tpl_frame->is_valid = 1;
cm->base_qindex = tpl_frame->base_qindex;
av1_frame_init_quantizer(cpi);
for (mi_row = 0; mi_row < cm->mi_rows; mi_row += mi_height) {
// Motion estimation row boundary
x->mv_limits.row_min = -((mi_row * MI_SIZE) + (17 - 2 * AOM_INTERP_EXTEND));
x->mv_limits.row_max =
(cm->mi_rows - 1 - mi_row) * MI_SIZE + (17 - 2 * AOM_INTERP_EXTEND);
for (mi_col = 0; mi_col < cm->mi_cols; mi_col += mi_width) {
TplDepStats tpl_stats;
mode_estimation(cpi, x, xd, &sf, gf_picture, frame_idx, src_diff, coeff,
qcoeff, dqcoeff, mi_row, mi_col, bsize, tx_size,
ref_frame, predictor, &recon_error, &sse, &tpl_stats);
// Motion flow dependency dispenser.
tpl_model_store(tpl_frame->tpl_stats_ptr, mi_row, mi_col, bsize,
tpl_frame->stride, &tpl_stats);
tpl_model_update(cpi->tpl_stats, tpl_frame->tpl_stats_ptr, mi_row, mi_col,
bsize);
}
}
}
static void init_gop_frames(AV1_COMP *cpi, GF_PICTURE *gf_picture,
const GF_GROUP *gf_group, int *tpl_group_frames,
const EncodeFrameInput *const frame_input) {
AV1_COMMON *cm = &cpi->common;
const SequenceHeader *const seq_params = &cm->seq_params;
int frame_idx = 0;
int i;
int gld_index = -1;
int alt_index = -1;
int lst_index = -1;
int extend_frame_count = 0;
int pframe_qindex = cpi->tpl_stats[2].base_qindex;
RefCntBuffer *frame_bufs = cm->buffer_pool->frame_bufs;
int recon_frame_index[INTER_REFS_PER_FRAME + 1] = { -1, -1, -1, -1,
-1, -1, -1, -1 };
// TODO(jingning): To be used later for gf frame type parsing.
(void)gf_group;
for (i = 0; i < FRAME_BUFFERS && frame_idx < INTER_REFS_PER_FRAME + 1; ++i) {
if (frame_bufs[i].ref_count == 0) {
alloc_frame_mvs(cm, &frame_bufs[i]);
if (aom_realloc_frame_buffer(
&frame_bufs[i].buf, cm->width, cm->height,
seq_params->subsampling_x, seq_params->subsampling_y,
seq_params->use_highbitdepth, cpi->oxcf.border_in_pixels,
cm->byte_alignment, NULL, NULL, NULL))
aom_internal_error(&cm->error, AOM_CODEC_MEM_ERROR,
"Failed to allocate frame buffer");
recon_frame_index[frame_idx] = i;
++frame_idx;
}
}
for (i = 0; i < INTER_REFS_PER_FRAME + 1; ++i) {
assert(recon_frame_index[i] >= 0);
cpi->tpl_recon_frames[i] = &frame_bufs[recon_frame_index[i]].buf;
}
*tpl_group_frames = 0;
// Initialize Golden reference frame.
gf_picture[0].frame = NULL;
RefCntBuffer *ref_buf = get_ref_frame_buf(cm, GOLDEN_FRAME);
if (ref_buf) gf_picture[0].frame = &ref_buf->buf;
for (i = 0; i < 7; ++i) gf_picture[0].ref_frame[i] = -1;
gld_index = 0;
++*tpl_group_frames;
// Initialize ARF frame
gf_picture[1].frame = frame_input->source;
gf_picture[1].ref_frame[0] = gld_index;
gf_picture[1].ref_frame[1] = lst_index;
gf_picture[1].ref_frame[2] = alt_index;
// TODO(yuec) Need o figure out full AV1 reference model
for (i = 3; i < 7; ++i) gf_picture[1].ref_frame[i] = -1;
alt_index = 1;
++*tpl_group_frames;
// Initialize P frames
for (frame_idx = 2; frame_idx < MAX_LAG_BUFFERS; ++frame_idx) {
struct lookahead_entry *buf =
av1_lookahead_peek(cpi->lookahead, frame_idx - 2);
if (buf == NULL) break;
gf_picture[frame_idx].frame = &buf->img;
gf_picture[frame_idx].ref_frame[0] = gld_index;
gf_picture[frame_idx].ref_frame[1] = lst_index;
gf_picture[frame_idx].ref_frame[2] = alt_index;
for (i = 3; i < 7; ++i) gf_picture[frame_idx].ref_frame[i] = -1;
++*tpl_group_frames;
lst_index = frame_idx;
if (frame_idx == cpi->rc.baseline_gf_interval + 1) break;
}
gld_index = frame_idx;
lst_index = AOMMAX(0, frame_idx - 1);
alt_index = -1;
++frame_idx;
// Extend two frames outside the current gf group.
for (; frame_idx < MAX_LAG_BUFFERS && extend_frame_count < 2; ++frame_idx) {
struct lookahead_entry *buf =
av1_lookahead_peek(cpi->lookahead, frame_idx - 2);
if (buf == NULL) break;
cpi->tpl_stats[frame_idx].base_qindex = pframe_qindex;
gf_picture[frame_idx].frame = &buf->img;
gf_picture[frame_idx].ref_frame[0] = gld_index;
gf_picture[frame_idx].ref_frame[1] = lst_index;
gf_picture[frame_idx].ref_frame[2] = alt_index;
for (i = 3; i < 7; ++i) gf_picture[frame_idx].ref_frame[i] = -1;
lst_index = frame_idx;
++*tpl_group_frames;
++extend_frame_count;
}
}
static void init_tpl_stats(AV1_COMP *cpi) {
int frame_idx;
for (frame_idx = 0; frame_idx < MAX_LAG_BUFFERS; ++frame_idx) {
TplDepFrame *tpl_frame = &cpi->tpl_stats[frame_idx];
memset(tpl_frame->tpl_stats_ptr, 0,
tpl_frame->height * tpl_frame->width *
sizeof(*tpl_frame->tpl_stats_ptr));
tpl_frame->is_valid = 0;
}
}
void av1_tpl_setup_stats(AV1_COMP *cpi,
const EncodeFrameInput *const frame_input) {
GF_PICTURE gf_picture[MAX_LAG_BUFFERS];
const GF_GROUP *gf_group = &cpi->twopass.gf_group;
int tpl_group_frames = 0;
int frame_idx;
init_gop_frames(cpi, gf_picture, gf_group, &tpl_group_frames, frame_input);
init_tpl_stats(cpi);
// Backward propagation from tpl_group_frames to 1.
for (frame_idx = tpl_group_frames - 1; frame_idx > 0; --frame_idx)
mc_flow_dispenser(cpi, gf_picture, frame_idx);
}

26
third_party/aom/av1/encoder/tpl_model.h vendored Normal file
View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2019, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#ifndef AOM_AV1_ENCODER_TPL_MODEL_H_
#define AOM_AV1_ENCODER_TPL_MODEL_H_
#ifdef __cplusplus
extern "C" {
#endif
void av1_tpl_setup_stats(AV1_COMP *cpi,
const EncodeFrameInput *const frame_input);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // AOM_AV1_ENCODER_TPL_MODEL_H_

View file

@ -11,45 +11,78 @@
#include "av1/encoder/x86/av1_txfm1d_sse4.h"
void av1_fdct32_new_sse4_1(const __m128i *input, __m128i *output,
int8_t cos_bit) {
void av1_fdct32_new_sse4_1(__m128i *input, __m128i *output, int cos_bit,
const int stride) {
__m128i buf0[32];
__m128i buf1[32];
const int32_t *cospi;
int startidx = 0 * stride;
int endidx = 31 * stride;
// stage 0
// stage 1
buf1[0] = _mm_add_epi32(input[0], input[31]);
buf1[31] = _mm_sub_epi32(input[0], input[31]);
buf1[1] = _mm_add_epi32(input[1], input[30]);
buf1[30] = _mm_sub_epi32(input[1], input[30]);
buf1[2] = _mm_add_epi32(input[2], input[29]);
buf1[29] = _mm_sub_epi32(input[2], input[29]);
buf1[3] = _mm_add_epi32(input[3], input[28]);
buf1[28] = _mm_sub_epi32(input[3], input[28]);
buf1[4] = _mm_add_epi32(input[4], input[27]);
buf1[27] = _mm_sub_epi32(input[4], input[27]);
buf1[5] = _mm_add_epi32(input[5], input[26]);
buf1[26] = _mm_sub_epi32(input[5], input[26]);
buf1[6] = _mm_add_epi32(input[6], input[25]);
buf1[25] = _mm_sub_epi32(input[6], input[25]);
buf1[7] = _mm_add_epi32(input[7], input[24]);
buf1[24] = _mm_sub_epi32(input[7], input[24]);
buf1[8] = _mm_add_epi32(input[8], input[23]);
buf1[23] = _mm_sub_epi32(input[8], input[23]);
buf1[9] = _mm_add_epi32(input[9], input[22]);
buf1[22] = _mm_sub_epi32(input[9], input[22]);
buf1[10] = _mm_add_epi32(input[10], input[21]);
buf1[21] = _mm_sub_epi32(input[10], input[21]);
buf1[11] = _mm_add_epi32(input[11], input[20]);
buf1[20] = _mm_sub_epi32(input[11], input[20]);
buf1[12] = _mm_add_epi32(input[12], input[19]);
buf1[19] = _mm_sub_epi32(input[12], input[19]);
buf1[13] = _mm_add_epi32(input[13], input[18]);
buf1[18] = _mm_sub_epi32(input[13], input[18]);
buf1[14] = _mm_add_epi32(input[14], input[17]);
buf1[17] = _mm_sub_epi32(input[14], input[17]);
buf1[15] = _mm_add_epi32(input[15], input[16]);
buf1[16] = _mm_sub_epi32(input[15], input[16]);
buf1[0] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[31] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[1] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[30] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[2] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[29] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[3] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[28] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[4] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[27] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[5] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[26] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[6] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[25] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[7] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[24] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[8] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[23] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[9] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[22] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[10] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[21] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[11] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[20] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[12] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[19] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[13] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[18] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[14] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[17] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += stride;
endidx -= stride;
buf1[15] = _mm_add_epi32(input[startidx], input[endidx]);
buf1[16] = _mm_sub_epi32(input[startidx], input[endidx]);
// stage 2
cospi = cospi_arr(cos_bit);
@ -296,39 +329,71 @@ void av1_fdct32_new_sse4_1(const __m128i *input, __m128i *output,
btf_32_sse4_1_type1(cospi[6], cospi[58], buf1[23], buf1[24], buf0[23],
buf0[24], cos_bit);
startidx = 0 * stride;
endidx = 31 * stride;
// stage 9
output[0] = buf0[0];
output[1] = buf0[16];
output[2] = buf0[8];
output[3] = buf0[24];
output[4] = buf0[4];
output[5] = buf0[20];
output[6] = buf0[12];
output[7] = buf0[28];
output[8] = buf0[2];
output[9] = buf0[18];
output[10] = buf0[10];
output[11] = buf0[26];
output[12] = buf0[6];
output[13] = buf0[22];
output[14] = buf0[14];
output[15] = buf0[30];
output[16] = buf0[1];
output[17] = buf0[17];
output[18] = buf0[9];
output[19] = buf0[25];
output[20] = buf0[5];
output[21] = buf0[21];
output[22] = buf0[13];
output[23] = buf0[29];
output[24] = buf0[3];
output[25] = buf0[19];
output[26] = buf0[11];
output[27] = buf0[27];
output[28] = buf0[7];
output[29] = buf0[23];
output[30] = buf0[15];
output[31] = buf0[31];
output[startidx] = buf0[0];
output[endidx] = buf0[31];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[16];
output[endidx] = buf0[15];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[8];
output[endidx] = buf0[23];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[24];
output[endidx] = buf0[7];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[4];
output[endidx] = buf0[27];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[20];
output[endidx] = buf0[11];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[12];
output[endidx] = buf0[19];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[28];
output[endidx] = buf0[3];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[2];
output[endidx] = buf0[29];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[18];
output[endidx] = buf0[13];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[10];
output[endidx] = buf0[21];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[26];
output[endidx] = buf0[5];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[6];
output[endidx] = buf0[25];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[22];
output[endidx] = buf0[9];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[14];
output[endidx] = buf0[17];
startidx += stride;
endidx -= stride;
output[startidx] = buf0[30];
output[endidx] = buf0[1];
}
void av1_fadst4_new_sse4_1(const __m128i *input, __m128i *output,
@ -394,9 +459,8 @@ void av1_fadst4_new_sse4_1(const __m128i *input, __m128i *output,
}
}
void av1_fdct64_new_sse4_1(const __m128i *input, __m128i *output,
int8_t cos_bit, const int instride,
const int outstride) {
void av1_fdct64_new_sse4_1(__m128i *input, __m128i *output, int8_t cos_bit,
const int instride, const int outstride) {
const int32_t *cospi = cospi_arr(cos_bit);
const __m128i __rounding = _mm_set1_epi32(1 << (cos_bit - 1));
@ -479,72 +543,136 @@ void av1_fdct64_new_sse4_1(const __m128i *input, __m128i *output,
__m128i cospi_p03 = _mm_set1_epi32(cospi[3]);
__m128i cospi_p61 = _mm_set1_epi32(cospi[61]);
int startidx = 0 * instride;
int endidx = 63 * instride;
// stage 1
__m128i x1[64];
x1[0] = _mm_add_epi32(input[0 * instride], input[63 * instride]);
x1[63] = _mm_sub_epi32(input[0 * instride], input[63 * instride]);
x1[1] = _mm_add_epi32(input[1 * instride], input[62 * instride]);
x1[62] = _mm_sub_epi32(input[1 * instride], input[62 * instride]);
x1[2] = _mm_add_epi32(input[2 * instride], input[61 * instride]);
x1[61] = _mm_sub_epi32(input[2 * instride], input[61 * instride]);
x1[3] = _mm_add_epi32(input[3 * instride], input[60 * instride]);
x1[60] = _mm_sub_epi32(input[3 * instride], input[60 * instride]);
x1[4] = _mm_add_epi32(input[4 * instride], input[59 * instride]);
x1[59] = _mm_sub_epi32(input[4 * instride], input[59 * instride]);
x1[5] = _mm_add_epi32(input[5 * instride], input[58 * instride]);
x1[58] = _mm_sub_epi32(input[5 * instride], input[58 * instride]);
x1[6] = _mm_add_epi32(input[6 * instride], input[57 * instride]);
x1[57] = _mm_sub_epi32(input[6 * instride], input[57 * instride]);
x1[7] = _mm_add_epi32(input[7 * instride], input[56 * instride]);
x1[56] = _mm_sub_epi32(input[7 * instride], input[56 * instride]);
x1[8] = _mm_add_epi32(input[8 * instride], input[55 * instride]);
x1[55] = _mm_sub_epi32(input[8 * instride], input[55 * instride]);
x1[9] = _mm_add_epi32(input[9 * instride], input[54 * instride]);
x1[54] = _mm_sub_epi32(input[9 * instride], input[54 * instride]);
x1[10] = _mm_add_epi32(input[10 * instride], input[53 * instride]);
x1[53] = _mm_sub_epi32(input[10 * instride], input[53 * instride]);
x1[11] = _mm_add_epi32(input[11 * instride], input[52 * instride]);
x1[52] = _mm_sub_epi32(input[11 * instride], input[52 * instride]);
x1[12] = _mm_add_epi32(input[12 * instride], input[51 * instride]);
x1[51] = _mm_sub_epi32(input[12 * instride], input[51 * instride]);
x1[13] = _mm_add_epi32(input[13 * instride], input[50 * instride]);
x1[50] = _mm_sub_epi32(input[13 * instride], input[50 * instride]);
x1[14] = _mm_add_epi32(input[14 * instride], input[49 * instride]);
x1[49] = _mm_sub_epi32(input[14 * instride], input[49 * instride]);
x1[15] = _mm_add_epi32(input[15 * instride], input[48 * instride]);
x1[48] = _mm_sub_epi32(input[15 * instride], input[48 * instride]);
x1[16] = _mm_add_epi32(input[16 * instride], input[47 * instride]);
x1[47] = _mm_sub_epi32(input[16 * instride], input[47 * instride]);
x1[17] = _mm_add_epi32(input[17 * instride], input[46 * instride]);
x1[46] = _mm_sub_epi32(input[17 * instride], input[46 * instride]);
x1[18] = _mm_add_epi32(input[18 * instride], input[45 * instride]);
x1[45] = _mm_sub_epi32(input[18 * instride], input[45 * instride]);
x1[19] = _mm_add_epi32(input[19 * instride], input[44 * instride]);
x1[44] = _mm_sub_epi32(input[19 * instride], input[44 * instride]);
x1[20] = _mm_add_epi32(input[20 * instride], input[43 * instride]);
x1[43] = _mm_sub_epi32(input[20 * instride], input[43 * instride]);
x1[21] = _mm_add_epi32(input[21 * instride], input[42 * instride]);
x1[42] = _mm_sub_epi32(input[21 * instride], input[42 * instride]);
x1[22] = _mm_add_epi32(input[22 * instride], input[41 * instride]);
x1[41] = _mm_sub_epi32(input[22 * instride], input[41 * instride]);
x1[23] = _mm_add_epi32(input[23 * instride], input[40 * instride]);
x1[40] = _mm_sub_epi32(input[23 * instride], input[40 * instride]);
x1[24] = _mm_add_epi32(input[24 * instride], input[39 * instride]);
x1[39] = _mm_sub_epi32(input[24 * instride], input[39 * instride]);
x1[25] = _mm_add_epi32(input[25 * instride], input[38 * instride]);
x1[38] = _mm_sub_epi32(input[25 * instride], input[38 * instride]);
x1[26] = _mm_add_epi32(input[26 * instride], input[37 * instride]);
x1[37] = _mm_sub_epi32(input[26 * instride], input[37 * instride]);
x1[27] = _mm_add_epi32(input[27 * instride], input[36 * instride]);
x1[36] = _mm_sub_epi32(input[27 * instride], input[36 * instride]);
x1[28] = _mm_add_epi32(input[28 * instride], input[35 * instride]);
x1[35] = _mm_sub_epi32(input[28 * instride], input[35 * instride]);
x1[29] = _mm_add_epi32(input[29 * instride], input[34 * instride]);
x1[34] = _mm_sub_epi32(input[29 * instride], input[34 * instride]);
x1[30] = _mm_add_epi32(input[30 * instride], input[33 * instride]);
x1[33] = _mm_sub_epi32(input[30 * instride], input[33 * instride]);
x1[31] = _mm_add_epi32(input[31 * instride], input[32 * instride]);
x1[32] = _mm_sub_epi32(input[31 * instride], input[32 * instride]);
x1[0] = _mm_add_epi32(input[startidx], input[endidx]);
x1[63] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[1] = _mm_add_epi32(input[startidx], input[endidx]);
x1[62] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[2] = _mm_add_epi32(input[startidx], input[endidx]);
x1[61] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[3] = _mm_add_epi32(input[startidx], input[endidx]);
x1[60] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[4] = _mm_add_epi32(input[startidx], input[endidx]);
x1[59] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[5] = _mm_add_epi32(input[startidx], input[endidx]);
x1[58] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[6] = _mm_add_epi32(input[startidx], input[endidx]);
x1[57] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[7] = _mm_add_epi32(input[startidx], input[endidx]);
x1[56] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[8] = _mm_add_epi32(input[startidx], input[endidx]);
x1[55] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[9] = _mm_add_epi32(input[startidx], input[endidx]);
x1[54] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[10] = _mm_add_epi32(input[startidx], input[endidx]);
x1[53] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[11] = _mm_add_epi32(input[startidx], input[endidx]);
x1[52] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[12] = _mm_add_epi32(input[startidx], input[endidx]);
x1[51] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[13] = _mm_add_epi32(input[startidx], input[endidx]);
x1[50] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[14] = _mm_add_epi32(input[startidx], input[endidx]);
x1[49] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[15] = _mm_add_epi32(input[startidx], input[endidx]);
x1[48] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[16] = _mm_add_epi32(input[startidx], input[endidx]);
x1[47] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[17] = _mm_add_epi32(input[startidx], input[endidx]);
x1[46] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[18] = _mm_add_epi32(input[startidx], input[endidx]);
x1[45] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[19] = _mm_add_epi32(input[startidx], input[endidx]);
x1[44] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[20] = _mm_add_epi32(input[startidx], input[endidx]);
x1[43] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[21] = _mm_add_epi32(input[startidx], input[endidx]);
x1[42] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[22] = _mm_add_epi32(input[startidx], input[endidx]);
x1[41] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[23] = _mm_add_epi32(input[startidx], input[endidx]);
x1[40] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[24] = _mm_add_epi32(input[startidx], input[endidx]);
x1[39] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[25] = _mm_add_epi32(input[startidx], input[endidx]);
x1[38] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[26] = _mm_add_epi32(input[startidx], input[endidx]);
x1[37] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[27] = _mm_add_epi32(input[startidx], input[endidx]);
x1[36] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[28] = _mm_add_epi32(input[startidx], input[endidx]);
x1[35] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[29] = _mm_add_epi32(input[startidx], input[endidx]);
x1[34] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[30] = _mm_add_epi32(input[startidx], input[endidx]);
x1[33] = _mm_sub_epi32(input[startidx], input[endidx]);
startidx += instride;
endidx -= instride;
x1[31] = _mm_add_epi32(input[startidx], input[endidx]);
x1[32] = _mm_sub_epi32(input[startidx], input[endidx]);
// stage 2
__m128i x2[64];
@ -1149,69 +1277,141 @@ void av1_fdct64_new_sse4_1(const __m128i *input, __m128i *output,
btf_32_type1_sse4_1_new(cospi_p03, cospi_p61, x9[47], x9[48], x10[47],
x10[48], __rounding, cos_bit);
startidx = 0 * outstride;
endidx = 63 * outstride;
// stage 11
output[0 * outstride] = x10[0];
output[1 * outstride] = x10[32];
output[2 * outstride] = x10[16];
output[3 * outstride] = x10[48];
output[4 * outstride] = x10[8];
output[5 * outstride] = x10[40];
output[6 * outstride] = x10[24];
output[7 * outstride] = x10[56];
output[8 * outstride] = x10[4];
output[9 * outstride] = x10[36];
output[10 * outstride] = x10[20];
output[11 * outstride] = x10[52];
output[12 * outstride] = x10[12];
output[13 * outstride] = x10[44];
output[14 * outstride] = x10[28];
output[15 * outstride] = x10[60];
output[16 * outstride] = x10[2];
output[17 * outstride] = x10[34];
output[18 * outstride] = x10[18];
output[19 * outstride] = x10[50];
output[20 * outstride] = x10[10];
output[21 * outstride] = x10[42];
output[22 * outstride] = x10[26];
output[23 * outstride] = x10[58];
output[24 * outstride] = x10[6];
output[25 * outstride] = x10[38];
output[26 * outstride] = x10[22];
output[27 * outstride] = x10[54];
output[28 * outstride] = x10[14];
output[29 * outstride] = x10[46];
output[30 * outstride] = x10[30];
output[31 * outstride] = x10[62];
output[32 * outstride] = x10[1];
output[33 * outstride] = x10[33];
output[34 * outstride] = x10[17];
output[35 * outstride] = x10[49];
output[36 * outstride] = x10[9];
output[37 * outstride] = x10[41];
output[38 * outstride] = x10[25];
output[39 * outstride] = x10[57];
output[40 * outstride] = x10[5];
output[41 * outstride] = x10[37];
output[42 * outstride] = x10[21];
output[43 * outstride] = x10[53];
output[44 * outstride] = x10[13];
output[45 * outstride] = x10[45];
output[46 * outstride] = x10[29];
output[47 * outstride] = x10[61];
output[48 * outstride] = x10[3];
output[49 * outstride] = x10[35];
output[50 * outstride] = x10[19];
output[51 * outstride] = x10[51];
output[52 * outstride] = x10[11];
output[53 * outstride] = x10[43];
output[54 * outstride] = x10[27];
output[55 * outstride] = x10[59];
output[56 * outstride] = x10[7];
output[57 * outstride] = x10[39];
output[58 * outstride] = x10[23];
output[59 * outstride] = x10[55];
output[60 * outstride] = x10[15];
output[61 * outstride] = x10[47];
output[62 * outstride] = x10[31];
output[63 * outstride] = x10[63];
output[startidx] = x10[0];
output[endidx] = x10[63];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[32];
output[endidx] = x10[31];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[16];
output[endidx] = x10[47];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[48];
output[endidx] = x10[15];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[8];
output[endidx] = x10[55];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[40];
output[endidx] = x10[23];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[24];
output[endidx] = x10[39];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[56];
output[endidx] = x10[7];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[4];
output[endidx] = x10[59];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[36];
output[endidx] = x10[27];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[20];
output[endidx] = x10[43];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[52];
output[endidx] = x10[11];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[12];
output[endidx] = x10[51];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[44];
output[endidx] = x10[19];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[28];
output[endidx] = x10[35];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[60];
output[endidx] = x10[3];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[2];
output[endidx] = x10[61];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[34];
output[endidx] = x10[29];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[18];
output[endidx] = x10[45];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[50];
output[endidx] = x10[13];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[10];
output[endidx] = x10[53];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[42];
output[endidx] = x10[21];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[26];
output[endidx] = x10[37];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[58];
output[endidx] = x10[5];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[6];
output[endidx] = x10[57];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[38];
output[endidx] = x10[25];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[22];
output[endidx] = x10[41];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[54];
output[endidx] = x10[9];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[14];
output[endidx] = x10[49];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[46];
output[endidx] = x10[17];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[30];
output[endidx] = x10[33];
startidx += outstride;
endidx -= outstride;
output[startidx] = x10[62];
output[endidx] = x10[1];
}
void av1_idtx32_new_sse4_1(__m128i *input, __m128i *output, int cos_bit,
const int col_num) {
(void)cos_bit;
for (int i = 0; i < 32; i++) {
output[i * col_num] = _mm_slli_epi32(input[i * col_num], 2);
}
}

View file

@ -1436,45 +1436,6 @@ static INLINE void fidentity16x32_new_avx2(const __m256i *input,
}
}
static INLINE void av1_round_shift_array_32_avx2(__m256i *input,
__m256i *output,
const int size,
const int bit) {
if (bit > 0) {
int i;
for (i = 0; i < size; i++) {
output[i] = av1_round_shift_32_avx2(input[i], bit);
}
} else {
int i;
for (i = 0; i < size; i++) {
output[i] = _mm256_slli_epi32(input[i], -bit);
}
}
}
static INLINE void av1_round_shift_rect_array_32_avx2(__m256i *input,
__m256i *output,
const int size,
const int bit) {
const __m256i sqrt2 = _mm256_set1_epi32(NewSqrt2);
if (bit > 0) {
int i;
for (i = 0; i < size; i++) {
const __m256i r0 = av1_round_shift_32_avx2(input[i], bit);
const __m256i r1 = _mm256_mullo_epi32(sqrt2, r0);
output[i] = av1_round_shift_32_avx2(r1, NewSqrt2Bits);
}
} else {
int i;
for (i = 0; i < size; i++) {
const __m256i r0 = _mm256_slli_epi32(input[i], -bit);
const __m256i r1 = _mm256_mullo_epi32(sqrt2, r0);
output[i] = av1_round_shift_32_avx2(r1, NewSqrt2Bits);
}
}
}
static INLINE void transpose_32_8x8_avx2(int stride, const __m256i *inputA,
__m256i *output) {
__m256i temp0 = _mm256_unpacklo_epi32(inputA[0], inputA[2]);
@ -1540,6 +1501,9 @@ static INLINE void store_rect_buffer_16bit_to_32bit_w16_avx2(
}
}
typedef void (*transform_1d_avx2)(const __m256i *input, __m256i *output,
int8_t cos_bit);
static const transform_1d_avx2 col_txfm16x32_arr[TX_TYPES] = {
fdct16x32_new_avx2, // DCT_DCT
NULL, // ADST_DCT
@ -1885,8 +1849,8 @@ static void lowbd_fwd_txfm2d_64x32_avx2(const int16_t *input, int32_t *output,
}
av1_fdct64_new_avx2(bufA, bufA, cos_bit_row);
av1_fdct64_new_avx2(bufB, bufB, cos_bit_row);
av1_round_shift_rect_array_32_avx2(bufA, bufA, 32, -shift[2]);
av1_round_shift_rect_array_32_avx2(bufB, bufB, 32, -shift[2]);
av1_round_shift_rect_array_32_avx2(bufA, bufA, 32, -shift[2], NewSqrt2);
av1_round_shift_rect_array_32_avx2(bufB, bufB, 32, -shift[2], NewSqrt2);
int32_t *output8 = output + 16 * 32 * i;
for (int j = 0; j < 4; ++j) {
@ -1935,8 +1899,8 @@ static void lowbd_fwd_txfm2d_32x64_avx2(const int16_t *input, int32_t *output,
}
av1_fdct32_new_avx2(bufA, bufA, cos_bit_row);
av1_fdct32_new_avx2(bufB, bufB, cos_bit_row);
av1_round_shift_rect_array_32_avx2(bufA, bufA, 32, -shift[2]);
av1_round_shift_rect_array_32_avx2(bufB, bufB, 32, -shift[2]);
av1_round_shift_rect_array_32_avx2(bufA, bufA, 32, -shift[2], NewSqrt2);
av1_round_shift_rect_array_32_avx2(bufB, bufB, 32, -shift[2], NewSqrt2);
int32_t *output8 = output + 16 * 32 * i;
for (int j = 0; j < 4; ++j) {

View file

@ -29,31 +29,22 @@ static INLINE void int16_array_with_stride_to_int32_array_without_stride(
}
}
typedef void (*TxfmFuncSSE2)(const __m128i *input, __m128i *output,
typedef void (*TxfmFuncSSE2)(__m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range);
static void fdct32_new_sse4_1(const __m128i *input, __m128i *output,
static void fdct32_new_sse4_1(__m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range) {
const int txfm_size = 32;
const int num_per_128 = 4;
__m128i buf0[32];
__m128i buf1[32];
int col_num = txfm_size / num_per_128;
int col;
(void)stage_range;
for (col = 0; col < col_num; col++) {
int j;
for (j = 0; j < 32; ++j) {
buf0[j] = input[j * col_num + col];
}
av1_fdct32_new_sse4_1(buf0, buf1, cos_bit);
for (j = 0; j < 32; ++j) {
output[j * col_num + col] = buf1[j];
}
av1_fdct32_new_sse4_1((input + col), (output + col), cos_bit, col_num);
}
}
static void fdct64_new_sse4_1(const __m128i *input, __m128i *output,
static void fdct64_new_sse4_1(__m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range) {
const int txfm_size = 64;
const int num_per_128 = 4;
@ -64,11 +55,20 @@ static void fdct64_new_sse4_1(const __m128i *input, __m128i *output,
col_num);
}
}
static void idtx32x32_sse4_1(__m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range) {
(void)stage_range;
for (int i = 0; i < 8; i++) {
av1_idtx32_new_sse4_1(&input[i * 32], &output[i * 32], cos_bit, 1);
}
}
static INLINE TxfmFuncSSE2 fwd_txfm_type_to_func(TXFM_TYPE txfm_type) {
switch (txfm_type) {
case TXFM_TYPE_DCT32: return fdct32_new_sse4_1; break;
case TXFM_TYPE_DCT64: return fdct64_new_sse4_1; break;
case TXFM_TYPE_IDENTITY32: return idtx32x32_sse4_1; break;
default: assert(0);
}
return NULL;
@ -142,7 +142,7 @@ static INLINE void fwd_txfm2d_64x64_sse4_1(const int16_t *input,
txfm2d_size_128 = (col_num >> 1) * (txfm_size >> 1);
av1_round_shift_array_32_sse4_1(out_128, buf_128, txfm2d_size_128, -shift[2]);
transpose_32x32(buf_128, out_128);
transpose_8nx8n(buf_128, out_128, 32, 32);
}
void av1_fwd_txfm2d_32x32_sse4_1(const int16_t *input, int32_t *output,
@ -317,8 +317,8 @@ static void lowbd_fwd_txfm2d_32x64_sse4_1(const int16_t *input, int32_t *output,
bufA[j] = _mm_cvtepi16_epi32(buf[j]);
bufB[j] = _mm_cvtepi16_epi32(_mm_unpackhi_epi64(buf[j], buf[j]));
}
av1_fdct32_new_sse4_1(bufA, bufA, cos_bit_row);
av1_fdct32_new_sse4_1(bufB, bufB, cos_bit_row);
av1_fdct32_new_sse4_1(bufA, bufA, cos_bit_row, 1);
av1_fdct32_new_sse4_1(bufB, bufB, cos_bit_row, 1);
av1_round_shift_rect_array_32_sse4_1(bufA, bufA, 32, -shift[2], NewSqrt2);
av1_round_shift_rect_array_32_sse4_1(bufB, bufB, 32, -shift[2], NewSqrt2);

View file

@ -13,13 +13,6 @@
#define AOM_AV1_ENCODER_X86_AV1_FWD_TXFM_AVX2_H_
#include <immintrin.h>
static INLINE __m256i av1_round_shift_32_avx2(__m256i vec, int bit) {
__m256i tmp, round;
round = _mm256_set1_epi32(1 << (bit - 1));
tmp = _mm256_add_epi32(vec, round);
return _mm256_srai_epi32(tmp, bit);
}
// out0 = in0*w0 + in1*w1
// out1 = -in1*w0 + in0*w1
static INLINE void btf_32_avx2_type0(const int32_t w0, const int32_t w1,

View file

@ -26,12 +26,10 @@ void av1_fdct8_new_sse4_1(const __m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range);
void av1_fdct16_new_sse4_1(const __m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range);
void av1_fdct32_new_sse4_1(const __m128i *input, __m128i *output,
int8_t cos_bit);
void av1_fdct64_new_sse4_1(const __m128i *input, __m128i *output,
int8_t cos_bit, const int instride,
const int outstride);
void av1_fdct32_new_sse4_1(__m128i *input, __m128i *output, int cos_bit,
const int stride);
void av1_fdct64_new_sse4_1(__m128i *input, __m128i *output, int8_t cos_bit,
const int instride, const int outstride);
void av1_fadst4_new_sse4_1(const __m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range);
void av1_fadst8_new_sse4_1(const __m128i *input, __m128i *output,
@ -56,6 +54,10 @@ void av1_iadst8_new_sse4_1(const __m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range);
void av1_iadst16_new_sse4_1(const __m128i *input, __m128i *output,
const int8_t cos_bit, const int8_t *stage_range);
void av1_idtx32_new_sse4_1(__m128i *input, __m128i *output, int cos_bit,
const int col_num);
static INLINE void transpose_32_4x4(int stride, const __m128i *input,
__m128i *output) {
__m128i temp0 = _mm_unpacklo_epi32(input[0 * stride], input[2 * stride]);

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2018, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <assert.h>
#include <immintrin.h>
#include "config/av1_rtcd.h"
#include "aom_ports/mem.h"
#include "av1/encoder/corner_match.h"
DECLARE_ALIGNED(16, static const uint8_t, byte_mask[16]) = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0
};
#if MATCH_SZ != 13
#error "Need to change byte_mask in corner_match_sse4.c if MATCH_SZ != 13"
#endif
/* Compute corr(im1, im2) * MATCH_SZ * stddev(im1), where the
correlation/standard deviation are taken over MATCH_SZ by MATCH_SZ windows
of each image, centered at (x1, y1) and (x2, y2) respectively.
*/
double compute_cross_correlation_avx2(unsigned char *im1, int stride1, int x1,
int y1, unsigned char *im2, int stride2,
int x2, int y2) {
int i, stride1_i = 0, stride2_i = 0;
__m256i temp1, sum_vec, sumsq2_vec, cross_vec, v, v1_1, v2_1;
const __m128i mask = _mm_load_si128((__m128i *)byte_mask);
const __m256i zero = _mm256_setzero_si256();
__m128i v1, v2;
sum_vec = zero;
sumsq2_vec = zero;
cross_vec = zero;
im1 += (y1 - MATCH_SZ_BY2) * stride1 + (x1 - MATCH_SZ_BY2);
im2 += (y2 - MATCH_SZ_BY2) * stride2 + (x2 - MATCH_SZ_BY2);
for (i = 0; i < MATCH_SZ; ++i) {
v1 = _mm_and_si128(_mm_loadu_si128((__m128i *)&im1[stride1_i]), mask);
v1_1 = _mm256_cvtepu8_epi16(v1);
v2 = _mm_and_si128(_mm_loadu_si128((__m128i *)&im2[stride2_i]), mask);
v2_1 = _mm256_cvtepu8_epi16(v2);
v = _mm256_insertf128_si256(_mm256_castsi128_si256(v1), v2, 1);
sumsq2_vec = _mm256_add_epi32(sumsq2_vec, _mm256_madd_epi16(v2_1, v2_1));
sum_vec = _mm256_add_epi16(sum_vec, _mm256_sad_epu8(v, zero));
cross_vec = _mm256_add_epi32(cross_vec, _mm256_madd_epi16(v1_1, v2_1));
stride1_i += stride1;
stride2_i += stride2;
}
__m256i sum_vec1 = _mm256_srli_si256(sum_vec, 8);
sum_vec = _mm256_add_epi32(sum_vec, sum_vec1);
int sum1_acc = _mm_cvtsi128_si32(_mm256_castsi256_si128(sum_vec));
int sum2_acc = _mm256_extract_epi32(sum_vec, 4);
__m256i unp_low = _mm256_unpacklo_epi64(sumsq2_vec, cross_vec);
__m256i unp_hig = _mm256_unpackhi_epi64(sumsq2_vec, cross_vec);
temp1 = _mm256_add_epi32(unp_low, unp_hig);
__m128i low_sumsq = _mm256_castsi256_si128(temp1);
low_sumsq = _mm_add_epi32(low_sumsq, _mm256_extractf128_si256(temp1, 1));
low_sumsq = _mm_add_epi32(low_sumsq, _mm_srli_epi64(low_sumsq, 32));
int sumsq2_acc = _mm_cvtsi128_si32(low_sumsq);
int cross_acc = _mm_extract_epi32(low_sumsq, 2);
int var2 = sumsq2_acc * MATCH_SZ_SQ - sum2_acc * sum2_acc;
int cov = cross_acc * MATCH_SZ_SQ - sum1_acc * sum2_acc;
return cov / sqrt((double)var2);
}

View file

@ -26,14 +26,6 @@ void av1_txb_init_levels_avx2(const tran_low_t *const coeff, const int width,
const int stride = width + TX_PAD_HOR;
const __m256i y_zeros = _mm256_setzero_si256();
const int32_t pre_len = sizeof(*levels) * TX_PAD_TOP * stride;
uint8_t *pre_buf = levels - TX_PAD_TOP * stride;
uint8_t *pre_buf_end = pre_buf + pre_len;
do {
yy_storeu_256(pre_buf, y_zeros);
pre_buf += 32;
} while (pre_buf < pre_buf_end);
const int32_t bottom_len = sizeof(*levels) * (TX_PAD_BOTTOM * stride);
uint8_t *bottom_buf_end = levels + (height + TX_PAD_BOTTOM) * stride;
uint8_t *bottom_buf = bottom_buf_end - ((bottom_len + 31) & (~31));

View file

@ -23,14 +23,6 @@ void av1_txb_init_levels_sse4_1(const tran_low_t *const coeff, const int width,
const int stride = width + TX_PAD_HOR;
const __m128i zeros = _mm_setzero_si128();
const int32_t pre_len = sizeof(*levels) * TX_PAD_TOP * stride;
uint8_t *pre_buf = levels - TX_PAD_TOP * stride;
uint8_t *pre_buf_end = pre_buf + pre_len;
do {
_mm_storeu_si128((__m128i *)(pre_buf), zeros);
pre_buf += 16;
} while (pre_buf < pre_buf_end);
const int32_t bottom_len = sizeof(*levels) * (TX_PAD_BOTTOM * stride);
uint8_t *bottom_buf = levels + stride * height;
uint8_t *bottom_buf_end = bottom_buf + bottom_len;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,243 @@
/*
* Copyright (c) 2018, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <stdbool.h>
#include <assert.h>
#include <pmmintrin.h>
#include "config/av1_rtcd.h"
#include "av1/encoder/ml.h"
// In order to avoid the high-latency of swapping between FPU and SIMD
// operations, we keep the result in a 128-bit register even though we only
// care about a single value.
static void nn_propagate_8to1(const float *const inputs,
const float *const weights,
__m128 *const output) {
const __m128 inputs_h = _mm_loadu_ps(&inputs[4]);
const __m128 inputs_l = _mm_loadu_ps(inputs);
const __m128 weights_h = _mm_loadu_ps(&weights[4]);
const __m128 weights_l = _mm_loadu_ps(weights);
const __m128 mul_h = _mm_mul_ps(inputs_h, weights_h);
const __m128 mul_l = _mm_mul_ps(inputs_l, weights_l);
// [7 6 5 4] [3 2 1 0] (weight and input indices)
const __m128 vadd = _mm_add_ps(mul_l, mul_h);
// [7+3 6+2 5+1 4+0]
const __m128 hadd1 = _mm_hadd_ps(vadd, vadd);
// [7+6+3+2 5+4+1+0 7+6+3+2 5+4+1+0]
const __m128 hadd2 = _mm_hadd_ps(hadd1, hadd1);
// [7+6+5+4+3+2+1+0 7+6+5+4+3+2+1+0 7+6+5+4+3+2+1+0 7+6+5+4+3+2+1+0]
*output = _mm_add_ps(*output, hadd2);
}
static void nn_propagate_4to1(const float *const inputs,
const float *const weights,
__m128 *const output) {
const __m128 inputs128 = _mm_loadu_ps(inputs);
const __m128 weights128 = _mm_loadu_ps(weights);
const __m128 mul = _mm_mul_ps(inputs128, weights128);
// [3 2 1 0] (weight and input indices)
const __m128 hadd1 = _mm_hadd_ps(mul, mul);
// [3+2 1+0 3+2 1+0]
const __m128 hadd2 = _mm_hadd_ps(hadd1, hadd1);
// [3+2+1+0 3+2+1+0 3+2+1+0 3+2+1+0]
*output = _mm_add_ps(*output, hadd2);
}
static void nn_propagate_4to4(const float *const inputs,
const float *const weights, __m128 *const outputs,
const int num_inputs) {
const __m128 inputs128 = _mm_loadu_ps(inputs);
__m128 hadd[2];
for (int i = 0; i < 2; i++) { // For each pair of outputs
const __m128 weight0 = _mm_loadu_ps(&weights[2 * i * num_inputs]);
const __m128 mul0 = _mm_mul_ps(weight0, inputs128);
const __m128 weight1 = _mm_loadu_ps(&weights[(2 * i + 1) * num_inputs]);
const __m128 mul1 = _mm_mul_ps(weight1, inputs128);
hadd[i] = _mm_hadd_ps(mul0, mul1);
}
// hadd[0] = [7+6 5+4 3+2 1+0] (weight indices)
// hadd[1] = [15+14 13+12 11+10 9+8]
const __m128 hh = _mm_hadd_ps(hadd[0], hadd[1]);
// [15+14+13+12 11+10+9+8 7+6+5+4 3+2+1+0]
*outputs = _mm_add_ps(*outputs, hh);
}
static void nn_propagate_4to8(const float *const inputs,
const float *const weights, __m128 *const out_h,
__m128 *const out_l, const int num_inputs) {
const __m128 inputs128 = _mm_loadu_ps(inputs);
__m128 hadd[4];
for (int i = 0; i < 4; i++) { // For each pair of outputs
const __m128 weight0 = _mm_loadu_ps(&weights[2 * i * num_inputs]);
const __m128 weight1 = _mm_loadu_ps(&weights[(2 * i + 1) * num_inputs]);
const __m128 mul0 = _mm_mul_ps(inputs128, weight0);
const __m128 mul1 = _mm_mul_ps(inputs128, weight1);
hadd[i] = _mm_hadd_ps(mul0, mul1);
}
// hadd[0] = [7+6 5+4 3+2 1+0] (weight indices)
// hadd[1] = [15+14 13+12 11+10 9+8]
// hadd[2] = [23+22 21+20 19+18 17+16]
// hadd[3] = [31+30 29+28 27+26 25+24]
const __m128 hh0 = _mm_hadd_ps(hadd[0], hadd[1]);
// [15+14+13+12 11+10+9+8 7+6+5+4 3+2+1+0]
const __m128 hh1 = _mm_hadd_ps(hadd[2], hadd[3]);
// [31+30+29+28 27+26+25+24 23+22+21+20 19+18+17+16]
*out_h = _mm_add_ps(*out_h, hh1);
*out_l = _mm_add_ps(*out_l, hh0);
}
static void nn_propagate_8to4(const float *const inputs,
const float *const weights, __m128 *const outputs,
const int num_inputs) {
const __m128 inputs_h = _mm_loadu_ps(inputs + 4);
const __m128 inputs_l = _mm_loadu_ps(inputs);
// [7 6 5 4] [3 2 1 0] (input indices)
__m128 add[4];
for (int i = 0; i < 4; i++) { // For each output:
const __m128 weight_h = _mm_loadu_ps(&weights[i * num_inputs + 4]);
const __m128 weight_l = _mm_loadu_ps(&weights[i * num_inputs]);
const __m128 mul_h = _mm_mul_ps(inputs_h, weight_h);
const __m128 mul_l = _mm_mul_ps(inputs_l, weight_l);
add[i] = _mm_add_ps(mul_l, mul_h);
}
// add[0] = [7+3 6+2 5+1 4+0]
// add[1] = [15+11 14+10 13+9 12+8]
// add[2] = [23+19 22+18 21+17 20+16]
// add[3] = [31+27 30+26 29+25 28+24]
const __m128 hadd_h = _mm_hadd_ps(add[2], add[3]);
// [31+30+27+26 29+28+25+24 23+22+19+18 21+20+17+16]
const __m128 hadd_l = _mm_hadd_ps(add[0], add[1]);
// [15+14+11+10 13+12+9+8 7+6+3+2 5+4+1+0]
const __m128 haddhadd = _mm_hadd_ps(hadd_l, hadd_h);
// [31+30+29+28+27+26+25+24 23+22+21+20+19+18+17+16
// 15+14+13+12+11+10+9+8 7+6+5+4+3+2+1+0]
*outputs = _mm_add_ps(*outputs, haddhadd);
}
static void nn_activate8(__m128 *out_h, __m128 *out_l) {
const __m128 zero = _mm_setzero_ps();
*out_h = _mm_max_ps(*out_h, zero);
*out_l = _mm_max_ps(*out_l, zero);
}
static void nn_activate4(__m128 *x) { *x = _mm_max_ps(*x, _mm_setzero_ps()); }
// Calculate prediction based on the given input features and neural net config.
// Assume there are no more than NN_MAX_NODES_PER_LAYER nodes in each hidden
// layer.
void av1_nn_predict_sse3(const float *input_nodes,
const NN_CONFIG *const nn_config,
float *const output) {
float buf[2][NN_MAX_NODES_PER_LAYER];
int buf_index = 0;
int num_inputs = nn_config->num_inputs;
// Hidden layers, except the final iteration is the output layer.
for (int layer = 0; layer <= nn_config->num_hidden_layers; layer++) {
const float *layer_weights = nn_config->weights[layer];
const float *layer_bias = nn_config->bias[layer];
bool output_layer = (layer == nn_config->num_hidden_layers);
float *const output_nodes = output_layer ? output : buf[buf_index];
const int num_outputs = output_layer ? nn_config->num_outputs
: nn_config->num_hidden_nodes[layer];
if (num_inputs % 4 == 0 && num_outputs % 8 == 0) {
for (int out = 0; out < num_outputs; out += 8) {
__m128 out_h = _mm_loadu_ps(&layer_bias[out + 4]);
__m128 out_l = _mm_loadu_ps(&layer_bias[out]);
for (int in = 0; in < num_inputs; in += 4) {
nn_propagate_4to8(&input_nodes[in],
&layer_weights[out * num_inputs + in], &out_h,
&out_l, num_inputs);
}
if (!output_layer) nn_activate8(&out_h, &out_l);
_mm_storeu_ps(&output_nodes[out + 4], out_h);
_mm_storeu_ps(&output_nodes[out], out_l);
}
} else if (num_inputs % 8 == 0 && num_outputs % 4 == 0) {
for (int out = 0; out < num_outputs; out += 4) {
__m128 outputs = _mm_loadu_ps(&layer_bias[out]);
for (int in = 0; in < num_inputs; in += 8) {
nn_propagate_8to4(&input_nodes[in],
&layer_weights[out * num_inputs + in], &outputs,
num_inputs);
}
if (!output_layer) nn_activate4(&outputs);
_mm_storeu_ps(&output_nodes[out], outputs);
}
} else if (num_inputs % 4 == 0 && num_outputs % 4 == 0) {
for (int out = 0; out < num_outputs; out += 4) {
__m128 outputs = _mm_loadu_ps(&layer_bias[out]);
for (int in = 0; in < num_inputs; in += 4) {
nn_propagate_4to4(&input_nodes[in],
&layer_weights[out * num_inputs + in], &outputs,
num_inputs);
}
if (!output_layer) nn_activate4(&outputs);
_mm_storeu_ps(&output_nodes[out], outputs);
}
} else if (num_inputs % 8 == 0) {
for (int out = 0; out < num_outputs; out++) {
__m128 total = _mm_load1_ps(&layer_bias[out]);
for (int in = 0; in < num_inputs; in += 8) {
nn_propagate_8to1(&input_nodes[in],
&layer_weights[out * num_inputs + in], &total);
}
if (!output_layer) nn_activate4(&total);
output_nodes[out] = _mm_cvtss_f32(total);
}
} else if (num_inputs % 4 == 0) {
for (int out = 0; out < num_outputs; out++) {
__m128 total = _mm_load1_ps(&layer_bias[out]);
for (int in = 0; in < num_inputs; in += 4) {
nn_propagate_4to1(&input_nodes[in],
&layer_weights[out * num_inputs + in], &total);
}
if (!output_layer) nn_activate4(&total);
output_nodes[out] = _mm_cvtss_f32(total);
}
} else {
// Use SSE instructions for scalar operations to avoid the latency of
// swapping between SIMD and FPU modes.
for (int out = 0; out < num_outputs; out++) {
__m128 total = _mm_load1_ps(&layer_bias[out]);
for (int in_node = 0; in_node < num_inputs; in_node++) {
__m128 input = _mm_load1_ps(&input_nodes[in_node]);
__m128 weight =
_mm_load1_ps(&layer_weights[num_inputs * out + in_node]);
total = _mm_add_ps(total, _mm_mul_ps(input, weight));
}
if (!output_layer) nn_activate4(&total);
output_nodes[out] = _mm_cvtss_f32(total);
}
}
input_nodes = output_nodes;
num_inputs = num_outputs;
buf_index = 1 - buf_index;
}
}

View file

@ -64,14 +64,13 @@ static INLINE void acc_stat_win7_one_line_avx2(
static INLINE void compute_stats_win7_opt_avx2(
const uint8_t *dgd, const uint8_t *src, int h_start, int h_end, int v_start,
int v_end, int dgd_stride, int src_stride, double *M, double *H) {
int v_end, int dgd_stride, int src_stride, int64_t *M, int64_t *H) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const double avg =
find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
uint8_t avg = find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int32_t M_int32[WIENER_WIN][WIENER_WIN] = { { 0 } };
int64_t M_int64[WIENER_WIN][WIENER_WIN] = { { 0 } };
@ -103,23 +102,281 @@ static INLINE void compute_stats_win7_opt_avx2(
}
}
const double avg_square_sum = avg * avg * pixel_count;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = M_int64[k][l] + avg_square_sum - avg * (sumX + sumY[k][l]);
double *H_ = H + idx0 * wiener_win2;
M[idx0] =
M_int64[k][l] + (avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]));
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int64[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] = H_int_[n * 8 + m] + avg_square_sum -
avg * (sumY[k][l] + sumY[n][m]);
(int64_t)avg * (sumY[k][l] + sumY[n][m]);
}
}
}
}
}
static INLINE void acc_stat_highbd_avx2(int64_t *dst, const uint16_t *dgd,
const __m256i *shuffle,
const __m256i *dgd_ijkl) {
// Load two 128-bit chunks from dgd
const __m256i s0 = _mm256_inserti128_si256(
_mm256_castsi128_si256(_mm_loadu_si128((__m128i *)dgd)),
_mm_loadu_si128((__m128i *)(dgd + 4)), 1);
// s0 = [11 10 9 8 7 6 5 4] [7 6 5 4 3 2 1 0] as u16 (values are dgd indices)
// The weird order is so the shuffle stays within 128-bit lanes
// Shuffle 16x u16 values within lanes according to the mask:
// [0 1 1 2 2 3 3 4] [0 1 1 2 2 3 3 4]
// (Actually we shuffle u8 values as there's no 16-bit shuffle)
const __m256i s1 = _mm256_shuffle_epi8(s0, *shuffle);
// s1 = [8 7 7 6 6 5 5 4] [4 3 3 2 2 1 1 0] as u16 (values are dgd indices)
// Multiply 16x 16-bit integers in dgd_ijkl and s1, resulting in 16x 32-bit
// integers then horizontally add pairs of these integers resulting in 8x
// 32-bit integers
const __m256i d0 = _mm256_madd_epi16(*dgd_ijkl, s1);
// d0 = [a b c d] [e f g h] as u32
// Take the lower-half of d0, extend to u64, add it on to dst (H)
const __m256i d0l = _mm256_cvtepu32_epi64(_mm256_extracti128_si256(d0, 0));
// d0l = [a b] [c d] as u64
const __m256i dst0 = yy_loadu_256(dst);
yy_storeu_256(dst, _mm256_add_epi64(d0l, dst0));
// Take the upper-half of d0, extend to u64, add it on to dst (H)
const __m256i d0h = _mm256_cvtepu32_epi64(_mm256_extracti128_si256(d0, 1));
// d0h = [e f] [g h] as u64
const __m256i dst1 = yy_loadu_256(dst + 4);
yy_storeu_256(dst + 4, _mm256_add_epi64(d0h, dst1));
}
static INLINE void acc_stat_highbd_win7_one_line_avx2(
const uint16_t *dgd, const uint16_t *src, int h_start, int h_end,
int dgd_stride, const __m256i *shuffle, int32_t *sumX,
int32_t sumY[WIENER_WIN][WIENER_WIN], int64_t M_int[WIENER_WIN][WIENER_WIN],
int64_t H_int[WIENER_WIN2][WIENER_WIN * 8]) {
int j, k, l;
const int wiener_win = WIENER_WIN;
for (j = h_start; j < h_end; j += 2) {
const uint16_t X1 = src[j];
const uint16_t X2 = src[j + 1];
*sumX += X1 + X2;
const uint16_t *dgd_ij = dgd + j;
for (k = 0; k < wiener_win; k++) {
const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
for (l = 0; l < wiener_win; l++) {
int64_t *H_ = &H_int[(l * wiener_win + k)][0];
const uint16_t D1 = dgd_ijk[l];
const uint16_t D2 = dgd_ijk[l + 1];
sumY[k][l] += D1 + D2;
M_int[k][l] += D1 * X1 + D2 * X2;
// Load two u16 values from dgd_ijkl combined as a u32,
// then broadcast to 8x u32 slots of a 256
const __m256i dgd_ijkl =
_mm256_set1_epi32(*((uint32_t *)(dgd_ijk + l)));
// dgd_ijkl = [y x y x y x y x] [y x y x y x y x] where each is a u16
acc_stat_highbd_avx2(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 5 * 8, dgd_ij + 5 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 6 * 8, dgd_ij + 6 * dgd_stride, shuffle,
&dgd_ijkl);
}
}
}
}
static INLINE void compute_stats_highbd_win7_opt_avx2(
const uint8_t *dgd8, const uint8_t *src8, int h_start, int h_end,
int v_start, int v_end, int dgd_stride, int src_stride, int64_t *M,
int64_t *H, aom_bit_depth_t bit_depth) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
const uint16_t avg =
find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int64_t M_int[WIENER_WIN][WIENER_WIN] = { { 0 } };
int64_t H_int[WIENER_WIN2][WIENER_WIN * 8] = { { 0 } };
int32_t sumY[WIENER_WIN][WIENER_WIN] = { { 0 } };
int32_t sumX = 0;
const uint16_t *dgd_win = dgd - wiener_halfwin * dgd_stride - wiener_halfwin;
const __m256i shuffle = yy_loadu_256(g_shuffle_stats_highbd_data);
for (j = v_start; j < v_end; j += 64) {
const int vert_end = AOMMIN(64, v_end - j) + j;
for (i = j; i < vert_end; i++) {
acc_stat_highbd_win7_one_line_avx2(
dgd_win + i * dgd_stride, src + i * src_stride, h_start, h_end,
dgd_stride, &shuffle, &sumX, sumY, M_int, H_int);
}
}
uint8_t bit_depth_divider = 1;
if (bit_depth == AOM_BITS_12)
bit_depth_divider = 16;
else if (bit_depth == AOM_BITS_10)
bit_depth_divider = 4;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = (M_int[k][l] +
(avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]))) /
bit_depth_divider;
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] =
(H_int_[n * 8 + m] +
(avg_square_sum - (int64_t)avg * (sumY[k][l] + sumY[n][m]))) /
bit_depth_divider;
}
}
}
}
}
static INLINE void acc_stat_highbd_win5_one_line_avx2(
const uint16_t *dgd, const uint16_t *src, int h_start, int h_end,
int dgd_stride, const __m256i *shuffle, int32_t *sumX,
int32_t sumY[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA],
int64_t M_int[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA],
int64_t H_int[WIENER_WIN2_CHROMA][WIENER_WIN_CHROMA * 8]) {
int j, k, l;
const int wiener_win = WIENER_WIN_CHROMA;
for (j = h_start; j < h_end; j += 2) {
const uint16_t X1 = src[j];
const uint16_t X2 = src[j + 1];
*sumX += X1 + X2;
const uint16_t *dgd_ij = dgd + j;
for (k = 0; k < wiener_win; k++) {
const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
for (l = 0; l < wiener_win; l++) {
int64_t *H_ = &H_int[(l * wiener_win + k)][0];
const uint16_t D1 = dgd_ijk[l];
const uint16_t D2 = dgd_ijk[l + 1];
sumY[k][l] += D1 + D2;
M_int[k][l] += D1 * X1 + D2 * X2;
// Load two u16 values from dgd_ijkl combined as a u32,
// then broadcast to 8x u32 slots of a 256
const __m256i dgd_ijkl =
_mm256_set1_epi32(*((uint32_t *)(dgd_ijk + l)));
// dgd_ijkl = [x y x y x y x y] [x y x y x y x y] where each is a u16
acc_stat_highbd_avx2(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_avx2(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
&dgd_ijkl);
}
}
}
}
static INLINE void compute_stats_highbd_win5_opt_avx2(
const uint8_t *dgd8, const uint8_t *src8, int h_start, int h_end,
int v_start, int v_end, int dgd_stride, int src_stride, int64_t *M,
int64_t *H, aom_bit_depth_t bit_depth) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN_CHROMA;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
const uint16_t avg =
find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int64_t M_int64[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
int64_t H_int64[WIENER_WIN2_CHROMA][WIENER_WIN_CHROMA * 8] = { { 0 } };
int32_t sumY[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
int32_t sumX = 0;
const uint16_t *dgd_win = dgd - wiener_halfwin * dgd_stride - wiener_halfwin;
const __m256i shuffle = yy_loadu_256(g_shuffle_stats_highbd_data);
for (j = v_start; j < v_end; j += 64) {
const int vert_end = AOMMIN(64, v_end - j) + j;
for (i = j; i < vert_end; i++) {
acc_stat_highbd_win5_one_line_avx2(
dgd_win + i * dgd_stride, src + i * src_stride, h_start, h_end,
dgd_stride, &shuffle, &sumX, sumY, M_int64, H_int64);
}
}
uint8_t bit_depth_divider = 1;
if (bit_depth == AOM_BITS_12)
bit_depth_divider = 16;
else if (bit_depth == AOM_BITS_10)
bit_depth_divider = 4;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = (M_int64[k][l] +
(avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]))) /
bit_depth_divider;
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int64[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] =
(H_int_[n * 8 + m] +
(avg_square_sum - (int64_t)avg * (sumY[k][l] + sumY[n][m]))) /
bit_depth_divider;
}
}
}
}
}
void av1_compute_stats_highbd_avx2(int wiener_win, const uint8_t *dgd8,
const uint8_t *src8, int h_start, int h_end,
int v_start, int v_end, int dgd_stride,
int src_stride, int64_t *M, int64_t *H,
aom_bit_depth_t bit_depth) {
if (wiener_win == WIENER_WIN) {
compute_stats_highbd_win7_opt_avx2(dgd8, src8, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H,
bit_depth);
} else if (wiener_win == WIENER_WIN_CHROMA) {
compute_stats_highbd_win5_opt_avx2(dgd8, src8, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H,
bit_depth);
} else {
av1_compute_stats_highbd_c(wiener_win, dgd8, src8, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H, bit_depth);
}
}
static INLINE void acc_stat_win5_one_line_avx2(
const uint8_t *dgd, const uint8_t *src, int h_start, int h_end,
int dgd_stride, const __m128i *shuffle, int32_t *sumX,
@ -156,14 +413,13 @@ static INLINE void acc_stat_win5_one_line_avx2(
static INLINE void compute_stats_win5_opt_avx2(
const uint8_t *dgd, const uint8_t *src, int h_start, int h_end, int v_start,
int v_end, int dgd_stride, int src_stride, double *M, double *H) {
int v_end, int dgd_stride, int src_stride, int64_t *M, int64_t *H) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN_CHROMA;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const double avg =
find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
uint8_t avg = find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int32_t M_int32[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
int64_t M_int64[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
@ -195,17 +451,18 @@ static INLINE void compute_stats_win5_opt_avx2(
}
}
const double avg_square_sum = avg * avg * pixel_count;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = M_int64[k][l] + avg_square_sum - avg * (sumX + sumY[k][l]);
double *H_ = H + idx0 * wiener_win2;
M[idx0] =
M_int64[k][l] + (avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]));
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int64[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] = H_int_[n * 8 + m] + avg_square_sum -
avg * (sumY[k][l] + sumY[n][m]);
(int64_t)avg * (sumY[k][l] + sumY[n][m]);
}
}
}
@ -215,7 +472,7 @@ static INLINE void compute_stats_win5_opt_avx2(
void av1_compute_stats_avx2(int wiener_win, const uint8_t *dgd,
const uint8_t *src, int h_start, int h_end,
int v_start, int v_end, int dgd_stride,
int src_stride, double *M, double *H) {
int src_stride, int64_t *M, int64_t *H) {
if (wiener_win == WIENER_WIN) {
compute_stats_win7_opt_avx2(dgd, src, h_start, h_end, v_start, v_end,
dgd_stride, src_stride, M, H);
@ -279,7 +536,7 @@ int64_t av1_lowbd_pixel_proj_error_avx2(
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -292,22 +549,25 @@ int64_t av1_lowbd_pixel_proj_error_avx2(
sum64 = _mm256_add_epi64(sum64, sum64_0);
sum64 = _mm256_add_epi64(sum64, sum64_1);
}
} else if (params->r[0] > 0) {
__m256i xq_coeff =
pair_set_epi16(xq[0], (-xq[0] * (1 << SGRPROJ_RST_BITS)));
} else if (params->r[0] > 0 || params->r[1] > 0) {
const int xq_active = (params->r[0] > 0) ? xq[0] : xq[1];
const __m256i xq_coeff =
pair_set_epi16(xq_active, (-xq_active * (1 << SGRPROJ_RST_BITS)));
const int32_t *flt = (params->r[0] > 0) ? flt0 : flt1;
const int flt_stride = (params->r[0] > 0) ? flt0_stride : flt1_stride;
for (i = 0; i < height; ++i) {
__m256i sum32 = _mm256_setzero_si256();
for (j = 0; j <= width - 16; j += 16) {
const __m256i d0 = _mm256_cvtepu8_epi16(xx_loadu_128(dat + j));
const __m256i s0 = _mm256_cvtepu8_epi16(xx_loadu_128(src + j));
const __m256i flt0_16b = _mm256_permute4x64_epi64(
_mm256_packs_epi32(yy_loadu_256(flt0 + j),
yy_loadu_256(flt0 + j + 8)),
const __m256i flt_16b = _mm256_permute4x64_epi64(
_mm256_packs_epi32(yy_loadu_256(flt + j),
yy_loadu_256(flt + j + 8)),
0xd8);
const __m256i v0 =
_mm256_madd_epi16(xq_coeff, _mm256_unpacklo_epi16(flt0_16b, d0));
_mm256_madd_epi16(xq_coeff, _mm256_unpacklo_epi16(flt_16b, d0));
const __m256i v1 =
_mm256_madd_epi16(xq_coeff, _mm256_unpackhi_epi16(flt0_16b, d0));
_mm256_madd_epi16(xq_coeff, _mm256_unpackhi_epi16(flt_16b, d0));
const __m256i vr0 =
_mm256_srai_epi32(_mm256_add_epi32(v0, rounding), shift);
const __m256i vr1 =
@ -319,53 +579,13 @@ int64_t av1_lowbd_pixel_proj_error_avx2(
}
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[0] * (flt0[k] - u);
int32_t v = xq_active * (flt[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
flt0 += flt0_stride;
const __m256i sum64_0 =
_mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
const __m256i sum64_1 =
_mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
sum64 = _mm256_add_epi64(sum64, sum64_0);
sum64 = _mm256_add_epi64(sum64, sum64_1);
}
} else if (params->r[1] > 0) {
__m256i xq_coeff = pair_set_epi16(xq[1], -(xq[1] << SGRPROJ_RST_BITS));
for (i = 0; i < height; ++i) {
__m256i sum32 = _mm256_setzero_si256();
for (j = 0; j <= width - 16; j += 16) {
const __m256i d0 = _mm256_cvtepu8_epi16(xx_loadu_128(dat + j));
const __m256i s0 = _mm256_cvtepu8_epi16(xx_loadu_128(src + j));
const __m256i flt1_16b = _mm256_permute4x64_epi64(
_mm256_packs_epi32(yy_loadu_256(flt1 + j),
yy_loadu_256(flt1 + j + 8)),
0xd8);
const __m256i v0 =
_mm256_madd_epi16(xq_coeff, _mm256_unpacklo_epi16(flt1_16b, d0));
const __m256i v1 =
_mm256_madd_epi16(xq_coeff, _mm256_unpackhi_epi16(flt1_16b, d0));
const __m256i vr0 =
_mm256_srai_epi32(_mm256_add_epi32(v0, rounding), shift);
const __m256i vr1 =
_mm256_srai_epi32(_mm256_add_epi32(v1, rounding), shift);
const __m256i e0 = _mm256_sub_epi16(
_mm256_add_epi16(_mm256_packs_epi32(vr0, vr1), d0), s0);
const __m256i err0 = _mm256_madd_epi16(e0, e0);
sum32 = _mm256_add_epi32(sum32, err0);
}
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[1] * (flt1[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += e * e;
}
dat += dat_stride;
src += src_stride;
flt1 += flt1_stride;
flt += flt_stride;
const __m256i sum64_0 =
_mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
const __m256i sum64_1 =
@ -385,7 +605,7 @@ int64_t av1_lowbd_pixel_proj_error_avx2(
}
for (k = j; k < width; ++k) {
const int32_t e = (int32_t)(dat[k]) - src[k];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -401,3 +621,223 @@ int64_t av1_lowbd_pixel_proj_error_avx2(
err += sum[0] + sum[1] + sum[2] + sum[3];
return err;
}
int64_t av1_highbd_pixel_proj_error_avx2(
const uint8_t *src8, int width, int height, int src_stride,
const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
int32_t *flt1, int flt1_stride, int xq[2], const sgr_params_type *params) {
int i, j, k;
const int32_t shift = SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS;
const __m256i rounding = _mm256_set1_epi32(1 << (shift - 1));
__m256i sum64 = _mm256_setzero_si256();
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
int64_t err = 0;
if (params->r[0] > 0 && params->r[1] > 0) { // Both filters are enabled
const __m256i xq0 = _mm256_set1_epi32(xq[0]);
const __m256i xq1 = _mm256_set1_epi32(xq[1]);
for (i = 0; i < height; ++i) {
__m256i sum32 = _mm256_setzero_si256();
for (j = 0; j <= width - 16; j += 16) { // Process 16 pixels at a time
// Load 16 pixels each from source image and corrupted image
const __m256i s0 = yy_loadu_256(src + j);
const __m256i d0 = yy_loadu_256(dat + j);
// s0 = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16 (indices)
// Shift-up each pixel to match filtered image scaling
const __m256i u0 = _mm256_slli_epi16(d0, SGRPROJ_RST_BITS);
// Split u0 into two halves and pad each from u16 to i32
const __m256i u0l = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(u0));
const __m256i u0h =
_mm256_cvtepu16_epi32(_mm256_extracti128_si256(u0, 1));
// u0h, u0l = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as u32
// Load 16 pixels from each filtered image
const __m256i flt0l = yy_loadu_256(flt0 + j);
const __m256i flt0h = yy_loadu_256(flt0 + j + 8);
const __m256i flt1l = yy_loadu_256(flt1 + j);
const __m256i flt1h = yy_loadu_256(flt1 + j + 8);
// flt?l, flt?h = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as u32
// Subtract shifted corrupt image from each filtered image
const __m256i flt0l_subu = _mm256_sub_epi32(flt0l, u0l);
const __m256i flt0h_subu = _mm256_sub_epi32(flt0h, u0h);
const __m256i flt1l_subu = _mm256_sub_epi32(flt1l, u0l);
const __m256i flt1h_subu = _mm256_sub_epi32(flt1h, u0h);
// Multiply basis vectors by appropriate coefficients
const __m256i v0l = _mm256_mullo_epi32(flt0l_subu, xq0);
const __m256i v0h = _mm256_mullo_epi32(flt0h_subu, xq0);
const __m256i v1l = _mm256_mullo_epi32(flt1l_subu, xq1);
const __m256i v1h = _mm256_mullo_epi32(flt1h_subu, xq1);
// Add together the contributions from the two basis vectors
const __m256i vl = _mm256_add_epi32(v0l, v1l);
const __m256i vh = _mm256_add_epi32(v0h, v1h);
// Right-shift v with appropriate rounding
const __m256i vrl =
_mm256_srai_epi32(_mm256_add_epi32(vl, rounding), shift);
const __m256i vrh =
_mm256_srai_epi32(_mm256_add_epi32(vh, rounding), shift);
// vrh, vrl = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0]
// Saturate each i32 to an i16 then combine both halves
// The permute (control=[3 1 2 0]) fixes weird ordering from AVX lanes
const __m256i vr =
_mm256_permute4x64_epi64(_mm256_packs_epi32(vrl, vrh), 0xd8);
// intermediate = [15 14 13 12 7 6 5 4] [11 10 9 8 3 2 1 0]
// vr = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0]
// Add twin-subspace-sgr-filter to corrupt image then subtract source
const __m256i e0 = _mm256_sub_epi16(_mm256_add_epi16(vr, d0), s0);
// Calculate squared error and add adjacent values
const __m256i err0 = _mm256_madd_epi16(e0, e0);
sum32 = _mm256_add_epi32(sum32, err0);
}
const __m256i sum32l =
_mm256_cvtepu32_epi64(_mm256_castsi256_si128(sum32));
sum64 = _mm256_add_epi64(sum64, sum32l);
const __m256i sum32h =
_mm256_cvtepu32_epi64(_mm256_extracti128_si256(sum32, 1));
sum64 = _mm256_add_epi64(sum64, sum32h);
// Process remaining pixels in this row (modulo 16)
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
flt0 += flt0_stride;
flt1 += flt1_stride;
}
} else if (params->r[0] > 0 || params->r[1] > 0) { // Only one filter enabled
const int32_t xq_on = (params->r[0] > 0) ? xq[0] : xq[1];
const __m256i xq_active = _mm256_set1_epi32(xq_on);
const __m256i xq_inactive =
_mm256_set1_epi32(-xq_on * (1 << SGRPROJ_RST_BITS));
const int32_t *flt = (params->r[0] > 0) ? flt0 : flt1;
const int flt_stride = (params->r[0] > 0) ? flt0_stride : flt1_stride;
for (i = 0; i < height; ++i) {
__m256i sum32 = _mm256_setzero_si256();
for (j = 0; j <= width - 16; j += 16) {
// Load 16 pixels from source image
const __m256i s0 = yy_loadu_256(src + j);
// s0 = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16
// Load 16 pixels from corrupted image and pad each u16 to i32
const __m256i d0 = yy_loadu_256(dat + j);
const __m256i d0h =
_mm256_cvtepu16_epi32(_mm256_extracti128_si256(d0, 1));
const __m256i d0l = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(d0));
// d0 = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16
// d0h, d0l = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as i32
// Load 16 pixels from the filtered image
const __m256i flth = yy_loadu_256(flt + j + 8);
const __m256i fltl = yy_loadu_256(flt + j);
// flth, fltl = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as i32
const __m256i flth_xq = _mm256_mullo_epi32(flth, xq_active);
const __m256i fltl_xq = _mm256_mullo_epi32(fltl, xq_active);
const __m256i d0h_xq = _mm256_mullo_epi32(d0h, xq_inactive);
const __m256i d0l_xq = _mm256_mullo_epi32(d0l, xq_inactive);
const __m256i vh = _mm256_add_epi32(flth_xq, d0h_xq);
const __m256i vl = _mm256_add_epi32(fltl_xq, d0l_xq);
// Shift this down with appropriate rounding
const __m256i vrh =
_mm256_srai_epi32(_mm256_add_epi32(vh, rounding), shift);
const __m256i vrl =
_mm256_srai_epi32(_mm256_add_epi32(vl, rounding), shift);
// vrh, vrl = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as i32
// Saturate each i32 to an i16 then combine both halves
// The permute (control=[3 1 2 0]) fixes weird ordering from AVX lanes
const __m256i vr =
_mm256_permute4x64_epi64(_mm256_packs_epi32(vrl, vrh), 0xd8);
// intermediate = [15 14 13 12 7 6 5 4] [11 10 9 8 3 2 1 0] as u16
// vr = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16
// Subtract twin-subspace-sgr filtered from source image to get error
const __m256i e0 = _mm256_sub_epi16(_mm256_add_epi16(vr, d0), s0);
// Calculate squared error and add adjacent values
const __m256i err0 = _mm256_madd_epi16(e0, e0);
sum32 = _mm256_add_epi32(sum32, err0);
}
const __m256i sum32l =
_mm256_cvtepu32_epi64(_mm256_castsi256_si128(sum32));
sum64 = _mm256_add_epi64(sum64, sum32l);
const __m256i sum32h =
_mm256_cvtepu32_epi64(_mm256_extracti128_si256(sum32, 1));
sum64 = _mm256_add_epi64(sum64, sum32h);
// Process remaining pixels in this row (modulo 16)
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq_on * (flt[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
flt += flt_stride;
}
} else { // Neither filter is enabled
for (i = 0; i < height; ++i) {
__m256i sum32 = _mm256_setzero_si256();
for (j = 0; j <= width - 32; j += 32) {
// Load 2x16 u16 from source image
const __m256i s0l = yy_loadu_256(src + j);
const __m256i s0h = yy_loadu_256(src + j + 16);
// Load 2x16 u16 from corrupted image
const __m256i d0l = yy_loadu_256(dat + j);
const __m256i d0h = yy_loadu_256(dat + j + 16);
// Subtract corrupted image from source image
const __m256i diffl = _mm256_sub_epi16(d0l, s0l);
const __m256i diffh = _mm256_sub_epi16(d0h, s0h);
// Square error and add adjacent values
const __m256i err0l = _mm256_madd_epi16(diffl, diffl);
const __m256i err0h = _mm256_madd_epi16(diffh, diffh);
sum32 = _mm256_add_epi32(sum32, err0l);
sum32 = _mm256_add_epi32(sum32, err0h);
}
const __m256i sum32l =
_mm256_cvtepu32_epi64(_mm256_castsi256_si128(sum32));
sum64 = _mm256_add_epi64(sum64, sum32l);
const __m256i sum32h =
_mm256_cvtepu32_epi64(_mm256_extracti128_si256(sum32, 1));
sum64 = _mm256_add_epi64(sum64, sum32h);
// Process remaining pixels (modulu 16)
for (k = j; k < width; ++k) {
const int32_t e = (int32_t)(dat[k]) - src[k];
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
}
}
// Sum 4 values from sum64l and sum64h into err
int64_t sum[4];
yy_storeu_256(sum, sum64);
err += sum[0] + sum[1] + sum[2] + sum[3];
return err;
}

View file

@ -68,13 +68,13 @@ static INLINE void acc_stat_win7_one_line_sse4_1(
static INLINE void compute_stats_win7_opt_sse4_1(
const uint8_t *dgd, const uint8_t *src, int h_start, int h_end, int v_start,
int v_end, int dgd_stride, int src_stride, double *M, double *H) {
int v_end, int dgd_stride, int src_stride, int64_t *M, int64_t *H) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const double avg =
const uint8_t avg =
find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int32_t M_int32[WIENER_WIN][WIENER_WIN] = { { 0 } };
@ -107,23 +107,286 @@ static INLINE void compute_stats_win7_opt_sse4_1(
}
}
const double avg_square_sum = avg * avg * pixel_count;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = M_int64[k][l] + avg_square_sum - avg * (sumX + sumY[k][l]);
double *H_ = H + idx0 * wiener_win2;
M[idx0] =
M_int64[k][l] + (avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]));
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int64[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] = H_int_[n * 8 + m] + avg_square_sum -
avg * (sumY[k][l] + sumY[n][m]);
(int64_t)avg * (sumY[k][l] + sumY[n][m]);
}
}
}
}
}
static INLINE void acc_stat_highbd_sse41(int64_t *dst, const uint16_t *dgd,
const __m128i *shuffle,
const __m128i *dgd_ijkl) {
// Load 256 bits from dgd in two chunks
const __m128i s0l = xx_loadu_128(dgd);
const __m128i s0h = xx_loadu_128(dgd + 4);
// s0l = [7 6 5 4 3 2 1 0] as u16 values (dgd indices)
// s0h = [11 10 9 8 7 6 5 4] as u16 values (dgd indices)
// (Slightly strange order so we can apply the same shuffle to both halves)
// Shuffle the u16 values in each half (actually using 8-bit shuffle mask)
const __m128i s1l = _mm_shuffle_epi8(s0l, *shuffle);
const __m128i s1h = _mm_shuffle_epi8(s0h, *shuffle);
// s1l = [4 3 3 2 2 1 1 0] as u16 values (dgd indices)
// s1h = [8 7 7 6 6 5 5 4] as u16 values (dgd indices)
// Multiply s1 by dgd_ijkl resulting in 8x u32 values
// Horizontally add pairs of u32 resulting in 4x u32
const __m128i dl = _mm_madd_epi16(*dgd_ijkl, s1l);
const __m128i dh = _mm_madd_epi16(*dgd_ijkl, s1h);
// dl = [d c b a] as u32 values
// dh = [h g f e] as u32 values
// Add these 8x u32 results on to dst in four parts
const __m128i dll = _mm_cvtepu32_epi64(dl);
const __m128i dlh = _mm_cvtepu32_epi64(_mm_srli_si128(dl, 8));
const __m128i dhl = _mm_cvtepu32_epi64(dh);
const __m128i dhh = _mm_cvtepu32_epi64(_mm_srli_si128(dh, 8));
// dll = [b a] as u64 values, etc.
const __m128i rll = _mm_add_epi64(xx_loadu_128(dst), dll);
xx_storeu_128(dst, rll);
const __m128i rlh = _mm_add_epi64(xx_loadu_128(dst + 2), dlh);
xx_storeu_128(dst + 2, rlh);
const __m128i rhl = _mm_add_epi64(xx_loadu_128(dst + 4), dhl);
xx_storeu_128(dst + 4, rhl);
const __m128i rhh = _mm_add_epi64(xx_loadu_128(dst + 6), dhh);
xx_storeu_128(dst + 6, rhh);
}
static INLINE void acc_stat_highbd_win7_one_line_sse4_1(
const uint16_t *dgd, const uint16_t *src, int h_start, int h_end,
int dgd_stride, const __m128i *shuffle, int32_t *sumX,
int32_t sumY[WIENER_WIN][WIENER_WIN], int64_t M_int[WIENER_WIN][WIENER_WIN],
int64_t H_int[WIENER_WIN2][WIENER_WIN * 8]) {
int j, k, l;
const int wiener_win = WIENER_WIN;
for (j = h_start; j < h_end; j += 2) {
const uint16_t X1 = src[j];
const uint16_t X2 = src[j + 1];
*sumX += X1 + X2;
const uint16_t *dgd_ij = dgd + j;
for (k = 0; k < wiener_win; k++) {
const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
for (l = 0; l < wiener_win; l++) {
int64_t *H_ = &H_int[(l * wiener_win + k)][0];
const uint16_t D1 = dgd_ijk[l];
const uint16_t D2 = dgd_ijk[l + 1];
sumY[k][l] += D1 + D2;
M_int[k][l] += D1 * X1 + D2 * X2;
// Load two u16 values from dgd as a single u32
// Then broadcast to 4x u32 slots of a 128
const __m128i dgd_ijkl = _mm_set1_epi32(*((uint32_t *)(dgd_ijk + l)));
// dgd_ijkl = [y x y x y x y x] as u16
acc_stat_highbd_sse41(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 5 * 8, dgd_ij + 5 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 6 * 8, dgd_ij + 6 * dgd_stride, shuffle,
&dgd_ijkl);
}
}
}
}
static INLINE void compute_stats_highbd_win7_opt_sse4_1(
const uint8_t *dgd8, const uint8_t *src8, int h_start, int h_end,
int v_start, int v_end, int dgd_stride, int src_stride, int64_t *M,
int64_t *H, aom_bit_depth_t bit_depth) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
const uint16_t avg =
find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int64_t M_int[WIENER_WIN][WIENER_WIN] = { { 0 } };
int64_t H_int[WIENER_WIN2][WIENER_WIN * 8] = { { 0 } };
int32_t sumY[WIENER_WIN][WIENER_WIN] = { { 0 } };
int32_t sumX = 0;
const uint16_t *dgd_win = dgd - wiener_halfwin * dgd_stride - wiener_halfwin;
// Load just half of the 256-bit shuffle control used for the AVX2 version
const __m128i shuffle = xx_loadu_128(g_shuffle_stats_highbd_data);
for (j = v_start; j < v_end; j += 64) {
const int vert_end = AOMMIN(64, v_end - j) + j;
for (i = j; i < vert_end; i++) {
acc_stat_highbd_win7_one_line_sse4_1(
dgd_win + i * dgd_stride, src + i * src_stride, h_start, h_end,
dgd_stride, &shuffle, &sumX, sumY, M_int, H_int);
}
}
uint8_t bit_depth_divider = 1;
if (bit_depth == AOM_BITS_12)
bit_depth_divider = 16;
else if (bit_depth == AOM_BITS_10)
bit_depth_divider = 4;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = (M_int[k][l] +
(avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]))) /
bit_depth_divider;
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] =
(H_int_[n * 8 + m] +
(avg_square_sum - (int64_t)avg * (sumY[k][l] + sumY[n][m]))) /
bit_depth_divider;
}
}
}
}
}
static INLINE void acc_stat_highbd_win5_one_line_sse4_1(
const uint16_t *dgd, const uint16_t *src, int h_start, int h_end,
int dgd_stride, const __m128i *shuffle, int32_t *sumX,
int32_t sumY[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA],
int64_t M_int[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA],
int64_t H_int[WIENER_WIN2_CHROMA][WIENER_WIN_CHROMA * 8]) {
int j, k, l;
const int wiener_win = WIENER_WIN_CHROMA;
for (j = h_start; j < h_end; j += 2) {
const uint16_t X1 = src[j];
const uint16_t X2 = src[j + 1];
*sumX += X1 + X2;
const uint16_t *dgd_ij = dgd + j;
for (k = 0; k < wiener_win; k++) {
const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
for (l = 0; l < wiener_win; l++) {
int64_t *H_ = &H_int[(l * wiener_win + k)][0];
const uint16_t D1 = dgd_ijk[l];
const uint16_t D2 = dgd_ijk[l + 1];
sumY[k][l] += D1 + D2;
M_int[k][l] += D1 * X1 + D2 * X2;
// Load two u16 values from dgd as a single u32
// then broadcast to 4x u32 slots of a 128
const __m128i dgd_ijkl = _mm_set1_epi32(*((uint32_t *)(dgd_ijk + l)));
// dgd_ijkl = [y x y x y x y x] as u16
acc_stat_highbd_sse41(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
&dgd_ijkl);
acc_stat_highbd_sse41(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
&dgd_ijkl);
}
}
}
}
static INLINE void compute_stats_highbd_win5_opt_sse4_1(
const uint8_t *dgd8, const uint8_t *src8, int h_start, int h_end,
int v_start, int v_end, int dgd_stride, int src_stride, int64_t *M,
int64_t *H, aom_bit_depth_t bit_depth) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN_CHROMA;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
const uint16_t avg =
find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int64_t M_int[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
int64_t H_int[WIENER_WIN2_CHROMA][WIENER_WIN_CHROMA * 8] = { { 0 } };
int32_t sumY[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
int32_t sumX = 0;
const uint16_t *dgd_win = dgd - wiener_halfwin * dgd_stride - wiener_halfwin;
// Load just half of the 256-bit shuffle control used for the AVX2 version
const __m128i shuffle = xx_loadu_128(g_shuffle_stats_highbd_data);
for (j = v_start; j < v_end; j += 64) {
const int vert_end = AOMMIN(64, v_end - j) + j;
for (i = j; i < vert_end; i++) {
acc_stat_highbd_win5_one_line_sse4_1(
dgd_win + i * dgd_stride, src + i * src_stride, h_start, h_end,
dgd_stride, &shuffle, &sumX, sumY, M_int, H_int);
}
}
uint8_t bit_depth_divider = 1;
if (bit_depth == AOM_BITS_12)
bit_depth_divider = 16;
else if (bit_depth == AOM_BITS_10)
bit_depth_divider = 4;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = (M_int[k][l] +
(avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]))) /
bit_depth_divider;
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] =
(H_int_[n * 8 + m] +
(avg_square_sum - (int64_t)avg * (sumY[k][l] + sumY[n][m]))) /
bit_depth_divider;
}
}
}
}
}
void av1_compute_stats_highbd_sse4_1(int wiener_win, const uint8_t *dgd8,
const uint8_t *src8, int h_start,
int h_end, int v_start, int v_end,
int dgd_stride, int src_stride, int64_t *M,
int64_t *H, aom_bit_depth_t bit_depth) {
if (wiener_win == WIENER_WIN) {
compute_stats_highbd_win7_opt_sse4_1(dgd8, src8, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H,
bit_depth);
} else if (wiener_win == WIENER_WIN_CHROMA) {
compute_stats_highbd_win5_opt_sse4_1(dgd8, src8, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H,
bit_depth);
} else {
av1_compute_stats_highbd_c(wiener_win, dgd8, src8, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H, bit_depth);
}
}
static INLINE void acc_stat_win5_one_line_sse4_1(
const uint8_t *dgd, const uint8_t *src, int h_start, int h_end,
int dgd_stride, const __m128i *shuffle, int32_t *sumX,
@ -160,13 +423,13 @@ static INLINE void acc_stat_win5_one_line_sse4_1(
static INLINE void compute_stats_win5_opt_sse4_1(
const uint8_t *dgd, const uint8_t *src, int h_start, int h_end, int v_start,
int v_end, int dgd_stride, int src_stride, double *M, double *H) {
int v_end, int dgd_stride, int src_stride, int64_t *M, int64_t *H) {
int i, j, k, l, m, n;
const int wiener_win = WIENER_WIN_CHROMA;
const int pixel_count = (h_end - h_start) * (v_end - v_start);
const int wiener_win2 = wiener_win * wiener_win;
const int wiener_halfwin = (wiener_win >> 1);
const double avg =
const uint8_t avg =
find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
int32_t M_int32[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
@ -199,17 +462,18 @@ static INLINE void compute_stats_win5_opt_sse4_1(
}
}
const double avg_square_sum = avg * avg * pixel_count;
const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
for (k = 0; k < wiener_win; k++) {
for (l = 0; l < wiener_win; l++) {
const int32_t idx0 = l * wiener_win + k;
M[idx0] = M_int64[k][l] + avg_square_sum - avg * (sumX + sumY[k][l]);
double *H_ = H + idx0 * wiener_win2;
M[idx0] =
M_int64[k][l] + (avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]));
int64_t *H_ = H + idx0 * wiener_win2;
int64_t *H_int_ = &H_int64[idx0][0];
for (m = 0; m < wiener_win; m++) {
for (n = 0; n < wiener_win; n++) {
H_[m * wiener_win + n] = H_int_[n * 8 + m] + avg_square_sum -
avg * (sumY[k][l] + sumY[n][m]);
(int64_t)avg * (sumY[k][l] + sumY[n][m]);
}
}
}
@ -218,7 +482,7 @@ static INLINE void compute_stats_win5_opt_sse4_1(
void av1_compute_stats_sse4_1(int wiener_win, const uint8_t *dgd,
const uint8_t *src, int h_start, int h_end,
int v_start, int v_end, int dgd_stride,
int src_stride, double *M, double *H) {
int src_stride, int64_t *M, int64_t *H) {
if (wiener_win == WIENER_WIN) {
compute_stats_win7_opt_sse4_1(dgd, src, h_start, h_end, v_start, v_end,
dgd_stride, src_stride, M, H);
@ -250,7 +514,7 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
__m128i xq_coeff = pair_set_epi16(xq[0], xq[1]);
for (i = 0; i < height; ++i) {
__m128i sum32 = _mm_setzero_si128();
for (j = 0; j < width - 8; j += 8) {
for (j = 0; j <= width - 8; j += 8) {
const __m128i d0 = _mm_cvtepu8_epi16(xx_loadl_64(dat + j));
const __m128i s0 = _mm_cvtepu8_epi16(xx_loadl_64(src + j));
const __m128i flt0_16b =
@ -275,7 +539,7 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -286,19 +550,23 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
sum64 = _mm_add_epi64(sum64, sum64_0);
sum64 = _mm_add_epi64(sum64, sum64_1);
}
} else if (params->r[0] > 0) {
__m128i xq_coeff = pair_set_epi16(xq[0], -(xq[0] << SGRPROJ_RST_BITS));
} else if (params->r[0] > 0 || params->r[1] > 0) {
const int xq_active = (params->r[0] > 0) ? xq[0] : xq[1];
const __m128i xq_coeff =
pair_set_epi16(xq_active, -(xq_active << SGRPROJ_RST_BITS));
const int32_t *flt = (params->r[0] > 0) ? flt0 : flt1;
const int flt_stride = (params->r[0] > 0) ? flt0_stride : flt1_stride;
for (i = 0; i < height; ++i) {
__m128i sum32 = _mm_setzero_si128();
for (j = 0; j < width - 8; j += 8) {
for (j = 0; j <= width - 8; j += 8) {
const __m128i d0 = _mm_cvtepu8_epi16(xx_loadl_64(dat + j));
const __m128i s0 = _mm_cvtepu8_epi16(xx_loadl_64(src + j));
const __m128i flt0_16b =
_mm_packs_epi32(xx_loadu_128(flt0 + j), xx_loadu_128(flt0 + j + 4));
const __m128i flt_16b =
_mm_packs_epi32(xx_loadu_128(flt + j), xx_loadu_128(flt + j + 4));
const __m128i v0 =
_mm_madd_epi16(xq_coeff, _mm_unpacklo_epi16(flt0_16b, d0));
_mm_madd_epi16(xq_coeff, _mm_unpacklo_epi16(flt_16b, d0));
const __m128i v1 =
_mm_madd_epi16(xq_coeff, _mm_unpackhi_epi16(flt0_16b, d0));
_mm_madd_epi16(xq_coeff, _mm_unpackhi_epi16(flt_16b, d0));
const __m128i vr0 = _mm_srai_epi32(_mm_add_epi32(v0, rounding), shift);
const __m128i vr1 = _mm_srai_epi32(_mm_add_epi32(v1, rounding), shift);
const __m128i e0 =
@ -308,47 +576,13 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
}
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[0] * (flt0[k] - u);
int32_t v = xq_active * (flt[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
flt0 += flt0_stride;
const __m128i sum64_0 = _mm_cvtepi32_epi64(sum32);
const __m128i sum64_1 = _mm_cvtepi32_epi64(_mm_srli_si128(sum32, 8));
sum64 = _mm_add_epi64(sum64, sum64_0);
sum64 = _mm_add_epi64(sum64, sum64_1);
}
} else if (params->r[1] > 0) {
__m128i xq_coeff = pair_set_epi16(xq[1], -(xq[1] << SGRPROJ_RST_BITS));
for (i = 0; i < height; ++i) {
__m128i sum32 = _mm_setzero_si128();
for (j = 0; j < width - 8; j += 8) {
const __m128i d0 = _mm_cvtepu8_epi16(xx_loadl_64(dat + j));
const __m128i s0 = _mm_cvtepu8_epi16(xx_loadl_64(src + j));
const __m128i flt1_16b =
_mm_packs_epi32(xx_loadu_128(flt1 + j), xx_loadu_128(flt1 + j + 4));
const __m128i v0 =
_mm_madd_epi16(xq_coeff, _mm_unpacklo_epi16(flt1_16b, d0));
const __m128i v1 =
_mm_madd_epi16(xq_coeff, _mm_unpackhi_epi16(flt1_16b, d0));
const __m128i vr0 = _mm_srai_epi32(_mm_add_epi32(v0, rounding), shift);
const __m128i vr1 = _mm_srai_epi32(_mm_add_epi32(v1, rounding), shift);
const __m128i e0 =
_mm_sub_epi16(_mm_add_epi16(_mm_packs_epi32(vr0, vr1), d0), s0);
const __m128i err0 = _mm_madd_epi16(e0, e0);
sum32 = _mm_add_epi32(sum32, err0);
}
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[1] * (flt1[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += e * e;
}
dat += dat_stride;
src += src_stride;
flt1 += flt1_stride;
flt += flt_stride;
const __m128i sum64_0 = _mm_cvtepi32_epi64(sum32);
const __m128i sum64_1 = _mm_cvtepi32_epi64(_mm_srli_si128(sum32, 8));
sum64 = _mm_add_epi64(sum64, sum64_0);
@ -357,7 +591,7 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
} else {
__m128i sum32 = _mm_setzero_si128();
for (i = 0; i < height; ++i) {
for (j = 0; j < width - 16; j += 16) {
for (j = 0; j <= width - 16; j += 16) {
const __m128i d = xx_loadu_128(dat + j);
const __m128i s = xx_loadu_128(src + j);
const __m128i d0 = _mm_cvtepu8_epi16(d);
@ -373,7 +607,7 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
}
for (k = j; k < width; ++k) {
const int32_t e = (int32_t)(dat[k]) - src[k];
err += e * e;
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
@ -387,3 +621,209 @@ int64_t av1_lowbd_pixel_proj_error_sse4_1(
err += sum[0] + sum[1];
return err;
}
int64_t av1_highbd_pixel_proj_error_sse4_1(
const uint8_t *src8, int width, int height, int src_stride,
const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
int32_t *flt1, int flt1_stride, int xq[2], const sgr_params_type *params) {
int i, j, k;
const int32_t shift = SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS;
const __m128i rounding = _mm_set1_epi32(1 << (shift - 1));
__m128i sum64 = _mm_setzero_si128();
const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
int64_t err = 0;
if (params->r[0] > 0 && params->r[1] > 0) { // Both filters are enabled
const __m128i xq0 = _mm_set1_epi32(xq[0]);
const __m128i xq1 = _mm_set1_epi32(xq[1]);
for (i = 0; i < height; ++i) {
__m128i sum32 = _mm_setzero_si128();
for (j = 0; j <= width - 8; j += 8) {
// Load 8x pixels from source image
const __m128i s0 = xx_loadu_128(src + j);
// s0 = [7 6 5 4 3 2 1 0] as i16 (indices of src[])
// Load 8x pixels from corrupted image
const __m128i d0 = xx_loadu_128(dat + j);
// d0 = [7 6 5 4 3 2 1 0] as i16 (indices of dat[])
// Shift each pixel value up by SGRPROJ_RST_BITS
const __m128i u0 = _mm_slli_epi16(d0, SGRPROJ_RST_BITS);
// Split u0 into two halves and pad each from u16 to i32
const __m128i u0l = _mm_cvtepu16_epi32(u0);
const __m128i u0h = _mm_cvtepu16_epi32(_mm_srli_si128(u0, 8));
// u0h = [7 6 5 4] as i32, u0l = [3 2 1 0] as i32, all dat[] indices
// Load 8 pixels from first and second filtered images
const __m128i flt0l = xx_loadu_128(flt0 + j);
const __m128i flt0h = xx_loadu_128(flt0 + j + 4);
const __m128i flt1l = xx_loadu_128(flt1 + j);
const __m128i flt1h = xx_loadu_128(flt1 + j + 4);
// flt0 = [7 6 5 4] [3 2 1 0] as i32 (indices of flt0+j)
// flt1 = [7 6 5 4] [3 2 1 0] as i32 (indices of flt1+j)
// Subtract shifted corrupt image from each filtered image
// This gives our two basis vectors for the projection
const __m128i flt0l_subu = _mm_sub_epi32(flt0l, u0l);
const __m128i flt0h_subu = _mm_sub_epi32(flt0h, u0h);
const __m128i flt1l_subu = _mm_sub_epi32(flt1l, u0l);
const __m128i flt1h_subu = _mm_sub_epi32(flt1h, u0h);
// flt?h_subu = [ f[7]-u[7] f[6]-u[6] f[5]-u[5] f[4]-u[4] ] as i32
// flt?l_subu = [ f[3]-u[3] f[2]-u[2] f[1]-u[1] f[0]-u[0] ] as i32
// Multiply each basis vector by the corresponding coefficient
const __m128i v0l = _mm_mullo_epi32(flt0l_subu, xq0);
const __m128i v0h = _mm_mullo_epi32(flt0h_subu, xq0);
const __m128i v1l = _mm_mullo_epi32(flt1l_subu, xq1);
const __m128i v1h = _mm_mullo_epi32(flt1h_subu, xq1);
// Add together the contribution from each scaled basis vector
const __m128i vl = _mm_add_epi32(v0l, v1l);
const __m128i vh = _mm_add_epi32(v0h, v1h);
// Right-shift v with appropriate rounding
const __m128i vrl = _mm_srai_epi32(_mm_add_epi32(vl, rounding), shift);
const __m128i vrh = _mm_srai_epi32(_mm_add_epi32(vh, rounding), shift);
// Saturate each i32 value to i16 and combine lower and upper halves
const __m128i vr = _mm_packs_epi32(vrl, vrh);
// Add twin-subspace-sgr-filter to corrupt image then subtract source
const __m128i e0 = _mm_sub_epi16(_mm_add_epi16(vr, d0), s0);
// Calculate squared error and add adjacent values
const __m128i err0 = _mm_madd_epi16(e0, e0);
sum32 = _mm_add_epi32(sum32, err0);
}
const __m128i sum32l = _mm_cvtepu32_epi64(sum32);
sum64 = _mm_add_epi64(sum64, sum32l);
const __m128i sum32h = _mm_cvtepu32_epi64(_mm_srli_si128(sum32, 8));
sum64 = _mm_add_epi64(sum64, sum32h);
// Process remaining pixels in this row (modulo 8)
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
flt0 += flt0_stride;
flt1 += flt1_stride;
}
} else if (params->r[0] > 0 || params->r[1] > 0) { // Only one filter enabled
const int32_t xq_on = (params->r[0] > 0) ? xq[0] : xq[1];
const __m128i xq_active = _mm_set1_epi32(xq_on);
const __m128i xq_inactive =
_mm_set1_epi32(-xq_on * (1 << SGRPROJ_RST_BITS));
const int32_t *flt = (params->r[0] > 0) ? flt0 : flt1;
const int flt_stride = (params->r[0] > 0) ? flt0_stride : flt1_stride;
for (i = 0; i < height; ++i) {
__m128i sum32 = _mm_setzero_si128();
for (j = 0; j <= width - 8; j += 8) {
// Load 8x pixels from source image
const __m128i s0 = xx_loadu_128(src + j);
// s0 = [7 6 5 4 3 2 1 0] as u16 (indices of src[])
// Load 8x pixels from corrupted image and pad each u16 to i32
const __m128i d0 = xx_loadu_128(dat + j);
const __m128i d0h = _mm_cvtepu16_epi32(_mm_srli_si128(d0, 8));
const __m128i d0l = _mm_cvtepu16_epi32(d0);
// d0h, d0l = [7 6 5 4], [3 2 1 0] as u32 (indices of dat[])
// Load 8 pixels from the filtered image
const __m128i flth = xx_loadu_128(flt + j + 4);
const __m128i fltl = xx_loadu_128(flt + j);
// flth, fltl = [7 6 5 4], [3 2 1 0] as i32 (indices of flt+j)
const __m128i flth_xq = _mm_mullo_epi32(flth, xq_active);
const __m128i fltl_xq = _mm_mullo_epi32(fltl, xq_active);
const __m128i d0h_xq = _mm_mullo_epi32(d0h, xq_inactive);
const __m128i d0l_xq = _mm_mullo_epi32(d0l, xq_inactive);
const __m128i vh = _mm_add_epi32(flth_xq, d0h_xq);
const __m128i vl = _mm_add_epi32(fltl_xq, d0l_xq);
// vh = [ xq0(f[7]-d[7]) xq0(f[6]-d[6]) xq0(f[5]-d[5]) xq0(f[4]-d[4]) ]
// vl = [ xq0(f[3]-d[3]) xq0(f[2]-d[2]) xq0(f[1]-d[1]) xq0(f[0]-d[0]) ]
// Shift this down with appropriate rounding
const __m128i vrh = _mm_srai_epi32(_mm_add_epi32(vh, rounding), shift);
const __m128i vrl = _mm_srai_epi32(_mm_add_epi32(vl, rounding), shift);
// Saturate vr0 and vr1 from i32 to i16 then pack together
const __m128i vr = _mm_packs_epi32(vrl, vrh);
// Subtract twin-subspace-sgr filtered from source image to get error
const __m128i e0 = _mm_sub_epi16(_mm_add_epi16(vr, d0), s0);
// Calculate squared error and add adjacent values
const __m128i err0 = _mm_madd_epi16(e0, e0);
sum32 = _mm_add_epi32(sum32, err0);
}
const __m128i sum32l = _mm_cvtepu32_epi64(sum32);
sum64 = _mm_add_epi64(sum64, sum32l);
const __m128i sum32h = _mm_cvtepu32_epi64(_mm_srli_si128(sum32, 8));
sum64 = _mm_add_epi64(sum64, sum32h);
// Process remaining pixels in this row (modulo 8)
for (k = j; k < width; ++k) {
const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
int32_t v = xq_on * (flt[k] - u);
const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
flt += flt_stride;
}
} else { // Neither filter is enabled
for (i = 0; i < height; ++i) {
__m128i sum32 = _mm_setzero_si128();
for (j = 0; j <= width - 16; j += 16) {
// Load 2x8 u16 from source image
const __m128i s0 = xx_loadu_128(src + j);
const __m128i s1 = xx_loadu_128(src + j + 8);
// Load 2x8 u16 from corrupted image
const __m128i d0 = xx_loadu_128(dat + j);
const __m128i d1 = xx_loadu_128(dat + j + 8);
// Subtract corrupted image from source image
const __m128i diff0 = _mm_sub_epi16(d0, s0);
const __m128i diff1 = _mm_sub_epi16(d1, s1);
// Square error and add adjacent values
const __m128i err0 = _mm_madd_epi16(diff0, diff0);
const __m128i err1 = _mm_madd_epi16(diff1, diff1);
sum32 = _mm_add_epi32(sum32, err0);
sum32 = _mm_add_epi32(sum32, err1);
}
const __m128i sum32l = _mm_cvtepu32_epi64(sum32);
sum64 = _mm_add_epi64(sum64, sum32l);
const __m128i sum32h = _mm_cvtepu32_epi64(_mm_srli_si128(sum32, 8));
sum64 = _mm_add_epi64(sum64, sum32h);
// Process remaining pixels (modulu 8)
for (k = j; k < width; ++k) {
const int32_t e = (int32_t)(dat[k]) - src[k];
err += ((int64_t)e * e);
}
dat += dat_stride;
src += src_stride;
}
}
// Sum 4 values from sum64l and sum64h into err
int64_t sum[2];
xx_storeu_128(sum, sum64);
err += sum[0] + sum[1];
return err;
}

View file

@ -0,0 +1,256 @@
/*
* Copyright (c) 2018, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <assert.h>
#include <immintrin.h>
#include "aom_dsp/x86/synonyms_avx2.h"
#include "aom_ports/system_state.h"
#include "config/av1_rtcd.h"
#include "av1/encoder/rdopt.h"
// Process horizontal and vertical correlations in a 4x4 block of pixels.
// We actually use the 4x4 pixels to calculate correlations corresponding to
// the top-left 3x3 pixels, so this function must be called with 1x1 overlap,
// moving the window along/down by 3 pixels at a time.
INLINE static void horver_correlation_4x4(const int16_t *diff, int stride,
__m256i *xy_sum_32,
__m256i *xz_sum_32, __m256i *x_sum_32,
__m256i *x2_sum_32) {
// Pixels in this 4x4 [ a b c d ]
// are referred to as: [ e f g h ]
// [ i j k l ]
// [ m n o p ]
const __m256i pixels = _mm256_set_epi64x(
*(uint64_t *)&diff[0 * stride], *(uint64_t *)&diff[1 * stride],
*(uint64_t *)&diff[2 * stride], *(uint64_t *)&diff[3 * stride]);
// pixels = [d c b a h g f e] [l k j i p o n m] as i16
const __m256i slli = _mm256_slli_epi64(pixels, 16);
// slli = [c b a 0 g f e 0] [k j i 0 o n m 0] as i16
const __m256i madd_xy = _mm256_madd_epi16(pixels, slli);
// madd_xy = [bc+cd ab fg+gh ef] [jk+kl ij no+op mn] as i32
*xy_sum_32 = _mm256_add_epi32(*xy_sum_32, madd_xy);
// Permute control [3 2] [1 0] => [2 1] [0 0], 0b10010000 = 0x90
const __m256i perm = _mm256_permute4x64_epi64(slli, 0x90);
// perm = [g f e 0 k j i 0] [o n m 0 o n m 0] as i16
const __m256i madd_xz = _mm256_madd_epi16(slli, perm);
// madd_xz = [cg+bf ae gk+fj ei] [ko+jn im oo+nn mm] as i32
*xz_sum_32 = _mm256_add_epi32(*xz_sum_32, madd_xz);
// Sum every element in slli (and then also their squares)
const __m256i madd1_slli = _mm256_madd_epi16(slli, _mm256_set1_epi16(1));
// madd1_slli = [c+b a g+f e] [k+j i o+n m] as i32
*x_sum_32 = _mm256_add_epi32(*x_sum_32, madd1_slli);
const __m256i madd_slli = _mm256_madd_epi16(slli, slli);
// madd_slli = [cc+bb aa gg+ff ee] [kk+jj ii oo+nn mm] as i32
*x2_sum_32 = _mm256_add_epi32(*x2_sum_32, madd_slli);
}
void av1_get_horver_correlation_full_avx2(const int16_t *diff, int stride,
int width, int height, float *hcorr,
float *vcorr) {
// The following notation is used:
// x - current pixel
// y - right neighbour pixel
// z - below neighbour pixel
// w - down-right neighbour pixel
int64_t xy_sum = 0, xz_sum = 0;
int64_t x_sum = 0, x2_sum = 0;
// Process horizontal and vertical correlations through the body in 4x4
// blocks. This excludes the final row and column and possibly one extra
// column depending how 3 divides into width and height
int32_t xy_xz_tmp[8] = { 0 }, x_x2_tmp[8] = { 0 };
__m256i xy_sum_32 = _mm256_setzero_si256();
__m256i xz_sum_32 = _mm256_setzero_si256();
__m256i x_sum_32 = _mm256_setzero_si256();
__m256i x2_sum_32 = _mm256_setzero_si256();
for (int i = 0; i <= height - 4; i += 3) {
for (int j = 0; j <= width - 4; j += 3) {
horver_correlation_4x4(&diff[i * stride + j], stride, &xy_sum_32,
&xz_sum_32, &x_sum_32, &x2_sum_32);
}
const __m256i hadd_xy_xz = _mm256_hadd_epi32(xy_sum_32, xz_sum_32);
// hadd_xy_xz = [ae+bf+cg ei+fj+gk ab+bc+cd ef+fg+gh]
// [im+jn+ko mm+nn+oo ij+jk+kl mn+no+op] as i32
yy_storeu_256(xy_xz_tmp, hadd_xy_xz);
xy_sum += (int64_t)xy_xz_tmp[5] + xy_xz_tmp[4] + xy_xz_tmp[1];
xz_sum += (int64_t)xy_xz_tmp[7] + xy_xz_tmp[6] + xy_xz_tmp[3];
const __m256i hadd_x_x2 = _mm256_hadd_epi32(x_sum_32, x2_sum_32);
// hadd_x_x2 = [aa+bb+cc ee+ff+gg a+b+c e+f+g]
// [ii+jj+kk mm+nn+oo i+j+k m+n+o] as i32
yy_storeu_256(x_x2_tmp, hadd_x_x2);
x_sum += (int64_t)x_x2_tmp[5] + x_x2_tmp[4] + x_x2_tmp[1];
x2_sum += (int64_t)x_x2_tmp[7] + x_x2_tmp[6] + x_x2_tmp[3];
xy_sum_32 = _mm256_setzero_si256();
xz_sum_32 = _mm256_setzero_si256();
x_sum_32 = _mm256_setzero_si256();
x2_sum_32 = _mm256_setzero_si256();
}
// x_sum now covers every pixel except the final 1-2 rows and 1-2 cols
int64_t x_finalrow = 0, x_finalcol = 0, x2_finalrow = 0, x2_finalcol = 0;
// Do we have 2 rows remaining or just the one? Note that width and height
// are powers of 2, so each modulo 3 must be 1 or 2.
if (height % 3 == 1) { // Just horiz corrs on the final row
const int16_t x0 = diff[(height - 1) * stride];
x_sum += x0;
x_finalrow += x0;
x2_sum += x0 * x0;
x2_finalrow += x0 * x0;
for (int j = 0; j < width - 1; ++j) {
const int16_t x = diff[(height - 1) * stride + j];
const int16_t y = diff[(height - 1) * stride + j + 1];
xy_sum += x * y;
x_sum += y;
x2_sum += y * y;
x_finalrow += y;
x2_finalrow += y * y;
}
} else { // Two rows remaining to do
const int16_t x0 = diff[(height - 2) * stride];
const int16_t z0 = diff[(height - 1) * stride];
x_sum += x0 + z0;
x2_sum += x0 * x0 + z0 * z0;
x_finalrow += z0;
x2_finalrow += z0 * z0;
for (int j = 0; j < width - 1; ++j) {
const int16_t x = diff[(height - 2) * stride + j];
const int16_t y = diff[(height - 2) * stride + j + 1];
const int16_t z = diff[(height - 1) * stride + j];
const int16_t w = diff[(height - 1) * stride + j + 1];
// Horizontal and vertical correlations for the penultimate row:
xy_sum += x * y;
xz_sum += x * z;
// Now just horizontal correlations for the final row:
xy_sum += z * w;
x_sum += y + w;
x2_sum += y * y + w * w;
x_finalrow += w;
x2_finalrow += w * w;
}
}
// Do we have 2 columns remaining or just the one?
if (width % 3 == 1) { // Just vert corrs on the final col
const int16_t x0 = diff[width - 1];
x_sum += x0;
x_finalcol += x0;
x2_sum += x0 * x0;
x2_finalcol += x0 * x0;
for (int i = 0; i < height - 1; ++i) {
const int16_t x = diff[i * stride + width - 1];
const int16_t z = diff[(i + 1) * stride + width - 1];
xz_sum += x * z;
x_finalcol += z;
x2_finalcol += z * z;
// So the bottom-right elements don't get counted twice:
if (i < height - (height % 3 == 1 ? 2 : 3)) {
x_sum += z;
x2_sum += z * z;
}
}
} else { // Two cols remaining
const int16_t x0 = diff[width - 2];
const int16_t y0 = diff[width - 1];
x_sum += x0 + y0;
x2_sum += x0 * x0 + y0 * y0;
x_finalcol += y0;
x2_finalcol += y0 * y0;
for (int i = 0; i < height - 1; ++i) {
const int16_t x = diff[i * stride + width - 2];
const int16_t y = diff[i * stride + width - 1];
const int16_t z = diff[(i + 1) * stride + width - 2];
const int16_t w = diff[(i + 1) * stride + width - 1];
// Horizontal and vertical correlations for the penultimate col:
// Skip these on the last iteration of this loop if we also had two
// rows remaining, otherwise the final horizontal and vertical correlation
// get erroneously processed twice
if (i < height - 2 || height % 3 == 1) {
xy_sum += x * y;
xz_sum += x * z;
}
x_finalcol += w;
x2_finalcol += w * w;
// So the bottom-right elements don't get counted twice:
if (i < height - (height % 3 == 1 ? 2 : 3)) {
x_sum += z + w;
x2_sum += z * z + w * w;
}
// Now just vertical correlations for the final column:
xz_sum += y * w;
}
}
// Calculate the simple sums and squared-sums
int64_t x_firstrow = 0, x_firstcol = 0;
int64_t x2_firstrow = 0, x2_firstcol = 0;
for (int j = 0; j < width; ++j) {
x_firstrow += diff[j];
x2_firstrow += diff[j] * diff[j];
}
for (int i = 0; i < height; ++i) {
x_firstcol += diff[i * stride];
x2_firstcol += diff[i * stride] * diff[i * stride];
}
int64_t xhor_sum = x_sum - x_finalcol;
int64_t xver_sum = x_sum - x_finalrow;
int64_t y_sum = x_sum - x_firstcol;
int64_t z_sum = x_sum - x_firstrow;
int64_t x2hor_sum = x2_sum - x2_finalcol;
int64_t x2ver_sum = x2_sum - x2_finalrow;
int64_t y2_sum = x2_sum - x2_firstcol;
int64_t z2_sum = x2_sum - x2_firstrow;
aom_clear_system_state();
const float num_hor = (float)(height * (width - 1));
const float num_ver = (float)((height - 1) * width);
const float xhor_var_n = x2hor_sum - (xhor_sum * xhor_sum) / num_hor;
const float xver_var_n = x2ver_sum - (xver_sum * xver_sum) / num_ver;
const float y_var_n = y2_sum - (y_sum * y_sum) / num_hor;
const float z_var_n = z2_sum - (z_sum * z_sum) / num_ver;
const float xy_var_n = xy_sum - (xhor_sum * y_sum) / num_hor;
const float xz_var_n = xz_sum - (xver_sum * z_sum) / num_ver;
if (xhor_var_n > 0 && y_var_n > 0) {
*hcorr = xy_var_n / sqrtf(xhor_var_n * y_var_n);
*hcorr = *hcorr < 0 ? 0 : *hcorr;
} else {
*hcorr = 1.0;
}
if (xver_var_n > 0 && z_var_n > 0) {
*vcorr = xz_var_n / sqrtf(xver_var_n * z_var_n);
*vcorr = *vcorr < 0 ? 0 : *vcorr;
} else {
*vcorr = 1.0;
}
}

View file

@ -0,0 +1,275 @@
/*
* Copyright (c) 2018, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <assert.h>
#include <emmintrin.h>
#include "aom_dsp/x86/synonyms.h"
#include "aom_ports/system_state.h"
#include "config/av1_rtcd.h"
#include "av1/encoder/rdopt.h"
// Process horizontal and vertical correlations in a 4x4 block of pixels.
// We actually use the 4x4 pixels to calculate correlations corresponding to
// the top-left 3x3 pixels, so this function must be called with 1x1 overlap,
// moving the window along/down by 3 pixels at a time.
INLINE static void horver_correlation_4x4(const int16_t *diff, int stride,
__m128i *xy_sum_32,
__m128i *xz_sum_32, __m128i *x_sum_32,
__m128i *x2_sum_32) {
// Pixels in this 4x4 [ a b c d ]
// are referred to as: [ e f g h ]
// [ i j k l ]
// [ m n o p ]
const __m128i pixelsa = _mm_set_epi64x(*(uint64_t *)&diff[0 * stride],
*(uint64_t *)&diff[2 * stride]);
const __m128i pixelsb = _mm_set_epi64x(*(uint64_t *)&diff[1 * stride],
*(uint64_t *)&diff[3 * stride]);
// pixelsa = [d c b a l k j i] as i16
// pixelsb = [h g f e p o n m] as i16
const __m128i slli_a = _mm_slli_epi64(pixelsa, 16);
const __m128i slli_b = _mm_slli_epi64(pixelsb, 16);
// slli_a = [c b a 0 k j i 0] as i16
// slli_b = [g f e 0 o n m 0] as i16
const __m128i xy_madd_a = _mm_madd_epi16(pixelsa, slli_a);
const __m128i xy_madd_b = _mm_madd_epi16(pixelsb, slli_b);
// xy_madd_a = [bc+cd ab jk+kl ij] as i32
// xy_madd_b = [fg+gh ef no+op mn] as i32
const __m128i xy32 = _mm_hadd_epi32(xy_madd_b, xy_madd_a);
// xy32 = [ab+bc+cd ij+jk+kl ef+fg+gh mn+no+op] as i32
*xy_sum_32 = _mm_add_epi32(*xy_sum_32, xy32);
const __m128i xz_madd_a = _mm_madd_epi16(slli_a, slli_b);
// xz_madd_a = [bf+cg ae jn+ko im] i32
const __m128i swap_b = _mm_srli_si128(slli_b, 8);
// swap_b = [0 0 0 0 g f e 0] as i16
const __m128i xz_madd_b = _mm_madd_epi16(slli_a, swap_b);
// xz_madd_b = [0 0 gk+fj ei] i32
const __m128i xz32 = _mm_hadd_epi32(xz_madd_b, xz_madd_a);
// xz32 = [ae+bf+cg im+jn+ko 0 ei+fj+gk] i32
*xz_sum_32 = _mm_add_epi32(*xz_sum_32, xz32);
// Now calculate the straight sums, x_sum += a+b+c+e+f+g+i+j+k
// (sum up every element in slli_a and swap_b)
const __m128i sum_slli_a = _mm_hadd_epi16(slli_a, slli_a);
const __m128i sum_slli_a32 = _mm_cvtepi16_epi32(sum_slli_a);
// sum_slli_a32 = [c+b a k+j i] as i32
const __m128i swap_b32 = _mm_cvtepi16_epi32(swap_b);
// swap_b32 = [g f e 0] as i32
*x_sum_32 = _mm_add_epi32(*x_sum_32, sum_slli_a32);
*x_sum_32 = _mm_add_epi32(*x_sum_32, swap_b32);
// sum = [c+b+g a+f k+j+e i] as i32
// Also sum their squares
const __m128i slli_a_2 = _mm_madd_epi16(slli_a, slli_a);
const __m128i swap_b_2 = _mm_madd_epi16(swap_b, swap_b);
// slli_a_2 = [c2+b2 a2 k2+j2 i2]
// swap_b_2 = [0 0 g2+f2 e2]
const __m128i sum2 = _mm_hadd_epi32(slli_a_2, swap_b_2);
// sum2 = [0 g2+f2+e2 c2+b2+a2 k2+j2+i2]
*x2_sum_32 = _mm_add_epi32(*x2_sum_32, sum2);
}
void av1_get_horver_correlation_full_sse4_1(const int16_t *diff, int stride,
int width, int height, float *hcorr,
float *vcorr) {
// The following notation is used:
// x - current pixel
// y - right neighbour pixel
// z - below neighbour pixel
// w - down-right neighbour pixel
int64_t xy_sum = 0, xz_sum = 0;
int64_t x_sum = 0, x2_sum = 0;
// Process horizontal and vertical correlations through the body in 4x4
// blocks. This excludes the final row and column and possibly one extra
// column depending how 3 divides into width and height
int32_t xy_tmp[4] = { 0 }, xz_tmp[4] = { 0 };
int32_t x_tmp[4] = { 0 }, x2_tmp[4] = { 0 };
__m128i xy_sum_32 = _mm_setzero_si128();
__m128i xz_sum_32 = _mm_setzero_si128();
__m128i x_sum_32 = _mm_setzero_si128();
__m128i x2_sum_32 = _mm_setzero_si128();
for (int i = 0; i <= height - 4; i += 3) {
for (int j = 0; j <= width - 4; j += 3) {
horver_correlation_4x4(&diff[i * stride + j], stride, &xy_sum_32,
&xz_sum_32, &x_sum_32, &x2_sum_32);
}
xx_storeu_128(xy_tmp, xy_sum_32);
xx_storeu_128(xz_tmp, xz_sum_32);
xx_storeu_128(x_tmp, x_sum_32);
xx_storeu_128(x2_tmp, x2_sum_32);
xy_sum += (int64_t)xy_tmp[3] + xy_tmp[2] + xy_tmp[1];
xz_sum += (int64_t)xz_tmp[3] + xz_tmp[2] + xz_tmp[0];
x_sum += (int64_t)x_tmp[3] + x_tmp[2] + x_tmp[1] + x_tmp[0];
x2_sum += (int64_t)x2_tmp[2] + x2_tmp[1] + x2_tmp[0];
xy_sum_32 = _mm_setzero_si128();
xz_sum_32 = _mm_setzero_si128();
x_sum_32 = _mm_setzero_si128();
x2_sum_32 = _mm_setzero_si128();
}
// x_sum now covers every pixel except the final 1-2 rows and 1-2 cols
int64_t x_finalrow = 0, x_finalcol = 0, x2_finalrow = 0, x2_finalcol = 0;
// Do we have 2 rows remaining or just the one? Note that width and height
// are powers of 2, so each modulo 3 must be 1 or 2.
if (height % 3 == 1) { // Just horiz corrs on the final row
const int16_t x0 = diff[(height - 1) * stride];
x_sum += x0;
x_finalrow += x0;
x2_sum += x0 * x0;
x2_finalrow += x0 * x0;
for (int j = 0; j < width - 1; ++j) {
const int16_t x = diff[(height - 1) * stride + j];
const int16_t y = diff[(height - 1) * stride + j + 1];
xy_sum += x * y;
x_sum += y;
x2_sum += y * y;
x_finalrow += y;
x2_finalrow += y * y;
}
} else { // Two rows remaining to do
const int16_t x0 = diff[(height - 2) * stride];
const int16_t z0 = diff[(height - 1) * stride];
x_sum += x0 + z0;
x2_sum += x0 * x0 + z0 * z0;
x_finalrow += z0;
x2_finalrow += z0 * z0;
for (int j = 0; j < width - 1; ++j) {
const int16_t x = diff[(height - 2) * stride + j];
const int16_t y = diff[(height - 2) * stride + j + 1];
const int16_t z = diff[(height - 1) * stride + j];
const int16_t w = diff[(height - 1) * stride + j + 1];
// Horizontal and vertical correlations for the penultimate row:
xy_sum += x * y;
xz_sum += x * z;
// Now just horizontal correlations for the final row:
xy_sum += z * w;
x_sum += y + w;
x2_sum += y * y + w * w;
x_finalrow += w;
x2_finalrow += w * w;
}
}
// Do we have 2 columns remaining or just the one?
if (width % 3 == 1) { // Just vert corrs on the final col
const int16_t x0 = diff[width - 1];
x_sum += x0;
x_finalcol += x0;
x2_sum += x0 * x0;
x2_finalcol += x0 * x0;
for (int i = 0; i < height - 1; ++i) {
const int16_t x = diff[i * stride + width - 1];
const int16_t z = diff[(i + 1) * stride + width - 1];
xz_sum += x * z;
x_finalcol += z;
x2_finalcol += z * z;
// So the bottom-right elements don't get counted twice:
if (i < height - (height % 3 == 1 ? 2 : 3)) {
x_sum += z;
x2_sum += z * z;
}
}
} else { // Two cols remaining
const int16_t x0 = diff[width - 2];
const int16_t y0 = diff[width - 1];
x_sum += x0 + y0;
x2_sum += x0 * x0 + y0 * y0;
x_finalcol += y0;
x2_finalcol += y0 * y0;
for (int i = 0; i < height - 1; ++i) {
const int16_t x = diff[i * stride + width - 2];
const int16_t y = diff[i * stride + width - 1];
const int16_t z = diff[(i + 1) * stride + width - 2];
const int16_t w = diff[(i + 1) * stride + width - 1];
// Horizontal and vertical correlations for the penultimate col:
// Skip these on the last iteration of this loop if we also had two
// rows remaining, otherwise the final horizontal and vertical correlation
// get erroneously processed twice
if (i < height - 2 || height % 3 == 1) {
xy_sum += x * y;
xz_sum += x * z;
}
x_finalcol += w;
x2_finalcol += w * w;
// So the bottom-right elements don't get counted twice:
if (i < height - (height % 3 == 1 ? 2 : 3)) {
x_sum += z + w;
x2_sum += z * z + w * w;
}
// Now just vertical correlations for the final column:
xz_sum += y * w;
}
}
// Calculate the simple sums and squared-sums
int64_t x_firstrow = 0, x_firstcol = 0;
int64_t x2_firstrow = 0, x2_firstcol = 0;
for (int j = 0; j < width; ++j) {
x_firstrow += diff[j];
x2_firstrow += diff[j] * diff[j];
}
for (int i = 0; i < height; ++i) {
x_firstcol += diff[i * stride];
x2_firstcol += diff[i * stride] * diff[i * stride];
}
int64_t xhor_sum = x_sum - x_finalcol;
int64_t xver_sum = x_sum - x_finalrow;
int64_t y_sum = x_sum - x_firstcol;
int64_t z_sum = x_sum - x_firstrow;
int64_t x2hor_sum = x2_sum - x2_finalcol;
int64_t x2ver_sum = x2_sum - x2_finalrow;
int64_t y2_sum = x2_sum - x2_firstcol;
int64_t z2_sum = x2_sum - x2_firstrow;
aom_clear_system_state();
const float num_hor = (float)(height * (width - 1));
const float num_ver = (float)((height - 1) * width);
const float xhor_var_n = x2hor_sum - (xhor_sum * xhor_sum) / num_hor;
const float xver_var_n = x2ver_sum - (xver_sum * xver_sum) / num_ver;
const float y_var_n = y2_sum - (y_sum * y_sum) / num_hor;
const float z_var_n = z2_sum - (z_sum * z_sum) / num_ver;
const float xy_var_n = xy_sum - (xhor_sum * y_sum) / num_hor;
const float xz_var_n = xz_sum - (xver_sum * z_sum) / num_ver;
if (xhor_var_n > 0 && y_var_n > 0) {
*hcorr = xy_var_n / sqrtf(xhor_var_n * y_var_n);
*hcorr = *hcorr < 0 ? 0 : *hcorr;
} else {
*hcorr = 1.0;
}
if (xver_var_n > 0 && z_var_n > 0) {
*vcorr = xz_var_n / sqrtf(xver_var_n * z_var_n);
*vcorr = *vcorr < 0 ? 0 : *vcorr;
} else {
*vcorr = 1.0;
}
}

View file

@ -16,6 +16,7 @@
SECTION .text
; TODO(yunqing) The following optimization is not used since the c code changes.
; void av1_temporal_filter_apply_sse2 | arg
; (unsigned char *frame1, | 0
; unsigned int stride, | 1