w4lle's Notes

人生如逆旅,我亦是行人。

w4lle's avatar w4lle

Flutter UI 渲染浅析(二)VSync 注册

在 Flutter App 启动过程或者 State 刷新过程中,会请求注册 VSync 信号。

本篇文章主要分析下 VSync 信号注册以及回调过程。

基于 Android 平台,Flutter v1.20.4。

调用时序图

1、Flutter App 启动

Flutter App 启动过程中调用 runApp()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// lib/src/widgets/binding.dart
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}

runApp()过程中初始化 WidgetsFlutterBinding,它是 Dart Framework 和 C++ Engine 之间的胶水层,并且混入了 7 个Binding 类,用于初始化 7 个 Binding 类。

上文中我们提到,Windowdart:ui 包中最重要的类,通过 Window 建立起了建立 Dart Framework 和 C++ Engine 的通讯连接,Window 在 Flutter Framework 和 C++ Engine 中分别存在,并通过 ffi 互相调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Window {
// 当窗口绘制区域变化时回调,比如[devicePixelRatio],
/// [physicalSize], [padding], [viewInsets], or [systemGestureInsets]变化
VoidCallback? get onMetricsChanged => _onMetricsChanged;
// 当语言环境发生变化时回调
VoidCallback? get onLocaleChanged => _onLocaleChanged;
// 当系统字体缩放变化时回调
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
// 绘制前回调,一般在收到 VSync 信号后回调,或者在App启动首帧主动调用
FrameCallback get onBeginFrame => _onBeginFrame;
// 绘制回调,一般在 onBeginFrame 和 microtask 执行完后执行,或者在App启动首帧主动调用
VoidCallback get onDrawFrame => _onDrawFrame;
// 绘制时间回调,用于计算fps
TimingsCallback? get onReportTimings => _onReportTimings;
// 点击或指针事件回调
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
// 触发 Engine Pipeline 渲染管线工作,注册 VSync 信号回调,当 VSync 信号到来时
// onBeginFrame和onDrawFrame 会被调用
void scheduleFrame() native 'Window_scheduleFrame';
// 触发 RasterThread 执行光栅化合成
void render(Scene scene) native 'Window_render';
// 发送平台消息
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) ;
// 平台通道消息处理回调
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
...
}

Window 中存在一些回调方法,由 C++ Engine 触发回调方法,触发 Flutter Framework 执行相应动作。

这些回调方法分散注册在 7 个 Binding 类中。

  • SchedulerBinding,渲染任务调度器,注册 onBeginFrameonDrawFrameonReportTimings 等回调,用于调度渲染流程、计算绘制耗时
  • WidgetsBinding,是视图层和 Flutter Engine 之间的胶水层,用于管理三棵树的构建和更新操作;注册 window.onLocaleChangedonBuildScheduled 回调;持有 BuilderOwner 对象
  • RendererBinding,是绘制树 (Render Tree) 和 Flutter Engine 之间的胶水层,用于布局和绘制,核心方法drawFrame();注册 window.onMetricsChangedwindow.onTextScaleFactorChanged 等回调;持有 PipelineOwner 对象
  • PaintingBinding,绑定绘制库,主要用于处理图片缓存
  • ServicesBinding,注册window.onPlatformMessage 回调, 用于绑定平台消息通道(message channel),处理原生和 Flutter 通信
  • GestureBinding,注册 window.onPointerDataPacket 回调,绑定Framework手势子系统,是Framework事件模型与底层事件的绑定入口
  • SemanticsBinding,语义化层与Flutter engine的桥梁,主要是辅助功能的底层支持

重点关注 SchedulerBindingWidgetsBindingRendererBinding 及他们注册的回调方法。

2、scheduleFrame

1
2
3
4
5
6
// lib/src/widgets/binding.dart
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}

runApp() 启动方法中

  • scheduleAttachRootWidget(app) 首先构建三棵树,并且通过 BuildOwner 调用 scheduleFrame() 触发注册 VSync 信号
  • scheduleWarmUpFrame() 准备第一帧的绘制工作,触发 beginFrame()drawFrame() 执行方法,这样就不用等待 VSync 信号回调,可以尽快的绘制第一帧

同样的,在 State.setState() 方法中也会触发 ScheduleFrame() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// lib/src/widgets/framework.dart
// State
abstract class State<T extends StatefulWidget> with Diagnosticable {
void setState(VoidCallback fn) {
...
// 调用callback
final dynamic result = fn() as dynamic;
...
// element 标脏
_element.markNeedsBuild();
}
}
// Element
abstract class Element extends DiagnosticableTree implements BuildContext {
void markNeedsBuild() {
...
// 防止重复标脏
if (dirty)
return;
_dirty = true;
// 触发绘制
owner.scheduleBuildFor(this);
}
}
// BuildOwner
class BuildOwner {
void scheduleBuildFor(Element element) {
...
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
// 调用scheduleFrame
onBuildScheduled();
}
// 添加到 _dirtyElements 脏列表
_dirtyElements.add(element);
element._inDirtyList = true;
}

最终会调用到 BuildOwner.onBuildScheduled() ,这个回调方法是在 WidgetsBinding.initInstances() 初始化过程中中注册的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// lib/src/widgets/binding.dart WidgetsBinding
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
//构造BuildeOwner,主要负责Widget的build过程
_buildOwner = BuildOwner();
// 注册 onBuildScheduled 回调
buildOwner.onBuildScheduled = _handleBuildScheduled;
//注册window相关回调
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
//导航channel
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
void _handleBuildScheduled() {
...
// 交给 SchedulerBinding 调度处理
ensureVisualUpdate();
}
}

交给 SchedulerBinding 调度处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// lib/src/scheduler/binding.dart
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
// 调用
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
void scheduleFrame() {
//防止重复注册
if (_hasScheduledFrame || !framesEnabled)
return;
// 注册回调方法
ensureFrameCallbacksRegistered();
// 通过window,调用Engine scheduleFrame
window.scheduleFrame();
}
void ensureFrameCallbacksRegistered() {
// 注册 onBeginFrame 、onDrawFrame 回调方法
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}

通过 window 调用Engine Window ::scheduleFrame() Native 方法,并注册 onBeginFrameonDrawFrame 回调方法。

看下 SchedulerBinding 的状态机,这里是和 C++ Engine 中的回调过程一一对应的,后面会看到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum SchedulerPhase {
//空闲状态
idle,
//调用瞬时回调状态,主要处理动画计算机更新等任务,由WidgetsBinding.scheduleFrameCallback注册回调
transientCallbacks,
//处理transientCallbacks阶段注册的微任务microtasks
midFrameMicrotasks,
//build/layout/paint阶段,同时处理WidgetsBinding.addPersistentFrameCallback注册的persistentCallbacks
persistentCallbacks,
//绘制完成,处理一些清理工作,同时处理WidgetsBinding.addPostFrameCallback注册的postFrameCallbacks,App层可以在这里处理一些绘制完成后的工作
postFrameCallbacks,
}

3、dart:ffi

在 Flutter 中,可以使用 dart:ffi 实现 Dart 方法和 Native 方法的互相调用绑定,类似于 Java 的 JNI

在 Engine Window 类中,通过 dart:ffi 注册 Native 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// flutter/lib/ui/window/window.cc
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
// 默认路由 '/'
{"Window_defaultRouteName", DefaultRouteName, 1, true},
// 触发绘制
{"Window_scheduleFrame", ScheduleFrame, 1, true},
// 发送消息回调
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
// 回调消息
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
// 光栅化合成
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
// 上报异常
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
{"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true},
...
});
}

列表中的参数:

  • 第一个参数是 Dart 方法id
  • 第二个参数是 Engine 中 Native 方法的指针,一般以 _ 开头的方法是 Dart 注册的回调方法或者需要回调的方法
  • 第三个参数是方法参数个数
  • 第四个参数为是否自动注册

DartVM 启动过程中,将 Native 方法注册到 dart:ffi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// flutter/lib/ui/dart_ui.cc
using tonic::ToDart;
namespace flutter {
namespace {
// 注册Native方法列表
static tonic::DartLibraryNatives* g_natives;
// 用的时候来查表
Dart_NativeFunction GetNativeFunction(Dart_Handle name,
int argument_count,
bool* auto_setup_scope) {
return g_natives->GetNativeFunction(name, argument_count, auto_setup_scope);
}
//获取 Dart 方法id
const uint8_t* GetSymbol(Dart_NativeFunction native_function) {
return g_natives->GetSymbol(native_function);
}
} // namespace
void DartUI::InitForGlobal() {
if (!g_natives) {
// 构造DartLibraryNatives类,持有方法列表,分为dart方法id映射表,和Native方法映射表
g_natives = new tonic::DartLibraryNatives();
Canvas::RegisterNatives(g_natives);
CanvasGradient::RegisterNatives(g_natives);
CanvasImage::RegisterNatives(g_natives);
CanvasPath::RegisterNatives(g_natives);
CanvasPathMeasure::RegisterNatives(g_natives);
Codec::RegisterNatives(g_natives);
ColorFilter::RegisterNatives(g_natives);
DartRuntimeHooks::RegisterNatives(g_natives);
EngineLayer::RegisterNatives(g_natives);
FontCollection::RegisterNatives(g_natives);
FrameInfo::RegisterNatives(g_natives);
ImageFilter::RegisterNatives(g_natives);
ImageShader::RegisterNatives(g_natives);
IsolateNameServerNatives::RegisterNatives(g_natives);
Paragraph::RegisterNatives(g_natives);
ParagraphBuilder::RegisterNatives(g_natives);
Picture::RegisterNatives(g_natives);
PictureRecorder::RegisterNatives(g_natives);
Scene::RegisterNatives(g_natives);
SceneBuilder::RegisterNatives(g_natives);
SemanticsUpdate::RegisterNatives(g_natives);
SemanticsUpdateBuilder::RegisterNatives(g_natives);
Vertices::RegisterNatives(g_natives);
Window::RegisterNatives(g_natives);
#if defined(LEGACY_FUCHSIA_EMBEDDER)
SceneHost::RegisterNatives(g_natives);
#endif
}
}
// DartVM 初始化过程中会调用
void DartUI::InitForIsolate() {
FML_DCHECK(g_natives);
Dart_Handle result = Dart_SetNativeResolver(
Dart_LookupLibrary(ToDart("dart:ui")), GetNativeFunction, GetSymbol);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
}

DartVM 初始化过程中会调用 DartUI::InitForIsolate() 进行方法注册。

上面的类都是 dart:ui 包下的类,通过 dart:ffi 将 Dart 方法和 Native 方法进行绑定。

dart:ffi 的详细介绍见 Binding to native code using dart:ffi

4、注册 VSync

继续调用 Window::ScheduleFrame()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// lib/ui/window.cc
void ScheduleFrame(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
UIDartState::Current()->window()->client()->ScheduleFrame();
}
// runtime/runtime_controller.cc
// |WindowClient|
void RuntimeController::ScheduleFrame() {
client_.ScheduleFrame();
}
// shell/common/engine.cc
void Engine::ScheduleFrame(bool regenerate_layer_tree) {
animator_->RequestFrame(regenerate_layer_tree);
}
// shell/common/animator.cc
void Animator::RequestFrame(bool regenerate_layer_tree) {
if (regenerate_layer_tree) {
// 默认值 true,决定是否重新生成layer tree
regenerate_layer_tree_ = true;
}
// 当调用 Animator::Stop() 停止请求
if (paused_ && !dimension_change_pending_) {
return;
}
if (!pending_frame_semaphore_.TryWait()) {
// 多次调用,只生效一次
return;
}
// 在 UIThread 执行 VSync 注册回调
task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
frame_number = frame_number_]() {
if (!self.get()) {
return;
}
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
//注册 VSync
self->AwaitVSync();
});
// 标记已经请求渲染
frame_scheduled_ = true;
}
  • 这里方法的参数 regenerate_layer_tree 默认值是 true,当为 false 时不需要重绘
  • 通过 pending_frame_semaphore_ 信号量控制 Animator::ScheduleFrame 只需注册一次 VSync 信号,初始值为1,为0 时,这里 pending_frame_semaphore_ -1,Animator::beginFrame() 被调用后 pending_frame_semaphore_ + 1
  • 在 UIThread 执行 VSync 注册回调

