-
Notifications
You must be signed in to change notification settings - Fork 6k
Reland "Smooth out iOS irregular input events delivery (#12280)" #12385
Conversation
gaaclarke
left a comment
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.
My only big question is why does the method ScheduleSecondaryCallback cause an AwaitVSync. That doesn't seem right.
For my comments about naming, those are just thoughts and suggestions, there wasn't anything I really felt strongly about.
For my comments about docstrings, you can probably just point them someplace else for more information instead of having to explain SetSecondaryCallback multiple times.
|
|
||
| void PlatformView::ReleaseResourceContext() const {} | ||
|
|
||
| PointerDataDispatcherMaker PlatformView::GetDispatcherMaker() { |
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.
Typically something whose job it is is to make something is called a "factory". Are we referring these to these as "makers" elsewhere?
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.
There doesn't seem to be any maker in Flutter, but there are several in Skia. I guess that I didn't make a full factory class just for simplicity (instead of calling factory->Make(...), I'll directly call maker(...).
I think that I can change it to factory for better readability. Although I probably won't do it in this PR to limit the scope because the naming of this Maker happened before this revert/reland.
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.
I was just suggesting a rename of the typedef, not actually making a factory class =)
| /// (2) The `PlatformView` can only be accessed from the PlatformThread while | ||
| /// this class (as owned by engine) can only be accessed in the UI thread. | ||
| /// Hence `PlatformView` creates a `PointerDataDispatchMaker` on the | ||
| /// platform thread, and send it to the UI thread for the final |
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.
s/send/sends
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.
shell/common/animator.h
Outdated
|
|
||
| void Render(std::unique_ptr<flutter::LayerTree> layer_tree); | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback); |
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.
Add docstring.
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.
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.
Nit: Using the @see doxygen directive will link the same nicely in generated documentation. For example, https://github.com/flutter/flutter/blob/26465f4c657bc5e3bdbcf3b0b399e6e41622a185/packages/flutter_tools/lib/src/macos/xcode.dart
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.
Can you make the docstring more descriptive please? It mostly just repeats the method name. I have no idea why this is necessary just based on reading that.
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.
I read the docstring later. Can you also repeat the same thing here for clarity without having to jump through links.
shell/common/engine.cc
Outdated
| static constexpr char kIsolateChannel[] = "flutter/isolate"; | ||
|
|
||
| Engine::Engine(Delegate& delegate, | ||
| PointerDataDispatcherMaker& dispatcher_maker, |
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.
I believe you want const here.
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.
Sure, done. (Although this has nothing to do with the fix for this revert/reland.)
shell/common/engine.h
Outdated
| void DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet, | ||
| uint64_t trace_flow_id) override; | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback) override; |
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.
Add docstring
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.
| [engine = engine_->GetWeakPtr(), packet = std::move(packet), | ||
| flow_id = next_pointer_flow_id_] { | ||
| task_runners_.GetUITaskRunner()->PostTask( | ||
| fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet), |
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.
This this looks good to me. It's easy to get wrong so just pointing it out.
shell/common/vsync_waiter.h
Outdated
|
|
||
| void AsyncWaitForVsync(Callback callback); | ||
|
|
||
| void ScheduleSecondaryCallback(std::function<void()> callback); |
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.
docstring
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.
| } | ||
| callback_ = std::move(callback); | ||
| if (secondary_callback_) { | ||
| // Return directly as `AwaitVSync` is already called by |
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.
This is unfortunate. I wonder if there is some way we can enforce it gets called at least once either way.
| return; | ||
| } | ||
| } | ||
| AwaitVSync(); |
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.
Why would scheduling a callback ever cause the caller to wait for a vsync?
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. Added a trace event similar to MultipleCallsToVsyncInFrameInterval.
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.
I would like to talk about this offline, too. I don't think I communicated my point well.
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.
Oops, I just realized that I replied to the wrong comment... Glad that we talked offline.
| std::scoped_lock lock(callback_mutex_); | ||
| if (secondary_callback_) { | ||
| // Multiple schedules must result in a single callback per frame interval. | ||
| return; |
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.
Shouldn't this at least print out a warning that the callback is getting thrown away?
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. Added a trace event similar to MultipleCallsToVsyncInFrameInterval.
liyuqian
left a comment
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.
Thank you very much for the detailed comments! I've fixed the easy things and let's discuss more subtle issues offline.
shell/common/engine.cc
Outdated
| static constexpr char kIsolateChannel[] = "flutter/isolate"; | ||
|
|
||
| Engine::Engine(Delegate& delegate, | ||
| PointerDataDispatcherMaker& dispatcher_maker, |
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.
Sure, done. (Although this has nothing to do with the fix for this revert/reland.)
shell/common/animator.h
Outdated
|
|
||
| void Render(std::unique_ptr<flutter::LayerTree> layer_tree); | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback); |
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.
|
|
||
| void PlatformView::ReleaseResourceContext() const {} | ||
|
|
||
| PointerDataDispatcherMaker PlatformView::GetDispatcherMaker() { |
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.
There doesn't seem to be any maker in Flutter, but there are several in Skia. I guess that I didn't make a full factory class just for simplicity (instead of calling factory->Make(...), I'll directly call maker(...).
I think that I can change it to factory for better readability. Although I probably won't do it in this PR to limit the scope because the naming of this Maker happened before this revert/reland.
shell/common/engine.h
Outdated
| void DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet, | ||
| uint64_t trace_flow_id) override; | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback) override; |
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.
| // The pointer_data_dispatcher_ depends on animator_ and runtime_controller_. | ||
| // So it should be defined after them to ensure that pointer_data_dispatcher_ | ||
| // is destructed first. | ||
| std::unique_ptr<PointerDataDispatcher> pointer_data_dispatcher_; |
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 you mean to use WeakPtr<Engine::Delegate> instead of Engine::Delegate&? I think we can do that in a new PR since this is not related to the revert/reland and I want the scope of this PR to be limited to that only.
| /// (2) The `PlatformView` can only be accessed from the PlatformThread while | ||
| /// this class (as owned by engine) can only be accessed in the UI thread. | ||
| /// Hence `PlatformView` creates a `PointerDataDispatchMaker` on the | ||
| /// platform thread, and send it to the UI thread for the final |
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.
| /// | ||
| /// This callback is used to provide the vsync signal needed by | ||
| /// `SmoothPointerDataDispatcher`. | ||
| virtual void ScheduleSecondaryVsyncCallback( |
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.
shell/common/shell_test.cc
Outdated
| fml::TaskRunner::RunNowOrPostTask( | ||
| shell->GetTaskRunners().GetUITaskRunner(), | ||
| [shell, &latch, &configuration]() { | ||
| bool restarted = shell->engine_->Restart(std::move(configuration)); |
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.
| return; | ||
| } | ||
| } | ||
| AwaitVSync(); |
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. Added a trace event similar to MultipleCallsToVsyncInFrameInterval.
shell/common/vsync_waiter.h
Outdated
|
|
||
| void AsyncWaitForVsync(Callback callback); | ||
|
|
||
| void ScheduleSecondaryCallback(std::function<void()> callback); |
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.
|
@gaaclarke @chinmaygarde : I now realized that my |
chinmaygarde
left a comment
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.
I agree that AwaitVsync should not block. Not sure about the implementation though. Will need to investigate further.
shell/common/animator.h
Outdated
|
|
||
| void Render(std::unique_ptr<flutter::LayerTree> layer_tree); | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback); |
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.
Nit: Using the @see doxygen directive will link the same nicely in generated documentation. For example, https://github.com/flutter/flutter/blob/26465f4c657bc5e3bdbcf3b0b399e6e41622a185/packages/flutter_tools/lib/src/macos/xcode.dart
shell/common/animator.h
Outdated
|
|
||
| void Render(std::unique_ptr<flutter::LayerTree> layer_tree); | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback); |
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.
Can you make the docstring more descriptive please? It mostly just repeats the method name. I have no idea why this is necessary just based on reading that.
shell/common/animator.cc
Outdated
| delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_); | ||
| } | ||
|
|
||
| void Animator::ScheduleSecondaryVsyncCallback(std::function<void()> callback) { |
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.
You can replace std::function<void(void)> with fml::closure.
shell/common/engine.h
Outdated
| /// tasks that require access to components | ||
| /// that cannot be safely accessed by the | ||
| /// engine. This is the shell. | ||
| /// @param dispatcher_maker The `std::function` provided by |
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.
"callback" is fine. You don't have to specify the type.
| class PointerDataDispatcher; | ||
|
|
||
| //------------------------------------------------------------------------------ | ||
| /// The `Engine` pointer data dispatcher that forwards the packet received from |
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.
This is great. Thank for explaining it.
| /// The dispatcher that filters out irregular input events delivery to provide | ||
| /// a smooth scroll on iPhone X/Xs. | ||
| /// | ||
| /// This fixes https://github.com/flutter/flutter/issues/31086. |
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.
I don't think this is necessary. You have provided all details necessary in the documentation (if you don't think you have, please elucidate it here). Git archaeologists will be able to get to the bug from the commit anyway.
| /// See also input_events_unittests.cc where we test all our claims above. | ||
| class SmoothPointerDataDispatcher : public DefaultPointerDataDispatcher { | ||
| public: | ||
| SmoothPointerDataDispatcher(Delegate& delegate) |
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.
out of line this please. Just like you did with the destructor.
shell/common/animator.h
Outdated
|
|
||
| void Render(std::unique_ptr<flutter::LayerTree> layer_tree); | ||
|
|
||
| void ScheduleSecondaryVsyncCallback(std::function<void()> callback); |
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.
I read the docstring later. Can you also repeat the same thing here for clarity without having to jump through links.
| /// by `Animator::RequestFrame`). | ||
| /// | ||
| /// Like the callback in `AsyncWaitForVsync`, this callback is | ||
| /// only scheduled to be called once, and it's supposed to be |
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.
"it will be called on" instead of "supposed to be called"
| }; | ||
|
|
||
| //------------------------------------------------------------------------------ | ||
| /// The dispatcher that filters out irregular input events delivery to provide |
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.
"A dispatcher that may temporarily store the last received PointerData for delivery next vsync in order to smooth out the events." ?
gaaclarke
left a comment
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.
LGTM with the note that I think the doc on SmoothPointerDataDispatcher could be more clear and concise.
git@github.com:flutter/engine.git/compare/1f454c75330c...9675ca2 git log 1f454c7..9675ca2 --no-merges --oneline 2019-09-30 liyuqian@google.com Reland "Smooth out iOS irregular input events delivery (#12280)" (flutter/engine#12385) 2019-09-30 gspencergoog@users.noreply.github.com Add missing flag for embedder. (flutter/engine#12700) 2019-09-30 lu.zuccarini@gmail.com Add a method to flutter_window_controller to destroy the current window. (flutter/engine#12076) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC aaclarke@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
git@github.com:flutter/engine.git/compare/1f454c75330c...9675ca2 git log 1f454c7..9675ca2 --no-merges --oneline 2019-09-30 liyuqian@google.com Reland "Smooth out iOS irregular input events delivery (flutter#12280)" (flutter/engine#12385) 2019-09-30 gspencergoog@users.noreply.github.com Add missing flag for embedder. (flutter/engine#12700) 2019-09-30 lu.zuccarini@gmail.com Add a method to flutter_window_controller to destroy the current window. (flutter/engine#12076) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC aaclarke@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
This reverts commit c2879ca.
Additionally, we fix flutter/flutter#40863 by adding a secondary VSYNC callback.
Unit tests are updated to provide VSYNC mocking and check the fix of flutter/flutter#40863.
The root cause of having flutter/flutter#40863 is the false assumption that each input event must trigger a new frame. That was true in the framework PR flutter/flutter#36616 because the input events there are all scrolling move events. When the PR was ported to the engine, we can no longer distinguish different types of events, and tap events may no longer trigger a new frame.
Therefore, this PR directly hooks into the
VsyncWaiterand uses its (newly added) secondary callback to dispatch the pending input event.It's probably more challenging to write a good unit test for this PR than to actually fix the issue because we have to mock the correct orders of numerous multi-threading events reliably. Therefore, this PR has 3 commits: