Add encoder/decoder pipeline to support single ref comp modes

Now the single ref comp mode should work with WEDGE and
COMPOUND_SEGMENT. For motion_var, the OBMC_CAUSAL mode uses the 2nd
predictor if the neighboring block is single ref comp mode predicted.

This patch removes the mode of SR_NEAREST_NEWMV and leaves four
single ref comp modes in total:

SR_NEAREST_NEARMV
SR_NEAR_NEWMV
SR_ZERO_NEWMV
SR_NEW_NEWMV

Change-Id: If6140455771f0f1a3b947766eccf82f23cc6b67a
diff --git a/av1/common/av1_loopfilter.c b/av1/common/av1_loopfilter.c
index 4b27ae9..e08aef9 100644
--- a/av1/common/av1_loopfilter.c
+++ b/av1/common/av1_loopfilter.c
@@ -273,6 +273,11 @@
 #endif         // CONFIG_ALT_INTRA
   1, 1, 0, 1,  // INTER_MODES (ZEROMV == 0)
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  // 1, 1, 1, 1, 1,       // INTER_SINGLEREF_COMP_MODES
+  // NOTE(zoeliu): Remove SR_NEAREST_NEWMV
+  1, 1, 1, 1,             // INTER_SINGLEREF_COMP_MODES
+#endif                    // CONFIG_COMPOUND_SINGLEREF
   1, 1, 1, 1, 1, 1, 0, 1  // INTER_COMPOUND_MODES (ZERO_ZEROMV == 0)
 #endif                    // CONFIG_EXT_INTER
 };
diff --git a/av1/common/blockd.h b/av1/common/blockd.h
index e134548..a7a6332 100644
--- a/av1/common/blockd.h
+++ b/av1/common/blockd.h
@@ -119,14 +119,17 @@
 static INLINE int is_inter_singleref_mode(PREDICTION_MODE mode) {
   return mode >= NEARESTMV && mode <= NEWMV;
 }
+static INLINE int is_inter_compound_mode(PREDICTION_MODE mode) {
+  return mode >= NEAREST_NEARESTMV && mode <= NEW_NEWMV;
+}
 #if CONFIG_COMPOUND_SINGLEREF
 static INLINE int is_inter_singleref_comp_mode(PREDICTION_MODE mode) {
   return mode >= SR_NEAREST_NEARMV && mode <= SR_NEW_NEWMV;
 }
-#endif  // CONFIG_COMPOUND_SINGLEREF
-static INLINE int is_inter_compound_mode(PREDICTION_MODE mode) {
-  return mode >= NEAREST_NEARESTMV && mode <= NEW_NEWMV;
+static INLINE int is_inter_anyref_comp_mode(PREDICTION_MODE mode) {
+  return is_inter_compound_mode(mode) || is_inter_singleref_comp_mode(mode);
 }
+#endif  // CONFIG_COMPOUND_SINGLEREF
 
 static INLINE PREDICTION_MODE compound_ref0_mode(PREDICTION_MODE mode) {
   static PREDICTION_MODE lut[] = {
@@ -153,7 +156,7 @@
     MB_MODE_COUNT,  // NEWMV
 #if CONFIG_COMPOUND_SINGLEREF
     NEARESTMV,  // SR_NEAREST_NEARMV
-    NEARESTMV,  // SR_NEAREST_NEWMV
+    // NEARESTMV,  // SR_NEAREST_NEWMV
     NEARMV,     // SR_NEAR_NEWMV
     ZEROMV,     // SR_ZERO_NEWMV
     NEWMV,      // SR_NEW_NEWMV
@@ -168,7 +171,11 @@
     NEWMV,      // NEW_NEWMV
   };
   assert(NELEMENTS(lut) == MB_MODE_COUNT);
+#if CONFIG_COMPOUND_SINGLEREF
+  assert(is_inter_anyref_comp_mode(mode));
+#else   // !CONFIG_COMPOUND_SINGLEREF
   assert(is_inter_compound_mode(mode));
+#endif  // CONFIG_COMPOUND_SINGLEREF
   return lut[mode];
 }
 
@@ -196,8 +203,8 @@
     MB_MODE_COUNT,  // ZEROMV
     MB_MODE_COUNT,  // NEWMV
 #if CONFIG_COMPOUND_SINGLEREF
-    NEARMV,     // SR_NEAREST_NEARMV
-    NEWMV,      // SR_NEAREST_NEWMV
+    NEARMV,  // SR_NEAREST_NEARMV
+    // NEWMV,      // SR_NEAREST_NEWMV
     NEWMV,      // SR_NEAR_NEWMV
     NEWMV,      // SR_ZERO_NEWMV
     NEWMV,      // SR_NEW_NEWMV
@@ -212,17 +219,28 @@
     NEWMV,      // NEW_NEWMV
   };
   assert(NELEMENTS(lut) == MB_MODE_COUNT);
+#if CONFIG_COMPOUND_SINGLEREF
+  assert(is_inter_anyref_comp_mode(mode));
+#else   // !CONFIG_COMPOUND_SINGLEREF
   assert(is_inter_compound_mode(mode));
+#endif  // CONFIG_COMPOUND_SINGLEREF
   return lut[mode];
 }
 
 static INLINE int have_nearmv_in_inter_mode(PREDICTION_MODE mode) {
   return (mode == NEARMV || mode == NEAR_NEARMV || mode == NEAR_NEWMV ||
+#if CONFIG_COMPOUND_SINGLEREF
+          mode == SR_NEAREST_NEARMV || mode == SR_NEAR_NEWMV ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
           mode == NEW_NEARMV);
 }
 
 static INLINE int have_newmv_in_inter_mode(PREDICTION_MODE mode) {
   return (mode == NEWMV || mode == NEW_NEWMV || mode == NEAREST_NEWMV ||
+#if CONFIG_COMPOUND_SINGLEREF
+          /* mode == SR_NEAREST_NEWMV || */ mode == SR_NEAR_NEWMV ||
+          mode == SR_ZERO_NEWMV || mode == SR_NEW_NEWMV ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
           mode == NEW_NEARESTMV || mode == NEAR_NEWMV || mode == NEW_NEARMV);
 }
 
@@ -246,7 +264,8 @@
   (void)type;
   return 0;
 }
-#else
+
+#else   // !CONFIG_EXT_INTER
 
 static INLINE int have_nearmv_in_inter_mode(PREDICTION_MODE mode) {
   return (mode == NEARMV);
@@ -1256,7 +1275,11 @@
 
 static INLINE int is_motion_variation_allowed_compound(
     const MB_MODE_INFO *mbmi) {
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi) && !is_inter_singleref_comp_mode(mbmi->mode))
+#else
   if (!has_second_ref(mbmi))
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     return 1;
   else
     return 0;
diff --git a/av1/common/entropymode.c b/av1/common/entropymode.c
index 3d8bf11..34842dc 100644
--- a/av1/common/entropymode.c
+++ b/av1/common/entropymode.c
@@ -1027,6 +1027,7 @@
 #if CONFIG_COMPOUND_SINGLEREF
 // TODO(zoeliu): Default values to be further adjusted based on the collected
 //               stats.
+/*
 static const aom_prob default_inter_singleref_comp_mode_probs
     [INTER_MODE_CONTEXTS][INTER_SINGLEREF_COMP_MODES - 1] = {
       { 2, 173, 68, 180 },   // 0 = both zero mv
@@ -1036,6 +1037,16 @@
       { 8, 64, 64, 180 },    // 4 = two new mvs
       { 17, 81, 52, 180 },   // 5 = one intra neighbour
       { 25, 29, 50, 180 },   // 6 = two intra neighbours
+    };*/
+static const aom_prob default_inter_singleref_comp_mode_probs
+    [INTER_MODE_CONTEXTS][INTER_SINGLEREF_COMP_MODES - 1] = {
+      { 2, 173, 68 },   // 0 = both zero mv
+      { 7, 145, 160 },  // 1 = 1 zero + 1 predicted
+      { 7, 166, 126 },  // 2 = two predicted mvs
+      { 7, 94, 132 },   // 3 = 1 pred/zero, 1 new
+      { 8, 64, 64 },    // 4 = two new mvs
+      { 17, 81, 52 },   // 5 = one intra neighbour
+      { 25, 29, 50 },   // 6 = two intra neighbours
     };
 #endif  // CONFIG_COMPOUND_SINGLEREF
 
@@ -1280,6 +1291,8 @@
 };
 
 #if CONFIG_COMPOUND_SINGLEREF
+// TODO(zoeliu): To redesign the tree structure once the number of mode changes.
+/*
 const aom_tree_index av1_inter_singleref_comp_mode_tree
     [TREE_SIZE(INTER_SINGLEREF_COMP_MODES)] = {
   -INTER_SINGLEREF_COMP_OFFSET(SR_ZERO_NEWMV), 2,
@@ -1287,6 +1300,14 @@
   6, -INTER_SINGLEREF_COMP_OFFSET(SR_NEW_NEWMV),
   -INTER_SINGLEREF_COMP_OFFSET(SR_NEAREST_NEWMV),
   -INTER_SINGLEREF_COMP_OFFSET(SR_NEAR_NEWMV)
+};*/
+
+const aom_tree_index av1_inter_singleref_comp_mode_tree
+    [TREE_SIZE(INTER_SINGLEREF_COMP_MODES)] = {
+  -INTER_SINGLEREF_COMP_OFFSET(SR_ZERO_NEWMV), 2,
+  -INTER_SINGLEREF_COMP_OFFSET(SR_NEAREST_NEARMV), 4,
+  -INTER_SINGLEREF_COMP_OFFSET(SR_NEAR_NEWMV),
+      -INTER_SINGLEREF_COMP_OFFSET(SR_NEW_NEWMV)
 };
 #endif  // CONFIG_COMPOUND_SINGLEREF
 
@@ -1368,7 +1389,7 @@
 // TODO(zoeliu): Default values to be further adjusted based on the collected
 //               stats.
 static const aom_prob default_comp_inter_mode_p[COMP_INTER_MODE_CONTEXTS] = {
-  41, 119, 187, 225
+  40, 110, 160, 220
 };
 #endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 
diff --git a/av1/common/enums.h b/av1/common/enums.h
index 3f905de..d105f94 100644
--- a/av1/common/enums.h
+++ b/av1/common/enums.h
@@ -346,7 +346,7 @@
 #if CONFIG_COMPOUND_SINGLEREF
   // Single ref compound modes
   SR_NEAREST_NEARMV,
-  SR_NEAREST_NEWMV,
+  // SR_NEAREST_NEWMV,
   SR_NEAR_NEWMV,
   SR_ZERO_NEWMV,
   SR_NEW_NEWMV,
diff --git a/av1/common/mvref_common.h b/av1/common/mvref_common.h
index ec5edee..209a636 100644
--- a/av1/common/mvref_common.h
+++ b/av1/common/mvref_common.h
@@ -66,7 +66,7 @@
 #if CONFIG_EXT_INTER
 #if CONFIG_COMPOUND_SINGLEREF
   0,    // SR_NEAREST_NEARMV
-  1,    // SR_NEAREST_NEWMV
+        //  1,    // SR_NEAREST_NEWMV
   1,    // SR_NEAR_NEWMV
   3,    // SR_ZERO_NEWMV
   1,    // SR_NEW_NEWMV
diff --git a/av1/common/pred_common.c b/av1/common/pred_common.c
index 51c343d..bda1fc5 100644
--- a/av1/common/pred_common.c
+++ b/av1/common/pred_common.c
@@ -223,27 +223,34 @@
 // The compound/single mode info data structure has one element border above and
 // to the left of the entries corresponding to real macroblocks.
 // The prediction flags in these dummy entries are initialized to 0.
-// 0 - single/single
-// 1 - single/--, --/single, --/--
-// 2 - single/comp, comp/single
-// 3 - comp/comp, comp/--, --/comp
 int av1_get_inter_mode_context(const MACROBLOCKD *xd) {
   const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
   const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
   const int has_above = xd->up_available;
   const int has_left = xd->left_available;
 
-  if (has_above && has_left) {  // both edges available (0/2/3)
-    const int above_inter_comp_mode = is_inter_compound_mode(above_mbmi->mode);
-    const int left_inter_comp_mode = is_inter_compound_mode(left_mbmi->mode);
-    return (above_inter_comp_mode && left_inter_comp_mode)
-               ? 3
-               : (above_inter_comp_mode || left_inter_comp_mode) * 2;
-  } else if (has_above || has_left) {  // one edge available (1/3)
+  if (has_above && has_left) {  // both edges available
+    const int above_inter_comp_mode =
+        is_inter_anyref_comp_mode(above_mbmi->mode);
+    const int left_inter_comp_mode = is_inter_anyref_comp_mode(left_mbmi->mode);
+    if (above_inter_comp_mode && left_inter_comp_mode)
+      return 0;
+    else if (above_inter_comp_mode || left_inter_comp_mode)
+      return 1;
+    else if (!is_inter_block(above_mbmi) && !is_inter_block(left_mbmi))
+      return 2;
+    else
+      return 3;
+  } else if (has_above || has_left) {  // one edge available
     const MB_MODE_INFO *const edge_mbmi = has_above ? above_mbmi : left_mbmi;
-    return is_inter_compound_mode(edge_mbmi->mode) ? 3 : 1;
-  } else {  // no edge available (1)
-    return 1;
+    if (is_inter_anyref_comp_mode(edge_mbmi->mode))
+      return 1;
+    else if (!is_inter_block(edge_mbmi))
+      return 2;
+    else
+      return 3;
+  } else {  // no edge available
+    return 2;
   }
 }
 #endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
diff --git a/av1/common/reconinter.c b/av1/common/reconinter.c
index a0d1b63..8ca06c0 100644
--- a/av1/common/reconinter.c
+++ b/av1/common/reconinter.c
@@ -710,7 +710,8 @@
                                      int p_col, int p_row, int ref,
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
                                      MACROBLOCKD *xd) {
-  MODE_INFO *mi = xd->mi[0];
+  const MODE_INFO *mi = xd->mi[0];
+
   const INTERINTER_COMPOUND_DATA comp_data = {
 #if CONFIG_WEDGE
     mi->mbmi.wedge_index,
@@ -903,6 +904,10 @@
   const MODE_INFO *mi = xd->mi[0];
 #endif  // CONFIG_MOTION_VAR
   int is_compound = has_second_ref(&mi->mbmi);
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  int is_comp_mode_pred =
+      is_compound || is_inter_singleref_comp_mode(mi->mbmi.mode);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   int ref;
 #if CONFIG_INTRABC
   const int is_intrabc = is_intrabc_block(&mi->mbmi);
@@ -914,6 +919,9 @@
     WarpedMotionParams *const wm = &xd->global_motion[mi->mbmi.ref_frame[ref]];
     is_global[ref] = is_global_mv_block(mi, block, wm->wmtype);
   }
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!is_compound && is_comp_mode_pred) is_global[1] = is_global[0];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_GLOBAL_MOTION
 
 #if CONFIG_CB4X4
@@ -968,7 +976,8 @@
       for (idx = 0; idx < b8_w; idx += b4_w) {
         MB_MODE_INFO *this_mbmi = &xd->mi[row * xd->mi_stride + col]->mbmi;
         is_compound = has_second_ref(this_mbmi);
-
+        // TODO(zoeliu): If single ref comp modes are considered here, a
+        //               mismatch was caused. Need a further investigation.
         for (ref = 0; ref < 1 + is_compound; ++ref) {
           struct buf_2d *const dst_buf = &pd->dst;
 
@@ -1065,7 +1074,7 @@
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 #if CONFIG_MOTION_VAR
                 mi_col_offset, mi_row_offset,
-#endif
+#endif  // CONFIG_MOTION_VAR
                 xs, ys, xd);
         }
         ++col;
@@ -1091,7 +1100,11 @@
     av1_zero(tmp_dst);
 #endif  // CONFIG_CONVOLVE_ROUND
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (ref = 0; ref < 1 + is_comp_mode_pred; ++ref) {
+#else
     for (ref = 0; ref < 1 + is_compound; ++ref) {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_INTRABC
       const struct scale_factors *const sf =
           is_intrabc ? &xd->sf_identity : &xd->block_refs[ref]->sf;
@@ -1149,7 +1162,12 @@
 #else
     ConvolveParams conv_params = get_conv_params(ref, ref, plane);
 #endif  // CONFIG_CONVOLVE_ROUND
+
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (ref = 0; ref < 1 + is_comp_mode_pred; ++ref) {
+#else
     for (ref = 0; ref < 1 + is_compound; ++ref) {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_INTRABC
       const struct scale_factors *const sf =
           is_intrabc ? &xd->sf_identity : &xd->block_refs[ref]->sf;
@@ -1197,7 +1215,7 @@
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 #if CONFIG_MOTION_VAR
             mi_col_offset, mi_row_offset,
-#endif
+#endif  // CONFIG_MOTION_VAR
             subpel_params[ref].xs, subpel_params[ref].ys, xd);
     }
 
@@ -1213,9 +1231,15 @@
                                      xd->bd);
       else
 #endif  // CONFIG_HIGHBITDEPTH
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
         av1_convolve_rounding(tmp_dst, MAX_SB_SIZE, dst, dst_buf->stride, w, h,
-                              FILTER_BITS * 2 + is_compound -
+                              FILTER_BITS * 2 + is_comp_mode_pred -
                                   conv_params.round_0 - conv_params.round_1);
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
+      av1_convolve_rounding(tmp_dst, MAX_SB_SIZE, dst, dst_buf->stride, w, h,
+                            FILTER_BITS * 2 + is_compound -
+                                conv_params.round_0 - conv_params.round_1);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     }
 #endif  // CONFIG_CONVOLVE_ROUND
   }
@@ -1962,6 +1986,14 @@
              is_masked_compound_type(mbmi->interinter_compound_type)) {
     mbmi->interinter_compound_type = COMPOUND_AVERAGE;
     mbmi->ref_frame[1] = NONE_FRAME;
+#if CONFIG_COMPOUND_SINGLEREF
+  } else if (!has_second_ref(mbmi) &&
+             is_inter_singleref_comp_mode(mbmi->mode)) {
+    // mbmi->mode = compound_ref0_mode(mbmi->mode);
+    mbmi->mode = compound_ref1_mode(mbmi->mode);
+    assert(is_inter_singleref_mode(mbmi->mode));
+    mbmi->mv[0].as_int = mbmi->mv[1].as_int;
+#endif  // CONFIG_COMPOUND_SINGLEREF
   }
 #endif  // CONFIG_EXT_INTER
   if (has_second_ref(mbmi)) mbmi->ref_frame[1] = NONE_FRAME;
@@ -2018,8 +2050,16 @@
                        tmp_height[j], tmp_stride[j], 0, i, NULL,
                        pd->subsampling_x, pd->subsampling_y);
     }
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (ref = 0; ref < 1 + (is_inter_anyref_comp_mode(above_mbmi->mode));
+         ++ref) {
+      const MV_REFERENCE_FRAME frame = has_second_ref(above_mbmi)
+                                           ? above_mbmi->ref_frame[ref]
+                                           : above_mbmi->ref_frame[0];
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
     for (ref = 0; ref < 1 + has_second_ref(above_mbmi); ++ref) {
       const MV_REFERENCE_FRAME frame = above_mbmi->ref_frame[ref];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
       const RefBuffer *const ref_buf = &cm->frame_refs[frame - LAST_FRAME];
 
       xd->block_refs[ref] = ref_buf;
@@ -2107,8 +2147,16 @@
                        tmp_height[j], tmp_stride[j], i, 0, NULL,
                        pd->subsampling_x, pd->subsampling_y);
     }
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (ref = 0; ref < 1 + (is_inter_anyref_comp_mode(left_mbmi->mode));
+         ++ref) {
+      const MV_REFERENCE_FRAME frame = has_second_ref(left_mbmi)
+                                           ? left_mbmi->ref_frame[ref]
+                                           : left_mbmi->ref_frame[0];
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
     for (ref = 0; ref < 1 + has_second_ref(left_mbmi); ++ref) {
       const MV_REFERENCE_FRAME frame = left_mbmi->ref_frame[ref];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
       const RefBuffer *const ref_buf = &cm->frame_refs[frame - LAST_FRAME];
 
       xd->block_refs[ref] = ref_buf;
@@ -2561,6 +2609,7 @@
 #endif  // CONFIG_HIGHBITDEPTH
 
   const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
+  // TODO(zoeliu): COMPOUND_SINGLEREF has not worked with NCOBMC yet.
   av1_build_prediction_by_bottom_preds(cm, xd, mi_row, mi_col, dst_buf1,
                                        dst_width1, dst_height1, dst_stride1);
   av1_build_prediction_by_right_preds(cm, xd, mi_row, mi_col, dst_buf2,
@@ -2911,7 +2960,13 @@
 #if CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
   WarpTypesAllowed warp_types;
 #if CONFIG_GLOBAL_MOTION
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  WarpedMotionParams *const wm =
+      mi->mbmi.ref_frame[ref] > 0 ? &xd->global_motion[mi->mbmi.ref_frame[ref]]
+                                  : &xd->global_motion[mi->mbmi.ref_frame[0]];
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
   WarpedMotionParams *const wm = &xd->global_motion[mi->mbmi.ref_frame[ref]];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   warp_types.global_warp_allowed = is_global_mv_block(mi, block, wm->wmtype);
 #endif  // CONFIG_GLOBAL_MOTION
 #if CONFIG_WARPED_MOTION
@@ -2966,6 +3021,10 @@
       const int num_4x4_w = num_4x4_blocks_wide_lookup[plane_bsize];
       const int num_4x4_h = num_4x4_blocks_high_lookup[plane_bsize];
       assert(bsize == BLOCK_8X8);
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      assert(has_second_ref(&xd->mi[0]->mbmi) ||
+             !is_inter_singleref_comp_mode(xd->mi[0]->mbmi.mode));
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
       for (y = 0; y < num_4x4_h; ++y)
         for (x = 0; x < num_4x4_w; ++x)
           build_inter_predictors_single_buf(
@@ -3003,7 +3062,12 @@
     mbmi->interinter_compound_type
   };
 
+#if CONFIG_COMPOUND_SINGLEREF
+  if ((is_compound || is_inter_singleref_comp_mode(mbmi->mode)) &&
+      is_masked_compound_type(mbmi->interinter_compound_type)) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (is_compound && is_masked_compound_type(mbmi->interinter_compound_type)) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_COMPOUND_SEGMENT
     if (!plane && comp_data.interinter_compound_type == COMPOUND_SEG) {
 #if CONFIG_HIGHBITDEPTH
@@ -3034,7 +3098,7 @@
           dst, dst_buf->stride, ext_dst0, ext_dst_stride0, ext_dst1,
           ext_dst_stride1, &comp_data, mbmi->sb_type, wedge_offset_x,
           wedge_offset_y, h, w);
-#else
+#else  // !CONFIG_SUPERTX
 #if CONFIG_HIGHBITDEPTH
     if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH)
       build_masked_compound_highbd(
diff --git a/av1/common/reconinter.h b/av1/common/reconinter.h
index 7ea4289..2977864 100644
--- a/av1/common/reconinter.h
+++ b/av1/common/reconinter.h
@@ -407,14 +407,23 @@
 
 #if CONFIG_WARPED_MOTION || CONFIG_GLOBAL_MOTION
   WarpedMotionParams final_warp_params;
-  const int do_warp = allow_warp(mi, warp_types,
+  const int do_warp = allow_warp(
+      mi, warp_types,
 #if CONFIG_GLOBAL_MOTION
-                                 &xd->global_motion[mi->mbmi.ref_frame[ref]],
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      // TODO(zoeliu): To further check the single
+      // ref comp mode to work together with
+      //               global motion.
+      has_second_ref(&mi->mbmi) ? &xd->global_motion[mi->mbmi.ref_frame[ref]]
+                                : &xd->global_motion[mi->mbmi.ref_frame[0]],
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
+      &xd->global_motion[mi->mbmi.ref_frame[ref]],
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_GLOBAL_MOTION
 #if CONFIG_MOTION_VAR
-                                 mi_col_offset, mi_row_offset,
+      mi_col_offset, mi_row_offset,
 #endif  // CONFIG_MOTION_VAR
-                                 &final_warp_params);
+      &final_warp_params);
   if (do_warp) {
     const struct macroblockd_plane *const pd = &xd->plane[plane];
     const struct buf_2d *const pre_buf = &pd->pre[ref];
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 5e5db93..37583d6 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -215,7 +215,23 @@
     }
   }
 }
+
+#if CONFIG_COMPOUND_SINGLEREF
+static void read_inter_singleref_comp_mode_probs(FRAME_CONTEXT *fc,
+                                                 aom_reader *r) {
+  int i, j;
+  if (aom_read(r, GROUP_DIFF_UPDATE_PROB, ACCT_STR)) {
+    for (j = 0; j < INTER_MODE_CONTEXTS; ++j) {
+      for (i = 0; i < INTER_SINGLEREF_COMP_MODES - 1; ++i) {
+        av1_diff_update_prob(r, &fc->inter_singleref_comp_mode_probs[j][i],
+                             ACCT_STR);
+      }
+    }
+  }
+}
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
+
 #if !CONFIG_EC_ADAPT
 #if !CONFIG_EXT_TX
 static void read_ext_tx_probs(FRAME_CONTEXT *fc, aom_reader *r) {
@@ -962,7 +978,13 @@
 static void set_ref(AV1_COMMON *const cm, MACROBLOCKD *const xd, int idx,
                     int mi_row, int mi_col) {
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  RefBuffer *ref_buffer =
+      has_second_ref(mbmi) ? &cm->frame_refs[mbmi->ref_frame[idx] - LAST_FRAME]
+                           : &cm->frame_refs[mbmi->ref_frame[0] - LAST_FRAME];
+#else
   RefBuffer *ref_buffer = &cm->frame_refs[mbmi->ref_frame[idx] - LAST_FRAME];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   xd->block_refs[idx] = ref_buffer;
   if (!av1_is_valid_scale(&ref_buffer->sf))
     aom_internal_error(&cm->error, AOM_CODEC_UNSUP_BITSTREAM,
@@ -1000,9 +1022,12 @@
   mbmi = set_offsets_extend(cm, xd, tile, bsize_pred, mi_row_pred, mi_col_pred,
                             mi_row_ori, mi_col_ori);
   set_ref(cm, xd, 0, mi_row_pred, mi_col_pred);
-  if (has_second_ref(&xd->mi[0]->mbmi))
+  if (has_second_ref(&xd->mi[0]->mbmi)
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      || is_inter_singleref_comp_mode(xd->mi[0]->mbmi.mode)
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          )
     set_ref(cm, xd, 1, mi_row_pred, mi_col_pred);
-
   if (!bextend) mbmi->tx_size = max_txsize_lookup[bsize_top];
 
   xd->plane[plane].dst.stride = dst_stride;
@@ -2014,8 +2039,14 @@
   } else {
     int ref;
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (ref = 0; ref < 1 + is_inter_anyref_comp_mode(mbmi->mode); ++ref) {
+      const MV_REFERENCE_FRAME frame =
+          has_second_ref(mbmi) ? mbmi->ref_frame[ref] : mbmi->ref_frame[0];
+#else
     for (ref = 0; ref < 1 + has_second_ref(mbmi); ++ref) {
       const MV_REFERENCE_FRAME frame = mbmi->ref_frame[ref];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
       if (frame < LAST_FRAME) {
 #if CONFIG_INTRABC
         assert(is_intrabc_block(mbmi));
@@ -4236,7 +4267,11 @@
   }
 #endif  // CONFIG_INTERINTRA
 #if CONFIG_WEDGE || CONFIG_COMPOUND_SEGMENT
+#if CONFIG_COMPOUND_SINGLEREF
+  if (!frame_is_intra_only(cm)) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (!frame_is_intra_only(cm) && cm->reference_mode != SINGLE_REFERENCE) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
     cm->allow_masked_compound = aom_rb_read_bit(rb);
   } else {
     cm->allow_masked_compound = 0;
@@ -4993,6 +5028,10 @@
 
 #if CONFIG_EXT_INTER
     read_inter_compound_mode_probs(fc, &r);
+#if CONFIG_COMPOUND_SINGLEREF
+    read_inter_singleref_comp_mode_probs(fc, &r);
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_INTERINTRA
     if (cm->reference_mode != COMPOUND_REFERENCE &&
         cm->allow_interintra_compound) {
@@ -5015,7 +5054,11 @@
     }
 #endif  // CONFIG_INTERINTRA
 #if CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
+#if CONFIG_COMPOUND_SINGLEREF
+    if (cm->allow_masked_compound) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
     if (cm->reference_mode != SINGLE_REFERENCE && cm->allow_masked_compound) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
       for (i = 0; i < BLOCK_SIZES; i++) {
         for (j = 0; j < COMPOUND_TYPES - 1; j++) {
           av1_diff_update_prob(&r, &fc->compound_type_prob[i][j], ACCT_STR);
@@ -5043,6 +5086,11 @@
       setup_compound_reference_mode(cm);
     read_frame_reference_mode_probs(cm, &r);
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (i = 0; i < COMP_INTER_MODE_CONTEXTS; i++)
+      av1_diff_update_prob(&r, &fc->comp_inter_mode_prob[i], ACCT_STR);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if !CONFIG_EC_ADAPT
     for (j = 0; j < BLOCK_SIZE_GROUPS; j++) {
       for (i = 0; i < INTRA_MODES - 1; ++i)
@@ -5120,6 +5168,10 @@
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   assert(!memcmp(cm->counts.intra_inter, zero_counts.intra_inter,
                  sizeof(cm->counts.intra_inter)));
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  assert(!memcmp(cm->counts.comp_inter_mode, zero_counts.comp_inter_mode,
+                 sizeof(cm->counts.comp_inter_mode)));
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   assert(!memcmp(cm->counts.comp_inter, zero_counts.comp_inter,
                  sizeof(cm->counts.comp_inter)));
   assert(!memcmp(cm->counts.single_ref, zero_counts.single_ref,
diff --git a/av1/decoder/decodemv.c b/av1/decoder/decodemv.c
index 657eb6e..a1700ff 100644
--- a/av1/decoder/decodemv.c
+++ b/av1/decoder/decodemv.c
@@ -32,6 +32,9 @@
 #include "aom_dsp/aom_dsp_common.h"
 
 #define ACCT_STR __func__
+
+#define COMPOUND_SINGLEREF_DEBUG 0
+
 #if CONFIG_EXT_INTRA || CONFIG_FILTER_INTRA || CONFIG_PALETTE
 static INLINE int read_uniform(aom_reader *r, int n) {
   const int l = get_unsigned_bits(n);
@@ -244,10 +247,15 @@
   mbmi->ref_mv_idx = 0;
 
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV ||
+      mbmi->mode == SR_NEW_NEWMV) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV) {
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
   if (mbmi->mode == NEWMV) {
-#endif
+#endif  // CONFIG_EXT_INTER
     int idx;
     for (idx = 0; idx < 2; ++idx) {
       if (xd->ref_mv_count[ref_frame_type] > idx + 1) {
@@ -329,6 +337,23 @@
   assert(is_inter_compound_mode(NEAREST_NEARESTMV + mode));
   return NEAREST_NEARESTMV + mode;
 }
+
+#if CONFIG_COMPOUND_SINGLEREF
+static PREDICTION_MODE read_inter_singleref_comp_mode(AV1_COMMON *cm,
+                                                      MACROBLOCKD *xd,
+                                                      aom_reader *r,
+                                                      int16_t ctx) {
+  const int mode =
+      aom_read_tree(r, av1_inter_singleref_comp_mode_tree,
+                    cm->fc->inter_singleref_comp_mode_probs[ctx], ACCT_STR);
+  FRAME_COUNTS *counts = xd->counts;
+
+  if (counts) ++counts->inter_singleref_comp_mode[ctx][mode];
+
+  assert(is_inter_singleref_comp_mode(SR_NEAREST_NEARMV + mode));
+  return SR_NEAREST_NEARMV + mode;
+}
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
 
 static int read_segment_id(aom_reader *r, struct segmentation_probs *segp) {
@@ -1654,6 +1679,84 @@
       break;
     }
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+    case SR_NEAREST_NEARMV: {
+      assert(!is_compound);
+      mv[0].as_int = nearest_mv[0].as_int;
+      mv[1].as_int = near_mv[0].as_int;
+      break;
+    }
+    /*
+    case SR_NEAREST_NEWMV: {
+      assert(!is_compound);
+      mv[0].as_int = nearest_mv[0].as_int;
+
+      FRAME_COUNTS *counts = xd->counts;
+      int8_t rf_type = av1_ref_frame_type(mbmi->ref_frame);
+      int nmv_ctx = av1_nmv_ctx(xd->ref_mv_count[rf_type],
+                                xd->ref_mv_stack[rf_type], 0, mbmi->ref_mv_idx);
+      nmv_context *const nmvc = &ec_ctx->nmvc[nmv_ctx];
+      nmv_context_counts *const mv_counts =
+          counts ? &counts->mv[nmv_ctx] : NULL;
+      read_mv(r, &mv[1].as_mv, &ref_mv[0].as_mv, nmvc, mv_counts, allow_hp);
+      ret = ret && is_mv_valid(&mv[1].as_mv);
+      break;
+    }*/
+    case SR_NEAR_NEWMV: {
+      assert(!is_compound);
+      mv[0].as_int = near_mv[0].as_int;
+
+      FRAME_COUNTS *counts = xd->counts;
+      int8_t rf_type = av1_ref_frame_type(mbmi->ref_frame);
+      int nmv_ctx = av1_nmv_ctx(xd->ref_mv_count[rf_type],
+                                xd->ref_mv_stack[rf_type], 0, mbmi->ref_mv_idx);
+      nmv_context *const nmvc = &ec_ctx->nmvc[nmv_ctx];
+      nmv_context_counts *const mv_counts =
+          counts ? &counts->mv[nmv_ctx] : NULL;
+      read_mv(r, &mv[1].as_mv, &ref_mv[0].as_mv, nmvc, mv_counts, allow_hp);
+      ret = ret && is_mv_valid(&mv[1].as_mv);
+      break;
+    }
+    case SR_ZERO_NEWMV: {
+      assert(!is_compound);
+#if CONFIG_GLOBAL_MOTION
+      mv[0].as_int = gm_get_motion_vector(&cm->global_motion[ref_frame[0]],
+                                          cm->allow_high_precision_mv, bsize,
+                                          mi_col, mi_row, block)
+                         .as_int;
+#else
+      mv[0].as_int = 0;
+#endif  // CONFIG_GLOBAL_MOTION
+
+      FRAME_COUNTS *counts = xd->counts;
+      int8_t rf_type = av1_ref_frame_type(mbmi->ref_frame);
+      int nmv_ctx = av1_nmv_ctx(xd->ref_mv_count[rf_type],
+                                xd->ref_mv_stack[rf_type], 0, mbmi->ref_mv_idx);
+      nmv_context *const nmvc = &ec_ctx->nmvc[nmv_ctx];
+      nmv_context_counts *const mv_counts =
+          counts ? &counts->mv[nmv_ctx] : NULL;
+      read_mv(r, &mv[1].as_mv, &ref_mv[0].as_mv, nmvc, mv_counts, allow_hp);
+      ret = ret && is_mv_valid(&mv[1].as_mv);
+      break;
+    }
+    case SR_NEW_NEWMV: {
+      assert(!is_compound);
+
+      FRAME_COUNTS *counts = xd->counts;
+      for (i = 0; i < 2; ++i) {
+        int8_t rf_type = av1_ref_frame_type(mbmi->ref_frame);
+        int nmv_ctx =
+            av1_nmv_ctx(xd->ref_mv_count[rf_type], xd->ref_mv_stack[rf_type], 0,
+                        mbmi->ref_mv_idx);
+        nmv_context *const nmvc = &ec_ctx->nmvc[nmv_ctx];
+        nmv_context_counts *const mv_counts =
+            counts ? &counts->mv[nmv_ctx] : NULL;
+        read_mv(r, &mv[i].as_mv, &ref_mv[0].as_mv, nmvc, mv_counts, allow_hp);
+        ret = ret && is_mv_valid(&mv[i].as_mv);
+      }
+      break;
+    }
+#endif  // CONFIG_COMPOUND_SINGLEREF
     case NEW_NEWMV: {
       FRAME_COUNTS *counts = xd->counts;
       assert(is_compound);
@@ -1775,6 +1878,22 @@
   }
 }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+static int read_is_inter_singleref_comp_mode(AV1_COMMON *const cm,
+                                             MACROBLOCKD *const xd,
+                                             int segment_id, aom_reader *r) {
+  if (segfeature_active(&cm->seg, segment_id, SEG_LVL_REF_FRAME)) return 0;
+
+  const int ctx = av1_get_inter_mode_context(xd);
+  const int is_singleref_comp_mode =
+      aom_read(r, cm->fc->comp_inter_mode_prob[ctx], ACCT_STR);
+  FRAME_COUNTS *counts = xd->counts;
+
+  if (counts) ++counts->comp_inter_mode[ctx][is_singleref_comp_mode];
+  return is_singleref_comp_mode;
+}
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 static void fpm_sync(void *const data, int mi_row) {
   AV1Decoder *const pbi = (AV1Decoder *)data;
   av1_frameworker_wait(pbi->frame_worker_owner, pbi->common.prev_frame,
@@ -1799,6 +1918,9 @@
   int_mv nearestmv[2], nearmv[2];
   int_mv ref_mvs[MODE_CTX_REF_FRAMES][MAX_MV_REF_CANDIDATES];
   int ref, is_compound;
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  int is_singleref_comp_mode = 0;
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   int16_t inter_mode_ctx[MODE_CTX_REF_FRAMES];
 #if CONFIG_EXT_INTER
   int16_t compound_inter_mode_ctx[MODE_CTX_REF_FRAMES];
@@ -1825,6 +1947,12 @@
   read_ref_frames(cm, xd, r, mbmi->segment_id, mbmi->ref_frame);
   is_compound = has_second_ref(mbmi);
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!is_compound)
+    is_singleref_comp_mode =
+        read_is_inter_singleref_comp_mode(cm, xd, mbmi->segment_id, r);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
   for (ref = 0; ref < 1 + is_compound; ++ref) {
     MV_REFERENCE_FRAME frame = mbmi->ref_frame[ref];
 
@@ -1876,7 +2004,11 @@
   }
 
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  if (is_compound || is_singleref_comp_mode)
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (is_compound)
+#endif  // CONFIG_COMPOUND_SINGLEREF
     mode_ctx = compound_inter_mode_ctx[mbmi->ref_frame[0]];
   else
 #endif  // CONFIG_EXT_INTER
@@ -1896,15 +2028,22 @@
 #if CONFIG_EXT_INTER
       if (is_compound)
         mbmi->mode = read_inter_compound_mode(cm, xd, r, mode_ctx);
+#if CONFIG_COMPOUND_SINGLEREF
+      else if (is_singleref_comp_mode)
+        mbmi->mode = read_inter_singleref_comp_mode(cm, xd, r, mode_ctx);
+#endif  // CONFIG_COMPOUND_SINGLEREF
       else
 #endif  // CONFIG_EXT_INTER
         mbmi->mode = read_inter_mode(ec_ctx, xd, r, mode_ctx);
 #if CONFIG_EXT_INTER
       if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV ||
+#if CONFIG_COMPOUND_SINGLEREF
+          mbmi->mode == SR_NEW_NEWMV ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
           have_nearmv_in_inter_mode(mbmi->mode))
-#else
+#else  // !CONFIG_EXT_INTER
       if (mbmi->mode == NEARMV || mbmi->mode == NEWMV)
-#endif
+#endif  // CONFIG_EXT_INTER
         read_drl_idx(cm, xd, mbmi, r);
     }
   }
@@ -1928,9 +2067,14 @@
   }
 
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  if ((is_compound || is_singleref_comp_mode) &&
+      (bsize >= BLOCK_8X8 || unify_bsize) && mbmi->mode != ZERO_ZEROMV) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (is_compound && (bsize >= BLOCK_8X8 || unify_bsize) &&
       mbmi->mode != ZERO_ZEROMV) {
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
   if (is_compound && (bsize >= BLOCK_8X8 || unify_bsize) &&
       mbmi->mode != NEWMV && mbmi->mode != ZEROMV) {
 #endif  // CONFIG_EXT_INTER
@@ -1949,7 +2093,12 @@
         lower_mv_precision(&nearestmv[0].as_mv, allow_hp);
         lower_mv_precision(&nearestmv[1].as_mv, allow_hp);
 #if CONFIG_EXT_INTER
-      } else if (mbmi->mode == NEAREST_NEWMV) {
+      } else if (mbmi->mode == NEAREST_NEWMV
+#if CONFIG_COMPOUND_SINGLEREF
+                 || mbmi->mode == SR_NEAREST_NEARMV
+// || mbmi->mode == SR_NEAREST_NEWMV
+#endif  // CONFIG_COMPOUND_SINGLEREF
+                 ) {
         nearestmv[0] = xd->ref_mv_stack[ref_frame_type][0].this_mv;
         lower_mv_precision(&nearestmv[0].as_mv, allow_hp);
       } else if (mbmi->mode == NEW_NEARESTMV) {
@@ -1962,17 +2111,30 @@
 #if CONFIG_EXT_INTER
     if (xd->ref_mv_count[ref_frame_type] > 1) {
       int ref_mv_idx = 1 + mbmi->ref_mv_idx;
-      if (compound_ref0_mode(mbmi->mode) == NEARMV) {
-        nearmv[0] = xd->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
-        lower_mv_precision(&nearmv[0].as_mv, allow_hp);
-      }
+#if CONFIG_COMPOUND_SINGLEREF
+      if (is_compound) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
+        if (compound_ref0_mode(mbmi->mode) == NEARMV) {
+          nearmv[0] = xd->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
+          lower_mv_precision(&nearmv[0].as_mv, allow_hp);
+        }
 
-      if (compound_ref1_mode(mbmi->mode) == NEARMV) {
-        nearmv[1] = xd->ref_mv_stack[ref_frame_type][ref_mv_idx].comp_mv;
-        lower_mv_precision(&nearmv[1].as_mv, allow_hp);
+        if (compound_ref1_mode(mbmi->mode) == NEARMV) {
+          nearmv[1] = xd->ref_mv_stack[ref_frame_type][ref_mv_idx].comp_mv;
+          lower_mv_precision(&nearmv[1].as_mv, allow_hp);
+        }
+#if CONFIG_COMPOUND_SINGLEREF
+      } else {
+        assert(is_singleref_comp_mode);
+        if (compound_ref0_mode(mbmi->mode) == NEARMV ||
+            compound_ref1_mode(mbmi->mode) == NEARMV) {
+          nearmv[0] = xd->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
+          lower_mv_precision(&nearmv[0].as_mv, allow_hp);
+        }
       }
+#endif  // CONFIG_COMPOUND_SINGLEREF
     }
-#else
+#else   // !CONFIG_EXT_INTER
     if (xd->ref_mv_count[ref_frame_type] > 1) {
       int ref_mv_idx = 1 + mbmi->ref_mv_idx;
       nearestmv[0] = xd->ref_mv_stack[ref_frame_type][0].this_mv;
@@ -2112,6 +2274,26 @@
         }
         nearestmv[1] = ref_mv[1];
       }
+#if CONFIG_COMPOUND_SINGLEREF
+    } else if (is_singleref_comp_mode) {
+      int ref_mv_idx = mbmi->ref_mv_idx;
+      // Special case: SR_NEAR_NEWMV use 1 + mbmi->ref_mv_idx (like NEARMV)
+      //               instead of mbmi->ref_mv_idx (like NEWMV)
+      if (mbmi->mode == SR_NEAR_NEWMV) ref_mv_idx = 1 + mbmi->ref_mv_idx;
+
+      if (compound_ref0_mode(mbmi->mode) == NEWMV ||
+          compound_ref1_mode(mbmi->mode) == NEWMV) {
+        uint8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
+        if (xd->ref_mv_count[ref_frame_type] > 1) {
+          ref_mv[0] = xd->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
+          clamp_mv_ref(&ref_mv[0].as_mv, xd->n8_w << MI_SIZE_LOG2,
+                       xd->n8_h << MI_SIZE_LOG2, xd);
+        }
+        // TODO(zoeliu): To further investigate why this would not cause a
+        //               mismatch for the mode of SR_NEAREST_NEWMV.
+        nearestmv[0] = ref_mv[0];
+      }
+#endif  // CONFIG_COMPOUND_SINGLEREF
     } else {
 #endif  // CONFIG_EXT_INTER
       if (mbmi->mode == NEWMV) {
@@ -2199,6 +2381,9 @@
     if (mbmi->ref_frame[1] != INTRA_FRAME)
 #endif  // CONFIG_EXT_INTER
       mbmi->motion_mode = read_motion_mode(cm, xd, mi, r);
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (is_singleref_comp_mode) assert(mbmi->motion_mode == SIMPLE_TRANSLATION);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_WARPED_MOTION
     if (mbmi->motion_mode == WARPED_CAUSAL) {
       mbmi->wm_params[0].wmtype = DEFAULT_WMTYPE;
@@ -2216,8 +2401,14 @@
 
 #if CONFIG_EXT_INTER
   mbmi->interinter_compound_type = COMPOUND_AVERAGE;
-  if (cm->reference_mode != SINGLE_REFERENCE &&
+  if (
+#if CONFIG_COMPOUND_SINGLEREF
+      (is_inter_compound_mode(mbmi->mode) ||
+       is_inter_singleref_comp_mode(mbmi->mode))
+#else   // !CONFIG_COMPOUND_SINGLEREF
+      cm->reference_mode != SINGLE_REFERENCE &&
       is_inter_compound_mode(mbmi->mode)
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
       && mbmi->motion_mode == SIMPLE_TRANSLATION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
@@ -2253,6 +2444,39 @@
 #if CONFIG_DUAL_FILTER || CONFIG_WARPED_MOTION || CONFIG_GLOBAL_MOTION
   read_mb_interp_filter(cm, xd, mbmi, r);
 #endif  // CONFIG_DUAL_FILTER || CONFIG_WARPED_MOTION
+
+#if CONFIG_EXT_INTER
+#if COMPOUND_SINGLEREF_DEBUG
+// NOTE(zoeliu): For debug
+#define FRAME_TO_CHECK 1
+  if (cm->current_video_frame == FRAME_TO_CHECK &&
+      ((cm->reference_mode != SINGLE_REFERENCE && cm->show_frame == 0) ||
+       cm->show_frame == 1)) {
+    const PREDICTION_MODE mode = mbmi->mode;
+
+    int_mv mv[2];
+#if CONFIG_COMPOUND_SINGLEREF
+    int is_comp_pred =
+        has_second_ref(mbmi) || is_inter_singleref_comp_mode(mbmi->mode);
+#else
+    int is_comp_pred = has_second_ref(mbmi);
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
+    mv[0].as_int = mbmi->mv[0].as_int;
+    mv[1].as_int = is_comp_pred ? mbmi->mv[1].as_int : 0;
+
+    printf(
+        "=== DECODER ===: "
+        "Frame=%d, (mi_row,mi_col)=(%d,%d), mode=%d, bsize=%d, "
+        "show_frame=%d, mv[0]=(%d,%d), mv[1]=(%d,%d), ref[0]=%d, ref[1]=%d, "
+        "motion_mode=%d\n",
+        cm->current_video_frame, mi_row, mi_col, mode, bsize, cm->show_frame,
+        mv[0].as_mv.row, mv[0].as_mv.col, mv[1].as_mv.row, mv[1].as_mv.col,
+        mbmi->ref_frame[0], mbmi->ref_frame[1], mbmi->motion_mode);
+  }
+#endif  // COMPOUND_SINGLEREF_DEBUG
+#undef COMPOUND_SINGLEREF_DEBUG
+#endif  // CONFIG_EXT_INTER
 }
 
 static void read_inter_frame_mode_info(AV1Decoder *const pbi,
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index fb8ab94..a9277fa 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -61,6 +61,8 @@
 #include "av1/encoder/pvq_encoder.h"
 #endif
 
+#define COMPOUND_SINGLEREF_DEBUG 0
+
 static struct av1_token intra_mode_encodings[INTRA_MODES];
 static struct av1_token switchable_interp_encodings[SWITCHABLE_FILTERS];
 static struct av1_token partition_encodings[PARTITION_TYPES];
@@ -70,6 +72,10 @@
       { 2, 2 },  { 12, 4 }, { 52, 6 }, { 53, 6 },
       { 54, 6 }, { 55, 6 }, { 0, 1 },  { 7, 3 }
     };
+#if CONFIG_COMPOUND_SINGLEREF
+static struct av1_token
+    inter_singleref_comp_mode_encodings[INTER_SINGLEREF_COMP_MODES];
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
 #if CONFIG_PALETTE
 static struct av1_token palette_size_encodings[PALETTE_SIZES];
@@ -161,6 +167,10 @@
 #if CONFIG_INTERINTRA
   av1_tokens_from_tree(interintra_mode_encodings, av1_interintra_mode_tree);
 #endif  // CONFIG_INTERINTRA
+#if CONFIG_COMPOUND_SINGLEREF
+  av1_tokens_from_tree(inter_singleref_comp_mode_encodings,
+                       av1_inter_singleref_comp_mode_tree);
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
   av1_tokens_from_tree(compound_type_encodings, av1_compound_type_tree);
 #endif  // CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
@@ -259,10 +269,15 @@
   assert(mbmi->ref_mv_idx < 3);
 
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV ||
+      mbmi->mode == SR_NEW_NEWMV) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV) {
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
   if (mbmi->mode == NEWMV) {
-#endif
+#endif  // CONFIG_EXT_INTER
     int idx;
     for (idx = 0; idx < 2; ++idx) {
       if (mbmi_ext->ref_mv_count[ref_frame_type] > idx + 1) {
@@ -305,6 +320,20 @@
   av1_write_token(w, av1_inter_compound_mode_tree, inter_compound_probs,
                   &inter_compound_mode_encodings[INTER_COMPOUND_OFFSET(mode)]);
 }
+
+#if CONFIG_COMPOUND_SINGLEREF
+static void write_inter_singleref_comp_mode(AV1_COMMON *cm, aom_writer *w,
+                                            PREDICTION_MODE mode,
+                                            const int16_t mode_ctx) {
+  assert(is_inter_singleref_comp_mode(mode));
+  const aom_prob *const inter_singleref_comp_probs =
+      cm->fc->inter_singleref_comp_mode_probs[mode_ctx];
+
+  av1_write_token(
+      w, av1_inter_singleref_comp_mode_tree, inter_singleref_comp_probs,
+      &inter_singleref_comp_mode_encodings[INTER_SINGLEREF_COMP_OFFSET(mode)]);
+}
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
 
 static void encode_unsigned_max(struct aom_write_bit_buffer *wb, int data,
@@ -490,6 +519,34 @@
     }
   }
 }
+
+#if CONFIG_COMPOUND_SINGLEREF
+static void update_inter_singleref_comp_mode_probs(AV1_COMMON *cm, int probwt,
+                                                   aom_writer *w) {
+  const int savings_thresh = av1_cost_one(GROUP_DIFF_UPDATE_PROB) -
+                             av1_cost_zero(GROUP_DIFF_UPDATE_PROB);
+  int i;
+  int savings = 0;
+  int do_update = 0;
+  for (i = 0; i < INTER_MODE_CONTEXTS; ++i) {
+    savings +=
+        prob_diff_update_savings(av1_inter_singleref_comp_mode_tree,
+                                 cm->fc->inter_singleref_comp_mode_probs[i],
+                                 cm->counts.inter_singleref_comp_mode[i],
+                                 INTER_SINGLEREF_COMP_MODES, probwt);
+  }
+  do_update = savings > savings_thresh;
+  aom_write(w, do_update, GROUP_DIFF_UPDATE_PROB);
+  if (do_update) {
+    for (i = 0; i < INTER_MODE_CONTEXTS; ++i) {
+      prob_diff_update(av1_inter_singleref_comp_mode_tree,
+                       cm->fc->inter_singleref_comp_mode_probs[i],
+                       cm->counts.inter_singleref_comp_mode[i],
+                       INTER_SINGLEREF_COMP_MODES, probwt, w);
+    }
+  }
+}
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
 
 static int write_skip(const AV1_COMMON *cm, const MACROBLOCKD *xd,
@@ -1891,8 +1948,21 @@
     int16_t mode_ctx;
     write_ref_frames(cm, xd, w);
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (!segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME)) {
+      // NOTE: Handle single ref comp mode
+      if (!is_compound)
+        aom_write(w, is_inter_singleref_comp_mode(mode),
+                  av1_get_inter_mode_prob(cm, xd));
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+    if (is_compound || is_inter_singleref_comp_mode(mode))
+#else   // !CONFIG_COMPOUND_SINGLEREF
     if (is_compound)
+#endif  // CONFIG_COMPOUND_SINGLEREF
       mode_ctx = mbmi_ext->compound_mode_context[mbmi->ref_frame[0]];
     else
 #endif  // CONFIG_EXT_INTER
@@ -1905,16 +1975,23 @@
 #if CONFIG_EXT_INTER
         if (is_inter_compound_mode(mode))
           write_inter_compound_mode(cm, w, mode, mode_ctx);
+#if CONFIG_COMPOUND_SINGLEREF
+        else if (is_inter_singleref_comp_mode(mode))
+          write_inter_singleref_comp_mode(cm, w, mode, mode_ctx);
+#endif  // CONFIG_COMPOUND_SINGLEREF
         else if (is_inter_singleref_mode(mode))
 #endif  // CONFIG_EXT_INTER
           write_inter_mode(w, mode, ec_ctx, mode_ctx);
 
 #if CONFIG_EXT_INTER
         if (mode == NEWMV || mode == NEW_NEWMV ||
+#if CONFIG_COMPOUND_SINGLEREF
+            mbmi->mode == SR_NEW_NEWMV ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
             have_nearmv_in_inter_mode(mode))
-#else
+#else   // !CONFIG_EXT_INTER
         if (mode == NEARMV || mode == NEWMV)
-#endif
+#endif  // CONFIG_EXT_INTER
           write_drl_idx(cm, mbmi, mbmi_ext, w);
         else
           assert(mbmi->ref_mv_idx == 0);
@@ -1926,6 +2003,10 @@
 #endif  // !CONFIG_DUAL_FILTER && !CONFIG_WARPED_MOTION
 
     if (bsize < BLOCK_8X8 && !unify_bsize) {
+#if CONFIG_COMPOUND_SINGLEREF
+      /// NOTE: Single ref comp mode does not support sub8x8.
+      assert(is_compound || !is_inter_singleref_comp_mode(mbmi->mode));
+#endif  // CONFIG_COMPOUND_SINGLEREF
       const int num_4x4_w = num_4x4_blocks_wide_lookup[bsize];
       const int num_4x4_h = num_4x4_blocks_high_lookup[bsize];
       int idx, idy;
@@ -2022,6 +2103,22 @@
         av1_encode_mv(cpi, w, &mbmi->mv[0].as_mv,
                       &mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0].as_mv, nmvc,
                       allow_hp);
+#if CONFIG_COMPOUND_SINGLEREF
+      } else if (  //  mode == SR_NEAREST_NEWMV ||
+          mode == SR_NEAR_NEWMV || mode == SR_ZERO_NEWMV ||
+          mode == SR_NEW_NEWMV) {
+        int8_t rf_type = av1_ref_frame_type(mbmi->ref_frame);
+        int nmv_ctx =
+            av1_nmv_ctx(mbmi_ext->ref_mv_count[rf_type],
+                        mbmi_ext->ref_mv_stack[rf_type], 0, mbmi->ref_mv_idx);
+        nmv_context *nmvc = &ec_ctx->nmvc[nmv_ctx];
+        int_mv ref_mv = mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0];
+        if (mode == SR_NEW_NEWMV)
+          av1_encode_mv(cpi, w, &mbmi->mv[0].as_mv, &ref_mv.as_mv, nmvc,
+                        allow_hp);
+        av1_encode_mv(cpi, w, &mbmi->mv[1].as_mv, &ref_mv.as_mv, nmvc,
+                      allow_hp);
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
       }
     }
@@ -2062,12 +2159,18 @@
 #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 (
+#if CONFIG_COMPOUND_SINGLEREF
+        (is_inter_compound_mode(mbmi->mode) ||
+         is_inter_singleref_comp_mode(mbmi->mode)) &&
+#else   // !CONFIG_COMPOUND_SINGLEREF
+        cpi->common.reference_mode != SINGLE_REFERENCE &&
+        is_inter_compound_mode(mbmi->mode) &&
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_MOTION_VAR
-        && mbmi->motion_mode == SIMPLE_TRANSLATION
+        mbmi->motion_mode == SIMPLE_TRANSLATION &&
 #endif  // CONFIG_MOTION_VAR
-        && is_any_masked_compound_used(bsize)) {
+        is_any_masked_compound_used(bsize)) {
 #if CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
       if (cm->allow_masked_compound) {
         av1_write_token(
@@ -2350,31 +2453,53 @@
     // up if they are scaled. has_subpel_mv_component is in turn needed by
     // write_switchable_interp_filter, which is called by pack_inter_mode_mvs.
     set_ref_ptrs(cm, xd, m->mbmi.ref_frame[0], m->mbmi.ref_frame[1]);
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (!has_second_ref(&m->mbmi) && is_inter_singleref_comp_mode(m->mbmi.mode))
+      xd->block_refs[1] = xd->block_refs[0];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_DUAL_FILTER
-#if 0
+
+#if CONFIG_EXT_INTER
+#if COMPOUND_SINGLEREF_DEBUG
     // NOTE(zoeliu): For debug
-    if (cm->current_video_frame == FRAME_TO_CHECK && cm->show_frame == 1) {
-      const PREDICTION_MODE mode = m->mbmi.mode;
-      const int segment_id = m->mbmi.segment_id;
-      const BLOCK_SIZE bsize = m->mbmi.sb_type;
+    if (is_inter_block(&m->mbmi)) {
+#define FRAME_TO_CHECK 1
+      if (cm->current_video_frame == FRAME_TO_CHECK &&
+          ((cm->reference_mode != SINGLE_REFERENCE && cm->show_frame == 0) ||
+           cm->show_frame == 1)) {
+        const MB_MODE_INFO *const mbmi = &m->mbmi;
+        const BLOCK_SIZE bsize = mbmi->sb_type;
 
-      // For sub8x8, simply dump out the first sub8x8 block info
-      const PREDICTION_MODE b_mode =
-          (bsize < BLOCK_8X8) ? m->bmi[0].as_mode : -1;
-      const int mv_x = (bsize < BLOCK_8X8) ?
-          m->bmi[0].as_mv[0].as_mv.row : m->mbmi.mv[0].as_mv.row;
-      const int mv_y = (bsize < BLOCK_8X8) ?
-          m->bmi[0].as_mv[0].as_mv.col : m->mbmi.mv[0].as_mv.col;
+        int_mv mv[2];
+        int is_comp_ref = has_second_ref(&m->mbmi);
+        int ref;
 
-      printf("Before pack_inter_mode_mvs(): "
-             "Frame=%d, (mi_row,mi_col)=(%d,%d), "
-             "mode=%d, segment_id=%d, bsize=%d, b_mode=%d, "
-             "mv[0]=(%d, %d), ref[0]=%d, ref[1]=%d\n",
-             cm->current_video_frame, mi_row, mi_col,
-             mode, segment_id, bsize, b_mode, mv_x, mv_y,
-             m->mbmi.ref_frame[0], m->mbmi.ref_frame[1]);
+        for (ref = 0; ref < 1 + is_comp_ref; ++ref)
+          mv[ref].as_mv = m->mbmi.mv[ref].as_mv;
+
+        if (!is_comp_ref) {
+#if CONFIG_COMPOUND_SINGLEREF
+          if (is_inter_singleref_comp_mode(m->mbmi.mode))
+            mv[1].as_mv = m->mbmi.mv[1].as_mv;
+          else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+            mv[1].as_int = 0;
+        }
+
+        printf(
+            "=== ENCODER ===: "
+            "Frame=%d, (mi_row,mi_col)=(%d,%d), mode=%d, bsize=%d, "
+            "show_frame=%d, mv[0]=(%d,%d), mv[1]=(%d,%d), ref[0]=%d, "
+            "ref[1]=%d, motion_mode=%d\n",
+            cm->current_video_frame, mi_row, mi_col, mbmi->mode, bsize,
+            cm->show_frame, mv[0].as_mv.row, mv[0].as_mv.col, mv[1].as_mv.row,
+            mv[1].as_mv.col, mbmi->ref_frame[0], mbmi->ref_frame[1],
+            mbmi->motion_mode);
+      }
     }
-#endif  // 0
+#endif  // COMPOUND_SINGLEREF_DEBUG
+#undef COMPOUND_SINGLEREF_DEBUG
+#endif  // CONFIG_EXT_INTER
     pack_inter_mode_mvs(cpi, mi_row, mi_col,
 #if CONFIG_SUPERTX
                         supertx_enabled,
@@ -3988,7 +4113,7 @@
       *max_tile_col_size = AOMMAX(*max_tile_col_size, col_size);
     }
   }
-#else
+#else  // !CONFIG_EXT_TILE
 #if CONFIG_TILE_GROUPS
   write_uncompressed_header(cpi, wb);
 
@@ -4303,7 +4428,11 @@
   }
 #endif  // CONFIG_INTERINTRA
 #if CONFIG_WEDGE || CONFIG_COMPOUND_SEGMENT
+#if CONFIG_COMPOUND_SINGLEREF
+  if (!frame_is_intra_only(cm)) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (!frame_is_intra_only(cm) && cm->reference_mode != SINGLE_REFERENCE) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
     aom_wb_write_bit(wb, cm->allow_masked_compound);
   } else {
     assert(cm->allow_masked_compound == 0);
@@ -4790,6 +4919,10 @@
     update_inter_mode_probs(cm, header_bc, counts);
 #if CONFIG_EXT_INTER
     update_inter_compound_mode_probs(cm, probwt, header_bc);
+#if CONFIG_COMPOUND_SINGLEREF
+    update_inter_singleref_comp_mode_probs(cm, probwt, header_bc);
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_INTERINTRA
     if (cm->reference_mode != COMPOUND_REFERENCE &&
         cm->allow_interintra_compound) {
@@ -4814,7 +4947,11 @@
     }
 #endif  // CONFIG_INTERINTRA
 #if CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
+#if CONFIG_COMPOUND_SINGLEREF
+    if (cm->allow_masked_compound) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
     if (cm->reference_mode != SINGLE_REFERENCE && cm->allow_masked_compound) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
       for (i = 0; i < BLOCK_SIZES; i++)
         prob_diff_update(av1_compound_type_tree, fc->compound_type_prob[i],
                          cm->counts.compound_interinter[i], COMPOUND_TYPES,
@@ -4873,6 +5010,12 @@
       }
     }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    for (i = 0; i < COMP_INTER_MODE_CONTEXTS; i++)
+      av1_cond_prob_diff_update(header_bc, &fc->comp_inter_mode_prob[i],
+                                counts->comp_inter_mode[i], probwt);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if !CONFIG_EC_ADAPT
     for (i = 0; i < BLOCK_SIZE_GROUPS; ++i) {
       prob_diff_update(av1_intra_mode_tree, cm->fc->y_mode_prob[i],
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index cf98b35..5c8744e 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -539,6 +539,21 @@
       mbmi->pred_mv[1] = this_mv;
       mi_pred_mv[1] = this_mv;
     }
+#if CONFIG_COMPOUND_SINGLEREF
+  } else if (is_inter_singleref_comp_mode(mbmi->mode)) {
+    // Special case: SR_NEAR_NEWMV uses 1 + mbmi->ref_mv_idx
+    // (like NEARMV) instead
+    if (mbmi->mode == SR_NEAR_NEWMV) ref_mv_idx += 1;
+
+    if (compound_ref0_mode(mbmi->mode) == NEWMV ||
+        compound_ref1_mode(mbmi->mode) == NEWMV) {
+      int_mv this_mv = curr_ref_mv_stack[ref_mv_idx].this_mv;
+      clamp_mv_ref(&this_mv.as_mv, bw, bh, xd);
+      mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0] = this_mv;
+      mbmi->pred_mv[0] = this_mv;
+      mi_pred_mv[0] = this_mv;
+    }
+#endif  // CONFIG_COMPOUND_SINGLEREF
   } else {
 #endif  // CONFIG_EXT_INTER
     if (mbmi->mode == NEWMV) {
@@ -1619,7 +1634,14 @@
 #endif  // CONFIG_EXT_REFS
         }
 
-#if CONFIG_EXT_INTER && CONFIG_INTERINTRA
+#if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+        if (!has_second_ref(mbmi))
+          counts->comp_inter_mode[av1_get_inter_mode_context(xd)]
+                                 [is_inter_singleref_comp_mode(mbmi->mode)]++;
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
+#if CONFIG_INTERINTRA
         if (cm->reference_mode != COMPOUND_REFERENCE &&
 #if CONFIG_SUPERTX
             !supertx_enabled &&
@@ -1635,7 +1657,8 @@
             counts->interintra[bsize_group][0]++;
           }
         }
-#endif  // CONFIG_EXT_INTER && CONFIG_INTERINTRA
+#endif  // CONFIG_INTERINTRA
+#endif  // CONFIG_EXT_INTER
 
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
         const MOTION_MODE motion_allowed = motion_mode_allowed(
@@ -1663,8 +1686,13 @@
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_EXT_INTER
-        if (cm->reference_mode != SINGLE_REFERENCE &&
+        if (
+#if CONFIG_COMPOUND_SINGLEREF
+            is_inter_anyref_comp_mode(mbmi->mode)
+#else   // !CONFIG_COMPOUND_SINGLEREF
+            cm->reference_mode != SINGLE_REFERENCE &&
             is_inter_compound_mode(mbmi->mode)
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
             && mbmi->motion_mode == SIMPLE_TRANSLATION
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
@@ -1683,6 +1711,12 @@
       if (has_second_ref(mbmi)) {
         mode_ctx = mbmi_ext->compound_mode_context[mbmi->ref_frame[0]];
         ++counts->inter_compound_mode[mode_ctx][INTER_COMPOUND_OFFSET(mode)];
+#if CONFIG_COMPOUND_SINGLEREF
+      } else if (is_inter_singleref_comp_mode(mode)) {
+        mode_ctx = mbmi_ext->compound_mode_context[mbmi->ref_frame[0]];
+        ++counts->inter_singleref_comp_mode[mode_ctx]
+                                           [INTER_SINGLEREF_COMP_OFFSET(mode)];
+#endif  // CONFIG_COMPOUND_SINGLEREF
       } else {
 #endif  // CONFIG_EXT_INTER
         mode_ctx = av1_mode_context_analyzer(mbmi_ext->mode_context,
@@ -1693,10 +1727,15 @@
 #endif  // CONFIG_EXT_INTER
 
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+      if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV ||
+          mbmi->mode == SR_NEW_NEWMV) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
       if (mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV) {
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
       if (mbmi->mode == NEWMV) {
-#endif
+#endif  // CONFIG_EXT_INTER
         uint8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
         int idx;
 
@@ -4950,7 +4989,11 @@
     cm->allow_interintra_compound = 0;
 #endif  // CONFIG_INTERINTRA
 #if CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
+#if CONFIG_COMPOUND_SINGLEREF
+  if (frame_is_intra_only(cm))
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (frame_is_intra_only(cm) || cm->reference_mode == SINGLE_REFERENCE)
+#endif  // CONFIG_COMPOUND_SINGLEREF
     cm->allow_masked_compound = 0;
 #endif  // CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
 }
@@ -5588,8 +5631,21 @@
       av1_setup_pre_planes(xd, ref, cfg, mi_row, mi_col,
                            &xd->block_refs[ref]->sf);
     }
-    av1_build_inter_predictors_sby(cm, xd, mi_row, mi_col, NULL, block_size);
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    // Single ref compound mode
+    if (!is_compound && is_inter_singleref_comp_mode(mbmi->mode)) {
+      xd->block_refs[1] = xd->block_refs[0];
+      YV12_BUFFER_CONFIG *cfg = get_ref_frame_buffer(cpi, mbmi->ref_frame[0]);
+#if CONFIG_INTRABC
+      assert(IMPLIES(!is_intrabc_block(mbmi), cfg));
+#else
+      assert(cfg != NULL);
+#endif  // !CONFIG_INTRABC
+      av1_setup_pre_planes(xd, 1, cfg, mi_row, mi_col, &xd->block_refs[1]->sf);
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 
+    av1_build_inter_predictors_sby(cm, xd, mi_row, mi_col, NULL, block_size);
     av1_build_inter_predictors_sbuv(cm, xd, mi_row, mi_col, NULL, block_size);
 #if CONFIG_MOTION_VAR
     if (mbmi->motion_mode == OBMC_CAUSAL) {
@@ -5888,6 +5944,16 @@
                          &xd->block_refs[ref]->sf);
   }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  // Single ref compound mode
+  if (!is_compound && is_inter_singleref_comp_mode(mbmi->mode)) {
+    xd->block_refs[1] = xd->block_refs[0];
+    YV12_BUFFER_CONFIG *cfg = get_ref_frame_buffer(cpi, mbmi->ref_frame[0]);
+    av1_setup_pre_planes(xd, 1, cfg, mi_row_pred, mi_col_pred,
+                         &xd->block_refs[1]->sf);
+  }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
   if (!b_sub8x8)
     av1_build_inter_predictor_sb_extend(cm, xd,
 #if CONFIG_EXT_INTER
diff --git a/av1/encoder/encodemv.c b/av1/encoder/encodemv.c
index e1b515b..8aed521 100644
--- a/av1/encoder/encodemv.c
+++ b/av1/encoder/encodemv.c
@@ -295,6 +295,27 @@
                     mbmi_ext->ref_mv_stack[rf_type], 0, mbmi->ref_mv_idx);
     nmv_context_counts *counts = &nmv_counts[nmv_ctx];
     av1_inc_mv(&diff, counts, 1);
+#if CONFIG_COMPOUND_SINGLEREF
+  } else {
+    assert(  // mode == SR_NEAREST_NEWMV ||
+        mode == SR_NEAR_NEWMV || mode == SR_ZERO_NEWMV || mode == SR_NEW_NEWMV);
+    const MV *ref = &mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0].as_mv;
+    int8_t rf_type = av1_ref_frame_type(mbmi->ref_frame);
+    int nmv_ctx =
+        av1_nmv_ctx(mbmi_ext->ref_mv_count[rf_type],
+                    mbmi_ext->ref_mv_stack[rf_type], 0, mbmi->ref_mv_idx);
+    nmv_context_counts *counts = &nmv_counts[nmv_ctx];
+    (void)pred_mvs;
+    MV diff;
+    if (mode == SR_NEW_NEWMV) {
+      diff.row = mvs[0].as_mv.row - ref->row;
+      diff.col = mvs[0].as_mv.col - ref->col;
+      av1_inc_mv(&diff, counts, 1);
+    }
+    diff.row = mvs[1].as_mv.row - ref->row;
+    diff.col = mvs[1].as_mv.col - ref->col;
+    av1_inc_mv(&diff, counts, 1);
+#endif  // CONFIG_COMPOUND_SINGLEREF
   }
 }
 
@@ -339,7 +360,7 @@
     av1_inc_mv(&diff, counts, 1);
   }
 }
-#else
+#else   // !CONFIG_EXT_INTER
 static void inc_mvs(const MB_MODE_INFO *mbmi, const MB_MODE_INFO_EXT *mbmi_ext,
                     const int_mv mvs[2], const int_mv pred_mvs[2],
                     nmv_context_counts *nmv_counts) {
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 70de507..82c47b8 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -539,6 +539,10 @@
 #if CONFIG_EXT_INTER
   unsigned int inter_compound_mode_cost[INTER_MODE_CONTEXTS]
                                        [INTER_COMPOUND_MODES];
+#if CONFIG_COMPOUND_SINGLEREF
+  unsigned int inter_singleref_comp_mode_cost[INTER_MODE_CONTEXTS]
+                                             [INTER_SINGLEREF_COMP_MODES];
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_INTERINTRA
   unsigned int interintra_mode_cost[BLOCK_SIZE_GROUPS][INTERINTRA_MODES];
 #endif  // CONFIG_INTERINTRA
diff --git a/av1/encoder/mcomp.c b/av1/encoder/mcomp.c
index 52080ca..79543c4 100644
--- a/av1/encoder/mcomp.c
+++ b/av1/encoder/mcomp.c
@@ -2763,7 +2763,13 @@
                                  ref_mv);
 
   if (use_upsampled_ref) {
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    int ref = has_second_ref(&xd->mi[0]->mbmi)
+                  ? xd->mi[0]->mbmi.ref_frame[is_second]
+                  : xd->mi[0]->mbmi.ref_frame[0];
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
     int ref = xd->mi[0]->mbmi.ref_frame[is_second];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     const YV12_BUFFER_CONFIG *upsampled_ref = get_upsampled_ref(cpi, ref);
     setup_pred_plane(&pd->pre[is_second], mbmi->sb_type,
                      upsampled_ref->y_buffer, upsampled_ref->y_crop_width,
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index d9a1333..ae49812 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -444,6 +444,12 @@
         av1_cost_tokens((int *)cpi->inter_compound_mode_cost[i],
                         cm->fc->inter_compound_mode_probs[i],
                         av1_inter_compound_mode_tree);
+#if CONFIG_COMPOUND_SINGLEREF
+      for (i = 0; i < INTER_MODE_CONTEXTS; ++i)
+        av1_cost_tokens((int *)cpi->inter_singleref_comp_mode_cost[i],
+                        cm->fc->inter_singleref_comp_mode_probs[i],
+                        av1_inter_singleref_comp_mode_tree);
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_INTERINTRA
       for (i = 0; i < BLOCK_SIZE_GROUPS; ++i)
         av1_cost_tokens((int *)cpi->interintra_mode_cost[i],
@@ -993,6 +999,54 @@
 
 #if CONFIG_EXT_INTER
 
+#if CONFIG_COMPOUND_SINGLEREF
+  rd->thresh_mult[THR_SR_NEAREST_NEARMV] += 1200;
+#if CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEAREST_NEARL2] += 1200;
+  rd->thresh_mult[THR_SR_NEAREST_NEARL3] += 1200;
+  rd->thresh_mult[THR_SR_NEAREST_NEARB] += 1200;
+#endif  // CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEAREST_NEARA] += 1200;
+  rd->thresh_mult[THR_SR_NEAREST_NEARG] += 1200;
+
+  /*
+  rd->thresh_mult[THR_SR_NEAREST_NEWMV] += 1200;
+#if CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEAREST_NEWL2] += 1200;
+  rd->thresh_mult[THR_SR_NEAREST_NEWL3] += 1200;
+  rd->thresh_mult[THR_SR_NEAREST_NEWB] += 1200;
+#endif  // CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEAREST_NEWA] += 1200;
+  rd->thresh_mult[THR_SR_NEAREST_NEWG] += 1200;*/
+
+  rd->thresh_mult[THR_SR_NEAR_NEWMV] += 1500;
+#if CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEAR_NEWL2] += 1500;
+  rd->thresh_mult[THR_SR_NEAR_NEWL3] += 1500;
+  rd->thresh_mult[THR_SR_NEAR_NEWB] += 1500;
+#endif  // CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEAR_NEWA] += 1500;
+  rd->thresh_mult[THR_SR_NEAR_NEWG] += 1500;
+
+  rd->thresh_mult[THR_SR_ZERO_NEWMV] += 2000;
+#if CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_ZERO_NEWL2] += 2000;
+  rd->thresh_mult[THR_SR_ZERO_NEWL3] += 2000;
+  rd->thresh_mult[THR_SR_ZERO_NEWB] += 2000;
+#endif  // CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_ZERO_NEWA] += 2000;
+  rd->thresh_mult[THR_SR_ZERO_NEWG] += 2000;
+
+  rd->thresh_mult[THR_SR_NEW_NEWMV] += 1700;
+#if CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEW_NEWL2] += 1700;
+  rd->thresh_mult[THR_SR_NEW_NEWL3] += 1700;
+  rd->thresh_mult[THR_SR_NEW_NEWB] += 1700;
+#endif  // CONFIG_EXT_REFS
+  rd->thresh_mult[THR_SR_NEW_NEWA] += 1700;
+  rd->thresh_mult[THR_SR_NEW_NEWG] += 1700;
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
   rd->thresh_mult[THR_COMP_NEAREST_NEARESTLA] += 1000;
 #if CONFIG_EXT_REFS
   rd->thresh_mult[THR_COMP_NEAREST_NEARESTL2A] += 1000;
diff --git a/av1/encoder/rd.h b/av1/encoder/rd.h
index dfd4fcc..2e35cff 100644
--- a/av1/encoder/rd.h
+++ b/av1/encoder/rd.h
@@ -96,6 +96,54 @@
 
 #if CONFIG_EXT_INTER
 
+#if CONFIG_COMPOUND_SINGLEREF
+  THR_SR_NEAREST_NEARMV,
+#if CONFIG_EXT_REFS
+  THR_SR_NEAREST_NEARL2,
+  THR_SR_NEAREST_NEARL3,
+  THR_SR_NEAREST_NEARB,
+#endif  // CONFIG_EXT_REFS
+  THR_SR_NEAREST_NEARG,
+  THR_SR_NEAREST_NEARA,
+
+  /*
+  THR_SR_NEAREST_NEWMV,
+#if CONFIG_EXT_REFS
+  THR_SR_NEAREST_NEWL2,
+  THR_SR_NEAREST_NEWL3,
+  THR_SR_NEAREST_NEWB,
+#endif  // CONFIG_EXT_REFS
+  THR_SR_NEAREST_NEWG,
+  THR_SR_NEAREST_NEWA,*/
+
+  THR_SR_NEAR_NEWMV,
+#if CONFIG_EXT_REFS
+  THR_SR_NEAR_NEWL2,
+  THR_SR_NEAR_NEWL3,
+  THR_SR_NEAR_NEWB,
+#endif  // CONFIG_EXT_REFS
+  THR_SR_NEAR_NEWG,
+  THR_SR_NEAR_NEWA,
+
+  THR_SR_ZERO_NEWMV,
+#if CONFIG_EXT_REFS
+  THR_SR_ZERO_NEWL2,
+  THR_SR_ZERO_NEWL3,
+  THR_SR_ZERO_NEWB,
+#endif  // CONFIG_EXT_REFS
+  THR_SR_ZERO_NEWG,
+  THR_SR_ZERO_NEWA,
+
+  THR_SR_NEW_NEWMV,
+#if CONFIG_EXT_REFS
+  THR_SR_NEW_NEWL2,
+  THR_SR_NEW_NEWL3,
+  THR_SR_NEW_NEWB,
+#endif  // CONFIG_EXT_REFS
+  THR_SR_NEW_NEWG,
+  THR_SR_NEW_NEWA,
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
   THR_COMP_NEAREST_NEARESTLA,
 #if CONFIG_EXT_REFS
   THR_COMP_NEAREST_NEARESTL2A,
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 953ce90..a220fb3 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -191,6 +191,56 @@
 // TODO(zoeliu): May need to reconsider the order on the modes to check
 
 #if CONFIG_EXT_INTER
+
+#if CONFIG_COMPOUND_SINGLEREF
+  // Single ref comp mode
+  { SR_NEAREST_NEARMV, { LAST_FRAME, NONE_FRAME } },
+#if CONFIG_EXT_REFS
+  { SR_NEAREST_NEARMV, { LAST2_FRAME, NONE_FRAME } },
+  { SR_NEAREST_NEARMV, { LAST3_FRAME, NONE_FRAME } },
+  { SR_NEAREST_NEARMV, { BWDREF_FRAME, NONE_FRAME } },
+#endif  // CONFIG_EXT_REFS
+  { SR_NEAREST_NEARMV, { GOLDEN_FRAME, NONE_FRAME } },
+  { SR_NEAREST_NEARMV, { ALTREF_FRAME, NONE_FRAME } },
+
+  /*
+  { SR_NEAREST_NEWMV, { LAST_FRAME, NONE_FRAME } },
+#if CONFIG_EXT_REFS
+  { SR_NEAREST_NEWMV, { LAST2_FRAME, NONE_FRAME } },
+  { SR_NEAREST_NEWMV, { LAST3_FRAME, NONE_FRAME } },
+  { SR_NEAREST_NEWMV, { BWDREF_FRAME, NONE_FRAME } },
+#endif  // CONFIG_EXT_REFS
+  { SR_NEAREST_NEWMV, { GOLDEN_FRAME, NONE_FRAME } },
+  { SR_NEAREST_NEWMV, { ALTREF_FRAME, NONE_FRAME } },*/
+
+  { SR_NEAR_NEWMV, { LAST_FRAME, NONE_FRAME } },
+#if CONFIG_EXT_REFS
+  { SR_NEAR_NEWMV, { LAST2_FRAME, NONE_FRAME } },
+  { SR_NEAR_NEWMV, { LAST3_FRAME, NONE_FRAME } },
+  { SR_NEAR_NEWMV, { BWDREF_FRAME, NONE_FRAME } },
+#endif  // CONFIG_EXT_REFS
+  { SR_NEAR_NEWMV, { GOLDEN_FRAME, NONE_FRAME } },
+  { SR_NEAR_NEWMV, { ALTREF_FRAME, NONE_FRAME } },
+
+  { SR_ZERO_NEWMV, { LAST_FRAME, NONE_FRAME } },
+#if CONFIG_EXT_REFS
+  { SR_ZERO_NEWMV, { LAST2_FRAME, NONE_FRAME } },
+  { SR_ZERO_NEWMV, { LAST3_FRAME, NONE_FRAME } },
+  { SR_ZERO_NEWMV, { BWDREF_FRAME, NONE_FRAME } },
+#endif  // CONFIG_EXT_REFS
+  { SR_ZERO_NEWMV, { GOLDEN_FRAME, NONE_FRAME } },
+  { SR_ZERO_NEWMV, { ALTREF_FRAME, NONE_FRAME } },
+
+  { SR_NEW_NEWMV, { LAST_FRAME, NONE_FRAME } },
+#if CONFIG_EXT_REFS
+  { SR_NEW_NEWMV, { LAST2_FRAME, NONE_FRAME } },
+  { SR_NEW_NEWMV, { LAST3_FRAME, NONE_FRAME } },
+  { SR_NEW_NEWMV, { BWDREF_FRAME, NONE_FRAME } },
+#endif  // CONFIG_EXT_REFS
+  { SR_NEW_NEWMV, { GOLDEN_FRAME, NONE_FRAME } },
+  { SR_NEW_NEWMV, { ALTREF_FRAME, NONE_FRAME } },
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
   { NEAREST_NEARESTMV, { LAST_FRAME, ALTREF_FRAME } },
 #if CONFIG_EXT_REFS
   { NEAREST_NEARESTMV, { LAST2_FRAME, ALTREF_FRAME } },
@@ -4967,6 +5017,12 @@
   if (is_inter_compound_mode(mode)) {
     return cpi
         ->inter_compound_mode_cost[mode_context][INTER_COMPOUND_OFFSET(mode)];
+#if CONFIG_COMPOUND_SINGLEREF
+  } else if (is_inter_singleref_comp_mode(mode)) {
+    return cpi
+        ->inter_singleref_comp_mode_cost[mode_context]
+                                        [INTER_SINGLEREF_COMP_OFFSET(mode)];
+#endif  // CONFIG_COMPOUND_SINGLEREF
   }
 #endif
 
@@ -5052,8 +5108,13 @@
   int segment_yrate;
   PREDICTION_MODE modes[4];
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  SEG_RDSTAT rdstat[4][INTER_MODES + INTER_SINGLEREF_COMP_MODES +
+                       INTER_COMPOUND_MODES];
+#else   // !CONFIG_COMPOUND_SINGLEREF
   SEG_RDSTAT rdstat[4][INTER_MODES + INTER_COMPOUND_MODES];
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
   SEG_RDSTAT rdstat[4][INTER_MODES];
 #endif  // CONFIG_EXT_INTER
   int mvthresh;
@@ -5158,8 +5219,11 @@
 }
 
 static void joint_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
-                                BLOCK_SIZE bsize, int_mv *frame_mv, int mi_row,
-                                int mi_col,
+                                BLOCK_SIZE bsize, int_mv *frame_mv,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                int_mv *frame_comp_mv,
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                int mi_row, int mi_col,
 #if CONFIG_EXT_INTER
                                 int_mv *ref_mv_sub8x8[2], const uint8_t *mask,
                                 int mask_stride,
@@ -5170,9 +5234,20 @@
   const int ph = block_size_high[bsize];
   MACROBLOCKD *xd = &x->e_mbd;
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
-  // This function should only ever be called for compound modes
+// This function should only ever be called for compound modes
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi)) {
+    assert(is_inter_singleref_comp_mode(mbmi->mode));
+    assert(frame_comp_mv);
+  }
+  assert(has_second_ref(mbmi) || is_inter_singleref_comp_mode(mbmi->mode));
+  const int refs[2] = { mbmi->ref_frame[0], has_second_ref(mbmi)
+                                                ? mbmi->ref_frame[1]
+                                                : mbmi->ref_frame[0] };
+#else
   assert(has_second_ref(mbmi));
   const int refs[2] = { mbmi->ref_frame[0], mbmi->ref_frame[1] };
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   int_mv ref_mv[2];
   int ite, ref;
   struct scale_factors sf;
@@ -5185,11 +5260,18 @@
   const int p_row = ((mi_row * MI_SIZE) >> pd->subsampling_y) + 4 * ir;
 #if CONFIG_GLOBAL_MOTION
   int is_global[2];
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  for (ref = 0; ref < 1 + has_second_ref(mbmi); ++ref) {
+#else
   for (ref = 0; ref < 2; ++ref) {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     WarpedMotionParams *const wm =
         &xd->global_motion[xd->mi[0]->mbmi.ref_frame[ref]];
     is_global[ref] = is_global_mv_block(xd->mi[0], block, wm->wmtype);
   }
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi)) is_global[1] = is_global[0];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_GLOBAL_MOTION
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 
@@ -5213,7 +5295,11 @@
   (void)ref_mv_sub8x8;
 #endif  // CONFIG_EXT_INTER && CONFIG_CB4X4
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  for (ref = 0; ref < 1 + has_second_ref(mbmi); ++ref) {
+#else
   for (ref = 0; ref < 2; ++ref) {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_EXT_INTER && !CONFIG_CB4X4
     if (bsize < BLOCK_8X8 && ref_mv_sub8x8 != NULL)
       ref_mv[ref].as_int = ref_mv_sub8x8[ref]->as_int;
@@ -5233,6 +5319,24 @@
     }
   }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi)) {
+    assert(is_inter_singleref_comp_mode(mbmi->mode));
+    // NOTE: For single ref comp mode, set up the 2nd set of ref_mv/pre_planes
+    //       all from the 1st reference frame, i.e. refs[0].
+    ref_mv[1] = x->mbmi_ext->ref_mvs[refs[0]][0];
+    if (scaled_ref_frame[0]) {
+      int i;
+      // Swap out the reference frame for a version that's been scaled to
+      // match the resolution of the current frame, allowing the existing
+      // motion search code to be used without additional modifications.
+      for (i = 0; i < MAX_MB_PLANE; i++)
+        backup_yv12[1][i] = xd->plane[i].pre[1];
+      av1_setup_pre_planes(xd, 1, scaled_ref_frame[0], mi_row, mi_col, NULL);
+    }
+  }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 // Since we have scaled the reference frames to match the size of the current
 // frame we must use a unit scaling factor during mode selection.
 #if CONFIG_HIGHBITDEPTH
@@ -5243,9 +5347,16 @@
                                     cm->height);
 #endif  // CONFIG_HIGHBITDEPTH
 
-  // Allow joint search multiple times iteratively for each reference frame
-  // and break out of the search loop if it couldn't find a better mv.
+// Allow joint search multiple times iteratively for each reference frame
+// and break out of the search loop if it couldn't find a better mv.
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  const int num_ites =
+      (has_second_ref(mbmi) || mbmi->mode == SR_NEW_NEWMV) ? 4 : 1;
+  const int start_ite = has_second_ref(mbmi) ? 0 : 1;
+  for (ite = start_ite; ite < (start_ite + num_ites); ite++) {
+#else
   for (ite = 0; ite < 4; ite++) {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     struct buf_2d ref_yv12[2];
     int bestsme = INT_MAX;
     int sadpb = x->sadperbit16;
@@ -5273,12 +5384,23 @@
     ref_yv12[1] = xd->plane[plane].pre[1];
 
 // Get the prediction block from the 'other' reference frame.
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    MV *const the_other_mv = (has_second_ref(mbmi) || id)
+                                 ? &frame_mv[refs[!id]].as_mv
+                                 : &frame_comp_mv[refs[0]].as_mv;
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_HIGHBITDEPTH
     if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
       second_pred = CONVERT_TO_BYTEPTR(second_pred_alloc_16);
       av1_highbd_build_inter_predictor(
           ref_yv12[!id].buf, ref_yv12[!id].stride, second_pred, pw,
-          &frame_mv[refs[!id]].as_mv, &sf, pw, ph, 0, mbmi->interp_filter,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          the_other_mv,
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
+          &frame_mv[refs[!id]].as_mv,
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          &sf, pw, ph, 0, mbmi->interp_filter,
 #if CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
           &warp_types, p_col, p_row,
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
@@ -5286,14 +5408,18 @@
     } else {
       second_pred = (uint8_t *)second_pred_alloc_16;
 #endif  // CONFIG_HIGHBITDEPTH
-      av1_build_inter_predictor(ref_yv12[!id].buf, ref_yv12[!id].stride,
-                                second_pred, pw, &frame_mv[refs[!id]].as_mv,
-                                &sf, pw, ph, &conv_params, mbmi->interp_filter,
+      av1_build_inter_predictor(
+          ref_yv12[!id].buf, ref_yv12[!id].stride, second_pred, pw,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          the_other_mv,
+#else   // !(CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF)
+        &frame_mv[refs[!id]].as_mv,
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          &sf, pw, ph, &conv_params, mbmi->interp_filter,
 #if CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
-                                &warp_types, p_col, p_row, plane, !id,
+          &warp_types, p_col, p_row, plane, !id,
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
-                                MV_PRECISION_Q3, mi_col * MI_SIZE,
-                                mi_row * MI_SIZE, xd);
+          MV_PRECISION_Q3, mi_col * MI_SIZE, mi_row * MI_SIZE, xd);
 #if CONFIG_HIGHBITDEPTH
     }
 #endif  // CONFIG_HIGHBITDEPTH
@@ -5302,13 +5428,24 @@
     if (id) xd->plane[plane].pre[0] = ref_yv12[id];
     av1_set_mv_search_range(&x->mv_limits, &ref_mv[id].as_mv);
 
-    // Use the mv result from the single mode as mv predictor.
-    *best_mv = frame_mv[refs[id]].as_mv;
+// Use the mv result from the single mode as mv predictor.
+// Use the mv result from the single mode as mv predictor.
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (!has_second_ref(mbmi) && id)
+      *best_mv = frame_comp_mv[refs[0]].as_mv;
+    else
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      *best_mv = frame_mv[refs[id]].as_mv;
 
     best_mv->col >>= 3;
     best_mv->row >>= 3;
 
-    av1_set_mvcost(x, refs[id], id, mbmi->ref_mv_idx);
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (!has_second_ref(mbmi))
+      av1_set_mvcost(x, refs[0], 0, mbmi->ref_mv_idx);
+    else
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      av1_set_mvcost(x, refs[id], id, mbmi->ref_mv_idx);
 
     // Small-range full-pixel motion search.
     bestsme =
@@ -5386,8 +5523,18 @@
     if (id) xd->plane[plane].pre[0] = ref_yv12[0];
 
     if (bestsme < last_besterr[id]) {
-      frame_mv[refs[id]].as_mv = *best_mv;
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      // NOTE: For single ref comp mode, frame_mv stores the first mv and
+      //       frame_comp_mv stores the second mv.
+      if (!has_second_ref(mbmi) && id)
+        frame_comp_mv[refs[0]].as_mv = *best_mv;
+      else
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+        frame_mv[refs[id]].as_mv = *best_mv;
       last_besterr[id] = bestsme;
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      if (!has_second_ref(mbmi)) last_besterr[!id] = last_besterr[id];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     } else {
       break;
     }
@@ -5395,27 +5542,67 @@
 
   *rate_mv = 0;
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  for (ref = 0; ref < 1 + has_second_ref(mbmi); ++ref) {
+#else
   for (ref = 0; ref < 2; ++ref) {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
     if (scaled_ref_frame[ref]) {
       // Restore the prediction frame pointers to their unscaled versions.
       int i;
       for (i = 0; i < MAX_MB_PLANE; i++)
         xd->plane[i].pre[ref] = backup_yv12[ref][i];
     }
-    av1_set_mvcost(x, refs[ref], ref, mbmi->ref_mv_idx);
-#if CONFIG_EXT_INTER && !CONFIG_CB4X4
-    if (bsize >= BLOCK_8X8)
-#endif  // CONFIG_EXT_INTER && !CONFIG_CB4X4
-      *rate_mv += av1_mv_bit_cost(&frame_mv[refs[ref]].as_mv,
-                                  &x->mbmi_ext->ref_mvs[refs[ref]][0].as_mv,
-                                  x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
-#if CONFIG_EXT_INTER && !CONFIG_CB4X4
+
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (!has_second_ref(mbmi))
+      av1_set_mvcost(x, refs[0], 0, mbmi->ref_mv_idx);
     else
-      *rate_mv += av1_mv_bit_cost(&frame_mv[refs[ref]].as_mv,
-                                  &ref_mv_sub8x8[ref]->as_mv, x->nmvjointcost,
-                                  x->mvcost, MV_COST_WEIGHT);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+      av1_set_mvcost(x, refs[ref], ref, mbmi->ref_mv_idx);
+
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    if (!has_second_ref(mbmi)) {
+      // NOTE: For single ref comp mode, i.e. !has_second_ref(mbmi) is true, the
+      //       first mv is stored in frame_mv[] and the second mv is stored in
+      //       frame_comp_mv[].
+      if (compound_ref0_mode(mbmi->mode) == NEWMV)  // SR_NEW_NEWMV
+        *rate_mv += av1_mv_bit_cost(&frame_mv[refs[0]].as_mv,
+                                    &x->mbmi_ext->ref_mvs[refs[0]][0].as_mv,
+                                    x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
+      assert(compound_ref1_mode(mbmi->mode) == NEWMV);
+      *rate_mv += av1_mv_bit_cost(&frame_comp_mv[refs[0]].as_mv,
+                                  &x->mbmi_ext->ref_mvs[refs[0]][0].as_mv,
+                                  x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
+    } else {
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+#if CONFIG_EXT_INTER && !CONFIG_CB4X4
+      if (bsize >= BLOCK_8X8)
 #endif  // CONFIG_EXT_INTER && !CONFIG_CB4X4
+        *rate_mv += av1_mv_bit_cost(&frame_mv[refs[ref]].as_mv,
+                                    &x->mbmi_ext->ref_mvs[refs[ref]][0].as_mv,
+                                    x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
+#if CONFIG_EXT_INTER && !CONFIG_CB4X4
+      else
+        *rate_mv += av1_mv_bit_cost(&frame_mv[refs[ref]].as_mv,
+                                    &ref_mv_sub8x8[ref]->as_mv, x->nmvjointcost,
+                                    x->mvcost, MV_COST_WEIGHT);
+#endif  // CONFIG_EXT_INTER && !CONFIG_CB4X4
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   }
+
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi)) {
+    if (scaled_ref_frame[0]) {
+      // Restore the prediction frame pointers to their unscaled versions.
+      int i;
+      for (i = 0; i < MAX_MB_PLANE; i++)
+        xd->plane[i].pre[1] = backup_yv12[1][i];
+    }
+  }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 }
 
 static void estimate_ref_frame_costs(const AV1_COMMON *cm,
@@ -5699,8 +5886,13 @@
   int sadpb = x->sadperbit16;
   MV mvp_full;
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  int ref =
+      has_second_ref(mbmi) ? mbmi->ref_frame[ref_idx] : mbmi->ref_frame[0];
+#else   // !CONFIG_COMPOUND_SINGLEREF
   int ref = mbmi->ref_frame[ref_idx];
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
   int ref = mbmi->ref_frame[0];
   int ref_idx = 0;
 #endif  // CONFIG_EXT_INTER
@@ -5942,7 +6134,12 @@
   const int ph = block_size_high[bsize];
   MACROBLOCKD *xd = &x->e_mbd;
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
+#if CONFIG_COMPOUND_SINGLEREF
+  const int other_ref =
+      has_second_ref(mbmi) ? mbmi->ref_frame[!ref_idx] : mbmi->ref_frame[0];
+#else  // !CONFIG_COMPOUND_SINGLEREF
   const int other_ref = mbmi->ref_frame[!ref_idx];
+#endif  // CONFIG_COMPOUND_SINGLEREF
   struct scale_factors sf;
 #if CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
   struct macroblockd_plane *const pd = &xd->plane[0];
@@ -5959,8 +6156,12 @@
   (void)block;
 #endif  // CONFIG_GLOBAL_MOTION || CONFIG_WARPED_MOTION
 
-  // This function should only ever be called for compound modes
+// This function should only ever be called for compound modes
+#if CONFIG_COMPOUND_SINGLEREF
+  assert(has_second_ref(mbmi) || is_inter_singleref_comp_mode(mbmi->mode));
+#else   // !CONFIG_COMPOUND_SINGLEREF
   assert(has_second_ref(mbmi));
+#endif  // CONFIG_COMPOUND_SINGLEREF
 
   struct buf_2d backup_yv12[MAX_MB_PLANE];
   const YV12_BUFFER_CONFIG *const scaled_ref_frame =
@@ -6044,7 +6245,12 @@
   const int ph = block_size_high[bsize];
   MACROBLOCKD *xd = &x->e_mbd;
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
+#if CONFIG_COMPOUND_SINGLEREF
+  const int ref =
+      has_second_ref(mbmi) ? mbmi->ref_frame[ref_idx] : mbmi->ref_frame[0];
+#else
   const int ref = mbmi->ref_frame[ref_idx];
+#endif  // CONFIG_COMPOUND_SINGLEREF
   int_mv ref_mv = x->mbmi_ext->ref_mvs[ref][0];
   struct macroblockd_plane *const pd = &xd->plane[0];
 
@@ -6054,6 +6260,10 @@
 
   // Check that this is either an interinter or an interintra block
   assert(has_second_ref(mbmi) ||
+#if CONFIG_COMPOUND_SINGLEREF
+         // or a single ref comp pred mode
+         is_inter_singleref_comp_mode(mbmi->mode) ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
          (ref_idx == 0 && mbmi->ref_frame[1] == INTRA_FRAME));
 
   if (scaled_ref_frame) {
@@ -6089,7 +6299,12 @@
   best_mv->col >>= 3;
   best_mv->row >>= 3;
 
-  av1_set_mvcost(x, ref, ref_idx, mbmi->ref_mv_idx);
+#if CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi))
+    av1_set_mvcost(x, ref, 0, mbmi->ref_mv_idx);
+  else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+    av1_set_mvcost(x, ref, ref_idx, mbmi->ref_mv_idx);
 
   // Small-range full-pixel motion search.
   bestsme = av1_refining_search_8p_c(x, sadpb, search_range,
@@ -6163,7 +6378,12 @@
       xd->plane[i].pre[ref_idx] = backup_yv12[i];
   }
 
-  av1_set_mvcost(x, ref, ref_idx, mbmi->ref_mv_idx);
+#if CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi))
+    av1_set_mvcost(x, ref, 0, mbmi->ref_mv_idx);
+  else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+    av1_set_mvcost(x, ref, ref_idx, mbmi->ref_mv_idx);
   *rate_mv += av1_mv_bit_cost(this_mv, &ref_mv.as_mv, x->nmvjointcost,
                               x->mvcost, MV_COST_WEIGHT);
 }
@@ -6172,13 +6392,23 @@
 // where the second prediction is also an inter mode.
 static void compound_single_motion_search_interinter(
     const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize, int_mv *frame_mv,
+#if CONFIG_COMPOUND_SINGLEREF
+    int_mv *frame_comp_mv,
+#endif  // CONFIG_COMPOUND_SINGLEREF
     int mi_row, int mi_col, const uint8_t *mask, int mask_stride, int *rate_mv,
     const int block, int ref_idx) {
   MACROBLOCKD *xd = &x->e_mbd;
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
 
-  // This function should only ever be called for compound modes
+// This function should only ever be called for compound modes
+#if CONFIG_COMPOUND_SINGLEREF
+  int is_singleref_comp_mode =
+      !has_second_ref(mbmi) && is_inter_singleref_comp_mode(mbmi->mode);
+  assert(has_second_ref(mbmi) || is_singleref_comp_mode);
+  if (is_singleref_comp_mode && ref_idx) assert(frame_comp_mv);
+#else   // !CONFIG_COMPOUND_SINGLEREF
   assert(has_second_ref(mbmi));
+#endif  // CONFIG_COMPOUND_SINGLEREF
 
 // Prediction buffer from second frame.
 #if CONFIG_HIGHBITDEPTH
@@ -6192,8 +6422,20 @@
   DECLARE_ALIGNED(16, uint8_t, second_pred[MAX_SB_SQUARE]);
 #endif  // CONFIG_HIGHBITDEPTH
 
+#if CONFIG_COMPOUND_SINGLEREF
+  MV *this_mv = has_second_ref(mbmi)
+                    ? &frame_mv[mbmi->ref_frame[ref_idx]].as_mv
+                    : (ref_idx ? &frame_comp_mv[mbmi->ref_frame[0]].as_mv
+                               : &frame_mv[mbmi->ref_frame[0]].as_mv);
+  const MV *other_mv =
+      has_second_ref(mbmi)
+          ? &frame_mv[mbmi->ref_frame[!ref_idx]].as_mv
+          : (ref_idx ? &frame_mv[mbmi->ref_frame[0]].as_mv
+                     : &frame_comp_mv[mbmi->ref_frame[0]].as_mv);
+#else   // !CONFIG_COMPOUND_SINGLEREF
   MV *this_mv = &frame_mv[mbmi->ref_frame[ref_idx]].as_mv;
   const MV *other_mv = &frame_mv[mbmi->ref_frame[!ref_idx]].as_mv;
+#endif  // CONFIG_COMPOUND_SINGLEREF
 
   build_second_inter_pred(cpi, x, bsize, other_mv, mi_row, mi_col, block,
                           ref_idx, second_pred);
@@ -6218,21 +6460,40 @@
   mask = av1_get_compound_type_mask(comp_data, sb_type);
 
   int_mv frame_mv[TOTAL_REFS_PER_FRAME];
+#if CONFIG_COMPOUND_SINGLEREF
+  int_mv frame_comp_mv[TOTAL_REFS_PER_FRAME];
+#endif  // CONFIG_COMPOUND_SINGLEREF
   MV_REFERENCE_FRAME rf[2] = { mbmi->ref_frame[0], mbmi->ref_frame[1] };
   assert(bsize >= BLOCK_8X8 || CONFIG_CB4X4);
 
   frame_mv[rf[0]].as_int = cur_mv[0].as_int;
-  frame_mv[rf[1]].as_int = cur_mv[1].as_int;
+#if CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi))
+    frame_comp_mv[rf[0]].as_int = cur_mv[1].as_int;
+  else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+    frame_mv[rf[1]].as_int = cur_mv[1].as_int;
   if (which == 0 || which == 1) {
-    compound_single_motion_search_interinter(cpi, x, bsize, frame_mv, mi_row,
-                                             mi_col, mask, mask_stride, rate_mv,
-                                             0, which);
+    compound_single_motion_search_interinter(
+        cpi, x, bsize, frame_mv,
+#if CONFIG_COMPOUND_SINGLEREF
+        has_second_ref(mbmi) ? NULL : frame_comp_mv,
+#endif  // CONFIG_COMPOUND_SINGLEREF
+        mi_row, mi_col, mask, mask_stride, rate_mv, 0, which);
   } else if (which == 2) {
-    joint_motion_search(cpi, x, bsize, frame_mv, mi_row, mi_col, NULL, mask,
-                        mask_stride, rate_mv, 0);
+    joint_motion_search(cpi, x, bsize, frame_mv,
+#if CONFIG_COMPOUND_SINGLEREF
+                        has_second_ref(mbmi) ? NULL : frame_comp_mv,
+#endif  // CONFIG_COMPOUND_SINGLEREF
+                        mi_row, mi_col, NULL, mask, mask_stride, rate_mv, 0);
   }
   tmp_mv[0].as_int = frame_mv[rf[0]].as_int;
-  tmp_mv[1].as_int = frame_mv[rf[1]].as_int;
+#if CONFIG_COMPOUND_SINGLEREF
+  if (!has_second_ref(mbmi))
+    tmp_mv[1].as_int = frame_comp_mv[rf[0]].as_int;
+  else  // comp ref
+#endif  // CONFIG_COMPOUND_SINGLEREF
+    tmp_mv[1].as_int = frame_mv[rf[1]].as_int;
 }
 #endif  // CONFIG_COMPOUND_SEGMENT || CONFIG_WEDGE
 #endif  // CONFIG_EXT_INTER
@@ -6727,7 +6988,17 @@
 #endif  // CONFIG_COMPOUND_SEGMENT
     mbmi->interinter_compound_type
   };
-  if (this_mode == NEW_NEWMV) {
+#if CONFIG_COMPOUND_SINGLEREF
+  // NOTE: Mode is needed to identify the compound mode prediction, regardless
+  //       of comp refs or single ref.
+  mbmi->mode = this_mode;
+#endif  // CONFIG_COMPOUND_SINGLEREF
+
+  if (this_mode == NEW_NEWMV
+#if CONFIG_COMPOUND_SINGLEREF
+      || this_mode == SR_NEW_NEWMV
+#endif  // CONFIG_COMPOUND_SINGLEREF
+      ) {
     do_masked_motion_search_indexed(cpi, x, cur_mv, &compound_data, bsize,
                                     mi_row, mi_col, tmp_mv, &tmp_rate_mv, 2);
     mbmi->mv[0].as_int = tmp_mv[0].as_int;
@@ -6736,7 +7007,12 @@
     do_masked_motion_search_indexed(cpi, x, cur_mv, &compound_data, bsize,
                                     mi_row, mi_col, tmp_mv, &tmp_rate_mv, 0);
     mbmi->mv[0].as_int = tmp_mv[0].as_int;
-  } else if (this_mode == NEAREST_NEWMV || this_mode == NEAR_NEWMV) {
+  } else if (this_mode == NEAREST_NEWMV || this_mode == NEAR_NEWMV
+#if CONFIG_COMPOUND_SINGLEREF
+             // || this_mode == SR_NEAREST_NEWMV
+             || this_mode == SR_NEAR_NEWMV || this_mode == SR_ZERO_NEWMV
+#endif  // CONFIG_COMPOUND_SINGLEREF
+             ) {
     do_masked_motion_search_indexed(cpi, x, cur_mv, &compound_data, bsize,
                                     mi_row, mi_col, tmp_mv, &tmp_rate_mv, 1);
     mbmi->mv[1].as_int = tmp_mv[1].as_int;
@@ -6830,6 +7106,9 @@
 static int64_t handle_newmv(const AV1_COMP *const cpi, MACROBLOCK *const x,
                             const BLOCK_SIZE bsize,
                             int_mv (*const mode_mv)[TOTAL_REFS_PER_FRAME],
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                            int_mv (*const mode_comp_mv)[TOTAL_REFS_PER_FRAME],
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
                             const int mi_row, const int mi_col,
                             int *const rate_mv, int_mv *const single_newmv,
                             HandleInterModeArgs *const args) {
@@ -6842,6 +7121,9 @@
   const int is_comp_interintra_pred = (mbmi->ref_frame[1] == INTRA_FRAME);
 #endif  // CONFIG_EXT_INTER
   int_mv *const frame_mv = mode_mv[this_mode];
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  int_mv *const frame_comp_mv = mode_comp_mv[this_mode];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   const int refs[2] = { mbmi->ref_frame[0],
                         mbmi->ref_frame[1] < 0 ? 0 : mbmi->ref_frame[1] };
   int i;
@@ -6859,8 +7141,11 @@
       frame_mv[refs[1]].as_int = single_newmv[refs[1]].as_int;
 
       if (cpi->sf.comp_inter_joint_search_thresh <= bsize) {
-        joint_motion_search(cpi, x, bsize, frame_mv, mi_row, mi_col, NULL, NULL,
-                            0, rate_mv, 0);
+        joint_motion_search(cpi, x, bsize, frame_mv,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                            NULL,  // int_mv *frame_comp_mv
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                            mi_row, mi_col, NULL, NULL, 0, rate_mv, 0);
       } else {
         *rate_mv = 0;
         for (i = 0; i < 2; ++i) {
@@ -6875,8 +7160,12 @@
       if (cpi->sf.comp_inter_joint_search_thresh <= bsize) {
         frame_mv[refs[0]].as_int =
             mode_mv[compound_ref0_mode(this_mode)][refs[0]].as_int;
-        compound_single_motion_search_interinter(
-            cpi, x, bsize, frame_mv, mi_row, mi_col, NULL, 0, rate_mv, 0, 1);
+        compound_single_motion_search_interinter(cpi, x, bsize, frame_mv,
+#if CONFIG_COMPOUND_SINGLEREF
+                                                 NULL,
+#endif  // CONFIG_COMPOUND_SINGLEREF
+                                                 mi_row, mi_col, NULL, 0,
+                                                 rate_mv, 0, 1);
       } else {
         av1_set_mvcost(x, refs[1], 1, mbmi->ref_mv_idx);
         *rate_mv = av1_mv_bit_cost(&frame_mv[refs[1]].as_mv,
@@ -6889,8 +7178,12 @@
       if (cpi->sf.comp_inter_joint_search_thresh <= bsize) {
         frame_mv[refs[1]].as_int =
             mode_mv[compound_ref1_mode(this_mode)][refs[1]].as_int;
-        compound_single_motion_search_interinter(
-            cpi, x, bsize, frame_mv, mi_row, mi_col, NULL, 0, rate_mv, 0, 0);
+        compound_single_motion_search_interinter(cpi, x, bsize, frame_mv,
+#if CONFIG_COMPOUND_SINGLEREF
+                                                 NULL,
+#endif  // CONFIG_COMPOUND_SINGLEREF
+                                                 mi_row, mi_col, NULL, 0,
+                                                 rate_mv, 0, 0);
       } else {
         av1_set_mvcost(x, refs[0], 0, mbmi->ref_mv_idx);
         *rate_mv = av1_mv_bit_cost(&frame_mv[refs[0]].as_mv,
@@ -6898,7 +7191,7 @@
                                    x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
       }
     }
-#else
+#else   // !CONFIG_EXT_INTER
     // Initialize mv using single prediction mode result.
     frame_mv[refs[0]].as_int = single_newmv[refs[0]].as_int;
     frame_mv[refs[1]].as_int = single_newmv[refs[1]].as_int;
@@ -6915,6 +7208,41 @@
       }
     }
 #endif  // CONFIG_EXT_INTER
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  } else if (is_inter_singleref_comp_mode(this_mode)) {
+    // Single ref comp mode
+    const int mode0 = compound_ref0_mode(this_mode);
+
+    single_newmv[refs[0]].as_int = args->single_newmv[refs[0]].as_int;
+    frame_mv[refs[0]].as_int = (mode0 == NEWMV)
+                                   ? single_newmv[refs[0]].as_int
+                                   : mode_mv[mode0][refs[0]].as_int;
+    assert(compound_ref1_mode(this_mode) == NEWMV);
+    frame_comp_mv[refs[0]].as_int = single_newmv[refs[0]].as_int;
+
+    if (cpi->sf.comp_inter_joint_search_thresh <= bsize) {
+      if (this_mode == SR_NEW_NEWMV) {
+        joint_motion_search(cpi, x, bsize, frame_mv, frame_comp_mv, mi_row,
+                            mi_col, NULL, NULL, 0, rate_mv, 0);
+      } else {
+        assert(  // this_mode == SR_NEAREST_NEWMV ||
+            this_mode == SR_NEAR_NEWMV || this_mode == SR_ZERO_NEWMV);
+        compound_single_motion_search_interinter(cpi, x, bsize, frame_mv,
+                                                 frame_comp_mv, mi_row, mi_col,
+                                                 NULL, 0, rate_mv, 0, 1);
+      }
+    } else {
+      *rate_mv = 0;
+      av1_set_mvcost(x, refs[0], 0, mbmi->ref_mv_idx);
+      if (mode0 == NEWMV)
+        *rate_mv += av1_mv_bit_cost(&frame_mv[refs[0]].as_mv,
+                                    &mbmi_ext->ref_mvs[refs[0]][0].as_mv,
+                                    x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
+      *rate_mv += av1_mv_bit_cost(&frame_comp_mv[refs[0]].as_mv,
+                                  &mbmi_ext->ref_mvs[refs[0]][0].as_mv,
+                                  x->nmvjointcost, x->mvcost, MV_COST_WEIGHT);
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   } else {
 #if CONFIG_EXT_INTER
     if (is_comp_interintra_pred) {
@@ -7153,7 +7481,11 @@
       *mbmi = *best_bmc_mbmi;
       mbmi->motion_mode = OBMC_CAUSAL;
 #endif  // CONFIG_EXT_INTER
-      if (!is_comp_pred && have_newmv_in_inter_mode(this_mode)) {
+      if (!is_comp_pred &&
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          !is_inter_singleref_comp_mode(this_mode) &&
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+          have_newmv_in_inter_mode(this_mode)) {
         int tmp_rate_mv = 0;
 
         single_motion_search(cpi, x, bsize, mi_row, mi_col,
@@ -7464,11 +7796,17 @@
   return 0;
 }
 
-static int64_t handle_inter_mode(
-    const AV1_COMP *const cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
-    RD_STATS *rd_stats, RD_STATS *rd_stats_y, RD_STATS *rd_stats_uv,
-    int *disable_skip, int_mv (*mode_mv)[TOTAL_REFS_PER_FRAME], int mi_row,
-    int mi_col, HandleInterModeArgs *args, const int64_t ref_best_rd) {
+static int64_t handle_inter_mode(const AV1_COMP *const cpi, MACROBLOCK *x,
+                                 BLOCK_SIZE bsize, RD_STATS *rd_stats,
+                                 RD_STATS *rd_stats_y, RD_STATS *rd_stats_uv,
+                                 int *disable_skip,
+                                 int_mv (*mode_mv)[TOTAL_REFS_PER_FRAME],
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                 int_mv (*mode_comp_mv)[TOTAL_REFS_PER_FRAME],
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                 int mi_row, int mi_col,
+                                 HandleInterModeArgs *args,
+                                 const int64_t ref_best_rd) {
   const AV1_COMMON *cm = &cpi->common;
   (void)cm;
   MACROBLOCKD *xd = &x->e_mbd;
@@ -7477,7 +7815,14 @@
   MB_MODE_INFO_EXT *const mbmi_ext = x->mbmi_ext;
   const int is_comp_pred = has_second_ref(mbmi);
   const int this_mode = mbmi->mode;
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  const int is_singleref_comp_mode = is_inter_singleref_comp_mode(this_mode);
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   int_mv *frame_mv = mode_mv[this_mode];
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  // The comp mv for the compound mode in single ref
+  int_mv *frame_comp_mv = mode_comp_mv[this_mode];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   int i;
   int refs[2] = { mbmi->ref_frame[0],
                   (mbmi->ref_frame[1] < 0 ? 0 : mbmi->ref_frame[1]) };
@@ -7544,7 +7889,11 @@
 #endif  // CONFIG_EXT_INTER
 
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+  if (is_comp_pred || is_singleref_comp_mode)
+#else   // !CONFIG_COMPOUND_SINGLEREF
   if (is_comp_pred)
+#endif  // CONFIG_COMPOUND_SINGLEREF
     mode_ctx = mbmi_ext->compound_mode_context[refs[0]];
   else
 #endif  // CONFIG_EXT_INTER
@@ -7570,12 +7919,22 @@
     if (frame_mv[refs[0]].as_int == INVALID_MV ||
         frame_mv[refs[1]].as_int == INVALID_MV)
       return INT64_MAX;
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  } else if (is_singleref_comp_mode) {
+    if (frame_mv[refs[0]].as_int == INVALID_MV ||
+        frame_comp_mv[refs[0]].as_int == INVALID_MV)
+      return INT64_MAX;
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   }
 
   mbmi->motion_mode = SIMPLE_TRANSLATION;
   if (have_newmv_in_inter_mode(this_mode)) {
-    const int64_t ret_val = handle_newmv(cpi, x, bsize, mode_mv, mi_row, mi_col,
-                                         &rate_mv, single_newmv, args);
+    const int64_t ret_val =
+        handle_newmv(cpi, x, bsize, mode_mv,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                     mode_comp_mv,
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                     mi_row, mi_col, &rate_mv, single_newmv, args);
     if (ret_val != 0)
       return ret_val;
     else
@@ -7589,6 +7948,16 @@
     mbmi->mv[i].as_int = cur_mv[i].as_int;
   }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  if (!is_comp_pred && is_singleref_comp_mode) {
+    cur_mv[1] = frame_comp_mv[refs[0]];
+    // Clip "next_nearest" so that it does not extend to far out of image
+    if (this_mode != NEWMV) clamp_mv2(&cur_mv[1].as_mv, xd);
+    if (mv_check_bounds(&x->mv_limits, &cur_mv[1].as_mv)) return INT64_MAX;
+    mbmi->mv[1].as_int = cur_mv[1].as_int;
+  }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_EXT_INTER
   if (this_mode == NEAREST_NEARESTMV)
 #else
@@ -7612,7 +7981,12 @@
 
 #if CONFIG_EXT_INTER
   if (mbmi_ext->ref_mv_count[ref_frame_type] > 0) {
+#if CONFIG_COMPOUND_SINGLEREF
+    if (this_mode == NEAREST_NEWMV ||  // this_mode == SR_NEAREST_NEWMV ||
+        this_mode == SR_NEAREST_NEARMV) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
     if (this_mode == NEAREST_NEWMV) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
       cur_mv[0] = mbmi_ext->ref_mv_stack[ref_frame_type][0].this_mv;
 
       lower_mv_precision(&cur_mv[0].as_mv, cm->allow_high_precision_mv);
@@ -7633,7 +8007,11 @@
 
   if (mbmi_ext->ref_mv_count[ref_frame_type] > 1) {
     int ref_mv_idx = mbmi->ref_mv_idx + 1;
-    if (this_mode == NEAR_NEWMV || this_mode == NEAR_NEARMV) {
+    if (this_mode == NEAR_NEWMV ||
+#if CONFIG_COMPOUND_SINGLEREF
+        this_mode == SR_NEAR_NEWMV ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
+        this_mode == NEAR_NEARMV) {
       cur_mv[0] = mbmi_ext->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
 
       lower_mv_precision(&cur_mv[0].as_mv, cm->allow_high_precision_mv);
@@ -7642,8 +8020,17 @@
       mbmi->mv[0].as_int = cur_mv[0].as_int;
     }
 
-    if (this_mode == NEW_NEARMV || this_mode == NEAR_NEARMV) {
-      cur_mv[1] = mbmi_ext->ref_mv_stack[ref_frame_type][ref_mv_idx].comp_mv;
+    if (this_mode == NEW_NEARMV ||
+#if CONFIG_COMPOUND_SINGLEREF
+        this_mode == SR_NEAREST_NEARMV ||
+#endif  // CONFIG_COMPOUND_SINGLEREF
+        this_mode == NEAR_NEARMV) {
+#if CONFIG_COMPOUND_SINGLEREF
+      if (this_mode == SR_NEAREST_NEARMV)
+        cur_mv[1] = mbmi_ext->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
+      else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+        cur_mv[1] = mbmi_ext->ref_mv_stack[ref_frame_type][ref_mv_idx].comp_mv;
 
       lower_mv_precision(&cur_mv[1].as_mv, cm->allow_high_precision_mv);
       clamp_mv2(&cur_mv[1].as_mv, xd);
@@ -7651,7 +8038,7 @@
       mbmi->mv[1].as_int = cur_mv[1].as_int;
     }
   }
-#else
+#else   // !CONFIG_EXT_INTER
   if (this_mode == NEARMV && is_comp_pred) {
     uint8_t ref_frame_type = av1_ref_frame_type(mbmi->ref_frame);
     if (mbmi_ext->ref_mv_count[ref_frame_type] > 1) {
@@ -7729,7 +8116,11 @@
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
 
 #if CONFIG_WEDGE || CONFIG_COMPOUND_SEGMENT
+#if CONFIG_COMPOUND_SINGLEREF
+  if (is_comp_pred || is_singleref_comp_mode) {
+#else
   if (is_comp_pred) {
+#endif  // CONFIG_COMPOUND_SINGLEREF
     int rate_sum, rs2;
     int64_t dist_sum;
     int64_t best_rd_compound = INT64_MAX, best_rd_cur = INT64_MAX;
@@ -7759,6 +8150,17 @@
     best_compound_data.seg_mask = tmp_mask_buf;
 #endif  // CONFIG_COMPOUND_SEGMENT
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    // TODO(zoeliu): To further check whether the following setups are needed.
+    // Single ref compound mode: Prepare the 2nd ref frame predictor the same as
+    // the 1st one.
+    if (!is_comp_pred && is_singleref_comp_mode) {
+      xd->block_refs[1] = xd->block_refs[0];
+      for (i = 0; i < MAX_MB_PLANE; i++)
+        xd->plane[i].pre[1] = xd->plane[i].pre[0];
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
     if (masked_compound_used) {
       av1_cost_tokens(compound_type_cost, cm->fc->compound_type_prob[bsize],
                       av1_compound_type_tree);
@@ -8691,6 +9093,9 @@
   unsigned char segment_id = mbmi->segment_id;
   int comp_pred, i, k;
   int_mv frame_mv[MB_MODE_COUNT][TOTAL_REFS_PER_FRAME];
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+  int_mv frame_comp_mv[MB_MODE_COUNT][TOTAL_REFS_PER_FRAME];
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   struct buf_2d yv12_mb[TOTAL_REFS_PER_FRAME][MAX_MB_PLANE];
   int_mv single_newmv[TOTAL_REFS_PER_FRAME] = { { 0 } };
 #if CONFIG_EXT_INTER
@@ -8871,6 +9276,10 @@
 #endif  // CONFIG_GLOBAL_MOTION
 #if CONFIG_EXT_INTER
     frame_mv[NEW_NEWMV][ref_frame].as_int = INVALID_MV;
+#if CONFIG_COMPOUND_SINGLEREF
+    frame_mv[SR_NEW_NEWMV][ref_frame].as_int = INVALID_MV;
+    frame_comp_mv[SR_NEW_NEWMV][ref_frame].as_int = INVALID_MV;
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #if CONFIG_GLOBAL_MOTION
     frame_mv[ZERO_ZEROMV][ref_frame].as_int =
         gm_get_motion_vector(&cm->global_motion[ref_frame],
@@ -8998,6 +9407,12 @@
         mode_skip_mask[ALTREF_FRAME] |= (1 << NEAREST_NEARESTMV);
       if (frame_mv[NEAR_NEARMV][ALTREF_FRAME].as_int != zeromv.as_int)
         mode_skip_mask[ALTREF_FRAME] |= (1 << NEAR_NEARMV);
+#if CONFIG_COMPOUND_SINGLEREF
+      if (frame_mv[SR_NEAREST_NEARMV][ALTREF_FRAME].as_int != zeromv.as_int ||
+          frame_comp_mv[SR_NEAREST_NEARMV][ALTREF_FRAME].as_int !=
+              zeromv.as_int)
+        mode_skip_mask[ALTREF_FRAME] |= (1 << SR_NEAREST_NEARMV);
+#endif  // CONFIG_COMPOUND_SINGLEREF
 #endif  // CONFIG_EXT_INTER
     }
   }
@@ -9104,6 +9519,13 @@
           frame_mv[compound_ref0_mode(this_mode)][ref_frame].as_int;
       frame_mv[this_mode][second_ref_frame].as_int =
           frame_mv[compound_ref1_mode(this_mode)][second_ref_frame].as_int;
+#if CONFIG_COMPOUND_SINGLEREF
+    } else if (is_inter_singleref_comp_mode(this_mode)) {
+      frame_mv[this_mode][ref_frame].as_int =
+          frame_mv[compound_ref0_mode(this_mode)][ref_frame].as_int;
+      frame_comp_mv[this_mode][ref_frame].as_int =
+          frame_mv[compound_ref1_mode(this_mode)][ref_frame].as_int;
+#endif  // CONFIG_COMPOUND_SINGLEREF
     }
 #endif  // CONFIG_EXT_INTER
 
@@ -9265,6 +9687,15 @@
       if (comp_pred) xd->plane[i].pre[1] = yv12_mb[second_ref_frame][i];
     }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    // Single ref compound mode
+    if (!comp_pred && is_inter_singleref_comp_mode(mbmi->mode)) {
+      xd->block_refs[1] = xd->block_refs[0];
+      for (i = 0; i < MAX_MB_PLANE; i++)
+        xd->plane[i].pre[1] = xd->plane[i].pre[0];
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_EXT_INTER && CONFIG_INTERINTRA
     mbmi->interintra_mode = (INTERINTRA_MODE)(II_DC_PRED - 1);
 #endif  // CONFIG_EXT_INTER && CONFIG_INTERINTRA
@@ -9479,6 +9910,27 @@
             mbmi_ext->ref_mvs[mbmi->ref_frame[1]][0] = this_mv;
           }
         }
+#if CONFIG_COMPOUND_SINGLEREF
+      } else if (is_inter_singleref_comp_mode(mbmi->mode)) {
+        if (mbmi_ext->ref_mv_count[ref_frame_type] > 1) {
+          // TODO(zoeliu): To further investigate which ref_mv_idx should be
+          //               chosen for the mode of SR_NEAR_NEWMV.
+          int ref_mv_idx = 0;
+          // Special case: SR_NEAR_NEWMV mode use
+          // 1 + mbmi->ref_mv_idx (like NEARMV) instead of
+          // mbmi->ref_mv_idx (like NEWMV)
+          if (mbmi->mode == SR_NEAR_NEWMV) ref_mv_idx = 1;
+
+          if (compound_ref0_mode(mbmi->mode) == NEWMV ||
+              compound_ref1_mode(mbmi->mode) == NEWMV) {
+            int_mv this_mv =
+                mbmi_ext->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
+            clamp_mv_ref(&this_mv.as_mv, xd->n8_w << MI_SIZE_LOG2,
+                         xd->n8_h << MI_SIZE_LOG2, xd);
+            mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0] = this_mv;
+          }
+        }
+#endif  // CONFIG_COMPOUND_SINGLEREF
       } else {
 #endif  // CONFIG_EXT_INTER
         if (mbmi->mode == NEWMV && mbmi_ext->ref_mv_count[ref_frame_type] > 1) {
@@ -9521,6 +9973,9 @@
 #endif  // CONFIG_EXT_INTER
         this_rd = handle_inter_mode(cpi, x, bsize, &rd_stats, &rd_stats_y,
                                     &rd_stats_uv, &disable_skip, frame_mv,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                    frame_comp_mv,
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
                                     mi_row, mi_col, &args, best_rd);
 
         rate2 = rd_stats.rate;
@@ -9544,15 +9999,23 @@
 // TODO(jingning): This needs some refactoring to improve code quality
 // and reduce redundant steps.
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+      if ((have_nearmv_in_inter_mode(mbmi->mode) &&
+           mbmi_ext->ref_mv_count[ref_frame_type] > 2) ||
+          ((mbmi->mode == NEWMV || mbmi->mode == SR_NEW_NEWMV ||
+            mbmi->mode == NEW_NEWMV) &&
+           mbmi_ext->ref_mv_count[ref_frame_type] > 1)) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
       if ((have_nearmv_in_inter_mode(mbmi->mode) &&
            mbmi_ext->ref_mv_count[ref_frame_type] > 2) ||
           ((mbmi->mode == NEWMV || mbmi->mode == NEW_NEWMV) &&
            mbmi_ext->ref_mv_count[ref_frame_type] > 1)) {
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
       if ((mbmi->mode == NEARMV &&
            mbmi_ext->ref_mv_count[ref_frame_type] > 2) ||
           (mbmi->mode == NEWMV && mbmi_ext->ref_mv_count[ref_frame_type] > 1)) {
-#endif
+#endif  // CONFIG_EXT_INTER
         int_mv backup_mv = frame_mv[NEARMV][ref_frame];
         MB_MODE_INFO backup_mbmi = *mbmi;
         int backup_skip = x->skip;
@@ -9646,6 +10109,34 @@
                            xd->n8_h << MI_SIZE_LOG2, xd);
               mbmi_ext->ref_mvs[mbmi->ref_frame[1]][0] = this_mv;
             }
+#if CONFIG_COMPOUND_SINGLEREF
+          } else if (is_inter_singleref_comp_mode(mbmi->mode)) {
+            int ref_mv_idx = mbmi->ref_mv_idx;
+            // Special case: SR_NEAR_NEWMV mode use
+            // 1 + mbmi->ref_mv_idx (like NEARMV) instead of
+            // mbmi->ref_mv_idx (like NEWMV)
+            if (mbmi->mode == SR_NEAR_NEWMV) ref_mv_idx = 1 + mbmi->ref_mv_idx;
+
+            // TODO(zoeliu): For the mode of SR_NEAREST_NEWMV, as it only runs
+            //               the "if", not the "else if",
+            //               mbmi_ext->ref_mvs[mbmi->ref_frame[0]] takes the
+            //               value for "NEWMV", instead of "NEARESTMV".
+            if (compound_ref0_mode(mbmi->mode) == NEWMV ||
+                compound_ref1_mode(mbmi->mode) == NEWMV) {
+              int_mv this_mv =
+                  mbmi_ext->ref_mv_stack[ref_frame_type][ref_mv_idx].this_mv;
+              clamp_mv_ref(&this_mv.as_mv, xd->n8_w << MI_SIZE_LOG2,
+                           xd->n8_h << MI_SIZE_LOG2, xd);
+              mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0] = this_mv;
+            } else if (compound_ref0_mode(mbmi->mode) == NEARESTMV ||
+                       compound_ref1_mode(mbmi->mode) == NEARESTMV) {
+              int_mv this_mv =
+                  mbmi_ext->ref_mv_stack[ref_frame_type][0].this_mv;
+              clamp_mv_ref(&this_mv.as_mv, xd->n8_w << MI_SIZE_LOG2,
+                           xd->n8_h << MI_SIZE_LOG2, xd);
+              mbmi_ext->ref_mvs[mbmi->ref_frame[0]][0] = this_mv;
+            }
+#endif  // CONFIG_COMPOUND_SINGLEREF
           } else {
 #endif  // CONFIG_EXT_INTER
             for (ref = 0; ref < 1 + comp_pred; ++ref) {
@@ -9691,9 +10182,13 @@
             args.single_newmv_rate = dummy_single_newmv_rate;
             args.modelled_rd = NULL;
 #endif  // CONFIG_EXT_INTER
-            tmp_alt_rd = handle_inter_mode(
-                cpi, x, bsize, &tmp_rd_stats, &tmp_rd_stats_y, &tmp_rd_stats_uv,
-                &dummy_disable_skip, frame_mv, mi_row, mi_col, &args, best_rd);
+            tmp_alt_rd = handle_inter_mode(cpi, x, bsize, &tmp_rd_stats,
+                                           &tmp_rd_stats_y, &tmp_rd_stats_uv,
+                                           &dummy_disable_skip, frame_mv,
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                           frame_comp_mv,
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+                                           mi_row, mi_col, &args, best_rd);
             // Prevent pointers from escaping local scope
             args.single_newmv = NULL;
 #if CONFIG_EXT_INTER
@@ -9813,6 +10308,15 @@
       rate2 += ref_costs_single[ref_frame];
     }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    // Add the cost to signal single/comp mode in single ref.
+    if (!comp_pred && cm->reference_mode != COMPOUND_REFERENCE) {
+      aom_prob singleref_comp_mode_p = av1_get_inter_mode_prob(cm, xd);
+      rate2 += av1_cost_bit(singleref_comp_mode_p,
+                            is_inter_singleref_comp_mode(mbmi->mode));
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
 #if CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
     if (ref_frame == INTRA_FRAME) {
 #else
@@ -10010,6 +10514,15 @@
         xd->plane[i].pre[1] = yv12_mb[mbmi->ref_frame[1]][i];
     }
 
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+    // Single ref compound mode
+    if (!has_second_ref(mbmi) && is_inter_singleref_comp_mode(mbmi->mode)) {
+      xd->block_refs[1] = xd->block_refs[0];
+      for (i = 0; i < MAX_MB_PLANE; i++)
+        xd->plane[i].pre[1] = xd->plane[i].pre[0];
+    }
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+
     if (is_inter_mode(mbmi->mode)) {
       av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, NULL, bsize);
 #if CONFIG_MOTION_VAR
@@ -10222,10 +10735,14 @@
   }
 #endif  // CONFIG_FILTER_INTRA
 
-  // The inter modes' rate costs are not calculated precisely in some cases.
-  // Therefore, sometimes, NEWMV is chosen instead of NEARESTMV, NEARMV, and
-  // ZEROMV. Here, checks are added for those cases, and the mode decisions
-  // are corrected.
+// The inter modes' rate costs are not calculated precisely in some cases.
+// Therefore, sometimes, NEWMV is chosen instead of NEARESTMV, NEARMV, and
+// ZEROMV. Here, checks are added for those cases, and the mode decisions
+// are corrected.
+#if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
+// NOTE: For SR_NEW_NEWMV, no need to check as the two mvs from the same ref
+//       are surely different from each other.
+#endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   if (best_mbmode.mode == NEWMV
 #if CONFIG_EXT_INTER
       || best_mbmode.mode == NEW_NEWMV
@@ -10344,11 +10861,17 @@
   // using a mode which can support ref_mv_idx
   if (best_mbmode.ref_mv_idx != 0 &&
 #if CONFIG_EXT_INTER
+#if CONFIG_COMPOUND_SINGLEREF
+      !(best_mbmode.mode == NEWMV || best_mbmode.mode == SR_NEW_NEWMV ||
+        best_mbmode.mode == NEW_NEWMV ||
+        have_nearmv_in_inter_mode(best_mbmode.mode))) {
+#else   // !CONFIG_COMPOUND_SINGLEREF
       !(best_mbmode.mode == NEWMV || best_mbmode.mode == NEW_NEWMV ||
         have_nearmv_in_inter_mode(best_mbmode.mode))) {
-#else
+#endif  // CONFIG_COMPOUND_SINGLEREF
+#else   // !CONFIG_EXT_INTER
       !(best_mbmode.mode == NEARMV || best_mbmode.mode == NEWMV)) {
-#endif
+#endif  // CONFIG_EXT_INTER
     best_mbmode.ref_mv_idx = 0;
   }
 
diff --git a/av1/encoder/speed_features.h b/av1/encoder/speed_features.h
index 5710d77..346d064 100644
--- a/av1/encoder/speed_features.h
+++ b/av1/encoder/speed_features.h
@@ -38,6 +38,11 @@
 
 #if CONFIG_EXT_INTER
 enum {
+#if CONFIG_COMPOUND_SINGLEREF
+// TODO(zoeliu): To further consider following single ref comp modes:
+//               SR_NEAREST_NEARMV, SR_NEAREST_NEWMV, SR_NEAR_NEWMV,
+//               SR_ZERO_NEWMV, and SR_NEW_NEWMV.
+#endif  // CONFIG_COMPOUND_SINGLEREF
   INTER_ALL = (1 << NEARESTMV) | (1 << NEARMV) | (1 << ZEROMV) | (1 << NEWMV) |
               (1 << NEAREST_NEARESTMV) | (1 << NEAR_NEARMV) | (1 << NEW_NEWMV) |
               (1 << NEAREST_NEWMV) | (1 << NEAR_NEWMV) | (1 << NEW_NEARMV) |
@@ -67,7 +72,7 @@
                             (1 << NEW_NEARMV) | (1 << NEAR_NEWMV) |
                             (1 << NEAR_NEARMV),
 };
-#else
+#else   // !CONFIG_EXT_INTER
 enum {
   INTER_ALL = (1 << NEARESTMV) | (1 << NEARMV) | (1 << ZEROMV) | (1 << NEWMV),
   INTER_NEAREST = (1 << NEARESTMV),
diff --git a/configure b/configure
index 3a609f1..0735dba 100755
--- a/configure
+++ b/configure
@@ -524,6 +524,7 @@
     enabled chroma_2x2 && disable_feature chroma_sub8x8
     enabled dpcm_intra && enable_feature ext_tx
     enabled chroma_sub8x8 && enable_feature cb4x4
+    enabled compound_singleref && enable_feature ext_inter
 
     if ! enabled delta_q && enabled ext_delta_q; then
       log_echo "ext_delta_q requires delta_q, so disabling ext_delta_q"