4.1、Animator::AwaitVSync()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// shell/common/animator.cc
void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
// VSync 信号的回调方法
// frame_start_time 为接受到 VSync 信号时间点
// frame_target_time 为这一帧渲染目标时间,超过这个时间即为掉帧
if (self) {
if (self->CanReuseLastLayerTree()) {
// regenerate_layer_tree 为false,复用上一次生成的LayerTree,直接光栅化合成
self->DrawLastLayerTree();
} else {
// 触发渲染管线工作
self->BeginFrame(frame_start_time, frame_target_time);
}
}
});
delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}
  • 注册VSync 信号的回调
  • frame_start_time 为接受到 VSync 信号时间点, frame_target_time 为这一帧渲染目标时间,超过这个时间点即为掉帧,下一帧就不会注册 VSync 信号回调
  • regenerate_layer_tree 为false,复用上一次生成的LayerTree,直接光栅化合成;为 false 继续触发渲染管线启动
  • waiter_VsyncWaiter 对象,在引擎启动过程中,初始化 Shell 时被创建,根据不同 Platform,构建出不同的对象,这里以 Android 为例,它是 VsyncWaiterAndroid 类的对象

4.2、VsyncWaiter::AsyncWaitForVsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// shell/common/async_waiter.cc
void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) {
...
TRACE_EVENT0("flutter", "AsyncWaitForVsync");
{
std::scoped_lock lock(callback_mutex_);
...
callback_ = std::move(callback);
...
}
AwaitVSync();
}
// shell/platform/android/vsync_waiter_android.cc
void VsyncWaiterAndroid::AwaitVSync() {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(weak_this);
task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {
JNIEnv* env = fml::jni::AttachCurrentThread();
env->CallStaticVoidMethod(g_vsync_waiter_class->obj(),
g_async_wait_for_vsync_method_,
java_baton
);
});
}
// 注册 JNI 方法
bool VsyncWaiterAndroid::Register(JNIEnv* env) {
// VSync 回调,Java 调用
static const JNINativeMethod methods[] = {{
.name = "nativeOnVsync",
.signature = "(JJJ)V",
.fnPtr = reinterpret_cast<void*>(&OnNativeVsync),
}};
jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");
g_vsync_waiter_class = new fml::jni::ScopedJavaGlobalRef<jclass>(env, clazz);
g_async_wait_for_vsync_method_ = env->GetStaticMethodID(
g_vsync_waiter_class->obj(), "asyncWaitForVsync", "(J)V");
return env->RegisterNatives(clazz, methods, fml::size(methods)) == 0;
}

在引擎启动过程中,已经通过 Java JNI 绑定了 C++ 和 Java 代码的映射,这里通过 JNI 调用 Android Java 代码

  • JNIEnv 代表了 Java 在本线程的执行环境(在这里就是 Android 主线程),它的结构是一个函数表
  • 注册 VSync 信号回调 nativeOnVsync Native 方法 OnNativeVsync
  • 运行在 PlatformThread,即 Android 主线程
  • g_vsync_waiter_classio/flutter/embedding/engine/FlutterJNI Java 类绑定
  • g_async_wait_for_vsync_method_io/flutter/embedding/engine/FlutterJNI.asyncWaitForVsync() Java 方法的指针

最终在 Android 主线程调用 io/flutter/embedding/engine/FlutterJNI.asyncWaitForVsync() 方法。

