Move some ref frame pruning logic to frame level

Create a frame level mask to mark which ref frames should be skipped
for encoding current frame during RDO. Before this patch, the pruning
decisions were made on block level repeatedly. A small instruction count
redcution is achieved.

Change-Id: Id2ecf54cc33c9f8a85f55e1f02a37d11c5187e3e
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index eceaf25..8c93bf4 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -5332,8 +5332,7 @@
         ref_buf[frame]->y_crop_height == cpi->source->y_crop_height &&
         do_gm_search_logic(&cpi->sf, frame) &&
         !prune_ref_by_selective_ref_frame(
-            cpi, NULL, ref_frame, cm->cur_frame->ref_display_order_hint,
-            cm->current_frame.display_order_hint) &&
+            cpi, NULL, ref_frame, cm->cur_frame->ref_display_order_hint) &&
         !(cpi->sf.gm_sf.selective_ref_gm && skip_gm_frame(cm, frame))) {
       assert(ref_buf[frame] != NULL);
       int relative_frame_dist = av1_encoder_get_relative_dist(
@@ -5412,6 +5411,60 @@
   }
 }
 
+static AOM_INLINE void setup_prune_ref_frame_mask(AV1_COMP *cpi) {
+  if (!cpi->sf.rt_sf.use_nonrd_pick_mode &&
+      cpi->sf.inter_sf.selective_ref_frame >= 2) {
+    AV1_COMMON *const cm = &cpi->common;
+    const OrderHintInfo *const order_hint_info =
+        &cm->seq_params.order_hint_info;
+    const int cur_frame_display_order_hint =
+        cm->current_frame.display_order_hint;
+    unsigned int *ref_display_order_hint =
+        cm->cur_frame->ref_display_order_hint;
+    const int arf2_dist = av1_encoder_get_relative_dist(
+        order_hint_info, ref_display_order_hint[ALTREF2_FRAME - LAST_FRAME],
+        cur_frame_display_order_hint);
+    const int bwd_dist = av1_encoder_get_relative_dist(
+        order_hint_info, ref_display_order_hint[BWDREF_FRAME - LAST_FRAME],
+        cur_frame_display_order_hint);
+
+    for (int ref_idx = REF_FRAMES; ref_idx < MODE_CTX_REF_FRAMES; ++ref_idx) {
+      MV_REFERENCE_FRAME rf[2];
+      av1_set_ref_frame(rf, ref_idx);
+      if (!(cpi->ref_frame_flags & av1_ref_frame_flag_list[rf[0]]) ||
+          !(cpi->ref_frame_flags & av1_ref_frame_flag_list[rf[1]])) {
+        continue;
+      }
+
+      if (!cpi->all_one_sided_refs) {
+        int ref_dist[2];
+        for (int i = 0; i < 2; ++i) {
+          ref_dist[i] = av1_encoder_get_relative_dist(
+              order_hint_info, ref_display_order_hint[rf[i] - LAST_FRAME],
+              cur_frame_display_order_hint);
+        }
+
+        // One-sided compound is used only when all reference frames are
+        // one-sided.
+        if ((ref_dist[0] > 0) == (ref_dist[1] > 0)) {
+          cpi->prune_ref_frame_mask |= 1 << ref_idx;
+        }
+      }
+
+      if (cpi->sf.inter_sf.selective_ref_frame >= 4 &&
+          (rf[0] == ALTREF2_FRAME || rf[1] == ALTREF2_FRAME) &&
+          (cpi->ref_frame_flags & av1_ref_frame_flag_list[BWDREF_FRAME])) {
+        // Check if both ALTREF2_FRAME and BWDREF_FRAME are future references.
+        if (arf2_dist > 0 && bwd_dist > 0 && bwd_dist <= arf2_dist) {
+          // Drop ALTREF2_FRAME as a reference if BWDREF_FRAME is a closer
+          // reference to the current frame than ALTREF2_FRAME
+          cpi->prune_ref_frame_mask |= 1 << ref_idx;
+        }
+      }
+    }
+  }
+}
+
 static AOM_INLINE void encode_frame_internal(AV1_COMP *cpi) {
   ThreadData *const td = &cpi->td;
   MACROBLOCK *const x = &td->mb;
@@ -5591,6 +5644,13 @@
   memcpy(cm->cur_frame->ref_deltas, cm->lf.ref_deltas, REF_FRAMES);
   memcpy(cm->cur_frame->mode_deltas, cm->lf.mode_deltas, MAX_MODE_LF_DELTAS);
 
+  cpi->all_one_sided_refs =
+      frame_is_intra_only(cm) ? 0 : av1_refs_are_one_sided(cm);
+
+  cpi->prune_ref_frame_mask = 0;
+  // Figure out which ref frames can be skipped at frame level.
+  setup_prune_ref_frame_mask(cpi);
+
   x->txb_split_count = 0;
 #if CONFIG_SPEED_STATS
   x->tx_search_count = 0;
@@ -5689,9 +5749,6 @@
   end_timing(cpi, av1_setup_motion_field_time);
 #endif
 
-  cpi->all_one_sided_refs =
-      frame_is_intra_only(cm) ? 0 : av1_refs_are_one_sided(cm);
-
   cm->current_frame.skip_mode_info.skip_mode_flag =
       check_skip_mode_enabled(cpi);
 
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 7b6e685..b36b186 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -1029,6 +1029,9 @@
   int dv_joint_cost[MV_JOINTS];
   int has_lossless_segment;
 
+  // Mark which ref frames can be skipped for encoding current frame druing RDO.
+  int prune_ref_frame_mask;
+
   // Factors to control gating of compound type selection based on best
   // approximate rd so far
   int max_comp_type_rd_threshold_mul;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 4e19e24..6a1c8e4 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -3682,6 +3682,9 @@
     return 1;
   }
 
