flutter UI渲染源码解析之Engin绘制过程(二)

engin绘制过程

上文中我们讲到了VSYNC的注册,注册之后会等待vsync信号回调doframe,然后回调到了FlutterJNI.nativeOnVsync(frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);,因为JNILoad的时候将FlutterJNI#nativeOnVsyncVsyncWaiterAndroid#OnNativeVsync做了绑定,此时会被回调到OnNativeVsync中

  • 1.vsnyc_waiter_android.cc VsyncWaiterAndroid::OnNativeVsync
/// static
void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env,
                                       jclass jcaller,
                                       jlong frameTimeNanos,
                                       jlong frameTargetTimeNanos,
                                       jlong java_baton) {
  TRACE_EVENT0("flutter", "VSYNC");

  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);
}

这里的target_time = frame_time+屏幕刷新时间(fps)
float fps = windowManager.getDefaultDisplay().getRefreshRate();
long refreshPeriodNanos = (long) (1000000000.0 / fps);
一般的屏幕刷新率每秒60次,fps =16.7

  • 2.vsnyc_waiter_android.cc VsyncWaiterAndroid:: ConsumePendingCallback
// static
void VsyncWaiterAndroid::ConsumePendingCallback(
    jlong java_baton,
    fml::TimePoint frame_start_time,
    fml::TimePoint frame_target_time) {
  auto* weak_this = reinterpret_cast<std::weak_ptr<VsyncWaiter>*>(java_baton);
  auto shared_this = weak_this->lock();
  delete weak_this;

  if (shared_this) {
    shared_this->FireCallback(frame_start_time, frame_target_time);
  }
}
  • 3.vsync_waiter.cc VsyncWaiter:: FireCallback
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_);
    secondary_callback = std::move(secondary_callback_);
  }
  if (!callback && !secondary_callback) {
   //判断之前设置的callback是否被设置为null了,若设置为null了也就没必要回调了
    TRACE_EVENT_INSTANT0("flutter", "MismatchedFrameCallback");
    return;
  }
  if (callback) {
    auto flow_identifier = fml::tracing::TraceNonce();
    TRACE_EVENT0("flutter", "VsyncFireCallback");
    TRACE_FLOW_BEGIN("flutter", kVsyncFlowName, flow_identifier);
    //向UI线程中post VSYNC的信号回调
    task_runners_.GetUITaskRunner()->PostTaskForTime(
        [callback, flow_identifier, frame_start_time, frame_target_time]() {
          FML_TRACE_EVENT("flutter", kVsyncTraceName, "StartTime",
                          frame_start_time, "TargetTime", frame_target_time);
          fml::tracing::TraceEventAsyncComplete(
              "flutter", "VsyncSchedulingOverhead", fml::TimePoint::Now(),
              frame_start_time);
          //执行callback
          callback(frame_start_time, frame_target_time);
          TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier);
        },
        frame_start_time);
  }

  if (secondary_callback) {
    task_runners_.GetUITaskRunner()->PostTaskForTime(
        std::move(secondary_callback), frame_start_time);
  }
}

这里的回调信号会post到UI线程中执行,同时UI线程会在MessageLoop::RunExpiredTasks将消息取出来执行。

  • 4.message_loop.cc MessageLoop::RunExpiredTasksNow
void MessageLoop::RunExpiredTasksNow() {
  loop_->RunExpiredTasksNow();
}
  • 5.message_loop_impl.cc
void MessageLoopImpl::RunExpiredTasksNow() {
  FlushTasks(FlushType::kAll);
}

void MessageLoopImpl::FlushTasks(FlushType type) {
  TRACE_EVENT0("fml", "MessageLoop::FlushTasks");
  std::vector<fml::closure> invocations;
  //取出task中的消息
  task_queue_->GetTasksToRunNow(queue_id_, type, invocations);
  //执行取出的消息
  for (const auto& invocation : invocations) {
    invocation();
    std::vector<fml::closure> observers =
        task_queue_->GetObserversToNotify(queue_id_);
    for (const auto& observer : observers) {
      observer();
    }
  }
}

从task_queue 中取出对应的消息,并执行invocation,此方法将真正执行对应的callback( AsyncWaitForVsync中的callback)

  • 6.message_loop_tast_queues.cc MessageLoopTaskQueues::GetTasksToRunNow
void MessageLoopTaskQueues::GetTasksToRunNow(
    TaskQueueId queue_id,
    FlushType type,
    std::vector<fml::closure>& invocations) {
  std::lock_guard guard(queue_mutex_);
  //是否有挂起的task
  if (!HasPendingTasksUnlocked(queue_id)) {
    return;
  }

  const auto now = fml::TimePoint::Now();
  //判断是否有挂起的任务,有取出来并判断时间是否到了,到了立马执行
  while (HasPendingTasksUnlocked(queue_id)) {
    TaskQueueId top_queue = _kUnmerged;
    const auto& top = PeekNextTaskUnlocked(queue_id, top_queue);
    //判断队列的头时间是否可以执行,不可以执行break
    if (top.GetTargetTime() > now) {
      break;
    }
    //将挂起的放到列表里面
    invocations.emplace_back(top.GetTask());
    queue_entries_.at(top_queue)->delayed_tasks.pop();
    if (type == FlushType::kSingle) {
      break;
    }
  }

  
  if (!HasPendingTasksUnlocked(queue_id)) {
    //若没有挂起的task睡眠当前loop,直到下一次message的到来
    WakeUpUnlocked(queue_id, fml::TimePoint::Max());
  } else {
    //若有挂起的task,执行并设置下次唤醒的时间
    WakeUpUnlocked(queue_id, GetNextWakeTimeUnlocked(queue_id));
  }
}

//为当前队列设置wakeup的唤醒时间
void MessageLoopTaskQueues::WakeUpUnlocked(TaskQueueId queue_id,
                                           fml::TimePoint time) const {
  if (queue_entries_.at(queue_id)->wakeable) {
    queue_entries_.at(queue_id)->wakeable->WakeUp(time);
  }
}

此方法会从消息队列中循环取出队列的消息
1.若没有挂起的task消息,那么将当前的loop进行睡眠直到新的消息到来唤醒(time = fml::TimePoint::Max())
2.若有挂起的task消息,但是时间没有到top.GetTargetTime() > now,那么将当前的loop进行睡眠到message执行的时间
3.若有挂起的task且时间是top.GetTargetTime() <= now,那么将执行此消息。然后将loop睡眠到下个message执行的时间

  • 7.animator.cc Animator::AwaitVSync callback的位置
void Animator::AwaitVSync() {
  waiter_->AsyncWaitForVsync(
      [self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
                                          fml::TimePoint frame_target_time) {
        if (self) {
          if (self->CanReuseLastLayerTree()) {
            self->DrawLastLayerTree();
          } else {
            self->BeginFrame(frame_start_time, frame_target_time);
          }
        }
      });

  delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}
  • 8.animator.cc#DrawLastLayerTree
///复用上一次的图层库
void Animator::DrawLastLayerTree() {
   //释放信号量,vsync信号可再次注册
  pending_frame_semaphore_.Signal();
  delegate_.OnAnimatorDrawLastLayerTree();
}

注意:delgate此处表示的是shell.cc:shell同时继承了PlatformView::Delegate,Animator::Delegate,Engine::Delegate,所以在Engine,Animator,PlatformView中的成员变量delegate_都是指Shell对象。

    1. shell.cc#OnAnimatorDrawLastLayerTree
void Shell::OnAnimatorDrawLastLayerTree() {
  FML_DCHECK(is_setup_);
//将任务交给Raster线程(draw过程并为GPU下发指令的线程)处理
  task_runners_.GetRasterTaskRunner()->PostTask(
      [rasterizer = rasterizer_->GetWeakPtr()]() {
        if (rasterizer) {
          rasterizer->DrawLastLayerTree();
        }
      });
}

这里的Raster线程是对LayerTree进行draw操作并将要draw内容下发给GPU进行渲染。Raster的处理过程后边文章介绍,本篇不进行介绍。

  • 10.animator.cc Animator::BeginFrame
void Animator::BeginFrame(fml::TimePoint frame_start_time,
                          fml::TimePoint frame_target_time) {
  TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);

  TRACE_EVENT0("flutter", "Animator::BeginFrame");
  while (!trace_flow_ids_.empty()) {
    uint64_t trace_flow_id = trace_flow_ids_.front();
    TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
    trace_flow_ids_.pop_front();
  }

  frame_scheduled_ = false;
  notify_idle_task_id_++;
  regenerate_layer_tree_ = false;
//非负信号量,容量1,每次获取会减1,释放加1。控制vysnc的注册`pending_frame_semaphore_(1),`
//此处+1
  pending_frame_semaphore_.Signal();

  if (!producer_continuation_) {
    //若之前的layertree并没有执行Animation :: Render,此次直接启用便可
    //layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)) 这里的管道是最大深度是2,即最大的执行laytree为2,每次UI线程执行poduce会减一,Raster线程执行Comsume之后会执行一次加一操作。
    producer_continuation_ = layer_tree_pipeline_->Produce();
    if (!producer_continuation_) {
    //pipeline已满,说明GPU线程繁忙,则结束本次UI绘制,重新注册Vsync
      RequestFrame();
      return;
    }
  }

  FML_DCHECK(producer_continuation_);

//从pipeline中获取有效的continuation,并准备为可能的frame服务
  last_frame_begin_time_ = frame_start_time;
//获取当前帧绘制截止时间,用于告知可GC的空闲时长
  last_frame_target_time_ = frame_target_time;
  dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
  {
    TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
                 FrameParity());
    //执行绘制
    delegate_.OnAnimatorBeginFrame(frame_target_time);
  }

  if (!frame_scheduled_) {
     //执行完绘制之后需要通知UI,闲置
    task_runners_.GetUITaskRunner()->PostDelayedTask(
        [self = weak_factory_.GetWeakPtr(),
         notify_idle_task_id = notify_idle_task_id_]() {
          if (!self.get()) {
            return;
          }
        //如果我们(此任务的)任务ID与当前任务ID相同(表示发布此任务的| BeginFrame |调用没有后续框架)并且当前没有可处理的frame,通知引擎当前处于空闲状态 100ms
          if (notify_idle_task_id == self->notify_idle_task_id_ &&
              !self->frame_scheduled_) {
            TRACE_EVENT0("flutter", "BeginFrame idle callback");
            self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
                                                 100000);
          }
        },
      //等待51毫秒(比60hz的3帧多1毫秒)
        kNotifyIdleTaskWaitTime);
  }
}

此处kNotifyIdleTaskWaitTime等于51ms,等于3帧的时间+1ms,之所以这样设计是由于在某些工作负载下(比如父视图调整大小,通过viewport metrics事件传达给子视图)实际上还没有schedule帧,尽管在下一个vsync会生成一帧(将在收到viewport事件后schedule),因此推迟调用OnAnimatorNotifyIdle一点点,从而避免可能垃圾回收在不希望的时间触发。

  • 11.shell.cc Shell::OnAnimatorNotifyIdle 以下都是一系列调用过程
// |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_);
    latest_frame_target_time_.emplace(frame_target_time);
  }
  if (engine_) {
    engine_->BeginFrame(frame_target_time);
  }
}
  • 12.engine.cc Engine::BeginFrame
void Engine::BeginFrame(fml::TimePoint frame_time) {
  TRACE_EVENT0("flutter", "Engine::BeginFrame");
  runtime_controller_->BeginFrame(frame_time);
}
  • 13.runtim_controller.cc RuntimeController::BeginFrame
bool RuntimeController::BeginFrame(fml::TimePoint frame_time) {
  if (auto* window = GetWindowIfAvailable()) {
    window->BeginFrame(frame_time);
    return true;
  }
  return false;
}
  • 14.window.cc Window::BeginFrame
void Window::BeginFrame(fml::TimePoint frameTime) {
  std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
  if (!dart_state)
    return;
  tonic::DartState::Scope scope(dart_state);

  int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds();

  tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame",
                                           {
                                               Dart_NewInteger(microseconds),
                                           }));

  UIDartState::Current()->FlushMicrotasksNow();

  tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {}));
}

之前交代过window.cc 中的方法跟window.dart是对应的。
BeginFrame 方法此时执行了
1._beginFrame 执行到了window.dart的onBeginFrame
2.FlushMicrotasksNow 这里便是我们平常使用的scheduleMicrotask异步事件,这里进行了执行,所以我们的Microtask本身耗时的话会阻塞我们的UI线程
3._drawFrame 执行到了window.dart的onDrawFrame

从hooks.dart 中可以知道

@pragma('vm:entry-point')
// ignore: unused_element
void _beginFrame(int microseconds) {
  _invoke1<Duration>(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds));
}

@pragma('vm:entry-point')
// ignore: unused_element
void _drawFrame() {
  _invoke(window.onDrawFrame, window._onDrawFrameZone);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335