4.3、Choreographer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// io.flutter.embedding.engine.FlutterJNI
private static void asyncWaitForVsync(final long cookie) {
asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);
...
}
//io.flutter.view.VsyncWaiter
public class VsyncWaiter {
private static VsyncWaiter instance;
@NonNull
public static VsyncWaiter getInstance(@NonNull WindowManager windowManager) {
if (instance == null) {
instance = new VsyncWaiter(windowManager);
}
return instance;
}
@NonNull private final WindowManager windowManager;
private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
new FlutterJNI.AsyncWaitForVsyncDelegate() {
@Override
public void asyncWaitForVsync(long cookie) {
Choreographer.getInstance()
// 注册 VSync 回调,只会回调一次
.postFrameCallback(
new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
// 获取屏幕刷新频率,例如60、90、120
float fps = windowManager.getDefaultDisplay().getRefreshRate();
// 根据刷新频率计算出每一帧绘制时间
long refreshPeriodNanos = (long) (1000000000.0 / fps);
// 回调 Native 方法,frameTimeNanos 即为 frame_start_time
FlutterJNI.nativeOnVsync(
frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
}
});
}
};
private VsyncWaiter(@NonNull WindowManager windowManager) {
this.windowManager = windowManager;
}
public void init() {
//设置 FlutterJNI delegate
FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);
// 获取屏幕刷新频率,例如60、90、120
float fps = windowManager.getDefaultDisplay().getRefreshRate();
FlutterJNI.setRefreshRateFPS(fps);
}
}
  • 在 App 启动过程中,FlutterLoader.startInitialization() 方法中初始化 VsyncWaiter 单例,设置 FlutterJNI delegate
  • 通过 Choreographer..postFrameCallback() 注册 VSync 信号回调,只会回调一次,每用一次注册一次
  • 根据屏幕刷新频率,计算 frame_start_timeframe_target_time
  • 收到 VSync 信号回调后,通过 JNI 回调 Native 方法

Choreographer 通过 SurfaceFlinger 注册 VSync 信号回调 FrameDisplayEventReceiver,一旦 VSync 信号到来,SurfaceFlinger 通知 FrameDisplayEventReceiver 回调 doFrame() 方法.

注意只会回调一次,每用一次注册一次。所以当屏幕静止未刷新时,FPS 会一为未 0。

4.4、OnNativeVsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// shell/platform/android/vsync_waiter_android.cc
void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env,
jclass jcaller,
jlong frameTimeNanos,
jlong frameTargetTimeNanos,
jlong java_baton) {
auto frame_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frameTimeNanos));
auto target_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frameTargetTimeNanos));
ConsumePendingCallback(java_baton, frame_time, target_time);
}
// shell/common/async_waiter.cc
void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
Callback callback;
fml::closure secondary_callback;
{
std::scoped_lock lock(callback_mutex_);
callback = std::move(callback_);
}
if (callback) {
auto flow_identifier = fml::tracing::TraceNonce();
TRACE_FLOW_BEGIN("flutter", kVsyncFlowName, flow_identifier);
// 运行在 UIThread
task_runners_.GetUITaskRunner()->PostTaskForTime(
[callback, flow_identifier, frame_start_time, frame_target_time]() {
// 执行回调方法
callback(frame_start_time, frame_target_time);
TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier);
},
frame_start_time);
}
}

执行注册的回调方法,运行在 UIThread,回到 #4.1

