Compute gradient data once at superblock level
This CL enables the generation of gradient data at superblock level
and the consumption of the same at block level when
partition_search_type is SEARCH_PARTITION. This optimization is
enabled only for intra frames. This design avoids repetitive
calculations of gradient data at pixel level as it is calculated
based on source pixels.
For AVIF still image encode,
Instruction Count
speed Reduction(%)
0 0.863
1 1.500
2 1.217
3 1.812
4 2.844
5 3.242
6 0.470
BUG=aomedia:2996
BUG=aomedia:2959
Change-Id: I0cf14b50c5598654dd6419b2793908c810ec7cbe
diff --git a/av1/encoder/block.h b/av1/encoder/block.h
index 91df25d..fd32458 100644
--- a/av1/encoder/block.h
+++ b/av1/encoder/block.h
@@ -834,6 +834,14 @@
int lighting_change;
int low_sumdiff;
} CONTENT_STATE_SB;
+
+// Structure to hold pixel level gradient info.
+typedef struct {
+ uint16_t abs_dx_abs_dy_sum;
+ int8_t hist_bin_idx;
+ bool is_dx_zero;
+} PixelLevelGradientInfo;
+
/*!\endcond */
/*! \brief Encoder's parameters related to the current coding block.
@@ -1173,6 +1181,15 @@
/*! \brief The mode to reuse during \ref av1_rd_pick_intra_mode_sb and
* \ref av1_rd_pick_inter_mode. */
const MB_MODE_INFO *mb_mode_cache;
+ /*! \brief Pointer to the buffer which caches gradient information.
+ *
+ * Pointer to the array of structures to store gradient information of each
+ * pixel in a superblock. The buffer constitutes of MAX_SB_SQUARE pixel level
+ * structures for each of the plane types (PLANE_TYPE_Y and PLANE_TYPE_UV).
+ */
+ PixelLevelGradientInfo *pixel_gradient_info;
+ /*! \brief Flags indicating the availability of cached gradient info. */
+ bool is_sb_gradient_cached[PLANE_TYPES];
/**@}*/
/*****************************************************************************
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index 35305ed..96960fd 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -55,6 +55,7 @@
#include "av1/encoder/encodetxb.h"
#include "av1/encoder/ethread.h"
#include "av1/encoder/extend.h"
+#include "av1/encoder/intra_mode_search_utils.h"
#include "av1/encoder/ml.h"
#include "av1/encoder/motion_search_facade.h"
#include "av1/encoder/partition_strategy.h"
@@ -863,6 +864,12 @@
seg_skip = segfeature_active(seg, segment_id, SEG_LVL_SKIP);
}
+ // Produce the gradient data at superblock level, when intra mode pruning
+ // based on hog is enabled.
+ if (cpi->sf.intra_sf.intra_pruning_with_hog ||
+ cpi->sf.intra_sf.chroma_intra_pruning_with_hog)
+ produce_gradients_for_sb(cpi, x, sb_size, mi_row, mi_col);
+
// encode the superblock
if (use_nonrd_mode) {
encode_nonrd_sb(cpi, td, tile_data, tp, mi_row, mi_col, seg_skip);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index cf76eac..cc79074 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -720,6 +720,12 @@
}
}
+ if (x->pixel_gradient_info == NULL) {
+ CHECK_MEM_ERROR(cm, x->pixel_gradient_info,
+ aom_malloc(sizeof(*x->pixel_gradient_info) * PLANE_TYPES *
+ MAX_SB_SQUARE));
+ }
+
av1_reset_segment_features(cm);
av1_set_high_precision_mv(cpi, 1, 0);
@@ -1401,6 +1407,7 @@
for (int j = 0; j < 2; ++j) {
aom_free(thread_data->td->tmp_pred_bufs[j]);
}
+ aom_free(thread_data->td->pixel_gradient_info);
release_obmc_buffers(&thread_data->td->obmc_buffer);
aom_free(thread_data->td->vt64x64);
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 994f691..00a19de 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -1309,6 +1309,10 @@
PICK_MODE_CONTEXT *firstpass_ctx;
TemporalFilterData tf_data;
TplTxfmStats tpl_txfm_stats;
+ // Pointer to the array of structures to store gradient information of each
+ // pixel in a superblock. The buffer constitutes of MAX_SB_SQUARE pixel level
+ // structures for each of the plane types (PLANE_TYPE_Y and PLANE_TYPE_UV).
+ PixelLevelGradientInfo *pixel_gradient_info;
} ThreadData;
struct EncWorkerData;
diff --git a/av1/encoder/encoder_alloc.h b/av1/encoder/encoder_alloc.h
index ef24a31..d6d7a32 100644
--- a/av1/encoder/encoder_alloc.h
+++ b/av1/encoder/encoder_alloc.h
@@ -268,6 +268,7 @@
for (int j = 0; j < 2; ++j) {
aom_free(cpi->td.mb.tmp_pred_bufs[j]);
}
+ aom_free(cpi->td.mb.pixel_gradient_info);
#if CONFIG_DENOISE
if (cpi->denoise_and_model) {
diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c
index 0d5d383..6cdc2c5 100644
--- a/av1/encoder/ethread.c
+++ b/av1/encoder/ethread.c
@@ -633,6 +633,10 @@
sizeof(*thread_data->td->tmp_pred_bufs[j])));
}
+ CHECK_MEM_ERROR(cm, thread_data->td->pixel_gradient_info,
+ aom_malloc(sizeof(*thread_data->td->pixel_gradient_info) *
+ PLANE_TYPES * MAX_SB_SQUARE));
+
if (cpi->sf.part_sf.partition_search_type == VAR_BASED_PARTITION) {
const int num_64x64_blocks =
(cm->seq_params.sb_size == BLOCK_64X64) ? 1 : 4;
@@ -873,6 +877,8 @@
thread_data->td->mb.tmp_pred_bufs[j] =
thread_data->td->tmp_pred_bufs[j];
}
+ thread_data->td->mb.pixel_gradient_info =
+ thread_data->td->pixel_gradient_info;
thread_data->td->mb.e_mbd.tmp_conv_dst = thread_data->td->mb.tmp_conv_dst;
for (int j = 0; j < 2; ++j) {
diff --git a/av1/encoder/intra_mode_search.c b/av1/encoder/intra_mode_search.c
index a5950e4..8fa5e46 100644
--- a/av1/encoder/intra_mode_search.c
+++ b/av1/encoder/intra_mode_search.c
@@ -673,7 +673,7 @@
const int is_chroma = 1;
const int is_intra_frame = frame_is_intra_only(cm);
prune_intra_mode_with_hog(
- x, bsize,
+ x, bsize, cm->seq_params.sb_size,
thresh[is_intra_frame]
[sf->intra_sf.chroma_intra_pruning_with_hog - 1],
intra_search_state.directional_mode_skip_mask, is_chroma);
@@ -1057,9 +1057,10 @@
!intra_search_state->dir_mode_skip_mask_ready) {
const float thresh[4] = { -1.2f, 0.0f, 0.0f, 1.2f };
const int is_chroma = 0;
- prune_intra_mode_with_hog(
- x, bsize, thresh[sf->intra_sf.intra_pruning_with_hog - 1],
- intra_search_state->directional_mode_skip_mask, is_chroma);
+ prune_intra_mode_with_hog(x, bsize, cm->seq_params.sb_size,
+ thresh[sf->intra_sf.intra_pruning_with_hog - 1],
+ intra_search_state->directional_mode_skip_mask,
+ is_chroma);
intra_search_state->dir_mode_skip_mask_ready = 1;
}
if (intra_search_state->directional_mode_skip_mask[mode]) return 0;
@@ -1206,7 +1207,8 @@
const float thresh[4] = { -1.2f, -1.2f, -0.6f, 0.4f };
const int is_chroma = 0;
prune_intra_mode_with_hog(
- x, bsize, thresh[cpi->sf.intra_sf.intra_pruning_with_hog - 1],
+ x, bsize, cpi->common.seq_params.sb_size,
+ thresh[cpi->sf.intra_sf.intra_pruning_with_hog - 1],
directional_mode_skip_mask, is_chroma);
}
mbmi->filter_intra_mode_info.use_filter_intra = 0;
diff --git a/av1/encoder/intra_mode_search_utils.h b/av1/encoder/intra_mode_search_utils.h
index 17f54eb..806a76e 100644
--- a/av1/encoder/intra_mode_search_utils.h
+++ b/av1/encoder/intra_mode_search_utils.h
@@ -22,6 +22,7 @@
#include "av1/common/reconintra.h"
#include "av1/encoder/encoder.h"
+#include "av1/encoder/encodeframe.h"
#include "av1/encoder/model_rd.h"
#include "av1/encoder/palette.h"
#include "av1/encoder/hybrid_fwd_txfm.h"
@@ -135,6 +136,11 @@
}
#undef FIX_PREC_BITS
+// Normalizes the hog data.
+static AOM_INLINE void normalize_hog(float total, float *hist) {
+ for (int i = 0; i < BINS; ++i) hist[i] /= total;
+}
+
static AOM_INLINE void generate_hog(const uint8_t *src, int stride, int rows,
int cols, float *hist) {
float total = 0.1f;
@@ -166,7 +172,123 @@
src += stride;
}
- for (int i = 0; i < BINS; ++i) hist[i] /= total;
+ normalize_hog(total, hist);
+}
+
+// Computes and stores pixel level gradient information for the given
+// superblock.
+static AOM_INLINE void compute_gradient_info_sb(MACROBLOCK *const x,
+ BLOCK_SIZE sb_size,
+ PLANE_TYPE plane) {
+ PixelLevelGradientInfo *grad_info_sb =
+ x->pixel_gradient_info + plane * MAX_SB_SQUARE;
+ const uint8_t *src = x->plane[plane].src.buf;
+ const int stride = x->plane[plane].src.stride;
+ const int ss_x = x->e_mbd.plane[plane].subsampling_x;
+ const int ss_y = x->e_mbd.plane[plane].subsampling_y;
+ const int sb_height = block_size_high[sb_size] >> ss_y;
+ const int sb_width = block_size_wide[sb_size] >> ss_x;
+ src += stride;
+ for (int r = 1; r < sb_height - 1; ++r) {
+ for (int c = 1; c < sb_width - 1; ++c) {
+ const uint8_t *above = &src[c - stride];
+ const uint8_t *below = &src[c + stride];
+ const uint8_t *left = &src[c - 1];
+ const uint8_t *right = &src[c + 1];
+ // Calculate gradient using Sobel filters.
+ const int dx = (right[-stride] + 2 * right[0] + right[stride]) -
+ (left[-stride] + 2 * left[0] + left[stride]);
+ const int dy = (below[-1] + 2 * below[0] + below[1]) -
+ (above[-1] + 2 * above[0] + above[1]);
+ grad_info_sb[r * sb_width + c].is_dx_zero = (dx == 0);
+ grad_info_sb[r * sb_width + c].abs_dx_abs_dy_sum =
+ (uint16_t)(abs(dx) + abs(dy));
+ grad_info_sb[r * sb_width + c].hist_bin_idx =
+ (dx != 0) ? get_hist_bin_idx(dx, dy) : -1;
+ }
+ src += stride;
+ }
+}
+
+// Function to generate pixel level gradient information for a given superblock.
+// Sets the flags 'is_sb_gradient_cached' for the specific plane-type if
+// gradient info is generated for the same.
+static AOM_INLINE void produce_gradients_for_sb(AV1_COMP *cpi, MACROBLOCK *x,
+ BLOCK_SIZE sb_size, int mi_row,
+ int mi_col) {
+ const SPEED_FEATURES *sf = &cpi->sf;
+ // Initialise flags related to hog data caching.
+ x->is_sb_gradient_cached[PLANE_TYPE_Y] = false;
+ x->is_sb_gradient_cached[PLANE_TYPE_UV] = false;
+
+ // SB level caching of gradient data may not help in speedup for the following
+ // cases:
+ // (1) Inter frames (due to early intra gating)
+ // (2) When partition_search_type is not SEARCH_PARTITION
+ // Hence, gradient data is computed at block level in such cases.
+
+ // TODO(https://crbug.com/aomedia/2996) Enable SB level caching of gradient
+ // data in high-bd path.
+ if (!frame_is_intra_only(&cpi->common) ||
+ sf->part_sf.partition_search_type != SEARCH_PARTITION ||
+ is_cur_buf_hbd(&x->e_mbd))
+ return;
+
+ av1_setup_src_planes(x, cpi->source, mi_row, mi_col,
+ av1_num_planes(&cpi->common), sb_size);
+
+ if (sf->intra_sf.intra_pruning_with_hog) {
+ compute_gradient_info_sb(x, sb_size, PLANE_TYPE_Y);
+ x->is_sb_gradient_cached[PLANE_TYPE_Y] = true;
+ }
+ if (sf->intra_sf.chroma_intra_pruning_with_hog) {
+ compute_gradient_info_sb(x, sb_size, PLANE_TYPE_UV);
+ x->is_sb_gradient_cached[PLANE_TYPE_UV] = true;
+ }
+}
+
+// Reuses the pixel level gradient data generated at superblock level for block
+// level histogram computation.
+static AOM_INLINE void generate_hog_using_gradient_cache(const MACROBLOCK *x,
+ int rows, int cols,
+ BLOCK_SIZE sb_size,
+ PLANE_TYPE plane,
+ float *hist) {
+ float total = 0.1f;
+ const int ss_x = x->e_mbd.plane[plane].subsampling_x;
+ const int ss_y = x->e_mbd.plane[plane].subsampling_y;
+ const int sb_width = block_size_wide[sb_size] >> ss_x;
+
+ // Derive the offset from the starting of the superblock in order to locate
+ // the block level gradient data in the cache.
+ const int mi_row_in_sb = x->e_mbd.mi_row & (mi_size_high[sb_size] - 1);
+ const int mi_col_in_sb = x->e_mbd.mi_col & (mi_size_wide[sb_size] - 1);
+ const int block_offset_in_grad_cache =
+ sb_width * (mi_row_in_sb << (MI_SIZE_LOG2 - ss_y)) +
+ (mi_col_in_sb << (MI_SIZE_LOG2 - ss_x));
+ const PixelLevelGradientInfo *grad_info_blk = x->pixel_gradient_info +
+ plane * MAX_SB_SQUARE +
+ block_offset_in_grad_cache;
+
+ // Retrieve the cached gradient information and generate the histogram.
+ for (int r = 1; r < rows - 1; ++r) {
+ for (int c = 1; c < cols - 1; ++c) {
+ const uint16_t abs_dx_abs_dy_sum =
+ grad_info_blk[r * sb_width + c].abs_dx_abs_dy_sum;
+ if (!abs_dx_abs_dy_sum) continue;
+ total += abs_dx_abs_dy_sum;
+ const bool is_dx_zero = grad_info_blk[r * sb_width + c].is_dx_zero;
+ if (is_dx_zero) {
+ hist[0] += abs_dx_abs_dy_sum >> 1;
+ hist[BINS - 1] += abs_dx_abs_dy_sum >> 1;
+ } else {
+ const int8_t idx = grad_info_blk[r * sb_width + c].hist_bin_idx;
+ assert(idx >= 0 && idx < BINS);
+ hist[idx] += abs_dx_abs_dy_sum;
+ }
+ }
+ }
+ normalize_hog(total, hist);
}
static AOM_INLINE void generate_hog_hbd(const uint8_t *src8, int stride,
@@ -201,11 +323,11 @@
src += stride;
}
- for (int i = 0; i < BINS; ++i) hist[i] /= total;
+ normalize_hog(total, hist);
}
static INLINE void collect_hog_data(const MACROBLOCK *x, BLOCK_SIZE bsize,
- int plane, float *hog) {
+ BLOCK_SIZE sb_size, int plane, float *hog) {
const MACROBLOCKD *xd = &x->e_mbd;
const struct macroblockd_plane *const pd = &xd->plane[plane];
const int ss_x = pd->subsampling_x;
@@ -218,12 +340,19 @@
const int cols =
((xd->mb_to_right_edge >= 0) ? bw : (xd->mb_to_right_edge >> 3) + bw) >>
ss_x;
- const int src_stride = x->plane[plane].src.stride;
- const uint8_t *src = x->plane[plane].src.buf;
- if (is_cur_buf_hbd(xd)) {
- generate_hog_hbd(src, src_stride, rows, cols, hog);
+
+ // If gradient data is already generated at SB level, reuse the cached data.
+ // Otherwise, compute the data.
+ if (x->is_sb_gradient_cached[plane]) {
+ generate_hog_using_gradient_cache(x, rows, cols, sb_size, plane, hog);
} else {
- generate_hog(src, src_stride, rows, cols, hog);
+ const uint8_t *src = x->plane[plane].src.buf;
+ const int src_stride = x->plane[plane].src.stride;
+ if (is_cur_buf_hbd(xd)) {
+ generate_hog_hbd(src, src_stride, rows, cols, hog);
+ } else {
+ generate_hog(src, src_stride, rows, cols, hog);
+ }
}
// Scale the hog so the luma and chroma are on the same scale
@@ -233,13 +362,13 @@
}
static AOM_INLINE void prune_intra_mode_with_hog(
- const MACROBLOCK *x, BLOCK_SIZE bsize, float th,
+ const MACROBLOCK *x, BLOCK_SIZE bsize, BLOCK_SIZE sb_size, float th,
uint8_t *directional_mode_skip_mask, int is_chroma) {
aom_clear_system_state();
const int plane = is_chroma ? AOM_PLANE_U : AOM_PLANE_Y;
float hist[BINS] = { 0.0f };
- collect_hog_data(x, bsize, plane, hist);
+ collect_hog_data(x, bsize, sb_size, plane, hist);
// Make prediction for each of the mode
float scores[DIRECTIONAL_MODES] = { 0.0f };