Add WARPED_MOTION experiment

Performance gain (REF_MV on by default)
WARPED_MOTION: 1.061%
WARPED_MOTION+MOTION_VAR: 2.917%
MOTION_VAR: 2.337%

Change-Id: I43f742a02cdd43d13ef333a0a15087062ad020ab
diff --git a/av1/common/blockd.h b/av1/common/blockd.h
index 6798b69..2a88725 100644
--- a/av1/common/blockd.h
+++ b/av1/common/blockd.h
@@ -309,6 +309,10 @@
   int mi_row;
   int mi_col;
 #endif
+#if CONFIG_WARPED_MOTION
+  int num_proj_ref[2];
+  WarpedMotionParams wm_params[2];
+#endif  // CONFIG_WARPED_MOTION
 } MB_MODE_INFO;
 
 typedef struct MODE_INFO {
@@ -894,13 +898,27 @@
   return (bsize >= BLOCK_8X8);
 }
 
-static INLINE int is_motion_variation_allowed(const MB_MODE_INFO *mbmi) {
+static INLINE MOTION_MODE motion_mode_allowed(const MB_MODE_INFO *mbmi) {
 #if CONFIG_EXT_INTER
-  return is_motion_variation_allowed_bsize(mbmi->sb_type) &&
-         mbmi->ref_frame[1] != INTRA_FRAME;
+  if (is_motion_variation_allowed_bsize(mbmi->sb_type) &&
+      is_inter_mode(mbmi->mode) && mbmi->ref_frame[1] != INTRA_FRAME) {
 #else
-  return is_motion_variation_allowed_bsize(mbmi->sb_type);
+  if (is_motion_variation_allowed_bsize(mbmi->sb_type) &&
+      is_inter_mode(mbmi->mode)) {
 #endif  // CONFIG_EXT_INTER
+#if CONFIG_WARPED_MOTION
+    if (!has_second_ref(mbmi) && mbmi->num_proj_ref[0] >= 3)
+      return WARPED_CAUSAL;
+    else
+#endif  // CONFIG_WARPED_MOTION
+#if CONFIG_MOTION_VAR
+      return OBMC_CAUSAL;
+#else
+    return SIMPLE_TRANSLATION;
+#endif  // CONFIG_MOTION_VAR
+  } else {
+    return SIMPLE_TRANSLATION;
+  }
 }
 
 #if CONFIG_MOTION_VAR
diff --git a/av1/common/entropymode.c b/av1/common/entropymode.c
index 83d0822..faa9abf 100644
--- a/av1/common/entropymode.c
+++ b/av1/common/entropymode.c
@@ -530,6 +530,7 @@
 const aom_tree_index av1_motion_mode_tree[TREE_SIZE(MOTION_MODES)] = {
   -SIMPLE_TRANSLATION, -WARPED_CAUSAL
 };
+
 static const aom_prob default_motion_mode_prob[BLOCK_SIZES]
                                               [MOTION_MODES - 1] = {
                                                 { 255 }, { 255 }, { 255 },
@@ -556,7 +557,15 @@
       { 252, 200 }, { 252, 200 }, { 252, 200 },
 #endif  // CONFIG_EXT_PARTITION
     };
-#endif  // CONFIG_MOTION_VAR || !CONFIG_WARPED_MOTION
+
+// Probability for the case that only 1 additional motion mode is allowed
+static const aom_prob default_obmc_prob[BLOCK_SIZES] = {
+  255, 255, 255, 151, 153, 144, 178, 165, 160, 207, 195, 168, 244,
+#if CONFIG_EXT_PARTITION
+  252, 252, 252,
+#endif  // CONFIG_EXT_PARTITION
+};
+#endif
 
 #if CONFIG_DELTA_Q
 static const aom_prob default_delta_q_probs[DELTA_Q_CONTEXTS] = { 220, 220,
@@ -1688,6 +1697,9 @@
   av1_copy(fc->inter_mode_probs, default_inter_mode_probs);
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   av1_copy(fc->motion_mode_prob, default_motion_mode_prob);
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  av1_copy(fc->obmc_prob, default_obmc_prob);
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 #if CONFIG_EXT_INTER
   av1_copy(fc->inter_compound_mode_probs, default_inter_compound_mode_probs);
@@ -1860,6 +1872,11 @@
   for (i = BLOCK_8X8; i < BLOCK_SIZES; ++i)
     aom_tree_merge_probs(av1_motion_mode_tree, pre_fc->motion_mode_prob[i],
                          counts->motion_mode[i], fc->motion_mode_prob[i]);
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  for (i = BLOCK_8X8; i < BLOCK_SIZES; ++i)
+    fc->obmc_prob[i] =
+        av1_mode_mv_merge_probs(pre_fc->obmc_prob[i], counts->obmc[i]);
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_SUPERTX
diff --git a/av1/common/entropymode.h b/av1/common/entropymode.h
index 3db5f67..4059cad 100644
--- a/av1/common/entropymode.h
+++ b/av1/common/entropymode.h
@@ -115,6 +115,9 @@
 #endif  // CONFIG_EXT_INTER
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   aom_prob motion_mode_prob[BLOCK_SIZES][MOTION_MODES - 1];
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  aom_prob obmc_prob[BLOCK_SIZES];
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   aom_prob intra_inter_prob[INTRA_INTER_CONTEXTS];
   aom_prob comp_inter_prob[COMP_INTER_CONTEXTS];
@@ -221,6 +224,9 @@
 #endif  // CONFIG_EXT_INTER
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   unsigned int motion_mode[BLOCK_SIZES][MOTION_MODES];
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  unsigned int obmc[BLOCK_SIZES][2];
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   unsigned int intra_inter[INTRA_INTER_CONTEXTS][2];
   unsigned int comp_inter[COMP_INTER_CONTEXTS][2];
diff --git a/av1/common/mvref_common.c b/av1/common/mvref_common.c
index 31568de..d47ce10 100644
--- a/av1/common/mvref_common.c
+++ b/av1/common/mvref_common.c
@@ -10,6 +10,9 @@
  */
 
 #include "av1/common/mvref_common.h"
+#if CONFIG_WARPED_MOTION
+#include "av1/common/warped_motion.h"
+#endif  // CONFIG_WARPED_MOTION
 
 #if CONFIG_REF_MV
 
@@ -884,3 +887,214 @@
     default: assert(0 && "Invalid block index.");
   }
 }
+
+#if CONFIG_WARPED_MOTION
+int findSamples(const AV1_COMMON *cm, MACROBLOCKD *xd, int mi_row, int mi_col,
+                double *pts, double *pts_inref) {
+  MB_MODE_INFO *const mbmi0 = &(xd->mi[0]->mbmi);
+  int ref_frame = mbmi0->ref_frame[0];
+  int up_available = xd->up_available;
+  int left_available = xd->left_available;
+  int i, mi_step, np = 0;
+  int mvasint[100];
+  int mvnumber = 0;
+  int global_offset_c = mi_col * 8;
+  int global_offset_r = mi_row * 8;
+  int samples_per_neighbor = 4;
+
+  // scan the above row
+  if (up_available) {
+    for (i = 0; i < AOMMIN(xd->n8_w, cm->mi_cols - mi_col); i += mi_step) {
+      int mi_row_offset = -1;
+      int mi_col_offset = i;
+
+      MODE_INFO *mi = xd->mi[mi_col_offset + mi_row_offset * xd->mi_stride];
+      MB_MODE_INFO *mbmi = &mi->mbmi;
+
+      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 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;
+        int cc_offset = i * 8 + AOMMAX(bw, 8) / 2 - 1;
+        int j;
+        int pixelperblock = samples_per_neighbor;
+
+        mvasint[mvnumber] = mbmi->mv[0].as_int;
+        mvnumber++;
+
+        for (j = 0; j < pixelperblock; j++) {
+          int r_offset = j / 2;
+          int c_offset = j % 2;
+
+          pts[0] = (double)(cc_offset + c_offset + global_offset_c);
+          pts[1] = (double)(cr_offset + r_offset + global_offset_r);
+
+          if (mbmi->motion_mode == WARPED_CAUSAL) {
+            int ipts[2], ipts_inref[2];
+            ipts[0] = cc_offset + c_offset + global_offset_c;
+            ipts[1] = cr_offset + r_offset + global_offset_r;
+
+            project_points(&mbmi->wm_params[0], ipts, ipts_inref, 1, 2, 2, 0,
+                           0);
+            pts_inref[0] =
+                (double)ipts_inref[0] / (double)WARPEDPIXEL_PREC_SHIFTS;
+            pts_inref[1] =
+                (double)ipts_inref[1] / (double)WARPEDPIXEL_PREC_SHIFTS;
+          } else {
+            pts_inref[0] = pts[0] + (double)(mv_col)*0.125;
+            pts_inref[1] = pts[1] + (double)(mv_row)*0.125;
+          }
+
+          pts += 2;
+          pts_inref += 2;
+        }
+        np += pixelperblock;
+      }
+    }
+  }
+
+  // scan the left column
+  if (left_available) {
+    for (i = 0; i < AOMMIN(xd->n8_h, cm->mi_rows - mi_row); i += mi_step) {
+      int mi_row_offset = i;
+      int mi_col_offset = -1;
+
+      MODE_INFO *mi = xd->mi[mi_col_offset + mi_row_offset * xd->mi_stride];
+      MB_MODE_INFO *mbmi = &mi->mbmi;
+
+      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 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;
+        int cc_offset = -AOMMAX(bw, 8) / 2 - 1;
+        int j;
+        int pixelperblock = samples_per_neighbor;
+
+        mvasint[mvnumber] = mbmi->mv[0].as_int;
+        mvnumber++;
+
+        for (j = 0; j < pixelperblock; j++) {
+          int r_offset = j / 2;
+          int c_offset = j % 2;
+
+          pts[0] = (double)(cc_offset + c_offset + global_offset_c);
+          pts[1] = (double)(cr_offset + r_offset + global_offset_r);
+
+          if (mbmi->motion_mode == WARPED_CAUSAL) {
+            int ipts[2], ipts_inref[2];
+            ipts[0] = cc_offset + c_offset + global_offset_c;
+            ipts[1] = cr_offset + r_offset + global_offset_r;
+
+            project_points(&mbmi->wm_params[0], ipts, ipts_inref, 1, 2, 2, 0,
+                           0);
+            pts_inref[0] =
+                (double)ipts_inref[0] / (double)WARPEDPIXEL_PREC_SHIFTS;
+            pts_inref[1] =
+                (double)ipts_inref[1] / (double)WARPEDPIXEL_PREC_SHIFTS;
+          } else {
+            pts_inref[0] = pts[0] + (double)(mv_col)*0.125;
+            pts_inref[1] = pts[1] + (double)(mv_row)*0.125;
+          }
+
+          pts += 2;
+          pts_inref += 2;
+        }
+        np += pixelperblock;
+      }
+    }
+  }
+
+  if (left_available && up_available) {
+    int mi_row_offset = -1;
+    int mi_col_offset = -1;
+
+    MODE_INFO *mi = xd->mi[mi_col_offset + mi_row_offset * xd->mi_stride];
+    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 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;
+      int cc_offset = -AOMMAX(bw, 8) / 2 - 1;
+      int j;
+      int pixelperblock = samples_per_neighbor;
+
+      mvasint[mvnumber] = mbmi->mv[0].as_int;
+      mvnumber++;
+
+      for (j = 0; j < pixelperblock; j++) {
+        int r_offset = j / 2;
+        int c_offset = j % 2;
+
+        pts[0] = (double)(cc_offset + c_offset + global_offset_c);
+        pts[1] = (double)(cr_offset + r_offset + global_offset_r);
+
+        if (mbmi->motion_mode == WARPED_CAUSAL) {
+          int ipts[2], ipts_inref[2];
+          ipts[0] = cc_offset + c_offset + global_offset_c;
+          ipts[1] = cr_offset + r_offset + global_offset_r;
+
+          project_points(&mbmi->wm_params[0], ipts, ipts_inref, 1, 2, 2, 0, 0);
+          pts_inref[0] =
+              (double)ipts_inref[0] / (double)WARPEDPIXEL_PREC_SHIFTS;
+          pts_inref[1] =
+              (double)ipts_inref[1] / (double)WARPEDPIXEL_PREC_SHIFTS;
+        } else {
+          pts_inref[0] = pts[0] + (double)(mv_col)*0.125;
+          pts_inref[1] = pts[1] + (double)(mv_row)*0.125;
+        }
+
+        pts += 2;
+        pts_inref += 2;
+      }
+      np += pixelperblock;
+    }
+  }
+
+  for (i = 0; i < (mvnumber - 1); ++i) {
+    if (mvasint[i] != mvasint[i + 1]) break;
+  }
+
+  if (np == 0 || i == (mvnumber - 1)) {
+    return 0;
+  } 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 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;
+    int cc_offset = AOMMAX(bw, 8) / 2 - 1;
+    int j;
+    int pixelperblock = samples_per_neighbor;
+
+    for (j = 0; j < pixelperblock; j++) {
+      int r_offset = j / 2;
+      int c_offset = j % 2;
+
+      pts[0] = (double)(cc_offset + c_offset + global_offset_c);
+      pts[1] = (double)(cr_offset + r_offset + global_offset_r);
+
+      pts_inref[0] = pts[0] + (double)(mv_col)*0.125;
+      pts_inref[1] = pts[1] + (double)(mv_row)*0.125;
+
+      pts += 2;
+      pts_inref += 2;
+    }
+    np += pixelperblock;
+  }
+
+  return np;
+}
+#endif  // CONFIG_WARPED_MOTION
diff --git a/av1/common/mvref_common.h b/av1/common/mvref_common.h
index 95fd6ca..e520336 100644
--- a/av1/common/mvref_common.h
+++ b/av1/common/mvref_common.h
@@ -488,6 +488,11 @@
                            int16_t *mode_context);
 #endif  // CONFIG_EXT_INTER
 
