Commit 9f41643359 for aom
commit 9f4164335928afb9b17a9f762d8cf540ebfc6834
Author: Frank Galligan <fgalligan@google.com>
Date: Sat Oct 31 09:54:05 2026 -0700
av1: Optimize ssim_rdmult and intra_mode_search
Avoid calling pow(x, 1.0) in av1_set_ssim_rdmult for blocks <= 16x16,
skip inter-only buffer allocations in allintra mode, copy only the
active portion of WinnerModeStats in store_winner_mode_stats, hoist
allowed_mode_mask in av1_rd_pick_intra_sby_mode, and broadcast cached
CfL DC prediction rows from scalar registers.
Benchmarking results (incremental vs prev CL / cumulative vs baseline):
- Stock avifenc still-image (Default 1T): -1.67% / -25.98% encode time
- Stock avifenc still-image (Default 8T): -2.47% / -21.16% encode time
- 48-image multi-rate corpus (Full Suite 1T): -1.18% / -26.10% time
- Bit-exact output (1T and 8T)
TAG=agy
CONV=b929a630-a6cd-45a0-979c-1767c30178ee
Change-Id: I22ee84baa4fa3b6fd5aa84f4ae6400a9c9dd5ef8
diff --git a/av1/common/cfl.c b/av1/common/cfl.c
index 7d4f07f863..25d557d3f9 100644
--- a/av1/common/cfl.c
+++ b/av1/common/cfl.c
@@ -46,18 +46,110 @@ void cfl_store_dc_pred(MACROBLOCKD *const xd, const uint8_t *input,
static void cfl_load_dc_pred_lbd(const int16_t *dc_pred_cache, uint8_t *dst,
int dst_stride, int width, int height) {
- for (int j = 0; j < height; j++) {
- memcpy(dst, dc_pred_cache, width);
- dst += dst_stride;
+ // Load the cached 1D row into scalar registers once and broadcast across all
+ // height rows to avoid repeated memory reads per row.
+ switch (width) {
+ case 4: {
+ uint32_t v;
+ memcpy(&v, dc_pred_cache, 4);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v, 4);
+ dst += dst_stride;
+ }
+ return;
+ }
+ case 8: {
+ uint64_t v;
+ memcpy(&v, dc_pred_cache, 8);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v, 8);
+ dst += dst_stride;
+ }
+ return;
+ }
+ case 16: {
+ uint64_t v0, v1;
+ memcpy(&v0, (const uint8_t *)dc_pred_cache, 8);
+ memcpy(&v1, (const uint8_t *)dc_pred_cache + 8, 8);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v0, 8);
+ memcpy(dst + 8, &v1, 8);
+ dst += dst_stride;
+ }
+ return;
+ }
+ case 32: {
+ uint64_t v0, v1, v2, v3;
+ memcpy(&v0, (const uint8_t *)dc_pred_cache, 8);
+ memcpy(&v1, (const uint8_t *)dc_pred_cache + 8, 8);
+ memcpy(&v2, (const uint8_t *)dc_pred_cache + 16, 8);
+ memcpy(&v3, (const uint8_t *)dc_pred_cache + 24, 8);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v0, 8);
+ memcpy(dst + 8, &v1, 8);
+ memcpy(dst + 16, &v2, 8);
+ memcpy(dst + 24, &v3, 8);
+ dst += dst_stride;
+ }
+ return;
+ }
+ default:
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, dc_pred_cache, width);
+ dst += dst_stride;
+ }
+ return;
}
}
static void cfl_load_dc_pred_hbd(const int16_t *dc_pred_cache, uint16_t *dst,
int dst_stride, int width, int height) {
- const size_t num_bytes = width << 1;
- for (int j = 0; j < height; j++) {
- memcpy(dst, dc_pred_cache, num_bytes);
- dst += dst_stride;
+ // Load the cached 1D row into scalar registers once and broadcast across all
+ // height rows to avoid repeated memory reads per row.
+ switch (width) {
+ case 4: {
+ uint64_t v;
+ memcpy(&v, dc_pred_cache, 8);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v, 8);
+ dst += dst_stride;
+ }
+ return;
+ }
+ case 8: {
+ uint64_t v0, v1;
+ memcpy(&v0, dc_pred_cache, 8);
+ memcpy(&v1, dc_pred_cache + 4, 8);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v0, 8);
+ memcpy(dst + 4, &v1, 8);
+ dst += dst_stride;
+ }
+ return;
+ }
+ case 16: {
+ uint64_t v0, v1, v2, v3;
+ memcpy(&v0, dc_pred_cache, 8);
+ memcpy(&v1, dc_pred_cache + 4, 8);
+ memcpy(&v2, dc_pred_cache + 8, 8);
+ memcpy(&v3, dc_pred_cache + 12, 8);
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, &v0, 8);
+ memcpy(dst + 4, &v1, 8);
+ memcpy(dst + 8, &v2, 8);
+ memcpy(dst + 12, &v3, 8);
+ dst += dst_stride;
+ }
+ return;
+ }
+ default: {
+ const size_t num_bytes = width << 1;
+ for (int j = 0; j < height; j++) {
+ memcpy(dst, dc_pred_cache, num_bytes);
+ dst += dst_stride;
+ }
+ return;
+ }
}
}
void cfl_load_dc_pred(MACROBLOCKD *const xd, uint8_t *dst, int dst_stride,
diff --git a/av1/encoder/block.h b/av1/encoder/block.h
index 16862d408f..6a49c1127f 100644
--- a/av1/encoder/block.h
+++ b/av1/encoder/block.h
@@ -119,10 +119,10 @@ typedef struct {
int rate_y;
//! Chroma rate of the winner mode.
int rate_uv;
- //! The color map needed to reconstruct palette mode.
- uint8_t color_index_map[MAX_SB_SQUARE];
//! The current winner mode.
THR_MODES mode_index;
+ //! The color map needed to reconstruct palette mode.
+ uint8_t color_index_map[MAX_SB_SQUARE];
} WinnerModeStats;
/*! \brief Each source plane of the current macroblock
@@ -1478,21 +1478,12 @@ static inline void zero_winner_mode_stats(BLOCK_SIZE bsize, int n_stats,
WinnerModeStats *stats) {
// When winner mode stats are not required, the memory allocation is avoided
// for x->winner_mode_stats. The stats pointer will be NULL in such cases.
+ (void)bsize;
if (stats == NULL) return;
- const int block_height = block_size_high[bsize];
- const int block_width = block_size_wide[bsize];
for (int i = 0; i < n_stats; ++i) {
WinnerModeStats *const stat = &stats[i];
- memset(&stat->mbmi, 0, sizeof(stat->mbmi));
- memset(&stat->rd_cost, 0, sizeof(stat->rd_cost));
- memset(&stat->rd, 0, sizeof(stat->rd));
- memset(&stat->rate_y, 0, sizeof(stat->rate_y));
- memset(&stat->rate_uv, 0, sizeof(stat->rate_uv));
- // Do not reset the whole array as it is CPU intensive.
- memset(&stat->color_index_map, 0,
- block_width * block_height * sizeof(stat->color_index_map[0]));
- memset(&stat->mode_index, 0, sizeof(stat->mode_index));
+ memset(stat, 0, offsetof(WinnerModeStats, color_index_map));
}
}
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c
index 8e3f387bba..c14a2f783d 100644
--- a/av1/encoder/encodeframe_utils.c
+++ b/av1/encoder/encodeframe_utils.c
@@ -30,12 +30,7 @@ void av1_set_ssim_rdmult(const AV1_COMP *const cpi, int *errorperbit,
const int num_mi_w = mi_size_wide[bsize_base];
const int num_mi_h = mi_size_high[bsize_base];
const int num_cols = (cm->mi_params.mi_cols + num_mi_w - 1) / num_mi_w;
- const int num_rows = (cm->mi_params.mi_rows + num_mi_h - 1) / num_mi_h;
- const int num_bcols = (mi_size_wide[bsize] + num_mi_w - 1) / num_mi_w;
- const int num_brows = (mi_size_high[bsize] + num_mi_h - 1) / num_mi_h;
- int row, col;
- double num_of_mi = 0.0;
- double geom_mean_of_scale = 1.0;
+ double geom_mean_of_scale;
// To avoid overflow of 'geom_mean_of_scale', bsize_base must be at least
// BLOCK_8X8.
@@ -52,17 +47,30 @@ void av1_set_ssim_rdmult(const AV1_COMP *const cpi, int *errorperbit,
cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ ||
cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2);
- for (row = mi_row / num_mi_w;
- row < num_rows && row < mi_row / num_mi_w + num_brows; ++row) {
- for (col = mi_col / num_mi_h;
- col < num_cols && col < mi_col / num_mi_h + num_bcols; ++col) {
- const int index = row * num_cols + col;
- assert(cpi->ssim_rdmult_scaling_factors[index] != 0.0);
- geom_mean_of_scale *= cpi->ssim_rdmult_scaling_factors[index];
- num_of_mi += 1.0;
+ if (mi_size_wide[bsize] <= 4 && mi_size_high[bsize] <= 4) {
+ geom_mean_of_scale =
+ cpi->ssim_rdmult_scaling_factors[(mi_row >> 2) * num_cols +
+ (mi_col >> 2)];
+ } else {
+ const int num_rows = (cm->mi_params.mi_rows + num_mi_h - 1) / num_mi_h;
+ const int num_bcols = (mi_size_wide[bsize] + num_mi_w - 1) / num_mi_w;
+ const int num_brows = (mi_size_high[bsize] + num_mi_h - 1) / num_mi_h;
+ double num_of_mi = 0.0;
+ geom_mean_of_scale = 1.0;
+ for (int row = mi_row / num_mi_w;
+ row < num_rows && row < mi_row / num_mi_w + num_brows; ++row) {
+ for (int col = mi_col / num_mi_h;
+ col < num_cols && col < mi_col / num_mi_h + num_bcols; ++col) {
+ const int index = row * num_cols + col;
+ assert(cpi->ssim_rdmult_scaling_factors[index] != 0.0);
+ geom_mean_of_scale *= cpi->ssim_rdmult_scaling_factors[index];
+ num_of_mi += 1.0;
+ }
+ }
+ if (num_of_mi > 1.0) {
+ geom_mean_of_scale = pow(geom_mean_of_scale, (1.0 / num_of_mi));
}
}
- geom_mean_of_scale = pow(geom_mean_of_scale, (1.0 / num_of_mi));
*rdmult = (int)((double)(*rdmult) * geom_mean_of_scale + 0.5);
*rdmult = AOMMAX(*rdmult, 0);
diff --git a/av1/encoder/encodeframe_utils.h b/av1/encoder/encodeframe_utils.h
index c988e018d1..72ae44fea0 100644
--- a/av1/encoder/encodeframe_utils.h
+++ b/av1/encoder/encodeframe_utils.h
@@ -483,9 +483,11 @@ static inline void av1_alloc_mb_data(const AV1_COMP *cpi,
av1_alloc_src_diff_buf(cm, mb);
- CHECK_MEM_ERROR(cm, mb->e_mbd.seg_mask,
- (uint8_t *)aom_memalign(
- 16, 2 * MAX_SB_SQUARE * sizeof(mb->e_mbd.seg_mask[0])));
+ if (!frame_is_intra_only(cm)) {
+ CHECK_MEM_ERROR(cm, mb->e_mbd.seg_mask,
+ (uint8_t *)aom_memalign(
+ 16, 2 * MAX_SB_SQUARE * sizeof(mb->e_mbd.seg_mask[0])));
+ }
allocate_winner_mode_stats(cpi, mb);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 0795123ee3..cd836c578e 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -964,12 +964,27 @@ void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf,
aom_memalign(16, sizeof(*x->palette_buffer)));
}
- if (x->tmp_conv_dst == NULL) {
- CHECK_MEM_ERROR(
- cm, x->tmp_conv_dst,
- aom_memalign(32, MAX_SB_SIZE * MAX_SB_SIZE * sizeof(*x->tmp_conv_dst)));
- x->e_mbd.tmp_conv_dst = x->tmp_conv_dst;
+ if (cpi->oxcf.mode != ALLINTRA) {
+ if (x->tmp_conv_dst == NULL) {
+ CHECK_MEM_ERROR(cm, x->tmp_conv_dst,
+ aom_memalign(32, MAX_SB_SIZE * MAX_SB_SIZE *
+ sizeof(*x->tmp_conv_dst)));
+ x->e_mbd.tmp_conv_dst = x->tmp_conv_dst;
+ }
+
+ if (x->upsample_pred == NULL) {
+ // The buffer 'upsampled_pred' is used to store the output of horizontal
+ // filtering in aom_(highbd_)upsampled_pred() function. As the length of
+ // the interpolation filter used is SUBPEL_TAPS a buffer size of
+ // (MAX_SB_SIZE + SUBPEL_TAPS) * MAX_SB_SIZE is allocated.
+ CHECK_MEM_ERROR(
+ cm, x->upsample_pred,
+ aom_memalign(16, (1 + is_highbitdepth) * (MAX_SB_SIZE + SUBPEL_TAPS) *
+ MAX_SB_SIZE * sizeof(*x->upsample_pred)));
+ x->e_mbd.tmp_upsample_pred = x->upsample_pred;
+ }
}
+
// The buffers 'tmp_pred_bufs[]' and 'comp_rd_buffer' are used in inter frames
// to store intermediate inter mode prediction results and are not required
// for allintra encoding mode. Hence, the memory allocations for these buffers
@@ -988,18 +1003,6 @@ void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf,
}
}
- if (x->upsample_pred == NULL) {
- // The buffer 'upsampled_pred' is used to store the output of horizontal
- // filtering in aom_(highbd_)upsampled_pred() function. As the length of the
- // interpolation filter used is SUBPEL_TAPS a buffer size of (MAX_SB_SIZE +
- // SUBPEL_TAPS) * MAX_SB_SIZE is allocated.
- CHECK_MEM_ERROR(
- cm, x->upsample_pred,
- aom_memalign(16, (1 + is_highbitdepth) * (MAX_SB_SIZE + SUBPEL_TAPS) *
- MAX_SB_SIZE * sizeof(*x->upsample_pred)));
- x->e_mbd.tmp_upsample_pred = x->upsample_pred;
- }
-
av1_reset_segment_features(cm);
av1_set_high_precision_mv(cpi, 1, 0);
@@ -1434,15 +1437,17 @@ AV1_PRIMARY *av1_create_primary_compressor(
enc_set_mb_mi(&mi_params, oxcf->frm_dim_cfg.width, oxcf->frm_dim_cfg.height,
BLOCK_4X4);
- const BLOCK_SIZE bsize = BLOCK_16X16;
- const int w = mi_size_wide[bsize];
- const int h = mi_size_high[bsize];
- const int num_cols = (mi_params.mi_cols + w - 1) / w;
- const int num_rows = (mi_params.mi_rows + h - 1) / h;
- AOM_CHECK_MEM_ERROR(
- &ppi->error, ppi->tpl_sb_rdmult_scaling_factors,
- aom_calloc(num_rows * num_cols,
- sizeof(*ppi->tpl_sb_rdmult_scaling_factors)));
+ if (oxcf->mode != ALLINTRA) {
+ const BLOCK_SIZE bsize = BLOCK_16X16;
+ const int w = mi_size_wide[bsize];
+ const int h = mi_size_high[bsize];
+ const int num_cols = (mi_params.mi_cols + w - 1) / w;
+ const int num_rows = (mi_params.mi_rows + h - 1) / h;
+ AOM_CHECK_MEM_ERROR(
+ &ppi->error, ppi->tpl_sb_rdmult_scaling_factors,
+ aom_calloc(num_rows * num_cols,
+ sizeof(*ppi->tpl_sb_rdmult_scaling_factors)));
+ }
#if CONFIG_INTERNAL_STATS
ppi->b_calculate_blockiness = 1;
@@ -1642,11 +1647,13 @@ AV1_COMP *av1_create_compressor(AV1_PRIMARY *ppi, const AV1EncoderConfig *oxcf,
max_mi_rows = size_in_mi(oxcf->frm_dim_cfg.forced_max_frame_height);
}
- const int consec_zero_mv_alloc_size = (max_mi_rows * max_mi_cols) >> 2;
- CHECK_MEM_ERROR(
- cm, cpi->consec_zero_mv,
- aom_calloc(consec_zero_mv_alloc_size, sizeof(*cpi->consec_zero_mv)));
- cpi->consec_zero_mv_alloc_size = consec_zero_mv_alloc_size;
+ if (cpi->oxcf.mode != ALLINTRA) {
+ const int consec_zero_mv_alloc_size = (max_mi_rows * max_mi_cols) >> 2;
+ CHECK_MEM_ERROR(
+ cm, cpi->consec_zero_mv,
+ aom_calloc(consec_zero_mv_alloc_size, sizeof(*cpi->consec_zero_mv)));
+ cpi->consec_zero_mv_alloc_size = consec_zero_mv_alloc_size;
+ }
cpi->mb_weber_stats = NULL;
cpi->mb_weber_stats_alloc_size = 0;
@@ -3046,11 +3053,13 @@ static int encode_without_recode(AV1_COMP *cpi) {
cpi->source = av1_realloc_and_scale_if_required(
cm, unscaled, &cpi->scaled_source, filter_scaler, phase_scaler, true,
false, cpi->oxcf.border_in_pixels, cpi->alloc_pyramid);
- if (frame_is_intra_only(cm) || resize_pending != 0) {
+ if (cpi->oxcf.mode != ALLINTRA &&
+ (frame_is_intra_only(cm) || resize_pending != 0 ||
+ cpi->consec_zero_mv == NULL)) {
const int current_size =
(cm->mi_params.mi_rows * cm->mi_params.mi_cols) >> 2;
- if (cpi->consec_zero_mv &&
- (cpi->consec_zero_mv_alloc_size < current_size)) {
+ if (cpi->consec_zero_mv == NULL ||
+ cpi->consec_zero_mv_alloc_size < current_size) {
aom_free(cpi->consec_zero_mv);
cpi->consec_zero_mv_alloc_size = 0;
CHECK_MEM_ERROR(cm, cpi->consec_zero_mv,
diff --git a/av1/encoder/encoder_alloc.h b/av1/encoder/encoder_alloc.h
index 4ee8301d37..ac1a6508a9 100644
--- a/av1/encoder/encoder_alloc.h
+++ b/av1/encoder/encoder_alloc.h
@@ -82,11 +82,13 @@ static inline void alloc_compressor_data(AV1_COMP *cpi) {
aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR,
"Failed to allocate SMS tree");
}
- cpi->td.firstpass_ctx =
- av1_alloc_pmc(cpi, BLOCK_16X16, &cpi->td.shared_coeff_buf);
- if (!cpi->td.firstpass_ctx)
- aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR,
- "Failed to allocate PICK_MODE_CONTEXT");
+ if (is_stat_generation_stage(cpi) || cpi->ppi->lap_enabled) {
+ cpi->td.firstpass_ctx =
+ av1_alloc_pmc(cpi, BLOCK_16X16, &cpi->td.shared_coeff_buf);
+ if (!cpi->td.firstpass_ctx)
+ aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR,
+ "Failed to allocate PICK_MODE_CONTEXT");
+ }
}
// Allocate mbmi buffers which are used to store mode information at block
@@ -155,14 +157,16 @@ static inline void alloc_rdmult_scaling_factors(AV1_COMP *cpi) {
aom_free(cpi->ssim_rdmult_scaling_factors);
cpi->ssim_rdmult_scaling_factors = NULL;
- aom_free(cpi->tpl_rdmult_scaling_factors);
- cpi->tpl_rdmult_scaling_factors = NULL;
CHECK_MEM_ERROR(cm, cpi->ssim_rdmult_scaling_factors,
- aom_calloc(num_rows * num_cols,
+ aom_malloc(num_rows * num_cols *
sizeof(*cpi->ssim_rdmult_scaling_factors)));
- CHECK_MEM_ERROR(cm, cpi->tpl_rdmult_scaling_factors,
- aom_calloc(num_rows * num_cols,
- sizeof(*cpi->tpl_rdmult_scaling_factors)));
+ aom_free(cpi->tpl_rdmult_scaling_factors);
+ cpi->tpl_rdmult_scaling_factors = NULL;
+ if (cpi->oxcf.mode != ALLINTRA) {
+ CHECK_MEM_ERROR(cm, cpi->tpl_rdmult_scaling_factors,
+ aom_calloc(num_rows * num_cols,
+ sizeof(*cpi->tpl_rdmult_scaling_factors)));
+ }
#if CONFIG_TUNE_VMAF
{
diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c
index 5a1e8a5627..1690e21878 100644
--- a/av1/encoder/ethread.c
+++ b/av1/encoder/ethread.c
@@ -979,11 +979,14 @@ void av1_init_tile_thread_data(AV1_PRIMARY *ppi, int is_first_pass) {
// Set up shared coeff buffers.
av1_setup_shared_coeff_buffer(&ppi->seq_params, &td->shared_coeff_buf,
&ppi->error);
- AOM_CHECK_MEM_ERROR(&ppi->error, td->tmp_conv_dst,
- aom_memalign(32, MAX_SB_SIZE * MAX_SB_SIZE *
- sizeof(*td->tmp_conv_dst)));
+ if (ppi->cpi->oxcf.mode != ALLINTRA) {
+ AOM_CHECK_MEM_ERROR(&ppi->error, td->tmp_conv_dst,
+ aom_memalign(32, MAX_SB_SIZE * MAX_SB_SIZE *
+ sizeof(*td->tmp_conv_dst)));
+ }
- if (i < p_mt_info->num_mod_workers[MOD_FP]) {
+ if ((is_first_pass || ppi->lap_enabled) &&
+ i < p_mt_info->num_mod_workers[MOD_FP]) {
// Set up firstpass PICK_MODE_CONTEXT.
td->firstpass_ctx =
av1_alloc_pmc(ppi->cpi, BLOCK_16X16, &td->shared_coeff_buf);
@@ -992,10 +995,13 @@ void av1_init_tile_thread_data(AV1_PRIMARY *ppi, int is_first_pass) {
"Failed to allocate PICK_MODE_CONTEXT");
}
- AOM_CHECK_MEM_ERROR(
- &ppi->error, td->upsample_pred,
- aom_memalign(16, (1 + is_highbitdepth) * (MAX_SB_SIZE + SUBPEL_TAPS) *
- MAX_SB_SIZE * sizeof(*td->upsample_pred)));
+ if (ppi->cpi->oxcf.mode != ALLINTRA) {
+ AOM_CHECK_MEM_ERROR(
+ &ppi->error, td->upsample_pred,
+ aom_memalign(16, (1 + is_highbitdepth) *
+ (MAX_SB_SIZE + SUBPEL_TAPS) * MAX_SB_SIZE *
+ sizeof(*td->upsample_pred)));
+ }
if (!is_first_pass && i < num_enc_workers) {
// Set up sms_tree.
diff --git a/av1/encoder/intra_mode_search.c b/av1/encoder/intra_mode_search.c
index 8c6cade9cc..5cfa8bede4 100644
--- a/av1/encoder/intra_mode_search.c
+++ b/av1/encoder/intra_mode_search.c
@@ -25,15 +25,6 @@
// the rd threshold check to prune -3 and 3 delta angles.
#define SIZE_OF_ANGLE_DELTA_RD_COST_ARRAY (2 * MAX_ANGLE_DELTA + 3)
-// The order for evaluating delta angles while processing the luma directional
-// intra modes. Currently, this order of evaluation is applicable only when
-// speed feature prune_luma_odd_delta_angles_in_intra is enabled. In this case,
-// even angles are evaluated first in order to facilitate the pruning of odd
-// delta angles based on the rd costs of the neighboring delta angles.
-static const int8_t luma_delta_angles_order[2 * MAX_ANGLE_DELTA] = {
- -2, 2, -3, -1, 1, 3,
-};
-
/*!\cond */
static const PREDICTION_MODE intra_rd_search_mode_order[INTRA_MODES] = {
DC_PRED, H_PRED, V_PRED, SMOOTH_PRED, PAETH_PRED,
@@ -406,17 +397,34 @@ void set_y_mode_and_delta_angle(const int mode_idx, MB_MODE_INFO *const mbmi,
mbmi->mode = intra_rd_search_mode_order[mode_idx];
mbmi->angle_delta[PLANE_TYPE_Y] = 0;
} else {
- mbmi->mode = (mode_idx - INTRA_MODE_END) / (MAX_ANGLE_DELTA * 2) + V_PRED;
- int delta_angle_eval_idx =
- (mode_idx - INTRA_MODE_END) % (MAX_ANGLE_DELTA * 2);
- if (reorder_delta_angle_eval) {
- mbmi->angle_delta[PLANE_TYPE_Y] =
- luma_delta_angles_order[delta_angle_eval_idx];
- } else {
- mbmi->angle_delta[PLANE_TYPE_Y] =
- (delta_angle_eval_idx < 3 ? (delta_angle_eval_idx - 3)
- : (delta_angle_eval_idx - 2));
- }
+ // Precomputed lookup tables for directional mode and delta angle indexed by
+ // (mode_idx - INTRA_MODE_END) to avoid division/modulo by 6.
+ static const uint8_t mode_div6[48] = {
+ V_PRED, V_PRED, V_PRED, V_PRED, V_PRED, V_PRED,
+ H_PRED, H_PRED, H_PRED, H_PRED, H_PRED, H_PRED,
+ D45_PRED, D45_PRED, D45_PRED, D45_PRED, D45_PRED, D45_PRED,
+ D135_PRED, D135_PRED, D135_PRED, D135_PRED, D135_PRED, D135_PRED,
+ D113_PRED, D113_PRED, D113_PRED, D113_PRED, D113_PRED, D113_PRED,
+ D157_PRED, D157_PRED, D157_PRED, D157_PRED, D157_PRED, D157_PRED,
+ D203_PRED, D203_PRED, D203_PRED, D203_PRED, D203_PRED, D203_PRED,
+ D67_PRED, D67_PRED, D67_PRED, D67_PRED, D67_PRED, D67_PRED,
+ };
+ static const int8_t angle_mod6[2][48] = {
+ {
+ -3, -2, -1, 1, 2, 3, -3, -2, -1, 1, 2, 3, -3, -2, -1, 1,
+ 2, 3, -3, -2, -1, 1, 2, 3, -3, -2, -1, 1, 2, 3, -3, -2,
+ -1, 1, 2, 3, -3, -2, -1, 1, 2, 3, -3, -2, -1, 1, 2, 3,
+ },
+ {
+ -2, 2, -3, -1, 1, 3, -2, 2, -3, -1, 1, 3, -2, 2, -3, -1,
+ 1, 3, -2, 2, -3, -1, 1, 3, -2, 2, -3, -1, 1, 3, -2, 2,
+ -3, -1, 1, 3, -2, 2, -3, -1, 1, 3, -2, 2, -3, -1, 1, 3,
+ },
+ };
+ const int offset = mode_idx - INTRA_MODE_END;
+ mbmi->mode = (PREDICTION_MODE)mode_div6[offset];
+ mbmi->angle_delta[PLANE_TYPE_Y] =
+ angle_mod6[reorder_delta_angle_eval != 0][offset];
}
}
@@ -1488,7 +1496,6 @@ int64_t av1_rd_pick_intra_sby_mode(const AV1_COMP *const cpi, MACROBLOCK *x,
MB_MODE_INFO *const mbmi = xd->mi[0];
assert(!is_inter_block(mbmi));
int64_t best_model_rd = INT64_MAX;
- int is_directional_mode;
uint8_t directional_mode_skip_mask[INTRA_MODES] = { 0 };
// Flag to check rd of any intra mode is better than best_rd passed to this
// function
@@ -1529,9 +1536,6 @@ int64_t av1_rd_pick_intra_sby_mode(const AV1_COMP *const cpi, MACROBLOCK *x,
set_mode_eval_params(cpi, x, MODE_EVAL);
MB_MODE_INFO best_mbmi = *mbmi;
- const int max_winner_mode_count =
- winner_mode_count_allowed[cpi->sf.winner_mode_sf.multi_winner_mode_type];
- zero_winner_mode_stats(bsize, max_winner_mode_count, x->winner_mode_stats);
x->winner_mode_count = 0;
// Searches the intra-modes except for intrabc, palette, and filter_intra.
@@ -1540,74 +1544,64 @@ int64_t av1_rd_pick_intra_sby_mode(const AV1_COMP *const cpi, MACROBLOCK *x,
top_intra_model_rd[i] = INT64_MAX;
}
- // Initialize the rdcost corresponding to all the directional and
- // non-directional intra modes.
- // 1. For directional modes, it stores the rdcost values for delta angles -4,
- // -3, ..., 3, 4.
- // 2. The rdcost value for luma_delta_angle is stored at index
- // luma_delta_angle + MAX_ANGLE_DELTA + 1.
- // 3. The rdcost values for fictitious/nonexistent luma_delta_angle -4 and 4
- // (array indices 0 and 8) are always set to INT64_MAX (the initial value).
+ const int use_angle_delta =
+ av1_use_angle_delta(bsize) && intra_mode_cfg->enable_angle_delta;
+ const int max_mode_idx =
+ (use_angle_delta && intra_mode_cfg->enable_directional_intra)
+ ? LUMA_MODE_COUNT
+ : INTRA_MODE_END;
+
+ uint16_t allowed_mode_mask =
+ intra_sf->intra_y_mode_mask[max_txsize_lookup[bsize]];
+ if (!intra_mode_cfg->enable_diagonal_intra)
+ allowed_mode_mask &=
+ ~((1 << D45_PRED) | (1 << D135_PRED) | (1 << D113_PRED) |
+ (1 << D157_PRED) | (1 << D203_PRED) | (1 << D67_PRED));
+ if (!intra_mode_cfg->enable_directional_intra)
+ allowed_mode_mask &=
+ ~((1 << V_PRED) | (1 << H_PRED) | (1 << D45_PRED) | (1 << D135_PRED) |
+ (1 << D113_PRED) | (1 << D157_PRED) | (1 << D203_PRED) |
+ (1 << D67_PRED));
+ if (!intra_mode_cfg->enable_smooth_intra || intra_sf->disable_smooth_intra) {
+ allowed_mode_mask &= ~((1 << SMOOTH_H_PRED) | (1 << SMOOTH_V_PRED));
+ if (!intra_mode_cfg->enable_smooth_intra ||
+ intra_sf->prune_filter_intra_level == 0)
+ allowed_mode_mask &= ~(1 << SMOOTH_PRED);
+ }
+ if (!intra_mode_cfg->enable_paeth_intra)
+ allowed_mode_mask &= ~(1 << PAETH_PRED);
+ if (x->use_mb_mode_cache)
+ allowed_mode_mask &= (uint16_t)(1u << x->mb_mode_cache->mode);
+ if (intra_sf->intra_pruning_with_hog) {
+ for (int m = V_PRED; m <= D67_PRED; ++m) {
+ if (directional_mode_skip_mask[m]) allowed_mode_mask &= ~(1 << m);
+ }
+ }
+
+ // Initialize the rdcost corresponding to directional intra modes when odd
+ // delta angle pruning is active.
int64_t intra_modes_rd_cost[INTRA_MODE_END]
[SIZE_OF_ANGLE_DELTA_RD_COST_ARRAY];
- for (int i = 0; i < INTRA_MODE_END; i++) {
- for (int j = 0; j < SIZE_OF_ANGLE_DELTA_RD_COST_ARRAY; j++) {
- intra_modes_rd_cost[i][j] = INT64_MAX;
+ if (use_angle_delta && intra_sf->prune_luma_odd_delta_angles_in_intra) {
+ for (int i = V_PRED; i <= D67_PRED; i++) {
+ for (int j = 0; j < SIZE_OF_ANGLE_DELTA_RD_COST_ARRAY; j++) {
+ intra_modes_rd_cost[i][j] = INT64_MAX;
+ }
}
}
- for (int mode_idx = INTRA_MODE_START; mode_idx < LUMA_MODE_COUNT;
- ++mode_idx) {
+ for (int mode_idx = INTRA_MODE_START; mode_idx < max_mode_idx; ++mode_idx) {
set_y_mode_and_delta_angle(mode_idx, mbmi,
intra_sf->prune_luma_odd_delta_angles_in_intra);
+ if (!(allowed_mode_mask & (1 << mbmi->mode))) continue;
+
RD_STATS this_rd_stats;
int this_rate, this_rate_tokenonly, s;
- int is_diagonal_mode;
int64_t this_distortion, this_rd;
const int luma_delta_angle = mbmi->angle_delta[PLANE_TYPE_Y];
- is_diagonal_mode = av1_is_diagonal_mode(mbmi->mode);
- if (is_diagonal_mode && !intra_mode_cfg->enable_diagonal_intra) continue;
- if (av1_is_directional_mode(mbmi->mode) &&
- !intra_mode_cfg->enable_directional_intra)
- continue;
-
- // The smooth prediction mode appears to be more frequently picked
- // than horizontal / vertical smooth prediction modes. Hence treat
- // them differently in speed features.
- if ((!intra_mode_cfg->enable_smooth_intra ||
- intra_sf->disable_smooth_intra) &&
- (mbmi->mode == SMOOTH_H_PRED || mbmi->mode == SMOOTH_V_PRED))
- continue;
- if (!intra_mode_cfg->enable_smooth_intra && mbmi->mode == SMOOTH_PRED)
- continue;
-
- // The functionality of filter intra modes and smooth prediction
- // overlap. Hence smooth prediction is pruned only if all the
- // filter intra modes are enabled.
- if (intra_sf->disable_smooth_intra &&
- intra_sf->prune_filter_intra_level == 0 && mbmi->mode == SMOOTH_PRED)
- continue;
- if (!intra_mode_cfg->enable_paeth_intra && mbmi->mode == PAETH_PRED)
- continue;
-
- // Skip the evaluation of modes that do not match with the winner mode in
- // x->mb_mode_cache.
- if (x->use_mb_mode_cache && mbmi->mode != x->mb_mode_cache->mode) continue;
-
- is_directional_mode = av1_is_directional_mode(mbmi->mode);
- if (is_directional_mode && directional_mode_skip_mask[mbmi->mode]) continue;
- if (is_directional_mode &&
- !(av1_use_angle_delta(bsize) && intra_mode_cfg->enable_angle_delta) &&
- luma_delta_angle != 0)
- continue;
-
- // Use intra_y_mode_mask speed feature to skip intra mode evaluation.
- if (!(intra_sf->intra_y_mode_mask[max_txsize_lookup[bsize]] &
- (1 << mbmi->mode)))
- continue;
-
- if (prune_luma_odd_delta_angles_using_rd_cost(
+ if ((luma_delta_angle & 1) &&
+ prune_luma_odd_delta_angles_using_rd_cost(
mbmi, intra_modes_rd_cost[mbmi->mode], best_rd,
intra_sf->prune_luma_odd_delta_angles_in_intra))
continue;
diff --git a/av1/encoder/rdopt_utils.h b/av1/encoder/rdopt_utils.h
index 208216327d..37475a501b 100644
--- a/av1/encoder/rdopt_utils.h
+++ b/av1/encoder/rdopt_utils.h
@@ -812,10 +812,26 @@ static inline void store_winner_mode_stats(
// No mode has higher rd cost than this_rd
return;
} else if (mode_idx < max_winner_mode_count - 1) {
- // Create a slot for current mode and move others to the next slot
- memmove(
- &winner_mode_stats[mode_idx + 1], &winner_mode_stats[mode_idx],
- (max_winner_mode_count - mode_idx - 1) * sizeof(*winner_mode_stats));
+ // Create a slot for current mode and move others to the next slot.
+ // Only copy the active fields up to color_index_map, and copy the active
+ // portion of color_index_map only when palette mode is used.
+ const int move_end =
+ AOMMIN(x->winner_mode_count, max_winner_mode_count - 1);
+ for (int j = move_end; j > mode_idx; --j) {
+ memcpy(&winner_mode_stats[j], &winner_mode_stats[j - 1],
+ offsetof(WinnerModeStats, color_index_map));
+ if (winner_mode_stats[j - 1]
+ .mbmi.palette_mode_info.palette_size[PLANE_TYPE_Y] > 0) {
+ const MACROBLOCKD *const xd = &x->e_mbd;
+ int block_width, block_height;
+ av1_get_block_dimensions(bsize, AOM_PLANE_Y, xd, &block_width,
+ &block_height, NULL, NULL);
+ memcpy(winner_mode_stats[j].color_index_map,
+ winner_mode_stats[j - 1].color_index_map,
+ block_width * block_height *
+ sizeof(winner_mode_stats[0].color_index_map[0]));
+ }
+ }
}
}
// Add a mode stat for winner mode processing