Merge "Add SSE2 versions of 128x128 vpx_sad*" into nextgenv2
diff --git a/test/cpu_speed_test.cc b/test/cpu_speed_test.cc
index 572834c..8e36666 100644
--- a/test/cpu_speed_test.cc
+++ b/test/cpu_speed_test.cc
@@ -83,7 +83,7 @@
   cfg_.rc_min_quantizer = 0;
 
   ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
-                                       20);
+                                       10);
 
   init_flags_ = VPX_CODEC_USE_PSNR;
 
@@ -92,7 +92,7 @@
 }
 
 TEST_P(CpuSpeedTest, TestScreencastQ0) {
-  ::libvpx_test::Y4mVideoSource video("screendata.y4m", 0, 25);
+  ::libvpx_test::Y4mVideoSource video("screendata.y4m", 0, 10);
   cfg_.g_timebase = video.timebase();
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
@@ -107,7 +107,7 @@
 }
 
 TEST_P(CpuSpeedTest, TestTuneScreen) {
-  ::libvpx_test::Y4mVideoSource video("screendata.y4m", 0, 25);
+  ::libvpx_test::Y4mVideoSource video("screendata.y4m", 0, 10);
   cfg_.g_timebase = video.timebase();
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_minsection_pct = 2000;
@@ -133,7 +133,7 @@
   cfg_.rc_min_quantizer = 0;
 
   ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
-                                       20);
+                                       10);
 
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 }
@@ -148,7 +148,7 @@
   cfg_.rc_min_quantizer = 40;
 
   ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
-                                       20);
+                                       10);
 
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 }
diff --git a/test/tile_independence_test.cc b/test/tile_independence_test.cc
index 1a78768..db5d5df 100644
--- a/test/tile_independence_test.cc
+++ b/test/tile_independence_test.cc
@@ -97,7 +97,7 @@
   cfg_.g_lag_in_frames = 12;
   cfg_.rc_end_usage = VPX_VBR;
 
-  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 704, 576,
+  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 704, 144,
                                      timebase.den, timebase.num, 0, 15);
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 
diff --git a/test/vp10_convolve_test.cc b/test/vp10_convolve_test.cc
index 122a8e5..f05ccb2 100644
--- a/test/vp10_convolve_test.cc
+++ b/test/vp10_convolve_test.cc
@@ -12,9 +12,18 @@
 namespace {
 TEST(VP10ConvolveTest, vp10_convolve8) {
   ACMRandom rnd(ACMRandom::DeterministicSeed());
+#if CONFIG_DUAL_FILTER
+  INTERP_FILTER interp_filter[4] = {
+      EIGHTTAP_REGULAR, EIGHTTAP_REGULAR,
+      EIGHTTAP_REGULAR, EIGHTTAP_REGULAR
+  };
+  InterpFilterParams filter_params =
+      vp10_get_interp_filter_params(interp_filter[0]);
+#else
   INTERP_FILTER interp_filter = EIGHTTAP_REGULAR;
   InterpFilterParams filter_params =
       vp10_get_interp_filter_params(interp_filter);
+#endif
   ptrdiff_t filter_size = filter_params.taps;
   int filter_center = filter_size / 2 - 1;
   uint8_t src[12 * 12];
@@ -36,7 +45,7 @@
   }
 
   vp10_convolve(src + src_stride * filter_center + filter_center, src_stride,
-                dst, dst_stride, w, h, filter_params, subpel_x_q4, x_step_q4,
+                dst, dst_stride, w, h, interp_filter, subpel_x_q4, x_step_q4,
                 subpel_y_q4, y_step_q4, avg);
 
   const int16_t* x_filter =
@@ -50,9 +59,18 @@
 }
 TEST(VP10ConvolveTest, vp10_convolve) {
   ACMRandom rnd(ACMRandom::DeterministicSeed());
+#if CONFIG_DUAL_FILTER
+  INTERP_FILTER interp_filter[4] = {
+      EIGHTTAP_REGULAR, EIGHTTAP_REGULAR,
+      EIGHTTAP_REGULAR, EIGHTTAP_REGULAR
+  };
+  InterpFilterParams filter_params =
+      vp10_get_interp_filter_params(interp_filter[0]);
+#else
   INTERP_FILTER interp_filter = EIGHTTAP_REGULAR;
   InterpFilterParams filter_params =
       vp10_get_interp_filter_params(interp_filter);
+#endif
   ptrdiff_t filter_size = filter_params.taps;
   int filter_center = filter_size / 2 - 1;
   uint8_t src[12 * 12];
@@ -75,7 +93,7 @@
   for (subpel_x_q4 = 0; subpel_x_q4 < 16; subpel_x_q4++) {
     for (subpel_y_q4 = 0; subpel_y_q4 < 16; subpel_y_q4++) {
       vp10_convolve(src + src_stride * filter_center + filter_center,
-                    src_stride, dst, dst_stride, w, h, filter_params,
+                    src_stride, dst, dst_stride, w, h, interp_filter,
                     subpel_x_q4, x_step_q4, subpel_y_q4, y_step_q4, avg);
 
       const int16_t* x_filter =
@@ -101,9 +119,18 @@
 
 TEST(VP10ConvolveTest, vp10_convolve_avg) {
   ACMRandom rnd(ACMRandom::DeterministicSeed());
+#if CONFIG_DUAL_FILTER
+  INTERP_FILTER interp_filter[4] = {
+      EIGHTTAP_REGULAR, EIGHTTAP_REGULAR,
+      EIGHTTAP_REGULAR, EIGHTTAP_REGULAR
+  };
+  InterpFilterParams filter_params =
+      vp10_get_interp_filter_params(interp_filter[0]);
+#else
   INTERP_FILTER interp_filter = EIGHTTAP_REGULAR;
   InterpFilterParams filter_params =
       vp10_get_interp_filter_params(interp_filter);
+#endif
   ptrdiff_t filter_size = filter_params.taps;
   int filter_center = filter_size / 2 - 1;
   uint8_t src0[12 * 12];
@@ -134,20 +161,20 @@
     for (subpel_y_q4 = 0; subpel_y_q4 < 16; subpel_y_q4++) {
       avg = 0;
       vp10_convolve(src0 + offset, src_stride, dst0, dst_stride, w, h,
-                    filter_params, subpel_x_q4, x_step_q4, subpel_y_q4,
+                    interp_filter, subpel_x_q4, x_step_q4, subpel_y_q4,
                     y_step_q4, avg);
       avg = 0;
       vp10_convolve(src1 + offset, src_stride, dst1, dst_stride, w, h,
-                    filter_params, subpel_x_q4, x_step_q4, subpel_y_q4,
+                    interp_filter, subpel_x_q4, x_step_q4, subpel_y_q4,
                     y_step_q4, avg);
 
       avg = 0;
       vp10_convolve(src0 + offset, src_stride, dst, dst_stride, w, h,
-                    filter_params, subpel_x_q4, x_step_q4, subpel_y_q4,
+                    interp_filter, subpel_x_q4, x_step_q4, subpel_y_q4,
                     y_step_q4, avg);
       avg = 1;
       vp10_convolve(src1 + offset, src_stride, dst, dst_stride, w, h,
-                    filter_params, subpel_x_q4, x_step_q4, subpel_y_q4,
+                    interp_filter, subpel_x_q4, x_step_q4, subpel_y_q4,
                     y_step_q4, avg);
 
       EXPECT_EQ(dst[0], ROUND_POWER_OF_TWO(dst0[0] + dst1[0], 1));
diff --git a/test/vp10_fht8x8_test.cc b/test/vp10_fht8x8_test.cc
index 468b8c9..aadd77d 100644
--- a/test/vp10_fht8x8_test.cc
+++ b/test/vp10_fht8x8_test.cc
@@ -36,14 +36,14 @@
 }
 
 #if CONFIG_VP9_HIGHBITDEPTH
-typedef void (*IhighbdHtFunc)(const tran_low_t *in, uint8_t *out, int stride,
-                              int tx_type, int bd);
-typedef void (*HBDFhtFunc)(const int16_t *input, int32_t *output, int stride,
-                        int tx_type, int bd);
+typedef void (*IHbdHtFunc)(const tran_low_t *in, uint8_t *out, int stride,
+                           int tx_type, int bd);
+typedef void (*HbdHtFunc)(const int16_t *input, int32_t *output, int stride,
+                          int tx_type, int bd);
 // Target optimized function, tx_type, bit depth
-typedef tuple<HBDFhtFunc, int, int> HighbdHt8x8Param;
+typedef tuple<HbdHtFunc, int, int> HighbdHt8x8Param;
 
-void highbe_fht8x8_ref(const int16_t *in, int32_t *out, int stride,
+void highbd_fht8x8_ref(const int16_t *in, int32_t *out, int stride,
                        int tx_type, int bd) {
   vp10_fwd_txfm2d_8x8_c(in, out, stride, tx_type, bd);
 }
@@ -91,18 +91,18 @@
 
   virtual void SetUp() {
     fwd_txfm_ = GET_PARAM(0);
-    fwd_txfm_ref_ = highbe_fht8x8_ref;
+    fwd_txfm_ref_ = highbd_fht8x8_ref;
     tx_type_  = GET_PARAM(1);
     bit_depth_ = GET_PARAM(2);
     mask_ = (1 << bit_depth_) - 1;
     num_coeffs_ = 64;
 
-    input_ = reinterpret_cast<int16_t *>
-       (vpx_memalign(16, sizeof(int16_t) * num_coeffs_));
-    output_ = reinterpret_cast<int32_t *>
-        (vpx_memalign(16, sizeof(int32_t) * num_coeffs_));
-    output_ref_ = reinterpret_cast<int32_t *>
-        (vpx_memalign(16, sizeof(int32_t) * num_coeffs_));
+    input_ = reinterpret_cast<int16_t *>(
+        vpx_memalign(16, sizeof(int16_t) * num_coeffs_));
+    output_ = reinterpret_cast<int32_t *>(
+        vpx_memalign(16, sizeof(int32_t) * num_coeffs_));
+    output_ref_ = reinterpret_cast<int32_t *>(
+        vpx_memalign(16, sizeof(int32_t) * num_coeffs_));
   }
 
   virtual void TearDown() {
@@ -116,8 +116,8 @@
   void RunBitexactCheck();
 
  private:
-  HBDFhtFunc fwd_txfm_;
-  HBDFhtFunc fwd_txfm_ref_;
+  HbdHtFunc fwd_txfm_;
+  HbdHtFunc fwd_txfm_ref_;
   int tx_type_;
   int bit_depth_;
   int mask_;
@@ -140,10 +140,11 @@
     }
 
     fwd_txfm_ref_(input_, output_ref_, stride, tx_type_, bit_depth_);
-    fwd_txfm_(input_, output_, stride, tx_type_, bit_depth_);
+    ASM_REGISTER_STATE_CHECK(fwd_txfm_(input_, output_, stride, tx_type_,
+                                       bit_depth_));
 
     for (j = 0; j < num_coeffs; ++j) {
-      EXPECT_EQ(output_[j], output_ref_[j])
+      EXPECT_EQ(output_ref_[j], output_[j])
           << "Not bit-exact result at index: " << j
           << " at test block: " << i;
     }
diff --git a/test/vp9_ethread_test.cc b/test/vp9_ethread_test.cc
index a9a9906..d6b6951 100644
--- a/test/vp9_ethread_test.cc
+++ b/test/vp9_ethread_test.cc
@@ -20,13 +20,15 @@
 namespace {
 class VPxEncoderThreadTest
     : public ::libvpx_test::EncoderTest,
-      public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
+      public ::libvpx_test::CodecTestWith3Params<libvpx_test::TestMode,
+                                                 int, int> {
  protected:
   VPxEncoderThreadTest()
       : EncoderTest(GET_PARAM(0)),
         encoder_initialized_(false),
         encoding_mode_(GET_PARAM(1)),
-        set_cpu_used_(GET_PARAM(2)) {
+        set_cpu_used_(GET_PARAM(2)),
+        vp10_(GET_PARAM(3)) {
     init_flags_ = VPX_CODEC_USE_PSNR;
     vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
     cfg.w = 1280;
@@ -72,9 +74,13 @@
     if (!encoder_initialized_) {
 #if CONFIG_EXT_TILE
       encoder->Control(VP9E_SET_TILE_COLUMNS, 1);
-      // TODO(geza): Start using multiple tile rows when the multi-threaded
-      // encoder can handle them
-      encoder->Control(VP9E_SET_TILE_ROWS, 32);
+      if (vp10_) {
+        // TODO(geza): Start using multiple tile rows when the multi-threaded
+        // encoder can handle them
+        encoder->Control(VP9E_SET_TILE_ROWS, 32);
+      } else {
+        encoder->Control(VP9E_SET_TILE_ROWS, 0);
+      }
 #else
       // Encode 4 tile columns.
       encoder->Control(VP9E_SET_TILE_COLUMNS, 2);
@@ -124,6 +130,8 @@
   std::vector<size_t> size_enc_;
   std::vector<std::string> md5_enc_;
   std::vector<std::string> md5_dec_;
+
+  bool vp10_;
 };
 
 TEST_P(VPxEncoderThreadTest, EncoderResultTest) {
@@ -166,10 +174,10 @@
     VPxEncoderThreadTest,
     ::testing::Values(::libvpx_test::kTwoPassGood, ::libvpx_test::kOnePassGood,
                       ::libvpx_test::kRealTime),
-    ::testing::Range(1, 9));
+    ::testing::Range(1, 9), ::testing::Values(0));
 
 VP10_INSTANTIATE_TEST_CASE(
     VPxEncoderThreadTest,
     ::testing::Values(::libvpx_test::kTwoPassGood, ::libvpx_test::kOnePassGood),
-    ::testing::Range(1, 9));
+    ::testing::Range(1, 9), ::testing::Values(1));
 }  // namespace
diff --git a/vp10/common/blockd.c b/vp10/common/blockd.c
index 7339493..5ca5c05 100644
--- a/vp10/common/blockd.c
+++ b/vp10/common/blockd.c
@@ -216,7 +216,7 @@
 
 // Returns whether filter selection is needed for a given
 // intra prediction angle.
-int pick_intra_filter(int angle) {
+int vp10_is_intra_filter_switchable(int angle) {
   assert(angle > 0 && angle < 270);
   if (angle % 45 == 0)
     return 0;
diff --git a/vp10/common/blockd.h b/vp10/common/blockd.h
index 25fb569..21147af 100644
--- a/vp10/common/blockd.h
+++ b/vp10/common/blockd.h
@@ -198,7 +198,11 @@
   PALETTE_MODE_INFO palette_mode_info;
 
   // Only for INTER blocks
+#if CONFIG_DUAL_FILTER
+  INTERP_FILTER interp_filter[4];
+#else
   INTERP_FILTER interp_filter;
+#endif
   MV_REFERENCE_FRAME ref_frame[2];
   TX_TYPE tx_type;
 
@@ -524,7 +528,7 @@
   ADST_ADST,  // FILTER_TM
 };
 
-int pick_intra_filter(int angle);
+int vp10_is_intra_filter_switchable(int angle);
 #endif  // CONFIG_EXT_INTRA
 
 #if CONFIG_EXT_TILE
diff --git a/vp10/common/entropymode.c b/vp10/common/entropymode.c
index 1baf27a..0ae2572 100644
--- a/vp10/common/entropymode.c
+++ b/vp10/common/entropymode.c
@@ -901,16 +901,7 @@
   192, 128, 64
 };
 
-#if CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 4
-static const vpx_prob default_switchable_interp_prob[SWITCHABLE_FILTER_CONTEXTS]
-                                                    [SWITCHABLE_FILTERS - 1] = {
-  { 235, 192, 128},
-  { 36, 243, 208},
-  { 34, 16, 128},
-  { 36, 243, 48},
-  { 149, 160, 128},
-};
-#elif CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 5
+#if CONFIG_EXT_INTERP
 static const vpx_prob default_switchable_interp_prob[SWITCHABLE_FILTER_CONTEXTS]
                                                     [SWITCHABLE_FILTERS - 1] = {
   { 235, 192, 128, 128},
@@ -1236,14 +1227,7 @@
   vp10_copy(fc->intra_ext_tx_prob, default_intra_ext_tx_prob);
 }
 
-#if CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 4
-const vpx_tree_index vp10_switchable_interp_tree
-[TREE_SIZE(SWITCHABLE_FILTERS)] = {
-  -EIGHTTAP_REGULAR, 2,
-  4, -MULTITAP_SHARP,
-  -EIGHTTAP_SMOOTH, -EIGHTTAP_SMOOTH2,
-};
-#elif CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 5
+#if CONFIG_EXT_INTERP
 const vpx_tree_index vp10_switchable_interp_tree
 [TREE_SIZE(SWITCHABLE_FILTERS)] = {
   -EIGHTTAP_REGULAR, 2,
diff --git a/vp10/common/filter.c b/vp10/common/filter.c
index 36a17a8..154dade 100644
--- a/vp10/common/filter.c
+++ b/vp10/common/filter.c
@@ -121,7 +121,6 @@
   {0,  -1,   2,  -4,   8, 127,  -6,   3,  -1, 0},
 };
 
-#if SWITCHABLE_FILTERS >= 4
 DECLARE_ALIGNED(256, static const InterpKernel,
                 sub_pel_filters_8smooth2[SUBPEL_SHIFTS]) = {
 // freqmultiplier = 0.35
@@ -163,9 +162,7 @@
   {0,   2, -12,  37,  94,  14,  -9, 2},
   {0,   2, -11,  31,  95,  19, -10, 2},
 };
-#endif  // SWITCHABLE_FILTERS >= 4
 
-#if SWITCHABLE_FILTERS == 5
 DECLARE_ALIGNED(16, static const int16_t,
                 sub_pel_filters_12sharp[SUBPEL_SHIFTS][12]) = {
   // intfilt 0.85
@@ -186,8 +183,6 @@
   {-1,   2,  -2,   4,  -8,  18, 124, -13,   6,  -3,   2, -1},
   {0,   1,  -1,   2,  -4,   8, 127,  -7,   3,  -2,   1, 0},
 };
-#endif
-
 #else  // CONFIG_EXT_INTERP
 
 DECLARE_ALIGNED(256, static const InterpKernel,
@@ -269,12 +264,8 @@
   {(const int16_t*)sub_pel_filters_8, SUBPEL_TAPS, SUBPEL_SHIFTS},
   {(const int16_t*)sub_pel_filters_8smooth, SUBPEL_TAPS, SUBPEL_SHIFTS},
   {(const int16_t*)sub_pel_filters_10sharp, 10, SUBPEL_SHIFTS},
-#if SWITCHABLE_FILTERS >= 4
   {(const int16_t*)sub_pel_filters_8smooth2, SUBPEL_TAPS, SUBPEL_SHIFTS},
-#endif
-#if SWITCHABLE_FILTERS == 5
   {(const int16_t*)sub_pel_filters_12sharp, 12, SUBPEL_SHIFTS},
-#endif
   {(const int16_t*)bilinear_filters, SUBPEL_TAPS, SUBPEL_SHIFTS}
 };
 #else
diff --git a/vp10/common/filter.h b/vp10/common/filter.h
index 74a0c99..f70d0cc 100644
--- a/vp10/common/filter.h
+++ b/vp10/common/filter.h
@@ -26,17 +26,13 @@
 #define MULTITAP_SHARP      2
 
 #if CONFIG_EXT_INTERP
+#define EIGHTTAP_SMOOTH2    3
+#define MULTITAP_SHARP2     4
+
 #define MAX_SUBPEL_TAPS    12
+
 #define SUPPORT_NONINTERPOLATING_FILTERS 0  /* turn it on for experimentation */
 #define SWITCHABLE_FILTERS  5 /* Number of switchable filters */
-
-#if SWITCHABLE_FILTERS >= 4
-#define EIGHTTAP_SMOOTH2    3
-#endif
-#if SWITCHABLE_FILTERS == 5
-#define MULTITAP_SHARP2     4
-#endif  // SWITCHABLE_FILTERS
-
 #else
 #define SWITCHABLE_FILTERS  3 /* Number of switchable filters */
 #endif  // CONFIG_EXT_INTERP
diff --git a/vp10/common/mvref_common.c b/vp10/common/mvref_common.c
index 3023589..17d539f 100644
--- a/vp10/common/mvref_common.c
+++ b/vp10/common/mvref_common.c
@@ -92,13 +92,12 @@
     // compound reference frame
     if (candidate->ref_frame[0] == rf[0] &&
         candidate->ref_frame[1] == rf[1]) {
-      int_mv this_refmv[2] = {
-          get_sub_block_mv(candidate_mi, 0, col, block),
-          get_sub_block_mv(candidate_mi, 1, col, block)
-      };
+      int_mv this_refmv[2];
 
-      for (ref = 0; ref < 2; ++ref)
+      for (ref = 0; ref < 2; ++ref) {
+        this_refmv[ref] = get_sub_block_mv(candidate_mi, ref, col, block);
         lower_mv_precision(&this_refmv[ref].as_mv, use_hp);
+      }
 
       for (index = 0; index < *refmv_count; ++index)
         if ((ref_mv_stack[index].this_mv.as_int == this_refmv[0].as_int) &&
diff --git a/vp10/common/pred_common.c b/vp10/common/pred_common.c
index 34f9fbe..0c698a7 100644
--- a/vp10/common/pred_common.c
+++ b/vp10/common/pred_common.c
@@ -14,6 +14,44 @@
 #include "vp10/common/seg_common.h"
 
 // Returns a context number for the given MB prediction signal
+#if CONFIG_DUAL_FILTER
+int vp10_get_pred_context_switchable_interp(const MACROBLOCKD *xd, int dir) {
+  const MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
+  MV_REFERENCE_FRAME ref_frame = (dir < 2) ?
+      mbmi->ref_frame[0] : mbmi->ref_frame[1];
+  // Note:
+  // The mode info data structure has a 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.
+  const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
+  const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
+  int left_type = SWITCHABLE_FILTERS;
+  int above_type = SWITCHABLE_FILTERS;
+
+  if (xd->left_available) {
+    if (left_mbmi->ref_frame[0] == ref_frame)
+      left_type = left_mbmi->interp_filter[(dir & 0x01)];
+    else if (left_mbmi->ref_frame[1] == ref_frame)
+      left_type = left_mbmi->interp_filter[(dir & 0x01) + 2];
+  }
+
+  if (xd->up_available) {
+    if (above_mbmi->ref_frame[0] == ref_frame)
+      above_type = above_mbmi->interp_filter[(dir & 0x01)];
+    else if (above_mbmi->ref_frame[1] == ref_frame)
+      above_type = above_mbmi->interp_filter[(dir & 0x01) + 2];
+  }
+
+  if (left_type == above_type)
+    return left_type;
+  else if (left_type == SWITCHABLE_FILTERS && above_type != SWITCHABLE_FILTERS)
+    return above_type;
+  else if (left_type != SWITCHABLE_FILTERS && above_type == SWITCHABLE_FILTERS)
+    return left_type;
+  else
+    return SWITCHABLE_FILTERS;
+}
+#else
 int vp10_get_pred_context_switchable_interp(const MACROBLOCKD *xd) {
   // Note:
   // The mode info data structure has a one element border above and to the
@@ -35,6 +73,7 @@
   else
     return SWITCHABLE_FILTERS;
 }
+#endif
 
 #if CONFIG_EXT_INTRA
 // Obtain the reference filter type from the above/left neighbor blocks.
@@ -44,7 +83,11 @@
   if (ref_mbmi->sb_type >= BLOCK_8X8) {
     PREDICTION_MODE mode = ref_mbmi->mode;
     if (is_inter_block(ref_mbmi)) {
+#if CONFIG_DUAL_FILTER
+      switch (ref_mbmi->interp_filter[0]) {
+#else
       switch (ref_mbmi->interp_filter) {
+#endif
         case EIGHTTAP_REGULAR:
           ref_type = INTRA_FILTER_8TAP;
           break;
@@ -64,7 +107,7 @@
       if (mode != DC_PRED && mode != TM_PRED) {
         int p_angle = mode_to_angle_map[mode] +
             ref_mbmi->angle_delta[0] * ANGLE_STEP;
-        if (pick_intra_filter(p_angle)) {
+        if (vp10_is_intra_filter_switchable(p_angle)) {
           ref_type = ref_mbmi->intra_filter;
         }
       }
diff --git a/vp10/common/pred_common.h b/vp10/common/pred_common.h
index 385a3e1..f321599 100644
--- a/vp10/common/pred_common.h
+++ b/vp10/common/pred_common.h
@@ -66,7 +66,11 @@
   return cm->fc->skip_probs[vp10_get_skip_context(xd)];
 }
 
+#if CONFIG_DUAL_FILTER
+int vp10_get_pred_context_switchable_interp(const MACROBLOCKD *xd, int dir);
+#else
 int vp10_get_pred_context_switchable_interp(const MACROBLOCKD *xd);
+#endif
 
 #if CONFIG_EXT_INTRA
 int vp10_get_pred_context_intra_interp(const MACROBLOCKD *xd);
diff --git a/vp10/common/reconinter.c b/vp10/common/reconinter.c
index e680c80..165b3ba 100644
--- a/vp10/common/reconinter.c
+++ b/vp10/common/reconinter.c
@@ -461,7 +461,11 @@
     const int subpel_y,
     const struct scale_factors *sf,
     int w, int h,
+#if CONFIG_DUAL_FILTER
+    const INTERP_FILTER *interp_filter,
+#else
     const INTERP_FILTER interp_filter,
+#endif
     int xs, int ys,
 #if CONFIG_SUPERTX
     int wedge_offset_x, int wedge_offset_y,
@@ -557,7 +561,11 @@
                                const MV *src_mv,
                                const struct scale_factors *sf,
                                int w, int h, int ref,
+#if CONFIG_DUAL_FILTER
+                               const INTERP_FILTER *interp_filter,
+#else
                                const INTERP_FILTER interp_filter,
+#endif
                                enum mv_precision precision,
                                int x, int y) {
   const int is_q4 = precision == MV_PRECISION_Q4;
@@ -591,7 +599,6 @@
   const MODE_INFO *mi = xd->mi[0];
 #endif  // CONFIG_OBMC
   const int is_compound = has_second_ref(&mi->mbmi);
-  const INTERP_FILTER interp_filter = mi->mbmi.interp_filter;
   int ref;
 
   for (ref = 0; ref < 1 + is_compound; ++ref) {
@@ -640,7 +647,7 @@
       vp10_make_masked_inter_predictor(
           pre, pre_buf->stride, dst, dst_buf->stride,
           subpel_x, subpel_y, sf, w, h,
-          interp_filter, xs, ys,
+          mi->mbmi.interp_filter, xs, ys,
 #if CONFIG_SUPERTX
           wedge_offset_x, wedge_offset_y,
 #endif  // CONFIG_SUPERTX
@@ -649,7 +656,7 @@
 #endif  // CONFIG_EXT_INTER
       vp10_make_inter_predictor(pre, pre_buf->stride, dst, dst_buf->stride,
                                 subpel_x, subpel_y, sf, w, h, ref,
-                                interp_filter, xs, ys, xd);
+                                mi->mbmi.interp_filter, xs, ys, xd);
   }
 }
 
@@ -665,7 +672,6 @@
   uint8_t *const dst = &pd->dst.buf[(ir * pd->dst.stride + ic) << 2];
   int ref;
   const int is_compound = has_second_ref(&mi->mbmi);
-  const INTERP_FILTER interp_filter = mi->mbmi.interp_filter;
 
   for (ref = 0; ref < 1 + is_compound; ++ref) {
     const uint8_t *pre =
@@ -676,7 +682,8 @@
                                       dst, pd->dst.stride,
                                       &mi->bmi[i].as_mv[ref].as_mv,
                                       &xd->block_refs[ref]->sf, width, height,
-                                      ref, interp_filter, MV_PRECISION_Q3,
+                                      ref, mi->mbmi.interp_filter,
+                                      MV_PRECISION_Q3,
                                       mi_col * MI_SIZE + 4 * ic,
                                       mi_row * MI_SIZE + 4 * ir, xd->bd);
   } else {
@@ -684,7 +691,7 @@
                                dst, pd->dst.stride,
                                &mi->bmi[i].as_mv[ref].as_mv,
                                &xd->block_refs[ref]->sf, width, height, ref,
-                               interp_filter, MV_PRECISION_Q3,
+                               mi->mbmi.interp_filter, MV_PRECISION_Q3,
                                mi_col * MI_SIZE + 4 * ic,
                                mi_row * MI_SIZE + 4 * ir);
   }
@@ -693,7 +700,7 @@
                                dst, pd->dst.stride,
                                &mi->bmi[i].as_mv[ref].as_mv,
                                &xd->block_refs[ref]->sf, width, height, ref,
-                               interp_filter, MV_PRECISION_Q3,
+                               mi->mbmi.interp_filter, MV_PRECISION_Q3,
                                mi_col * MI_SIZE + 4 * ic,
                                mi_row * MI_SIZE + 4 * ir);
 #endif  // CONFIG_VP9_HIGHBITDEPTH
@@ -2151,7 +2158,6 @@
                                               int ext_dst_stride) {
   struct macroblockd_plane *const pd = &xd->plane[plane];
   const MODE_INFO *mi = xd->mi[0];
-  const INTERP_FILTER interp_filter = mi->mbmi.interp_filter;
 
   const struct scale_factors *const sf = &xd->block_refs[ref]->sf;
   struct buf_2d *const pre_buf = &pd->pre[ref];
@@ -2199,7 +2205,7 @@
 
   vp10_make_inter_predictor(pre, pre_buf->stride, dst, ext_dst_stride,
                             subpel_x, subpel_y, sf, w, h, 0,
-                            interp_filter, xs, ys, xd);
+                            mi->mbmi.interp_filter, xs, ys, xd);
 }
 
 void vp10_build_inter_predictors_for_planes_single_buf(
diff --git a/vp10/common/reconinter.h b/vp10/common/reconinter.h
index c5e2c3a..b3bc187 100644
--- a/vp10/common/reconinter.h
+++ b/vp10/common/reconinter.h
@@ -25,16 +25,37 @@
                                    const int subpel_x,
                                    const int subpel_y,
                                    const struct scale_factors *sf,
-                                   int w, int h, int ref,
+                                   int w, int h, int ref_idx,
+#if CONFIG_DUAL_FILTER
+                                   const INTERP_FILTER *interp_filter,
+#else
                                    const INTERP_FILTER interp_filter,
+#endif
                                    int xs, int ys) {
+#if CONFIG_DUAL_FILTER
+  InterpFilterParams interp_filter_params_x =
+      vp10_get_interp_filter_params(interp_filter[1 + 2 * ref_idx]);
+  InterpFilterParams interp_filter_params_y =
+      vp10_get_interp_filter_params(interp_filter[0 + 2 * ref_idx]);
+#else
   InterpFilterParams interp_filter_params =
       vp10_get_interp_filter_params(interp_filter);
+#endif
+
+#if CONFIG_DUAL_FILTER
+  if (interp_filter_params_x.taps == SUBPEL_TAPS &&
+      interp_filter_params_y.taps == SUBPEL_TAPS) {
+    const int16_t *kernel_x =
+        vp10_get_interp_filter_subpel_kernel(interp_filter_params_x, subpel_x);
+    const int16_t *kernel_y =
+        vp10_get_interp_filter_subpel_kernel(interp_filter_params_y, subpel_y);
+#else
   if (interp_filter_params.taps == SUBPEL_TAPS) {
     const int16_t *kernel_x =
         vp10_get_interp_filter_subpel_kernel(interp_filter_params, subpel_x);
     const int16_t *kernel_y =
         vp10_get_interp_filter_subpel_kernel(interp_filter_params, subpel_y);
+#endif
 #if CONFIG_EXT_INTERP && SUPPORT_NONINTERPOLATING_FILTERS
     if (IsInterpolatingFilter(interp_filter)) {
       // Interpolating filter
@@ -47,17 +68,16 @@
           kernel_x, xs, kernel_y, ys, w, h);
     }
 #else
-    sf->predict[subpel_x != 0][subpel_y != 0][ref](
+    sf->predict[subpel_x != 0][subpel_y != 0][ref_idx](
         src, src_stride, dst, dst_stride,
         kernel_x, xs, kernel_y, ys, w, h);
 #endif  // CONFIG_EXT_INTERP && SUPPORT_NONINTERPOLATING_FILTERS
   } else {
-    // ref > 0 means this is the second reference frame
+    // ref_idx > 0 means this is the second reference frame
     // first reference frame's prediction result is already in dst
     // therefore we need to average the first and second results
-    int avg = ref > 0;
-    vp10_convolve(src, src_stride, dst, dst_stride, w, h, interp_filter_params,
-                  subpel_x, xs, subpel_y, ys, avg);
+    vp10_convolve(src, src_stride, dst, dst_stride, w, h, interp_filter,
+                  subpel_x, xs, subpel_y, ys, ref_idx);
   }
 }
 
@@ -126,7 +146,11 @@
     const int subpel_y,
     const struct scale_factors *sf,
     int w, int h, int ref,
+#if CONFIG_DUAL_FILTER
+    const INTERP_FILTER *interp_filter,
+#else
     const INTERP_FILTER interp_filter,
+#endif
     int xs, int ys,
     const MACROBLOCKD *xd) {
   (void) xd;
@@ -152,7 +176,11 @@
     const int subpel_y,
     const struct scale_factors *sf,
     int w, int h,
+#if CONFIG_DUAL_FILTER
+    const INTERP_FILTER *interp_filter,
+#else
     const INTERP_FILTER interp_filter,
+#endif
     int xs, int ys,
 #if CONFIG_SUPERTX
     int wedge_offset_x, int wedge_offset_y,
@@ -284,7 +312,11 @@
                                const MV *mv_q3,
                                const struct scale_factors *sf,
                                int w, int h, int do_avg,
+#if CONFIG_DUAL_FILTER
+                               const INTERP_FILTER *interp_filter,
+#else
                                const INTERP_FILTER interp_filter,
+#endif
                                enum mv_precision precision,
                                int x, int y);
 
@@ -325,6 +357,54 @@
                           const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
                           const struct scale_factors *sf);
 
+#if CONFIG_DUAL_FILTER
+// Detect if the block have sub-pixel level motion vectors
+// per component.
+static INLINE int has_subpel_mv_component(const MACROBLOCKD *const xd,
+                                          int dir) {
+  MODE_INFO *const mi = xd->mi[0];
+  MB_MODE_INFO *const mbmi = &mi->mbmi;
+  const BLOCK_SIZE bsize = mbmi->sb_type;
+  int plane;
+  int ref = (dir >> 1);
+
+  if (bsize >= BLOCK_8X8) {
+    if (dir & 0x01) {
+      if (mbmi->mv[ref].as_mv.col & SUBPEL_MASK)
+        return 1;
+    } else {
+      if (mbmi->mv[ref].as_mv.row & SUBPEL_MASK)
+        return 1;
+    }
+  } else {
+    for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
+      const PARTITION_TYPE bp = BLOCK_8X8 - bsize;
+      const struct macroblockd_plane *const pd = &xd->plane[plane];
+      const int have_vsplit = bp != PARTITION_HORZ;
+      const int have_hsplit = bp != PARTITION_VERT;
+      const int num_4x4_w = 2 >> ((!have_vsplit) | pd->subsampling_x);
+      const int num_4x4_h = 2 >> ((!have_hsplit) | pd->subsampling_y);
+
+      int x, y;
+      for (y = 0; y < num_4x4_h; ++y) {
+        for (x = 0; x < num_4x4_w; ++x) {
+          const MV mv = average_split_mvs(pd, mi, ref, y * 2 + x);
+          if (dir & 0x01) {
+            if (mv.col & SUBPEL_MASK)
+              return 1;
+          } else {
+            if (mv.row & SUBPEL_MASK)
+              return 1;
+          }
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+#endif
+
 #if CONFIG_EXT_INTERP
 static INLINE int vp10_is_interp_needed(const MACROBLOCKD *const xd) {
   MODE_INFO *const mi = xd->mi[0];
diff --git a/vp10/common/reconintra.c b/vp10/common/reconintra.c
index ef046e9..6b4a460 100644
--- a/vp10/common/reconintra.c
+++ b/vp10/common/reconintra.c
@@ -1290,7 +1290,7 @@
   if (mode != DC_PRED && mode != TM_PRED &&
       xd->mi[0]->mbmi.sb_type >= BLOCK_8X8) {
     INTRA_FILTER filter = INTRA_FILTER_LINEAR;
-    if (plane == 0 && pick_intra_filter(p_angle))
+    if (plane == 0 && vp10_is_intra_filter_switchable(p_angle))
       filter = xd->mi[0]->mbmi.intra_filter;
     highbd_dr_predictor(dst, dst_stride, bs, const_above_row, left_col,
                         p_angle, xd->bd, filter);
@@ -1450,7 +1450,7 @@
   if (mode != DC_PRED && mode != TM_PRED &&
       xd->mi[0]->mbmi.sb_type >= BLOCK_8X8) {
     INTRA_FILTER filter = INTRA_FILTER_LINEAR;
-    if (plane == 0 && pick_intra_filter(p_angle))
+    if (plane == 0 && vp10_is_intra_filter_switchable(p_angle))
       filter = xd->mi[0]->mbmi.intra_filter;
     dr_predictor(dst, dst_stride, tx_size, const_above_row, left_col, p_angle,
                  filter);
diff --git a/vp10/common/reconintra.h b/vp10/common/reconintra.h
index 77489c1..b53c2bf 100644
--- a/vp10/common/reconintra.h
+++ b/vp10/common/reconintra.h
@@ -26,7 +26,7 @@
                              uint8_t *dst, int dst_stride,
                              int aoff, int loff, int plane);
 #if CONFIG_EXT_INTRA
-int pick_intra_filter(int angle);
+int vp10_is_intra_filter_switchable(int angle);
 #endif  // CONFIG_EXT_INTRA
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/vp10/common/vp10_convolve.c b/vp10/common/vp10_convolve.c
index 9e0dc29..7ed02d8 100644
--- a/vp10/common/vp10_convolve.c
+++ b/vp10/common/vp10_convolve.c
@@ -95,10 +95,13 @@
 
 void vp10_convolve(const uint8_t *src, int src_stride, uint8_t *dst,
                    int dst_stride, int w, int h,
-                   const InterpFilterParams filter_params,
+#if CONFIG_DUAL_FILTER
+                   const INTERP_FILTER *interp_filter,
+#else
+                   const INTERP_FILTER interp_filter,
+#endif
                    const int subpel_x_q4, int x_step_q4, const int subpel_y_q4,
-                   int y_step_q4, int avg) {
-  int filter_size = filter_params.taps;
+                   int y_step_q4, int ref_idx) {
   int ignore_horiz = x_step_q4 == 16 && subpel_x_q4 == 0;
   int ignore_vert = y_step_q4 == 16 && subpel_y_q4 == 0;
 
@@ -106,16 +109,31 @@
   assert(h <= MAX_BLOCK_HEIGHT);
   assert(y_step_q4 <= MAX_STEP);
   assert(x_step_q4 <= MAX_STEP);
-  assert(filter_params.taps <= MAX_FILTER_TAP);
 
   if (ignore_horiz && ignore_vert) {
-    convolve_copy(src, src_stride, dst, dst_stride, w, h, avg);
+    convolve_copy(src, src_stride, dst, dst_stride, w, h, ref_idx);
   } else if (ignore_vert) {
+#if CONFIG_DUAL_FILTER
+    InterpFilterParams filter_params =
+        vp10_get_interp_filter_params(interp_filter[1 + 2 * ref_idx]);
+#else
+    InterpFilterParams filter_params =
+        vp10_get_interp_filter_params(interp_filter);
+#endif
+    assert(filter_params.taps <= MAX_FILTER_TAP);
     convolve_horiz(src, src_stride, dst, dst_stride, w, h, filter_params,
-                   subpel_x_q4, x_step_q4, avg);
+                   subpel_x_q4, x_step_q4, ref_idx);
   } else if (ignore_horiz) {
+#if CONFIG_DUAL_FILTER
+    InterpFilterParams filter_params =
+        vp10_get_interp_filter_params(interp_filter[2 * ref_idx]);
+#else
+    InterpFilterParams filter_params =
+        vp10_get_interp_filter_params(interp_filter);
+#endif
+    assert(filter_params.taps <= MAX_FILTER_TAP);
     convolve_vert(src, src_stride, dst, dst_stride, w, h, filter_params,
-                  subpel_y_q4, y_step_q4, avg);
+                  subpel_y_q4, y_step_q4, ref_idx);
   } else {
     // temp's size is set to (maximum possible intermediate_height) *
     // MAX_BLOCK_WIDTH
@@ -123,15 +141,34 @@
                   MAX_FILTER_TAP) *
                  MAX_BLOCK_WIDTH];
     int temp_stride = MAX_BLOCK_WIDTH;
-
+#if CONFIG_DUAL_FILTER
+    InterpFilterParams filter_params =
+        vp10_get_interp_filter_params(interp_filter[1 + 2 * ref_idx]);
+#else
+    InterpFilterParams filter_params =
+        vp10_get_interp_filter_params(interp_filter);
+#endif
+    int filter_size = filter_params.taps;
     int intermediate_height =
         (((h - 1) * y_step_q4 + subpel_y_q4) >> SUBPEL_BITS) + filter_size;
 
+    assert(filter_params.taps <= MAX_FILTER_TAP);
+
     convolve_horiz(src - src_stride * (filter_size / 2 - 1), src_stride, temp,
                    temp_stride, w, intermediate_height, filter_params,
                    subpel_x_q4, x_step_q4, 0);
+
+#if CONFIG_DUAL_FILTER
+    filter_params = vp10_get_interp_filter_params(interp_filter[2 * ref_idx]);
+#else
+    filter_params = vp10_get_interp_filter_params(interp_filter);
+#endif
+    filter_size = filter_params.taps;
+    assert(filter_params.taps <= MAX_FILTER_TAP);
+
     convolve_vert(temp + temp_stride * (filter_size / 2 - 1), temp_stride, dst,
-                  dst_stride, w, h, filter_params, subpel_y_q4, y_step_q4, avg);
+                  dst_stride, w, h, filter_params,
+                  subpel_y_q4, y_step_q4, ref_idx);
   }
 }
 
diff --git a/vp10/common/vp10_convolve.h b/vp10/common/vp10_convolve.h
index a3d6c65..c1f55e7 100644
--- a/vp10/common/vp10_convolve.h
+++ b/vp10/common/vp10_convolve.h
@@ -9,7 +9,11 @@
 void vp10_convolve(const uint8_t *src, int src_stride,
                    uint8_t *dst, int dst_stride,
                    int w, int h,
-                   const InterpFilterParams filter_params,
+#if CONFIG_DUAL_FILTER
+                   const INTERP_FILTER *interp_filter,
+#else
+                   const INTERP_FILTER interp_filter,
+#endif
                    const int subpel_x,
                    const int subpel_y,
                    int xstep, int ystep, int avg);
diff --git a/vp10/common/vp10_fwd_txfm2d.c b/vp10/common/vp10_fwd_txfm2d.c
index ccb820f..cd5ce71 100644
--- a/vp10/common/vp10_fwd_txfm2d.c
+++ b/vp10/common/vp10_fwd_txfm2d.c
@@ -15,7 +15,7 @@
 #include "vp10/common/vp10_fwd_txfm2d_cfg.h"
 #include "vp10/common/vp10_txfm.h"
 
-static inline TxfmFunc fwd_txfm_type_to_func(TXFM_TYPE txfm_type) {
+static INLINE TxfmFunc fwd_txfm_type_to_func(TXFM_TYPE txfm_type) {
   switch (txfm_type) {
     case TXFM_TYPE_DCT4:
       return vp10_fdct4_new;
@@ -50,7 +50,7 @@
   }
 }
 
-static inline void fwd_txfm2d_c(const int16_t *input, int32_t *output,
+static INLINE void fwd_txfm2d_c(const int16_t *input, int32_t *output,
                                 const int stride, const TXFM_2D_CFG *cfg,
                                 int32_t *buf) {
   int i, j;
diff --git a/vp10/common/vp10_inv_txfm2d.c b/vp10/common/vp10_inv_txfm2d.c
index a1ad4b8..5227fc8 100644
--- a/vp10/common/vp10_inv_txfm2d.c
+++ b/vp10/common/vp10_inv_txfm2d.c
@@ -11,7 +11,7 @@
 #include "vp10/common/vp10_txfm.h"
 #include "vp10/common/vp10_inv_txfm1d.h"
 
-static inline TxfmFunc inv_txfm_type_to_func(TXFM_TYPE txfm_type) {
+static INLINE TxfmFunc inv_txfm_type_to_func(TXFM_TYPE txfm_type) {
   switch (txfm_type) {
     case TXFM_TYPE_DCT4:
       return vp10_idct4_new;
@@ -46,7 +46,7 @@
   }
 }
 
-static inline void inv_txfm2d_add_c(const int32_t *input, int16_t *output,
+static INLINE void inv_txfm2d_add_c(const int32_t *input, int16_t *output,
                                     int stride, const TXFM_2D_CFG *cfg,
                                     int32_t *txfm_buf) {
   const int txfm_size = cfg->txfm_size;
diff --git a/vp10/common/x86/vp10_fwd_txfm2d_sse4.c b/vp10/common/x86/vp10_fwd_txfm2d_sse4.c
index daed25e..c02d2d8 100644
--- a/vp10/common/x86/vp10_fwd_txfm2d_sse4.c
+++ b/vp10/common/x86/vp10_fwd_txfm2d_sse4.c
@@ -11,7 +11,7 @@
 #include "vp10/common/vp10_fwd_txfm2d_cfg.h"
 #include "vp10/common/x86/vp10_txfm1d_sse4.h"
 
-static inline void int16_array_with_stride_to_int32_array_without_stride(
+static INLINE void int16_array_with_stride_to_int32_array_without_stride(
     const int16_t *input, int stride, int32_t *output, int txfm1d_size) {
   int r, c;
   for (r = 0; r < txfm1d_size; r++) {
@@ -24,7 +24,7 @@
 typedef void (*TxfmFuncSSE2)(const __m128i *input, __m128i *output,
                              const int8_t *cos_bit, const int8_t *stage_range);
 
-static inline TxfmFuncSSE2 fwd_txfm_type_to_func(TXFM_TYPE txfm_type) {
+static INLINE TxfmFuncSSE2 fwd_txfm_type_to_func(TXFM_TYPE txfm_type) {
   switch (txfm_type) {
     case TXFM_TYPE_DCT4:
       return vp10_fdct4_new_sse4_1;
@@ -59,7 +59,7 @@
   return NULL;
 }
 
-static inline void fwd_txfm2d_sse4_1(const int16_t *input, int32_t *output,
+static INLINE void fwd_txfm2d_sse4_1(const int16_t *input, int32_t *output,
                                      const int stride, const TXFM_2D_CFG *cfg,
                                      int32_t *txfm_buf) {
   const int txfm_size = cfg->txfm_size;
diff --git a/vp10/decoder/decodeframe.c b/vp10/decoder/decodeframe.c
index 6daa01b..6006e2d 100644
--- a/vp10/decoder/decodeframe.c
+++ b/vp10/decoder/decodeframe.c
@@ -521,7 +521,11 @@
                                int border_offset,
                                uint8_t *const dst, int dst_buf_stride,
                                int subpel_x, int subpel_y,
+#if CONFIG_DUAL_FILTER
+                               const INTERP_FILTER *interp_filter,
+#else
                                const INTERP_FILTER interp_filter,
+#endif
                                const struct scale_factors *sf,
 #if CONFIG_EXT_INTER
                                int wedge_offset_x, int wedge_offset_y,
@@ -563,7 +567,11 @@
                                        int wedge_offset_x, int wedge_offset_y,
 #endif  // CONFIG_EXT_INTER
                                        int mi_x, int mi_y,
+#if CONFIG_DUAL_FILTER
+                                       const INTERP_FILTER *interp_filter,
+#else
                                        const INTERP_FILTER interp_filter,
+#endif
                                        const struct scale_factors *sf,
                                        struct buf_2d *pre_buf,
                                        struct buf_2d *dst_buf, const MV* mv,
@@ -670,9 +678,17 @@
     int x1 = ((x0_16 + (w - 1) * xs) >> SUBPEL_BITS) + 1;
     int x_pad = 0, y_pad = 0;
 
+#if CONFIG_DUAL_FILTER
+    InterpFilterParams filter_params_y =
+        vp10_get_interp_filter_params(interp_filter[0]);
+    InterpFilterParams filter_params_x =
+        vp10_get_interp_filter_params(interp_filter[1]);
+    int filter_size = VPXMAX(filter_params_y.taps, filter_params_x.taps);
+#else
     InterpFilterParams filter_params =
         vp10_get_interp_filter_params(interp_filter);
     int filter_size = filter_params.taps;
+#endif
 
     if (subpel_x ||
 #if CONFIG_EXT_INTERP
@@ -772,7 +788,6 @@
   const int wedge_offset_y = (mi_row_ori - mi_row) * MI_SIZE;
 #endif  // CONFIG_EXT_INTER
   const MODE_INFO *mi = xd->mi[0];
-  const INTERP_FILTER interp_filter = mi->mbmi.interp_filter;
   const BLOCK_SIZE sb_type = mi->mbmi.sb_type;
   const int is_compound = has_second_ref(&mi->mbmi);
 
@@ -819,7 +834,7 @@
                 wedge_offset_y,
 #endif  // CONFIG_EXT_INTER
                 mi_x, mi_y,
-                interp_filter, sf, pre_buf, dst_buf,
+                mi->mbmi.interp_filter, sf, pre_buf, dst_buf,
                 &mv, ref_frame_buf, is_scaled, ref);
           }
         }
@@ -837,7 +852,7 @@
             wedge_offset_y,
 #endif  // CONFIG_EXT_INTER
             mi_x, mi_y,
-            interp_filter, sf, pre_buf, dst_buf,
+            mi->mbmi.interp_filter, sf, pre_buf, dst_buf,
             &mv, ref_frame_buf,
             is_scaled, ref);
       }
@@ -874,7 +889,6 @@
   const int wedge_offset_y = (mi_row_ori - mi_row) * MI_SIZE;
 #endif  // CONFIG_EXT_INTER
   const MODE_INFO *mi = xd->mi[0];
-  const INTERP_FILTER interp_filter = mi->mbmi.interp_filter;
   const int is_compound = has_second_ref(&mi->mbmi);
 
   // For sub8x8 uv:
@@ -910,7 +924,7 @@
                                  wedge_offset_y,
 #endif  // CONFIG_EXT_INTER
                                  mi_x, mi_y,
-                                 interp_filter, sf, pre_buf, dst_buf,
+                                 mi->mbmi.interp_filter, sf, pre_buf, dst_buf,
                                  &mv, ref_frame_buf, is_scaled, ref);
     }
   }
diff --git a/vp10/decoder/decodemv.c b/vp10/decoder/decodemv.c
index bcbcafe..38ede53 100644
--- a/vp10/decoder/decodemv.c
+++ b/vp10/decoder/decodemv.c
@@ -580,7 +580,7 @@
             read_uniform(r, 2 * MAX_ANGLE_DELTAS + 1) - MAX_ANGLE_DELTAS;
         p_angle = mode_to_angle_map[mbmi->mode] +
             mbmi->angle_delta[0] * ANGLE_STEP;
-        if (pick_intra_filter(p_angle)) {
+        if (vp10_is_intra_filter_switchable(p_angle)) {
           FRAME_COUNTS *counts = xd->counts;
           mbmi->intra_filter = vp10_read_tree(r, vp10_intra_filter_tree,
                                              cm->fc->intra_filter_probs[ctx]);
@@ -866,6 +866,9 @@
 
 static INLINE INTERP_FILTER read_interp_filter(
     VP10_COMMON *const cm, MACROBLOCKD *const xd,
+#if CONFIG_DUAL_FILTER
+    int dir,
+#endif
     vp10_reader *r) {
 #if CONFIG_EXT_INTERP
   if (!vp10_is_interp_needed(xd)) return EIGHTTAP_REGULAR;
@@ -873,7 +876,11 @@
   if (cm->interp_filter != SWITCHABLE) {
     return cm->interp_filter;
   } else {
+#if CONFIG_DUAL_FILTER
+    const int ctx = vp10_get_pred_context_switchable_interp(xd, dir);
+#else
     const int ctx = vp10_get_pred_context_switchable_interp(xd);
+#endif
     FRAME_COUNTS *counts = xd->counts;
     const INTERP_FILTER type =
       (INTERP_FILTER)vp10_read_tree(r, vp10_switchable_interp_tree,
@@ -922,7 +929,7 @@
             read_uniform(r, 2 * MAX_ANGLE_DELTAS + 1) - MAX_ANGLE_DELTAS;
         p_angle =
             mode_to_angle_map[mbmi->mode] + mbmi->angle_delta[0] * ANGLE_STEP;
-        if (pick_intra_filter(p_angle)) {
+        if (vp10_is_intra_filter_switchable(p_angle)) {
           FRAME_COUNTS *counts = xd->counts;
           const int ctx = vp10_get_pred_context_intra_interp(xd);
           mbmi->intra_filter = vp10_read_tree(r, vp10_intra_filter_tree,
@@ -1384,9 +1391,9 @@
   }
 #endif
 
-#if !CONFIG_EXT_INTERP
+#if !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER
   mbmi->interp_filter = read_interp_filter(cm, xd, r);
-#endif  // !CONFIG_EXT_INTERP
+#endif  // !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER
 
   if (bsize < BLOCK_8X8) {
     const int num_4x4_w = 1 << xd->bmode_blocks_wl;
@@ -1508,7 +1515,10 @@
     mbmi->mv[1].as_int = mi->bmi[3].as_mv[1].as_int;
   } else {
     int ref;
-    int_mv ref_mv[2] = { nearestmv[0], nearestmv[1] };
+    int_mv ref_mv[2];
+    ref_mv[0] = nearestmv[0];
+    ref_mv[1] = nearestmv[1];
+
     for (ref = 0; ref < 1 + is_compound && mbmi->mode == NEWMV; ++ref) {
 #if CONFIG_REF_MV
       uint8_t ref_frame_type = vp10_ref_frame_type(mbmi->ref_frame);
@@ -1606,9 +1616,21 @@
   }
 #endif  // CONFIG_EXT_INTER
 
+#if CONFIG_DUAL_FILTER
+  for (ref = 0; ref < 4; ++ref) {
+    const int frame_idx = (ref >> 1);
+    mbmi->interp_filter[ref] = (cm->interp_filter == SWITCHABLE) ?
+        EIGHTTAP_REGULAR : cm->interp_filter;
+
+    if (mbmi->ref_frame[frame_idx] > INTRA_FRAME &&
+        has_subpel_mv_component(xd, ref))
+      mbmi->interp_filter[ref] = read_interp_filter(cm, xd, ref, r);
+  }
+#else
 #if CONFIG_EXT_INTERP
   mbmi->interp_filter = read_interp_filter(cm, xd, r);
 #endif  // CONFIG_EXT_INTERP
+#endif  // CONFIG_DUAL_FILTER
 }
 
 static void read_inter_frame_mode_info(VP10Decoder *const pbi,
diff --git a/vp10/encoder/bitstream.c b/vp10/encoder/bitstream.c
index 93e07d4..b79b94a 100644
--- a/vp10/encoder/bitstream.c
+++ b/vp10/encoder/bitstream.c
@@ -42,16 +42,13 @@
 static const struct vp10_token intra_mode_encodings[INTRA_MODES] = {
   {0, 1}, {6, 3}, {28, 5}, {30, 5}, {58, 6}, {59, 6}, {126, 7}, {127, 7},
   {62, 6}, {2, 2}};
-#if CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 4
-static const struct vp10_token switchable_interp_encodings[SWITCHABLE_FILTERS] =
-  {{0, 1}, {4, 3}, {3, 2}, {5, 3}};
-#elif CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 5
+#if CONFIG_EXT_INTERP
 static const struct vp10_token switchable_interp_encodings[SWITCHABLE_FILTERS] =
   {{0, 1}, {4, 3}, {6, 3}, {5, 3}, {7, 3}};
 #else
 static const struct vp10_token switchable_interp_encodings[SWITCHABLE_FILTERS] =
   {{0, 1}, {2, 2}, {3, 2}};
-#endif  // CONFIG_EXT_INTERP && SWITCHABLE_FILTERS == 4
+#endif  // CONFIG_EXT_INTERP
 #if CONFIG_EXT_PARTITION_TYPES
 static const struct vp10_token ext_partition_encodings[EXT_PARTITION_TYPES] =
   {{0, 1}, {4, 3}, {12, 4}, {7, 3}, {10, 4}, {11, 4}, {26, 5}, {27, 5}};
@@ -923,18 +920,42 @@
                                            vp10_writer *w) {
   VP10_COMMON *const cm = &cpi->common;
   const MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
+#if CONFIG_DUAL_FILTER
+  int dir;
+#endif
   if (cm->interp_filter == SWITCHABLE) {
-    const int ctx = vp10_get_pred_context_switchable_interp(xd);
 #if CONFIG_EXT_INTERP
+#if CONFIG_DUAL_FILTER
+    if (!vp10_is_interp_needed(xd)) {
+      assert(mbmi->interp_filter[0] == EIGHTTAP_REGULAR);
+      return;
+    }
+#else
     if (!vp10_is_interp_needed(xd)) {
       assert(mbmi->interp_filter == EIGHTTAP_REGULAR);
       return;
     }
-#endif
+#endif  // CONFIG_DUAL_FILTER
+#endif  // CONFIG_EXT_INTERP
+#if CONFIG_DUAL_FILTER
+    for (dir = 0; dir < 4; ++dir) {
+      const int frame_idx = (dir >> 1);
+      if (mbmi->ref_frame[frame_idx] > INTRA_FRAME &&
+          has_subpel_mv_component(xd, dir)) {
+        const int ctx = vp10_get_pred_context_switchable_interp(xd, dir);
+        vp10_write_token(w, vp10_switchable_interp_tree,
+              cm->fc->switchable_interp_prob[ctx],
+              &switchable_interp_encodings[mbmi->interp_filter[dir]]);
+        ++cpi->interp_filter_selected[0][mbmi->interp_filter[dir]];
+      }
+    }
+#else
+    const int ctx = vp10_get_pred_context_switchable_interp(xd);
     vp10_write_token(w, vp10_switchable_interp_tree,
                      cm->fc->switchable_interp_prob[ctx],
                      &switchable_interp_encodings[mbmi->interp_filter]);
     ++cpi->interp_filter_selected[0][mbmi->interp_filter];
+#endif
   }
 }
 
@@ -1076,7 +1097,7 @@
         write_uniform(w, 2 * MAX_ANGLE_DELTAS + 1,
                       MAX_ANGLE_DELTAS + mbmi->angle_delta[0]);
         p_angle = mode_to_angle_map[mode] + mbmi->angle_delta[0] * ANGLE_STEP;
-        if (pick_intra_filter(p_angle)) {
+        if (vp10_is_intra_filter_switchable(p_angle)) {
           const int ctx = vp10_get_pred_context_intra_interp(xd);
           vp10_write_token(w, vp10_intra_filter_tree,
                            cm->fc->intra_filter_probs[ctx],
@@ -1143,7 +1164,7 @@
       }
     }
 
-#if !CONFIG_EXT_INTERP
+#if !CONFIG_EXT_INTERP && !CONFIG_DUAL_FILTER
     write_switchable_interp_filter(cpi, xd, w);
 #endif  // !CONFIG_EXT_INTERP
 
@@ -1351,7 +1372,7 @@
     }
 #endif  // CONFIG_EXT_INTER
 
-#if CONFIG_EXT_INTERP
+#if CONFIG_EXT_INTERP || CONFIG_DUAL_FILTER
     write_switchable_interp_filter(cpi, xd, w);
 #endif  // CONFIG_EXT_INTERP
   }
@@ -1439,7 +1460,7 @@
                     MAX_ANGLE_DELTAS + mbmi->angle_delta[0]);
       p_angle =
           mode_to_angle_map[mbmi->mode] + mbmi->angle_delta[0] * ANGLE_STEP;
-      if (pick_intra_filter(p_angle)) {
+      if (vp10_is_intra_filter_switchable(p_angle)) {
         vp10_write_token(w, vp10_intra_filter_tree,
                          cm->fc->intra_filter_probs[intra_filter_ctx],
                          &intra_filter_encodings[mbmi->intra_filter]);
@@ -2621,14 +2642,20 @@
 
 static int get_refresh_mask(VP10_COMP *cpi) {
   int refresh_mask = 0;
+
 #if CONFIG_EXT_REFS
-  int ref_frame;
-  for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame) {
-    refresh_mask |= (cpi->refresh_last_frames[ref_frame - LAST_FRAME] <<
-                     cpi->lst_fb_idxes[ref_frame - LAST_FRAME]);
-  }
+  // NOTE(zoeliu): When LAST_FRAME is to get refreshed, the decoder will be
+  // notified to get LAST4_FRAME refreshed and then the virtual indexes for all
+  // the 4 LAST reference frames will be updated accordingly, i.e.:
+  // (1) The original virtual index for LAST4_FRAME will become the new virtual
+  //     index for LAST_FRAME; and
+  // (2) The original virtual indexes for LAST_FRAME ~ LAST3_FRAME will be
+  //     shifted and become the new virtual indexes for LAST2_FRAME ~
+  //     LAST4_FRAME.
+  refresh_mask |= (cpi->refresh_last_frame <<
+      cpi->lst_fb_idxes[LAST4_FRAME - LAST_FRAME]);
 #else
-  refresh_mask = cpi->refresh_last_frame << cpi->lst_fb_idx;
+  refresh_mask |= (cpi->refresh_last_frame << cpi->lst_fb_idx);
 #endif  // CONFIG_EXT_REFS
 
   if (vp10_preserve_existing_gf(cpi)) {
diff --git a/vp10/encoder/denoiser.c b/vp10/encoder/denoiser.c
index fb0280a..43c94b1 100644
--- a/vp10/encoder/denoiser.c
+++ b/vp10/encoder/denoiser.c
@@ -387,17 +387,9 @@
 void vp10_denoiser_update_frame_info(VP9_DENOISER *denoiser,
                                     YV12_BUFFER_CONFIG src,
                                     FRAME_TYPE frame_type,
-#if CONFIG_EXT_REFS
-                                    int refresh_last_frames[LAST_REF_FRAMES],
-#else
                                     int refresh_last_frame,
-#endif  // CONFIG_EXT_REFS
                                     int refresh_alt_ref_frame,
                                     int refresh_golden_frame) {
-#if CONFIG_EXT_REFS
-  int ref_frame;
-#endif  // CONFIG_EXT_REFS
-
   if (frame_type == KEY_FRAME) {
     int i;
     // Start at 1 so as not to overwrite the INTRA_FRAME
@@ -415,19 +407,10 @@
     swap_frame_buffer(&denoiser->running_avg_y[GOLDEN_FRAME],
                       &denoiser->running_avg_y[INTRA_FRAME]);
   }
-#if CONFIG_EXT_REFS
-  for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame) {
-    if (refresh_last_frames[ref_frame - LAST_FRAME]) {
-      swap_frame_buffer(&denoiser->running_avg_y[ref_frame],
-                        &denoiser->running_avg_y[INTRA_FRAME]);
-    }
-  }
-#else
   if (refresh_last_frame) {
     swap_frame_buffer(&denoiser->running_avg_y[LAST_FRAME],
                       &denoiser->running_avg_y[INTRA_FRAME]);
   }
-#endif  // CONFIG_EXT_REFS
 }
 
 void vp10_denoiser_reset_frame_stats(PICK_MODE_CONTEXT *ctx) {
diff --git a/vp10/encoder/denoiser.h b/vp10/encoder/denoiser.h
index f48cbb0..8182762 100644
--- a/vp10/encoder/denoiser.h
+++ b/vp10/encoder/denoiser.h
@@ -35,11 +35,7 @@
 void vp10_denoiser_update_frame_info(VP9_DENOISER *denoiser,
                                     YV12_BUFFER_CONFIG src,
                                     FRAME_TYPE frame_type,
-#if CONFIG_EXT_REFS
-                                    int refresh_last_frames[LAST_REF_FRAMES],
-#else
                                     int refresh_last_frame,
-#endif  // CONFIG_EXT_REFS
                                     int refresh_alt_ref_frame,
                                     int refresh_golden_frame);
 
diff --git a/vp10/encoder/encodeframe.c b/vp10/encoder/encodeframe.c
index 01d68ca..3050d1e 100644
--- a/vp10/encoder/encodeframe.c
+++ b/vp10/encoder/encodeframe.c
@@ -927,7 +927,12 @@
     mbmi->ref_frame[1] = NONE;
     mbmi->sb_type = cm->sb_size;
     mbmi->mv[0].as_int = 0;
+#if CONFIG_DUAL_FILTER
+    for (i = 0; i < 4; ++i)
+      mbmi->interp_filter[i] = BILINEAR;
+#else
     mbmi->interp_filter = BILINEAR;
+#endif
 
     y_sad = vp10_int_pro_motion_estimation(cpi, x, bsize, mi_row, mi_col);
 
@@ -1018,6 +1023,34 @@
   set_vt_partitioning(cpi, x, xd, vt, mi_row, mi_col, thre, bmin);
 }
 
+#if CONFIG_DUAL_FILTER
+static void reset_intmv_filter_type(VP10_COMMON *cm,
+                                    MACROBLOCKD *xd, MB_MODE_INFO *mbmi) {
+  int dir;
+  for (dir = 0; dir < 4; ++dir) {
+    const int frame_idx = (dir >> 1);
+    if (mbmi->ref_frame[frame_idx] > INTRA_FRAME &&
+        !has_subpel_mv_component(xd, dir))
+      mbmi->interp_filter[dir] = (cm->interp_filter == SWITCHABLE) ?
+          EIGHTTAP_REGULAR : cm->interp_filter;
+  }
+}
+
+static void update_filter_type_count(FRAME_COUNTS *counts,
+                                     const MACROBLOCKD *xd,
+                                     const MB_MODE_INFO *mbmi) {
+  int dir;
+  for (dir = 0; dir < 4; ++dir) {
+    const int frame_idx = (dir >> 1);
+    if (mbmi->ref_frame[frame_idx] > INTRA_FRAME &&
+        has_subpel_mv_component(xd, dir)) {
+      const int ctx = vp10_get_pred_context_switchable_interp(xd, dir);
+      ++counts->switchable_interp[ctx][mbmi->interp_filter[dir]];
+    }
+  }
+}
+#endif
+
 static void update_state(VP10_COMP *cpi, ThreadData *td,
                          PICK_MODE_CONTEXT *ctx,
                          int mi_row, int mi_col, BLOCK_SIZE bsize,
@@ -1057,6 +1090,10 @@
   *mi_addr = *mi;
   *x->mbmi_ext = ctx->mbmi_ext;
 
+#if CONFIG_DUAL_FILTER
+  reset_intmv_filter_type(cm, xd, mbmi);
+#endif
+
 #if CONFIG_REF_MV
   rf_type = vp10_ref_frame_type(mbmi->ref_frame);
   if (x->mbmi_ext->ref_mv_count[rf_type] > 1 &&
@@ -1167,8 +1204,12 @@
           && vp10_is_interp_needed(xd)
 #endif
           ) {
+#if CONFIG_DUAL_FILTER
+        update_filter_type_count(td->counts, xd, mbmi);
+#else
         const int ctx = vp10_get_pred_context_switchable_interp(xd);
         ++td->counts->switchable_interp[ctx][mbmi->interp_filter];
+#endif
       }
     }
 
@@ -1224,6 +1265,10 @@
   assert(is_inter_block(mbmi));
   assert(mbmi->tx_size == ctx->mic.mbmi.tx_size);
 
+#if CONFIG_DUAL_FILTER
+  reset_intmv_filter_type(cm, xd, mbmi);
+#endif
+
 #if CONFIG_REF_MV
   rf_type = vp10_ref_frame_type(mbmi->ref_frame);
   if (x->mbmi_ext->ref_mv_count[rf_type] > 1 &&
@@ -1311,8 +1356,12 @@
         && vp10_is_interp_needed(xd)
 #endif
         ) {
+#if CONFIG_DUAL_FILTER
+      update_filter_type_count(td->counts, xd, mbmi);
+#else
       const int ctx = vp10_get_pred_context_switchable_interp(xd);
       ++td->counts->switchable_interp[ctx][mbmi->interp_filter];
+#endif
     }
 
     rdc->comp_pred_diff[SINGLE_REFERENCE] += ctx->single_pred_diff;
@@ -3563,9 +3612,15 @@
     subsize = get_subsize(bsize, PARTITION_SPLIT);
     if (bsize == BLOCK_8X8) {
       i = 4;
+#if CONFIG_DUAL_FILTER
+      if (cpi->sf.adaptive_pred_interp_filter && partition_none_allowed)
+        pc_tree->leaf_split[0]->pred_interp_filter =
+            ctx->mic.mbmi.interp_filter[0];
+#else
       if (cpi->sf.adaptive_pred_interp_filter && partition_none_allowed)
         pc_tree->leaf_split[0]->pred_interp_filter =
             ctx->mic.mbmi.interp_filter;
+#endif
 #if CONFIG_SUPERTX
       rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &sum_rdc,
                        &sum_rate_nocoef,
@@ -3747,10 +3802,17 @@
     subsize = get_subsize(bsize, PARTITION_HORZ);
     if (cpi->sf.adaptive_motion_search)
       load_pred_mv(x, ctx);
+#if CONFIG_DUAL_FILTER
+    if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
+        partition_none_allowed)
+      pc_tree->horizontal[0].pred_interp_filter =
+          ctx->mic.mbmi.interp_filter[0];
+#else
     if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
         partition_none_allowed)
       pc_tree->horizontal[0].pred_interp_filter =
           ctx->mic.mbmi.interp_filter;
+#endif
     rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &sum_rdc,
 #if CONFIG_SUPERTX
                      &sum_rate_nocoef,
@@ -3775,10 +3837,18 @@
 
       if (cpi->sf.adaptive_motion_search)
         load_pred_mv(x, ctx);
+
+#if CONFIG_DUAL_FILTER
+      if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
+          partition_none_allowed)
+        pc_tree->horizontal[1].pred_interp_filter =
+            ctx->mic.mbmi.interp_filter[0];
+#else
       if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
           partition_none_allowed)
         pc_tree->horizontal[1].pred_interp_filter =
             ctx->mic.mbmi.interp_filter;
+#endif
 #if CONFIG_SUPERTX
       rd_pick_sb_modes(cpi, tile_data, x, mi_row + mi_step, mi_col,
                        &this_rdc, &this_rate_nocoef,
@@ -3878,10 +3948,18 @@
 
     if (cpi->sf.adaptive_motion_search)
       load_pred_mv(x, ctx);
+
+#if CONFIG_DUAL_FILTER
+    if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
+        partition_none_allowed)
+      pc_tree->vertical[0].pred_interp_filter =
+          ctx->mic.mbmi.interp_filter[0];
+#else
     if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
         partition_none_allowed)
       pc_tree->vertical[0].pred_interp_filter =
           ctx->mic.mbmi.interp_filter;
+#endif
     rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &sum_rdc,
 #if CONFIG_SUPERTX
                      &sum_rate_nocoef,
@@ -3905,10 +3983,18 @@
 
       if (cpi->sf.adaptive_motion_search)
         load_pred_mv(x, ctx);
+
+#if CONFIG_DUAL_FILTER
+      if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
+          partition_none_allowed)
+        pc_tree->vertical[1].pred_interp_filter =
+            ctx->mic.mbmi.interp_filter[0];
+#else
       if (cpi->sf.adaptive_pred_interp_filter && bsize == BLOCK_8X8 &&
           partition_none_allowed)
         pc_tree->vertical[1].pred_interp_filter =
             ctx->mic.mbmi.interp_filter;
+#endif
 #if CONFIG_SUPERTX
       rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col + mi_step, &this_rdc,
                        &this_rate_nocoef,
@@ -4477,10 +4563,13 @@
   cpi->last_frame_distortion = cpi->frame_distortion;
 #endif
 }
+
+#if !CONFIG_DUAL_FILTER
 static INTERP_FILTER get_cm_interp_filter(VP10_COMP *cpi) {
   (void)cpi;
   return SWITCHABLE;
 }
+#endif
 
 void vp10_encode_frame(VP10_COMP *cpi) {
   VP10_COMMON *const cm = &cpi->common;
@@ -4548,9 +4637,11 @@
     else
       cm->reference_mode = REFERENCE_MODE_SELECT;
 
+#if !CONFIG_DUAL_FILTER
     if (cm->interp_filter == SWITCHABLE) {
       cm->interp_filter = get_cm_interp_filter(cpi);
     }
+#endif
 
     encode_frame_internal(cpi);
 
@@ -4858,7 +4949,7 @@
         const int intra_filter_ctx = vp10_get_pred_context_intra_interp(xd);
         p_angle = mode_to_angle_map[mbmi->mode] +
             mbmi->angle_delta[0] * ANGLE_STEP;
-        if (pick_intra_filter(p_angle))
+        if (vp10_is_intra_filter_switchable(p_angle))
           ++counts->intra_filter[intra_filter_ctx][mbmi->intra_filter];
       }
     }
@@ -4879,6 +4970,7 @@
   } else {
     int ref;
     const int is_compound = has_second_ref(mbmi);
+
     set_ref_ptrs(cm, xd, mbmi->ref_frame[0], mbmi->ref_frame[1]);
     for (ref = 0; ref < 1 + is_compound; ++ref) {
       YV12_BUFFER_CONFIG *cfg = get_ref_frame_buffer(cpi,
diff --git a/vp10/encoder/encoder.c b/vp10/encoder/encoder.c
index 21ed965..8ed09f3 100644
--- a/vp10/encoder/encoder.c
+++ b/vp10/encoder/encoder.c
@@ -931,10 +931,6 @@
   cpi->td.counts = &cm->counts;
 
   // change includes all joint functionality
-#if CONFIG_EXT_REFS
-  cpi->last_ref_to_refresh = LAST_FRAME;
-#endif  // CONFIG_EXT_REFS
-
   vp10_change_config(cpi, oxcf);
 
   cpi->static_mb_pct = 0;
@@ -1963,9 +1959,6 @@
 void vp10_change_config(struct VP10_COMP *cpi, const VP10EncoderConfig *oxcf) {
   VP10_COMMON *const cm = &cpi->common;
   RATE_CONTROL *const rc = &cpi->rc;
-#if CONFIG_EXT_REFS
-  int ref_frame;
-#endif  // CONFIG_EXT_REFS
 
   if (cm->profile != oxcf->profile)
     cm->profile = oxcf->profile;
@@ -1990,17 +1983,7 @@
   }
 
   cpi->refresh_golden_frame = 0;
-
-#if CONFIG_EXT_REFS
-  for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame) {
-    if (ref_frame == cpi->last_ref_to_refresh)
-      cpi->refresh_last_frames[ref_frame - LAST_FRAME] = 1;
-    else
-      cpi->refresh_last_frames[ref_frame - LAST_FRAME] = 0;
-  }
-#else
   cpi->refresh_last_frame = 1;
-#endif  // CONFIG_EXT_REFS
 
   cm->refresh_frame_context =
       oxcf->error_resilient_mode ? REFRESH_FRAME_CONTEXT_OFF :
@@ -2691,14 +2674,7 @@
 void vp10_update_reference(VP10_COMP *cpi, int ref_frame_flags) {
   cpi->ext_refresh_golden_frame = (ref_frame_flags & VP9_GOLD_FLAG) != 0;
   cpi->ext_refresh_alt_ref_frame = (ref_frame_flags & VP9_ALT_FLAG) != 0;
-#if CONFIG_EXT_REFS
-  cpi->ext_refresh_last_frames[0] = (ref_frame_flags & VP9_LAST_FLAG) != 0;
-  cpi->ext_refresh_last_frames[1] = (ref_frame_flags & VP9_LAST2_FLAG) != 0;
-  cpi->ext_refresh_last_frames[2] = (ref_frame_flags & VP9_LAST3_FLAG) != 0;
-  cpi->ext_refresh_last_frames[3] = (ref_frame_flags & VP9_LAST4_FLAG) != 0;
-#else
   cpi->ext_refresh_last_frame = (ref_frame_flags & VP9_LAST_FLAG) != 0;
-#endif  // CONFIG_EXT_REFS
   cpi->ext_refresh_frame_flags_pending = 1;
 }
 
@@ -3136,27 +3112,79 @@
     }
   }
 
-#if CONFIG_EXT_REFS
-  for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame) {
-    const int ref_idx = ref_frame - LAST_FRAME;
-    if (cpi->refresh_last_frames[ref_idx]) {
-      ref_cnt_fb(pool->frame_bufs,
-                 &cm->ref_frame_map[cpi->lst_fb_idxes[ref_idx]],
-                 cm->new_fb_idx);
-      if (!cpi->rc.is_src_frame_alt_ref) {
-        memcpy(cpi->interp_filter_selected[ref_frame],
-               cpi->interp_filter_selected[0],
-               sizeof(cpi->interp_filter_selected[0]));
-      }
-    }
-  }
-  // NOTE: The order for the refreshing of the 4 last reference frames are:
-  // LAST_FRAME -> LAST2_FRAME -> LAST3_FRAME -> LAST4_FRAME -> LAST_FRAME
-  cpi->last_ref_to_refresh += 1;
-  if (cpi->last_ref_to_refresh == LAST4_FRAME)
-    cpi->last_ref_to_refresh = LAST_FRAME;
-#else
   if (cpi->refresh_last_frame) {
+#if CONFIG_EXT_REFS
+    // NOTE(zoeliu): We have two layers of mapping (1) from the per-frame
+    // reference to the reference frame buffer virtual index; and then (2) from
+    // the virtual index to the reference frame buffer physical index:
+    //
+    // LAST_FRAME,      ..., LAST4_FRAME,     GOLDEN_FRAME,    ALTREF_FRAME
+    //      |                     |                |                |
+    //      v                     v                v                v
+    // lst_fb_idxes[0], ..., lst_fb_idxes[3], gld_fb_idx,      alt_fb_idx
+    //      |                     |                |                |
+    //      v                     v                v                v
+    // ref_frame_map[], ..., ref_frame_map[], ref_frame_map[], ref_frame_map[]
+    //
+    // When refresh_last_frame is set, it is intended to retire LAST4_FRAME,
+    // have all the other 3 reference frames shifted as follows:
+    // LAST_FRAME -> LAST2_FRAME -> LAST3_FRAME -> LAST4_FRAME,
+    // , and then have LAST_FRAME refreshed by the newly coded frame.
+    //
+    // To fulfill it, the decoder will be notified to execute following 2 steps:
+    //
+    // (a) To change ref_frame_map[] and have the virtual index of LAST4_FRAME
+    //     to point to the newly coded frame, i.e.
+    //     ref_frame_map[lst_fb_idexes[3]] => new_fb_idx;
+    //
+    // (b) To change the 1st layer mapping to have LAST_FRAME mapped to the
+    //     original virtual index of LAST4_FRAME and have all the other mapping
+    //     shifted as follows:
+    // LAST_FRAME,      LAST2_FRAME,     LAST3_FRAME,     LAST4_FRAME
+    //      |                |                |                |
+    //      v                v                v                v
+    // lst_fb_idxes[3], lst_fb_idxes[0], lst_fb_idxes[1], lst_fb_idxes[2]
+    int tmp;
+
+    if (cm->frame_type == KEY_FRAME) {
+      for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame) {
+        ref_cnt_fb(pool->frame_bufs,
+                   &cm->ref_frame_map[cpi->lst_fb_idxes[ref_frame-LAST_FRAME]],
+                   cm->new_fb_idx);
+
+        if (use_upsampled_ref)
+          uref_cnt_fb(
+              cpi->upsampled_ref_bufs,
+              &cpi->upsampled_ref_idx[cpi->lst_fb_idxes[ref_frame-LAST_FRAME]],
+              new_uidx);
+      }
+    } else {
+      ref_cnt_fb(pool->frame_bufs,
+                 &cm->ref_frame_map[cpi->lst_fb_idxes[LAST4_FRAME-LAST_FRAME]],
+                 cm->new_fb_idx);
+
+      if (use_upsampled_ref)
+        uref_cnt_fb(
+            cpi->upsampled_ref_bufs,
+            &cpi->upsampled_ref_idx[cpi->lst_fb_idxes[LAST4_FRAME-LAST_FRAME]],
+            new_uidx);
+
+      tmp = cpi->lst_fb_idxes[LAST4_FRAME-LAST_FRAME];
+      for (ref_frame = LAST4_FRAME; ref_frame > LAST_FRAME; --ref_frame) {
+        cpi->lst_fb_idxes[ref_frame - LAST_FRAME] =
+            cpi->lst_fb_idxes[ref_frame - LAST_FRAME - 1];
+        if (!cpi->rc.is_src_frame_alt_ref) {
+          memcpy(cpi->interp_filter_selected[ref_frame],
+                 cpi->interp_filter_selected[ref_frame - 1],
+                 sizeof(cpi->interp_filter_selected[ref_frame - 1]));
+        }
+      }
+      cpi->lst_fb_idxes[LAST_FRAME-LAST_FRAME] = tmp;
+      memcpy(cpi->interp_filter_selected[LAST_FRAME],
+             cpi->interp_filter_selected[0],
+             sizeof(cpi->interp_filter_selected[0]));
+    }
+#else  // CONFIG_EXT_REFS
     ref_cnt_fb(pool->frame_bufs,
                &cm->ref_frame_map[cpi->lst_fb_idx], cm->new_fb_idx);
     if (use_upsampled_ref)
@@ -3168,19 +3196,15 @@
              cpi->interp_filter_selected[0],
              sizeof(cpi->interp_filter_selected[0]));
     }
-  }
 #endif  // CONFIG_EXT_REFS
+  }
 
 #if CONFIG_VP9_TEMPORAL_DENOISING
   if (cpi->oxcf.noise_sensitivity > 0) {
     vp10_denoiser_update_frame_info(&cpi->denoiser,
                                    *cpi->Source,
                                    cpi->common.frame_type,
-#if CONFIG_EXT_REFS
-                                   cpi->refresh_last_frames,
-#else
                                    cpi->refresh_last_frame,
-#endif  // CONFIG_EXT_REFS
                                    cpi->refresh_alt_ref_frame,
                                    cpi->refresh_golden_frame);
   }
@@ -3375,14 +3399,12 @@
     // Only release scaled references under certain conditions:
     // if reference will be updated, or if scaled reference has same resolution.
     int refresh[REFS_PER_FRAME];
+    refresh[0] = (cpi->refresh_last_frame) ? 1 : 0;
 #if CONFIG_EXT_REFS
-    for (i = LAST_FRAME; i <= LAST4_FRAME; ++i)
-      refresh[i - LAST_FRAME] =
-          (cpi->refresh_last_frames[i - LAST_FRAME]) ? 1 : 0;
+    refresh[1] = refresh[2] = refresh[3] = 0;
     refresh[4] = (cpi->refresh_golden_frame) ? 1 : 0;
     refresh[5] = (cpi->refresh_alt_ref_frame) ? 1 : 0;
 #else
-    refresh[0] = (cpi->refresh_last_frame) ? 1 : 0;
     refresh[1] = (cpi->refresh_golden_frame) ? 1 : 0;
     refresh[2] = (cpi->refresh_alt_ref_frame) ? 1 : 0;
 #endif  // CONFIG_EXT_REFS
@@ -4169,15 +4191,7 @@
     cpi->ext_refresh_frame_context_pending = 0;
   }
   if (cpi->ext_refresh_frame_flags_pending) {
-#if CONFIG_EXT_REFS
-    int ref_frame;
-    for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame) {
-      cpi->refresh_last_frames[ref_frame - LAST_FRAME] =
-          cpi->ext_refresh_last_frames[ref_frame - LAST_FRAME];
-    }
-#else
     cpi->refresh_last_frame = cpi->ext_refresh_last_frame;
-#endif  // CONFIG_EXT_REFS
     cpi->refresh_golden_frame = cpi->ext_refresh_golden_frame;
     cpi->refresh_alt_ref_frame = cpi->ext_refresh_alt_ref_frame;
     cpi->ext_refresh_frame_flags_pending = 0;
@@ -4374,14 +4388,7 @@
 
   // If the encoder forced a KEY_FRAME decision
   if (cm->frame_type == KEY_FRAME) {
-#if CONFIG_EXT_REFS
-    int ref_frame;
-    for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame)
-      cpi->refresh_last_frames[ref_frame - LAST_FRAME] = 1;
-    cpi->last_ref_to_refresh = LAST_FRAME;
-#else
     cpi->refresh_last_frame = 1;
-#endif  // CONFIG_EXT_REFS
   }
 
   cm->frame_to_show = get_frame_new_buffer(cm);
@@ -4606,14 +4613,7 @@
   const VP10_COMMON *cm = &cpi->common;
 
   return cm->frame_type == KEY_FRAME ||
-#if CONFIG_EXT_REFS
-         cpi->refresh_last_frames[LAST_FRAME - LAST_FRAME] ||
-         cpi->refresh_last_frames[LAST2_FRAME - LAST_FRAME] ||
-         cpi->refresh_last_frames[LAST3_FRAME - LAST_FRAME] ||
-         cpi->refresh_last_frames[LAST4_FRAME - LAST_FRAME] ||
-#else
          cpi->refresh_last_frame ||
-#endif  // CONFIG_EXT_REFS
          cpi->refresh_golden_frame ||
          cpi->refresh_alt_ref_frame ||
          cm->refresh_frame_context != REFRESH_FRAME_CONTEXT_OFF ||
@@ -4693,21 +4693,12 @@
   }
 
   if (rc->is_src_frame_alt_ref) {
-#if CONFIG_EXT_REFS
-    int ref_frame;
-#endif  // CONFIG_EXT_REFS
-
     // Current frame is an ARF overlay frame.
     cpi->alt_ref_source = NULL;
 
     // Don't refresh the last buffer for an ARF overlay frame. It will
     // become the GF so preserve last as an alternative prediction option.
-#if CONFIG_EXT_REFS
-    for (ref_frame = LAST_FRAME; ref_frame <= LAST4_FRAME; ++ref_frame)
-      cpi->refresh_last_frames[ref_frame - LAST_FRAME] = 0;
-#else
     cpi->refresh_last_frame = 0;
-#endif  // CONFIG_EXT_REFS
   }
 }
 
@@ -4857,16 +4848,7 @@
           oxcf->frame_parallel_decoding_mode ? REFRESH_FRAME_CONTEXT_FORWARD
                                              : REFRESH_FRAME_CONTEXT_BACKWARD;
 
-#if CONFIG_EXT_REFS
-  for (i = LAST_FRAME; i <= LAST4_FRAME; ++i) {
-    if (i == cpi->last_ref_to_refresh)
-      cpi->refresh_last_frames[i - LAST_FRAME] = 1;
-    else
-      cpi->refresh_last_frames[i - LAST_FRAME] = 0;
-  }
-#else
   cpi->refresh_last_frame = 1;
-#endif  // CONFIG_EXT_REFS
   cpi->refresh_golden_frame = 0;
   cpi->refresh_alt_ref_frame = 0;
 
@@ -4904,12 +4886,7 @@
       cm->intra_only = 0;
       cpi->refresh_alt_ref_frame = 1;
       cpi->refresh_golden_frame = 0;
-#if CONFIG_EXT_REFS
-      for (i = LAST_FRAME; i <= LAST4_FRAME; ++i)
-        cpi->refresh_last_frames[i - LAST_FRAME] = 0;
-#else
       cpi->refresh_last_frame = 0;
-#endif  // CONFIG_EXT_REFS
       rc->is_src_frame_alt_ref = 0;
     }
     rc->source_alt_ref_pending = 0;
diff --git a/vp10/encoder/encoder.h b/vp10/encoder/encoder.h
index a66b4e9..93f274d 100644
--- a/vp10/encoder/encoder.h
+++ b/vp10/encoder/encoder.h
@@ -344,10 +344,6 @@
   // For a still frame, this flag is set to 1 to skip partition search.
   int partition_search_skippable_frame;
 
-#if CONFIG_EXT_REFS
-  int last_ref_to_refresh;
-#endif  // CONFIG_EXT_REFS
-
   int scaled_ref_idx[MAX_REF_FRAMES];
 #if CONFIG_EXT_REFS
   int lst_fb_idxes[LAST_REF_FRAMES];
@@ -357,20 +353,12 @@
   int gld_fb_idx;
   int alt_fb_idx;
 
-#if CONFIG_EXT_REFS
-  int refresh_last_frames[LAST_REF_FRAMES];
-#else
   int refresh_last_frame;
-#endif  // CONFIG_EXT_REFS
   int refresh_golden_frame;
   int refresh_alt_ref_frame;
 
   int ext_refresh_frame_flags_pending;
-#if CONFIG_EXT_REFS
-  int ext_refresh_last_frames[LAST_REF_FRAMES];
-#else
   int ext_refresh_last_frame;
-#endif  // CONFIG_EXT_REFS
   int ext_refresh_golden_frame;
   int ext_refresh_alt_ref_frame;
 
diff --git a/vp10/encoder/firstpass.c b/vp10/encoder/firstpass.c
index 61afe9d..68e8107 100644
--- a/vp10/encoder/firstpass.c
+++ b/vp10/encoder/firstpass.c
@@ -2401,48 +2401,28 @@
   cpi->rc.is_src_frame_alt_ref = 0;
   switch (twopass->gf_group.update_type[twopass->gf_group.index]) {
     case KF_UPDATE:
-#if CONFIG_EXT_REFS
-      cpi->refresh_last_frames[LAST_FRAME - LAST_FRAME] = 1;
-#else
       cpi->refresh_last_frame = 1;
-#endif  // CONFIG_EXT_REFS
       cpi->refresh_golden_frame = 1;
       cpi->refresh_alt_ref_frame = 1;
       break;
     case LF_UPDATE:
-#if CONFIG_EXT_REFS
-      cpi->refresh_last_frames[LAST_FRAME - LAST_FRAME] = 1;
-#else
       cpi->refresh_last_frame = 1;
-#endif  // CONFIG_EXT_REFS
       cpi->refresh_golden_frame = 0;
       cpi->refresh_alt_ref_frame = 0;
       break;
     case GF_UPDATE:
-#if CONFIG_EXT_REFS
-      cpi->refresh_last_frames[LAST_FRAME - LAST_FRAME] = 1;
-#else
       cpi->refresh_last_frame = 1;
-#endif  // CONFIG_EXT_REFS
       cpi->refresh_golden_frame = 1;
       cpi->refresh_alt_ref_frame = 0;
       break;
     case OVERLAY_UPDATE:
-#if CONFIG_EXT_REFS
-      cpi->refresh_last_frames[LAST_FRAME - LAST_FRAME] = 0;
-#else
       cpi->refresh_last_frame = 0;
-#endif  // CONFIG_EXT_REFS
       cpi->refresh_golden_frame = 1;
       cpi->refresh_alt_ref_frame = 0;
       cpi->rc.is_src_frame_alt_ref = 1;
       break;
     case ARF_UPDATE:
-#if CONFIG_EXT_REFS
-      cpi->refresh_last_frames[LAST_FRAME - LAST_FRAME] = 0;
-#else
       cpi->refresh_last_frame = 0;
-#endif  // CONFIG_EXT_REFS
       cpi->refresh_golden_frame = 0;
       cpi->refresh_alt_ref_frame = 1;
       break;
diff --git a/vp10/encoder/rd.c b/vp10/encoder/rd.c
index 7ea1735..8eff086 100644
--- a/vp10/encoder/rd.c
+++ b/vp10/encoder/rd.c
@@ -722,6 +722,25 @@
           &cm->buffer_pool->frame_bufs[scaled_idx].buf : NULL;
 }
 
+#if CONFIG_DUAL_FILTER
+int vp10_get_switchable_rate(const VP10_COMP *cpi,
+                             const MACROBLOCKD *const xd) {
+  const MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
+  int inter_filter_cost = 0;
+  int dir;
+
+  for (dir = 0; dir < 4; ++dir) {
+    const int frame_idx = (dir >> 1);
+    if (mbmi->ref_frame[frame_idx] > INTRA_FRAME &&
+        has_subpel_mv_component(xd, dir)) {
+      const int ctx = vp10_get_pred_context_switchable_interp(xd, dir);
+      inter_filter_cost +=
+          cpi->switchable_interp_costs[ctx][mbmi->interp_filter[dir]];
+    }
+  }
+  return SWITCHABLE_INTERP_RATE_FACTOR * inter_filter_cost;
+}
+#else
 int vp10_get_switchable_rate(const VP10_COMP *cpi,
                              const MACROBLOCKD *const xd) {
   const MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
@@ -732,6 +751,7 @@
   return SWITCHABLE_INTERP_RATE_FACTOR *
       cpi->switchable_interp_costs[ctx][mbmi->interp_filter];
 }
+#endif
 
 void vp10_set_rd_speed_thresholds(VP10_COMP *cpi) {
   int i;
diff --git a/vp10/encoder/rdopt.c b/vp10/encoder/rdopt.c
index 87d6173..d040e0b 100644
--- a/vp10/encoder/rdopt.c
+++ b/vp10/encoder/rdopt.c
@@ -2422,7 +2422,7 @@
           mbmi->angle_delta[0] * ANGLE_STEP;
       for (filter = INTRA_FILTER_LINEAR; filter < INTRA_FILTERS; ++filter) {
         int64_t tmp_best_rd;
-        if ((FILTER_FAST_SEARCH || !pick_intra_filter(p_angle)) &&
+        if ((FILTER_FAST_SEARCH || !vp10_is_intra_filter_switchable(p_angle)) &&
             filter != INTRA_FILTER_LINEAR)
           continue;
         mic->mbmi.intra_filter = filter;
@@ -2464,8 +2464,8 @@
             mbmi->angle_delta[0] * ANGLE_STEP;
         for (filter = INTRA_FILTER_LINEAR; filter < INTRA_FILTERS; ++filter) {
           mic->mbmi.intra_filter = filter;
-          if ((FILTER_FAST_SEARCH || !pick_intra_filter(p_angle)) &&
-              filter != INTRA_FILTER_LINEAR)
+          if ((FILTER_FAST_SEARCH || !vp10_is_intra_filter_switchable(p_angle))
+              && filter != INTRA_FILTER_LINEAR)
             continue;
           pick_intra_angle_routine_sby(cpi, x, rate, rate_tokenonly,
                                        distortion, skippable,
@@ -2486,7 +2486,7 @@
           mbmi->angle_delta[0] * ANGLE_STEP;
       for (filter = INTRA_FILTER_LINEAR; filter < INTRA_FILTERS; ++filter) {
         mic->mbmi.intra_filter = filter;
-        if ((FILTER_FAST_SEARCH || !pick_intra_filter(p_angle)) &&
+        if ((FILTER_FAST_SEARCH || !vp10_is_intra_filter_switchable(p_angle)) &&
             filter != INTRA_FILTER_LINEAR)
           continue;
         pick_intra_angle_routine_sby(cpi, x, rate, rate_tokenonly,
@@ -2505,7 +2505,7 @@
     mbmi->angle_delta[0] = best_angle_delta;
     p_angle = mode_to_angle_map[mbmi->mode] +
         mbmi->angle_delta[0] * ANGLE_STEP;
-    if (pick_intra_filter(p_angle)) {
+    if (vp10_is_intra_filter_switchable(p_angle)) {
       for (filter = INTRA_FILTER_LINEAR + 1; filter < INTRA_FILTERS; ++filter) {
         mic->mbmi.intra_filter = filter;
         pick_intra_angle_routine_sby(cpi, x, rate, rate_tokenonly,
@@ -2778,7 +2778,7 @@
                                       mic->mbmi.angle_delta[0]);
       p_angle = mode_to_angle_map[mic->mbmi.mode] +
           mic->mbmi.angle_delta[0] * ANGLE_STEP;
-      if (pick_intra_filter(p_angle))
+      if (vp10_is_intra_filter_switchable(p_angle))
         this_rate +=
             cpi->intra_filter_cost[intra_filter_ctx][mic->mbmi.intra_filter];
     }
@@ -4607,7 +4607,14 @@
                        mbmi->ref_frame[1] < 0 ? 0 : mbmi->ref_frame[1]};
   int_mv ref_mv[2];
   int ite, ref;
+#if CONFIG_DUAL_FILTER
+  INTERP_FILTER interp_filter[4] = {
+      mbmi->interp_filter[0], mbmi->interp_filter[1],
+      mbmi->interp_filter[2], mbmi->interp_filter[3],
+  };
+#else
   const INTERP_FILTER interp_filter = mbmi->interp_filter;
+#endif
   struct scale_factors sf;
 
   // Do joint motion search in compound mode to get more accurate mv.
@@ -4680,6 +4687,14 @@
     ref_yv12[0] = xd->plane[0].pre[0];
     ref_yv12[1] = xd->plane[0].pre[1];
 
+#if CONFIG_DUAL_FILTER
+    // reload the filter types
+    interp_filter[0] = (id == 0) ?
+        mbmi->interp_filter[2] : mbmi->interp_filter[0];
+    interp_filter[1] = (id == 0) ?
+        mbmi->interp_filter[3] : mbmi->interp_filter[1];
+#endif
+
     // Get the prediction block from the 'other' reference frame.
 #if CONFIG_VP9_HIGHBITDEPTH
     if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) {
@@ -5255,13 +5270,21 @@
             continue;
         }
 
+#if CONFIG_DUAL_FILTER
+        (void)run_mv_search;
+#endif
+
         if (has_second_rf &&
 #if CONFIG_EXT_INTER
             this_mode == NEW_NEWMV &&
 #else
             this_mode == NEWMV &&
 #endif  // CONFIG_EXT_INTER
+#if CONFIG_DUAL_FILTER
+            1) {
+#else
             (mbmi->interp_filter == EIGHTTAP_REGULAR || run_mv_search)) {
+#endif
           // adjust src pointers
           mi_buf_shift(x, i);
           if (cpi->sf.comp_inter_joint_search_thresh <= bsize) {
@@ -6182,6 +6205,10 @@
   const int this_mode = mbmi->mode;
   int refs[2] = { mbmi->ref_frame[0],
       (mbmi->ref_frame[1] < 0 ? 0 : mbmi->ref_frame[1]) };
+#if CONFIG_DUAL_FILTER
+  (void)pred_filter_search;
+  return SWITCHABLE;
+#else
   if (pred_filter_search) {
     INTERP_FILTER af = SWITCHABLE, lf = SWITCHABLE;
     if (xd->up_available)
@@ -6197,6 +6224,7 @@
 #endif  // CONFIG_EXT_INTER
       best_filter = af;
   }
+#endif
   if (is_comp_pred) {
     if (cpi->sf.adaptive_mode_search) {
 #if CONFIG_EXT_INTER
@@ -6672,7 +6700,12 @@
       int tmp_skip_sb = 0;
       int64_t tmp_skip_sse = INT64_MAX;
 
+#if CONFIG_DUAL_FILTER
+      for (j = 0; j < 4; ++j)
+        mbmi->interp_filter[j] = i;
+#else
       mbmi->interp_filter = i;
+#endif
       rs = vp10_get_switchable_rate(cpi, xd);
       rs_rd = RDCOST(x->rdmult, x->rddiv, rs, 0);
 
@@ -6697,7 +6730,13 @@
             is_comp_interintra_pred ||
 #endif  // CONFIG_EXT_INTER
             (cm->interp_filter != SWITCHABLE &&
-             (cm->interp_filter == mbmi->interp_filter ||
+             (
+#if CONFIG_DUAL_FILTER
+              cm->interp_filter == mbmi->interp_filter[0]
+#else
+              cm->interp_filter == mbmi->interp_filter
+#endif
+              ||
               (i == 0 && intpel_mv && IsInterpolatingFilter(i))))) {
           restore_dst_buf(xd, orig_dst, orig_dst_stride);
         } else {
@@ -6730,7 +6769,11 @@
 
       if (newbest) {
         best_rd = rd;
+#if CONFIG_DUAL_FILTER
+        best_filter = mbmi->interp_filter[0];
+#else
         best_filter = mbmi->interp_filter;
+#endif
         if (cm->interp_filter == SWITCHABLE && i &&
             !(intpel_mv && IsInterpolatingFilter(i)))
           best_needs_copy = !best_needs_copy;
@@ -6738,7 +6781,11 @@
 
       if ((cm->interp_filter == SWITCHABLE && newbest) ||
           (cm->interp_filter != SWITCHABLE &&
+#if CONFIG_DUAL_FILTER
+           cm->interp_filter == mbmi->interp_filter[0])) {
+#else
            cm->interp_filter == mbmi->interp_filter)) {
+#endif
         pred_exists = 1;
         tmp_rd = best_rd;
 
@@ -6754,8 +6801,17 @@
   }
 
   // Set the appropriate filter
+#if CONFIG_DUAL_FILTER
+  for (i = 0; i < 4; ++i) {
+    const int frame_idx = (i >> 1);
+    if (mbmi->ref_frame[frame_idx] > INTRA_FRAME)
+      mbmi->interp_filter[i] = cm->interp_filter != SWITCHABLE ?
+          cm->interp_filter : best_filter;
+  }
+#else
   mbmi->interp_filter = cm->interp_filter != SWITCHABLE ?
       cm->interp_filter : best_filter;
+#endif
   rs = cm->interp_filter == SWITCHABLE ? vp10_get_switchable_rate(cpi, xd) : 0;
 
 #if CONFIG_EXT_INTER
@@ -7106,7 +7162,12 @@
 
 #if CONFIG_EXT_INTERP
   if (!vp10_is_interp_needed(xd) && cm->interp_filter == SWITCHABLE) {
+#if CONFIG_DUAL_FILTER
+    for (i = 0; i < 4; ++i)
+      mbmi->interp_filter[i] = EIGHTTAP_REGULAR;
+#else
     mbmi->interp_filter = EIGHTTAP_REGULAR;
+#endif
     pred_exists = 0;
   }
 #endif  // CONFIG_EXT_INTERP
@@ -7136,8 +7197,13 @@
     memcpy(bsse, x->bsse, sizeof(bsse));
   }
 
+#if CONFIG_DUAL_FILTER
+  if (!is_comp_pred)
+    single_filter[this_mode][refs[0]] = mbmi->interp_filter[0];
+#else
   if (!is_comp_pred)
     single_filter[this_mode][refs[0]] = mbmi->interp_filter;
+#endif
 
   if (cpi->sf.adaptive_mode_search)
     if (is_comp_pred)
@@ -8190,8 +8256,15 @@
 #endif  // CONFIG_EXT_INTRA
     // Evaluate all sub-pel filters irrespective of whether we can use
     // them for this frame.
+#if CONFIG_DUAL_FILTER
+    for (i = 0; i < 4; ++i) {
+      mbmi->interp_filter[i] = cm->interp_filter == SWITCHABLE ?
+          EIGHTTAP_REGULAR : cm->interp_filter;
+    }
+#else
     mbmi->interp_filter = cm->interp_filter == SWITCHABLE ? EIGHTTAP_REGULAR
                                                           : cm->interp_filter;
+#endif
     mbmi->mv[0].as_int = mbmi->mv[1].as_int = 0;
 #if CONFIG_OBMC
     mbmi->obmc = 0;
@@ -8333,7 +8406,7 @@
                                     mbmi->angle_delta[0]);
         p_angle = mode_to_angle_map[mbmi->mode] +
             mbmi->angle_delta[0] * ANGLE_STEP;
-        if (pick_intra_filter(p_angle))
+        if (vp10_is_intra_filter_switchable(p_angle))
           rate2 += cpi->intra_filter_cost[intra_filter_ctx][mbmi->intra_filter];
       }
 
@@ -8955,16 +9028,17 @@
       else if (best_mbmode.mv[0].as_int == 0)
         best_mbmode.mode = ZEROMV;
     } else {
-      int_mv nearestmv[2] = { frame_mv[NEARESTMV][refs[0]],
-                              frame_mv[NEARESTMV][refs[1]] };
-      int_mv nearmv[2] = { frame_mv[NEARMV][refs[0]],
-                           frame_mv[NEARMV][refs[1]] };
+      int_mv nearestmv[2];
+      int_mv nearmv[2];
 
 #if CONFIG_EXT_INTER
       if (mbmi_ext->ref_mv_count[rf_type] > 1) {
-         nearmv[0] = mbmi_ext->ref_mv_stack[rf_type][1].this_mv;
-         nearmv[1] = mbmi_ext->ref_mv_stack[rf_type][1].comp_mv;
-       }
+        nearmv[0] = mbmi_ext->ref_mv_stack[rf_type][1].this_mv;
+        nearmv[1] = mbmi_ext->ref_mv_stack[rf_type][1].comp_mv;
+      } else {
+        nearmv[0] = frame_mv[NEARMV][refs[0]];
+        nearmv[1] = frame_mv[NEARMV][refs[1]];
+      }
 #else
       int i;
       int ref_set = (mbmi_ext->ref_mv_count[rf_type] >= 2) ?
@@ -8981,10 +9055,12 @@
         }
       }
 #endif
-
       if (mbmi_ext->ref_mv_count[rf_type] >= 1) {
         nearestmv[0] = mbmi_ext->ref_mv_stack[rf_type][0].this_mv;
         nearestmv[1] = mbmi_ext->ref_mv_stack[rf_type][0].comp_mv;
+      } else {
+        nearestmv[0] = frame_mv[NEARESTMV][refs[0]];
+        nearestmv[1] = frame_mv[NEARESTMV][refs[1]];
       }
 
       if (nearestmv[0].as_int == best_mbmode.mv[0].as_int &&
@@ -9097,9 +9173,26 @@
     }
   }
 
+#if CONFIG_DUAL_FILTER
+  assert((cm->interp_filter == SWITCHABLE) ||
+         (cm->interp_filter == best_mbmode.interp_filter[0]) ||
+         !is_inter_block(&best_mbmode));
+  assert((cm->interp_filter == SWITCHABLE) ||
+         (cm->interp_filter == best_mbmode.interp_filter[1]) ||
+         !is_inter_block(&best_mbmode));
+  if (best_mbmode.ref_frame[1] > INTRA_FRAME) {
+    assert((cm->interp_filter == SWITCHABLE) ||
+           (cm->interp_filter == best_mbmode.interp_filter[2]) ||
+           !is_inter_block(&best_mbmode));
+    assert((cm->interp_filter == SWITCHABLE) ||
+           (cm->interp_filter == best_mbmode.interp_filter[3]) ||
+           !is_inter_block(&best_mbmode));
+  }
+#else
   assert((cm->interp_filter == SWITCHABLE) ||
          (cm->interp_filter == best_mbmode.interp_filter) ||
          !is_inter_block(&best_mbmode));
+#endif
 
   if (!cpi->rc.is_src_frame_alt_ref)
     vp10_update_rd_thresh_fact(cm, tile_data->thresh_freq_fact,
@@ -9211,21 +9304,41 @@
       int rs;
       int best_rs = INT_MAX;
       for (i = 0; i < SWITCHABLE_FILTERS; ++i) {
+#if CONFIG_DUAL_FILTER
+        int k;
+        for (k = 0; k < 4; ++k)
+          mbmi->interp_filter[k] = i;
+#else
         mbmi->interp_filter = i;
+#endif
         rs = vp10_get_switchable_rate(cpi, xd);
         if (rs < best_rs) {
           best_rs = rs;
+#if CONFIG_DUAL_FILTER
+          best_filter = mbmi->interp_filter[0];
+#else
           best_filter = mbmi->interp_filter;
+#endif
         }
       }
     }
   }
   // Set the appropriate filter
   if (cm->interp_filter == SWITCHABLE) {
+#if CONFIG_DUAL_FILTER
+    for (i = 0; i < 4; ++i)
+      mbmi->interp_filter[i] = best_filter;
+#else
     mbmi->interp_filter = best_filter;
+#endif
     rate2 += vp10_get_switchable_rate(cpi, xd);
   } else {
+#if CONFIG_DUAL_FILTER
+    for (i = 0; i < 4; ++i)
+      mbmi->interp_filter[0] = cm->interp_filter;
+#else
     mbmi->interp_filter = cm->interp_filter;
+#endif
   }
 
   if (cm->reference_mode == REFERENCE_MODE_SELECT)
@@ -9246,8 +9359,13 @@
     return;
   }
 
+#if CONFIG_DUAL_FILTER
+  assert((cm->interp_filter == SWITCHABLE) ||
+         (cm->interp_filter == mbmi->interp_filter[0]));
+#else
   assert((cm->interp_filter == SWITCHABLE) ||
          (cm->interp_filter == mbmi->interp_filter));
+#endif
 
   vp10_update_rd_thresh_fact(cm, tile_data->thresh_freq_fact,
                              cpi->sf.adaptive_rd_thresh, bsize, THR_ZEROMV);
@@ -9539,8 +9657,14 @@
     mbmi->ref_frame[1] = second_ref_frame;
     // Evaluate all sub-pel filters irrespective of whether we can use
     // them for this frame.
+#if CONFIG_DUAL_FILTER
+    for (i = 0; i < 4; ++i)
+      mbmi->interp_filter[i] = cm->interp_filter == SWITCHABLE ?
+          EIGHTTAP_REGULAR : cm->interp_filter;
+#else
     mbmi->interp_filter = cm->interp_filter == SWITCHABLE ? EIGHTTAP_REGULAR
                                                           : cm->interp_filter;
+#endif
     x->skip = 0;
     set_ref_ptrs(cm, xd, ref_frame, second_ref_frame);
 
@@ -9635,7 +9759,13 @@
             int newbest, rs;
             int64_t rs_rd;
             MB_MODE_INFO_EXT *mbmi_ext = x->mbmi_ext;
+#if CONFIG_DUAL_FILTER
+            int dir;
+            for (dir = 0; dir < 4; ++dir)
+              mbmi->interp_filter[dir] = switchable_filter_index;
+#else
             mbmi->interp_filter = switchable_filter_index;
+#endif
             tmp_rd = rd_pick_best_sub8x8_mode(cpi, x,
                                               &mbmi_ext->ref_mvs[ref_frame][0],
                                               second_ref, best_yrd, &rate,
@@ -9648,9 +9778,15 @@
                                               bsi, switchable_filter_index,
                                               mi_row, mi_col);
 #if CONFIG_EXT_INTERP
+#if CONFIG_DUAL_FILTER
+            if (!vp10_is_interp_needed(xd) && cm->interp_filter == SWITCHABLE &&
+                mbmi->interp_filter[0] != EIGHTTAP_REGULAR)  // invalid config
+              continue;
+#else
             if (!vp10_is_interp_needed(xd) && cm->interp_filter == SWITCHABLE &&
                 mbmi->interp_filter != EIGHTTAP_REGULAR)  // invalid config
               continue;
+#endif
 #endif  // CONFIG_EXT_INTERP
             if (tmp_rd == INT64_MAX)
               continue;
@@ -9661,11 +9797,21 @@
 
             newbest = (tmp_rd < tmp_best_rd);
             if (newbest) {
+#if CONFIG_DUAL_FILTER
+              tmp_best_filter = mbmi->interp_filter[0];
+#else
               tmp_best_filter = mbmi->interp_filter;
+#endif
               tmp_best_rd = tmp_rd;
             }
             if ((newbest && cm->interp_filter == SWITCHABLE) ||
-                (mbmi->interp_filter == cm->interp_filter &&
+                (
+#if CONFIG_DUAL_FILTER
+                 mbmi->interp_filter[0] == cm->interp_filter
+#else
+                 mbmi->interp_filter == cm->interp_filter
+#endif
+                 &&
                  cm->interp_filter != SWITCHABLE)) {
               tmp_best_rdu = tmp_rd;
               tmp_best_rate = rate;
@@ -9679,17 +9825,6 @@
                 x->zcoeff_blk[TX_4X4][i] = !x->plane[0].eobs[i];
               }
               pred_exists = 1;
-              if (switchable_filter_index == 0 &&
-                  sf->use_rd_breakout &&
-                  best_rd < INT64_MAX) {
-                if (tmp_best_rdu / 2 > best_rd) {
-                  // skip searching the other filters if the first is
-                  // already substantially larger than the best so far
-                  tmp_best_filter = mbmi->interp_filter;
-                  tmp_best_rdu = INT64_MAX;
-                  break;
-                }
-              }
             }
           }  // switchable_filter_index loop
         }
@@ -9698,8 +9833,14 @@
       if (tmp_best_rdu == INT64_MAX && pred_exists)
         continue;
 
+#if CONFIG_DUAL_FILTER
+      for (i = 0; i < 4; ++i)
+        mbmi->interp_filter[i] = (cm->interp_filter == SWITCHABLE ?
+                               tmp_best_filter : cm->interp_filter);
+#else
       mbmi->interp_filter = (cm->interp_filter == SWITCHABLE ?
                              tmp_best_filter : cm->interp_filter);
+#endif
 
       if (!pred_exists) {
         // Handles the special case when a filter that is not in the
@@ -9715,10 +9856,16 @@
                                           bsi, 0,
                                           mi_row, mi_col);
 #if CONFIG_EXT_INTERP
+#if CONFIG_DUAL_FILTER
         if (!vp10_is_interp_needed(xd) && cm->interp_filter == SWITCHABLE &&
-            mbmi->interp_filter != EIGHTTAP_REGULAR) {
+            mbmi->interp_filter[0] != EIGHTTAP_REGULAR)
+          for (i = 0; i < 4; ++i)
+            mbmi->interp_filter[i] = EIGHTTAP_REGULAR;
+#else
+        if (!vp10_is_interp_needed(xd) && cm->interp_filter == SWITCHABLE &&
+            mbmi->interp_filter != EIGHTTAP_REGULAR)
           mbmi->interp_filter = EIGHTTAP_REGULAR;
-        }
+#endif  // CONFIG_DUAL_FILTER
 #endif  // CONFIG_EXT_INTERP
         if (tmp_rd == INT64_MAX)
           continue;
@@ -9965,9 +10112,15 @@
     return;
   }
 
+#if CONFIG_DUAL_FILTER
+  assert((cm->interp_filter == SWITCHABLE) ||
+         (cm->interp_filter == best_mbmode.interp_filter[0]) ||
+         !is_inter_block(&best_mbmode));
+#else
   assert((cm->interp_filter == SWITCHABLE) ||
          (cm->interp_filter == best_mbmode.interp_filter) ||
          !is_inter_block(&best_mbmode));
+#endif
 
   vp10_update_rd_thresh_fact(cm, tile_data->thresh_freq_fact,
                              sf->adaptive_rd_thresh, bsize, best_ref_index);
diff --git a/vp10/encoder/temporal_filter.c b/vp10/encoder/temporal_filter.c
index b3cf899..39be057 100644
--- a/vp10/encoder/temporal_filter.c
+++ b/vp10/encoder/temporal_filter.c
@@ -49,7 +49,14 @@
   int uv_stride;
 
 #if USE_TEMPORALFILTER_12TAP
+#if CONFIG_DUAL_FILTER
+  const INTERP_FILTER interp_filter[4] = {
+      TEMPORALFILTER_12TAP, TEMPORALFILTER_12TAP,
+      TEMPORALFILTER_12TAP, TEMPORALFILTER_12TAP
+  };
+#else
   const INTERP_FILTER interp_filter = TEMPORALFILTER_12TAP;
+#endif
   (void)xd;
 #else
   const INTERP_FILTER interp_filter = xd->mi[0]->mbmi.interp_filter;
diff --git a/vp10/encoder/x86/highbd_fwd_txfm_sse4.c b/vp10/encoder/x86/highbd_fwd_txfm_sse4.c
index 949816c..d48610e 100644
--- a/vp10/encoder/x86/highbd_fwd_txfm_sse4.c
+++ b/vp10/encoder/x86/highbd_fwd_txfm_sse4.c
@@ -205,8 +205,8 @@
   in[3] = _mm_unpackhi_epi64(v1, v3);
 }
 
-void vp10_fwd_txfm2d_4x4_sse4_1(const int16_t *input, tran_low_t *coeff,
-                                int input_stride, int tx_type,
+void vp10_fwd_txfm2d_4x4_sse4_1(const int16_t *input, int32_t *coeff,
+                                const int input_stride, int tx_type,
                                 const int bd) {
   __m128i in[4];
   const TXFM_2D_CFG *cfg = NULL;
@@ -917,8 +917,8 @@
   out[15] = _mm_sub_epi32(kZero, u[1]);
 }
 
-void vp10_fwd_txfm2d_8x8_sse4_1(const int16_t *input, tran_low_t *coeff,
-                                int stride, int tx_type, int bd) {
+void vp10_fwd_txfm2d_8x8_sse4_1(const int16_t *input, int32_t *coeff,
+                                const int stride, int tx_type, const int bd) {
   __m128i in[16], out[16];
   const TXFM_2D_CFG *cfg = NULL;