Flutter UI 渲染浅析(二)VSync 注册
source link: http://w4lle.com/2020/11/11/flutter-ui-vsync/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Flutter UI 渲染浅析(二)VSync 注册
在 Flutter App 启动过程或者 State 刷新过程中,会请求注册 VSync 信号。
本篇文章主要分析下 VSync 信号注册以及回调过程。
基于 Android 平台,Flutter v1.20.4。
调用时序图
1、Flutter App 启动
Flutter App 启动过程中调用 runApp()
方法
// lib/src/widgets/binding.dart void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) ..scheduleWarmUpFrame(); } class WidgetsFlutterBindingextends BindingBasewithGestureBinding,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 类。
上文中我们提到, Window
是 dart:ui
包中最重要的类,通过 Window
建立起了建立 Dart Framework 和 C++ Engine 的通讯连接, Window
在 Flutter Framework 和 C++ Engine 中分别存在,并通过 ffi
互相调用。
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,渲染任务调度器,注册
onBeginFrame
、onDrawFrame
、onReportTimings
等回调,用于调度渲染流程、计算绘制耗时 - WidgetsBinding,是视图层和 Flutter Engine 之间的胶水层,用于管理三棵树的构建和更新操作;注册
window.onLocaleChanged
、onBuildScheduled
回调;持有 BuilderOwner 对象 - RendererBinding,是绘制树 (Render Tree) 和 Flutter Engine 之间的胶水层,用于布局和绘制,核心方法
drawFrame()
;注册window.onMetricsChanged
、window.onTextScaleFactorChanged
等回调;持有 PipelineOwner 对象 - PaintingBinding,绑定绘制库,主要用于处理图片缓存
- ServicesBinding,注册
window.onPlatformMessage
回调, 用于绑定平台消息通道(message channel),处理原生和 Flutter 通信 - GestureBinding,注册
window.onPointerDataPacket
回调,绑定Framework手势子系统,是Framework事件模型与底层事件的绑定入口 - SemanticsBinding,语义化层与Flutter engine的桥梁,主要是辅助功能的底层支持
重点关注 SchedulerBinding
、 WidgetsBinding
、 RendererBinding
及他们注册的回调方法。
2、scheduleFrame
// 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()
方法。
// lib/src/widgets/framework.dart // State abstract class State<Textends StatefulWidget>withDiagnosticable{ void setState(VoidCallback fn) { ... // 调用callback final dynamic result = fn() as dynamic; ... // element 标脏 _element.markNeedsBuild(); } } // Element abstract class Elementextends DiagnosticableTreeimplements 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()
初始化过程中中注册的。
// 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
调度处理
// 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 方法,并注册 onBeginFrame
、 onDrawFrame
回调方法。
看下 SchedulerBinding
的状态机,这里是和 C++ Engine 中的回调过程一一对应的,后面会看到。
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
方法
// 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
// flutter/lib/ui/dart_ui.cc using tonic::ToDart; namespace flutter { namespace { // 注册Native方法列表 static tonic::DartLibraryNatives* g_natives; // 用的时候来查表 Dart_NativeFunctionGetNativeFunction(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); #ifdefined(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()
// 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()
// 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
// shell/common/async_waiter.cc void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) { ... TRACE_EVENT0("flutter", "AsyncWaitForVsync"); { std::scoped_locklock(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_class
与io/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
// 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_time
和frame_target_time
- 收到 VSync 信号回调后,通过 JNI 回调 Native 方法
Choreographer 通过 SurfaceFlinger
注册 VSync 信号回调 FrameDisplayEventReceiver
,一旦 VSync 信号到来, SurfaceFlinger
通知 FrameDisplayEventReceiver 回调 doFrame()
方法.
注意只会回调一次,每用一次注册一次。所以当屏幕静止未刷新时,FPS 会一为未 0。
4.4、OnNativeVsync
// 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_locklock(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
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_frame semaphore +1,可以继续接收 RequestFrame 请求
- regenerate_layer tree 赋值 false,在一些情况下调用 RequestFrame 可以复用这次生成的
flutter::LayerTree
-
producer_continuation_
是ProducerContinuation
类的对象,持有生产对象,并通过信号量控制管线任务 -
layer_tree_pipeline_
是LayerTreePipeline
类的对象,在 Animator 初始化过程中被创建,LayerTreePipeline
是一个线程安全的生产者消费者队列, UIThread 负责生产flutter::LayerTree
和 RasterThread 负责消费flutter::LayerTree
,flutter::LayerTree
持有 Dart Framework 生成的Layer Tree映射到 Engine 的flow::Layer
的根节点 - 执行
delegate_.OnAnimatorBeginFrame(frame_target_time)
, delagate_ 的实现在Shell
类
5.1、LayerTreePipeline
// 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()
方法时
// shell/common/pipeline.h ProducerContinuationProduce(){ 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_
对象
// shell/common/pipeline.h bool ProducerCommit(ResourcePtr resource,size_t trace_id){ { std::scoped_locklock(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::LayerTree
,available_
+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)
方法
// 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_locktime_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::BeginFrame(fml::TimePoint frameTime) { tonic::DartState::Scopescope(dart_state); int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); // 通过 fii 回调 Dart Framework window._beginFrame() tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame", { Dart_NewInteger(microseconds), })); UIDartState::Current()->FlushMicrotasksNow(); }
通过 Shell::OnAnimatorBeginFrame -> Engine::BeginFrame -> RuntimeController::BeginFrame -> Window::BeginFrame 层层调用,通过 fii 回调 Dart Framework window._beginFrame()
。
到这里,VSync 注册流程结束,接下来回到 Dart Framework 中继续进行。
参考
[Flutter渲染机制—UI线程](
本文链接: http://w4lle.com/2020/11/11/flutter-ui-vsync/
版权声明:本文为 w4lle 原创文章,可以随意转载,但必须在明确位置注明出处!
本文链接: http://w4lle.com/2020/11/11/flutter-ui-vsync/
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK