Merge "Refactor denoiser frame buffer update"
diff --git a/test/datarate_test.cc b/test/datarate_test.cc
index 3d40148..573870e 100644
--- a/test/datarate_test.cc
+++ b/test/datarate_test.cc
@@ -246,6 +246,8 @@
     for (int i = 0; i < 3; ++i) {
       bits_total_[i] = 0;
     }
+    denoiser_offon_test_ = 0;
+    denoiser_offon_period_ = -1;
   }
 
   //
@@ -316,6 +318,15 @@
     if (video->frame() == 1)
       encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
 
+    if (denoiser_offon_test_) {
+      ASSERT_GT(denoiser_offon_period_, 0)
+          << "denoiser_offon_period_ is not positive.";
+      if ((video->frame() + 1) % denoiser_offon_period_ == 0) {
+        // Flip denoiser_on_ periodically
+        denoiser_on_ ^= 1;
+      }
+    }
+
     encoder->Control(VP9E_SET_NOISE_SENSITIVITY, denoiser_on_);
 
     if (cfg_.ts_number_layers > 1) {
@@ -399,6 +410,8 @@
   vpx_codec_pts_t first_drop_;
   int num_drops_;
   int denoiser_on_;
+  int denoiser_offon_test_;
+  int denoiser_offon_period_;
 };
 
 // Check basic rate targeting,
@@ -653,6 +666,38 @@
   ASSERT_LE(effective_datarate_[0], cfg_.rc_target_bitrate * 1.15)
       << " The datarate for the file is greater than target by too much!";
 }
+
+// Check basic datarate targeting, for a single bitrate, when denoiser is off
+// and on.
+TEST_P(DatarateTestVP9Large, DenoiserOffon) {
+  cfg_.rc_buf_initial_sz = 500;
+  cfg_.rc_buf_optimal_sz = 500;
+  cfg_.rc_buf_sz = 1000;
+  cfg_.rc_dropframe_thresh = 1;
+  cfg_.rc_min_quantizer = 2;
+  cfg_.rc_max_quantizer = 56;
+  cfg_.rc_end_usage = VPX_CBR;
+  cfg_.g_lag_in_frames = 0;
+
+  ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
+                                       30, 1, 0, 299);
+
+  // For the temporal denoiser (#if CONFIG_VP9_TEMPORAL_DENOISING),
+  // there is only one denoiser mode: denoiserYonly(which is 1),
+  // but may add more modes in the future.
+  cfg_.rc_target_bitrate = 300;
+  ResetModel();
+  // The denoiser is off by default.
+  denoiser_on_ = 0;
+  // Set the offon test flag.
+  denoiser_offon_test_ = 1;
+  denoiser_offon_period_ = 100;
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  ASSERT_GE(effective_datarate_[0], cfg_.rc_target_bitrate * 0.85)
+      << " The datarate for the file is lower than target by too much!";
+  ASSERT_LE(effective_datarate_[0], cfg_.rc_target_bitrate * 1.15)
+      << " The datarate for the file is greater than target by too much!";
+}
 #endif  // CONFIG_VP9_TEMPORAL_DENOISING
 
 VP8_INSTANTIATE_TEST_CASE(DatarateTestLarge, ALL_TEST_MODES);
diff --git a/vp9/common/vp9_mfqe.c b/vp9/common/vp9_mfqe.c
index 110404f..e7ee903 100644
--- a/vp9/common/vp9_mfqe.c
+++ b/vp9/common/vp9_mfqe.c
@@ -136,13 +136,27 @@
   }
 }
 
+static void get_thr(BLOCK_SIZE bs, int qdiff, int *sad_thr, int *vdiff_thr) {
+  const int adj = qdiff >> MFQE_PRECISION;
+  if (bs == BLOCK_16X16) {
+    *sad_thr = 7 + adj;
+  } else if (bs == BLOCK_32X32) {
+    *sad_thr = 6 + adj;
+  } else {  // BLOCK_64X64
+    *sad_thr = 5 + adj;
+  }
+  *vdiff_thr = 125 + qdiff;
+}
+
 static void mfqe_block(BLOCK_SIZE bs, const uint8_t *y, const uint8_t *u,
                        const uint8_t *v, int y_stride, int uv_stride,
-                       uint8_t *yd, uint8_t *ud, uint8_t *vd,
-                       int yd_stride, int uvd_stride) {
-  int sad, sad_thr, vdiff;
+                       uint8_t *yd, uint8_t *ud, uint8_t *vd, int yd_stride,
+                       int uvd_stride, int qdiff) {
+  int sad, sad_thr, vdiff, vdiff_thr;
   uint32_t sse;
 
+  get_thr(bs, qdiff, &sad_thr, &vdiff_thr);
+
   if (bs == BLOCK_16X16) {
     vdiff = (vp9_variance16x16(y, y_stride, yd, yd_stride, &sse) + 128) >> 8;
     sad = (vp9_sad16x16(y, y_stride, yd, yd_stride) + 128) >> 8;
@@ -154,23 +168,18 @@
     sad = (vp9_sad64x64(y, y_stride, yd, yd_stride) + 2048) >> 12;
   }
 
-  if (bs == BLOCK_16X16) {
-    sad_thr = 8;
-  } else if (bs == BLOCK_32X32) {
-    sad_thr = 7;
-  } else {  // BLOCK_64X64
-    sad_thr = 6;
-  }
-
-  // TODO(jackychen): More experiments and remove magic numbers.
   // vdiff > sad * 3 means vdiff should not be too small, otherwise,
   // it might be a lighting change in smooth area. When there is a
   // lighting change in smooth area, it is dangerous to do MFQE.
-  if (sad > 1 && sad < sad_thr && vdiff > sad * 3 && vdiff < 150) {
-    int weight = ((float)sad / (sad_thr - 1)) * ((float)vdiff / (150 - 1)) *
-                 (1 << MFQE_PRECISION);
-    apply_ifactor(y, y_stride, yd, yd_stride, u, v, uv_stride,
-                  ud, vd, uvd_stride, bs, weight);
+  if (sad > 1 && vdiff > sad * 3) {
+    const int weight = 1 << MFQE_PRECISION;
+    int ifactor = weight * sad * vdiff / (sad_thr * vdiff_thr);
+    // When ifactor equals weight, no MFQE is done.
+    if (ifactor > weight) {
+      ifactor = weight;
+    }
+    apply_ifactor(y, y_stride, yd, yd_stride, u, v, uv_stride, ud, vd,
+                  uvd_stride, bs, ifactor);
   } else {
     // Copy the block from current frame (i.e., no mfqe is done).
     copy_block(y, u, v, y_stride, uv_stride, yd, ud, vd,
@@ -199,8 +208,7 @@
                            int yd_stride, int uvd_stride) {
   int mi_offset, y_offset, uv_offset;
   const BLOCK_SIZE cur_bs = mi->mbmi.sb_type;
-  // TODO(jackychen): Consider how and whether to use qdiff in MFQE.
-  // int qdiff = cm->base_qindex - cm->postproc_state.last_base_qindex;
+  const int qdiff = cm->base_qindex - cm->postproc_state.last_base_qindex;
   const int bsl = b_width_log2_lookup[bs];
   PARTITION_TYPE partition = partition_lookup[bsl][cur_bs];
   const BLOCK_SIZE subsize = get_subsize(bs, partition);
@@ -235,18 +243,18 @@
       if (mfqe_decision(mi, mfqe_bs)) {
         // Do mfqe on the first square partition.
         mfqe_block(bs_tmp, y, u, v, y_stride, uv_stride,
-                   yd, ud, vd, yd_stride, uvd_stride);
+                   yd, ud, vd, yd_stride, uvd_stride, qdiff);
         // Do mfqe on the second square partition.
         mfqe_block(bs_tmp, y + y_offset, u + uv_offset, v + uv_offset,
                    y_stride, uv_stride, yd + y_offset, ud + uv_offset,
-                   vd + uv_offset, yd_stride, uvd_stride);
+                   vd + uv_offset, yd_stride, uvd_stride, qdiff);
       }
       if (mfqe_decision(mi + mi_offset * cm->mi_stride, mfqe_bs)) {
         // Do mfqe on the first square partition.
         mfqe_block(bs_tmp, y + y_offset * y_stride, u + uv_offset * uv_stride,
                    v + uv_offset * uv_stride, y_stride, uv_stride,
                    yd + y_offset * yd_stride, ud + uv_offset * uvd_stride,
-                   vd + uv_offset * uvd_stride, yd_stride, uvd_stride);
+                   vd + uv_offset * uvd_stride, yd_stride, uvd_stride, qdiff);
         // Do mfqe on the second square partition.
         mfqe_block(bs_tmp, y + y_offset * y_stride + y_offset,
                    u + uv_offset * uv_stride + uv_offset,
@@ -254,7 +262,7 @@
                    uv_stride, yd + y_offset * yd_stride + y_offset,
                    ud + uv_offset * uvd_stride + uv_offset,
                    vd + uv_offset * uvd_stride + uv_offset,
-                   yd_stride, uvd_stride);
+                   yd_stride, uvd_stride, qdiff);
       }
       break;
     case PARTITION_VERT:
