FlutterUI 呼叫系統渲染引擎-13

AidenCang 發表於 2019-12-04

Flutter是Google開發的一套全新的跨平臺、開源UI框架,支援iOS、Android系統開發,並且是未來新作業系統Fuchsia的預設開發套件。自從2017年5月釋出第一個版本以來,目前Flutter已經發布了近60個版本,並且在2018年5月釋出了第一個“Ready for Production Apps”的Beta 3版本,6月20日釋出了第一個“Release Preview”版本。Flutter的目標是使同一套程式碼同時執行在Android和iOS系統上,並且擁有媲美原生應用的效能,Flutter甚至提供了兩套控制元件來適配Android和iOS(滾動效果、字型和控制元件圖示等等)為了讓App在細節處看起來更像原生應用。

FlutterEngine是一個跨平臺的UI框架,可以在不同的平臺上執行同一套程式碼,整個渲染過程如下圖所示,下面接著分析整個渲染過程

FlutterUI 呼叫系統渲染引擎-13

RuntimeController&WindowClient&Window關係

RuntimeController 作為FlutterEngine引擎初始化Dart相關的環境的分析在開一篇來說明,這篇文章還是在說明整個FlutterEngine和FlutterUI和平臺之間的一個互動框架,整體的框架理解了之後,我們在來對具體的每一個知識點進行分析,同時修改原始碼邏輯。詳細的資訊已經在FlutterEngine啟動過程中進行分析。

RuntimeController

在前面的文字中,介紹了FlutterEngine初始化過程,FlutterEngine的初始化時,建立了RuntimeController做為Engine的代理物件,負責處理FlutterUI層和底層通訊的介面,作為FlutterEngine的通訊和排程中心 engine/src/flutter/runtime/runtime_controller.cc

Engine::Engine(Delegate& delegate,
               blink::DartVM& vm,
               fml::RefPtr<blink::DartSnapshot> isolate_snapshot,
               fml::RefPtr<blink::DartSnapshot> shared_snapshot,
               blink::TaskRunners task_runners,
               blink::Settings settings,
               std::unique_ptr<Animator> animator,
               fml::WeakPtr<blink::SnapshotDelegate> snapshot_delegate,
               fml::WeakPtr<blink::IOManager> io_manager)
    : delegate_(delegate),
      settings_(std::move(settings)),
      animator_(std::move(animator)),
      activity_running_(false),
      have_surface_(false),
      weak_factory_(this) {
  // Runtime controller is initialized here because it takes a reference to this
  // object as its delegate. The delegate may be called in the constructor and
  // we want to be fully initilazed by that point.
  runtime_controller_ = std::make_unique<blink::RuntimeController>(
      *this,                                 // runtime delegate
      &vm,                                   // VM
      std::move(isolate_snapshot),           // isolate snapshot
      std::move(shared_snapshot),            // shared snapshot
      std::move(task_runners),               // task runners
      std::move(snapshot_delegate),          // snapshot delegate
      std::move(io_manager),                 // io manager
      settings_.advisory_script_uri,         // advisory script uri
      settings_.advisory_script_entrypoint,  // advisory script entrypoint
      settings_.idle_notification_callback   // idle notification callback
  );
}
複製程式碼

RuntimeController初始化時,作為flutter/runtime/runtime_delegate.cc代理物件,同時持有DartVM物件,IOManager管理上下文物件,最終建立Dart執行的DartIsolate物件,isolate是Dart對actor併發模式的實現。執行中的Dart程式由一個或多個actor組成,這些actor也就是Dart概念裡面的isolate。isolate是有自己的記憶體和單執行緒控制的執行實體。isolate本身的意思是“隔離”,因為isolate之間的記憶體在邏輯上是隔離的。isolate中的程式碼是按順序執行的,任何Dart程式的併發都是執行多個isolate的結果。因為Dart沒有共享記憶體的併發,沒有競爭的可能性所以不需要鎖,也就不用擔心死鎖的問題。

flutterisolate.png

FlutterUI 呼叫系統渲染引擎-13

isolate之間的通訊

由於isolate之間沒有共享記憶體,所以他們之間的通訊唯一方式只能是通過Port進行,而且Dart中的訊息傳遞總是非同步的。

isolate與普通執行緒的區別

我們可以看到isolate神似Thread,但實際上兩者有本質的區別。作業系統內內的執行緒之間是可以有共享記憶體的而isolate沒有,這是最為關鍵的區別。

isolate實現簡述 我們可以閱讀Dart原始碼裡面的isolate.cc檔案看看isolate的具體實現。 我們可以看到在isolate建立的時候有以下幾個主要步驟:

初始化isolate資料結構 初始化堆記憶體(Heap) 進入新建立的isolate,使用跟isolate一對一的執行緒執行isolate 配置Port 配置訊息處理機制(Message Handler) 配置Debugger,如果有必要的話 將isolate註冊到全域性監控器(Monitor)

Flutter Engine Runners與Dart Isolate

有朋友看到這裡可能會問既然Flutter Engine有自己的Runner,那為何還要Dart的Isolate呢,他們之間又是什麼關係呢?

那我們還要從Runner具體的實現說起,Runner是一個抽象概念,我們可以往Runner裡面提交任務,任務被Runner放到它所在的執行緒去執行,這跟iOS GCD的執行佇列很像。我們檢視iOS Runner的實現實際上裡面是一個loop,這個loop就是CFRunloop,在iOS平臺上Runner具體實現就是CFRunloop。被提交的任務被放到CFRunloop去執行。

Dart的Isolate是Dart虛擬機器自己管理的,Flutter Engine無法直接訪問。Root Isolate通過Dart的C++呼叫能力把UI渲染相關的任務提交到UI Runner執行這樣就可以跟Flutter Engine相關模組進行互動,Flutter UI相關的任務也被提交到UI Runner也可以相應的給Isolate一些事件通知,UI Runner同時也處理來自App方面Native Plugin的任務。

所以簡單來說Dart isolate跟Flutter Runner是相互獨立的,他們通過任務排程機制相互協作。

RuntimeController初始化

1.建立DartIsolate物件 2.獲取Window物件 3.載入dart:ui程式碼到虛擬機器中DidCreateIsolate 4.初始化FlutterUI層的引數FlushRuntimeStateToIsolate


RuntimeController::RuntimeController(
    RuntimeDelegate& p_client,
    DartVM* p_vm,
    fml::RefPtr<DartSnapshot> p_isolate_snapshot,
    fml::RefPtr<DartSnapshot> p_shared_snapshot,
    TaskRunners p_task_runners,
    fml::WeakPtr<SnapshotDelegate> p_snapshot_delegate,
    fml::WeakPtr<IOManager> p_io_manager,
    std::string p_advisory_script_uri,
    std::string p_advisory_script_entrypoint,
    std::function<void(int64_t)> idle_notification_callback,
    WindowData p_window_data)
    : client_(p_client),
      vm_(p_vm),
      isolate_snapshot_(std::move(p_isolate_snapshot)),
      shared_snapshot_(std::move(p_shared_snapshot)),
      task_runners_(p_task_runners),
      snapshot_delegate_(p_snapshot_delegate),
      io_manager_(p_io_manager),
      advisory_script_uri_(p_advisory_script_uri),
      advisory_script_entrypoint_(p_advisory_script_entrypoint),
      idle_notification_callback_(idle_notification_callback),
      window_data_(std::move(p_window_data)),
      root_isolate_(
          DartIsolate::CreateRootIsolate(vm_,
                                         isolate_snapshot_,
                                         shared_snapshot_,
                                         task_runners_,
                                         std::make_unique<Window>(this),
                                         snapshot_delegate_,
                                         io_manager_,
                                         p_advisory_script_uri,
                                         p_advisory_script_entrypoint)) {
  std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
  root_isolate->SetReturnCodeCallback([this](uint32_t code) {
    root_isolate_return_code_ = {true, code};
  });
  if (auto* window = GetWindowIfAvailable()) {
    tonic::DartState::Scope scope(root_isolate);
    ///ISOlate建立完成
    window->DidCreateIsolate();
    if (!FlushRuntimeStateToIsolate()) {
      FML_DLOG(ERROR) << "Could not setup intial isolate state.";
    }
  } else {
    FML_DCHECK(false) << "RuntimeController created without window binding.";
  }
  FML_DCHECK(Dart_CurrentIsolate() == nullptr);
}
複製程式碼

這裡需要一篇檔案繼續展開來講解,我們現在的目的是搞清楚整個邏輯呼叫過程的主幹,在來了解整個DartIsolate建立過程

參考:

engine/src/flutter/lib/ui/ui_dart_state.cc engine/src/flutter/runtime/dart_isolate.cc

WindowClient

RuntimeController 實現了WindowClient物件engine/src/flutter/lib/ui/window/window.h,

class WindowClient {
 public:
  virtual std::string DefaultRouteName() = 0;
  virtual void ScheduleFrame() = 0;
  virtual void Render(Scene* scene) = 0;
  virtual void UpdateSemantics(SemanticsUpdate* update) = 0;
  virtual void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) = 0;
  virtual FontCollection& GetFontCollection() = 0;
  virtual void UpdateIsolateDescription(const std::string isolate_name,
                                        int64_t isolate_port) = 0;

 protected:
  virtual ~WindowClient();
};
複製程式碼

Window::RegisterNatives

flutterUI層的ui.window.dart和FlutterEngine層的Window.cc建立管理關係,通過RuntimeController

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},
  });
}
複製程式碼

小結

上面的介紹主要是理清楚FlutterEngine初始化在建立FlutterEngine和FlutterUI層的一個呼叫關係,在FlutterUI層回撥FlutterEngine時,會呼叫window.dart的本地方法,從而,呼叫FlutterEngine的Window.cc類,RuntimeController作為Engine的代理物件,實現了WindowClient類,Window物件中次有WindowClient物件的引用,也就是持有RuntimeController引用物件,從而和Engine能夠進行相互呼叫,言歸正傳,接下分析一下FlutterUI層在構建好一幀之後,是怎麼新增到FlutterEngine中進行顯示的。

Android端是怎麼把SurfaceView註冊到FlutterEngine中的

Android端在初始化是自定義SurfaceView作為彙總Flutter的View,在載入libflutter.so庫是新增到FlutterEngine中,具體呼叫步驟參考下面的程式碼,前面的檔案已經分析過來FlutterEngine啟動過程中SurfaceView註冊到FlutterEngine引擎的過程

engine/src/flutter/shell/platform/android/library_loader.cc

1.JNI_OnLoad註冊本地方法

2.PlatformViewAndroid::Register(env);註冊本地方法

3.SurfaceCreated通過本地方法呼叫和AndroidNative層的View進行關聯

4.PlatformViewAndroid中中進行引用

// This is called by the VM when the shared library is first loaded.
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  // Initialize the Java VM.
  fml::jni::InitJavaVM(vm);

  JNIEnv* env = fml::jni::AttachCurrentThread();
  bool result = false;

  // Register FlutterMain.
  result = shell::FlutterMain::Register(env);
  FML_CHECK(result);

  // Register PlatformView
  result = shell::PlatformViewAndroid::Register(env);
  FML_CHECK(result);

  // Register VSyncWaiter.
  result = shell::VsyncWaiterAndroid::Register(env);
  FML_CHECK(result);

  return JNI_VERSION_1_4;
}

複製程式碼

AndroidNativeWindow

static void SurfaceCreated(JNIEnv* env,
                           jobject jcaller,
                           jlong shell_holder,
                           jobject jsurface) {
  // Note: This frame ensures that any local references used by
  // ANativeWindow_fromSurface are released immediately. This is needed as a
  // workaround for https://code.google.com/p/android/issues/detail?id=68174
  fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);
  auto window = fml::MakeRefCounted<AndroidNativeWindow>(
      ANativeWindow_fromSurface(env, jsurface));
  ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window));
}
複製程式碼

PlatformViewAndroid

void PlatformViewAndroid::NotifyCreated(
    fml::RefPtr<AndroidNativeWindow> native_window) {
  if (android_surface_) {
    InstallFirstFrameCallback();
    android_surface_->SetNativeWindow(native_window);
  }
  PlatformView::NotifyCreated();
}
複製程式碼

AndroidSurfaceSoftware

bool AndroidSurfaceSoftware::SetNativeWindow(
    fml::RefPtr<AndroidNativeWindow> window) {
  native_window_ = std::move(window);
  if (!(native_window_ && native_window_->IsValid()))
    return false;
  int32_t window_format = ANativeWindow_getFormat(native_window_->handle());
  if (window_format < 0)
    return false;
  if (!GetSkColorType(window_format, &target_color_type_, &target_alpha_type_))
    return false;
  return true;
}
複製程式碼

呼叫FlutterEngine進行初始化

Window:render 方法

在上一篇中分析了FlutterUI的初始化過程,FlutterUI初始化完成之後,就把構建好的Scene傳遞到FlutterEngine層進行渲染,呼叫ui.window的本地方法,void render(Scene scene) native 'Window_render';上一個部分在FlutterEngine中註冊的本地方法,

/// Uploads the composited layer tree to the engine.
///
/// Actually causes the output of the rendering pipeline to appear on screen.
void compositeFrame() {
  Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
  try {
    final ui.SceneBuilder builder = ui.SceneBuilder();
    final ui.Scene scene = layer.buildScene(builder);
    if (automaticSystemUiAdjustment)
      _updateSystemChrome();
    _window.render(scene);
    scene.dispose();
    assert(() {
      if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
        debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
      return true;
    }());
  } finally {
    Timeline.finishSync();
  }
}
複製程式碼

Window:render

通過Window物件對呼叫Engine的方法

engine/src/flutter/lib/ui/window/window.cc中把資料傳遞個sky引擎進行渲染

void Render(Dart_NativeArguments args) {
  Dart_Handle exception = nullptr;
  Scene* scene =
      tonic::DartConverter<Scene*>::FromArguments(args, 1, exception);
  if (exception) {
    Dart_ThrowException(exception);
    return;
  }
  UIDartState::Current()->window()->client()->Render(scene);
}

複製程式碼

RuntimeController::Render

整個呼叫邏輯都很接單

在Engine代理物件RuntimeController::Render方法中,獲取Scene的Layer資料,並且呼叫Engine:render方法,進入FlutterEngine核心處理邏輯中

void RuntimeController::Render(Scene* scene) {
  client_.Render(scene->takeLayerTree());
}
複製程式碼

Engine::Render

void Engine::Render(std::unique_ptr<flow::LayerTree> layer_tree) {
  if (!layer_tree)
    return;

  SkISize frame_size = SkISize::Make(viewport_metrics_.physical_width,
                                     viewport_metrics_.physical_height);
  if (frame_size.isEmpty())
    return;

  layer_tree->set_frame_size(frame_size);
  animator_->Render(std::move(layer_tree));
}
複製程式碼

Animator::Render

void Animator::Render(std::unique_ptr<flow::LayerTree> layer_tree) {
  if (dimension_change_pending_ &&
      layer_tree->frame_size() != last_layer_tree_size_) {
    dimension_change_pending_ = false;
  }
  last_layer_tree_size_ = layer_tree->frame_size();

  if (layer_tree) {
    // Note the frame time for instrumentation.
    layer_tree->set_construction_time(fml::TimePoint::Now() -
                                      last_begin_frame_time_);
  }

  // Commit the pending continuation.
  producer_continuation_.Complete(std::move(layer_tree));

  delegate_.OnAnimatorDraw(layer_tree_pipeline_);
}
複製程式碼

Shell::OnAnimatorDraw

呼叫平臺端的持久化物件進行柵格化rasterizer

// |shell::Animator::Delegate|
void Shell::OnAnimatorDraw(
    fml::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
  FML_DCHECK(is_setup_);

  task_runners_.GetGPUTaskRunner()->PostTask(
      [rasterizer = rasterizer_->GetWeakPtr(),
       pipeline = std::move(pipeline)]() {
        if (rasterizer) {
          rasterizer->Draw(pipeline);
        }
      });
}

複製程式碼

Rasterizer::Draw

void Rasterizer::Draw(
    fml::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
  TRACE_EVENT0("flutter", "GPURasterizer::Draw");

  flutter::Pipeline<flow::LayerTree>::Consumer consumer =
      std::bind(&Rasterizer::DoDraw, this, std::placeholders::_1);

  // Consume as many pipeline items as possible. But yield the event loop
  // between successive tries.
  switch (pipeline->Consume(consumer)) {
    case flutter::PipelineConsumeResult::MoreAvailable: {
      task_runners_.GetGPUTaskRunner()->PostTask(
          [weak_this = weak_factory_.GetWeakPtr(), pipeline]() {
            if (weak_this) {
              weak_this->Draw(pipeline);
            }
          });
      break;
    }
    default:
      break;
  }
}
複製程式碼

上面的過程中已經把FltuterUI層生成的資料新增到了Skia中,接下來時要把Skia已經渲染好的資料,新增到SurfaceView上進行渲染

ui.Window.dart

void scheduleFrame() native 'Window_scheduleFrame';呼叫通知FlutterEngine已經準備好了一幀,可以渲染到Android提供的SurfaceView上

engine/src/flutter/lib/ui/window/window.cc作為FlutterEngine的介面呼叫,整個呼叫過程很簡單,中間省掉了部分呼叫過程,可以從window.cc開始追蹤呼叫過程

Animator::RequestFrame

void Animator::RequestFrame(bool regenerate_layer_tree) {
  if (regenerate_layer_tree) {
    regenerate_layer_tree_ = true;
  }
  if (paused_ && !dimension_change_pending_) {
    return;
  }

  if (!pending_frame_semaphore_.TryWait()) {
    // Multiple calls to Animator::RequestFrame will still result in a
    // single request to the VsyncWaiter.
    return;
  }

  // The AwaitVSync is going to call us back at the next VSync. However, we want
  // to be reasonably certain that the UI thread is not in the middle of a
  // particularly expensive callout. We post the AwaitVSync to run right after
  // an idle. This does NOT provide a guarantee that the UI thread has not
  // started an expensive operation right after posting this message however.
  // To support that, we need edge triggered wakes on 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);
    self->AwaitVSync();
  });
  frame_scheduled_ = true;
}
複製程式碼

Rasterizer::DrawToSurface

在這裡正在的合成一幀,張上一個步驟中已經把相關的資料傳遞到AndroidNative層

bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) {
  FML_DCHECK(surface_);

  auto frame = surface_->AcquireFrame(layer_tree.frame_size());

  if (frame == nullptr) {
    return false;
  }

  // There is no way for the compositor to know how long the layer tree
  // construction took. Fortunately, the layer tree does. Grab that time
  // for instrumentation.
  compositor_context_->engine_time().SetLapTime(layer_tree.construction_time());

  auto* canvas = frame->SkiaCanvas();

  auto* external_view_embedder = surface_->GetExternalViewEmbedder();

  if (external_view_embedder != nullptr) {
    external_view_embedder->BeginFrame(layer_tree.frame_size());
  }

  auto compositor_frame = compositor_context_->AcquireFrame(
      surface_->GetContext(), canvas, external_view_embedder,
      surface_->GetRootTransformation(), true);

  if (compositor_frame && compositor_frame->Raster(layer_tree, false)) {
    frame->Submit();
    if (external_view_embedder != nullptr) {
      external_view_embedder->SubmitFrame(surface_->GetContext());
    }
    FireNextFrameCallbackIfPresent();

    if (surface_->GetContext())
      surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);

    return true;
  }

  return false;
}
複製程式碼

小結

從上面的呼叫過程來看這個呼叫過程並不複雜 1.FlutterUI層把渲染好的資料通過Window.cc的Render方法呼叫到Shell上繼續持久化

2.將這邊好的資料通過Vsync通過系統繪製到螢幕上

整個FlutterEngine對FlutterUI彙總到螢幕上的操作控制還是比較少,整個流程呼叫也非常單一