Commit cdcac6d5 authored by David Barker's avatar David Barker Committed by Debargha Mukherjee

Make gm_get_motion_vector respect allow_high_precision_mv

This fixes a rare encode/decode mismatch due to inconsistent
rounding of NEARMV/NEARESTMV vs. ZEROMV motion vectors when both
global motion and ref-mv are enabled.

Change-Id: Ia2bbaf63020f5ce7762e027f9bf835fd96797bec
parent deaf05ea
......@@ -131,12 +131,19 @@ typedef struct {
// Convert a global motion translation vector (which may have more bits than a
// regular motion vector) into a motion vector
static INLINE int_mv gm_get_motion_vector(const WarpedMotionParams *gm) {
static INLINE int_mv gm_get_motion_vector(const WarpedMotionParams *gm,
int allow_hp) {
int_mv res;
res.as_mv.row = (int16_t)ROUND_POWER_OF_TWO_SIGNED(gm->wmmat[1],
WARPEDMODEL_PREC_BITS - 3);
res.as_mv.col = (int16_t)ROUND_POWER_OF_TWO_SIGNED(gm->wmmat[0],
WARPEDMODEL_PREC_BITS - 3);
res.as_mv.row = allow_hp ? (int16_t)ROUND_POWER_OF_TWO_SIGNED(
gm->wmmat[1], WARPEDMODEL_PREC_BITS - 3)
: (int16_t)ROUND_POWER_OF_TWO_SIGNED(
gm->wmmat[1], WARPEDMODEL_PREC_BITS - 2) *
2;
res.as_mv.col = allow_hp ? (int16_t)ROUND_POWER_OF_TWO_SIGNED(
gm->wmmat[0], WARPEDMODEL_PREC_BITS - 3)
: (int16_t)ROUND_POWER_OF_TWO_SIGNED(
gm->wmmat[0], WARPEDMODEL_PREC_BITS - 2) *
2;
return res;
}
......
......@@ -499,7 +499,8 @@ static void find_mv_refs_idx(const AV1_COMMON *cm, const MACROBLOCKD *xd,
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 @@ Done:
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,8 +746,12 @@ void av1_find_mv_refs(const AV1_COMMON *cm, const MACROBLOCKD *xd,
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
int all_zero = 1;
int idx, all_zero = 1;
#endif
#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,
......@@ -756,47 +761,58 @@ void av1_find_mv_refs(const AV1_COMMON *cm, const MACROBLOCKD *xd,
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]],
cm->allow_high_precision_mv)
.as_int;
zeromv[1].as_int = (rf[1] != NONE)
? gm_get_motion_vector(&cm->global_motion[rf[1]],
cm->allow_high_precision_mv)
.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);
/* 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) {
int idx;
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) {
int idx;
for (idx = 0; idx < MAX_MV_REF_CANDIDATES; ++idx)
if (mv_ref_list[idx].as_int != 0) all_zero = 0;
}
#if CONFIG_GLOBAL_MOTION
if (all_zero) {
MV_REFERENCE_FRAME rf[2];
av1_set_ref_frame(rf, ref_frame);
if (gm_get_motion_vector(&cm->global_motion[rf[0]]).as_int != 0) {
all_zero = 0;
} else if (rf[1] != NONE &&
gm_get_motion_vector(&cm->global_motion[rf[1]]).as_int != 0) {
all_zero = 0;
}
if (mv_ref_list[idx].as_int != zeromv[0].as_int) all_zero = 0;
}
#endif // CONFIG_GLOBAL_MOTION
if (all_zero) mode_context[ref_frame] |= (1 << ALL_ZERO_FLAG_OFFSET);
#endif // CONFIG_REF_MV
#endif
}
void av1_find_best_ref_mvs(int allow_hp, int_mv *mvlist, int_mv *nearest_mv,
......@@ -826,6 +842,7 @@ void av1_append_sub8x8_mvs_for_idx(const AV1_COMMON *cm, MACROBLOCKD *xd,
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;
......@@ -836,8 +853,15 @@ void av1_append_sub8x8_mvs_for_idx(const AV1_COMMON *cm, MACROBLOCKD *xd,
assert(MAX_MV_REF_CANDIDATES == 2);
#if CONFIG_GLOBAL_MOTION
zeromv.as_int =
gm_get_motion_vector(&cm->global_motion[ref], cm->allow_high_precision_mv)
.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,
......@@ -922,8 +946,8 @@ int findSamples(const AV1_COMMON *cm, MACROBLOCKD *xd, int mi_row, int mi_col,
mi_step = AOMMIN(xd->n8_w, num_8x8_blocks_wide_lookup[mbmi->sb_type]);
if (mbmi->ref_frame[0] == ref_frame && mbmi->ref_frame[1] == NONE) {
int bw = num_4x4_blocks_wide_lookup[mbmi->sb_type] * 4;
int bh = num_4x4_blocks_high_lookup[mbmi->sb_type] * 4;
int bw = block_size_wide[mbmi->sb_type];
int bh = block_size_high[mbmi->sb_type];
int mv_row = mbmi->mv[0].as_mv.row;
int mv_col = mbmi->mv[0].as_mv.col;
int cr_offset = -AOMMAX(bh, 8) / 2 - 1;
......@@ -977,8 +1001,8 @@ int findSamples(const AV1_COMMON *cm, MACROBLOCKD *xd, int mi_row, int mi_col,
mi_step = AOMMIN(xd->n8_h, num_8x8_blocks_high_lookup[mbmi->sb_type]);
if (mbmi->ref_frame[0] == ref_frame && mbmi->ref_frame[1] == NONE) {
int bw = num_4x4_blocks_wide_lookup[mbmi->sb_type] * 4;
int bh = num_4x4_blocks_high_lookup[mbmi->sb_type] * 4;
int bw = block_size_wide[mbmi->sb_type];
int bh = block_size_high[mbmi->sb_type];
int mv_row = mbmi->mv[0].as_mv.row;
int mv_col = mbmi->mv[0].as_mv.col;
int cr_offset = i * 8 + AOMMAX(bh, 8) / 2 - 1;
......@@ -1028,8 +1052,8 @@ int findSamples(const AV1_COMMON *cm, MACROBLOCKD *xd, int mi_row, int mi_col,
MB_MODE_INFO *mbmi = &mi->mbmi;
if (mbmi->ref_frame[0] == ref_frame && mbmi->ref_frame[1] == NONE) {
int bw = num_4x4_blocks_wide_lookup[mbmi->sb_type] * 4;
int bh = num_4x4_blocks_high_lookup[mbmi->sb_type] * 4;
int bw = block_size_wide[mbmi->sb_type];
int bh = block_size_high[mbmi->sb_type];
int mv_row = mbmi->mv[0].as_mv.row;
int mv_col = mbmi->mv[0].as_mv.col;
int cr_offset = -AOMMAX(bh, 8) / 2 - 1;
......@@ -1078,8 +1102,8 @@ int findSamples(const AV1_COMMON *cm, MACROBLOCKD *xd, int mi_row, int mi_col,
} else {
MODE_INFO *mi = xd->mi[0];
MB_MODE_INFO *mbmi = &mi->mbmi;
int bw = num_4x4_blocks_wide_lookup[mbmi->sb_type] * 4;
int bh = num_4x4_blocks_high_lookup[mbmi->sb_type] * 4;
int bw = block_size_wide[mbmi->sb_type];
int bh = block_size_high[mbmi->sb_type];
int mv_row = mbmi->mv[0].as_mv.row;
int mv_col = mbmi->mv[0].as_mv.col;
int cr_offset = AOMMAX(bh, 8) / 2 - 1;
......
......@@ -1230,11 +1230,13 @@ static INLINE int assign_mv(AV1_COMMON *cm, MACROBLOCKD *xd,
}
case ZEROMV: {
#if CONFIG_GLOBAL_MOTION
mv[0].as_int =
gm_get_motion_vector(&cm->global_motion[ref_frame[0]]).as_int;
mv[0].as_int = gm_get_motion_vector(&cm->global_motion[ref_frame[0]],
cm->allow_high_precision_mv)
.as_int;
if (is_compound)
mv[1].as_int =
gm_get_motion_vector(&cm->global_motion[ref_frame[1]]).as_int;
mv[1].as_int = gm_get_motion_vector(&cm->global_motion[ref_frame[1]],
cm->allow_high_precision_mv)
.as_int;
#else
mv[0].as_int = 0;
if (is_compound) mv[1].as_int = 0;
......@@ -1479,14 +1481,29 @@ static void read_inter_block_mode_info(AV1Decoder *const pbi,
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]],
cm->allow_high_precision_mv)
.as_int;
zeromv[1].as_int = (rf[1] != NONE)
? gm_get_motion_vector(&cm->global_motion[rf[1]],
cm->allow_high_precision_mv)
.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);
}
}
......
......@@ -4400,12 +4400,14 @@ static int set_and_cost_bmi_mvs(const AV1_COMP *const cpi, MACROBLOCK *x,
case ZEROMV:
#if CONFIG_GLOBAL_MOTION
this_mv[0].as_int =
gm_get_motion_vector(&cpi->common.global_motion[mbmi->ref_frame[0]])
gm_get_motion_vector(&cpi->common.global_motion[mbmi->ref_frame[0]],
cpi->common.allow_high_precision_mv)
.as_int;
thismvcost += GLOBAL_MOTION_RATE(mbmi->ref_frame[0]);
if (is_compound) {
this_mv[1].as_int =
gm_get_motion_vector(&cpi->common.global_motion[mbmi->ref_frame[1]])
gm_get_motion_vector(&cpi->common.global_motion[mbmi->ref_frame[1]],
cpi->common.allow_high_precision_mv)
.as_int;
thismvcost += GLOBAL_MOTION_RATE(mbmi->ref_frame[1]);
}
......@@ -5193,7 +5195,9 @@ static int64_t rd_pick_best_sub8x8_mode(
#endif // CONFIG_EXT_INTER
#if CONFIG_GLOBAL_MOTION
frame_mv[ZEROMV][frame].as_int =
gm_get_motion_vector(&cm->global_motion[frame]).as_int;
gm_get_motion_vector(&cm->global_motion[frame],
cm->allow_high_precision_mv)
.as_int;
#else // CONFIG_GLOBAL_MOTION
frame_mv[ZEROMV][frame].as_int = 0;
#endif // CONFIG_GLOBAL_MOTION
......@@ -8649,7 +8653,9 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
frame_mv[NEWMV][ref_frame].as_int = INVALID_MV;
#if CONFIG_GLOBAL_MOTION
frame_mv[ZEROMV][ref_frame].as_int =
gm_get_motion_vector(&cm->global_motion[ref_frame]).as_int;
gm_get_motion_vector(&cm->global_motion[ref_frame],
cm->allow_high_precision_mv)
.as_int;
#else // CONFIG_GLOBAL_MOTION
frame_mv[ZEROMV][ref_frame].as_int = 0;
#endif // CONFIG_GLOBAL_MOTION
......@@ -8675,10 +8681,13 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
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);
}
}
......@@ -8751,8 +8760,9 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
// BWDREF_FRAME as well.
mode_skip_mask[ALTREF_FRAME] = ~INTER_NEAREST_NEAR_ZERO;
#if CONFIG_GLOBAL_MOTION
zeromv.as_int =
gm_get_motion_vector(&cm->global_motion[ALTREF_FRAME]).as_int;
zeromv.as_int = gm_get_motion_vector(&cm->global_motion[ALTREF_FRAME],
cm->allow_high_precision_mv)
.as_int;
#else
zeromv.as_int = 0;
#endif // CONFIG_GLOBAL_MOTION
......@@ -9898,11 +9908,14 @@ PALETTE_EXIT:
const uint8_t rf_type = av1_ref_frame_type(best_mbmode.ref_frame);
#endif // CONFIG_REF_MV
#if CONFIG_GLOBAL_MOTION
zeromv[0].as_int = gm_get_motion_vector(&cm->global_motion[refs[0]]).as_int;
zeromv[1].as_int =
comp_pred_mode
? gm_get_motion_vector(&cm->global_motion[refs[1]]).as_int
: 0;
zeromv[0].as_int = gm_get_motion_vector(&cm->global_motion[refs[0]],
cm->allow_high_precision_mv)
.as_int;
zeromv[1].as_int = comp_pred_mode
? gm_get_motion_vector(&cm->global_motion[refs[1]],
cm->allow_high_precision_mv)
.as_int
: 0;
#else
zeromv[0].as_int = 0;
zeromv[1].as_int = 0;
......@@ -10033,27 +10046,36 @@ PALETTE_EXIT:
}
#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]],
cm->allow_high_precision_mv)
.as_int;
zeromv[1].as_int = gm_get_motion_vector(&cm->global_motion[refs[1]],
cm->allow_high_precision_mv)
.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
......@@ -10170,7 +10192,9 @@ void av1_rd_pick_inter_mode_sb_seg_skip(const AV1_COMP *cpi,
mbmi->ref_frame[1] = NONE;
#if CONFIG_GLOBAL_MOTION
mbmi->mv[0].as_int =
gm_get_motion_vector(&cm->global_motion[mbmi->ref_frame[0]]).as_int;
gm_get_motion_vector(&cm->global_motion[mbmi->ref_frame[0]],
cm->allow_high_precision_mv)
.as_int;
#else // CONFIG_GLOBAL_MOTION
mbmi->mv[0].as_int = 0;
#endif // CONFIG_GLOBAL_MOTION
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment