Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 59 additions & 7 deletions shell/platform/fuchsia/flutter/vsync_waiter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

#include "vsync_waiter.h"

#include <cstdint>

#include <lib/async/default.h>

#include "flutter/fml/logging.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/time/time_delta.h"
#include "flutter/fml/trace_event.h"

#include "vsync_recorder.h"
Expand Down Expand Up @@ -61,14 +66,61 @@ VsyncWaiter::~VsyncWaiter() {
ui_latch.Wait();
}

static fml::TimePoint SnapToNextPhase(fml::TimePoint value,
fml::TimePoint phase,
fml::TimeDelta interval) {
fml::TimeDelta offset = (phase - value) % interval;
if (offset < fml::TimeDelta::Zero()) {
offset = offset + interval;
/// Returns the system time at which the next frame is likely to be presented.
///
/// Consider the following scenarios, where in both the
/// scenarious the result will be the same.
///
/// Scenario 1:
/// presentation_interval is 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we clarify here that presentation_interval is a time delta, not the number of frames

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type of the param is fml::TimeDelta as opposed to an integral type, so this should be clear once the reader gets to the method definition.

/// ^ ^ ^ ^ ^
/// + + + + +
/// 0--1--2--3--4--5--6--7--8--9--
/// + + +
/// | | +---------> result: next_presentation_time
/// | v
/// v now
/// last_presentation_time
///
/// Scenario 2:
/// presentation_interval is 2
/// ^ ^ ^ ^ ^
/// + + + + +
/// 0--1--2--3--4--5--6--7--8--9--
/// + + +
/// | | +--------->result: next_presentation_time
/// | |
/// | +>now
/// |
/// +->last_presentation_time
fml::TimePoint VsyncWaiter::SnapToNextPhase(
const fml::TimePoint now,
const fml::TimePoint last_frame_presentation_time,
const fml::TimeDelta presentation_interval) {
if (presentation_interval <= fml::TimeDelta::Zero()) {
FML_LOG(ERROR) << "Presentation interval must be positive. The value was: "
<< presentation_interval.ToMilliseconds() << "ms.";
return now;
}

if (last_frame_presentation_time >= now) {
FML_LOG(ERROR)
<< "Last frame was presented in the future. Clamping to now.";
return now + presentation_interval;
}

const fml::TimeDelta time_since_last_presentation =
now - last_frame_presentation_time;
// this will be the most likely scenario if we are rendering at a good
// frame rate, short circuiting the other checks in this case.
if (time_since_last_presentation < presentation_interval) {
return last_frame_presentation_time + presentation_interval;
} else {
const int64_t num_phases_passed =
(time_since_last_presentation / presentation_interval);
return last_frame_presentation_time +
(presentation_interval * (num_phases_passed + 1));
}
return value + offset;
}

void VsyncWaiter::AwaitVSync() {
Expand Down
5 changes: 5 additions & 0 deletions shell/platform/fuchsia/flutter/vsync_waiter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ class VsyncWaiter final : public flutter::VsyncWaiter {
public:
static constexpr zx_signals_t SessionPresentSignal = ZX_EVENT_SIGNALED;

static fml::TimePoint SnapToNextPhase(
const fml::TimePoint now,
const fml::TimePoint last_frame_presentation_time,
const fml::TimeDelta presentation_interval);

VsyncWaiter(std::string debug_label,
zx_handle_t session_present_handle,
flutter::TaskRunners task_runners);
Expand Down
45 changes: 45 additions & 0 deletions shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <zircon/syscalls.h>

#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/time/time_delta.h"
#include "flutter/fml/time/time_point.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/shell/common/vsync_waiter.h"
#include "flutter/shell/platform/fuchsia/flutter/task_runner_adapter.h"
Expand Down Expand Up @@ -85,4 +87,47 @@ TEST_F(VsyncWaiterTest, AwaitVsync) {
}
}

TEST_F(VsyncWaiterTest, SnapToNextPhaseOverlapsWithNow) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(10);
const auto delta = fml::TimeDelta::FromNanoseconds(10);
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
now, last_presentation_time, delta);

EXPECT_EQ(now + delta, next_vsync);
}

TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNow) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(9);
const auto delta = fml::TimeDelta::FromNanoseconds(10);
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
now, last_presentation_time, delta);

// math here: 10 - 9 = 1
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(1), next_vsync);
}

TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNowMultiJump) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(34);
const auto delta = fml::TimeDelta::FromNanoseconds(10);
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
now, last_presentation_time, delta);

// zeroes: -34, -24, -14, -4, 6, ...
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(6), next_vsync);
}

TEST_F(VsyncWaiterTest, SnapToNextPhaseAfterNowMultiJumpAccountForCeils) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(20);
const auto delta = fml::TimeDelta::FromNanoseconds(16);
const auto next_vsync = flutter_runner::VsyncWaiter::SnapToNextPhase(
now, last_presentation_time, delta);

// zeroes: -20, -4, 12, 28, ...
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(12), next_vsync);
}

} // namespace flutter_runner_test