-
Notifications
You must be signed in to change notification settings - Fork 30.5k
iOS] Migrate VSyncClient to a pure Obj-C implementation (#186166) #186935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -274,8 +274,8 @@ if (enable_ios_unittests) { | |
| # considerably slower to start than the tests in | ||
| # "//flutter/shell/platform/darwin/common:framework_common_swift_unittests". | ||
| # If your tests do not have to be run on a simulator (no UIKit dependencies | ||
| # for example), consider adding them to the ":framework_common_swift_unittests" | ||
| # target. | ||
| # for example), consider adding them to the | ||
| # ":framework_common_swift_unittests" target. | ||
| shared_library("ios_test_flutter") { | ||
| testonly = true | ||
| visibility = [ "*" ] | ||
|
|
@@ -322,6 +322,7 @@ if (enable_ios_unittests) { | |
| "framework/Source/SemanticsObjectTest.mm", | ||
| "framework/Source/SemanticsObjectTestMocks.h", | ||
| "framework/Source/UIViewController_FlutterScreenAndSceneIfLoadedTest.mm", | ||
| "framework/Source/VsyncWaiterIOSTest.mm", | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the filename is inconsistent with the implementation file, but the test is an Obj-C XCTest and the class under test is a C++ class, hence the difference in naming. |
||
| "framework/Source/accessibility_bridge_test.mm", | ||
| "framework/Source/availability_version_check_test.mm", | ||
| "ios_context_noop_unittests.mm", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -273,7 +273,7 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { | |
| // Set animation begin value and DisplayLink tracking values. | ||
| CGFloat currentInset = delegate.physicalViewInsetBottom; | ||
| self.keyboardAnimationView.frame = CGRectMake(0, currentInset, 0, 0); | ||
| self.keyboardAnimationStartTime = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); | ||
| self.keyboardAnimationStartTime = CACurrentMediaTime(); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this was adjusted since |
||
| self.originalViewInsetBottom = currentInset; | ||
|
|
||
| // Invalidate old vsync client if old animation is not completed. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #import <XCTest/XCTest.h> | ||
|
|
||
| #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" | ||
|
|
||
| @interface VsyncWaiterIOSTest : XCTestCase | ||
| @end | ||
|
|
||
| @implementation VsyncWaiterIOSTest | ||
|
|
||
| - (void)testSnapDurationWithValidDuration { | ||
| // 60Hz: 1/60 = 0.016666... | ||
| CFTimeInterval duration = 0.016667; | ||
| CFTimeInterval snapped = flutter::VsyncWaiterIOS::SnapDuration(duration, 60.0); | ||
| XCTAssertEqualWithAccuracy(snapped, 1.0 / 60.0, 0.0001); | ||
|
|
||
| // 120Hz: 1/120 = 0.008333... | ||
| duration = 0.008334; | ||
| snapped = flutter::VsyncWaiterIOS::SnapDuration(duration, 120.0); | ||
| XCTAssertEqualWithAccuracy(snapped, 1.0 / 120.0, 0.0001); | ||
| } | ||
|
|
||
| - (void)testSnapDurationWithInvalidDuration { | ||
| // Zero duration should fallback to max_refresh_rate. | ||
| CFTimeInterval snapped = flutter::VsyncWaiterIOS::SnapDuration(0.0, 120.0); | ||
| XCTAssertEqualWithAccuracy(snapped, 1.0 / 120.0, 0.0001); | ||
|
|
||
| // Negative duration should fallback to max_refresh_rate. | ||
| snapped = flutter::VsyncWaiterIOS::SnapDuration(-0.1, 80.0); | ||
| XCTAssertEqualWithAccuracy(snapped, 1.0 / 80.0, 0.0001); | ||
| } | ||
|
|
||
| - (void)testSnapDurationWithZeroMaxRefreshRateFallback { | ||
| // If duration is invalid AND max_refresh_rate is 0, fallback to 60Hz. | ||
| CFTimeInterval snapped = flutter::VsyncWaiterIOS::SnapDuration(0.0, 0.0); | ||
| XCTAssertEqualWithAccuracy(snapped, 1.0 / 60.0, 0.0001); | ||
|
|
||
| snapped = flutter::VsyncWaiterIOS::SnapDuration(-1.0, -10.0); | ||
| XCTAssertEqualWithAccuracy(snapped, 1.0 / 60.0, 0.0001); | ||
| } | ||
|
|
||
| @end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,8 +18,21 @@ | |
| VsyncWaiterIOS::VsyncWaiterIOS(const flutter::TaskRunners& task_runners) | ||
| : VsyncWaiter(task_runners) { | ||
| auto vsyncCallback = ^(CFTimeInterval startTime, CFTimeInterval targetTime) { | ||
| fml::TimePoint start_time = fml::TimePoint() + fml::TimeDelta::FromSecondsF(startTime); | ||
| fml::TimePoint target_time = fml::TimePoint() + fml::TimeDelta::FromSecondsF(targetTime); | ||
| // Compute delay using the same CACurrentMediaTime() clock. | ||
| CFTimeInterval delay = CACurrentMediaTime() - startTime; | ||
| if (delay < 0.0) { | ||
| delay = 0.0; | ||
| } | ||
|
|
||
| // Align the start time to the C++ steady_clock used by fml::TimePoint. | ||
| fml::TimePoint start_time = fml::TimePoint::Now() - fml::TimeDelta::FromSecondsF(delay); | ||
|
|
||
| // Snap to the nearest whole Hz value to avoid floating point errors. | ||
| CFTimeInterval duration = | ||
| VsyncWaiterIOS::SnapDuration(targetTime - startTime, max_refresh_rate_); | ||
|
|
||
| // Align target time to the C++ steady_clock used by fml::TimePoint. | ||
| fml::TimePoint target_time = start_time + fml::TimeDelta::FromSecondsF(duration); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The core engine deals in Consistent with embedders on the embedder API, we adjust the epoch for start and target time here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This comment is helpful. can you add this info to the comment? |
||
| FireCallback(start_time, target_time, true); | ||
| }; | ||
| FlutterFMLTaskRunner* uiTaskRunner = | ||
|
|
@@ -52,4 +65,13 @@ | |
| return client_.refreshRate; | ||
| } | ||
|
|
||
| CFTimeInterval VsyncWaiterIOS::SnapDuration(CFTimeInterval duration, double max_refresh_rate) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's "snap duration"?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the comment on the method: it snaps the duration to a round number of Hz. Due to floating point error or tiny inconsistencies in timing the duration may be off by a few microseconds etc, so we compute the inverse (frequency in Hz) snap to the closes integer Hz (60, 120, etc. whatever the nearest int value of Hz is), then adjust the time to keep the frame timing from drifting) We were doing this before but decided it'd be better to put it in a named method with a doc comment. |
||
| if (duration > 0.0) { | ||
| double roundedRefreshRate = round(1.0 / duration); | ||
| return 1.0 / roundedRefreshRate; | ||
| } | ||
| double fallbackRefreshRate = max_refresh_rate > 0.0 ? max_refresh_rate : 60.0; | ||
| return 1.0 / fallbackRefreshRate; | ||
| } | ||
|
|
||
| } // namespace flutter | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not an actual change but turns out we were past 80 cols which upset the format presubmit.