+#if CONFIG_WARPED_MOTION
+int findSamples(const AV1_COMMON *cm, MACROBLOCKD *xd, int mi_row, int mi_col,
+                double *pts, double *pts_inref);
+#endif  // CONFIG_WARPED_MOTION
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/av1/common/reconinter.c b/av1/common/reconinter.c
index 3642f90..21921a8 100644
--- a/av1/common/reconinter.c
+++ b/av1/common/reconinter.c
@@ -24,9 +24,9 @@
 #if CONFIG_MOTION_VAR
 #include "av1/common/onyxc_int.h"
 #endif  // CONFIG_MOTION_VAR
-#if CONFIG_GLOBAL_MOTION
+#if CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 #include "av1/common/warped_motion.h"
-#endif  // CONFIG_GLOBAL_MOTION
+#endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 
 #if CONFIG_EXT_INTER
 
@@ -1377,12 +1377,30 @@
                                    mi_x, mi_y);
           }
       } else {
-        build_inter_predictors(xd, j, mi_col_offset, mi_row_offset, 0, bw, bh,
-                               0, 0, bw, bh,
+#if CONFIG_WARPED_MOTION
+        if (above_mbmi->motion_mode == WARPED_CAUSAL) {
+          av1_warp_plane(&above_mbmi->wm_params[0],
+#if CONFIG_AOM_HIGHBITDEPTH
+                         xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+                         pd->pre[0].buf0, pd->pre[0].width, pd->pre[0].height,
+                         pd->pre[0].stride, pd->dst.buf,
+                         (((mi_col + i) * MI_SIZE) >> pd->subsampling_x),
+                         ((mi_row * MI_SIZE) >> pd->subsampling_y), bw, bh,
+                         pd->dst.stride, pd->subsampling_x, pd->subsampling_y,
+                         16, 16, 0);
+
+        } else {
+#endif  // CONFIG_WARPED_MOTION
+          build_inter_predictors(xd, j, mi_col_offset, mi_row_offset, 0, bw, bh,
+                                 0, 0, bw, bh,
 #if CONFIG_SUPERTX && CONFIG_EXT_INTER
-                               0, 0,
+                                 0, 0,
 #endif  // CONFIG_SUPERTX && CONFIG_EXT_INTER
-                               mi_x, mi_y);
+                                 mi_x, mi_y);
+#if CONFIG_WARPED_MOTION
+        }
+#endif  // CONFIG_WARPED_MOTION
       }
     }
 #if CONFIG_EXT_INTER
@@ -1480,12 +1498,30 @@
                                    mi_x, mi_y);
           }
       } else {
-        build_inter_predictors(xd, j, mi_col_offset, mi_row_offset, 0, bw, bh,
-                               0, 0, bw, bh,
+#if CONFIG_WARPED_MOTION
+        if (left_mbmi->motion_mode == WARPED_CAUSAL) {
+          av1_warp_plane(&left_mbmi->wm_params[0],
+#if CONFIG_AOM_HIGHBITDEPTH
+                         xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+                         pd->pre[0].buf0, pd->pre[0].width, pd->pre[0].height,
+                         pd->pre[0].stride, pd->dst.buf,
+                         ((mi_col * MI_SIZE) >> pd->subsampling_x),
+                         (((mi_row + i) * MI_SIZE) >> pd->subsampling_y), bw,
+                         bh, pd->dst.stride, pd->subsampling_x,
+                         pd->subsampling_y, 16, 16, 0);
+
+        } else {
+#endif  // CONFIG_WARPED_MOTION
+          build_inter_predictors(xd, j, mi_col_offset, mi_row_offset, 0, bw, bh,
+                                 0, 0, bw, bh,
 #if CONFIG_SUPERTX && CONFIG_EXT_INTER
-                               0, 0,
+                                 0, 0,
 #endif  // CONFIG_SUPERTX && CONFIG_EXT_INTER
-                               mi_x, mi_y);
+                                 mi_x, mi_y);
+#if CONFIG_WARPED_MOTION
+        }
+#endif  // CONFIG_WARPED_MOTION
       }
     }
 #if CONFIG_EXT_INTER
diff --git a/av1/common/warped_motion.c b/av1/common/warped_motion.c
index 1d3a869..d7d81db 100644
--- a/av1/common/warped_motion.c
+++ b/av1/common/warped_motion.c
@@ -210,6 +210,29 @@
   }
 }
 
