mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

3 notable places I did not migrate. 1. engine/src/flutter/docs/Engine-disk-footprint.md treemaps and what hash is used for what upload. 1. engine/src/flutter/lib/gpu/pubspec.yaml, I didnt want this pr to update code that could need to be reverted and I didnt know what to do to test that publishing would not break or cause a downstream breakage. 1. engine/src/flutter/build/zip_bundle.gni I wasnt sure how to test my changes. Reviewers: Please let me know if you want a different link or if you would prefer something unmodified. Commits: - **Replace triage links with equivalents, change pull request to generic flutter/flutter, replace code search link with equivalent** - **Change link from flutter/engine to engine folder, modify link text to team** - **replace engine repo link with engine folder, replace text repo with folder** - **replace engine specific security info with flutter generic** - **replace engine roller comment with a skia roller equivalent** - **link to same file in new location** - **Remove comment that some code lives in flutter/flutter and some in flutter/engine** - **Say to bump dart in flutter/flutter without mentioning engine** - **Replace documentation with new locations** - **replace code printed comments with new locations** Partially addresses https://github.com/flutter/flutter/issues/167478 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
331 lines
17 KiB
Markdown
331 lines
17 KiB
Markdown
Flutter combines [a Dart framework](https://github.com/flutter/flutter) with a high-performance [engine](https://github.com/flutter/flutter/tree/main/engine).
|
||
|
||
The Flutter Engine is a portable runtime for high-quality cross-platform
|
||
applications. It implements Flutter's core libraries, including animation and
|
||
graphics, file and network I/O, accessibility support, plugin architecture, and
|
||
a Dart runtime and toolchain for developing, compiling, and running Flutter
|
||
applications.
|
||
|
||
## Anatomy of a Flutter app
|
||
|
||
The following diagram gives an overview of the pieces that make up a regular
|
||
Flutter app with one engine generated by `flutter create`. It shows where the
|
||
Flutter Engine sits in this stack, highlights API boundaries, and identifies the
|
||
repositories where the individual pieces live. The legend below clarifies some
|
||
of the terminology commonly used to describe the pieces of a Flutter app.
|
||
|
||
<img src="https://raw.githubusercontent.com/flutter/flutter/main/engine/src/flutter/docs/app_anatomy.svg?sanitize=true" alt="Flutter Architecture Diagram" width="40%"/>
|
||
|
||
#### Dart App
|
||
|
||
* Composes widgets into the desired UI.
|
||
* Implements business logic.
|
||
* Owned by app developer.
|
||
|
||
#### Framework ([source code](https://github.com/flutter/flutter/tree/main/packages/flutter/lib))
|
||
|
||
* Provides higher-level API to build high-quality apps (e.g. widgets,
|
||
hit-testing, gesture detection, accessibility, text input, etc.).
|
||
* Composites the app's widget tree into a scene.
|
||
|
||
#### Engine ([source code](https://github.com/flutter/flutter/tree/main/engine/src/flutter/shell/common))
|
||
|
||
* Responsible for rasterizing composited scenes.
|
||
* Provides low-level implementation of Flutter's core APIs (e.g. graphics, text
|
||
layout, Dart runtime, etc.).
|
||
* Exposes its functionality via its **dart:ui API** to the framework.
|
||
* Integrates with a specific platform via the Engine's **Embedder API**.
|
||
|
||
#### Embedder ([source code](https://github.com/flutter/flutter/tree/main/engine/src/flutter/shell/platform))
|
||
|
||
* Coordinates with the underlying operating system for access to services like
|
||
rendering surfaces, accessibility, and input.
|
||
* Manages the event loop.
|
||
* Exposes **platform-specific API** to integrate the Embedder into apps.
|
||
|
||
#### Runner
|
||
|
||
* Composes the pieces exposed by the platform-specific API of the Embedder into
|
||
an app package runnable on the target platform.
|
||
* Part of app template generated by `flutter create`, owned by app developer.
|
||
|
||
## Architecture overview
|
||
|
||
Flutter's engine takes core technologies, Skia, a 2D graphics rendering library,
|
||
and Dart, a VM for a garbage-collected object-oriented language, and hosts them
|
||
in a shell. Different platforms have different shells, for example we have
|
||
shells for
|
||
[Android](https://github.com/flutter/flutter/tree/main/engine/src/flutter/shell/platform/android)
|
||
and [iOS](https://github.com/flutter/flutter/tree/main/engine/src/flutter/shell/platform/darwin). We
|
||
also have an [embedder
|
||
API](https://github.com/flutter/flutter/tree/main/engine/src/flutter/shell/platform/embedder) which
|
||
allows Flutter's engine to be used as a library (see [Custom Flutter Engine
|
||
Embedders](../engine/Custom-Flutter-Engine-Embedders.md)).
|
||
|
||
The shells implement platform-specific code such as communicating with IMEs
|
||
(on-screen keyboards) and the system's application lifecycle events.
|
||
|
||
The Dart VM implements the normal Dart core libraries, plus an additional
|
||
library called `dart:ui` to provide low-level access to Skia features and the
|
||
shell. The shells can also communicate directly to Dart code via [Platform
|
||
Channels](https://flutter.io/platform-channels/) which bypass the engine.
|
||
|
||

|
||
|
||
## Threading
|
||
|
||
### Overview
|
||
|
||
The Flutter engine does not create or manage its own threads. Instead, it is the
|
||
responsibility of the embedder to create and manage threads (and their message
|
||
loops) for the Flutter engine. The embedder gives the Flutter engine task
|
||
runners for the threads it manages. In addition to the threads managed by the
|
||
embedder for the engine, the Dart VM also has its own thread pool. Neither the
|
||
Flutter engine nor the embedder have any access to the threads in this pool.
|
||
|
||
### Task Runner Configuration
|
||
|
||
The Flutter engine requires the embedder to give it references to 4 task
|
||
runners. The engine does not care if the references are to the same task runner,
|
||
or, if multiple task runners are serviced on the same thread. For optimum
|
||
performance, the embedder should create a dedicated thread per task runner.
|
||
Though the engine does not care about the threads the task runners are serviced
|
||
on, it does expect that the threading configuration remain stable for the entire
|
||
lifetime of the engine. That is, once the embedder decides to service a task
|
||
runner on a particular thread, it should execute tasks for that task runner only
|
||
on that one thread (till the engine is torn down).
|
||
|
||
The main task runners are:
|
||
|
||
* Platform Task Runner
|
||
* UI Task Runner
|
||
* Raster Task Runner
|
||
* IO Task Runner
|
||
|
||
### Platform Task Runner
|
||
|
||
This is the task runner for the thread the embedder considers as its main
|
||
thread. For example, this is typically the [Android Main
|
||
Thread](https://developer.android.com/guide/components/processes-and-threads.html)
|
||
or the [Main
|
||
Thread](https://developer.apple.com/documentation/foundation/nsthread/1412704-ismainthread?language=objc)
|
||
referenced by Foundation on Apple platforms.
|
||
|
||
Any significance assigned to the thread for this task runner is entirely
|
||
assigned by the embedder. The Flutter engine assigns no special meaning to this
|
||
thread. In fact, multiple Flutter engines can be launched with platform task
|
||
runners based on different threads. This is how the Flutter Content Handler in
|
||
Fuchsia works. A new Flutter engine is created in the process for each Flutter
|
||
application and a new platform thread is created for each engine.
|
||
|
||
Interacting with the Flutter engine in any way must happen on the platform
|
||
thread. Interacting with the engine on any other thread will trip assertions in
|
||
unoptimized builds and is not thread safe in release builds. There are numerous
|
||
components in the Flutter engine that are not thread safe. Once the Flutter
|
||
engine is set up and running, the embedder does not have to post tasks to any of
|
||
the task runners used to configure the engine as long as all accesses to the
|
||
embedder API are made on the platform thread.
|
||
|
||
In addition to being the thread on which the embedder interacts with the engine
|
||
after it is launched, this task runner also executes any pending platform
|
||
messages. This is handy because accessing most platform APIs is only safe on the
|
||
platform’s main thread. Plugins don’t have to rethread their calls to the main
|
||
thread. If plugins manage their own worker threads, it is their responsibility
|
||
to queue responses back onto the platform thread before they can be submitted
|
||
back to the engine for processing by Dart code. The rule of always interacting
|
||
with the engine on the platform thread holds here.
|
||
|
||
Even though blocking the platform thread for inordinate amounts of time will not
|
||
block the Flutter rendering pipeline, platforms do impose restrictions on
|
||
expensive operations on this thread. So it is advised that any expensive work in
|
||
response to platform messages be performed on separate worker threads (unrelated
|
||
to the four threads discussed above) before having the responses queued back on
|
||
the the platform thread for submission to the engine. Not doing so may result in
|
||
platform-specific watchdogs terminating the application. Embeddings such as
|
||
Android and iOS also uses the platform thread to pipe through user input events.
|
||
A blocked platform thread can also cause gestures to be dropped.
|
||
|
||
### UI Task Runner
|
||
|
||
The UI task runner is where the engine executes all Dart code for the root
|
||
isolate. The root isolate is a special isolate that has the necessary bindings
|
||
for Flutter to function. This isolate runs the application's main Dart code.
|
||
Bindings are set up on this isolate by the engine to schedule and submit frames.
|
||
For each frame that Flutter has to render:
|
||
|
||
* The root isolate has to tell the engine that a frame needs to be rendered.
|
||
* The engine will ask the platform that it should be notified on the next vsync.
|
||
* The platform waits for the next vsync.
|
||
* On vsync, the engine will wake up the Dart code and [perform the
|
||
following](https://api.flutter.dev/flutter/widgets/WidgetsBinding/drawFrame.html):
|
||
* Update animation interpolators.
|
||
* Rebuild the widgets in the application in a build phase.
|
||
* Lay out the newly constructed and widgets and paint them into a tree of
|
||
layers that are immediately submitted to the engine. Nothing is actually
|
||
rasterized here; only a description of what needs to be painted is
|
||
constructed as part of the paint phase.
|
||
* Construct or update a tree of nodes containing semantic information about
|
||
widgets on screen. This is used to update platform specific accessibility
|
||
components.
|
||
|
||
Apart from building frames for the engine to eventually render, the root isolate
|
||
also executes all responses for platform plugin messages, timers, microtasks and
|
||
asynchronous I/O (from sockets, file handles, etc.).
|
||
|
||
Since the UI thread constructs the layer tree that determines what the engine
|
||
will eventually paint onto the screen, it is the source of truth for everything
|
||
on the screen. Consequently, performing long synchronous operations on this
|
||
thread will cause jank in Flutter applications (a few milliseconds is enough to
|
||
miss the next frame!). Long operations can typically only be caused by Dart code
|
||
since the engine will not schedule any native code tasks on this task runner.
|
||
Because of this, this task runner (or thread) is typically referred to as the
|
||
Dart thread. It is possible for the embedder to post tasks onto this task
|
||
runner. This may cause jank in Flutter and embedders are advised not to do this
|
||
and instead assign a dedicated thread for this task runner.
|
||
|
||
If it is unavoidable for Dart code to perform expensive work, it is advised that
|
||
this code be moved into a separate [Dart
|
||
isolate](https://api.flutter.dev/flutter/dart-isolate/dart-isolate-library.html)
|
||
(e.g. using the
|
||
[`compute`](https://api.flutter.dev/flutter/foundation/compute-constant.html)
|
||
method). Dart code executing on a non-root isolate executes on a thread from a
|
||
Dart VM managed thread pool. This cannot cause jank in a Flutter application.
|
||
Terminating the root isolate will also terminate all isolates spawned by that
|
||
root isolate. Also, non-root isolates are incapable of scheduling frames and do
|
||
not have bindings that the Flutter framework depends on. Due to this, you cannot
|
||
interact with the Flutter framework in any meaningful way on the secondary
|
||
isolate. Use secondary isolates for tasks that require heavy computation.
|
||
|
||
### Raster Task Runner
|
||
|
||
The raster task runner executes tasks that need to access the rasterizer
|
||
(usually backed by GPU) on the device. The layer tree created by the Dart code
|
||
on the UI task runner is client-rendering-API agnostic. That is, the same layer
|
||
tree can be used to render a frame using OpenGL, Vulkan, software or really any
|
||
other backend configured for Skia. Components on the GPU task runner take the
|
||
layer tree and construct the appropriate draw commands. The raster task runner
|
||
components are also responsible for setting up all the GPU resources for a
|
||
particular frame. This includes talking to the platform to set up the
|
||
framebuffer, managing surface lifecycle, and ensuring that textures and buffers
|
||
for a particular frame are fully prepared.
|
||
|
||
Depending on how long it takes for the layer tree to be processed and the device
|
||
to finish displaying the frame, the various components of the raster task runner
|
||
may delay scheduling of further frames on the UI thread. Typically, the UI and
|
||
raster task runners are on different threads. In such cases, the raster thread
|
||
can be in the process of submitting a frame to the GPU while the UI thread is
|
||
already preparing the next frame. The pipelining mechanism makes sure that the
|
||
UI thread does not schedule too much work for the rasterizer.
|
||
|
||
Since the raster task runner components can introduce frame scheduling delays on
|
||
the UI thread, performing too much work on the raster thread will cause jank in
|
||
Flutter applications. Typically, there is no opportunity for the user to perform
|
||
custom tasks on this task runner because neither platform code nor Dart code can
|
||
access this task runner. However, it is still possible for the embedder to
|
||
schedule tasks on this thread. For this reason, it is recommended that embedders
|
||
provide a dedicated thread for the raster task runner per engine instance.
|
||
|
||
### IO Task Runner
|
||
|
||
All the task runners mentioned so far have pretty strong restrictions on the
|
||
kinds of operations that can be performed on this. Blocking the platform task
|
||
runner for an inordinate amount of time may trigger the platform's watchdog, and
|
||
blocking either the UI or raster task runners will cause jank in Flutter
|
||
applications. However, there are tasks necessary for the raster thread that
|
||
require doing some very expensive work. This expensive work is performed on the
|
||
IO task runner.
|
||
|
||
The main function of the IO task runner is reading compressed images from an
|
||
asset store and making sure these images are ready for rendering on the raster
|
||
task runner. To make sure a texture is ready for rendering, it first has to be
|
||
read as a blob of compressed data (typically PNG, JPEG, etc.) from an asset
|
||
store, decompressed into a GPU friendly format and uploaded to the GPU. These
|
||
operations are expensive and will cause jank if performed on the raster task
|
||
runner. Since only the raster task runner can access the GPU, the IO task runner
|
||
components set up a special context that is in the same sharegroup as the main
|
||
raster task runner context. This happens very early during engine setup and is
|
||
also the reason there is a single task runner for IO tasks. In reality, the
|
||
reading of the compressed bytes and decompression can happen on a thread pool.
|
||
The IO task runner is special because access to the context is only safe from a
|
||
specific thread. The only way to get a resource like
|
||
[`ui.Image`](https://docs.flutter.io/flutter/dart-ui/instantiateImageCodec.html)
|
||
is via an async call; this allows the framework to talk to the IO runner so that
|
||
it can asynchronously perform all the texture operations mentioned. The image
|
||
can then be immediately used in a frame without the raster thread having to do
|
||
expensive work.
|
||
|
||
There is no way for user code to access this thread either via Dart or native
|
||
plugins. Even the embedder is free to schedule tasks on this thread that are
|
||
fairly expensive. This won’t cause jank in Flutter applications but may delay
|
||
having the futures images and other resources be resolved in a timely manner.
|
||
Even so, it is recommended that custom embedders set up a dedicated thread for
|
||
this task runner.
|
||
|
||
### Flutter Engine Groups
|
||
|
||
There are two different ways to create Flutter engines which has ramifications
|
||
on how threading is managed across multiple Flutter engines. Flutter engines can
|
||
be instantiated directly or through the FlutterEngineGroup APIs
|
||
[[ios](https://github.com/flutter/flutter/blob/main/engine/src/flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h),
|
||
[android](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/FlutterEngineGroup.html)].
|
||
If an engine is instantiated directly, it gets its own engine threads and [dart isolate
|
||
group](https://dart.dev/language/concurrency#performance-and-isolate-groups).
|
||
|
||
However, all engines instantiated with a FlutterEngineGroup share the same
|
||
threads across the group. Each engine has their own root isolate but share the
|
||
same UI task runner, Dart isolate group and source code.
|
||
|
||
As a result engines in a FlutterEngineGroup will have a smaller memory
|
||
footprint, faster startup latency, and better CPU core usage with shared threads
|
||
and a shared thread pool for isolates.
|
||
|
||
### Current Platform Specific Threading Configurations
|
||
|
||
As mentioned, the engine can support multiple threading configurations, the
|
||
configurations currently used by the supported platforms are:
|
||
|
||
#### iOS
|
||
|
||
A dedicated thread is created for the UI, raster and IO task runners per engine
|
||
instance. All engine instances share the same platform thread and task runner.
|
||
|
||
#### Android
|
||
|
||
A dedicated thread is created for the UI, raster and IO task runners per engine
|
||
instance. All engine instances share the same platform thread and task runner.
|
||
|
||
#### Fuchsia
|
||
|
||
A dedicated thread is created for the UI, raster, IO and Platform task runners
|
||
per engine instance.
|
||
|
||
#### macOS
|
||
|
||
A dedicated thread is created for the UI, raster and IO task runners per engine
|
||
instance. All engine instances share the same platform thread and task runner.
|
||
|
||
#### Windows
|
||
|
||
A dedicated thread is created for the UI, raster and IO task runners per engine
|
||
instance. All engine instances share the same platform thread and task runner.
|
||
|
||
#### Linux
|
||
|
||
A dedicated thread is created for the UI and IO task runners per engine
|
||
instance. The platform thread and the raster thread are the same thread. All
|
||
engine instances share the same platform thread and task runner.
|
||
|
||
#### Flutter Tester (used by `flutter test`)
|
||
|
||
The same main thread is used for the UI, raster, IO and Platform task runners
|
||
for the single instance engine supported in the process.
|
||
|
||
## Text rendering
|
||
|
||
Our text rendering stack is as follows:
|
||
|
||
* A minikin derivative we call libtxt (font selection, bidi, line breaking).
|
||
* HarfBuzz (glyph selection, shaping).
|
||
* Skia (rendering/GPU back-end), which uses FreeType for font rendering on
|
||
Android and Fuchsia, and CoreGraphics for font rendering on iOS.
|