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];