@@ -268,18 +276,18 @@
       if (mfqe_decision(mi, mfqe_bs)) {
         // Do mfqe on the first square partition.
         mfqe_block(bs_tmp, y, u, v, y_stride, uv_stride,
-                   yd, ud, vd, yd_stride, uvd_stride);
+                   yd, ud, vd, yd_stride, uvd_stride, qdiff);
         // Do mfqe on the second square partition.
         mfqe_block(bs_tmp, y + y_offset * y_stride, u + uv_offset * uv_stride,
                    v + uv_offset * uv_stride, y_stride, uv_stride,
                    yd + y_offset * yd_stride, ud + uv_offset * uvd_stride,
-                   vd + uv_offset * uvd_stride, yd_stride, uvd_stride);
+                   vd + uv_offset * uvd_stride, yd_stride, uvd_stride, qdiff);
       }
       if (mfqe_decision(mi + mi_offset, mfqe_bs)) {
         // Do mfqe on the first square partition.
         mfqe_block(bs_tmp, y + y_offset, u + uv_offset, v + uv_offset,
                    y_stride, uv_stride, yd + y_offset, ud + uv_offset,
-                   vd + uv_offset, yd_stride, uvd_stride);
+                   vd + uv_offset, yd_stride, uvd_stride, qdiff);
         // Do mfqe on the second square partition.
         mfqe_block(bs_tmp, y + y_offset * y_stride + y_offset,
                    u + uv_offset * uv_stride + uv_offset,
@@ -287,14 +295,14 @@
                    uv_stride, yd + y_offset * yd_stride + y_offset,
                    ud + uv_offset * uvd_stride + uv_offset,
                    vd + uv_offset * uvd_stride + uv_offset,
-                   yd_stride, uvd_stride);
+                   yd_stride, uvd_stride, qdiff);
       }
       break;
     case PARTITION_NONE:
       if (mfqe_decision(mi, cur_bs)) {
         // Do mfqe on this partition.
         mfqe_block(cur_bs, y, u, v, y_stride, uv_stride,
-                   yd, ud, vd, yd_stride, uvd_stride);
+                   yd, ud, vd, yd_stride, uvd_stride, qdiff);
       } else {
         // Copy the block from current frame(i.e., no mfqe is done).
         copy_block(y, u, v, y_stride, uv_stride, yd, ud, vd,
diff --git a/vp9/common/vp9_tile_common.c b/vp9/common/vp9_tile_common.c
index 8c4a303..7a20e0a 100644
--- a/vp9/common/vp9_tile_common.c
+++ b/vp9/common/vp9_tile_common.c
@@ -36,24 +36,24 @@
   vp9_tile_set_col(tile, cm, col);
 }
 
+static int get_min_log2_tile_cols(const int sb64_cols) {
+  int min_log2 = 0;
+  while ((MAX_TILE_WIDTH_B64 << min_log2) < sb64_cols)
+    ++min_log2;
+  return min_log2;
+}
+
+static int get_max_log2_tile_cols(const int sb64_cols) {
+  int max_log2 = 1;
+  while ((sb64_cols >> max_log2) >= MIN_TILE_WIDTH_B64)
+    ++max_log2;
+  return max_log2 - 1;
+}
+
 void vp9_get_tile_n_bits(int mi_cols,
                          int *min_log2_tile_cols, int *max_log2_tile_cols) {
-  const int sb_cols = mi_cols_aligned_to_sb(mi_cols) >> MI_BLOCK_SIZE_LOG2;
-  int min_log2 = 0, max_log2 = 0;
-
-  // max
-  while ((sb_cols >> max_log2) >= MIN_TILE_WIDTH_B64)
-    ++max_log2;
-  --max_log2;
-  if (max_log2 < 0)
-    max_log2 = 0;
-
-  // min
-  while ((MAX_TILE_WIDTH_B64 << min_log2) < sb_cols)
-    ++min_log2;
-
-  assert(min_log2 <= max_log2);
-
-  *min_log2_tile_cols = min_log2;
-  *max_log2_tile_cols = max_log2;
+  const int sb64_cols = mi_cols_aligned_to_sb(mi_cols) >> MI_BLOCK_SIZE_LOG2;
+  *min_log2_tile_cols = get_min_log2_tile_cols(sb64_cols);
+  *max_log2_tile_cols = get_max_log2_tile_cols(sb64_cols);
+  assert(*min_log2_tile_cols <= *max_log2_tile_cols);
 }
diff --git a/vp9/decoder/vp9_decodeframe.c b/vp9/decoder/vp9_decodeframe.c
index 9677173..3b8af84 100644
--- a/vp9/decoder/vp9_decodeframe.c
+++ b/vp9/decoder/vp9_decodeframe.c
@@ -1546,8 +1546,6 @@
     vpx_internal_error(&cm->error, VPX_CODEC_CORRUPT_FRAME,
                        "Truncated packet or corrupt header length");
 
-  init_macroblockd(cm, &pbi->mb);
-
   cm->use_prev_frame_mvs = !cm->error_resilient_mode &&
                            cm->width == cm->last_width &&
                            cm->height == cm->last_height &&
diff --git a/vp9/encoder/vp9_dct.c b/vp9/encoder/vp9_dct.c
index 506f6de..41f72f8 100644
--- a/vp9/encoder/vp9_dct.c
+++ b/vp9/encoder/vp9_dct.c
@@ -170,7 +170,6 @@
     vp9_fdct4x4_c(input, output, stride);
   } else {
     tran_low_t out[4 * 4];
-    tran_low_t *outptr = &out[0];
     int i, j;
     tran_low_t temp_in[4], temp_out[4];
     const transform_2d ht = FHT_4[tx_type];
@@ -183,7 +182,7 @@
         temp_in[0] += 1;
       ht.cols(temp_in, temp_out);
       for (j = 0; j < 4; ++j)
-        outptr[j * 4 + i] = temp_out[j];
+        out[j * 4 + i] = temp_out[j];
     }
 
     // Rows
