-
Notifications
You must be signed in to change notification settings - Fork 6k
[iOS] Avoid jitter and laggy when user is dragging on iOS Promotion devices #35592
Changes from 10 commits
1b1b2d7
b655406
8e93dae
85bc1b5
ba09d7c
59000d6
47abf4e
c6b77a9
4bf7f3f
8fceea1
2cd6988
2bc5880
72b3ff7
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 |
|---|---|---|
|
|
@@ -65,6 +65,14 @@ @interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegat | |
| @property(nonatomic, assign) double targetViewInsetBottom; | ||
| @property(nonatomic, retain) VSyncClient* keyboardAnimationVSyncClient; | ||
|
|
||
| /// VSyncClient for touch events delivery frame rate correction. | ||
| /// | ||
| /// On promotion devices(eg: iPhone13 Pro), the delivery frame rate of touch events is 60HZ | ||
| /// but the frame rate of rendering is 120HZ, which is different and will leads junk and laggy. | ||
| /// With this VSyncClient, we can correct the delivery frame rate of touch events to let it keep | ||
| /// the same with frame rate of rendering. | ||
| @property(nonatomic, retain) VSyncClient* touchRateCorrectionVSyncClient; | ||
|
Member
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. Is VSyncClient necessary here? Since this is just for increasing resolution of touch events and doesn't directly affect rendering, should we just use CADisplayLink directly?
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. Using VSyncClient ensures the preferred frame rate that used is the same as the one used on UIThread. |
||
|
|
||
| /* | ||
| * Mouse and trackpad gesture recognizers | ||
| */ | ||
|
|
@@ -671,6 +679,9 @@ - (void)viewDidLoad { | |
| // Register internal plugins. | ||
| [self addInternalPlugins]; | ||
|
|
||
| // Create a vsync client to correct delivery frame rate of touch events if needed. | ||
| [self createTouchRateCorrectionVSyncClientIfNeeded]; | ||
|
|
||
| if (@available(iOS 13.4, *)) { | ||
| _hoverGestureRecognizer = | ||
| [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(hoverEvent:)]; | ||
|
|
@@ -833,6 +844,7 @@ - (void)dealloc { | |
| [self deregisterNotifications]; | ||
|
|
||
| [self invalidateKeyboardAnimationVSyncClient]; | ||
| [self invalidateTouchRateCorrectionVSyncClient]; | ||
| _scrollView.get().delegate = nil; | ||
| _hoverGestureRecognizer.delegate = nil; | ||
| [_hoverGestureRecognizer release]; | ||
|
|
@@ -966,6 +978,9 @@ - (void)dispatchTouches:(NSSet*)touches | |
| } | ||
| } | ||
|
|
||
| // Activate or pause the correction of delivery frame rate of touch events. | ||
| [self triggerTouchRateCorrectionIfNeeded:touches]; | ||
|
|
||
| const CGFloat scale = [UIScreen mainScreen].scale; | ||
| auto packet = | ||
| std::make_unique<flutter::PointerDataPacket>(touches.count + touches_to_remove_count); | ||
|
|
@@ -1111,6 +1126,63 @@ - (void)forceTouchesCancelled:(NSSet*)touches { | |
| [self dispatchTouches:touches pointerDataChangeOverride:&cancel event:nullptr]; | ||
| } | ||
|
|
||
| #pragma mark - Touch events rate correction | ||
|
|
||
| - (void)createTouchRateCorrectionVSyncClientIfNeeded { | ||
| if (_touchRateCorrectionVSyncClient != nil) { | ||
| return; | ||
| } | ||
|
|
||
| double displayRefreshRate = [DisplayLinkManager displayRefreshRate]; | ||
| const double epsilon = 0.1; | ||
| if (displayRefreshRate < 60.0 + epsilon) { // displayRefreshRate <= 60.0 | ||
|
|
||
| // If current device's max frame rate is not larger than 60HZ, the delivery rate of touch events | ||
| // is the same with render vsync rate. So we don't need to create | ||
|
||
| // _touchRateCorrectionVSyncClient to correct touch callback's rate. | ||
| return; | ||
| } | ||
|
|
||
| flutter::Shell& shell = [_engine.get() shell]; | ||
| auto callback = [](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) { | ||
| // Do nothing in this block. Just trigger system to callback touch events with correct rate. | ||
| }; | ||
| _touchRateCorrectionVSyncClient = | ||
| [[VSyncClient alloc] initWithTaskRunner:shell.GetTaskRunners().GetPlatformTaskRunner() | ||
| callback:callback]; | ||
| _touchRateCorrectionVSyncClient.allowPauseAfterVsync = NO; | ||
| } | ||
|
|
||
| - (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches { | ||
| if (_touchRateCorrectionVSyncClient == nil) { | ||
| // If the _touchRateCorrectionVSyncClient is not created, means current devices doesn't | ||
| // need to correct the touch rate. So just return. | ||
| return; | ||
| } | ||
|
|
||
| // As long as there is a touch's phase is UITouchPhaseBegan or UITouchPhaseMoved, | ||
| // we should activate the correction. Otherwise we will pause the correction. | ||
|
||
| BOOL isUserInteracting = NO; | ||
| for (UITouch* touch in touches) { | ||
| if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved) { | ||
| isUserInteracting = YES; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (isUserInteracting && [_engine.get() viewController] == self) { | ||
| [_touchRateCorrectionVSyncClient await]; | ||
| } else { | ||
| [_touchRateCorrectionVSyncClient pause]; | ||
| } | ||
| } | ||
|
|
||
| - (void)invalidateTouchRateCorrectionVSyncClient { | ||
| [_touchRateCorrectionVSyncClient invalidate]; | ||
| [_touchRateCorrectionVSyncClient release]; | ||
| _touchRateCorrectionVSyncClient = nil; | ||
| } | ||
|
|
||
| #pragma mark - Handle view resizing | ||
|
|
||
| - (void)updateViewportMetrics { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,8 @@ | |
|
|
||
| - (void)await; | ||
|
|
||
| - (void)pause; | ||
|
|
||
| - (void)invalidate; | ||
|
|
||
| - (double)getRefreshRate; | ||
|
|
||
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.
Do not use
wein comments.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.
Done^_^