-
Notifications
You must be signed in to change notification settings - Fork 6k
[Windows] Add force redraw to the C++ client wrapper #42061
[Windows] Add force redraw to the C++ client wrapper #42061
Conversation
cbracken
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.
The change itself seems technically correct and tested, but I wonder if there's a way to do this without adding public API -- i.e. trigger this from the internal window.cc when it goes from hidden to shown?
I don't think there's necessarily anything wrong with adding public API to support this -- we expose frame scheduling to the Dart side app too -- but it would be nice to just do the right thing automatically with no new code in the runner, if we can.
I don't think we can avoid changing the runner. The "correct" solution would be to add the view and register the next frame callback atomically. This would require non-trivial embedder API changes. Currently both of these operations work by enqueueing work to the raster thread's task runner - nothing prevents these two tasks from being interleaved with raster operations submitted by the UI thread. In other words, we cannot avoid this race simply by moving/reordering when we add the view or the next frame callback. Another potential solution would be to register the "show window" callback after we've created the view but before we've sent the window metrics: engine/shell/platform/windows/flutter_windows.cc Lines 78 to 87 in 5cf141f
This would require changes to the Windows embedder's runner API: the bool FlutterWindow::OnCreate() {
// ...
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
- frame.right - frame.left, frame.bottom - frame.top, project_);
+ frame.right - frame.left,
+ frame.bottom - frame.top,
+ project_,
+ [&]() {
+ this->Show();
+ });
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
- flutter_controller_->engine()->SetNextFrameCallback([&]() {
- this->Show();
- });
return true;
}The "force frame" approach requires the following runner modification: ```diff
bool FlutterWindow::OnCreate() {
// ...
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show();
});
+ // It is possible Flutter rendered the first frame before the "show window"
+ // callback was registered. In case this happened, schedule another frame to
+ // ensure the window is shown. This no-ops if the first frame hasn't completed yet.
+ flutter_controller_->ForceFrame();
return true;
}In my mind the "force frame" approach is the least intrusive change to the Windows runner. |
cbracken
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.
Thanks for the detailed write-up; agreed the required embedder API changes to work around this aren't worth it, plus we already expose this to the Dart bits of the app and it's in the C API anyway.
lgtm
yaakovschectman
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 if we want this in the API
…sions) (#126961) Manual roll requested by [email protected] flutter/engine@fe24767...1c775e3 2023-05-16 [email protected] Revert "[ios_platform_view] only recycle maskView when the view is applying mutators" (flutter/engine#42080) 2023-05-16 [email protected] [macOS] Wait for binding to be ready before requesting exits from framework (flutter/engine#41753) 2023-05-16 [email protected] [linux] Wait for binding to be ready before requesting exits from framework (flutter/engine#41782) 2023-05-16 [email protected] Initial support for images in Skwasm (flutter/engine#42019) 2023-05-16 [email protected] Use new `unresolvedCodePoints` API from skia. (flutter/engine#41991) 2023-05-16 [email protected] Convert public API NativeFieldWrapper classes to abstract interfaces (flutter/engine#41945) 2023-05-16 [email protected] [Windows] Add force redraw to the C++ client wrapper (flutter/engine#42061) 2023-05-16 [email protected] Fix drone_dimension host_engine_builder. (flutter/engine#42068) 2023-05-16 [email protected] Add linux_clang_tidy builder. (flutter/engine#41990) 2023-05-16 [email protected] [ios_platform_view] only recycle maskView when the view is applying mutators (flutter/engine#41573) 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],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose 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/+doc/main/autoroll/README.md
## Background The Windows runner has a race at startup: 1. **Platform thread**: creates a hidden window 2. **Platform thread**: launches the Flutter engine 3. **UI/Raster threads**: renders the first frame 4. **Platform thread**: Registers a callback to show the window once the next frame has been rendered. Steps 3 and 4 happen in parallel and it is possible for step 3 to complete before step 4 starts. In this scenario, the next frame callback is never called and the window is never shown. As a result the `windows_startup_test`'s test, which [verifies that the "show window" callback is called](https://github.com/flutter/flutter/blob/1f09a8662dad3bb1959b24e9124e05e2b9dbff1d/dev/integration_tests/windows_startup_test/windows/runner/flutter_window.cpp#L60-L64), can flake if the first frame is rendered before the show window callback has been registered. ## Solution This change makes the runner schedule a frame after it registers the next frame callback. If step 3 hasn't completed yet, this no-ops as a frame is already scheduled. If step 3 has already completed, a new frame will be rendered, which will call the next frame callback and show the window. Part of #119415 See this thread for alternatives that were considered: flutter/engine#42061 (comment)
…sions) (flutter#126961) Manual roll requested by [email protected] flutter/engine@fe24767...1c775e3 2023-05-16 [email protected] Revert "[ios_platform_view] only recycle maskView when the view is applying mutators" (flutter/engine#42080) 2023-05-16 [email protected] [macOS] Wait for binding to be ready before requesting exits from framework (flutter/engine#41753) 2023-05-16 [email protected] [linux] Wait for binding to be ready before requesting exits from framework (flutter/engine#41782) 2023-05-16 [email protected] Initial support for images in Skwasm (flutter/engine#42019) 2023-05-16 [email protected] Use new `unresolvedCodePoints` API from skia. (flutter/engine#41991) 2023-05-16 [email protected] Convert public API NativeFieldWrapper classes to abstract interfaces (flutter/engine#41945) 2023-05-16 [email protected] [Windows] Add force redraw to the C++ client wrapper (flutter/engine#42061) 2023-05-16 [email protected] Fix drone_dimension host_engine_builder. (flutter/engine#42068) 2023-05-16 [email protected] Add linux_clang_tidy builder. (flutter/engine#41990) 2023-05-16 [email protected] [ios_platform_view] only recycle maskView when the view is applying mutators (flutter/engine#41573) 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],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose 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/+doc/main/autoroll/README.md
## Background The Windows runner has a race at startup: 1. **Platform thread**: creates a hidden window 2. **Platform thread**: launches the Flutter engine 3. **UI/Raster threads**: renders the first frame 4. **Platform thread**: Registers a callback to show the window once the next frame has been rendered. Steps 3 and 4 happen in parallel and it is possible for step 3 to complete before step 4 starts. In this scenario, the next frame callback is never called and the window is never shown. As a result the `windows_startup_test`'s test, which [verifies that the "show window" callback is called](https://github.com/flutter/flutter/blob/1f09a8662dad3bb1959b24e9124e05e2b9dbff1d/dev/integration_tests/windows_startup_test/windows/runner/flutter_window.cpp#L60-L64), can flake if the first frame is rendered before the show window callback has been registered. ## Solution This change makes the runner schedule a frame after it registers the next frame callback. If step 3 hasn't completed yet, this no-ops as a frame is already scheduled. If step 3 has already completed, a new frame will be rendered, which will call the next frame callback and show the window. Part of flutter#119415 See this thread for alternatives that were considered: flutter/engine#42061 (comment)
This change adds a C++ client wrapper to Windows's "force redraw" C API. This API can be used to schedule a frame.
Part of flutter/flutter#119415
Background
The Windows runner has a race at startup:
Steps 3 and 4 happen in parallel and it is possible for step 3 to complete before step 4 starts. In this scenario, the next frame callback is never called and the window is never shown.
The Windows runner will be updated to call the "force redraw" API after it registers the next frame callback. If step 3 hasn't completed yet, the "force redraw" will no-op as a frame is already scheduled. If step 3 has already completed, the "force redraw" will schedule another frame, which will call the next frame callback and show the window.
See this discussion below on why we cannot avoid changing the Windows runner to fix this issue: #42061 (comment)
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.