Skip to content

Commit f5250ec

Browse files
authored
[fuchsia] SnapToNextPhase refactor + add tests and documentation (flutter#14158)
1 parent 6447d2b commit f5250ec

File tree

3 files changed

+109
-7
lines changed

3 files changed

+109
-7
lines changed

shell/platform/fuchsia/flutter/vsync_waiter.cc

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44

55
#include "vsync_waiter.h"
66

7+
#include <cstdint>
8+
79
#include <lib/async/default.h>
10+
11+
#include "flutter/fml/logging.h"
812
#include "flutter/fml/make_copyable.h"
913
#include "flutter/fml/synchronization/waitable_event.h"
14+
#include "flutter/fml/time/time_delta.h"
1015
#include "flutter/fml/trace_event.h"
1116

1217
#include "vsync_recorder.h"
@@ -61,14 +66,61 @@ VsyncWaiter::~VsyncWaiter() {
6166
ui_latch.Wait();
6267
}
6368

64-
static fml::TimePoint SnapToNextPhase(fml::TimePoint value,
65-
fml::TimePoint phase,
66-
fml::TimeDelta interval) {
67-
fml::TimeDelta offset = (phase - value) % interval;
68-
if (offset < fml::TimeDelta::Zero()) {
69-
offset = offset + interval;
69+
/// Returns the system time at which the next frame is likely to be presented.
70+
///
71+
/// Consider the following scenarios, where in both the
72+
/// scenarious the result will be the same.
73+
///
74+
/// Scenario 1:
75+
/// presentation_interval is 2
76+
/// ^ ^ ^ ^ ^
77+
/// + + + + +
78+
/// 0--1--2--3--4--5--6--7--8--9--
79+
/// + + +
80+
/// | | +---------> result: next_presentation_time
81+
/// | v
82+
/// v now
83+
/// last_presentation_time
84+
///
85+
/// Scenario 2:
86+
/// presentation_interval is 2
87+
/// ^ ^ ^ ^ ^
88+
/// + + + + +
89+
/// 0--1--2--3--4--5--6--7--8--9--
90+
/// + + +
91+
/// | | +--------->result: next_presentation_time
92+
/// | |
93+
/// | +>now
94+
/// |
95+
/// +->last_presentation_time
96+
fml::TimePoint VsyncWaiter::SnapToNextPhase(
97+
const fml::TimePoint now,
98+
const fml::TimePoint last_frame_presentation_time,
99+
const fml::TimeDelta presentation_interval) {
100+
if (presentation_interval <= fml::TimeDelta::Zero()) {
101+
FML_LOG(ERROR) << "Presentation interval must be positive. The value was: "
102+
<< presentation_interval.ToMilliseconds() << "ms.";
103+
return now;
104+
}
105+
106+
if (last_frame_presentation_time >= now) {
107+
FML_LOG(ERROR)
108+
<< "Last frame was presented in the future. Clamping to now.";
109+
return now + presentation_interval;
110+
}
111+
112+
const fml::TimeDelta time_since_last_presentation =
113+
now - last_frame_presentation_time;
114+
// this will be the most likely scenario if we are rendering at a good
115+
// frame rate, short circuiting the other checks in this case.
116+
if (time_since_last_presentation < presentation_interval) {
117+
return last_frame_presentation_time + presentation_interval;
118+
} else {
119+
const int64_t num_phases_passed =
120+
(time_since_last_presentation / presentation_interval);
121+
return last_frame_presentation_time +
122+
(presentation_interval * (num_phases_passed + 1));
70123
}
71-
return value + offset;
72124
}
73125

74126
void VsyncWaiter::AwaitVSync() {

shell/platform/fuchsia/flutter/vsync_waiter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ class VsyncWaiter final : public flutter::VsyncWaiter {
1818
public:
1919
static constexpr zx_signals_t SessionPresentSignal = ZX_EVENT_SIGNALED;
2020

21+
static fml::TimePoint SnapToNextPhase(
22+
const fml::TimePoint now,
23+
const fml::TimePoint last_frame_presentation_time,
24+
const fml::TimeDelta presentation_interval);
25+
2126
VsyncWaiter(std::string debug_label,
2227
zx_handle_t session_present_handle,
2328
flutter::TaskRunners task_runners);

shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <zircon/syscalls.h>
1111

1212
#include "flutter/fml/synchronization/waitable_event.h"
13+
#include "flutter/fml/time/time_delta.h"
14+
#include "flutter/fml/time/time_point.h"
1315
#include "flutter/shell/common/thread_host.h"
1416
#include "flutter/shell/common/vsync_waiter.h"
1517
#include "flutter/shell/platform/fuchsia/flutter/task_runner_adapter.h"
@@ -85,4 +87,47 @@ TEST_F(VsyncWaiterTest, AwaitVsync) {
8587
}
8688
}
8789

90+
TEST_F(VsyncWaiterTest, SnapToNextPhaseOverlapsWithNow) {
91+
const auto now = fml::TimePoint::Now();
92+
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(10);
93+
const auto delta = fml::TimeDelta::FromNanoseconds(10);
94+
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
95+
now, last_presentation_time, delta);
96+
97+
EXPECT_EQ(now + delta, next_vsync);
98+
}
99+
100+
TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNow) {
101+
const auto now = fml::TimePoint::Now();
102+
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(9);
103+
const auto delta = fml::TimeDelta::FromNanoseconds(10);
104+
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
105+
now, last_presentation_time, delta);
106+
107+
// math here: 10 - 9 = 1
108+
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(1), next_vsync);
109+
}
110+
111+
TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNowMultiJump) {
112+
const auto now = fml::TimePoint::Now();
113+
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(34);
114+
const auto delta = fml::TimeDelta::FromNanoseconds(10);
115+
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
116+
now, last_presentation_time, delta);
117+
118+
// zeroes: -34, -24, -14, -4, 6, ...
119+
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(6), next_vsync);
120+
}
121+
122+
TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNowMultiJumpAccountForCeils) {
123+
const auto now = fml::TimePoint::Now();
124+
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(20);
125+
const auto delta = fml::TimeDelta::FromNanoseconds(16);
126+
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
127+
now, last_presentation_time, delta);
128+
129+
// zeroes: -20, -4, 12, 28, ...
130+
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(12), next_vsync);
131+
}
132+
88133
} // namespace flutter_runner_test

0 commit comments

Comments
 (0)