rtc-svc: Skip testing of non-zeromv for inter-layer pred
For RTC SVC in non-rd mode: skip testing of non-zeromv for
inter-(spatial) layer prediction. Add svc frame level flags,
updated for each frame based on the refresh and ref_idx map.
Negligible quality loss, ~20% speedup on enhancement layer of
superframes whose spatial layer is key frame.
Tested for 2 spatial layers with the svc sample encoder.
Change-Id: Ib8e6f38d8295dcf3ebdb4d5667c4731a4ad845d7
diff --git a/av1/encoder/nonrd_pickmode.c b/av1/encoder/nonrd_pickmode.c
index cbe69be..fd92dbc 100644
--- a/av1/encoder/nonrd_pickmode.c
+++ b/av1/encoder/nonrd_pickmode.c
@@ -1405,6 +1405,14 @@
if (ref_frame > usable_ref_frame) continue;
if (skip_ref_find_pred[ref_frame]) continue;
+ // Skip non-zero motion for SVC if skip_nonzeromv_ref is set.
+ if (frame_mv[this_mode][ref_frame].as_int != 0) {
+ if (ref_frame == LAST_FRAME && cpi->svc.skip_nonzeromv_last)
+ continue;
+ else if (ref_frame == GOLDEN_FRAME && cpi->svc.skip_nonzeromv_gf)
+ continue;
+ }
+
// If the segment reference frame feature is enabled then do nothing if the
// current ref frame is not allowed.
if (segfeature_active(seg, mi->segment_id, SEG_LVL_REF_FRAME) &&
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index 59a4f3d..1564e31 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -27,6 +27,7 @@
int mi_rows = cpi->common.mi_rows;
int mi_cols = cpi->common.mi_cols;
svc->base_framerate = 30.0;
+ svc->current_superframe = 0;
for (int sl = 0; sl < svc->number_spatial_layers; ++sl) {
for (int tl = 0; tl < svc->number_temporal_layers; ++tl) {
@@ -149,6 +150,7 @@
void av1_restore_layer_context(AV1_COMP *const cpi) {
GF_GROUP *const gf_group = &cpi->gf_group;
+ SVC *const svc = &cpi->svc;
LAYER_CONTEXT *const lc = get_layer_context(cpi);
const int old_frame_since_key = cpi->rc.frames_since_key;
const int old_frame_to_key = cpi->rc.frames_to_key;
@@ -163,7 +165,7 @@
// For spatial-svc, allow cyclic-refresh to be applied on the spatial layers,
// for the base temporal layer.
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ &&
- cpi->svc.number_spatial_layers > 1 && cpi->svc.temporal_layer_id == 0) {
+ svc->number_spatial_layers > 1 && svc->temporal_layer_id == 0) {
CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
swap_ptr(&cr->map, &lc->map);
swap_ptr(&cr->last_coded_q_map, &lc->last_coded_q_map);
@@ -171,19 +173,36 @@
cr->actual_num_seg1_blocks = lc->actual_num_seg1_blocks;
cr->actual_num_seg2_blocks = lc->actual_num_seg2_blocks;
}
+ svc->skip_nonzeromv_last = 0;
+ svc->skip_nonzeromv_gf = 0;
+ // For each reference (LAST/GOLDEN) set the skip_nonzero_last/gf frame flags.
+ // This is to skip testing nonzero-mv for that reference if it was last
+ // refreshed (i.e., buffer slot holding that reference was refreshed) on the
+ // previous spatial layer at the same time (current_superframe).
+ if (svc->external_ref_frame_config) {
+ int ref_frame_idx = svc->ref_idx[LAST_FRAME - 1];
+ if (svc->buffer_time_index[ref_frame_idx] == svc->current_superframe &&
+ svc->buffer_spatial_layer[ref_frame_idx] == svc->spatial_layer_id - 1)
+ svc->skip_nonzeromv_last = 1;
+ ref_frame_idx = svc->ref_idx[GOLDEN_FRAME - 1];
+ if (svc->buffer_time_index[ref_frame_idx] == svc->current_superframe &&
+ svc->buffer_spatial_layer[ref_frame_idx] == svc->spatial_layer_id - 1)
+ svc->skip_nonzeromv_gf = 1;
+ }
}
void av1_save_layer_context(AV1_COMP *const cpi) {
GF_GROUP *const gf_group = &cpi->gf_group;
+ SVC *const svc = &cpi->svc;
LAYER_CONTEXT *lc = get_layer_context(cpi);
lc->rc = cpi->rc;
lc->target_bandwidth = (int)cpi->oxcf.target_bandwidth;
lc->group_index = gf_group->index;
- if (cpi->svc.spatial_layer_id == 0) cpi->svc.base_framerate = cpi->framerate;
+ if (svc->spatial_layer_id == 0) svc->base_framerate = cpi->framerate;
// For spatial-svc, allow cyclic-refresh to be applied on the spatial layers,
// for the base temporal layer.
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ &&
- cpi->svc.number_spatial_layers > 1 && cpi->svc.temporal_layer_id == 0) {
+ cpi->svc.number_spatial_layers > 1 && svc->temporal_layer_id == 0) {
CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
signed char *temp = lc->map;
uint8_t *temp2 = lc->last_coded_q_map;
@@ -195,6 +214,25 @@
lc->actual_num_seg1_blocks = cr->actual_num_seg1_blocks;
lc->actual_num_seg2_blocks = cr->actual_num_seg2_blocks;
}
+ // For any buffer slot that is refreshed, update it with
+ // the spatial_layer_id and the current_superframe.
+ if (cpi->common.current_frame.frame_type == KEY_FRAME) {
+ // All slots are refreshed on KEY.
+ for (unsigned int i = 0; i < REF_FRAMES; i++) {
+ svc->buffer_time_index[i] = svc->current_superframe;
+ svc->buffer_spatial_layer[i] = svc->spatial_layer_id;
+ }
+ } else if (cpi->svc.external_ref_frame_config) {
+ for (unsigned int i = 0; i < INTER_REFS_PER_FRAME; i++) {
+ int ref_frame_map_idx = svc->ref_idx[i];
+ if (cpi->svc.refresh[ref_frame_map_idx]) {
+ svc->buffer_time_index[ref_frame_map_idx] = svc->current_superframe;
+ svc->buffer_spatial_layer[ref_frame_map_idx] = svc->spatial_layer_id;
+ }
+ }
+ }
+ if (svc->spatial_layer_id == svc->number_spatial_layers - 1)
+ svc->current_superframe++;
}
void av1_free_svc_cyclic_refresh(AV1_COMP *const cpi) {
diff --git a/av1/encoder/svc_layercontext.h b/av1/encoder/svc_layercontext.h
index b018f48..885d252 100644
--- a/av1/encoder/svc_layercontext.h
+++ b/av1/encoder/svc_layercontext.h
@@ -53,6 +53,11 @@
int ref_idx[INTER_REFS_PER_FRAME];
int refresh[REF_FRAMES];
double base_framerate;
+ unsigned int current_superframe;
+ unsigned char buffer_time_index[REF_FRAMES];
+ unsigned char buffer_spatial_layer[REF_FRAMES];
+ int skip_nonzeromv_last;
+ int skip_nonzeromv_gf;
// Layer context used for rate control in one pass temporal CBR mode or
// two pass spatial mode.
LAYER_CONTEXT layer_context[AOM_MAX_LAYERS];