5、BeginFrame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void Animator::BeginFrame(fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
frame_scheduled_ = false;
notify_idle_task_id_++;
regenerate_layer_tree_ = false;
//信号量 pending_frame_semaphore_ +1,可以继续接收 RequestFrame 请求
pending_frame_semaphore_.Signal();
if (!producer_continuation_) {
// producer_continuation_ 持有生产对象,并通过信号量控制管线任务
producer_continuation_ = layer_tree_pipeline_->Produce();
if (!producer_continuation_) {
// layer_tree_pipeline_ 管线任务已满(2个),忽略本次绘制
RequestFrame();
return;
}
}
last_frame_begin_time_ = frame_start_time;
last_frame_target_time_ = frame_target_time;
dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
{
delegate_.OnAnimatorBeginFrame(frame_target_time);
}
if (!frame_scheduled_) {
// Animator::RequestFrame() 方法最后置为 true,标记已经请求渲染
task_runners_.GetUITaskRunner()->PostDelayedTask(
[self = weak_factory_.GetWeakPtr(),
notify_idle_task_id = notify_idle_task_id_]() {
if (!self.get()) {
return;
}
// 51ms 时间之内没有新的提交新的任务,通知Engine空闲可以进行 GC
if (notify_idle_task_id == self->notify_idle_task_id_ &&
!self->frame_scheduled_) {
self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
100000);
}
},
// 51ms,通知 Engine 空闲
kNotifyIdleTaskWaitTime);
}
}
  • 信号量 pending_framesemaphore +1,可以继续接收 RequestFrame 请求
  • regenerate_layertree 赋值 false,在一些情况下调用 RequestFrame 可以复用这次生成的 flutter::LayerTree
  • producer_continuation_ProducerContinuation 类的对象,持有生产对象,并通过信号量控制管线任务
  • layer_tree_pipeline_LayerTreePipeline 类的对象,在 Animator 初始化过程中被创建,LayerTreePipeline 是一个线程安全的生产者消费者队列, UIThread 负责生产 flutter::LayerTree 和 RasterThread 负责消费 flutter::LayerTreeflutter::LayerTree 持有 Dart Framework 生成的Layer Tree映射到 Engine 的 flow::Layer 的根节点
  • 执行 delegate_.OnAnimatorBeginFrame(frame_target_time) , delagate_ 的实现在 Shell

5.1、LayerTreePipeline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// shell/common/animator.h
using LayerTreePipeline = Pipeline<flutter::LayerTree>;
...
// shell/common/animator.cc
Animator::Animator(Delegate& delegate,
TaskRunners task_runners,
std::unique_ptr<VsyncWaiter> waiter)
: delegate_(delegate),
task_runners_(std::move(task_runners)),
waiter_(std::move(waiter)),
last_frame_begin_time_(),
last_frame_target_time_(),
dart_frame_deadline_(0),
#if FLUTTER_SHELL_ENABLE_METAL
// 支持 Metal 深度为2
layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)),
#else
// 这里在一些情况下有bug,后续版本会写死深度为2
layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(
task_runners.GetPlatformTaskRunner() ==
task_runners.GetRasterTaskRunner()
? 1
: 2)),
#endif // FLUTTER_SHELL_ENABLE_METAL
// pending_frame_semaphore_ 初始化为1,可以接受一个 RequestFrame 请求
pending_frame_semaphore_(1),
...
// shell/common/pipeline.h
explicit Pipeline(uint32_t depth)
: depth_(depth), empty_(depth), available_(0), inflight_(0) {}
fml::Semaphore empty_; // 默认为2
fml::Semaphore available_ // 默认为0

构建深度为 2 的 Pipeline 对象,可以持有 flutter::LayerTree 对象。

Pipeline 持有两个信号量 empty_available_,用来控制管线任务调度。

当调用 layer_tree_pipeline_->Produce() 方法时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// shell/common/pipeline.h
ProducerContinuation Produce() {
if (!empty_.TryWait()) {
// 信号量 empty_ 为0时,返回空
return {};
}
++inflight_;
return ProducerContinuation{
// 持有 Pipeline::ProducerCommit 方法引用
std::bind(&Pipeline::ProducerCommit, this, std::placeholders::_1,
std::placeholders::_2), // continuation
GetNextPipelineTraceID()}; // trace id
}

生成一个 ProducerContinuation 类的对象 producer_continuation_continuation_ 对象持有 Pipeline::ProducerCommit() 方法引用。

当 UIThread 在 Dart Framework 生成 Layer Tree,并通过 SceneBuilder 构建出 Scene 对象,持有 flutter:LayerTree,调用 window.render() 方法开启光栅化合成之前,会调用Pipeline::ProducerCommit() 方法,并将 flutter:LayerTree 绑定到 producer_continuation_对象

1
2
3
4
5
6
7
8
9
10
11
// shell/common/pipeline.h
bool ProducerCommit(ResourcePtr resource, size_t trace_id) {
{
std::scoped_lock lock(queue_mutex_);
//resource 是 flutter:LayerTree 对象,添加到队列
queue_.emplace_back(std::move(resource), trace_id);
}
// available_ + 1,触发 RasterThread 光栅化
available_.Signal();
return true;
}

整个 Pipeline 管线的流程为:

  • Animator::RequestFrame()方法触发绘制开始_empty -1,开始生成 flutter::LayerTree,运行在 UIThread

  • 当 UIThread 准备好 Engine flutter::LayerTreeavailable_ +1

  • available_ > 0 时,触发 Raster Thread 工作,拿到 flutter:LayerTree 进行光栅化合成

  • 当 RasterThread 处理完成, _empty + 1,下次 Animator::RequestFrame() 可以正常开始处理生成 flutter::LayerTree 工作

  • _empty 为 0 时,管线任务已满,忽略本次 Animator::RequestFrame() 请求,直到下一次 VSync 信号到来

通过两个信号量来管理管线的调度,这种调度机制可以确保 RasterThread 不至于过载(2个任务),同时也可以避免 UIThread 不必要的资源消耗。

所以不论在 UIThread 还是在 RasterThread 耗时太久,都可能会导致 Flutter 应用卡顿,因为会导致延迟接受 VSync 信号,导致掉帧。

5.2、OnAnimatorBeginFrame

继续调用 delegate_.OnAnimatorBeginFrame(frame_target_time)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// shell/common/shell.cc
// |Animator::Delegate|
void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
// record the target time for use by rasterizer.
{
std::scoped_lock time_recorder_lock(time_recorder_mutex_);
// 记录frame_target_time
latest_frame_target_time_.emplace(frame_target_time);
}
if (engine_) {
engine_->BeginFrame(frame_target_time);
}
}
// shell/common/engine.cc
void Engine::BeginFrame(fml::TimePoint frame_time) {
TRACE_EVENT0("flutter", "Engine::BeginFrame");
runtime_controller_->BeginFrame(frame_time);
}
// runtime/runtime_controller.cc
bool RuntimeController::BeginFrame(fml::TimePoint frame_time) {
if (auto* window = GetWindowIfAvailable()) {
window->BeginFrame(frame_time);
return true;
}
return false;
}
// lib/ui/window/window.cc
void Window::DidCreateIsolate() {
library_.Set(tonic::DartState::Current(),
Dart_LookupLibrary(tonic::ToDart("dart:ui")));
}
void Window::BeginFrame(fml::TimePoint frameTime) {
tonic::DartState::Scope scope(dart_state);
int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds();
// 通过 fii 回调 Dart Framework window._beginFrame()
tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame",
{
Dart_NewInteger(microseconds),
}));
// 执行 microtasks
UIDartState::Current()->FlushMicrotasksNow();
// 通过 ffi 回调 Dart Framework window._drawFrame()
tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {}));
}

通过 Shell::OnAnimatorBeginFrame -> Engine::BeginFrame -> RuntimeController::BeginFrame -> Window::BeginFrame 层层调用,通过 ffi 回调 Dart Framework 继续绘制流程。

做三件事情:

  • 执行 Dart Framework dart:ui 包下的 _beginFrame()
  • 执行 microtasks 任务
  • 执行 Dart Framework dart:ui 包下的 _drawFrame()

这个流程和文章开头提到的 SchedulerBinding 的状态机 SchedulerPhase 中声明的状态一一对应,接下来回调到 Dart Framework 后,由 SchedulerBinding 进行绘制调度。

到这里,VSync 注册流程结束,接下来回到 Dart Framework 中继续进行。

参考

14.4 Flutter运行机制-从启动到显示

Flutter渲染机制—UI线程

本文链接: http://w4lle.com/2020/11/11/flutter-ui-vsync/

版权声明:本文为 w4lle 原创文章,可以随意转载,但必须在明确位置注明出处!
本文链接: http://w4lle.com/2020/11/11/flutter-ui-vsync/