Skip full-pel search based on start mv of prior ref_mv_idx

This CL adds a speed feature to skip full pixel search based on
the start mv of NEWMV mode evaluated prior to current ref_mv_idx.
This SF is enabled for speed 6.

    Instruction Count        BD-Rate Loss(%)
cpu   Reduction(%)    avg.psnr   ovr.psnr    ssim
 6       0.966         0.1043     0.1049    0.1251

STATS_CHANGED

Change-Id: I160a928d9b4c9a07a5c6867cd816e4f3b98b75a8
diff --git a/av1/encoder/interp_search.h b/av1/encoder/interp_search.h
index e25d44d..8eba483 100644
--- a/av1/encoder/interp_search.h
+++ b/av1/encoder/interp_search.h
@@ -119,6 +119,17 @@
    * as a previous search.
    */
   INTERPOLATION_FILTER_STATS interp_filter_stats[MAX_INTERP_FILTER_STATS];
+
+  /*!
+   * Stack to store full pixel search start mv of NEWMV mode.
+   */
+  FULLPEL_MV start_mv_stack[(MAX_REF_MV_SEARCH - 1) * 2];
+
+  /*!
+   * Count of mvs in start mv stack.
+   */
+  int start_mv_cnt;
+
   /*!
    * Index of the last set of saved stats in the interp_filter_stats array.
    */
diff --git a/av1/encoder/motion_search_facade.c b/av1/encoder/motion_search_facade.c
index f95092e..cad4e62 100644
--- a/av1/encoder/motion_search_facade.c
+++ b/av1/encoder/motion_search_facade.c
@@ -24,7 +24,7 @@
 #define RIGHT_SHIFT_MV(x) (((x) + 3 + ((x) >= 0)) >> 3)
 
 typedef struct {
-  FULLPEL_MV fmv;
+  int_mv fmv;
   int weight;
 } cand_mv_t;
 
@@ -90,8 +90,10 @@
                                  GET_MV_RAWPEL(mv.as_mv.col) };
         int unique = 1;
         for (int m = 0; m < *cand_count; m++) {
-          if (RIGHT_SHIFT_MV(fmv.row) == RIGHT_SHIFT_MV(cand[m].fmv.row) &&
-              RIGHT_SHIFT_MV(fmv.col) == RIGHT_SHIFT_MV(cand[m].fmv.col)) {
+          if (RIGHT_SHIFT_MV(fmv.row) ==
+                  RIGHT_SHIFT_MV(cand[m].fmv.as_fullmv.row) &&
+              RIGHT_SHIFT_MV(fmv.col) ==
+                  RIGHT_SHIFT_MV(cand[m].fmv.as_fullmv.col)) {
             unique = 0;
             cand[m].weight++;
             break;
@@ -99,7 +101,7 @@
         }
 
         if (unique) {
-          cand[*cand_count].fmv = fmv;
+          cand[*cand_count].fmv.as_fullmv = fmv;
           cand[*cand_count].weight = 1;
           (*cand_count)++;
         }
@@ -168,9 +170,9 @@
     start_mv = get_fullmv_from_mv(&ref_mv);
 
   // cand stores start_mv and all possible MVs in a SB.
-  cand_mv_t cand[MAX_TPL_BLK_IN_SB * MAX_TPL_BLK_IN_SB + 1] = { { { 0, 0 },
-                                                                  0 } };
-  cand[0].fmv = start_mv;
+  cand_mv_t cand[MAX_TPL_BLK_IN_SB * MAX_TPL_BLK_IN_SB + 1];
+  av1_zero(cand);
+  cand[0].fmv.as_fullmv = start_mv;
   int cnt = 1;
   int total_weight = 0;
 
@@ -179,6 +181,41 @@
     get_mv_candidate_from_tpl(cpi, x, bsize, ref, cand, &cnt, &total_weight);
   }
 
+  const int cand_cnt = AOMMIN(2, cnt);
+  // TODO(any): Test the speed feature for OBMC_CAUSAL mode.
+  if (cpi->sf.mv_sf.skip_fullpel_search_using_startmv &&
+      mbmi->motion_mode == SIMPLE_TRANSLATION) {
+    const int stack_size = args->start_mv_cnt;
+    for (int cand_idx = 0; cand_idx < cand_cnt; cand_idx++) {
+      int_mv *fmv_cand = &cand[cand_idx].fmv;
+      int skip_cand_mv = 0;
+
+      // Check difference between mvs in the stack and candidate mv.
+      for (int stack_idx = 0; stack_idx < stack_size; stack_idx++) {
+        FULLPEL_MV *fmv_stack = &args->start_mv_stack[stack_idx];
+        const int row = abs(fmv_stack->row - fmv_cand->as_fullmv.row);
+        const int col = abs(fmv_stack->col - fmv_cand->as_fullmv.col);
+
+        if (row <= 1 && col <= 1) {
+          skip_cand_mv = 1;
+          break;
+        }
+      }
+      if (skip_cand_mv) {
+        // Mark the candidate mv as invalid so that motion search gets skipped.
+        cand[cand_idx].fmv.as_int = INVALID_MV;
+      } else {
+        // Store start mv candidate of full-pel search in the mv stack (except
+        // last ref_mv_idx).
+        if (mbmi->ref_mv_idx != MAX_REF_MV_SEARCH - 1) {
+          args->start_mv_stack[args->start_mv_cnt] = fmv_cand->as_fullmv;
+          args->start_mv_cnt++;
+          assert(args->start_mv_cnt <= (MAX_REF_MV_SEARCH - 1) * 2);
+        }
+      }
+    }
+  }
+
   // Further reduce the search range.
   if (search_range < INT_MAX) {
     const search_site_config *search_site_cfg =
@@ -211,13 +248,16 @@
     case SIMPLE_TRANSLATION: {
       // Perform a search with the top 2 candidates
       int sum_weight = 0;
-      for (int m = 0; m < AOMMIN(2, cnt); m++) {
-        FULLPEL_MV smv = cand[m].fmv;
+      for (int m = 0; m < cand_cnt; m++) {
+        int_mv smv = cand[m].fmv;
         FULLPEL_MV this_best_mv, this_second_best_mv;
 
-        int thissme = av1_full_pixel_search(
-            smv, &full_ms_params, step_param, cond_cost_list(cpi, cost_list),
-            &this_best_mv, &this_second_best_mv);
+        if (smv.as_int == INVALID_MV) continue;
+
+        int thissme =
+            av1_full_pixel_search(smv.as_fullmv, &full_ms_params, step_param,
+                                  cond_cost_list(cpi, cost_list), &this_best_mv,
+                                  &this_second_best_mv);
 
         if (thissme < bestsme) {
           bestsme = thissme;
@@ -235,6 +275,7 @@
       break;
     default: assert(0 && "Invalid motion mode!\n");
   }
+  if (best_mv->as_int == INVALID_MV) return;
 
   if (scaled_ref_frame) {
     // Swap back the original buffers for subpel motion search.
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 110ddfa..bdfd2de 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -2821,6 +2821,7 @@
     save_mv[i][0].as_int = INVALID_MV;
     save_mv[i][1].as_int = INVALID_MV;
   }
+  args->start_mv_cnt = 0;
 
   // Main loop of this function. This will  iterate over all of the ref mvs
   // in the dynamic reference list and do the following:
@@ -5686,6 +5687,8 @@
                                0,
                                interintra_modes,
                                { { { 0 }, { { 0 } }, { 0 }, 0, 0, 0, 0 } },
+                               { { 0, 0 } },
+                               0,
                                0,
                                -1,
                                -1,
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c
index 1add4f3..5c9f1b2 100644
--- a/av1/encoder/speed_features.c
+++ b/av1/encoder/speed_features.c
@@ -1197,6 +1197,7 @@
 
     sf->mv_sf.simple_motion_subpel_force_stop = FULL_PEL;
     sf->mv_sf.use_bsize_dependent_search_method = 1;
+    sf->mv_sf.skip_fullpel_search_using_startmv = boosted ? 0 : 1;
 
     sf->tpl_sf.gop_length_decision_method = 3;
     sf->tpl_sf.disable_filtered_key_tpl = 1;
@@ -1685,6 +1686,7 @@
   mv_sf->use_downsampled_sad = 0;
   mv_sf->disable_extensive_joint_motion_search = 0;
   mv_sf->disable_second_mv = 0;
+  mv_sf->skip_fullpel_search_using_startmv = 0;
 }
 
 static AOM_INLINE void init_inter_sf(INTER_MODE_SPEED_FEATURES *inter_sf) {
diff --git a/av1/encoder/speed_features.h b/av1/encoder/speed_features.h
index f46a8d8..e10b91e 100644
--- a/av1/encoder/speed_features.h
+++ b/av1/encoder/speed_features.h
@@ -762,6 +762,9 @@
   // 1: use var as the metric
   // 2: disable second MV
   int disable_second_mv;
+
+  // Skips full pixel search based on start mv of prior ref_mv_idx.
+  int skip_fullpel_search_using_startmv;
 } MV_SPEED_FEATURES;
 
 typedef struct INTER_MODE_SPEED_FEATURES {