[fuchsia] Set presentation interval (#38549)

diff --git a/shell/platform/fuchsia/flutter/flatland_connection.cc b/shell/platform/fuchsia/flutter/flatland_connection.cc
index ec7846a..710dd0b 100644
--- a/shell/platform/fuchsia/flutter/flatland_connection.cc
+++ b/shell/platform/fuchsia/flutter/flatland_connection.cc
@@ -10,6 +10,17 @@
 
 namespace flutter_runner {
 
+namespace {
+
+fml::TimePoint GetNextPresentationTime(fml::TimePoint now,
+                                       fml::TimePoint next_presentation_time) {
+  return now > next_presentation_time
+             ? now + flutter_runner::kDefaultFlatlandPresentationInterval
+             : next_presentation_time;
+}
+
+}  // namespace
+
 FlatlandConnection::FlatlandConnection(
     std::string debug_label,
     fuchsia::ui::composition::FlatlandHandle flatland,
@@ -54,7 +65,6 @@
   --present_credits_;
 
   fuchsia::ui::composition::PresentArgs present_args;
-  // TODO(fxbug.dev/114588): compute a better presentation time;
   present_args.set_requested_presentation_time(0);
   present_args.set_acquire_fences(std::move(acquire_fences_));
   present_args.set_release_fences(std::move(previous_present_release_fences_));
@@ -85,10 +95,10 @@
   threadsafe_state_.fire_callback_ = callback;
 
   if (threadsafe_state_.fire_callback_pending_) {
-    fml::TimePoint now = fml::TimePoint::Now();
-    // TODO(fxbug.dev/114588): Calculate correct frame times.
+    const fml::TimePoint now = fml::TimePoint::Now();
     threadsafe_state_.fire_callback_(
-        now, now + kDefaultFlatlandPresentationInterval);
+        now, GetNextPresentationTime(
+                 now, threadsafe_state_.next_presentation_time_));
     threadsafe_state_.fire_callback_ = nullptr;
     threadsafe_state_.fire_callback_pending_ = false;
   }
@@ -97,8 +107,10 @@
 // This method is called from the UI thread.
 void FlatlandConnection::AwaitVsyncForSecondaryCallback(
     FireCallbackCallback callback) {
-  fml::TimePoint now = fml::TimePoint::Now();
-  callback(now, now);
+  const fml::TimePoint now = fml::TimePoint::Now();
+  std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
+  callback(now, GetNextPresentationTime(
+                    now, threadsafe_state_.next_presentation_time_));
 }
 
 void FlatlandConnection::OnError(
@@ -118,15 +130,21 @@
   }
 
   if (present_credits_ > 0) {
+    FML_CHECK(values.has_future_presentation_infos() &&
+              !values.future_presentation_infos().empty());
+    const auto next_presentation_time =
+        fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(
+            values.future_presentation_infos().front().presentation_time()));
+
     std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
     if (threadsafe_state_.fire_callback_) {
-      fml::TimePoint now = fml::TimePoint::Now();
-      // TODO(fxbug.dev/114588): Calculate correct frame times.
       threadsafe_state_.fire_callback_(
-          now, now + kDefaultFlatlandPresentationInterval);
+          /*frame_start=*/fml::TimePoint::Now(),
+          /*frame_target=*/next_presentation_time);
       threadsafe_state_.fire_callback_ = nullptr;
     } else {
       threadsafe_state_.fire_callback_pending_ = true;
+      threadsafe_state_.next_presentation_time_ = next_presentation_time;
     }
   }
 }
diff --git a/shell/platform/fuchsia/flutter/flatland_connection.h b/shell/platform/fuchsia/flutter/flatland_connection.h
index 72940ace..46efd45 100644
--- a/shell/platform/fuchsia/flutter/flatland_connection.h
+++ b/shell/platform/fuchsia/flutter/flatland_connection.h
@@ -10,6 +10,7 @@
 #include "flutter/fml/closure.h"
 #include "flutter/fml/macros.h"
 #include "flutter/fml/time/time_delta.h"
+#include "flutter/fml/time/time_point.h"
 
 #include "vsync_waiter.h"
 
@@ -22,9 +23,10 @@
 using on_frame_presented_event =
     std::function<void(fuchsia::scenic::scheduling::FramePresentedInfo)>;
 
-// Set to zero for now for maximum simplicity until we have real values.
+// 2ms interval to target vsync is only used until Scenic sends presentation
+// feedback, or when we run out of present credits.
 static constexpr fml::TimeDelta kDefaultFlatlandPresentationInterval =
-    fml::TimeDelta::FromSecondsF(0);
+    fml::TimeDelta::FromMilliseconds(2);
 
 // The component residing on the raster thread that is responsible for
 // maintaining the Flatland instance connection and presenting updates.
@@ -87,6 +89,7 @@
     FireCallbackCallback fire_callback_;
     bool fire_callback_pending_ = false;
     bool first_present_called_ = false;
+    fml::TimePoint next_presentation_time_;
   } threadsafe_state_;
 
   std::vector<zx::event> acquire_fences_;
diff --git a/shell/platform/fuchsia/flutter/tests/flatland_connection_unittests.cc b/shell/platform/fuchsia/flutter/tests/flatland_connection_unittests.cc
index 5da43cc..7aab547 100644
--- a/shell/platform/fuchsia/flutter/tests/flatland_connection_unittests.cc
+++ b/shell/platform/fuchsia/flutter/tests/flatland_connection_unittests.cc
@@ -40,6 +40,26 @@
       });
 }
 