@@ -711,7 +710,6 @@
     vp9_fdct8x8_c(input, output, stride);
   } else {
     tran_low_t out[64];
-    tran_low_t *outptr = &out[0];
     int i, j;
     tran_low_t temp_in[8], temp_out[8];
     const transform_2d ht = FHT_8[tx_type];
@@ -722,7 +720,7 @@
         temp_in[j] = input[j * stride + i] * 4;
       ht.cols(temp_in, temp_out);
       for (j = 0; j < 8; ++j)
-        outptr[j * 8 + i] = temp_out[j];
+        out[j * 8 + i] = temp_out[j];
     }
 
     // Rows
@@ -1103,7 +1101,6 @@
     vp9_fdct16x16_c(input, output, stride);
   } else {
     tran_low_t out[256];
-    tran_low_t *outptr = &out[0];
     int i, j;
     tran_low_t temp_in[16], temp_out[16];
     const transform_2d ht = FHT_16[tx_type];
@@ -1114,7 +1111,7 @@
         temp_in[j] = input[j * stride + i] * 4;
       ht.cols(temp_in, temp_out);
       for (j = 0; j < 16; ++j)
-        outptr[j * 16 + i] = (temp_out[j] + 1 + (temp_out[j] < 0)) >> 2;
+        out[j * 16 + i] = (temp_out[j] + 1 + (temp_out[j] < 0)) >> 2;
     }
 
     // Rows
diff --git a/vp9/encoder/vp9_denoiser.c b/vp9/encoder/vp9_denoiser.c
index dec340d..febad49 100644
--- a/vp9/encoder/vp9_denoiser.c
+++ b/vp9/encoder/vp9_denoiser.c
@@ -405,7 +405,10 @@
 
 void vp9_denoiser_reset_frame_stats(PICK_MODE_CONTEXT *ctx) {
   ctx->zeromv_sse = UINT_MAX;
-  ctx->newmv_sse = UINT_MAX;
+  // This should be initialized as zero since mode search stage might skip
+  // NEWMV mode if inferred motion vector modes provide sufficiently good
+  // prediction quality.
+  ctx->newmv_sse = 0;
 }
 
 void vp9_denoiser_update_frame_stats(MB_MODE_INFO *mbmi, unsigned int sse,
diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c
index 628660b..230df1f 100644
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -1780,9 +1780,7 @@
   }
 
 #if CONFIG_VP9_TEMPORAL_DENOISING
-  if (cpi->oxcf.noise_sensitivity > 0) {
-    vp9_denoiser_free(&(cpi->denoiser));
-  }
+  vp9_denoiser_free(&(cpi->denoiser));
 #endif
 
   for (t = 0; t < cpi->num_workers; ++t) {
diff --git a/vp9/encoder/vp9_pickmode.c b/vp9/encoder/vp9_pickmode.c
index 60c7160..5acfcc5 100644
--- a/vp9/encoder/vp9_pickmode.c
+++ b/vp9/encoder/vp9_pickmode.c
@@ -605,6 +605,10 @@
   mbmi->interp_filter = cm->interp_filter == SWITCHABLE ?
                         EIGHTTAP : cm->interp_filter;
 
+#if CONFIG_VP9_TEMPORAL_DENOISING
+  vp9_denoiser_reset_frame_stats(ctx);
+#endif
+
   for (ref_frame = LAST_FRAME; ref_frame <= GOLDEN_FRAME; ++ref_frame) {
     x->pred_mv_sad[ref_frame] = INT_MAX;
     frame_mv[NEWMV][ref_frame].as_int = INVALID_MV;