+  const int ref_type = av1_ref_frame_type(ref_frame);
+  if ((cpi->prune_ref_frame_mask >> ref_type) & 1) return 1;
+
   // This is only used in motion vector unit test.
   if (cpi->oxcf.motion_vector_unit_test && ref_frame[0] == INTRA_FRAME)
     return 1;
@@ -3707,7 +3710,6 @@
 
   int skip_motion_mode = 0;
   if (mbmi->partition != PARTITION_NONE && mbmi->partition != PARTITION_SPLIT) {
-    const int ref_type = av1_ref_frame_type(ref_frame);
     int skip_ref = skip_ref_frame_mask & (1 << ref_type);
     if (ref_type <= ALTREF_FRAME && skip_ref) {
       // Since the compound ref modes depends on the motion estimation result of
@@ -3745,8 +3747,7 @@
   }
 
   if (prune_ref_by_selective_ref_frame(cpi, x, ref_frame,
-                                       cm->cur_frame->ref_display_order_hint,
-                                       cm->current_frame.display_order_hint))
+                                       cm->cur_frame->ref_display_order_hint))
     return 1;
 
   if (skip_motion_mode) return 2;
diff --git a/av1/encoder/rdopt.h b/av1/encoder/rdopt.h
index 986ac28..b07f56c 100644
--- a/av1/encoder/rdopt.h
+++ b/av1/encoder/rdopt.h
@@ -182,85 +182,43 @@
 static INLINE int prune_ref_by_selective_ref_frame(
     const AV1_COMP *const cpi, const MACROBLOCK *const x,
     const MV_REFERENCE_FRAME *const ref_frame,
-    const unsigned int *const ref_display_order_hint,
-    const unsigned int cur_frame_display_order_hint) {
+    const unsigned int *const ref_display_order_hint) {
   const SPEED_FEATURES *const sf = &cpi->sf;
+  if (!sf->inter_sf.selective_ref_frame) return 0;
 
-  if (sf->inter_sf.selective_ref_frame) {
-    const AV1_COMMON *const cm = &cpi->common;
-    const OrderHintInfo *const order_hint_info =
-        &cm->seq_params.order_hint_info;
-    const int comp_pred = ref_frame[1] > INTRA_FRAME;
-    if (sf->inter_sf.selective_ref_frame >= 2 ||
-        (sf->inter_sf.selective_ref_frame == 1 && comp_pred)) {
-      int ref_frame_list[2] = { LAST3_FRAME, LAST2_FRAME };
+  const AV1_COMMON *const cm = &cpi->common;
+  const OrderHintInfo *const order_hint_info = &cm->seq_params.order_hint_info;
+  const int comp_pred = ref_frame[1] > INTRA_FRAME;
 
-      if (x != NULL) {
-        if (x->search_ref_frame[LAST3_FRAME]) ref_frame_list[0] = NONE_FRAME;
-        if (x->search_ref_frame[LAST2_FRAME]) ref_frame_list[1] = NONE_FRAME;
-      }
+  if (sf->inter_sf.selective_ref_frame >= 2 ||
+      (sf->inter_sf.selective_ref_frame == 1 && comp_pred)) {
+    int ref_frame_list[2] = { LAST3_FRAME, LAST2_FRAME };
 
-      if (prune_ref(ref_frame, order_hint_info, ref_display_order_hint,
-                    ref_display_order_hint[GOLDEN_FRAME - LAST_FRAME],
-                    ref_frame_list))
-        return 1;
+    if (x != NULL) {
+      if (x->search_ref_frame[LAST3_FRAME]) ref_frame_list[0] = NONE_FRAME;
+      if (x->search_ref_frame[LAST2_FRAME]) ref_frame_list[1] = NONE_FRAME;
     }
 
-    // One-sided compound is used only when all reference frames are one-sided.
-    if (sf->inter_sf.selective_ref_frame >= 2 && comp_pred &&
-        !cpi->all_one_sided_refs) {
-      unsigned int ref_offsets[2];
-      int ref_dist[2];
-      for (int i = 0; i < 2; ++i) {
-        const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame[i]);
-        assert(buf != NULL);
-        ref_offsets[i] = buf->display_order_hint;
-        ref_dist[i] = av1_encoder_get_relative_dist(
-            order_hint_info, ref_offsets[i], cur_frame_display_order_hint);
-      }
-
-      // If both references are in same direction
-      if ((ref_dist[0] > 0) == (ref_dist[1] > 0)) {
-        return 1;
-      }
-    }
-
-    if (sf->inter_sf.selective_ref_frame >= 3) {
-      int ref_frame_list[2] = { ALTREF2_FRAME, BWDREF_FRAME };
-
-      if (x != NULL) {
-        if (x->search_ref_frame[ALTREF2_FRAME]) ref_frame_list[0] = NONE_FRAME;
-        if (x->search_ref_frame[BWDREF_FRAME]) ref_frame_list[1] = NONE_FRAME;
-      }
-
-      if (prune_ref(ref_frame, order_hint_info, ref_display_order_hint,
-                    ref_display_order_hint[LAST_FRAME - LAST_FRAME],
-                    ref_frame_list))
-        return 1;
-    }
-
-    if (sf->inter_sf.selective_ref_frame >= 4 && comp_pred) {
-      // Check if one of the reference is ALTREF2_FRAME and BWDREF_FRAME is a
-      // valid reference.
-      if ((ref_frame[0] == ALTREF2_FRAME || ref_frame[1] == ALTREF2_FRAME) &&
-          (cpi->ref_frame_flags & av1_ref_frame_flag_list[BWDREF_FRAME])) {
-        // Check if both ALTREF2_FRAME and BWDREF_FRAME are future references.
-        const int arf2_dist = av1_encoder_get_relative_dist(
-            order_hint_info, ref_display_order_hint[ALTREF2_FRAME - LAST_FRAME],
-            cur_frame_display_order_hint);
-        const int bwd_dist = av1_encoder_get_relative_dist(
-            order_hint_info, ref_display_order_hint[BWDREF_FRAME - LAST_FRAME],
-            cur_frame_display_order_hint);
-        if (arf2_dist > 0 && bwd_dist > 0 && bwd_dist <= arf2_dist) {
-          // Drop ALTREF2_FRAME as a reference if BWDREF_FRAME is a closer
-          // reference to the current frame than ALTREF2_FRAME
-          assert(get_ref_frame_buf(cm, ALTREF2_FRAME) != NULL);
-          assert(get_ref_frame_buf(cm, BWDREF_FRAME) != NULL);
-          return 1;
-        }
-      }
-    }
+    if (prune_ref(ref_frame, order_hint_info, ref_display_order_hint,
+                  ref_display_order_hint[GOLDEN_FRAME - LAST_FRAME],
+                  ref_frame_list))
+      return 1;
   }
+
+  if (sf->inter_sf.selective_ref_frame >= 3) {
+    int ref_frame_list[2] = { ALTREF2_FRAME, BWDREF_FRAME };
+
+    if (x != NULL) {
+      if (x->search_ref_frame[ALTREF2_FRAME]) ref_frame_list[0] = NONE_FRAME;
+      if (x->search_ref_frame[BWDREF_FRAME]) ref_frame_list[1] = NONE_FRAME;
+    }
+
+    if (prune_ref(ref_frame, order_hint_info, ref_display_order_hint,
+                  ref_display_order_hint[LAST_FRAME - LAST_FRAME],
+                  ref_frame_list))
+      return 1;
+  }
+
   return 0;
 }
 #ifdef __cplusplus