Add AVX2 variant for temporal_filter_plane_c

             encode time
  cpu used    reduction
     4          1.17%
     3          0.95%
     2          0.47%
     1          0.24%

Module gains improved by a factor of ~5x w.r.t C

Change-Id: If8dd1c60cd9fc0b65fb17d05f5d207eaeba30e9a
diff --git a/av1/av1.cmake b/av1/av1.cmake
index 16d1e34..66ee33b 100644
--- a/av1/av1.cmake
+++ b/av1/av1.cmake
@@ -345,6 +345,7 @@
             "${AOM_ROOT}/av1/encoder/x86/wedge_utils_avx2.c"
             "${AOM_ROOT}/av1/encoder/x86/encodetxb_avx2.c"
             "${AOM_ROOT}/av1/encoder/x86/rdopt_avx2.c"
+            "${AOM_ROOT}/av1/encoder/x86/temporal_filter_avx2.c"
             "${AOM_ROOT}/av1/encoder/x86/pickrst_avx2.c")
 
 if(NOT CONFIG_AV1_HIGHBITDEPTH)
diff --git a/av1/common/av1_rtcd_defs.pl b/av1/common/av1_rtcd_defs.pl
index 3dd0423..a00975f 100644
--- a/av1/common/av1_rtcd_defs.pl
+++ b/av1/common/av1_rtcd_defs.pl
@@ -281,6 +281,10 @@
     specialize qw/av1_apply_temporal_filter sse4_1/;
   }
 
+  if (aom_config("CONFIG_REALTIME_ONLY") ne "yes") {
+    add_proto qw/void av1_temporal_filter_plane/, "uint8_t *frame1, unsigned int stride, uint8_t *frame2, unsigned int stride2, int block_width, int block_height, int strength, double sigma, int decay_control, const int *blk_fw, int use_32x32, unsigned int *accumulator, uint16_t *count";
+    specialize qw/av1_temporal_filter_plane avx2/;
+  }
   add_proto qw/void av1_quantize_b/, "const tran_low_t *coeff_ptr, intptr_t n_coeffs, const int16_t *zbin_ptr, const int16_t *round_ptr, const int16_t *quant_ptr, const int16_t *quant_shift_ptr, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, const int16_t *iscan, const qm_val_t * qm_ptr, const qm_val_t * iqm_ptr, int log_scale";
 
   # ENCODEMB INVOKE
diff --git a/av1/encoder/temporal_filter.c b/av1/encoder/temporal_filter.c
index f8a318c1..493c621 100644
--- a/av1/encoder/temporal_filter.c
+++ b/av1/encoder/temporal_filter.c
@@ -36,11 +36,6 @@
 #include "aom_ports/system_state.h"
 #include "aom_scale/aom_scale.h"
 
-#define EXPERIMENT_TEMPORAL_FILTER 1
-#define WINDOW_LENGTH 2
-#define WINDOW_SIZE 25
-#define SCALE 1000
-
 static unsigned int index_mult[14] = { 0,     0,     0,     0,     49152,
                                        39322, 32768, 28087, 24576, 21846,
                                        19661, 17874, 0,     15124 };
@@ -849,17 +844,17 @@
     } else {
       decay_control = 3;
     }
-    av1_temporal_filter_plane_c(frame->y_buffer + mb_y_src_offset,
-                                frame->y_stride, predictor, BW, BW, BH,
-                                strength, sigma, decay_control, blk_fw,
-                                use_32x32, accumulator, count);
+    av1_temporal_filter_plane(frame->y_buffer + mb_y_src_offset,
+                              frame->y_stride, predictor, BW, BW, BH, strength,
+                              sigma, decay_control, blk_fw, use_32x32,
+                              accumulator, count);
     if (num_planes > 1) {
-      av1_temporal_filter_plane_c(
+      av1_temporal_filter_plane(
           frame->u_buffer + mb_uv_src_offset, frame->uv_stride,
           predictor + BLK_PELS, mb_uv_width, mb_uv_width, mb_uv_height,
           strength, sigma, decay_control, blk_fw, use_32x32,
           accumulator + BLK_PELS, count + BLK_PELS);
-      av1_temporal_filter_plane_c(
+      av1_temporal_filter_plane(
           frame->v_buffer + mb_uv_src_offset, frame->uv_stride,
           predictor + (BLK_PELS << 1), mb_uv_width, mb_uv_width, mb_uv_height,
           strength, sigma, decay_control, blk_fw, use_32x32,
diff --git a/av1/encoder/temporal_filter.h b/av1/encoder/temporal_filter.h
index 2df2666..a8982f5 100644
--- a/av1/encoder/temporal_filter.h
+++ b/av1/encoder/temporal_filter.h
@@ -34,6 +34,11 @@
 #define EDGE_THRESHOLD 50
 #define SQRT_PI_BY_2 1.25331413732
 
+#define EXPERIMENT_TEMPORAL_FILTER 1
+#define WINDOW_LENGTH 2
+#define WINDOW_SIZE 25
+#define SCALE 1000
+
 static INLINE BLOCK_SIZE dims_to_size(int w, int h) {
   if (w != h) return -1;
   switch (w) {
diff --git a/av1/encoder/x86/temporal_filter_avx2.c b/av1/encoder/x86/temporal_filter_avx2.c
new file mode 100644
index 0000000..3838039
--- /dev/null
+++ b/av1/encoder/x86/temporal_filter_avx2.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2019, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <assert.h>
+#include <immintrin.h>
+
+#include "config/av1_rtcd.h"
+#include "av1/encoder/encoder.h"
+#include "av1/encoder/temporal_filter.h"
+
+#define SSE_STRIDE (BW + 2)
+
+#if EXPERIMENT_TEMPORAL_FILTER
+DECLARE_ALIGNED(32, const uint32_t, sse_bytemask[4][8]) = {
+  { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000 },
+  { 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000 },
+  { 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000 },
+  { 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }
+};
+
+DECLARE_ALIGNED(32, const uint8_t, shufflemask_16b[2][16]) = {
+  { 0, 1, 0, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
+  { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 10, 11, 10, 11 }
+};
+
+AOM_FORCE_INLINE void get_squared_error_16x16_avx2(
+    uint8_t *frame1, unsigned int stride, uint8_t *frame2, unsigned int stride2,
+    int block_width, int block_height, uint16_t *frame_sse,
+    unsigned int sse_stride) {
+  (void)block_width;
+  uint8_t *src1 = frame1;
+  uint8_t *src2 = frame2;
+  uint16_t *dst = frame_sse;
+  for (int i = 0; i < block_height; i++) {
+    __m128i vf1_128, vf2_128;
+    __m256i vf1, vf2, vdiff1, vsqdiff1;
+
+    vf1_128 = _mm_loadu_si128((__m128i *)(src1));
+    vf2_128 = _mm_loadu_si128((__m128i *)(src2));
+    vf1 = _mm256_cvtepu8_epi16(vf1_128);
+    vf2 = _mm256_cvtepu8_epi16(vf2_128);
+    vdiff1 = _mm256_sub_epi16(vf1, vf2);
+    vsqdiff1 = _mm256_mullo_epi16(vdiff1, vdiff1);
+
+    _mm256_storeu_si256((__m256i *)(dst), vsqdiff1);
+    // Set zero to unitialized memory to avoid uninitialized loads later
+    *(uint32_t *)(dst + 16) = _mm_cvtsi128_si32(_mm_setzero_si128());
+
+    src1 += stride, src2 += stride2;
+    dst += sse_stride;
+  }
+}
+
+AOM_FORCE_INLINE void get_squared_error_32x32_avx2(
+    uint8_t *frame1, unsigned int stride, uint8_t *frame2, unsigned int stride2,
+    int block_width, int block_height, uint16_t *frame_sse,
+    unsigned int sse_stride) {
+  (void)block_width;
+  uint8_t *src1 = frame1;
+  uint8_t *src2 = frame2;
+  uint16_t *dst = frame_sse;
+  for (int i = 0; i < block_height; i++) {
+    __m256i vsrc1, vsrc2, vmin, vmax, vdiff, vdiff1, vdiff2, vres1, vres2;
+
+    vsrc1 = _mm256_loadu_si256((__m256i *)src1);
+    vsrc2 = _mm256_loadu_si256((__m256i *)src2);
+    vmax = _mm256_max_epu8(vsrc1, vsrc2);
+    vmin = _mm256_min_epu8(vsrc1, vsrc2);
+    vdiff = _mm256_subs_epu8(vmax, vmin);
+
+    __m128i vtmp1 = _mm256_castsi256_si128(vdiff);
+    __m128i vtmp2 = _mm256_extracti128_si256(vdiff, 1);
+    vdiff1 = _mm256_cvtepu8_epi16(vtmp1);
+    vdiff2 = _mm256_cvtepu8_epi16(vtmp2);
+
+    vres1 = _mm256_mullo_epi16(vdiff1, vdiff1);
+    vres2 = _mm256_mullo_epi16(vdiff2, vdiff2);
+    _mm256_storeu_si256((__m256i *)(dst), vres1);
+    _mm256_storeu_si256((__m256i *)(dst + 16), vres2);
+    // Set zero to unitialized memory to avoid uninitialized loads later
+    *(uint32_t *)(dst + 32) = _mm_cvtsi128_si32(_mm_setzero_si128());
+
+    src1 += stride;
+    src2 += stride2;
+    dst += sse_stride;
+  }
+}
+
+AOM_FORCE_INLINE __m256i xx_load_and_pad(uint16_t *src, int col,
+                                         int block_width) {
+  __m128i v128tmp = _mm_loadu_si128((__m128i *)(src));
+  if (col == 0) {
+    // For the first column, replicate the first element twice to the left
+    v128tmp = _mm_shuffle_epi8(v128tmp, *(__m128i *)shufflemask_16b[0]);
+  }
+  if (col == block_width - 4) {
+    // For the last column, replicate the last element twice to the right
+    v128tmp = _mm_shuffle_epi8(v128tmp, *(__m128i *)shufflemask_16b[1]);
+  }
+  return _mm256_cvtepi16_epi32(v128tmp);
+}
+
+AOM_FORCE_INLINE int32_t xx_mask_and_hadd(__m256i vsum, int i) {
+  // Mask the required 5 values inside the vector
+  __m256i vtmp = _mm256_and_si256(vsum, *(__m256i *)sse_bytemask[i]);
+  __m128i v128a, v128b;
+  // Extract 256b as two 128b registers A and B
+  v128a = _mm256_castsi256_si128(vtmp);
+  v128b = _mm256_extracti128_si256(vtmp, 1);
+  // A = [A0+B0, A1+B1, A2+B2, A3+B3]
+  v128a = _mm_add_epi32(v128a, v128b);
+  // B = [A2+B2, A3+B3, 0, 0]
+  v128b = _mm_srli_si128(v128a, 8);
+  // A = [A0+B0+A2+B2, A1+B1+A3+B3, X, X]
+  v128a = _mm_add_epi32(v128a, v128b);
+  // B = [A1+B1+A3+B3, 0, 0, 0]
+  v128b = _mm_srli_si128(v128a, 4);
+  // A = [A0+B0+A2+B2+A1+B1+A3+B3, X, X, X]
+  v128a = _mm_add_epi32(v128a, v128b);
+  return _mm_extract_epi32(v128a, 0);
+}
+
+void av1_temporal_filter_plane_avx2(uint8_t *frame1, unsigned int stride,
+                                    uint8_t *frame2, unsigned int stride2,
+                                    int block_width, int block_height,
+                                    int strength, double sigma,
+                                    int decay_control, const int *blk_fw,
+                                    int use_32x32, unsigned int *accumulator,
+                                    uint16_t *count) {
+  (void)strength;
+  (void)blk_fw;
+  (void)use_32x32;
+  const double decay = decay_control * exp(1 - sigma);
+  const double h = decay * sigma;
+  const double beta = 1.0;
+
+  uint16_t frame_sse[SSE_STRIDE * BH];
+  uint32_t acc_5x5_sse[BH][BW];
+
+  assert(((block_width == 32) && (block_height == 32)) ||
+         ((block_width == 16) && (block_height == 16)));
+
+  if (block_width == 32) {
+    get_squared_error_32x32_avx2(frame1, stride, frame2, stride2, block_width,
+                                 block_height, frame_sse, SSE_STRIDE);
+  } else {
+    get_squared_error_16x16_avx2(frame1, stride, frame2, stride2, block_width,
+                                 block_height, frame_sse, SSE_STRIDE);
+  }
+
+  __m256i vsrc[5];
+
+  // Traverse 4 columns at a time
+  // First and last columns will require padding
+  for (int col = 0; col < block_width; col += 4) {
+    uint16_t *src = (col) ? frame_sse + col - 2 : frame_sse;
+
+    // Load and pad(for first and last col) 3 rows from the top
+    for (int i = 2; i < 5; i++) {
+      vsrc[i] = xx_load_and_pad(src, col, block_width);
+      src += SSE_STRIDE;
+    }
+
+    // Copy first row to first 2 vectors
+    vsrc[0] = vsrc[2];
+    vsrc[1] = vsrc[2];
+
+    for (int row = 0; row < block_height; row++) {
+      __m256i vsum = _mm256_setzero_si256();
+
+      // Add 5 consecutive rows
+      for (int i = 0; i < 5; i++) {
+        vsum = _mm256_add_epi32(vsum, vsrc[i]);
+      }
+
+      // Push all elements by one element to the top
+      for (int i = 0; i < 4; i++) {
+        vsrc[i] = vsrc[i + 1];
+      }
+
+      // Load next row to the last element
+      if (row <= block_width - 4) {
+        vsrc[4] = xx_load_and_pad(src, col, block_width);
+        src += SSE_STRIDE;
+      } else {
+        vsrc[4] = vsrc[3];
+      }
+
+      // Accumulate the sum horizontally
+      for (int i = 0; i < 4; i++) {
+        acc_5x5_sse[row][col + i] = xx_mask_and_hadd(vsum, i);
+      }
+    }
+  }
+
+  for (int i = 0, k = 0; i < block_height; i++) {
+    for (int j = 0; j < block_width; j++, k++) {
+      const int pixel_value = frame2[i * stride2 + j];
+
+      int diff_sse = acc_5x5_sse[i][j];
+      diff_sse /= WINDOW_SIZE;
+
+      double scaled_diff = -diff_sse / (2 * beta * h * h);
+      // clamp the value to avoid underflow in exp()
+      if (scaled_diff < -15) scaled_diff = -15;
+      double w = exp(scaled_diff);
+      const int weight = (int)(w * SCALE);
+
+      count[k] += weight;
+      accumulator[k] += weight * pixel_value;
+    }
+  }
+}
+#endif
diff --git a/test/temporal_filter_plane_test.cc b/test/temporal_filter_plane_test.cc
new file mode 100644
index 0000000..1d7cd1e
--- /dev/null
+++ b/test/temporal_filter_plane_test.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2019, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <cmath>
+#include <cstdlib>
+#include <string>
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+#include "config/aom_config.h"
+#include "config/aom_dsp_rtcd.h"
+#include "config/av1_rtcd.h"
+
+#include "aom_ports/mem.h"
+#include "test/acm_random.h"
+#include "test/clear_system_state.h"
+#include "test/register_state_check.h"
+#include "test/util.h"
+#include "test/function_equivalence_test.h"
+
+using libaom_test::ACMRandom;
+using libaom_test::FunctionEquivalenceTest;
+using ::testing::Combine;
+using ::testing::Range;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+#if !CONFIG_REALTIME_ONLY
+namespace {
+
+typedef void (*temporal_filter_plane_func)(
+    uint8_t *frame1, unsigned int stride, uint8_t *frame2, unsigned int stride2,
+    int block_width, int block_height, int strength, double sigma,
+    int decay_control, const int *blk_fw, int use_32x32,
+    unsigned int *accumulator, uint16_t *count);
+typedef libaom_test::FuncParam<temporal_filter_plane_func>
+    TestTemporal_FilterPlane;
+
+typedef ::testing::tuple<TestTemporal_FilterPlane, int> TemporalFilter_Params;
+
+class TemporalFilterTest
+    : public ::testing::TestWithParam<TemporalFilter_Params> {
+ public:
+  virtual ~TemporalFilterTest() {}
+  virtual void SetUp() {
+    params_ = GET_PARAM(0);
+    rnd_.Reset(ACMRandom::DeterministicSeed());
+    src1_ = reinterpret_cast<uint8_t *>(aom_memalign(8, 256 * 256));
+    src2_ = reinterpret_cast<uint8_t *>(aom_memalign(8, 256 * 256));
+
+    ASSERT_TRUE(src1_ != NULL);
+    ASSERT_TRUE(src2_ != NULL);
+  }
+
+  virtual void TearDown() {
+    libaom_test::ClearSystemState();
+    aom_free(src1_);
+    aom_free(src2_);
+  }
+  void RunTest(int isRandom, int width, int height, int run_times);
+
+  void GenRandomData(int width, int height, int stride, int stride2) {
+    for (int ii = 0; ii < height; ii++) {
+      for (int jj = 0; jj < width; jj++) {
+        src1_[ii * stride + jj] = rnd_.Rand8();
+        src2_[ii * stride2 + jj] = rnd_.Rand8();
+      }
+    }
+  }
+
+  void GenExtremeData(int width, int height, int stride, uint8_t *data,
+                      int stride2, uint8_t *data2, uint8_t val) {
+    for (int ii = 0; ii < height; ii++) {
+      for (int jj = 0; jj < width; jj++) {
+        data[ii * stride + jj] = val;
+        data2[ii * stride2 + jj] = (255 - val);
+      }
+    }
+  }
+
+ protected:
+  TestTemporal_FilterPlane params_;
+  uint8_t *src1_;
+  uint8_t *src2_;
+  ACMRandom rnd_;
+};
+
+void TemporalFilterTest::RunTest(int isRandom, int width, int height,
+                                 int run_times) {
+  aom_usec_timer ref_timer, test_timer;
+  for (int k = 0; k < 3; k++) {
+    int stride = 5 << rnd_(6);  // Up to 256 stride
+    int stride2 = 5 << rnd_(6);
+
+    while (stride < width) {  // Make sure it's valid
+      stride = 5 << rnd_(6);
+      stride2 = 5 << rnd_(6);
+    }
+    if (isRandom) {
+      GenRandomData(width, height, stride, stride2);
+    } else {
+      const int msb = 8;  // Up to 12 bit input
+      const int limit = (1 << msb) - 1;
+      if (k == 0) {
+        GenExtremeData(width, height, stride, src1_, stride2, src2_, limit);
+      } else {
+        GenExtremeData(width, height, stride, src1_, stride2, src2_, 0);
+      }
+    }
+    int use32X32 = 1;
+    int strength = rnd_(16);
+    double sigma = 2.1002103677063437;
+    int decay_control = 5;
+    int blk_fw = rnd_(16);
+    DECLARE_ALIGNED(16, unsigned int, accumulator_ref[1024 * 3]);
+    DECLARE_ALIGNED(16, uint16_t, count_ref[1024 * 3]);
+    memset(accumulator_ref, 0, 1024 * 3 * sizeof(accumulator_ref[0]));
+    memset(count_ref, 0, 1024 * 3 * sizeof(count_ref[0]));
+    DECLARE_ALIGNED(16, unsigned int, accumulator_mod[1024 * 3]);
+    DECLARE_ALIGNED(16, uint16_t, count_mod[1024 * 3]);
+    memset(accumulator_mod, 0, 1024 * 3 * sizeof(accumulator_mod[0]));
+    memset(count_mod, 0, 1024 * 3 * sizeof(count_mod[0]));
+
+    params_.ref_func(src1_, stride, src2_, stride2, width, height, strength,
+                     sigma, decay_control, &blk_fw, use32X32, accumulator_ref,
+                     count_ref);
+    params_.tst_func(src1_, stride, src2_, stride2, width, height, strength,
+                     sigma, decay_control, &blk_fw, use32X32, accumulator_mod,
+                     count_mod);
+
+    if (run_times > 1) {
+      aom_usec_timer_start(&ref_timer);
+      for (int j = 0; j < run_times; j++) {
+        params_.ref_func(src1_, stride, src2_, stride2, width, height, strength,
+                         sigma, decay_control, &blk_fw, use32X32,
+                         accumulator_ref, count_ref);
+      }
+      aom_usec_timer_mark(&ref_timer);
+      const int elapsed_time_c =
+          static_cast<int>(aom_usec_timer_elapsed(&ref_timer));
+
+      aom_usec_timer_start(&test_timer);
+      for (int j = 0; j < run_times; j++) {
+        params_.tst_func(src1_, stride, src2_, stride2, width, height, strength,
+                         sigma, decay_control, &blk_fw, use32X32,
+                         accumulator_mod, count_mod);
+      }
+      aom_usec_timer_mark(&test_timer);
+      const int elapsed_time_simd =
+          static_cast<int>(aom_usec_timer_elapsed(&test_timer));
+
+      printf(
+          "c_time=%d \t simd_time=%d \t "
+          "gain=%f\t width=%d\t height=%d \n",
+          elapsed_time_c, elapsed_time_simd,
+          (float)((float)elapsed_time_c / (float)elapsed_time_simd), width,
+          height);
+
+    } else {
+      for (int i = 0, l = 0; i < height; i++) {
+        for (int j = 0; j < width; j++, l++) {
+          EXPECT_EQ(accumulator_ref[l], accumulator_mod[l])
+              << "Error:" << k << " SSE Sum Test [" << width << "x" << height
+              << "] C accumulator does not match optimized accumulator.";
+          EXPECT_EQ(count_ref[l], count_mod[l])
+              << "Error:" << k << " SSE Sum Test [" << width << "x" << height
+              << "] C count does not match optimized count.";
+        }
+      }
+    }
+  }
+}
+
+TEST_P(TemporalFilterTest, OperationCheck) {
+  for (int height = 16; height <= 32; height = height * 2) {
+    RunTest(1, height, height, 1);  // GenRandomData
+  }
+}
+
+TEST_P(TemporalFilterTest, ExtremeValues) {
+  for (int height = 16; height <= 32; height = height * 2) {
+    RunTest(0, height, height, 1);
+  }
+}
+
+TEST_P(TemporalFilterTest, DISABLED_Speed) {
+  for (int height = 16; height <= 32; height = height * 2) {
+    RunTest(1, height, height, 100000);
+  }
+}
+
+#if HAVE_AVX2
+TestTemporal_FilterPlane Temporal_filter_test[] = { TestTemporal_FilterPlane(
+    &av1_temporal_filter_plane_c, &av1_temporal_filter_plane_avx2) };
+INSTANTIATE_TEST_CASE_P(AVX2, TemporalFilterTest,
+                        Combine(ValuesIn(Temporal_filter_test),
+                                Range(64, 65, 4)));
+#endif  // HAVE_AVX2
+}  // namespace
+#endif
diff --git a/test/test.cmake b/test/test.cmake
index 9b2ad47..eb3f528 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -130,6 +130,7 @@
                 "${AOM_ROOT}/test/segment_binarization_sync.cc"
                 "${AOM_ROOT}/test/superframe_test.cc"
                 "${AOM_ROOT}/test/tile_independence_test.cc"
+                "${AOM_ROOT}/test/temporal_filter_plane_test.cc"
                 "${AOM_ROOT}/test/yuv_temporal_filter_test.cc")
     if(CONFIG_REALTIME_ONLY)
       list(REMOVE_ITEM AOM_UNIT_TEST_COMMON_SOURCES