+void AwaitVsyncChecked(FlatlandConnection& flatland_connection,
+                       bool& condition_variable,
+                       fml::TimePoint expected_frame_end) {
+  flatland_connection.AwaitVsync(
+      [&condition_variable, expected_frame_end = std::move(expected_frame_end)](
+          fml::TimePoint frame_start, fml::TimePoint frame_end) {
+        EXPECT_EQ(frame_end, expected_frame_end);
+        condition_variable = true;
+      });
+}
+
+std::vector<fuchsia::scenic::scheduling::PresentationInfo>
+CreateFuturePresentationInfos(int presentation_time) {
+  fuchsia::scenic::scheduling::PresentationInfo info_1;
+  info_1.set_presentation_time(presentation_time);
+  std::vector<fuchsia::scenic::scheduling::PresentationInfo> infos;
+  infos.push_back(std::move(info_1));
+  return infos;
+}
+
 }  // namespace
 
 class FlatlandConnectionTest : public ::testing::Test {
@@ -61,10 +81,12 @@
   }
 
   // Syntactic sugar for OnNextFrameBegin
-  void OnNextFrameBegin(int num_present_credits) {
+  void OnNextFrameBegin(int num_present_credits, int presentation_time = 345) {
     fuchsia::ui::composition::OnNextFrameBeginValues on_next_frame_begin_values;
     on_next_frame_begin_values.set_additional_present_credits(
         num_present_credits);
+    on_next_frame_begin_values.set_future_presentation_infos(
+        CreateFuturePresentationInfos(presentation_time));
     fake_flatland().FireOnNextFrameBeginEvent(
         std::move(on_next_frame_begin_values));
   }
@@ -171,10 +193,14 @@
   EXPECT_FALSE(await_vsync_fired);
 
   // Fire the `OnNextFrameBegin` event. AwaitVsync should be fired.
+  const int kPresentationTime = 123;
   AwaitVsyncChecked(flatland_connection, await_vsync_fired,
-                    kDefaultFlatlandPresentationInterval);
+                    fml::TimePoint::FromEpochDelta(
+                        fml::TimeDelta::FromNanoseconds(kPresentationTime)));
   fuchsia::ui::composition::OnNextFrameBeginValues on_next_frame_begin_values;
   on_next_frame_begin_values.set_additional_present_credits(3);
+  on_next_frame_begin_values.set_future_presentation_infos(
+      CreateFuturePresentationInfos(kPresentationTime));
   fake_flatland().FireOnNextFrameBeginEvent(
       std::move(on_next_frame_begin_values));
   loop().RunUntilIdle();
@@ -275,24 +301,29 @@
 
   // Set the callback with AwaitVsync, callback should not be fired
   await_vsync_callback_fired = false;
+  const int kPresentationTime1 = 567;
   AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
-                    kDefaultFlatlandPresentationInterval);
+                    fml::TimePoint::FromEpochDelta(
+                        fml::TimeDelta::FromNanoseconds(kPresentationTime1)));
   EXPECT_FALSE(await_vsync_callback_fired);
 
-  // Fire the `OnNextFrameBegin` event. AwaitVsync callback should be fired.
+  // Fire the `OnNextFrameBegin` event. AwaitVsync callback should be fired with
+  // the given presentation time.
   await_vsync_callback_fired = false;
-  OnNextFrameBegin(1);
+  OnNextFrameBegin(1, kPresentationTime1);
   loop().RunUntilIdle();
   EXPECT_TRUE(await_vsync_callback_fired);
 
   // Second consecutive ONFB should not call the fire callback and should
   // instead set it to be pending to fire on next AwaitVsync
   await_vsync_callback_fired = false;
-  OnNextFrameBegin(1);
+  const int kPresentationTime2 = 678;
+  OnNextFrameBegin(1, kPresentationTime2);
   loop().RunUntilIdle();
   EXPECT_FALSE(await_vsync_callback_fired);
 
-  // Now an AwaitVsync should immediately fire the pending callback
+  // Now an AwaitVsync should immediately fire the pending callback with the
+  // default presentation interval.
   await_vsync_callback_fired = false;
   AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
                     kDefaultFlatlandPresentationInterval);
@@ -301,13 +332,15 @@
   // With the pending callback fired, The new callback should be set for the
   // next OnNextFrameBegin to call
   await_vsync_callback_fired = false;
+  const int kPresentationTime3 = 789;
   AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
-                    kDefaultFlatlandPresentationInterval);
+                    fml::TimePoint::FromEpochDelta(
+                        fml::TimeDelta::FromNanoseconds(kPresentationTime3)));
   EXPECT_FALSE(await_vsync_callback_fired);
 
   // Now OnNextFrameBegin should fire the callback
   await_vsync_callback_fired = false;
-  OnNextFrameBegin(1);
+  OnNextFrameBegin(1, kPresentationTime3);
   loop().RunUntilIdle();
   EXPECT_TRUE(await_vsync_callback_fired);
 }
diff --git a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc
index eb9b79d..57e740e 100644
--- a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc
+++ b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc
@@ -300,8 +300,12 @@
 fuchsia::ui::composition::OnNextFrameBeginValues WithPresentCredits(
     uint32_t additional_present_credits) {
   fuchsia::ui::composition::OnNextFrameBeginValues values;
-
   values.set_additional_present_credits(additional_present_credits);
+  fuchsia::scenic::scheduling::PresentationInfo info_1;
+  info_1.set_presentation_time(123);
+  std::vector<fuchsia::scenic::scheduling::PresentationInfo> infos;
+  infos.push_back(std::move(info_1));
+  values.set_future_presentation_infos(std::move(infos));
   return values;
 }