Reuse neighbor's warped motion parameters

If a block's motion_mode is WARPED_CAUSAL and its mode is NEARESTMV, search
its immediate above and left neighbors to get the set of neighbor blocks
using WARPED_CAUSAL motion mode, pick the one with largest block size, and
use that neighbor's warped motion parameters directly for the current block.
If none of the neighbors uses WARPED_CAUSAL motion mode, we estimate the
current block's warped motion parameters.

Before this patch, for every block, we estimate its warped motion parameters.
With this patch, we reduce the number of blocks doing parameter estimation.
Here are results by testing on clips with camera motions.
                    WARPED_CAUSAL blocks   blocks reusing parameters
station2_240p(30f):     3857                    1678
netflix_arieal(30f):     692                     223

No noticable changes in coding gain. Borg test result showed a PSNR
change of +0.006% on cam_lowres set, and -0.014% on lowres set.

Change-Id: If12387ad0ca8a1996ea4c3f1bedcb269ebf78c6c
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 2fc97b7..db7fa25 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -7287,6 +7287,39 @@
 }
 #endif
 
+#if CONFIG_EXT_WARPED_MOTION
+static int handle_zero_mv(const AV1_COMMON *const cm, MACROBLOCK *const x,
+                          BLOCK_SIZE bsize, int mi_col, int mi_row) {
+  MACROBLOCKD *xd = &x->e_mbd;
+  MODE_INFO *mi = xd->mi[0];
+  MB_MODE_INFO *mbmi = &mi->mbmi;
+  int skip = 0;
+
+  // Handle the special case of 0 MV.
+  if (mbmi->ref_frame[0] > INTRA_FRAME && mbmi->ref_frame[1] <= INTRA_FRAME) {
+    int8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
+    int16_t mode_ctx = x->mbmi_ext->mode_context[ref_frame_type];
+    if (mode_ctx & (1 << ALL_ZERO_FLAG_OFFSET)) {
+      int_mv zeromv;
+      const MV_REFERENCE_FRAME ref = mbmi->ref_frame[0];
+      zeromv.as_int = gm_get_motion_vector(&cm->global_motion[ref],
+                                           cm->allow_high_precision_mv, bsize,
+                                           mi_col, mi_row, 0
+#if CONFIG_AMVR
+                                           ,
+                                           cm->cur_frame_mv_precision_level
+#endif
+                                           )
+                          .as_int;
+      if (mbmi->mv[0].as_int == zeromv.as_int && mbmi->mode != GLOBALMV) {
+        skip = 1;
+      }
+    }
+  }
+  return skip;
+}
+#endif  // CONFIG_EXT_WARPED_MOTION
+
 // TODO(afergs): Refactor the MBMI references in here - there's four
 // TODO(afergs): Refactor optional args - add them to a struct or remove
 static int64_t motion_mode_rd(
@@ -7323,7 +7356,7 @@
 
 #if CONFIG_EXT_WARPED_MOTION
   int pts0[SAMPLES_ARRAY_SIZE], pts_inref0[SAMPLES_ARRAY_SIZE];
-  int pts_mv0[SAMPLES_ARRAY_SIZE];
+  int pts_mv0[SAMPLES_ARRAY_SIZE], pts_wm[SAMPLES_ARRAY_SIZE];
   int total_samples;
 #else
   int pts[SAMPLES_ARRAY_SIZE], pts_inref[SAMPLES_ARRAY_SIZE];
@@ -7335,7 +7368,7 @@
   aom_clear_system_state();
 #if CONFIG_EXT_WARPED_MOTION
   mbmi->num_proj_ref[0] =
-      findSamples(cm, xd, mi_row, mi_col, pts0, pts_inref0, pts_mv0);
+      findSamples(cm, xd, mi_row, mi_col, pts0, pts_inref0, pts_mv0, pts_wm);
   total_samples = mbmi->num_proj_ref[0];
 #else
   mbmi->num_proj_ref[0] = findSamples(cm, xd, mi_row, mi_col, pts, pts_inref);
@@ -7395,77 +7428,112 @@
           av1_unswitchable_filter(cm->interp_filter));
 
 #if CONFIG_EXT_WARPED_MOTION
