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 7d4f07f..25d557d 100644 --- a/av1/common/cfl.c +++ b/av1/common/cfl.c
@@ -46,18 +46,110 @@ 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 16862d4..6a49c11 100644 --- a/av1/encoder/block.h +++ b/av1/encoder/block.h
@@ -119,10 +119,10 @@ 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 @@ 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 8e3f387..c14a2f7 100644 --- a/av1/encoder/encodeframe_utils.c +++ b/av1/encoder/encodeframe_utils.c
@@ -30,12 +30,7 @@ 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 @@ 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 c988e01..72ae44f 100644 --- a/av1/encoder/encodeframe_utils.h +++ b/av1/encoder/encodeframe_utils.h
@@ -483,9 +483,11 @@ 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 0795123..cd836c5 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -964,12 +964,27 @@ 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 @@ } } - 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 @@ 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 @@ 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 @@ 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 4ee8301..ac1a650 100644 --- a/av1/encoder/encoder_alloc.h +++ b/av1/encoder/encoder_alloc.h
@@ -82,11 +82,13 @@ 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 @@ aom_free(cpi->ssim_rdmult_scaling_factors); cpi->ssim_rdmult_scaling_factors = NULL; + CHECK_MEM_ERROR(cm, cpi->ssim_rdmult_scaling_factors, + aom_malloc(num_rows * num_cols * + sizeof(*cpi->ssim_rdmult_scaling_factors))); 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, - 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))); + 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 5a1e8a5..1690e21 100644 --- a/av1/encoder/ethread.c +++ b/av1/encoder/ethread.c
@@ -979,11 +979,14 @@ // 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 @@ "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 8c6cade..5cfa8be 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 @@ 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 @@ 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 @@ 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 @@ 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). - 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; + 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); } } - for (int mode_idx = INTRA_MODE_START; mode_idx < LUMA_MODE_COUNT; - ++mode_idx) { + // 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]; + 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 < 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 2082163..37475a5 100644 --- a/av1/encoder/rdopt_utils.h +++ b/av1/encoder/rdopt_utils.h
@@ -812,10 +812,26 @@ // 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