Reland "Smooth out iOS irregular input events delivery (#12280)"#12385
Reland "Smooth out iOS irregular input events delivery (#12280)"#12385liyuqian merged 6 commits intoflutter:masterfrom
Conversation
gaaclarke
left a comment
There was a problem hiding this comment.
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.
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.
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.
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 |
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.
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.
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.
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.
I believe you want const here.
There was a problem hiding this comment.
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; |
| [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.
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); |
| } | ||
| callback_ = std::move(callback); | ||
| if (secondary_callback_) { | ||
| // Return directly as `AwaitVSync` is already called by |
There was a problem hiding this comment.
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.
Why would scheduling a callback ever cause the caller to wait for a vsync?
There was a problem hiding this comment.
Done. Added a trace event similar to MultipleCallsToVsyncInFrameInterval.
There was a problem hiding this comment.
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.
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.
Shouldn't this at least print out a warning that the callback is getting thrown away?
There was a problem hiding this comment.
Done. Added a trace event similar to MultipleCallsToVsyncInFrameInterval.
liyuqian
left a comment
There was a problem hiding this comment.
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.
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); |
|
|
||
| void PlatformView::ReleaseResourceContext() const {} | ||
|
|
||
| PointerDataDispatcherMaker PlatformView::GetDispatcherMaker() { |
There was a problem hiding this comment.
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; |
| // 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.
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 |
| /// | ||
| /// This callback is used to provide the vsync signal needed by | ||
| /// `SmoothPointerDataDispatcher`. | ||
| virtual void ScheduleSecondaryVsyncCallback( |
shell/common/shell_test.cc
Outdated
| fml::TaskRunner::RunNowOrPostTask( | ||
| shell->GetTaskRunners().GetUITaskRunner(), | ||
| [shell, &latch, &configuration]() { | ||
| bool restarted = shell->engine_->Restart(std::move(configuration)); |
| return; | ||
| } | ||
| } | ||
| AwaitVSync(); |
There was a problem hiding this comment.
Done. Added a trace event similar to MultipleCallsToVsyncInFrameInterval.
shell/common/vsync_waiter.h
Outdated
|
|
||
| void AsyncWaitForVsync(Callback callback); | ||
|
|
||
| void ScheduleSecondaryCallback(std::function<void()> callback); |
|
@gaaclarke @chinmaygarde : I now realized that my |
chinmaygarde
left a comment
There was a problem hiding this comment.
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.
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.
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.
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.
"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.
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.
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.
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.
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.
"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.
"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.
LGTM with the note that I think the doc on SmoothPointerDataDispatcher could be more clear and concise.
[email protected]:flutter/engine.git/compare/1f454c75330c...9675ca2 git log 1f454c7..9675ca2 --no-merges --oneline 2019-09-30 [email protected] Reland "Smooth out iOS irregular input events delivery (#12280)" (flutter/engine#12385) 2019-09-30 [email protected] Add missing flag for embedder. (flutter/engine#12700) 2019-09-30 [email protected] 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 [email protected] 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
[email protected]:flutter/engine.git/compare/1f454c75330c...9675ca2 git log 1f454c7..9675ca2 --no-merges --oneline 2019-09-30 [email protected] Reland "Smooth out iOS irregular input events delivery (flutter#12280)" (flutter/engine#12385) 2019-09-30 [email protected] Add missing flag for embedder. (flutter/engine#12700) 2019-09-30 [email protected] 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 [email protected] 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: