flutter UI渲染源碼解析之Engin繪制過程(二)

engin繪制過程

上文中我們講到了VSYNC的注冊峡捡,注冊之后會(huì)等待vsync信號(hào)回調(diào)doframe,然后回調(diào)到了FlutterJNI.nativeOnVsync(frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);,因?yàn)镴NILoad的時(shí)候?qū)?code>FlutterJNI#nativeOnVsync跟VsyncWaiterAndroid#OnNativeVsync做了綁定嗡载,此時(shí)會(huì)被回調(diào)到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+屏幕刷新時(shí)間(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) {
   //判斷之前設(shè)置的callback是否被設(shè)置為null了,若設(shè)置為null了也就沒必要回調(diào)了
    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的信號(hào)回調(diào)
    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);
          //執(zhí)行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);
  }
}

這里的回調(diào)信號(hào)會(huì)post到UI線程中執(zhí)行,同時(shí)UI線程會(huì)在MessageLoop::RunExpiredTasks將消息取出來執(zhí)行。

  • 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);
  //執(zhí)行取出的消息
  for (const auto& invocation : invocations) {
    invocation();
    std::vector<fml::closure> observers =
        task_queue_->GetObserversToNotify(queue_id_);
    for (const auto& observer : observers) {
      observer();
    }
  }
}

從task_queue 中取出對應(yīng)的消息,并執(zhí)行invocation炼邀,此方法將真正執(zhí)行對應(yīng)的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();
  //判斷是否有掛起的任務(wù),有取出來并判斷時(shí)間是否到了吃沪,到了立馬執(zhí)行
  while (HasPendingTasksUnlocked(queue_id)) {
    TaskQueueId top_queue = _kUnmerged;
    const auto& top = PeekNextTaskUnlocked(queue_id, top_queue);
    //判斷隊(duì)列的頭時(shí)間是否可以執(zhí)行汤善,不可以執(zhí)行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睡眠當(dāng)前l(fā)oop,直到下一次message的到來
    WakeUpUnlocked(queue_id, fml::TimePoint::Max());
  } else {
    //若有掛起的task票彪,執(zhí)行并設(shè)置下次喚醒的時(shí)間
    WakeUpUnlocked(queue_id, GetNextWakeTimeUnlocked(queue_id));
  }
}

//為當(dāng)前隊(duì)列設(shè)置wakeup的喚醒時(shí)間
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);
  }
}

此方法會(huì)從消息隊(duì)列中循環(huán)取出隊(duì)列的消息
1.若沒有掛起的task消息红淡,那么將當(dāng)前的loop進(jìn)行睡眠直到新的消息到來喚醒(time = fml::TimePoint::Max())
2.若有掛起的task消息,但是時(shí)間沒有到top.GetTargetTime() > now,那么將當(dāng)前的loop進(jìn)行睡眠到message執(zhí)行的時(shí)間
3.若有掛起的task且時(shí)間是top.GetTargetTime() <= now,那么將執(zhí)行此消息降铸。然后將loop睡眠到下個(gè)message執(zhí)行的時(shí)間

  • 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
///復(fù)用上一次的圖層庫
void Animator::DrawLastLayerTree() {
   //釋放信號(hào)量在旱,vsync信號(hào)可再次注冊
  pending_frame_semaphore_.Signal();
  delegate_.OnAnimatorDrawLastLayerTree();
}

注意:delgate此處表示的是shell.cc:shell同時(shí)繼承了PlatformView::Delegate,Animator::Delegate推掸,Engine::Delegate桶蝎,所以在Engine驻仅,Animator,PlatformView中的成員變量delegate_都是指Shell對象登渣。

    1. shell.cc#OnAnimatorDrawLastLayerTree
void Shell::OnAnimatorDrawLastLayerTree() {
  FML_DCHECK(is_setup_);
//將任務(wù)交給Raster線程(draw過程并為GPU下發(fā)指令的線程)處理
  task_runners_.GetRasterTaskRunner()->PostTask(
      [rasterizer = rasterizer_->GetWeakPtr()]() {
        if (rasterizer) {
          rasterizer->DrawLastLayerTree();
        }
      });
}

這里的Raster線程是對LayerTree進(jìn)行draw操作并將要draw內(nèi)容下發(fā)給GPU進(jìn)行渲染噪服。Raster的處理過程后邊文章介紹,本篇不進(jìn)行介紹胜茧。

  • 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;