+// 'points' are at original scale, output 'proj's are scaled up by
+// 1 << WARPEDPIXEL_PREC_BITS
+void project_points(WarpedMotionParams *wm_params, int *points, int *proj,
+                    const int n, const int stride_points, const int stride_proj,
+                    const int subsampling_x, const int subsampling_y) {
+  switch (wm_params->wmtype) {
+    case AFFINE:
+      project_points_affine(wm_params->wmmat, points, proj, n, stride_points,
+                            stride_proj, subsampling_x, subsampling_y);
+      break;
+    case ROTZOOM:
+      project_points_rotzoom(wm_params->wmmat, points, proj, n, stride_points,
+                             stride_proj, subsampling_x, subsampling_y);
+      break;
+    case HOMOGRAPHY:
+      project_points_homography(wm_params->wmmat, points, proj, n,
+                                stride_points, stride_proj, subsampling_x,
+                                subsampling_y);
+      break;
+    default: assert(0 && "Invalid warped motion type!"); return;
+  }
+}
+
 static const int16_t
     filter_ntap[WARPEDPIXEL_PREC_SHIFTS][WARPEDPIXEL_FILTER_TAPS] = {
       { 0, 0, 128, 0, 0, 0 },      { 0, -1, 128, 2, -1, 0 },
@@ -342,11 +365,11 @@
 
   if (ix < 0 && iy < 0)
     return ref[0];
-  else if (ix < 0 && iy > height - 1)
+  else if (ix < 0 && iy >= height - 1)
     return ref[(height - 1) * stride];
-  else if (ix > width - 1 && iy < 0)
+  else if (ix >= width - 1 && iy < 0)
     return ref[width - 1];
-  else if (ix > width - 1 && iy > height - 1)
+  else if (ix >= width - 1 && iy >= height - 1)
     return ref[(height - 1) * stride + (width - 1)];
   else if (ix < 0) {
     v = ROUND_POWER_OF_TWO_SIGNED(
@@ -359,13 +382,13 @@
         ref[ix] * (WARPEDPIXEL_PREC_SHIFTS - sx) + ref[ix + 1] * sx,
         WARPEDPIXEL_PREC_BITS);
     return clip_pixel(v);
-  } else if (ix > width - 1) {
+  } else if (ix >= width - 1) {
     v = ROUND_POWER_OF_TWO_SIGNED(
         ref[iy * stride + width - 1] * (WARPEDPIXEL_PREC_SHIFTS - sy) +
             ref[(iy + 1) * stride + width - 1] * sy,
         WARPEDPIXEL_PREC_BITS);
     return clip_pixel(v);
-  } else if (iy > height - 1) {
+  } else if (iy >= height - 1) {
     v = ROUND_POWER_OF_TWO_SIGNED(
         ref[(height - 1) * stride + ix] * (WARPEDPIXEL_PREC_SHIFTS - sx) +
             ref[(height - 1) * stride + ix + 1] * sx,
@@ -1318,3 +1341,19 @@
   }
   return 0;
 }
+
+int find_projection(const int np, double *pts1, double *pts2,
+                    WarpedMotionParams *wm_params) {
+  double H[9];
+  int result = 1;
+
+  switch (wm_params->wmtype) {
+    case AFFINE: result = find_affine(np, pts1, pts2, H); break;
+    case ROTZOOM: result = find_rotzoom(np, pts1, pts2, H); break;
+    case HOMOGRAPHY: result = find_homography(np, pts1, pts2, H); break;
+    default: assert(0 && "Invalid warped motion type!"); return 1;
+  }
+  if (result == 0) av1_integerize_model(H, wm_params->wmtype, wm_params);
+
+  return result;
+}
diff --git a/av1/common/warped_motion.h b/av1/common/warped_motion.h
index e3894d8..76ab53f 100644
--- a/av1/common/warped_motion.h
+++ b/av1/common/warped_motion.h
@@ -24,6 +24,9 @@
 #include "av1/common/mv.h"
 
 #define MAX_PARAMDIM 9
+#if CONFIG_WARPED_MOTION
+#define DEFAULT_WMTYPE AFFINE
+#endif  // CONFIG_WARPED_MOTION
 
 typedef void (*ProjectPointsFunc)(int32_t *mat, int *points, int *proj,
                                   const int n, const int stride_points,
@@ -49,6 +52,10 @@
                                const int stride_proj, const int subsampling_x,
                                const int subsampling_y);
 
+void project_points(WarpedMotionParams *wm_params, int *points, int *proj,
+                    const int n, const int stride_points, const int stride_proj,
+                    const int subsampling_x, const int subsampling_y);
+
 double av1_warp_erroradv(WarpedMotionParams *wm,
 #if CONFIG_AOM_HIGHBITDEPTH
                          int use_hbd, int bd,
@@ -75,4 +82,6 @@
 int find_rotzoom(const int np, double *pts1, double *pts2, double *mat);
 int find_affine(const int np, double *pts1, double *pts2, double *mat);
 int find_homography(const int np, double *pts1, double *pts2, double *mat);
+int find_projection(const int np, double *pts1, double *pts2,
+                    WarpedMotionParams *wm_params);
 #endif  // AV1_COMMON_WARPED_MOTION_H_
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index c155963..2b00c51 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -54,6 +54,10 @@
 #include "av1/decoder/detokenize.h"
 #include "av1/decoder/dsubexp.h"
 
+#if CONFIG_WARPED_MOTION
+#include "av1/common/warped_motion.h"
+#endif  // CONFIG_WARPED_MOTION
+
 #define MAX_AV1_HEADER_SIZE 80
 #define ACCT_STR __func__
 
@@ -1588,8 +1592,36 @@
                                               tx_size);
     }
   } else {
-    // Prediction
-    av1_build_inter_predictors_sb(xd, mi_row, mi_col, AOMMAX(bsize, BLOCK_8X8));
+// Prediction
+#if CONFIG_WARPED_MOTION
+    if (mbmi->motion_mode == WARPED_CAUSAL) {
+      int i;
+#if CONFIG_AOM_HIGHBITDEPTH
+      int use_hbd = xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH;
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+
+      for (i = 0; i < 3; ++i) {
+        const struct macroblockd_plane *pd = &xd->plane[i];
+
+        av1_warp_plane(&mbmi->wm_params[0],
+#if CONFIG_AOM_HIGHBITDEPTH
+                       xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+                       pd->pre[0].buf0, pd->pre[0].width, pd->pre[0].height,
+                       pd->pre[0].stride, pd->dst.buf,
+                       ((mi_col * MI_SIZE) >> pd->subsampling_x),
+                       ((mi_row * MI_SIZE) >> pd->subsampling_y),
+                       xd->n8_w * (8 >> pd->subsampling_x),
+                       xd->n8_h * (8 >> pd->subsampling_y), pd->dst.stride,
+                       pd->subsampling_x, pd->subsampling_y, 16, 16, 0);
+      }
+    } else {
+#endif  // CONFIG_WARPED_MOTION
+      av1_build_inter_predictors_sb(xd, mi_row, mi_col,
+                                    AOMMAX(bsize, BLOCK_8X8));
+#if CONFIG_WARPED_MOTION
+    }
+#endif  // CONFIG_WARPED_MOTION
 #if CONFIG_MOTION_VAR
     if (mbmi->motion_mode == OBMC_CAUSAL) {
       av1_build_obmc_inter_predictors_sb(cm, xd, mi_row, mi_col);
diff --git a/av1/decoder/decodemv.c b/av1/decoder/decodemv.c
index f9830d9..101ed3e 100644
--- a/av1/decoder/decodemv.c
+++ b/av1/decoder/decodemv.c
@@ -19,6 +19,9 @@
 #include "av1/common/pred_common.h"
 #include "av1/common/reconinter.h"
 #include "av1/common/seg_common.h"
+#if CONFIG_WARPED_MOTION
+#include "av1/common/warped_motion.h"
+#endif  // CONFIG_WARPED_MOTION
 
 #include "av1/decoder/decodeframe.h"
 #include "av1/decoder/decodemv.h"
@@ -252,18 +255,26 @@
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 static MOTION_MODE read_motion_mode(AV1_COMMON *cm, MACROBLOCKD *xd,
                                     MB_MODE_INFO *mbmi, aom_reader *r) {
-  if (is_motion_variation_allowed(mbmi)) {
-    int motion_mode;
-    FRAME_COUNTS *counts = xd->counts;
+  MOTION_MODE last_motion_mode_allowed = motion_mode_allowed(mbmi);
+  int motion_mode;
+  FRAME_COUNTS *counts = xd->counts;
 
+  if (last_motion_mode_allowed == SIMPLE_TRANSLATION) return SIMPLE_TRANSLATION;
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  if (last_motion_mode_allowed == OBMC_CAUSAL) {
+    motion_mode = aom_read(r, cm->fc->obmc_prob[mbmi->sb_type], ACCT_STR);
+    if (counts) ++counts->obmc[mbmi->sb_type][motion_mode];
+    return (MOTION_MODE)(SIMPLE_TRANSLATION + motion_mode);
+  } else {
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
     motion_mode =
         aom_read_tree(r, av1_motion_mode_tree,
                       cm->fc->motion_mode_prob[mbmi->sb_type], ACCT_STR);
     if (counts) ++counts->motion_mode[mbmi->sb_type][motion_mode];
     return (MOTION_MODE)(SIMPLE_TRANSLATION + motion_mode);
-  } else {
-    return SIMPLE_TRANSLATION;
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
   }
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 }
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
@@ -1423,6 +1434,9 @@
   int16_t compound_inter_mode_ctx[MODE_CTX_REF_FRAMES];
 #endif  // CONFIG_REF_MV && CONFIG_EXT_INTER
   int16_t mode_ctx = 0;
+#if CONFIG_WARPED_MOTION
+  double pts[144], pts_inref[144];
+#endif  // CONFIG_WARPED_MOTION
 
 #if CONFIG_PALETTE
   mbmi->palette_mode_info.palette_size[0] = 0;
@@ -1591,9 +1605,9 @@
   }
 #endif
 
-#if !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER
+#if !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER && !CONFIG_WARPED_MOTION
   read_mb_interp_filter(cm, xd, mbmi, r);
-#endif  // !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER
+#endif  // !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER && !CONFIG_WARPED_MOTION
 
   if (bsize < BLOCK_8X8) {
     const int num_4x4_w = 1 << xd->bmode_blocks_wl;
@@ -1777,13 +1791,28 @@
 
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   mbmi->motion_mode = SIMPLE_TRANSLATION;
+#if CONFIG_WARPED_MOTION
+  if (mbmi->sb_type >= BLOCK_8X8 && !has_second_ref(mbmi))
+    mbmi->num_proj_ref[0] = findSamples(cm, xd, mi_row, mi_col, pts, pts_inref);
+#endif  // CONFIG_WARPED_MOTION
+
 #if CONFIG_SUPERTX
-  if (!supertx_enabled)
+  if (!supertx_enabled) {
 #endif  // CONFIG_SUPERTX
 #if CONFIG_EXT_INTER
     if (mbmi->ref_frame[1] != INTRA_FRAME)
 #endif  // CONFIG_EXT_INTER
       mbmi->motion_mode = read_motion_mode(cm, xd, mbmi, r);
+#if CONFIG_WARPED_MOTION
+    if (mbmi->motion_mode == WARPED_CAUSAL) {
+      mbmi->wm_params[0].wmtype = DEFAULT_WMTYPE;
+      find_projection(mbmi->num_proj_ref[0], pts, pts_inref,
+                      &mbmi->wm_params[0]);
+    }
+#endif  // CONFIG_WARPED_MOTION
+#if CONFIG_SUPERTX
+  }
+#endif  // CONFIG_SUPERTX
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_EXT_INTER
@@ -1791,8 +1820,7 @@
   if (cm->reference_mode != SINGLE_REFERENCE &&
       is_inter_compound_mode(mbmi->mode) &&
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
-      !(is_motion_variation_allowed(mbmi) &&
-        mbmi->motion_mode != SIMPLE_TRANSLATION) &&
+      mbmi->motion_mode == SIMPLE_TRANSLATION &&
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
       is_interinter_wedge_used(bsize)) {
     mbmi->interinter_compound = aom_read_tree(
@@ -1807,9 +1835,25 @@
   }
 #endif  // CONFIG_EXT_INTER
 
-#if CONFIG_DUAL_FILTER || CONFIG_EXT_INTERP
-  read_mb_interp_filter(cm, xd, mbmi, r);
-#endif  // CONFIG_DUAL_FILTER || CONFIG_EXT_INTERP
+#if CONFIG_WARPED_MOTION
+  if (mbmi->motion_mode != WARPED_CAUSAL) {
+#endif  // CONFIG_WARPED_MOTION
+#if CONFIG_DUAL_FILTER || CONFIG_EXT_INTERP || CONFIG_WARPED_MOTION
+    read_mb_interp_filter(cm, xd, mbmi, r);
+#endif  // CONFIG_DUAL_FILTER || CONFIG_EXT_INTERP || CONFIG_WARPED_MOTION
+#if CONFIG_WARPED_MOTION
+  } else {
+#if CONFIG_DUAL_FILTER
+    mbmi->interp_filter[0] =
+        cm->interp_filter == SWITCHABLE ? EIGHTTAP_REGULAR : cm->interp_filter;
+    mbmi->interp_filter[1] =
+        cm->interp_filter == SWITCHABLE ? EIGHTTAP_REGULAR : cm->interp_filter;
+#else
+    mbmi->interp_filter =
+        cm->interp_filter == SWITCHABLE ? EIGHTTAP_REGULAR : cm->interp_filter;
+#endif  // CONFIG_DUAL_FILTER
+  }
+#endif  // CONFIG_WARPED_MOTION
 }
 
 static void read_inter_frame_mode_info(AV1Decoder *const pbi,
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 11d11e0..e4d8db0 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -506,6 +506,27 @@
   }
 }
 
+#if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
+static void write_motion_mode(const AV1_COMMON *cm, const MB_MODE_INFO *mbmi,
+                              aom_writer *w) {
+  MOTION_MODE last_motion_mode_allowed = motion_mode_allowed(mbmi);
+
+  if (last_motion_mode_allowed == SIMPLE_TRANSLATION) return;
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  if (last_motion_mode_allowed == OBMC_CAUSAL) {
+    aom_write(w, mbmi->motion_mode == OBMC_CAUSAL,
+              cm->fc->obmc_prob[mbmi->sb_type]);
+  } else {
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+    av1_write_token(w, av1_motion_mode_tree,
+                    cm->fc->motion_mode_prob[mbmi->sb_type],
+                    &motion_mode_encodings[mbmi->motion_mode]);
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  }
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+}
+#endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
+
 #if CONFIG_DELTA_Q
 static void write_delta_qindex(const AV1_COMMON *cm, int delta_qindex,
                                aom_writer *w) {
@@ -1383,9 +1404,9 @@
       }
     }
 
-#if !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER
+#if !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER && !CONFIG_WARPED_MOTION
     write_mb_interp_filter(cpi, xd, w);
-#endif  // !CONFIG_EXT_INTERP
+#endif  // !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER && !CONFIG_WARPED_MOTION
 
     if (bsize < BLOCK_8X8) {
       const int num_4x4_w = num_4x4_blocks_wide_lookup[bsize];
@@ -1573,23 +1594,14 @@
 #if CONFIG_EXT_INTER
       if (mbmi->ref_frame[1] != INTRA_FRAME)
 #endif  // CONFIG_EXT_INTER
-        if (is_motion_variation_allowed(mbmi)) {
-          // TODO(debargha): Might want to only emit this if SEG_LVL_SKIP
-          // is not active, and assume SIMPLE_TRANSLATION in the decoder if
-          // it is active.
-          assert(mbmi->motion_mode < MOTION_MODES);
-          av1_write_token(w, av1_motion_mode_tree,
-                          cm->fc->motion_mode_prob[bsize],
-                          &motion_mode_encodings[mbmi->motion_mode]);
-        }
+        write_motion_mode(cm, mbmi, w);
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_EXT_INTER
     if (cpi->common.reference_mode != SINGLE_REFERENCE &&
         is_inter_compound_mode(mbmi->mode) &&
 #if CONFIG_MOTION_VAR
-        !(is_motion_variation_allowed(mbmi) &&
-          mbmi->motion_mode != SIMPLE_TRANSLATION) &&
+        mbmi->motion_mode == SIMPLE_TRANSLATION &&
 #endif  // CONFIG_MOTION_VAR
         is_interinter_wedge_used(bsize)) {
       av1_write_token(w, av1_compound_type_tree,
@@ -1603,9 +1615,12 @@
     }
 #endif  // CONFIG_EXT_INTER
 
-#if CONFIG_EXT_INTERP || CONFIG_DUAL_FILTER
-    write_mb_interp_filter(cpi, xd, w);
-#endif  // CONFIG_EXT_INTERP
+#if CONFIG_WARPED_MOTION
+    if (mbmi->motion_mode != WARPED_CAUSAL)
+#endif  // CONFIG_WARPED_MOTION
+#if CONFIG_EXT_INTERP || CONFIG_DUAL_FILTER || CONFIG_WARPED_MOTION
+      write_mb_interp_filter(cpi, xd, w);
+#endif  // CONFIG_EXT_INTERP || CONFIG_DUAL_FILTE || CONFIG_WARPED_MOTION
   }
 
   write_tx_type(cm, mbmi,
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index d828275..8540cc4 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -40,10 +40,12 @@
 #if CONFIG_SUPERTX
 #include "av1/encoder/cost.h"
 #endif
-#if CONFIG_GLOBAL_MOTION
+#if CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 #include "av1/common/warped_motion.h"
+#endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
+#if CONFIG_GLOBAL_MOTION
 #include "av1/encoder/global_motion.h"
-#endif
+#endif  // CONFIG_GLOBAL_MOTION
 #include "av1/encoder/encodeframe.h"
 #include "av1/encoder/encodemb.h"
 #include "av1/encoder/encodemv.h"
@@ -1182,7 +1184,10 @@
 #if CONFIG_EXT_INTERP
           && av1_is_interp_needed(xd)
 #endif
-              ) {
+#if CONFIG_WARPED_MOTION
+          && mbmi->motion_mode != WARPED_CAUSAL
+#endif  // CONFIG_WARPED_MOTION
+          ) {
 #if CONFIG_DUAL_FILTER
         update_filter_type_count(td->counts, xd, mbmi);
 #else
@@ -1972,16 +1977,24 @@
 #if CONFIG_EXT_INTER
           if (mbmi->ref_frame[1] != INTRA_FRAME)
 #endif  // CONFIG_EXT_INTER
-            if (is_motion_variation_allowed(mbmi))
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+          {
+            if (motion_mode_allowed(mbmi) == WARPED_CAUSAL)
               counts->motion_mode[mbmi->sb_type][mbmi->motion_mode]++;
+            else if (motion_mode_allowed(mbmi) == OBMC_CAUSAL)
+              counts->obmc[mbmi->sb_type][mbmi->motion_mode == OBMC_CAUSAL]++;
+          }
+#else
+        if (motion_mode_allowed(mbmi) > SIMPLE_TRANSLATION)
+          counts->motion_mode[mbmi->sb_type][mbmi->motion_mode]++;
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_EXT_INTER
         if (cm->reference_mode != SINGLE_REFERENCE &&
             is_inter_compound_mode(mbmi->mode) &&
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
-            !(is_motion_variation_allowed(mbmi) &&
-              mbmi->motion_mode != SIMPLE_TRANSLATION) &&
+            mbmi->motion_mode == SIMPLE_TRANSLATION &&
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
             is_interinter_wedge_used(bsize)) {
           counts->compound_interinter[bsize][mbmi->interinter_compound]++;
@@ -5488,12 +5501,39 @@
       av1_setup_pre_planes(xd, ref, cfg, mi_row, mi_col,
                            &xd->block_refs[ref]->sf);
     }
-    if (!(cpi->sf.reuse_inter_pred_sby && ctx->pred_pixel_ready) || seg_skip)
-      av1_build_inter_predictors_sby(xd, mi_row, mi_col,
-                                     AOMMAX(bsize, BLOCK_8X8));
+#if CONFIG_WARPED_MOTION
+    if (mbmi->motion_mode == WARPED_CAUSAL) {
+      int i;
+#if CONFIG_AOM_HIGHBITDEPTH
+      int use_hbd = xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH;
+#endif  // CONFIG_AOM_HIGHBITDEPTH
 
-    av1_build_inter_predictors_sbuv(xd, mi_row, mi_col,
-                                    AOMMAX(bsize, BLOCK_8X8));
+      for (i = 0; i < 3; ++i) {
+        const struct macroblockd_plane *pd = &xd->plane[i];
+
+        av1_warp_plane(&mbmi->wm_params[0],
+#if CONFIG_AOM_HIGHBITDEPTH
+                       xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+                       pd->pre[0].buf0, pd->pre[0].width, pd->pre[0].height,
+                       pd->pre[0].stride, pd->dst.buf,
+                       ((mi_col * MI_SIZE) >> pd->subsampling_x),
+                       ((mi_row * MI_SIZE) >> pd->subsampling_y),
+                       xd->n8_w * (8 >> pd->subsampling_x),
+                       xd->n8_h * (8 >> pd->subsampling_y), pd->dst.stride,
+                       pd->subsampling_x, pd->subsampling_y, 16, 16, 0);
+      }
+    } else {
+#endif  // CONFIG_WARPED_MOTION
+      if (!(cpi->sf.reuse_inter_pred_sby && ctx->pred_pixel_ready) || seg_skip)
+        av1_build_inter_predictors_sby(xd, mi_row, mi_col,
+                                       AOMMAX(bsize, BLOCK_8X8));
+
+      av1_build_inter_predictors_sbuv(xd, mi_row, mi_col,
+                                      AOMMAX(bsize, BLOCK_8X8));
+#if CONFIG_WARPED_MOTION
+    }
+#endif  // CONFIG_WARPED_MOTION
 
 #if CONFIG_MOTION_VAR
     if (mbmi->motion_mode == OBMC_CAUSAL) {
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 28a44c3..be64d79 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -535,6 +535,9 @@
 #endif  // CONFIG_EXT_INTER
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   int motion_mode_cost[BLOCK_SIZES][MOTION_MODES];
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+  int motion_mode_cost1[BLOCK_SIZES][2];
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   int intra_uv_mode_cost[INTRA_MODES][INTRA_MODES];
   int y_mode_costs[INTRA_MODES][INTRA_MODES][INTRA_MODES];
diff --git a/av1/encoder/ransac.c b/av1/encoder/ransac.c
index c47e1ae..9c19903 100644
--- a/av1/encoder/ransac.c
+++ b/av1/encoder/ransac.c
@@ -18,7 +18,6 @@
 #include "av1/encoder/ransac.h"
 
 #define MAX_MINPTS 4
-
 #define MAX_DEGENERATE_ITER 10
 #define MINPTS_MULTIPLIER 5
 
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index e1e1595..9b327b2 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -443,6 +443,12 @@
         av1_cost_tokens((int *)cpi->motion_mode_cost[i],
                         cm->fc->motion_mode_prob[i], av1_motion_mode_tree);
       }
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+      for (i = BLOCK_8X8; i < BLOCK_SIZES; i++) {
+        cpi->motion_mode_cost1[i][0] = av1_cost_bit(cm->fc->obmc_prob[i], 0);
+        cpi->motion_mode_cost1[i][1] = av1_cost_bit(cm->fc->obmc_prob[i], 1);
+      }
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
     }
   }
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 23e1729..27db8f6 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -33,6 +33,9 @@
 #include "av1/common/reconintra.h"
 #include "av1/common/scan.h"
 #include "av1/common/seg_common.h"
+#if CONFIG_WARPED_MOTION
+#include "av1/common/warped_motion.h"
+#endif  // CONFIG_WARPED_MOTION
 
 #include "av1/encoder/aq_variance.h"
 #include "av1/encoder/cost.h"
@@ -6907,12 +6910,7 @@
   uint8_t *tmp_buf;
 
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
-  MOTION_MODE motion_mode;
-  int allow_motvar =
-#if CONFIG_EXT_INTER
-      !is_comp_interintra_pred &&
-#endif  // CONFIG_EXT_INTER
-      is_motion_variation_allowed(mbmi);
+  MOTION_MODE motion_mode, last_motion_mode_allowed;
   int rate2_nocoeff = 0, best_xskip, best_disable_skip = 0;
   RD_STATS best_rd_stats, best_rd_stats_y, best_rd_stats_uv;
 #if CONFIG_VAR_TX
@@ -6922,10 +6920,15 @@
   MB_MODE_INFO base_mbmi, best_mbmi;
 #if CONFIG_EXT_INTER
   int rate2_bmc_nocoeff;
-  int rate_mv_bmc;
   MB_MODE_INFO best_bmc_mbmi;
+#if CONFIG_MOTION_VAR
+  int rate_mv_bmc;
+#endif  // CONFIG_MOTION_VAR
 #endif  // CONFIG_EXT_INTER
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
+#if CONFIG_WARPED_MOTION
+  double pts[144], pts_inref[144];
+#endif  // CONFIG_WARPED_MOTION
   int64_t rd = INT64_MAX;
   uint8_t *orig_dst[MAX_MB_PLANE];
   int orig_dst_stride[MAX_MB_PLANE];
@@ -6975,6 +6978,11 @@
   // to tmp_buf at the end of the last iteration
   assert(xd->plane[0].dst.buf != tmp_buf);
 
+#if CONFIG_WARPED_MOTION
+  mbmi->num_proj_ref[0] = 0;
+  mbmi->num_proj_ref[1] = 0;
+#endif  // CONFIG_WARPED_MOTION
+
   if (is_comp_pred) {
     if (frame_mv[refs[0]].as_int == INVALID_MV ||
         frame_mv[refs[1]].as_int == INVALID_MV)
@@ -7340,12 +7348,14 @@
   }
 
 #if CONFIG_EXT_INTER
-#if CONFIG_MOTION_VAR
+#if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   best_bmc_mbmi = *mbmi;
-  rate_mv_bmc = rate_mv;
   rate2_bmc_nocoeff = rd_stats->rate;
   if (cm->interp_filter == SWITCHABLE) rate2_bmc_nocoeff += rs;
+#if CONFIG_MOTION_VAR
+  rate_mv_bmc = rate_mv;
 #endif  // CONFIG_MOTION_VAR
+#endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
   if (is_comp_pred && is_interinter_wedge_used(bsize)) {
     int rate_sum, rs2;
@@ -7684,17 +7694,23 @@
   }
 
   if (cm->interp_filter == SWITCHABLE) rd_stats->rate += rs;
-#if CONFIG_MOTION_VAR
+#if CONFIG_WARPED_MOTION
+  aom_clear_system_state();
+  mbmi->num_proj_ref[0] = findSamples(cm, xd, mi_row, mi_col, pts, pts_inref);
+#endif  // CONFIG_WARPED_MOTION
+#if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   rate2_nocoeff = rd_stats->rate;
+  last_motion_mode_allowed = motion_mode_allowed(mbmi);
   base_mbmi = *mbmi;
-#endif  // CONFIG_MOTION_VAR
+#endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   best_rd = INT64_MAX;
   for (motion_mode = SIMPLE_TRANSLATION;
-       motion_mode < (allow_motvar ? MOTION_MODES : 1); motion_mode++) {
-    int64_t tmp_rd = INT64_MAX, tmp_dist;
+       motion_mode <= last_motion_mode_allowed; motion_mode++) {
+    int64_t tmp_rd = INT64_MAX;
     int tmp_rate;
+    int64_t tmp_dist;
 #if CONFIG_EXT_INTER
     int tmp_rate2 =
         motion_mode != SIMPLE_TRANSLATION ? rate2_bmc_nocoeff : rate2_nocoeff;
@@ -7757,7 +7773,51 @@
 
 #if CONFIG_WARPED_MOTION
     if (mbmi->motion_mode == WARPED_CAUSAL) {
-      // TODO(yuec): Add code
+#if CONFIG_EXT_INTER
+      *mbmi = best_bmc_mbmi;
+      mbmi->motion_mode = WARPED_CAUSAL;
+#endif  // CONFIG_EXT_INTER
+      mbmi->wm_params[0].wmtype = DEFAULT_WMTYPE;
+#if CONFIG_DUAL_FILTER
+      mbmi->interp_filter[0] = cm->interp_filter == SWITCHABLE
+                                   ? EIGHTTAP_REGULAR
+                                   : cm->interp_filter;
+      mbmi->interp_filter[1] = cm->interp_filter == SWITCHABLE
+                                   ? EIGHTTAP_REGULAR
+                                   : cm->interp_filter;
+#else
+      mbmi->interp_filter = cm->interp_filter == SWITCHABLE ? EIGHTTAP_REGULAR
+                                                            : cm->interp_filter;
+#endif  // CONFIG_DUAL_FILTER
+
+      if (find_projection(mbmi->num_proj_ref[0], pts, pts_inref,
+                          &mbmi->wm_params[0]) == 0) {
+        int plane;
+#if CONFIG_AOM_HIGHBITDEPTH
+        int use_hbd = xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH;
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+
+        for (plane = 0; plane < 3; ++plane) {
+          const struct macroblockd_plane *pd = &xd->plane[plane];
+
+          av1_warp_plane(&mbmi->wm_params[0],
+#if CONFIG_AOM_HIGHBITDEPTH
+                         xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+                         pd->pre[0].buf0, pd->pre[0].width, pd->pre[0].height,
+                         pd->pre[0].stride, pd->dst.buf,
+                         (mi_col * MI_SIZE) >> pd->subsampling_x,
+                         (mi_row * MI_SIZE) >> pd->subsampling_y,
+                         (xd->n8_w * 8) >> pd->subsampling_x,
+                         (xd->n8_h * 8) >> pd->subsampling_y, pd->dst.stride,
+                         pd->subsampling_x, pd->subsampling_y, 16, 16, 0);
+        }
+
+        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;
+      }
     }
 #endif  // CONFIG_WARPED_MOTION
     x->skip = 0;
@@ -7766,8 +7826,21 @@
     rd_stats->sse = 0;
     rd_stats->skip = 1;
     rd_stats->rate = tmp_rate2;
-    if (allow_motvar)
-      rd_stats->rate += cpi->motion_mode_cost[bsize][mbmi->motion_mode];
+    if (last_motion_mode_allowed > SIMPLE_TRANSLATION) {
+#if CONFIG_WARPED_MOTION && CONFIG_MOTION_VAR
+      if (last_motion_mode_allowed == WARPED_CAUSAL)
+#endif  // CONFIG_WARPED_MOTION && CONFIG_MOTION_VAR
+        rd_stats->rate += cpi->motion_mode_cost[bsize][mbmi->motion_mode];
+#if CONFIG_WARPED_MOTION && CONFIG_MOTION_VAR
+      else
+        rd_stats->rate += cpi->motion_mode_cost1[bsize][mbmi->motion_mode];
+#endif  // CONFIG_WARPED_MOTION && CONFIG_MOTION_VAR
+    }
+#if CONFIG_WARPED_MOTION
+    if (mbmi->motion_mode == WARPED_CAUSAL) {
+      rd_stats->rate -= rs;
+    }
+#endif  // CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
     if (!skip_txfm_sb) {
       int64_t rdcosty = INT64_MAX;
@@ -9259,7 +9332,7 @@
           }
 
           if (tmp_alt_rd < INT64_MAX) {
-#if CONFIG_MOTION_VAR
+#if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
             tmp_alt_rd = RDCOST(x->rdmult, x->rddiv, tmp_rd_stats.rate,
                                 tmp_rd_stats.dist);
 #else
@@ -9279,7 +9352,7 @@
                              av1_cost_bit(av1_get_skip_prob(cm, xd), 1) -
                              tmp_rd_stats_y.rate - tmp_rd_stats_uv.rate,
                          tmp_rd_stats.sse);
-#endif  // CONFIG_MOTION_VAR
+#endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
           }
 
           if (tmp_ref_rd > tmp_alt_rd) {
@@ -9345,11 +9418,11 @@
       rate2 += ref_costs_single[ref_frame];
     }
 
-#if CONFIG_MOTION_VAR
+#if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
     if (ref_frame == INTRA_FRAME) {
 #else
     if (!disable_skip) {
-#endif  // CONFIG_MOTION_VAR
+#endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
       if (skippable) {
         // Back out the coefficient coding costs
         rate2 -= (rate_y + rate_uv);
@@ -9385,7 +9458,7 @@
 
       // Calculate the final RD estimate for this mode.
       this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
-#if CONFIG_MOTION_VAR
+#if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
     } else {
       this_skip2 = mbmi->skip;
       this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
@@ -9393,7 +9466,7 @@
         rate_y = 0;
         rate_uv = 0;
       }
-#endif  // CONFIG_MOTION_VAR
+#endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
     }
 
     if (ref_frame == INTRA_FRAME) {
@@ -9440,8 +9513,15 @@
         *returnrate_nocoef -= av1_cost_bit(av1_get_intra_inter_prob(cm, xd),
                                            mbmi->ref_frame[0] != INTRA_FRAME);
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
-        if (is_inter_block(mbmi) && is_motion_variation_allowed(mbmi))
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+        if (motion_mode_allowed(mbmi) == WARPED_CAUSAL)
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
           *returnrate_nocoef -= cpi->motion_mode_cost[bsize][mbmi->motion_mode];
+#if CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
+        else if (motion_mode_allowed(mbmi) == OBMC_CAUSAL)
+          *returnrate_nocoef -=
+              cpi->motion_mode_cost1[bsize][mbmi->motion_mode];
+#endif  // CONFIG_MOTION_VAR && CONFIG_WARPED_MOTION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 #endif  // CONFIG_SUPERTX
         rd_cost->dist = distortion2;
@@ -9514,7 +9594,35 @@
     }
 
     if (is_inter_mode(mbmi->mode)) {
-      av1_build_inter_predictors_sb(xd, mi_row, mi_col, bsize);
+#if CONFIG_WARPED_MOTION
+      if (mbmi->motion_mode == WARPED_CAUSAL) {
+        int plane;
+#if CONFIG_AOM_HIGHBITDEPTH
+        int use_hbd = xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH;
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+        assert(!has_second_ref(mbmi));
+
+        for (plane = 0; plane < 3; ++plane) {
+          const struct macroblockd_plane *pd = &xd->plane[plane];
+
+          av1_warp_plane(&mbmi->wm_params[0],
+#if CONFIG_AOM_HIGHBITDEPTH
+                         xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
+#endif  // CONFIG_AOM_HIGHBITDEPTH
+                         pd->pre[0].buf0, pd->pre[0].width, pd->pre[0].height,
+                         pd->pre[0].stride, pd->dst.buf,
+                         ((mi_col * MI_SIZE) >> pd->subsampling_x),
+                         ((mi_row * MI_SIZE) >> pd->subsampling_y),
+                         xd->n8_w * (8 >> pd->subsampling_x),
+                         xd->n8_h * (8 >> pd->subsampling_y), pd->dst.stride,
+                         pd->subsampling_x, pd->subsampling_y, 16, 16, 0);
+        }
+      } else {
+#endif  // CONFIG_WARPED_MOTION
+        av1_build_inter_predictors_sb(xd, mi_row, mi_col, bsize);
+#if CONFIG_WARPED_MOTION
+      }
+#endif  // CONFIG_WARPED_MOTION
 #if CONFIG_MOTION_VAR
       if (mbmi->motion_mode == OBMC_CAUSAL)
         av1_build_obmc_inter_prediction(cm, xd, mi_row, mi_col, dst_buf1,
@@ -10181,6 +10289,10 @@
   mbmi->interinter_compound = COMPOUND_AVERAGE;
   mbmi->use_wedge_interintra = 0;
 #endif  // CONFIG_EXT_INTER
+#if CONFIG_WARPED_MOTION
+  mbmi->num_proj_ref[0] = 0;
+  mbmi->num_proj_ref[1] = 0;
+#endif  // CONFIG_WARPED_MOTION
 
   for (i = 0; i < 4; i++) {
     int j;