likes
comments
collection
share

Flutter Engine 的启动简析

作者站长头像
站长
· 阅读数 10

起因于自己想研究一下 game-engine,但是主流的 ue 什么的太过庞大了(庞大也是必然),于是想到了比较亲切的 Flutter Engine(其实也蛮复杂的),于是想梳理一下其 启动绘制 的大概流程。

开始

注意, Flutter Engine 也相当庞大,由于精力有限,只能梳理部分内容。

Flutter Engine - Github

在开始之前,列举一下相关的代码文件夹

  • fml (基础功能的封装,比如基于task 的线程,log,时钟,路径,哈希等 很底层很硬核的东西)

  • shell/common (直接引用 shell.h 注释的一句话:Perhaps the single most important class in the Flutter engine repository.)

  • shell/runtime (不同平台对 embedder engine 具体实现)

  • impeller (图形后端,也就是roadmap 提到的在重构东西,你会发现它的 commit 时间最近,还带了好多个⚠️)

启动

Flutter 有几个线程? 首先排除单线程,再排除经常看到的 5 线程模型(其实没啥问题)。 为什么皮这一下,因为实际情况可能比你想象的复杂的多,举两个例子:你在 Dart 开了一个 Compute Isolate,并且愉快的使用 fronted-server 提供的 hot-reload,这下情况就复杂了吧。

言归正传,我们从官方的 examples/vulkan_glfw 例子进行分析(我接触的时候当时只有一个 glfw-opengl 的例子,令人感概)

如何启动 Fluuter Engine ?

#include "embedder.h" // Flutter's Embedder ABI.
...
FlutterRendererConfig config = {};
FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, ...);
...

代码是简洁的,但 实现是复杂的!

这里提一句,启动 engine 还需要你的 dart_project_path(就你写dart 代码的那个目录) 和 icudtl.dat(flutter engine 编译后的产物,一年前我的 Ryzen 3600 要编译半小时,不知道现在如何)这两个参数,还有一个可选的 snapshot(提高重新 run & debug 的构建速度)

提一嘴,印象中应该是任何平台的 engine 启动函数定义,都是基于 embedder.h

接下来,让我们慢慢看看 FlutterEngineRun 函数做了什么 (实现在此 embedder.cc)

Run 很重要,作用就是调用了后面这两位重量级函数,可以说没有那两行代码的调用,Flutter 就无法运行,所以我们就不鸟他了。

Flutter Engine 的启动简析

FlutterEngineInitialize

这个函数,创建一堆我们需要的资源,但是很多都没有立即使用(启动交给 FlutterEngineRunIntialized

因为东西太多了,比如 commandline,aot 资源加载, 供 root_isolate, PlatformViewEmbedder ... 的 回调的各种函数创建等等,这里挑个大伙最想知道的:

真正flutter engine 创建

Flutter Engine 的启动简析

他的参数最有趣的 就属 thread_hosttask_runners

task_runners 基于任务的线程模型在此,感兴趣的可以去看 effective c++,这里你只需要可以下发任务给这个线程工作就行。

thread_host 我的建议是点进去看 那个很长的 create 函数,看了就明白了。

Flutter Engine 的启动简析

这个 thread_host 的创建走同文件内的静态函数 CreateEngineManagedThreadHostCreateEmbedderManagedThreadHost,做了什么事情呢?一张截图就够了。

Flutter Engine 的启动简析

FlutterEngineRunIntialized

数据都准备好了,那么现在就要开始 启动了!! 启动的步骤分为三步,具体看下方:

Flutter Engine 的启动简析

步骤2/3 具体实现还没看,不过基本可以确定自己的屎山 dart 代码 在最后一个 RunRiitIsolate 被跑起来了。 我们感兴趣的还是开头官方注释的那句话, 这个 Shell 可能就是 Flutter Engine 最重要的部分。

shell.cc

LaunchShell 也很重要,因为它调用了后面的 Create 函数,所以不看了。

Flutter Engine 的启动简析

  1. Shell::Create() 负责启动 DartVMisolate 如果提供了 snapshot 就从中启动。(我猜没有提供快照,应该会生成一份供下次使用)

  2. Shell::CreateWithSnapshot() 主要负责run 一下面这个CreateShellOnPlatformThread 作为他的 task

  3. Shell::CreateShellOnPlatformThread 就比较重量级了,同样也是创建资源, 比如 raster 线程需要的 Rasterizer 对象,还有诸如 io_manager, platformview 或者 ui-threadanimator 等等。这里可以看到很多 std 提供的 future 和 promise的东西,不知道是不是和 js|dart 那一套类似,总之就是异步操作,只能说亲切可爱了,

  4. Shell::Setup 我截个参数图,你也清楚了

Flutter Engine 的启动简析

小结

其实源代码比上面讲的多太多了, 虽说 连组件都写不明白的 还要操心 架构 的事情有点🤭,不过总该得研究一下。 感觉最有趣的就是🤔,基于 任务的发送给对应 线程的思维 和 从线程出发 编写任务的方式 有很大的不同。

绘制

绘制实际上是很复杂的,而且图形后端还在重构,详细的还得看 SkiaANGLE,shader 使用的语言还是自家的 sksl,还有各种 Delegate。再比如官方的 Vulkan Example 内,处理同步也是简单的 vkQueueWaitIdle

反正很复杂(懒得看),所以,我拎出连个比较抽象简单的类瞅瞅。

Shell::CreateShellOnPlatformThread 创建了这个对象,这里列出大家都熟悉的函数。

RasterStatus Draw(std::shared_ptr<LayerTreePipeline> pipeline, 
                      LayerTreeDiscardCallback discard_callback = NoDiscard);

void DrawLastLayerTree(std::unique_ptr<FrameTimingsRecorder>frame_timings_recorder);
          

对于 LayerTree 的内容,参阅 flutter/flow/layers/layer_tree.h, 有趣的是 Rasterizer 还有屏幕截图相关的代码,也是对 LayerTree 进行操作

  • Animator (UI 线程持有)

    平台层会有一个 vsync_waiter, Animator 通过传来的这个信号,开始他的逻辑。(我也不知道这个vsync具体是怎么实现的,是类似 G-Sync 那种显示设备通知 GPU 还是什么的,还有手机那种 LTPO 技术的底层原理,反正咱也是不懂)

    Animator 做了什么? 如果你了解 Flutter Dart端 的 ScheduleFrame,下面的代码你也猜出来什么意思了。通过某个 Delegate,搞个回调函数。接下来是猜想部分,反正是通过某一个 Dispatcher 把需要逻辑更新的指令下发给 Dart 端就对了。

void RequestFrame(bool regenerate_layer_tree = true);

void BeginFrame(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder);  

bool CanReuseLastLayerTree();    

void DrawLastLayerTree(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder);

如果你操刀过某些图形API的代码,一般会有一个render_loop,但是我没找到engine 的render_loop在哪,但是想了一想,其实感觉就是 example 里面的那个 glfwShouldCloseWindow,也就是你开发者自己管理的,还有平台指针事件下发,也是通过一个static 函数调用一次 SendEvent塞给 engine,这就是抽象的魅力。这也应该是 Flutter 更适合作为一个 寄生🐛,跑到某个宿主环境上原因之一吧。

总结

看这个有用吗?有用,自己的文章数量会上升。

为什么看这个?看到dart 各种 external 方法,心里很痒想知道这些到底在干什么,现在舒服了😌。

Flutter 还值得学吗,有必要看这么深吗? 我的评价是自己做取舍,没有一劳永逸,YYDS 的东西,但是有长期不变的设计思维经典算法底层知识,这些才是阅读源代码获得最有价值的东西!