The "size interpolation" solution didn't go well (more context [here](https://github.com/flutter/engine/pull/40412#issuecomment-1485938933)). Then a new solution came to my mind, and I call it **"delayed swap"**:
In the originally behavior, we swap the width/height immediately before the rotation, resulting in roughly ~4x distortion in the beginning. With "delayed swap" solution, we **swap the width/height right in the middle of the rotation** (i.e. delay the swap for half of the transition duration).
This new "delayed swap" solution gives us the same benefit as the "snapshot" solution:
- reducing ~4x distortion to ~2x
- most distorted frames occur in the middle of rotation when it's moving the fastest, making it hard to notice
And it fixes the drawback of "snapshot" solution:
- it works well with dynamic content like animation or video
- it doesn't have a ~0.5 second penalty when taking the snapshot
Looks pretty good on flutter gallery:
https://user-images.githubusercontent.com/41930132/228383137-7cd09982-89a9-4c83-bf55-9431de708278.mp4
*List which issues are fixed by this PR. You must list at least one issue.*
Fixes https://github.com/flutter/flutter/issues/16322
*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Issue fixed by this PR: https://github.com/flutter/flutter/issues/110138
This PR fixes an issue which is causing app crash when User tries to navigate to a new instance **FlutterFragment** (whose old instance is already present in the fragment backstack) in an Activity (which was restored to saved state after being killed in background due to memory pressure).
Detailed case to reproduce the crash and identify its' root cause:
Setup: Let's say we've an Activity1 which has a bottom nav bar with 3 tabs. Each of this 3 tabs are **FlutterFragment** i.e. Fragment1, Fragment2 & Fragment3 and all of them are using separate **FlutterEngine** but all of them will be cached. e.g. Multiple instances of **Fragment1** will be going to use same cached **FlutterEngine1**.
1. When User opens the app, Fragment1 gets added to fragment backstack
2. Then User navigates to Fragment2 (gets added to backstack as well)
3. Then User navigates to Fragment3 (gets added to backstack as well)
4. Then User puts the app in background. Due to memory pressure OS/platform kills the Activity1 and all 3 FlutterFragments while the app is in background.
5. Then after sometime User tries to bring the app to foreground from the app stack. Since Activity1 was killed by the OS/platform the app process will try to restore the Activity1 in the same state it was before it got killed. This leads to all 3 fragments present in backstack to get instantiated and then the **onAttach()** gets called for all 3, but only Fragment3 gets **onCreateView()** lifecycle event as it was the top most visible Fragment before the FragmentManager saved the state and app went into background. All 3 FlutterFragment goes through following function calls.
FlutterFragment.onAttach() -> FlutterActivityAndFragmentDelegate.onAttach(). There is a one-to-one mapping between **FlutterFragment <-> FlutterActivityAndFragmentDelegate**
<img width="977" alt="1" src="https://user-images.githubusercontent.com/8373036/231222642-1596c77c-d127-476b-9bce-8ad2e9cd3639.png">
FlutterActivityAndFragmentDelegate.onAttach() -> FlutterEngineConnectionRegistry.attachToActivity(). There is a one-to-one mapping between **FlutterEngine <-> FlutterEngineConnectionRegistry**. _**NOTE**: THIS IS VERY IMPORTANT POINT TO KEEP IN MIND TO UNDERSTAND THE ROOT CAAUSE OF THIS CRASH._
<img width="962" alt="2" src="https://user-images.githubusercontent.com/8373036/231222672-016ee708-c310-49c8-8016-070b6057af7b.png">
Since all the 3 **FlutterFragment** were just instantiated on activity restore **exclusiveActivity** will be null and exclusiveActivity will be assigned the host **FlutterActivityAndFragmentDelegate**.
<img width="880" alt="3" src="https://user-images.githubusercontent.com/8373036/231241491-c47f5aa6-96e9-4c1f-b92e-7cfed67381e2.png">
6. Then FlutterFragment.onCreateView() will be called only for Fragment3 and it will be visible without an issue as its' state gets restored properly.
7. Then if User tries to navigate to Fragment2 via instantiating new instance of it (this means that now there will be two instances of Fragment2 in the backstack), then there will be crash as it'll go through following function calls.
FlutterFragment.onAttach() -> FlutterActivityAndFragmentDelegate.onAttach().
<img width="977" alt="1" src="https://user-images.githubusercontent.com/8373036/231222642-1596c77c-d127-476b-9bce-8ad2e9cd3639.png">
FlutterActivityAndFragmentDelegate.onAttach() -> FlutterEngineConnectionRegistry.attachToActivity().
<img width="962" alt="2" src="https://user-images.githubusercontent.com/8373036/231222672-016ee708-c310-49c8-8016-070b6057af7b.png">
THIS IS WHERE THE CRASH STARTS. Since this is the second instance of Fragment2 and both instances are going to use the same cached **FlutterEngine** and hence same **FlutterEngineConnectionRegistry**, this time around **exclusiveActivity** will be non-null as it was assigned during step 5. And since exclusiveActivity will be be non null it'll try to detach from the old **ExclusiveAppComponent** via calling **exclusiveActivity.detachFromFlutterEngine()**.
<img width="878" alt="7" src="https://user-images.githubusercontent.com/8373036/231241550-09908108-1747-4ec3-bc70-145446b86eae.png">
FlutterActivityAndFragmentDelegate.detachFromFlutterEngine() -> FlutterFragment.detachFromFlutterEngine()
<img width="905" alt="4" src="https://user-images.githubusercontent.com/8373036/231222702-5ee739f2-fd71-4e20-807d-e3786e6f27fc.png">
FlutterFragment.detachFromFlutterEngine() -> FlutterActivityAndFragmentDelegate.onDestroyView(). This ideally should not be called if the hosts' **FlutterFragments.onCreateView()** was not called in the first place. Also since the previous author has added **// Redundant calls are ok.** comment, I'm guessing that this is just a fallback cleanup.
<img width="902" alt="5" src="https://user-images.githubusercontent.com/8373036/231222719-2ea28157-bd62-45fe-89ef-dd41ee7b3cca.png">
THIS IS WHERE THE CRASH HAPPENS. FlutterActivityAndFragmentDelegate.onDestroyView() -> FlutterView.detachFromFlutterEngine(). Since the lifecycle of older instance of this FlutterFragment2 was capped at onAttach(), it's **FlutterView** property will be null and calling FlutterView.detachFromFlutterEngine() will throw NPE.
<img width="902" alt="6" src="https://user-images.githubusercontent.com/8373036/231222735-55b63911-6a2d-4967-8043-298f4d413f2a.png">
[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
In Skia, there is such a timeline event in `SnapshotControllerSkia::DoMakeRasterSnapshot`. Therefore, since Impeller wants to mimic Skia and this event does take a long time sometimes, it seems reasonable to add this.
[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Migrate Paint API to `UniqueRef`. This includes `Paint`, `ImageFilter` (and all subtypes), `ColorFilter` (and all subtypes).
Also fix the following memory leaks:
* `CkPaint` is frequently used by layers where a one-off paint object is created, used, and immediately dropped. `CkPaint` now has a `dispose` method, and all one-off usages now dispose of the paint after they are done.
* `CkColorFilter.initRawImageFilter` was leaking the `SkColorFilter` created by `_initRawColorFilter` inside the expression.
* `CkManagedSkImageFilterConvertible.imageFilter` now takes a closure, which allows the implementation decide on the lifetime of the `SkImageFilter` vended to the caller. Because `CkColorFilter` is a const class it cannot store C++ instances inside its own fields, so it creates a temporary `SkImageFilter` class to be used by the caller and then it needs to delete it. Now it does.
Part of: https://github.com/flutter/flutter/issues/124819.
Adds an offscreen texture checkerboarding feature with an overridable
color picker. By default, it should look the same as our current
checkerboarding for Skia. It can be turned on/off at any time while
recording commands.
FlutterMutatorViewTest.TransformedFrameIsCorrect and
FlutterMutatorViewTest.RoundRectClipsToPath both apply transforms then
do exact comparisons on the resulting 4x4 matrices of CGFloat values.
There are very small differences between the elements in the resulting
matrices, on the order of 1e-15, depending on the CPU architecture on
which the floating point operations were run.
Similar to elsewhere in the framework and engine, we now perform
floating point equality tests within some small error bound -- I've
arbitrarily selected 1e-10.
Issue: https://github.com/flutter/flutter/issues/124840
`mac_unopt` orchestrator kicks off Mac swarming tasks, but don't seem to need to run on a Mac. Swap Linux bots which are more plentiful and have a shorter queue time.
Start by adding new `bringup` builder to see if it passes in staging. If it passes I will remove the Mac variant ASAP.
Led run: 49f579a163/+/build.proto
Keep `mac_ios_engine` and `mac_host_engine` as Macs since they need to run Xcodes on Macs to create the xcframeworks.
See also https://github.com/flutter/engine/pull/41181 and https://github.com/flutter/engine/pull/41210
GN+Ninja artifacts have been validated manually. The number of files and their content is the same and presubmit tests are passing correctly in the engine and flutter.
[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Follow up to #41183. Remove the `cores` dimensions so this builder can run on any arm machine which currently all have 8 cores but there's no reason to specify now that the arch is arm.
Introduced in #38261 to avoid 4-core Intel machines.
From the logs reported in https://github.com/flutter/flutter/issues/124864 I noticed we are making multiple calls to `goldctl init` which could be causing some race conditions with the `goldctl imgtest add` calls.
This PR makes sure we only call `goldctl init` once.
Reverts flutter/engine#40746
Googler bug: b/278174021
Failing on
```
shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java:239: Error: This method should only be accessed from tests or within private scope [VisibleForTests]
imeSyncCallback.remove();
```
`mac_android_aot_engine` orchestrator kicks off Mac swarming tasks, but don't seem to need to run on a Mac. Swap Linux bots which are more plentiful and have a shorter queue time.
Start by adding new `bringup` builder to see if it passes in staging. If it passes I will remove the Mac variants ASAP.
Keep `mac_ios_engine` and `mac_host_engine` as Macs since they need to run Xcodes on Macs to create the xcframeworks.
This eliminates a lot of surface area when debugging non-recoverable
frame failures such as https://github.com/flutter/engine/pull/41129.
Every failure case should be covered with at least one log, but without
recursive log spam.