13

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

 3 years ago
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。

调用时序图

36Fr6fQ.png!mobile

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 类。

上文中我们提到, Windowdart: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,渲染任务调度器,注册 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

// 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 方法,并注册 onBeginFrameonDrawFrame 回调方法。

看下 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_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

// 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

// 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::LayerTreeflutter::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::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) 方法

// 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 中继续进行。

参考

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

[Flutter渲染机制—UI线程](

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

版权声明:本文为 w4lle 原创文章,可以随意转载,但必须在明确位置注明出处!

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK