Cache corner list for each frame buffer

Store the corner list for each frame in a similar way to the
downsampling pyramid. This way, it can be computed once on-demand,
and then cached for later usage.

Now that both the pyramid and the corner list are cached, it becomes
possible to push the calls to compute these values down into the
per-estimation-type functions. This ensures that we only compute exactly
what we need, and the caching ensures that we don't do any redundant
calculations.

Finally, move the invalidation of cached global motion state in
encoder.c. Previously this logic was only in encode_with_recode_loop,
which meant that stale data could be used we used encode_without_recode
instead. By moving the logic up to a common ancestor function, we can
avoid this problem without duplicating code.

Results (150 frames, speed 1):

lowres2: 0.000% BDRATE, -0.124% encode time
midres2: 0.000% BDRATE, -0.118% encode time
hdres2:  0.000% BDRATE, -0.088% encode time

Change-Id: I473adebb63451cd91e0ec637e6d339037d130961
diff --git a/aom_dsp/flow_estimation/corner_detect.c b/aom_dsp/flow_estimation/corner_detect.c
index d97ab58..2fc2ca0 100644
--- a/aom_dsp/flow_estimation/corner_detect.c
+++ b/aom_dsp/flow_estimation/corner_detect.c
@@ -17,21 +17,109 @@
 
 #include "third_party/fastfeat/fast.h"
 
+#include "aom_dsp/aom_dsp_common.h"
 #include "aom_dsp/flow_estimation/corner_detect.h"
+#include "aom_mem/aom_mem.h"
 
-// Fast_9 wrapper
 #define FAST_BARRIER 18
-int av1_fast_corner_detect(const unsigned char *buf, int width, int height,
-                           int stride, int *points, int max_points) {
-  int num_points;
-  xy *const frm_corners_xy = aom_fast9_detect_nonmax(buf, width, height, stride,
-                                                     FAST_BARRIER, &num_points);
-  num_points = (num_points <= max_points ? num_points : max_points);
-  if (num_points > 0 && frm_corners_xy) {
-    memcpy(points, frm_corners_xy, sizeof(*frm_corners_xy) * num_points);
-    free(frm_corners_xy);
-    return num_points;
+
+size_t av1_get_corner_list_size() { return sizeof(CornerList); }
+
+CornerList *av1_alloc_corner_list() {
+  CornerList *corners = (CornerList *)aom_calloc(1, sizeof(CornerList));
+  if (!corners) {
+    return NULL;
+  }
+
+  corners->valid = false;
+#if CONFIG_MULTITHREAD
+  pthread_mutex_init(&corners->mutex, NULL);
+#endif  // CONFIG_MULTITHREAD
+  return corners;
+}
+
+void compute_corner_list(const ImagePyramid *pyr, CornerList *corners) {
+  const uint8_t *buf = pyr->layers[0].buffer;
+  int width = pyr->layers[0].width;
+  int height = pyr->layers[0].height;
+  int stride = pyr->layers[0].stride;
+
+  int num_corners;
+  xy *const frm_corners_xy = aom_fast9_detect_nonmax(
+      buf, width, height, stride, FAST_BARRIER, &num_corners);
+  num_corners = AOMMIN(num_corners, MAX_CORNERS);
+  if (num_corners > 0 && frm_corners_xy) {
+    memcpy(corners->corners, frm_corners_xy,
+           sizeof(*frm_corners_xy) * num_corners);
+    corners->num_corners = num_corners;
+  } else {
+    corners->num_corners = 0;
   }
   free(frm_corners_xy);
-  return 0;
+}
+
+void av1_compute_corner_list(const ImagePyramid *pyr, CornerList *corners) {
+  assert(corners);
+
+#if CONFIG_MULTITHREAD
+  pthread_mutex_lock(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+
+  if (!corners->valid) {
+    compute_corner_list(pyr, corners);
+    corners->valid = true;
+  }
+
+#if CONFIG_MULTITHREAD
+  pthread_mutex_unlock(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+}
+
+#ifndef NDEBUG
+// Check if a corner list has already been computed.
+// This is mostly a debug helper - as it is necessary to hold corners->mutex
+// while reading the valid flag, we cannot just write:
+//   assert(corners->valid);
+// This function allows the check to be correctly written as:
+//   assert(aom_is_corner_list_valid(corners));
+bool aom_is_corner_list_valid(CornerList *corners) {
+  assert(corners);
+
+  // Per the comments in the CornerList struct, we must take this mutex
+  // before reading or writing the "valid" flag, and hold it while computing
+  // the pyramid, to ensure proper behaviour if multiple threads call this
+  // function simultaneously
+#if CONFIG_MULTITHREAD
+  pthread_mutex_lock(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+
+  bool valid = corners->valid;
+
+#if CONFIG_MULTITHREAD
+  pthread_mutex_unlock(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+
+  return valid;
+}
+#endif
+
+void av1_invalidate_corner_list(CornerList *corners) {
+  if (corners) {
+#if CONFIG_MULTITHREAD
+    pthread_mutex_lock(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+    corners->valid = false;
+#if CONFIG_MULTITHREAD
+    pthread_mutex_unlock(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+  }
+}
+
+void av1_free_corner_list(CornerList *corners) {
+  if (corners) {
+#if CONFIG_MULTITHREAD
+    pthread_mutex_destroy(&corners->mutex);
+#endif  // CONFIG_MULTITHREAD
+    aom_free(corners);
+  }
 }
diff --git a/aom_dsp/flow_estimation/corner_detect.h b/aom_dsp/flow_estimation/corner_detect.h
index 1c6ee72..c77813e 100644
--- a/aom_dsp/flow_estimation/corner_detect.h
+++ b/aom_dsp/flow_estimation/corner_detect.h
@@ -14,14 +14,64 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <memory.h>
 
+#include "aom_dsp/pyramid.h"
+#include "aom_util/aom_thread.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-int av1_fast_corner_detect(const unsigned char *buf, int width, int height,
-                           int stride, int *points, int max_points);
+#define MAX_CORNERS 4096
+
+typedef struct corner_list {
+#if CONFIG_MULTITHREAD
+  // Mutex which is used to prevent the corner list from being computed twice
+  // at the same time
+  //
+  // Semantics:
+  // * This mutex must be held whenever reading or writing the `valid` flag
+  //
+  // * This mutex must also be held while computing the image pyramid,
+  //   to ensure that only one thread may do so at a time.
+  //
+  // * However, once you have read the valid flag and seen a true value,
+  //   it is safe to drop the mutex and read from the remaining fields.
+  //   This is because, once the image pyramid is computed, its contents
+  //   will not be changed until the parent frame buffer is recycled,
+  //   which will not happen until there are no more outstanding references
+  //   to the frame buffer.
+  pthread_mutex_t mutex;
+#endif  // CONFIG_MULTITHREAD
+  // Flag indicating whether the corner list contains valid data
+  bool valid;
+  // Number of corners found
+  int num_corners;
+  // (x, y) coordinates of each corner
+  int corners[2 * MAX_CORNERS];
+} CornerList;
+
+size_t av1_get_corner_list_size();
+
+CornerList *av1_alloc_corner_list();
+
+void av1_compute_corner_list(const ImagePyramid *pyr, CornerList *corners);
+
+#ifndef NDEBUG
+// Check if a corner list has already been computed.
+// This is mostly a debug helper - as it is necessary to hold corners->mutex
+// while reading the valid flag, we cannot just write:
+//   assert(corners->valid);
+// This function allows the check to be correctly written as:
+//   assert(aom_is_corner_list_valid(corners));
+bool aom_is_corner_list_valid(CornerList *corners);
+#endif
+
+void av1_invalidate_corner_list(CornerList *corners);
+
+void av1_free_corner_list(CornerList *corners);
 
 #ifdef __cplusplus
 }
diff --git a/aom_dsp/flow_estimation/corner_match.c b/aom_dsp/flow_estimation/corner_match.c
index 5cd55be..a5ea528 100644
--- a/aom_dsp/flow_estimation/corner_match.c
+++ b/aom_dsp/flow_estimation/corner_match.c
@@ -145,9 +145,10 @@
   }
 }
 
-int aom_determine_correspondence(const unsigned char *src, int *src_corners,
-                                 int num_src_corners, const unsigned char *ref,
-                                 int *ref_corners, int num_ref_corners,
+int aom_determine_correspondence(const unsigned char *src,
+                                 const int *src_corners, int num_src_corners,
+                                 const unsigned char *ref,
+                                 const int *ref_corners, int num_ref_corners,
                                  int width, int height, int src_stride,
                                  int ref_stride, int *correspondence_pts) {
   // TODO(sarahparker) Improve this to include 2-way match
@@ -213,39 +214,42 @@
 }
 
 int av1_compute_global_motion_feature_based(
-    TransformationType type, ImagePyramid *src_pyramid, int *src_corners,
-    int num_src_corners, ImagePyramid *ref_pyramid, int *num_inliers_by_motion,
-    MotionModel *params_by_motion, int num_motions) {
+    TransformationType type, YV12_BUFFER_CONFIG *src, YV12_BUFFER_CONFIG *ref,
+    int bit_depth, int *num_inliers_by_motion, MotionModel *params_by_motion,
+    int num_motions) {
   int i;
-  int num_ref_corners;
   int num_correspondences;
   int *correspondences;
-  int ref_corners[2 * MAX_CORNERS];
   RansacFunc ransac = av1_get_ransac_type(type);
+  ImagePyramid *src_pyramid = src->y_pyramid;
+  CornerList *src_corners = src->corners;
+  ImagePyramid *ref_pyramid = ref->y_pyramid;
+  CornerList *ref_corners = ref->corners;
 
-  assert(aom_is_pyramid_valid(src_pyramid));
+  // Precompute information we will need about each frame
+  aom_compute_pyramid(src, bit_depth, src_pyramid);
+  av1_compute_corner_list(src_pyramid, src_corners);
+  aom_compute_pyramid(ref, bit_depth, ref_pyramid);
+  av1_compute_corner_list(ref_pyramid, ref_corners);
+
   const uint8_t *src_buffer = src_pyramid->layers[0].buffer;
   const int src_width = src_pyramid->layers[0].width;
   const int src_height = src_pyramid->layers[0].height;
   const int src_stride = src_pyramid->layers[0].stride;
 
-  assert(aom_is_pyramid_valid(ref_pyramid));
   const uint8_t *ref_buffer = ref_pyramid->layers[0].buffer;
-  const int ref_width = ref_pyramid->layers[0].width;
-  const int ref_height = ref_pyramid->layers[0].height;
+  assert(ref_pyramid->layers[0].width == src_width);
+  assert(ref_pyramid->layers[0].height == src_height);
   const int ref_stride = ref_pyramid->layers[0].stride;
 
-  num_ref_corners = av1_fast_corner_detect(
-      ref_buffer, ref_width, ref_height, ref_stride, ref_corners, MAX_CORNERS);
-
   // find correspondences between the two images
-  correspondences =
-      (int *)aom_malloc(num_src_corners * 4 * sizeof(*correspondences));
+  correspondences = (int *)aom_malloc(src_corners->num_corners * 4 *
+                                      sizeof(*correspondences));
   if (!correspondences) return 0;
   num_correspondences = aom_determine_correspondence(
-      src_buffer, (int *)src_corners, num_src_corners, ref_buffer,
-      (int *)ref_corners, num_ref_corners, src_width, src_height, src_stride,
-      ref_stride, correspondences);
+      src_buffer, src_corners->corners, src_corners->num_corners, ref_buffer,
+      ref_corners->corners, ref_corners->num_corners, src_width, src_height,
+      src_stride, ref_stride, correspondences);
 
   ransac(correspondences, num_correspondences, num_inliers_by_motion,
          params_by_motion, num_motions);
diff --git a/aom_dsp/flow_estimation/corner_match.h b/aom_dsp/flow_estimation/corner_match.h
index a5ae643..2d175d9 100644
--- a/aom_dsp/flow_estimation/corner_match.h
+++ b/aom_dsp/flow_estimation/corner_match.h
@@ -16,6 +16,7 @@
 #include <stdlib.h>
 #include <memory.h>
 
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_dsp/flow_estimation/flow_estimation.h"
 #include "aom_scale/yv12config.h"
 
@@ -32,16 +33,17 @@
   int rx, ry;
 } Correspondence;
 
-int aom_determine_correspondence(const unsigned char *src, int *src_corners,
-                                 int num_src_corners, const unsigned char *ref,
-                                 int *ref_corners, int num_ref_corners,
+int aom_determine_correspondence(const unsigned char *src,
+                                 const int *src_corners, int num_src_corners,
+                                 const unsigned char *ref,
+                                 const int *ref_corners, int num_ref_corners,
                                  int width, int height, int src_stride,
                                  int ref_stride, int *correspondence_pts);
 
 int av1_compute_global_motion_feature_based(
-    TransformationType type, ImagePyramid *src_pyramid, int *src_corners,
-    int num_src_corners, ImagePyramid *ref_pyramid, int *num_inliers_by_motion,
-    MotionModel *params_by_motion, int num_motions);
+    TransformationType type, YV12_BUFFER_CONFIG *src, YV12_BUFFER_CONFIG *ref,
+    int bit_depth, int *num_inliers_by_motion, MotionModel *params_by_motion,
+    int num_motions);
 
 #ifdef __cplusplus
 }
diff --git a/aom_dsp/flow_estimation/disflow.c b/aom_dsp/flow_estimation/disflow.c
index 01d34d5..04795a6 100644
--- a/aom_dsp/flow_estimation/disflow.c
+++ b/aom_dsp/flow_estimation/disflow.c
@@ -59,7 +59,7 @@
          (y < (height - PATCH_SIZE - PATCH_CENTER));
 }
 
-static int determine_disflow_correspondence(int *frm_corners,
+static int determine_disflow_correspondence(const int *frm_corners,
                                             int num_frm_corners, double *flow_u,
                                             double *flow_v, int width,
                                             int height, int stride,
@@ -540,21 +540,27 @@
 }
 
 int av1_compute_global_motion_disflow_based(
-    TransformationType type, ImagePyramid *frm_pyramid, int *frm_corners,
-    int num_frm_corners, ImagePyramid *ref_pyramid, int *num_inliers_by_motion,
-    MotionModel *params_by_motion, int num_motions) {
+    TransformationType type, YV12_BUFFER_CONFIG *frm, YV12_BUFFER_CONFIG *ref,
+    int bit_depth, int *num_inliers_by_motion, MotionModel *params_by_motion,
+    int num_motions) {
   const int pad_size = AOMMAX(PATCH_SIZE, MIN_PAD);
   int num_correspondences;
   double *correspondences;
   RansacFuncDouble ransac = av1_get_ransac_double_prec_type(type);
+  ImagePyramid *frm_pyramid = frm->y_pyramid;
+  CornerList *frm_corners = frm->corners;
+  ImagePyramid *ref_pyramid = ref->y_pyramid;
 
-  assert(aom_is_pyramid_valid(frm_pyramid));
+  // Precompute information we will need about each frame
+  aom_compute_pyramid(frm, bit_depth, frm_pyramid);
+  av1_compute_corner_list(frm_pyramid, frm_corners);
+  aom_compute_pyramid(ref, bit_depth, ref_pyramid);
+
   const uint8_t *frm_buffer = frm_pyramid->layers[0].buffer;
   const int frm_width = frm_pyramid->layers[0].width;
   const int frm_height = frm_pyramid->layers[0].height;
   const int frm_stride = frm_pyramid->layers[0].stride;
 
-  assert(aom_is_pyramid_valid(ref_pyramid));
   const uint8_t *ref_buffer = ref_pyramid->layers[0].buffer;
   const int ref_width = ref_pyramid->layers[0].width;
   const int ref_height = ref_pyramid->layers[0].height;
@@ -607,11 +613,12 @@
   if (!compute_flow_field(frm_pyr, ref_pyr, flow_u, flow_v)) goto Error;
 
   // find correspondences between the two images using the flow field
-  correspondences = aom_malloc(num_frm_corners * 4 * sizeof(*correspondences));
+  correspondences =
+      aom_malloc(frm_corners->num_corners * 4 * sizeof(*correspondences));
   if (!correspondences) goto Error;
   num_correspondences = determine_disflow_correspondence(
-      frm_corners, num_frm_corners, flow_u, flow_v, frm_width, frm_height,
-      frm_pyr->strides[0], correspondences);
+      frm_corners->corners, frm_corners->num_corners, flow_u, flow_v, frm_width,
+      frm_height, frm_pyr->strides[0], correspondences);
   ransac(correspondences, num_correspondences, num_inliers_by_motion,
          params_by_motion, num_motions);
 
diff --git a/aom_dsp/flow_estimation/disflow.h b/aom_dsp/flow_estimation/disflow.h
index f4506c9..738b21a 100644
--- a/aom_dsp/flow_estimation/disflow.h
+++ b/aom_dsp/flow_estimation/disflow.h
@@ -20,9 +20,9 @@
 #endif
 
 int av1_compute_global_motion_disflow_based(
-    TransformationType type, ImagePyramid *frm_pyramid, int *frm_corners,
-    int num_frm_corners, ImagePyramid *ref_pyramid, int *num_inliers_by_motion,
-    MotionModel *params_by_motion, int num_motions);
+    TransformationType type, YV12_BUFFER_CONFIG *frm, YV12_BUFFER_CONFIG *ref,
+    int bit_depth, int *num_inliers_by_motion, MotionModel *params_by_motion,
+    int num_motions);
 
 #ifdef __cplusplus
 }
diff --git a/aom_dsp/flow_estimation/flow_estimation.c b/aom_dsp/flow_estimation/flow_estimation.c
index 05cdf0e..f7684a5 100644
--- a/aom_dsp/flow_estimation/flow_estimation.c
+++ b/aom_dsp/flow_estimation/flow_estimation.c
@@ -11,27 +11,27 @@
 
 #include <assert.h>
 
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_dsp/flow_estimation/corner_match.h"
 #include "aom_dsp/flow_estimation/disflow.h"
 #include "aom_dsp/flow_estimation/flow_estimation.h"
 #include "aom_ports/mem.h"
 #include "aom_scale/yv12config.h"
 
-int aom_compute_global_motion(TransformationType type,
-                              ImagePyramid *src_pyramid, int *src_corners,
-                              int num_src_corners, ImagePyramid *ref_pyramid,
+int aom_compute_global_motion(TransformationType type, YV12_BUFFER_CONFIG *src,
+                              YV12_BUFFER_CONFIG *ref, int bit_depth,
                               GlobalMotionEstimationType gm_estimation_type,
                               int *num_inliers_by_motion,
                               MotionModel *params_by_motion, int num_motions) {
   switch (gm_estimation_type) {
     case GLOBAL_MOTION_FEATURE_BASED:
       return av1_compute_global_motion_feature_based(
-          type, src_pyramid, src_corners, num_src_corners, ref_pyramid,
-          num_inliers_by_motion, params_by_motion, num_motions);
+          type, src, ref, bit_depth, num_inliers_by_motion, params_by_motion,
+          num_motions);
     case GLOBAL_MOTION_DISFLOW_BASED:
       return av1_compute_global_motion_disflow_based(
-          type, src_pyramid, src_corners, num_src_corners, ref_pyramid,
-          num_inliers_by_motion, params_by_motion, num_motions);
+          type, src, ref, bit_depth, num_inliers_by_motion, params_by_motion,
+          num_motions);
     default: assert(0 && "Unknown global motion estimation type");
   }
   return 0;
diff --git a/aom_dsp/flow_estimation/flow_estimation.h b/aom_dsp/flow_estimation/flow_estimation.h
index a9c23c4..0b30110 100644
--- a/aom_dsp/flow_estimation/flow_estimation.h
+++ b/aom_dsp/flow_estimation/flow_estimation.h
@@ -13,6 +13,7 @@
 #define AOM_AOM_DSP_FLOW_ESTIMATION_H_
 
 #include "aom_dsp/pyramid.h"
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_ports/mem.h"
 #include "aom_scale/yv12config.h"
 
@@ -48,9 +49,8 @@
   int num_inliers;
 } MotionModel;
 
-int aom_compute_global_motion(TransformationType type,
-                              ImagePyramid *src_pyramid, int *src_corners,
-                              int num_src_corners, ImagePyramid *ref_pyramid,
+int aom_compute_global_motion(TransformationType type, YV12_BUFFER_CONFIG *src,
+                              YV12_BUFFER_CONFIG *ref, int bit_depth,
                               GlobalMotionEstimationType gm_estimation_type,
                               int *num_inliers_by_motion,
                               MotionModel *params_by_motion, int num_motions);
diff --git a/aom_scale/generic/yv12config.c b/aom_scale/generic/yv12config.c
index 673e82a..f4635c1 100644
--- a/aom_scale/generic/yv12config.c
+++ b/aom_scale/generic/yv12config.c
@@ -13,6 +13,7 @@
 
 #include "aom/internal/aom_image_internal.h"
 #include "aom_dsp/pyramid.h"
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_mem/aom_mem.h"
 #include "aom_ports/mem.h"
 #include "aom_scale/yv12config.h"
@@ -36,6 +37,9 @@
     if (ybf->y_pyramid) {
       aom_free_pyramid(ybf->y_pyramid);
     }
+    if (ybf->corners) {
+      av1_free_corner_list(ybf->corners);
+    }
 #endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
     aom_remove_metadata_from_frame_buffer(ybf);
     /* buffer_alloc isn't accessed by most functions.  Rather y_buffer,
@@ -79,6 +83,7 @@
     if (num_pyramid_levels > 0) {
       alloc_size += aom_get_pyramid_alloc_size(
           aligned_width, aligned_height, num_pyramid_levels, use_highbitdepth);
+      alloc_size += av1_get_corner_list_size();
     }
 #endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
     // The decoder may allocate REF_FRAMES frame buffers in the frame buffer
@@ -181,9 +186,14 @@
       aom_free_pyramid(ybf->y_pyramid);
       ybf->y_pyramid = NULL;
     }
+    if (ybf->corners) {
+      av1_free_corner_list(ybf->corners);
+      ybf->corners = NULL;
+    }
     if (num_pyramid_levels > 0) {
       ybf->y_pyramid = aom_alloc_pyramid(aligned_width, aligned_height,
                                          num_pyramid_levels, use_highbitdepth);
+      ybf->corners = av1_alloc_corner_list();
     }
 #endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
 
diff --git a/aom_scale/yv12config.h b/aom_scale/yv12config.h
index 3789d11..f192a30 100644
--- a/aom_scale/yv12config.h
+++ b/aom_scale/yv12config.h
@@ -34,6 +34,7 @@
 
 #if CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
 struct image_pyramid;
+struct corner_list;
 #endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
 
 /*!\endcond */
@@ -98,6 +99,7 @@
 #if CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
   // 8-bit downsampling pyramid for the Y plane
   struct image_pyramid *y_pyramid;
+  struct corner_list *corners;
 #endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
 
   uint8_t *buffer_alloc;
diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h
index f070ab8..4488e0e 100644
--- a/av1/common/av1_common_int.h
+++ b/av1/common/av1_common_int.h
@@ -16,6 +16,7 @@
 #include "config/av1_rtcd.h"
 
 #include "aom/internal/aom_codec_internal.h"
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_util/aom_thread.h"
 #include "av1/common/alloccommon.h"
 #include "av1/common/av1_loopfilter.h"
@@ -1136,6 +1137,7 @@
   cm->cur_frame = &cm->buffer_pool->frame_bufs[new_fb_idx];
 #if CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
   aom_invalidate_pyramid(cm->cur_frame->buf.y_pyramid);
+  av1_invalidate_corner_list(cm->cur_frame->buf.corners);
 #endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
   av1_zero(cm->cur_frame->interp_filter_selected);
   return cm->cur_frame;
diff --git a/av1/common/resize.c b/av1/common/resize.c
index 930eaf0..f4bfcd0 100644
--- a/av1/common/resize.c
+++ b/av1/common/resize.c
@@ -20,6 +20,7 @@
 #include "config/aom_config.h"
 
 #include "aom_dsp/aom_dsp_common.h"
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_ports/mem.h"
 #include "aom_scale/aom_scale.h"
 #include "av1/common/common.h"
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 332556a..c21c920 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -27,6 +27,7 @@
 #include "aom_dsp/noise_util.h"
 #include "aom_dsp/noise_model.h"
 #endif
+#include "aom_dsp/flow_estimation/corner_detect.h"
 #include "aom_dsp/psnr.h"
 #if CONFIG_INTERNAL_STATS
 #include "aom_dsp/ssim.h"
@@ -2726,7 +2727,6 @@
       cpi->sf.interp_sf.adaptive_interp_filter_search)
     cpi->interp_search_flags.interp_filter_search_mask =
         av1_setup_interp_filter_search_mask(cpi);
-  aom_invalidate_pyramid(cpi->source->y_pyramid);
 
   av1_setup_frame_size(cpi);
 
@@ -3785,6 +3785,15 @@
     features->disable_cdf_update = 1;
   }
 
+#if !CONFIG_REALTIME_ONLY
+  if (cpi->oxcf.tool_cfg.enable_global_motion && !frame_is_intra_only(cm)) {
+    // Flush any stale global motion information, which may be left over
+    // from a previous frame
+    aom_invalidate_pyramid(cpi->source->y_pyramid);
+    av1_invalidate_corner_list(cpi->source->corners);
+  }
+#endif  // !CONFIG_REALTIME_ONLY
+
   int largest_tile_id = 0;
   if (av1_superres_in_recode_allowed(cpi)) {
     if (encode_with_and_without_superres(cpi, size, dest, &largest_tile_id) !=
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index c125ff8..eb484c8 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -1971,18 +1971,6 @@
   int segment_map_w; /*!< segment map width */
   int segment_map_h; /*!< segment map height */
   /**@}*/
-
-  /*!
-   * Holds the total number of corner points detected in the source frame.
-   */
-  int num_src_corners;
-
-  /*!
-   * Holds the x and y co-ordinates of the corner points detected in the source
-   * frame. src_corners[i] holds the x co-ordinate and src_corners[i+1] holds
-   * the y co-ordinate of the ith corner point detected.
-   */
-  int src_corners[2 * MAX_CORNERS];
 } GlobalMotionInfo;
 
 /*!
diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c
index b390aeb..5f2f6d9 100644
--- a/av1/encoder/ethread.c
+++ b/av1/encoder/ethread.c
@@ -2277,8 +2277,7 @@
 
     // Compute global motion for the given ref_buf_idx.
     av1_compute_gm_for_valid_ref_frames(
-        cpi, gm_info->ref_buf, ref_buf_idx, gm_info->num_src_corners,
-        gm_info->src_corners, gm_thread_data->params_by_motion,
+        cpi, gm_info->ref_buf, ref_buf_idx, gm_thread_data->params_by_motion,
         gm_thread_data->segment_map, gm_info->segment_map_w,
         gm_info->segment_map_h);
 
diff --git a/av1/encoder/global_motion_facade.c b/av1/encoder/global_motion_facade.c
index 3c44528..2859ae1 100644
--- a/av1/encoder/global_motion_facade.c
+++ b/av1/encoder/global_motion_facade.c
@@ -82,8 +82,8 @@
 // different motion models and finds the best.
 static AOM_INLINE void compute_global_motion_for_ref_frame(
     AV1_COMP *cpi, YV12_BUFFER_CONFIG *ref_buf[REF_FRAMES], int frame,
-    int num_src_corners, int *src_corners, MotionModel *params_by_motion,
-    uint8_t *segment_map, const int segment_map_w, const int segment_map_h,
+    MotionModel *params_by_motion, uint8_t *segment_map,
+    const int segment_map_w, const int segment_map_h,
     const WarpedMotionParams *ref_params) {
   ThreadData *const td = &cpi->td;
   MACROBLOCK *const x = &td->mb;
@@ -103,13 +103,7 @@
   int inliers_by_motion[RANSAC_NUM_MOTIONS];
   assert(ref_buf[frame] != NULL);
   TransformationType model;
-
-  ImagePyramid *src_pyramid = cpi->source->y_pyramid;
-
   int bit_depth = cpi->common.seq_params->bit_depth;
-  YV12_BUFFER_CONFIG *ref = ref_buf[frame];
-  ImagePyramid *ref_pyramid = ref->y_pyramid;
-  aom_compute_pyramid(ref, bit_depth, ref_pyramid);
 
   // TODO(sarahparker, debargha): Explore do_adaptive_gm_estimation = 1
   const int do_adaptive_gm_estimation = 0;
@@ -131,10 +125,9 @@
       params_by_motion[i].num_inliers = 0;
     }
 
-    aom_compute_global_motion(model, src_pyramid, src_corners, num_src_corners,
-                              ref_pyramid, gm_estimation_type,
-                              inliers_by_motion, params_by_motion,
-                              RANSAC_NUM_MOTIONS);
+    aom_compute_global_motion(model, cpi->source, ref_buf[frame], bit_depth,
+                              gm_estimation_type, inliers_by_motion,
+                              params_by_motion, RANSAC_NUM_MOTIONS);
     int64_t ref_frame_error = 0;
     for (i = 0; i < RANSAC_NUM_MOTIONS; ++i) {
       if (inliers_by_motion[i] == 0) continue;
@@ -227,34 +220,32 @@
 // Computes global motion for the given reference frame.
 void av1_compute_gm_for_valid_ref_frames(
     AV1_COMP *cpi, YV12_BUFFER_CONFIG *ref_buf[REF_FRAMES], int frame,
-    int num_src_corners, int *src_corners, MotionModel *params_by_motion,
-    uint8_t *segment_map, int segment_map_w, int segment_map_h) {
+    MotionModel *params_by_motion, uint8_t *segment_map, int segment_map_w,
+    int segment_map_h) {
   AV1_COMMON *const cm = &cpi->common;
   const WarpedMotionParams *ref_params =
       cm->prev_frame ? &cm->prev_frame->global_motion[frame]
                      : &default_warp_params;
 
-  compute_global_motion_for_ref_frame(
-      cpi, ref_buf, frame, num_src_corners, src_corners, params_by_motion,
-      segment_map, segment_map_w, segment_map_h, ref_params);
+  compute_global_motion_for_ref_frame(cpi, ref_buf, frame, params_by_motion,
+                                      segment_map, segment_map_w, segment_map_h,
+                                      ref_params);
 }
 
 // Loops over valid reference frames and computes global motion estimation.
 static AOM_INLINE void compute_global_motion_for_references(
     AV1_COMP *cpi, YV12_BUFFER_CONFIG *ref_buf[REF_FRAMES],
     FrameDistPair reference_frame[REF_FRAMES - 1], int num_ref_frames,
-    int num_src_corners, int *src_corners, MotionModel *params_by_motion,
-    uint8_t *segment_map, const int segment_map_w, const int segment_map_h) {
-  // Computation of frame corners for the source frame will be done already.
-  assert(num_src_corners != -1);
+    MotionModel *params_by_motion, uint8_t *segment_map,
+    const int segment_map_w, const int segment_map_h) {
   AV1_COMMON *const cm = &cpi->common;
   // Compute global motion w.r.t. reference frames starting from the nearest ref
   // frame in a given direction.
   for (int frame = 0; frame < num_ref_frames; frame++) {
     int ref_frame = reference_frame[frame].frame;
-    av1_compute_gm_for_valid_ref_frames(
-        cpi, ref_buf, ref_frame, num_src_corners, src_corners, params_by_motion,
-        segment_map, segment_map_w, segment_map_h);
+    av1_compute_gm_for_valid_ref_frames(cpi, ref_buf, ref_frame,
+                                        params_by_motion, segment_map,
+                                        segment_map_w, segment_map_h);
     // If global motion w.r.t. current ref frame is
     // INVALID/TRANSLATION/IDENTITY, skip the evaluation of global motion w.r.t
     // the remaining ref frames in that direction. The below exit is disabled
@@ -441,19 +432,6 @@
         sizeof(gm_info->reference_frames[0][0]), compare_distance);
   qsort(gm_info->reference_frames[1], gm_info->num_ref_frames[1],
         sizeof(gm_info->reference_frames[1][0]), compare_distance);
-
-  gm_info->num_src_corners = -1;
-  // If at least one valid reference frame exists in past/future directions,
-  // compute interest points of source frame using FAST features.
-  if (gm_info->num_ref_frames[0] > 0 || gm_info->num_ref_frames[1] > 0) {
-    aom_compute_pyramid(source, cpi->common.seq_params->bit_depth,
-                        source->y_pyramid);
-
-    PyramidLayer *layer0 = &source->y_pyramid->layers[0];
-    gm_info->num_src_corners = av1_fast_corner_detect(
-        layer0->buffer, layer0->width, layer0->height, layer0->stride,
-        gm_info->src_corners, MAX_CORNERS);
-  }
 }
 
 // Computes global motion w.r.t. valid reference frames.
@@ -471,8 +449,7 @@
     if (gm_info->num_ref_frames[dir] > 0)
       compute_global_motion_for_references(
           cpi, gm_info->ref_buf, gm_info->reference_frames[dir],
-          gm_info->num_ref_frames[dir], gm_info->num_src_corners,
-          gm_info->src_corners, params_by_motion, segment_map,
+          gm_info->num_ref_frames[dir], params_by_motion, segment_map,
           gm_info->segment_map_w, gm_info->segment_map_h);
   }
 
diff --git a/av1/encoder/global_motion_facade.h b/av1/encoder/global_motion_facade.h
index 2450a74..9d33153 100644
--- a/av1/encoder/global_motion_facade.h
+++ b/av1/encoder/global_motion_facade.h
@@ -19,9 +19,9 @@
 struct AV1_COMP;
 
 void av1_compute_gm_for_valid_ref_frames(
-    struct AV1_COMP *cpi, YV12_BUFFER_CONFIG *ref_buf[REF_FRAMES], int frame,
-    int num_src_corners, int *src_corners, MotionModel *params_by_motion,
-    uint8_t *segment_map, int segment_map_w, int segment_map_h);
+    AV1_COMP *cpi, YV12_BUFFER_CONFIG *ref_buf[REF_FRAMES], int frame,
+    MotionModel *params_by_motion, uint8_t *segment_map, int segment_map_w,
+    int segment_map_h);
 void av1_compute_global_motion_facade(struct AV1_COMP *cpi);
 #ifdef __cplusplus
 }  // extern "C"