Improve ALL_ZERO flag handling with global motion Change-Id: I9b2465807c2dd6e356807f3f3555394b382918ad
diff --git a/av1/common/mvref_common.c b/av1/common/mvref_common.c index 36e916f..542ec5c 100644 --- a/av1/common/mvref_common.c +++ b/av1/common/mvref_common.c
@@ -499,7 +499,8 @@ MODE_INFO *mi, MV_REFERENCE_FRAME ref_frame, int_mv *mv_ref_list, int block, int mi_row, int mi_col, find_mv_refs_sync sync, - void *const data, int16_t *mode_context) { + void *const data, int16_t *mode_context, + int_mv zeromv) { const int *ref_sign_bias = cm->ref_frame_sign_bias; int i, refmv_count = 0; #if !CONFIG_REF_MV @@ -661,7 +662,7 @@ if (mode_context) mode_context[ref_frame] = counter_to_context[context_counter]; for (i = refmv_count; i < MAX_MV_REF_CANDIDATES; ++i) - mv_ref_list[i].as_int = 0; + mv_ref_list[i].as_int = zeromv.as_int; } #if CONFIG_EXT_INTER @@ -745,11 +746,12 @@ int_mv *mv_ref_list, int mi_row, int mi_col, find_mv_refs_sync sync, void *const data, int16_t *mode_context) { + int_mv zeromv[2]; #if CONFIG_REF_MV -#if !CONFIG_GLOBAL_MOTION - int idx; + int idx, all_zero = 1; #endif - int all_zero = 1; +#if CONFIG_GLOBAL_MOTION + MV_REFERENCE_FRAME rf[2]; #endif #if CONFIG_EXT_INTER av1_update_mv_context(xd, mi, ref_frame, mv_ref_list, -1, mi_row, mi_col, @@ -759,33 +761,51 @@ mode_context); #endif // CONFIG_REF_MV #endif // CONFIG_EXT_INTER + +#if CONFIG_GLOBAL_MOTION + av1_set_ref_frame(rf, ref_frame); + zeromv[0].as_int = gm_get_motion_vector(&cm->global_motion[rf[0]]).as_int; + zeromv[1].as_int = + (rf[1] != NONE) ? gm_get_motion_vector(&cm->global_motion[rf[1]]).as_int + : 0; +#else + zeromv[0].as_int = zeromv[1].as_int = 0; +#endif + #if CONFIG_REF_MV if (ref_frame <= ALTREF_FRAME) - find_mv_refs_idx(cm, xd, mi, ref_frame, mv_ref_list, -1, mi_row, mi_col, - sync, data, mode_context); -#else - find_mv_refs_idx(cm, xd, mi, ref_frame, mv_ref_list, -1, mi_row, mi_col, sync, - data, mode_context); #endif // CONFIG_REF_MV + find_mv_refs_idx(cm, xd, mi, ref_frame, mv_ref_list, -1, mi_row, mi_col, + sync, data, mode_context, zeromv[0]); #if CONFIG_REF_MV setup_ref_mv_list(cm, xd, ref_frame, ref_mv_count, ref_mv_stack, mv_ref_list, -1, mi_row, mi_col, mode_context); - -#if CONFIG_GLOBAL_MOTION - if ((*ref_mv_count >= 2) || (ref_frame <= ALTREF_FRAME)) all_zero = 0; -#else + /* Note: If global motion is enabled, then we want to set the ALL_ZERO flag + iff all of the MVs we could generate with NEARMV/NEARESTMV are equivalent + to the global motion vector. + Note: For the following to work properly, the encoder can't throw away + any global motion models after calling this function, even if they are + unused. Instead we rely on the recode loop: If any non-IDENTITY model + is unused, the whole frame will be re-encoded without it. + The problem is that, otherwise, we can end up in the following situation: + * Encoder has a global motion model with nonzero translational part, + and all candidate MVs are zero. So the ALL_ZERO flag is unset. + * Encoder throws away global motion because it is never used. + * Decoder sees that there is no global motion and all candidate MVs are + zero, so sets the ALL_ZERO flag. + * This leads to an encode/decode mismatch. + */ if (*ref_mv_count >= 2) { for (idx = 0; idx < AOMMIN(3, *ref_mv_count); ++idx) { - if (ref_mv_stack[idx].this_mv.as_int != 0) all_zero = 0; + if (ref_mv_stack[idx].this_mv.as_int != zeromv[0].as_int) all_zero = 0; if (ref_frame > ALTREF_FRAME) - if (ref_mv_stack[idx].comp_mv.as_int != 0) all_zero = 0; + if (ref_mv_stack[idx].comp_mv.as_int != zeromv[1].as_int) all_zero = 0; } } else if (ref_frame <= ALTREF_FRAME) { for (idx = 0; idx < MAX_MV_REF_CANDIDATES; ++idx) - if (mv_ref_list[idx].as_int != 0) all_zero = 0; + if (mv_ref_list[idx].as_int != zeromv[0].as_int) all_zero = 0; } -#endif if (all_zero) mode_context[ref_frame] |= (1 << ALL_ZERO_FLAG_OFFSET); #endif @@ -818,6 +838,7 @@ MODE_INFO *const mi = xd->mi[0]; b_mode_info *bmi = mi->bmi; int n; + int_mv zeromv; #if CONFIG_REF_MV CANDIDATE_MV tmp_mv; uint8_t idx; @@ -828,8 +849,13 @@ assert(MAX_MV_REF_CANDIDATES == 2); +#if CONFIG_GLOBAL_MOTION + zeromv.as_int = gm_get_motion_vector(&cm->global_motion[ref]).as_int; +#else + zeromv.as_int = 0; +#endif find_mv_refs_idx(cm, xd, mi, mi->mbmi.ref_frame[ref], mv_list, block, mi_row, - mi_col, NULL, NULL, NULL); + mi_col, NULL, NULL, NULL, zeromv); #if CONFIG_REF_MV scan_blk_mbmi(cm, xd, mi_row, mi_col, block, rf, -1, 0, ref_mv_stack,
diff --git a/av1/decoder/decodemv.c b/av1/decoder/decodemv.c index 101ed3e..826d30d 100644 --- a/av1/decoder/decodemv.c +++ b/av1/decoder/decodemv.c
@@ -1479,14 +1479,26 @@ if (xd->ref_mv_count[ref_frame] < 2) { MV_REFERENCE_FRAME rf[2]; + int_mv zeromv[2]; av1_set_ref_frame(rf, ref_frame); +#if CONFIG_GLOBAL_MOTION + zeromv[0].as_int = gm_get_motion_vector(&cm->global_motion[rf[0]]).as_int; + zeromv[1].as_int = + (rf[1] != NONE) + ? gm_get_motion_vector(&cm->global_motion[rf[1]]).as_int + : 0; +#else + zeromv[0].as_int = zeromv[1].as_int = 0; +#endif for (ref = 0; ref < 2; ++ref) { lower_mv_precision(&ref_mvs[rf[ref]][0].as_mv, allow_hp); lower_mv_precision(&ref_mvs[rf[ref]][1].as_mv, allow_hp); } - if (ref_mvs[rf[0]][0].as_int != 0 || ref_mvs[rf[0]][1].as_int != 0 || - ref_mvs[rf[1]][0].as_int != 0 || ref_mvs[rf[1]][1].as_int != 0) + if (ref_mvs[rf[0]][0].as_int != zeromv[0].as_int || + ref_mvs[rf[0]][1].as_int != zeromv[0].as_int || + ref_mvs[rf[1]][0].as_int != zeromv[1].as_int || + ref_mvs[rf[1]][1].as_int != zeromv[1].as_int) inter_mode_ctx[ref_frame] &= ~(1 << ALL_ZERO_FLAG_OFFSET); } }
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c index 4915f78..acce688 100644 --- a/av1/encoder/bitstream.c +++ b/av1/encoder/bitstream.c
@@ -4094,9 +4094,14 @@ AV1_COMMON *const cm = &cpi->common; int frame; for (frame = LAST_FRAME; frame <= ALTREF_FRAME; ++frame) { +#if !CONFIG_REF_MV + // With ref-mv, clearing unused global motion models here is + // unsafe, and we need to rely on the recode loop to do it + // instead. See av1_find_mv_refs for details. if (!cpi->global_motion_used[frame]) { set_default_gmparams(&cm->global_motion[frame]); } +#endif write_global_motion_params(&cm->global_motion[frame], cm->fc->global_motion_types_prob, w); /*
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index dd65ee6..81122c7 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -2889,7 +2889,11 @@ if (cm->global_motion[i].wmtype != IDENTITY && cpi->global_motion_used[i] < MIN_GLOBAL_MOTION_BLKS) { set_default_gmparams(&cm->global_motion[i]); +#if CONFIG_REF_MV + recode = 1; +#else recode |= (cpi->global_motion_used[i] > 0); +#endif } } return recode;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c index 0f5db59..98c5455 100644 --- a/av1/encoder/rdopt.c +++ b/av1/encoder/rdopt.c
@@ -8611,10 +8611,13 @@ if (mbmi_ext->ref_mv_count[ref_frame] < 2) { MV_REFERENCE_FRAME rf[2]; av1_set_ref_frame(rf, ref_frame); - if (mbmi_ext->ref_mvs[rf[0]][0].as_int != 0 || - mbmi_ext->ref_mvs[rf[0]][1].as_int != 0 || - mbmi_ext->ref_mvs[rf[1]][0].as_int != 0 || - mbmi_ext->ref_mvs[rf[1]][1].as_int != 0) + if (mbmi_ext->ref_mvs[rf[0]][0].as_int != + frame_mv[ZEROMV][rf[0]].as_int || + mbmi_ext->ref_mvs[rf[0]][1].as_int != + frame_mv[ZEROMV][rf[0]].as_int || + mbmi_ext->ref_mvs[rf[1]][0].as_int != + frame_mv[ZEROMV][rf[1]].as_int || + mbmi_ext->ref_mvs[rf[1]][1].as_int != frame_mv[ZEROMV][rf[1]].as_int) mbmi_ext->mode_context[ref_frame] &= ~(1 << ALL_ZERO_FLAG_OFFSET); } } @@ -9969,27 +9972,34 @@ } #if CONFIG_REF_MV - if (best_mbmode.ref_frame[0] > INTRA_FRAME && best_mbmode.mv[0].as_int == 0 && -#if CONFIG_EXT_INTER - (best_mbmode.ref_frame[1] <= INTRA_FRAME) -#else - (best_mbmode.ref_frame[1] == NONE || best_mbmode.mv[1].as_int == 0) -#endif // CONFIG_EXT_INTER - ) { + { int8_t ref_frame_type = av1_ref_frame_type(best_mbmode.ref_frame); int16_t mode_ctx = mbmi_ext->mode_context[ref_frame_type]; - if (mode_ctx & (1 << ALL_ZERO_FLAG_OFFSET)) { - best_mbmode.mode = ZEROMV; + int_mv zeromv[2]; #if CONFIG_GLOBAL_MOTION - best_mbmode.mv[0].as_int = - gm_get_motion_vector(&cm->global_motion[best_mbmode.ref_frame[0]]) - .as_int; - if (best_mbmode.ref_frame[1] != NONE) - best_mbmode.mv[1].as_int = - gm_get_motion_vector(&cm->global_motion[best_mbmode.ref_frame[1]]) - .as_int; -#endif + const MV_REFERENCE_FRAME refs[2] = { best_mbmode.ref_frame[0], + best_mbmode.ref_frame[1] }; + zeromv[0].as_int = + gm_get_motion_vector(&cm->global_motion[refs[0]]).as_int; + zeromv[1].as_int = + gm_get_motion_vector(&cm->global_motion[refs[1]]).as_int; + lower_mv_precision(&zeromv[0].as_mv, cm->allow_high_precision_mv); + lower_mv_precision(&zeromv[1].as_mv, cm->allow_high_precision_mv); +#else + zeromv[0].as_int = zeromv[1].as_int = 0; +#endif // CONFIG_GLOBAL_MOTION + if (best_mbmode.ref_frame[0] > INTRA_FRAME && + best_mbmode.mv[0].as_int == zeromv[0].as_int && +#if CONFIG_EXT_INTER + (best_mbmode.ref_frame[1] <= INTRA_FRAME) +#else + (best_mbmode.ref_frame[1] == NONE || + best_mbmode.mv[1].as_int == zeromv[1].as_int) +#endif // CONFIG_EXT_INTER + ) { + best_mbmode.mode = ZEROMV; + } } } #endif