rtc: Some fixes to cyclic refresh
This change sets the segment on skipped blocks
such that segment_id is the same on encoder/decoder
for skip_txfm=1 blocks. Also cm->cur_frame->seg_map
needs to be updated, and over 4x4 blocks. These changes
are needed to allow for loopfilter-delta
on the segments in the cyclic refresh:
https://aomedia-review.googlesource.com/c/aom/+/135621
bdrate gain for speed 9: 0.25%, negligble change in speed
Change-Id: I42984f198d90f717ea5fe9a5a2ff0a8719926da5
diff --git a/av1/encoder/aq_cyclicrefresh.c b/av1/encoder/aq_cyclicrefresh.c
index 63f99b3..f78d90c 100644
--- a/av1/encoder/aq_cyclicrefresh.c
+++ b/av1/encoder/aq_cyclicrefresh.c
@@ -12,6 +12,7 @@
#include <limits.h>
#include <math.h>
+#include "av1/common/pred_common.h"
#include "av1/common/seg_common.h"
#include "av1/encoder/aq_cyclicrefresh.h"
#include "av1/encoder/ratectrl.h"
@@ -147,6 +148,42 @@
return bits_per_mb;
}
+void av1_cyclic_reset_segment_skip(const AV1_COMP *cpi, MACROBLOCK *const x,
+ int mi_row, int mi_col, BLOCK_SIZE bsize) {
+ int cdf_num;
+ const AV1_COMMON *const cm = &cpi->common;
+ MACROBLOCKD *const xd = &x->e_mbd;
+ MB_MODE_INFO *const mbmi = xd->mi[0];
+ const int prev_segment_id = mbmi->segment_id;
+ mbmi->segment_id = av1_get_spatial_seg_pred(cm, xd, &cdf_num);
+ if (prev_segment_id != mbmi->segment_id) {
+ CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
+ const int bw = mi_size_wide[bsize];
+ const int bh = mi_size_high[bsize];
+ const int xmis = AOMMIN(cm->mi_params.mi_cols - mi_col, bw);
+ const int ymis = AOMMIN(cm->mi_params.mi_rows - mi_row, bh);
+ const int block_index = mi_row * cm->mi_params.mi_cols + mi_col;
+ for (int mi_y = 0; mi_y < ymis; mi_y++) {
+ for (int mi_x = 0; mi_x < xmis; mi_x++) {
+ const int map_offset =
+ block_index + mi_y * cm->mi_params.mi_cols + mi_x;
+ cr->map[map_offset] = 0;
+ cpi->enc_seg.map[map_offset] = mbmi->segment_id;
+ cm->cur_frame->seg_map[map_offset] = mbmi->segment_id;
+ }
+ }
+ if (cyclic_refresh_segment_id(prev_segment_id) == CR_SEGMENT_ID_BOOST1)
+ x->actual_num_seg1_blocks -= xmis * ymis;
+ else if (cyclic_refresh_segment_id(prev_segment_id) == CR_SEGMENT_ID_BOOST2)
+ x->actual_num_seg2_blocks -= xmis * ymis;
+ if (cyclic_refresh_segment_id(mbmi->segment_id) == CR_SEGMENT_ID_BOOST1)
+ x->actual_num_seg1_blocks += xmis * ymis;
+ else if (cyclic_refresh_segment_id(mbmi->segment_id) ==
+ CR_SEGMENT_ID_BOOST2)
+ x->actual_num_seg2_blocks += xmis * ymis;
+ }
+}
+
void av1_cyclic_refresh_update_segment(const AV1_COMP *cpi, MACROBLOCK *const x,
int mi_row, int mi_col, BLOCK_SIZE bsize,
int64_t rate, int64_t dist, int skip,
@@ -191,22 +228,21 @@
// Update entries in the cyclic refresh map with new_map_value, and
// copy mbmi->segment_id into global segmentation map.
- // 8x8 is smallest coding block size for non-key frames.
- const int sh = bw << 1;
- for (int mi_y = 0; mi_y < ymis; mi_y += 2) {
- for (int mi_x = 0; mi_x < xmis; mi_x += 2) {
- int map_offset = block_index + mi_y * cm->mi_params.mi_cols + mi_x;
+ for (int mi_y = 0; mi_y < ymis; mi_y++) {
+ for (int mi_x = 0; mi_x < xmis; mi_x++) {
+ const int map_offset = block_index + mi_y * cm->mi_params.mi_cols + mi_x;
cr->map[map_offset] = new_map_value;
cpi->enc_seg.map[map_offset] = mbmi->segment_id;
+ cm->cur_frame->seg_map[map_offset] = mbmi->segment_id;
}
- // Accumulate cyclic refresh update counters.
- if (!dry_run && !frame_is_intra_only(cm)) {
- if (cyclic_refresh_segment_id(mbmi->segment_id) == CR_SEGMENT_ID_BOOST1)
- x->actual_num_seg1_blocks += sh;
- else if (cyclic_refresh_segment_id(mbmi->segment_id) ==
- CR_SEGMENT_ID_BOOST2)
- x->actual_num_seg2_blocks += sh;
- }
+ }
+ // Accumulate cyclic refresh update counters.
+ if (!dry_run) {
+ if (cyclic_refresh_segment_id(mbmi->segment_id) == CR_SEGMENT_ID_BOOST1)
+ x->actual_num_seg1_blocks += xmis * ymis;
+ else if (cyclic_refresh_segment_id(mbmi->segment_id) ==
+ CR_SEGMENT_ID_BOOST2)
+ x->actual_num_seg2_blocks += xmis * ymis;
}
}
diff --git a/av1/encoder/aq_cyclicrefresh.h b/av1/encoder/aq_cyclicrefresh.h
index 97bd6f2..1c0d5cb 100644
--- a/av1/encoder/aq_cyclicrefresh.h
+++ b/av1/encoder/aq_cyclicrefresh.h
@@ -161,6 +161,30 @@
int av1_cyclic_refresh_rc_bits_per_mb(const struct AV1_COMP *cpi, int i,
double correction_factor);
+/*!\brief Update segment_id for blocks are skipped.
+ *
+ * After encoding a given prediction block, of size bsize at (mi_row, mi_col),
+ * check if we should reset the segment_id based on skip_txfm,
+ * and update the cyclic_refresh map and segmentation counters.
+ *
+ * \ingroup cyclic_refresh
+ * \callgraph
+ * \callergraph
+ *
+ * \param[in] cpi Top level encoder structure
+ * \param[in] x Pointer to MACROBLOCK structure
+ * \param[in] mi_row Row coordinate of the block in a step size of MI_SIZE
+ * \param[in] mi_col Col coordinate of the block in a step size of MI_SIZE
+ * \param[in] bsize Block size
+ *
+ * \return Update the \c mbmi->segment_id, the \c cpi->cyclic_refresh and
+ * the \c cm->cpi->enc_seg.map.
+ */
+
+void av1_cyclic_reset_segment_skip(const struct AV1_COMP *cpi,
+ MACROBLOCK *const x, int mi_row, int mi_col,
+ BLOCK_SIZE bsize);
+
/*!\brief Update segment_id for block based on mode selected.
*
* Prior to coding a given prediction block, of size bsize at (mi_row, mi_col),
diff --git a/av1/encoder/partition_search.c b/av1/encoder/partition_search.c
index da46762..3265e76 100644
--- a/av1/encoder/partition_search.c
+++ b/av1/encoder/partition_search.c
@@ -1855,6 +1855,8 @@
update_cb_offsets(x, bsize, subsampling_x, subsampling_y);
if (tile_data->allow_update_cdf) update_stats(&cpi->common, td);
}
+ if (cpi->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ && mbmi->skip_txfm)
+ av1_cyclic_reset_segment_skip(cpi, x, mi_row, mi_col, bsize);
// TODO(Ravi/Remya): Move this copy function to a better logical place
// This function will copy the best mode information from block
// level (x->mbmi_ext) to frame level (cpi->mbmi_ext_info.frame_base). This