Mozart: Move frame lag compensation to new class.

Moved code from Choreographer to FrameTracker to allow it to be reused
more broadly and without taking a dependency on //base.

BUG=
R=abarth@google.com

Review URL: https://codereview.chromium.org/1943153002 .
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 62678f5..2b9c79e 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -70,6 +70,8 @@
     "//mojo/public/platform/native:tests",
     "//mojo/services/files/c:apptests",
     "//mojo/services/files/cpp:files_impl_apptests",
+    "//mojo/services/gfx/composition/cpp:apptests",
+    "//mojo/services/gfx/images/cpp:image_pipe_apptest",
     "//mojo/services/gfx/images/cpp:image_pipe_apptest",
     "//mojo/services/log/cpp:log_client_apptests",
     "//mojo/tools:message_generator",
diff --git a/mojo/services/gfx/composition/cpp/BUILD.gn b/mojo/services/gfx/composition/cpp/BUILD.gn
index 8be2931..135df22 100644
--- a/mojo/services/gfx/composition/cpp/BUILD.gn
+++ b/mojo/services/gfx/composition/cpp/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/mojo_application.gni")
 import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
 
 mojo_sdk_source_set("cpp") {
@@ -11,6 +12,8 @@
   sources = [
     "formatting.cc",
     "formatting.h",
+    "frame_tracker.cc",
+    "frame_tracker.h",
   ]
 
   deps = [
@@ -18,3 +21,21 @@
     "../interfaces",
   ]
 }
+
+mojo_native_application("apptests") {
+  output_name = "gfx_composition_apptests"
+
+  testonly = true
+
+  sources = [
+    "frame_tracker_apptest.cc",
+  ]
+
+  deps = [
+    ":cpp",
+    "$mojo_sdk_root/mojo/public/cpp/application:standalone",
+    "$mojo_sdk_root/mojo/public/cpp/application:test_support_standalone",
+    "../interfaces",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/services/gfx/composition/cpp/frame_tracker.cc b/mojo/services/gfx/composition/cpp/frame_tracker.cc
new file mode 100644
index 0000000..d85246a
--- /dev/null
+++ b/mojo/services/gfx/composition/cpp/frame_tracker.cc
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/services/gfx/composition/cpp/frame_tracker.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace gfx {
+namespace composition {
+
+FrameTracker::FrameTracker() {}
+
+FrameTracker::~FrameTracker() {}
+
+void FrameTracker::Clear() {
+  frame_count_ = 0u;
+  frame_info_ = mojo::gfx::composition::FrameInfo();
+}
+
+uint64_t FrameTracker::Update(
+    const mojo::gfx::composition::FrameInfo& raw_frame_info,
+    MojoTimeTicks now) {
+  const int64_t old_frame_time = frame_info_.frame_time;
+  const int64_t old_presentation_time = frame_info_.presentation_time;
+  frame_info_ = raw_frame_info;
+
+  // Ensure frame info is sane since it comes from another service.
+  if (frame_info_.frame_time > now) {
+    MOJO_LOG(WARNING) << "Frame time is in the future: frame_time="
+                      << frame_info_.frame_time << ", now=" << now;
+    frame_info_.frame_time = now;
+  }
+  if (frame_info_.frame_deadline < frame_info_.frame_time) {
+    MOJO_LOG(WARNING)
+        << "Frame deadline is earlier than frame time: frame_deadline="
+        << frame_info_.frame_deadline
+        << ", frame_time=" << frame_info_.frame_time << ", now=" << now;
+    frame_info_.frame_deadline = frame_info_.frame_time;
+  }
+  if (frame_info_.presentation_time < frame_info_.frame_deadline) {
+    MOJO_LOG(WARNING) << "Presentation time is earlier than frame deadline: "
+                         "presentation_time="
+                      << frame_info_.presentation_time
+                      << ", frame_deadline=" << frame_info_.frame_deadline
+                      << ", now=" << now;
+    frame_info_.presentation_time = frame_info_.frame_deadline;
+  }
+
+  // Compensate for significant lag by adjusting the frame time if needed
+  // to step past skipped frames.
+  uint64_t lag = now - frame_info_.frame_time;
+  if (frame_info_.frame_interval > 0u && lag >= frame_info_.frame_interval) {
+    uint64_t offset = lag % frame_info_.frame_interval;
+    uint64_t adjustment = now - offset - frame_info_.frame_time;
+    frame_info_.frame_time = now - offset;
+    frame_info_.frame_deadline += adjustment;
+    frame_info_.presentation_time += adjustment;
+
+    // Jank warning.
+    // TODO(jeffbrown): Suppress this once we're happy with things.
+    MOJO_LOG(WARNING) << "Missed " << frame_info_.frame_interval
+                      << " us frame deadline by " << lag << " us, skipping "
+                      << (lag / frame_info_.frame_interval) << " frames";
+  }
+
+  // Ensure monotonicity.
+  if (frame_count_++ == 0u)
+    return 0u;
+  if (frame_info_.frame_time < old_frame_time) {
+    MOJO_LOG(WARNING) << "Frame time is going backwards: new="
+                      << frame_info_.frame_time << ", old=" << old_frame_time
+                      << ", now=" << now;
+    frame_info_.frame_time = old_frame_time;
+  }
+  if (frame_info_.presentation_time < old_presentation_time) {
+    MOJO_LOG(WARNING) << "Presentation time is going backwards: new="
+                      << frame_info_.presentation_time
+                      << ", old=" << old_presentation_time << ", now=" << now;
+    frame_info_.presentation_time = old_presentation_time;
+  }
+  return frame_info_.frame_time - old_frame_time;
+}
+
+}  // namespace composition
+}  // namespace gfx
+}  // namespace mojo
diff --git a/mojo/services/gfx/composition/cpp/frame_tracker.h b/mojo/services/gfx/composition/cpp/frame_tracker.h
new file mode 100644
index 0000000..1093462
--- /dev/null
+++ b/mojo/services/gfx/composition/cpp/frame_tracker.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_GFX_COMPOSITION_CPP_SCHEDULING_H_
+#define MOJO_SERVICES_GFX_COMPOSITION_CPP_SCHEDULING_H_
+
+#include "mojo/services/gfx/composition/interfaces/scheduling.mojom.h"
+
+namespace mojo {
+namespace gfx {
+namespace composition {
+
+// Tracks frame scheduling information.
+class FrameTracker {
+ public:
+  FrameTracker();
+  ~FrameTracker();
+
+  // Returns the number of frames that have been tracked.
+  uint64_t frame_count() const { return frame_count_; }
+
+  // Returns the current frame info.
+  // This value is not meaningful when |frame_count()| is zero.
+  const mojo::gfx::composition::FrameInfo& frame_info() const {
+    return frame_info_;
+  }
+
+  // Clears the frame tracker's state such that the next update will be
+  // treated as if it were the first.
+  void Clear();
+
+  // Updates |frame_info()| with new frame scheduling information
+  // from |raw_frame_info| and applies compensation for lag.
+  //
+  // |now| should come from a recent call to |mojo::GetTimeTicksNow()|.
+  //
+  // Whenever an application receives new frame scheduling information from the
+  // system, it should call this function before using it.
+  //
+  // Returns the time delta between the previous frame and the current frame
+  // in microseconds, or 0 if this is the first frame.
+  uint64_t Update(const mojo::gfx::composition::FrameInfo& raw_frame_info,
+                  MojoTimeTicks now);
+
+ private:
+  uint64_t frame_count_ = 0u;
+  mojo::gfx::composition::FrameInfo frame_info_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(FrameTracker);
+};
+
+}  // namespace composition
+}  // namespace gfx
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_GFX_COMPOSITION_CPP_SCHEDULING_H_
diff --git a/mojo/services/gfx/composition/cpp/frame_tracker_apptest.cc b/mojo/services/gfx/composition/cpp/frame_tracker_apptest.cc
new file mode 100644
index 0000000..0591ed0
--- /dev/null
+++ b/mojo/services/gfx/composition/cpp/frame_tracker_apptest.cc
@@ -0,0 +1,186 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/services/gfx/composition/cpp/frame_tracker.h"
+
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace test {
+
+class FrameTrackerTest : public mojo::test::ApplicationTestBase {
+ public:
+  FrameTrackerTest() {}
+  ~FrameTrackerTest() override {}
+
+ protected:
+  mojo::gfx::composition::FrameTracker frame_tracker_;
+
+  uint64_t Update(int64_t frame_time,
+                  uint64_t frame_interval,
+                  int64_t frame_deadline,
+                  int64_t presentation_time,
+                  MojoTimeTicks now) {
+    mojo::gfx::composition::FrameInfo frame_info;
+    frame_info.frame_time = frame_time;
+    frame_info.frame_interval = frame_interval;
+    frame_info.frame_deadline = frame_deadline;
+    frame_info.presentation_time = presentation_time;
+    return frame_tracker_.Update(frame_info, now);
+  }
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(FrameTrackerTest);
+};
+
+namespace {
+
+TEST_F(FrameTrackerTest, InitialState) {
+  EXPECT_EQ(0u, frame_tracker_.frame_count());
+  EXPECT_EQ(0, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(0u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(0, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(0, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, ClearResetsEverything) {
+  Update(10, 10u, 10, 10, 10);
+
+  frame_tracker_.Clear();
+  EXPECT_EQ(0u, frame_tracker_.frame_count());
+  EXPECT_EQ(0, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(0u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(0, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(0, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, TypicalUpdate) {
+  // Signalled right at frame time.
+  // No corrections.
+  EXPECT_EQ(0u, Update(12, 10u, 24, 28, 12));
+  EXPECT_EQ(1u, frame_tracker_.frame_count());
+  EXPECT_EQ(12, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(24, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(28, frame_tracker_.frame_info().presentation_time);
+
+  // Signalled 1 ms after frame time.
+  // No corrections.
+  EXPECT_EQ(10u, Update(22, 10u, 34, 38, 22 + 1));
+  EXPECT_EQ(2u, frame_tracker_.frame_count());
+  EXPECT_EQ(22, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(34, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(38, frame_tracker_.frame_info().presentation_time);
+
+  // Signalled 9 ms after frame time (frame interval is 10 ms).
+  // No corrections.
+  EXPECT_EQ(10u, Update(32, 10u, 44, 48, 32 + 9));
+  EXPECT_EQ(3u, frame_tracker_.frame_count());
+  EXPECT_EQ(32, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(44, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(48, frame_tracker_.frame_info().presentation_time);
+
+  // Frame interval changed.
+  // No corrections.
+  EXPECT_EQ(14u, Update(46, 15u, 59, 62, 46 + 2));
+  EXPECT_EQ(4u, frame_tracker_.frame_count());
+  EXPECT_EQ(46, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(15u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(59, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(62, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, LagCompensation) {
+  // Received signal exactly when next frame should begin.
+  // Skip 1 frame.
+  EXPECT_EQ(0u, Update(12, 10u, 24, 28, 12 + 10));
+  EXPECT_EQ(1u, frame_tracker_.frame_count());
+  EXPECT_EQ(22, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(34, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(38, frame_tracker_.frame_info().presentation_time);
+
+  // Received signal 2 ms after next frame should begin.
+  // Skip 1 frame.
+  EXPECT_EQ(20u, Update(32, 10u, 44, 48, 32 + 10 + 2));
+  EXPECT_EQ(2u, frame_tracker_.frame_count());
+  EXPECT_EQ(42, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(54, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(58, frame_tracker_.frame_info().presentation_time);
+
+  // Received signal 35 ms after next frame should begin.
+  // Skip 4 frames.
+  EXPECT_EQ(50u, Update(52, 10u, 64, 68, 52 + 10 + 35));
+  EXPECT_EQ(3u, frame_tracker_.frame_count());
+  EXPECT_EQ(92, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(104, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(108, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, FrameTimeInPast) {
+  // Frame time is in the future.
+  // Clamp frame time to present.
+  EXPECT_EQ(0u, Update(12, 10u, 24, 28, 12 - 1));
+  EXPECT_EQ(1u, frame_tracker_.frame_count());
+  EXPECT_EQ(11, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(24, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(28, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, FrameDeadlineBehindFrameTime) {
+  // Frame deadline is earlier than frame time.
+  // Clamp frame deadline time to frame time.
+  EXPECT_EQ(0u, Update(12, 10u, 12 - 1, 28, 12));
+  EXPECT_EQ(1u, frame_tracker_.frame_count());
+  EXPECT_EQ(12, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(12, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(28, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, PresentationTimeBehindFrameDeadline) {
+  // Presentation time is earlier than frame deadline.
+  // Clamp presentation time to frame deadline.
+  EXPECT_EQ(0u, Update(12, 10u, 24, 24 - 1, 12));
+  EXPECT_EQ(1u, frame_tracker_.frame_count());
+  EXPECT_EQ(12, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(24, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(24, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, NonMonotonicFrameTime) {
+  Update(12, 10u, 24, 28, 12);
+
+  // Frame time is going backwards.
+  // Clamp frame time to old frame time.
+  EXPECT_EQ(0u, Update(10, 10u, 24, 28, 13));
+  EXPECT_EQ(2u, frame_tracker_.frame_count());
+  EXPECT_EQ(12, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(24, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(28, frame_tracker_.frame_info().presentation_time);
+}
+
+TEST_F(FrameTrackerTest, NonMonotonicPresentationTime) {
+  Update(12, 10u, 24, 28, 12);
+
+  // Presentation time is going backwards.
+  // Clamp presentation time to old presentation time.
+  EXPECT_EQ(10u, Update(22, 10u, 26, 27, 22));
+  EXPECT_EQ(2u, frame_tracker_.frame_count());
+  EXPECT_EQ(22, frame_tracker_.frame_info().frame_time);
+  EXPECT_EQ(10u, frame_tracker_.frame_info().frame_interval);
+  EXPECT_EQ(26, frame_tracker_.frame_info().frame_deadline);
+  EXPECT_EQ(28, frame_tracker_.frame_info().presentation_time);
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/services/gfx/composition/interfaces/scheduling.mojom b/mojo/services/gfx/composition/interfaces/scheduling.mojom
index dac60f9..7b5dee1 100644
--- a/mojo/services/gfx/composition/interfaces/scheduling.mojom
+++ b/mojo/services/gfx/composition/interfaces/scheduling.mojom
@@ -42,6 +42,12 @@
 
 // Provides timestamp information about a frame which has been scheduled
 // to be drawn.
+//
+// As there may be latency in receiving an updated |FrameInfo| from the
+// system, the consumer of this structure should apply compensation for
+// skipped frames.
+//
+// See |mojo::gfx::composition::FrameTracker|.
 struct FrameInfo {
   // A timestamp indicating when the work of updating the frame was scheduled
   // to begin which may be used to coordinate animations that require a
diff --git a/mojo/ui/BUILD.gn b/mojo/ui/BUILD.gn
index f9ec43d..b239d8c 100644
--- a/mojo/ui/BUILD.gn
+++ b/mojo/ui/BUILD.gn
@@ -22,6 +22,7 @@
     "//mojo/common",
     "//mojo/public/cpp/bindings",
     "//mojo/public/interfaces/application",
+    "//mojo/services/gfx/composition/cpp",
     "//mojo/services/gfx/composition/interfaces",
     "//mojo/services/ui/input/interfaces",
     "//mojo/services/ui/views/interfaces",
diff --git a/mojo/ui/choreographer.cc b/mojo/ui/choreographer.cc
index 16a6b32..758b70e 100644
--- a/mojo/ui/choreographer.cc
+++ b/mojo/ui/choreographer.cc
@@ -58,60 +58,9 @@
     // this exacerbates starvation issues in the Mojo message pump.
     // ScheduleFrame();
 
-    // Ensure frame info is sane since it comes from another service.
-    // TODO(jeffbrown): Would be better to report an error to the client
-    // who can shut things down if needed.
-    MojoTimeTicks now = mojo::GetTimeTicksNow();
-    if (frame_info->frame_time > now) {
-      LOG(WARNING) << "Frame time is in the future: frame_time="
-                   << frame_info->frame_time << ", now=" << now;
-      frame_info->frame_time = now;
-    }
-    if (frame_info->frame_deadline < frame_info->frame_time) {
-      LOG(WARNING)
-          << "Frame deadline is earlier than frame time: frame_deadline="
-          << frame_info->frame_deadline
-          << ", frame_time=" << frame_info->frame_time << ", now=" << now;
-      frame_info->frame_deadline = frame_info->frame_time;
-    }
-    if (frame_info->presentation_time < frame_info->frame_deadline) {
-      LOG(WARNING) << "Presentation time is earlier than frame deadline: "
-                      "presentation_time="
-                   << frame_info->presentation_time
-                   << ", frame_deadline=" << frame_info->frame_deadline
-                   << ", now=" << now;
-      frame_info->presentation_time = frame_info->frame_deadline;
-    }
-
-    // Compensate for significant lag by adjusting the frame time if needed
-    // to step past skipped frames.
-    uint64_t lag = now - frame_info->frame_time;
-    if (frame_info->frame_interval > 0u && lag > frame_info->frame_interval) {
-      uint64_t offset = lag % frame_info->frame_interval;
-      uint64_t adjustment = now - offset - frame_info->frame_time;
-      frame_info->frame_time = now - offset;
-      frame_info->frame_deadline += adjustment;
-      frame_info->presentation_time += adjustment;
-
-      // Jank warning.
-      // TODO(jeffbrown): Suppress this once we're happy with things.
-      LOG(WARNING) << "Missed " << frame_info->frame_interval
-                   << " us frame deadline by " << lag << " us, skipping "
-                   << (lag / frame_info->frame_interval) << " frames";
-    }
-
-    // Ensure frame time isn't going backwards, just in case the compositor's
-    // timing is seriously broken.
-    base::TimeDelta time_delta;
-    if (last_frame_info_) {
-      DCHECK(frame_info->frame_time >= last_frame_info_->frame_time);
-      time_delta = base::TimeDelta::FromMicroseconds(
-          frame_info->frame_time - last_frame_info_->frame_time);
-    }
-
-    // Invoke the callback.
-    last_frame_info_ = frame_info.Pass();
-    delegate_->OnDraw(*last_frame_info_, time_delta);
+    base::TimeDelta time_delta = base::TimeDelta::FromMicroseconds(
+        frame_tracker_.Update(*frame_info, mojo::GetTimeTicksNow()));
+    delegate_->OnDraw(frame_tracker_.frame_info(), time_delta);
   }
 }
 
diff --git a/mojo/ui/choreographer.h b/mojo/ui/choreographer.h
index 3708cc2..4e0cad5 100644
--- a/mojo/ui/choreographer.h
+++ b/mojo/ui/choreographer.h
@@ -7,6 +7,7 @@
 
 #include "base/time/time.h"
 #include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/gfx/composition/cpp/frame_tracker.h"
 #include "mojo/services/gfx/composition/interfaces/scenes.mojom.h"
 #include "mojo/services/gfx/composition/interfaces/scheduling.mojom.h"
 
@@ -27,7 +28,7 @@
 //          const mojo::ui::ViewProvider::CreateViewCallback&
 //              create_view_callback)
 //          : BaseView(app_impl, "MyView", create_view_callback),
-//            choreographer_(scene_scheduler(), this) {}
+//            choreographer_(scene(), this) {}
 //   ~MyView() override {}
 //
 //  private:
@@ -51,9 +52,9 @@
     return scene_scheduler_.get();
   }
 
-  // Gets the most recent frame info, or null if none.
-  mojo::gfx::composition::FrameInfo* last_frame_info() {
-    return last_frame_info_.get();
+  // Gets the frame tracker.
+  mojo::gfx::composition::FrameTracker& frame_tracker() {
+    return frame_tracker_;
   }
 
   // Schedules a call to the delegate's |OnDraw| using the scene scheduler.
@@ -62,7 +63,7 @@
  private:
   mojo::gfx::composition::SceneSchedulerPtr scene_scheduler_;
   ChoreographerDelegate* delegate_;
-  mojo::gfx::composition::FrameInfoPtr last_frame_info_;
+  mojo::gfx::composition::FrameTracker frame_tracker_;
 
   void ScheduleFrame();
   void DoFrame(mojo::gfx::composition::FrameInfoPtr frame_info);