//非負(fù)信號(hào)量粘优,容量1,每次獲取會(huì)減1呻顽,釋放加1雹顺。控制vysnc的注冊`pending_frame_semaphore_(1),`
//此處+1
  pending_frame_semaphore_.Signal();

  if (!producer_continuation_) {
    //若之前的layertree并沒有執(zhí)行Animation :: Render廊遍,此次直接啟用便可
    //layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)) 這里的管道是最大深度是2嬉愧,即最大的執(zhí)行l(wèi)aytree為2,每次UI線程執(zhí)行poduce會(huì)減一喉前,Raster線程執(zhí)行Comsume之后會(huì)執(zhí)行一次加一操作没酣。
    producer_continuation_ = layer_tree_pipeline_->Produce();
    if (!producer_continuation_) {
    //pipeline已滿,說明GPU線程繁忙被饿,則結(jié)束本次UI繪制四康,重新注冊Vsync
      RequestFrame();
      return;
    }
  }

  FML_DCHECK(producer_continuation_);

//從pipeline中獲取有效的continuation,并準(zhǔn)備為可能的frame服務(wù)
  last_frame_begin_time_ = frame_start_time;
//獲取當(dāng)前幀繪制截止時(shí)間狭握,用于告知可GC的空閑時(shí)長
  last_frame_target_time_ = frame_target_time;
  dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
  {
    TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
                 FrameParity());
    //執(zhí)行繪制
    delegate_.OnAnimatorBeginFrame(frame_target_time);
  }

  if (!frame_scheduled_) {
     //執(zhí)行完繪制之后需要通知UI,閑置
    task_runners_.GetUITaskRunner()->PostDelayedTask(
        [self = weak_factory_.GetWeakPtr(),
         notify_idle_task_id = notify_idle_task_id_]() {
          if (!self.get()) {
            return;
          }
        //如果我們(此任務(wù)的)任務(wù)ID與當(dāng)前任務(wù)ID相同(表示發(fā)布此任務(wù)的| BeginFrame |調(diào)用沒有后續(xù)框架)并且當(dāng)前沒有可處理的frame疯溺,通知引擎當(dāng)前處于空閑狀態(tài) 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幀的時(shí)間+1ms,之所以這樣設(shè)計(jì)是由于在某些工作負(fù)載下(比如父視圖調(diào)整大小囱嫩,通過viewport metrics事件傳達(dá)給子視圖)實(shí)際上還沒有schedule幀恃疯,盡管在下一個(gè)vsync會(huì)生成一幀(將在收到viewport事件后schedule),因此推遲調(diào)用OnAnimatorNotifyIdle一點(diǎn)點(diǎn)墨闲,從而避免可能垃圾回收在不希望的時(shí)間觸發(fā)今妄。

  • 11.shell.cc Shell::OnAnimatorNotifyIdle 以下都是一系列調(diào)用過程
// |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是對應(yīng)的。
BeginFrame 方法此時(shí)執(zhí)行了
1._beginFrame 執(zhí)行到了window.dart的onBeginFrame
2.FlushMicrotasksNow 這里便是我們平常使用的scheduleMicrotask異步事件鸳碧,這里進(jìn)行了執(zhí)行盾鳞,所以我們的Microtask本身耗時(shí)的話會(huì)阻塞我們的UI線程
3._drawFrame 執(zhí)行到了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);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咐刨,一起剝皮案震驚了整個(gè)濱河市蒿褂,隨后出現(xiàn)的幾起案子愈捅,更是在濱河造成了極大的恐慌钧排,老刑警劉巖季眷,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兜辞,死亡現(xiàn)場離奇詭異父晶,居然都是意外死亡城豁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門验辞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稿黄,“玉大人,你說我怎么就攤上這事跌造「伺拢” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵鼻听,是天一觀的道長财著。 經(jīng)常有香客問我,道長撑碴,這世上最難降的妖魔是什么撑教? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮醉拓,結(jié)果婚禮上伟姐,老公的妹妹穿的比我還像新娘。我一直安慰自己亿卤,他們只是感情好愤兵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著排吴,像睡著了一般秆乳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钻哩,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天屹堰,我揣著相機(jī)與錄音,去河邊找鬼街氢。 笑死扯键,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的珊肃。 我是一名探鬼主播荣刑,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伦乔!你這毒婦竟也來了厉亏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤评矩,失蹤者是張志新(化名)和其女友劉穎叶堆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斥杜,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虱颗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年沥匈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忘渔。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡高帖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畦粮,到底是詐尸還是另有隱情散址,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布宣赔,位于F島的核電站预麸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏儒将。R本人自食惡果不足惜吏祸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钩蚊。 院中可真熱鬧贡翘,春花似錦、人聲如沸砰逻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝠咆。三九已至踊东,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刚操,已是汗流浹背递胧。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赡茸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓祝闻,卻偏偏與公主長得像占卧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子联喘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355