-      memcpy(pts, pts0, total_samples * 2 * sizeof(*pts0));
-      memcpy(pts_inref, pts_inref0, total_samples * 2 * sizeof(*pts_inref0));
-      // Rank the samples by motion vector difference
-      if (mbmi->num_proj_ref[0] > 1) {
-        mbmi->num_proj_ref[0] = sortSamples(pts_mv0, &mbmi->mv[0].as_mv, pts,
-                                            pts_inref, mbmi->num_proj_ref[0]);
-        best_bmc_mbmi->num_proj_ref[0] = mbmi->num_proj_ref[0];
-      }
-#endif  // CONFIG_EXT_WARPED_MOTION
+      // Find a warped neighbor.
+      int cand;
+      int best_cand = -1;
+      int best_weight = 0;
 
-      if (!find_projection(mbmi->num_proj_ref[0], pts, pts_inref, bsize,
-                           mbmi->mv[0].as_mv.row, mbmi->mv[0].as_mv.col,
-                           &mbmi->wm_params[0], mi_row, mi_col)) {
-        // Refine MV for NEWMV mode
-        if (!is_comp_pred && have_newmv_in_inter_mode(this_mode)) {
-          int tmp_rate_mv = 0;
-          const int_mv mv0 = mbmi->mv[0];
-          WarpedMotionParams wm_params0 = mbmi->wm_params[0];
-#if CONFIG_EXT_WARPED_MOTION
-          int num_proj_ref0 = mbmi->num_proj_ref[0];
-
-          // Refine MV in a small range.
-          av1_refine_warped_mv(cpi, x, bsize, mi_row, mi_col, pts0, pts_inref0,
-                               pts_mv0, total_samples);
-#else
-          // Refine MV in a small range.
-          av1_refine_warped_mv(cpi, x, bsize, mi_row, mi_col, pts, pts_inref);
-#endif  // CONFIG_EXT_WARPED_MOTION
-
-          // Keep the refined MV and WM parameters.
-          if (mv0.as_int != mbmi->mv[0].as_int) {
-            const int ref = refs[0];
-            const MV ref_mv = x->mbmi_ext->ref_mvs[ref][0].as_mv;
-
-            tmp_rate_mv =
-                av1_mv_bit_cost(&mbmi->mv[0].as_mv, &ref_mv, x->nmvjointcost,
-                                x->mvcost, MV_COST_WEIGHT);
-
-            if (cpi->sf.adaptive_motion_search)
-              x->pred_mv[ref] = mbmi->mv[0].as_mv;
-
-            single_newmv[ref] = mbmi->mv[0];
-
-            if (discount_newmv_test(cpi, this_mode, mbmi->mv[0], mode_mv,
-                                    refs[0])) {
-              tmp_rate_mv = AOMMAX((tmp_rate_mv / NEW_MV_DISCOUNT_FACTOR), 1);
-            }
-#if CONFIG_EXT_WARPED_MOTION
-            best_bmc_mbmi->num_proj_ref[0] = mbmi->num_proj_ref[0];
-#endif  // CONFIG_EXT_WARPED_MOTION
-            tmp_rate2 = rate2_bmc_nocoeff - rate_mv_bmc + tmp_rate_mv;
-#if CONFIG_DUAL_FILTER
-            mbmi->interp_filters =
-                condition_interp_filters_on_mv(mbmi->interp_filters, xd);
-#endif  // CONFIG_DUAL_FILTER
-          } else {
-            // Restore the old MV and WM parameters.
-            mbmi->mv[0] = mv0;
-            mbmi->wm_params[0] = wm_params0;
-#if CONFIG_EXT_WARPED_MOTION
-            mbmi->num_proj_ref[0] = num_proj_ref0;
-#endif  // CONFIG_EXT_WARPED_MOTION
+      assert(this_mode >= NEARESTMV && this_mode <= NEWMV);
+      if (this_mode == NEARESTMV) {
+        for (cand = 0; cand < mbmi->num_proj_ref[0]; cand++) {
+          if (pts_wm[cand * 2 + 1] > best_weight) {
+            best_weight = pts_wm[cand * 2 + 1];
+            best_cand = cand;
           }
         }
+      }
+
+      if (best_cand != -1) {
+        MODE_INFO *best_mi = xd->mi[pts_wm[2 * best_cand]];
+        assert(best_mi->mbmi.motion_mode == WARPED_CAUSAL);
+        mbmi->wm_params[0] = best_mi->mbmi.wm_params[0];
+
+        // Handle the special case of 0 MV.
+        if (handle_zero_mv(cm, x, bsize, mi_col, mi_row)) continue;
 
         av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, NULL, bsize);
         model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
                         &tmp_dist, skip_txfm_sb, skip_sse_sb);
       } else {
-        continue;
+        memcpy(pts, pts0, total_samples * 2 * sizeof(*pts0));
+        memcpy(pts_inref, pts_inref0, total_samples * 2 * sizeof(*pts_inref0));
+        // Rank the samples by motion vector difference
+        if (mbmi->num_proj_ref[0] > 1) {
+          mbmi->num_proj_ref[0] = sortSamples(pts_mv0, &mbmi->mv[0].as_mv, pts,
+                                              pts_inref, mbmi->num_proj_ref[0]);
+          best_bmc_mbmi->num_proj_ref[0] = mbmi->num_proj_ref[0];
+        }
+#endif  // CONFIG_EXT_WARPED_MOTION
+
+        if (!find_projection(mbmi->num_proj_ref[0], pts, pts_inref, bsize,
+                             mbmi->mv[0].as_mv.row, mbmi->mv[0].as_mv.col,
+                             &mbmi->wm_params[0], mi_row, mi_col)) {
+          // Refine MV for NEWMV mode
+          if (!is_comp_pred && have_newmv_in_inter_mode(this_mode)) {
+            int tmp_rate_mv = 0;
+            const int_mv mv0 = mbmi->mv[0];
+            WarpedMotionParams wm_params0 = mbmi->wm_params[0];
+#if CONFIG_EXT_WARPED_MOTION
+            int num_proj_ref0 = mbmi->num_proj_ref[0];
+
+            // Refine MV in a small range.
+            av1_refine_warped_mv(cpi, x, bsize, mi_row, mi_col, pts0,
+                                 pts_inref0, pts_mv0, total_samples);
+#else
+          // Refine MV in a small range.
+          av1_refine_warped_mv(cpi, x, bsize, mi_row, mi_col, pts, pts_inref);
+#endif  // CONFIG_EXT_WARPED_MOTION
+
+            // Keep the refined MV and WM parameters.
+            if (mv0.as_int != mbmi->mv[0].as_int) {
+              const int ref = refs[0];
+              const MV ref_mv = x->mbmi_ext->ref_mvs[ref][0].as_mv;
+
+              tmp_rate_mv =
+                  av1_mv_bit_cost(&mbmi->mv[0].as_mv, &ref_mv, x->nmvjointcost,
+                                  x->mvcost, MV_COST_WEIGHT);
+
+              if (cpi->sf.adaptive_motion_search)
+                x->pred_mv[ref] = mbmi->mv[0].as_mv;
+
+              single_newmv[ref] = mbmi->mv[0];
+
+              if (discount_newmv_test(cpi, this_mode, mbmi->mv[0], mode_mv,
+                                      refs[0])) {
+                tmp_rate_mv = AOMMAX((tmp_rate_mv / NEW_MV_DISCOUNT_FACTOR), 1);
+              }
+#if CONFIG_EXT_WARPED_MOTION
+              best_bmc_mbmi->num_proj_ref[0] = mbmi->num_proj_ref[0];
+#endif  // CONFIG_EXT_WARPED_MOTION
+              tmp_rate2 = rate2_bmc_nocoeff - rate_mv_bmc + tmp_rate_mv;
+#if CONFIG_DUAL_FILTER
+              mbmi->interp_filters =
+                  condition_interp_filters_on_mv(mbmi->interp_filters, xd);
+#endif  // CONFIG_DUAL_FILTER
+            } else {
+              // Restore the old MV and WM parameters.
+              mbmi->mv[0] = mv0;
+              mbmi->wm_params[0] = wm_params0;
+#if CONFIG_EXT_WARPED_MOTION
+              mbmi->num_proj_ref[0] = num_proj_ref0;
+#endif  // CONFIG_EXT_WARPED_MOTION
+            }
+          }
+
+#if CONFIG_EXT_WARPED_MOTION
+          // Handle the special case of 0 MV.
+          if (handle_zero_mv(cm, x, bsize, mi_col, mi_row)) continue;
+#endif  // CONFIG_EXT_WARPED_MOTION
+
+          av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, NULL, bsize);
+          model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+                          &tmp_dist, skip_txfm_sb, skip_sse_sb);
+        } else {
+          continue;
+        }
+#if CONFIG_EXT_WARPED_MOTION
       }
+#endif  // CONFIG_EXT_WARPED_MOTION
     }
     x->skip = 0;
 
@@ -9960,7 +10028,11 @@
   // Therefore, sometimes, NEWMV is chosen instead of NEARESTMV, NEARMV, and
   // GLOBALMV. Here, checks are added for those cases, and the mode decisions
   // are corrected.
-  if (best_mbmode.mode == NEWMV || best_mbmode.mode == NEW_NEWMV) {
+  if ((best_mbmode.mode == NEWMV || best_mbmode.mode == NEW_NEWMV)
+#if CONFIG_EXT_WARPED_MOTION
+      && best_mbmode.motion_mode != WARPED_CAUSAL
+#endif  // CONFIG_EXT_WARPED_MOTION
+      ) {
     const MV_REFERENCE_FRAME refs[2] = { best_mbmode.ref_frame[0],
                                          best_mbmode.ref_frame[1] };
     int comp_pred_mode = refs[1] > INTRA_FRAME;
@@ -10059,7 +10131,11 @@
   }
 
   if (best_mbmode.ref_frame[0] > INTRA_FRAME &&
-      best_mbmode.ref_frame[1] <= INTRA_FRAME) {
+      best_mbmode.ref_frame[1] <= INTRA_FRAME
+#if CONFIG_EXT_WARPED_MOTION
+      && best_mbmode.motion_mode != WARPED_CAUSAL
+#endif  // CONFIG_EXT_WARPED_MOTION
+      ) {
     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)) {
@@ -10221,9 +10297,9 @@
   if (is_motion_variation_allowed_bsize(bsize) && !has_second_ref(mbmi)) {
     int pts[SAMPLES_ARRAY_SIZE], pts_inref[SAMPLES_ARRAY_SIZE];
 #if CONFIG_EXT_WARPED_MOTION
-    int pts_mv[SAMPLES_ARRAY_SIZE];
+    int pts_mv[SAMPLES_ARRAY_SIZE], pts_wm[SAMPLES_ARRAY_SIZE];
     mbmi->num_proj_ref[0] =
-        findSamples(cm, xd, mi_row, mi_col, pts, pts_inref, pts_mv);
+        findSamples(cm, xd, mi_row, mi_col, pts, pts_inref, pts_mv, pts_wm);
     // Rank the samples by motion vector difference
     if (mbmi->num_proj_ref[0] > 1)
       mbmi->num_proj_ref[0] = sortSamples(pts_mv, &mbmi->mv[0].as_mv, pts,