Flutter 渲染引擎详解 - Android GL 篇

写作费时,敬请点赞,关注,收藏三连。

Flutter 渲染引擎在 iOS 上的实现请参考我的文章 Flutter 渲染引擎详解 - iOS Metal 篇

Flutter 渲染引擎在 Android 上也支持三种渲染方式,分别是纯软件(CPU),GL 和 Vulkan。其中纯软件模式是运行时由 Settings 的 enable_software_rendering 开关控制,默认为 false,目前只看到是通过命令行开关来控制,猜测是用于特定的测试用途。

跟 iOS 不一样的是,Flutter 在 Android 上并不是动态判断系统和硬件环境,在运行时来选择 Vulkan 或者 GL,而是需要开发者自行编译一个开启 SHELL_ENABLE_VULKAN 宏的 Flutter Engine,可能 Skia 对 Vulkan 在 Android 上的支持还不够完善的缘故,这个宏是默认关闭的,所以 Flutter 在 Android 上目前还是以 GL 为主。

这篇文章的主要内容是讲解在 Android 上,Flutter 渲染引擎:

  1. 需要的 GL GPU 上下文环境是如何完成初始化;
  2. 目标输出 Surface 的设置过程;
  3. 渲染流水线执行光栅化的调用过程。



上图显示了 Flutter 渲染引擎在 Android 上主要涉及的对象,黄色背景是平台相关的适配对象,白色背景是平台无关的通用对象。后面的内容我们会频繁地引用图中的对象,这张图可以方便读者了解它们之间的关系。

Flutter 在 1.20 之前的版本,Context 和 Surface 这部分的实现存在比较多的 hardcode,代码逻辑比较混乱,1.20 版本做了比较大的重构,整体设计跟 iOS 基本趋同,本文是根据 1.20 的代码写就。

GL GPU 上下文环境初始化



上图显示了 Android 应用在主线程初始化 Flutter Engine 的调用栈:

  1. Flutter Engine Native 部分的主要入口在 AndroidShellHolder,它在 engine 初始化的时候被创建;
  2. AndroidShellHolder 创建 Shell 并持有它;
  3. Shell 在创建时调用 AndroidShellHolder 提供的 Callback 创建 PlatformViewAndroid 并获得所有权;
  4. PlatformViewAndroid 在被创建时先创建 AndroidContextGL,然后把它传递给接着创建的 AndroidSurfaceGL,最后 PlatformViewAndroid 持有 AndroidSurfaceGL,AndroidSurfaceGL 持有 AndroidContextGL;

AndroidContextGL 提供了 Flutter 渲染引擎所需要的 GL 上下文环境。它会先创建两个 EGLContext,一个 main context 在 raster 线程用于光栅化,一个 resource context 在 io 线程用于图片纹理上传,因为 main context 是 resource context 的 share context,所以 resource context 上传的纹理可以被 main context 直接使用。AndroidSurfaceGL 在构建时会马上请求 AndroidContextGL 创建 offscreen 的 AndroidEGLSurface。AndroidEGLSurface 是 EGLSurface 的封装,对于 offscreen 来说,只是用一个 1x1 的 PbufferSurface 作为 resource context 的 target EGLSurface,这个PbufferSurface 只是作为必须的占位符,没有实际用途。

AndroidSurfaceGL 构建完毕之后,Flutter 渲染引擎所需要的 GL 上下文环境就已经初始化完成了。跟 iOS Metal 不同的是,因为 GL Context 的线程相关性,Skia GrContext 需要延迟到 GL Context 设置到目标线程真正使用的时候才创建,无法提前创建。

设置目标输出 Surface

Flutter 允许应用选择 SurfaceView 或者 TextureView 作为目标输出 Window,无论是选择 SurfaceView 还是 TextureView,Flutter 都是通过注册回调方法,在 SurfaceView/TextureView 可见时获得回调,得到封装了 Android Native Window 的 Surface 对象。



  1. 当主线程获得系统回调通知 Surface 已经创建时,它会调用 Engine Native 的代码 SurfaceCreated(platform_view_android_jni_impl.cc);
  2. SurfaceCreated 会取出 Surface 对象里面的 Window Handle,并封装成 AndroidNativeWindow 对象传递给 PlatformViewAndroid::NotifyCreated;
  3. PlatformViewAndroid::NotifyCreated 在主线程被调用时,会通知 raster 线程,并同步等待结果;
  4. raster 线程接收到消息后会调用 AndroidSurfaceGL::SetNativeWindow;
  5. AndroidSurfaceGL::SetNativeWindow 调用 AndroidContextGL::CreateOnscreenSurface;
std::unique_ptr<AndroidEGLSurface> AndroidContextGL::CreateOnscreenSurface(
    fml::RefPtr<AndroidNativeWindow> window) const {
  EGLDisplay display = environment_->Display();

  const EGLint attribs[] = {EGL_NONE};

  EGLSurface surface = eglCreateWindowSurface(
      display, config_, reinterpret_cast<EGLNativeWindowType>(window->handle()),
      attribs);
  return std::make_unique<AndroidEGLSurface>(surface, display, context_);
}

AndroidContextGL::CreateOnscreenSurface 实际上就是使用 Window Handle 创建对应的 EGLSurface,并包装成 AndroidEGLSurface 返回给 AndroidSurfaceGL 作为光栅化输出的 onscreen EGLSurface。

主线程等待完 raster 线程创建 onscreen EGLSurface 后,再次同步请求 raster 线程调用 PlatformViewAndroid::CreateRenderingSurface,实际上这里就是创建 GPUSurfaceGL。GPUSurfaceGL 被创建时会先请求 AndroidSurfaceGL 将 AndroidContextGL 里面的 main EGLContext 作为 raster 线程的当前 GL 上下文,然后创建对应的 GrContext 并持有。这个 GrContext 将来就会用于光栅化。

主线程同步等待 GPUSurfaceGL 创建完毕后,把获得的 GPUSurfaceGL 对象通过 Shell 传递给 Rasterizer 持有,到这里光栅化器就完成了目标输出 Surface 的设置,现在我们可以开始绘制第一帧了。

光栅化输出

关于 Flutter 渲染流水线比较完整的说明请参考我之前的文章 Flutter 渲染流水线浅析,在这里我们只关注光栅化的部分。Flutter 光栅化的过程比较简单,Android 和 iOS 基本流程也是大同小异:

  1. Rasterizer 请求 GPUSurfaceGL 创建一个 SkSurface,这个 SkSurface 实际上就是当前 EGLContext 的 FBO 0 的包装,也就是之前创建的 onscreen EGLSurface,实际上就是源自 SurfaceView 或者 TextureView 的 Android Native Window;
  2. 通过上述获得 SkSurface 对象,取得对应的 SkCanvas 对象;
  3. 将生成的图层树里面的 DisplayList(SkPicture)通过上面的 SkCanvas 逐个绘制到 SkSurface 上,Skia 会先存储经过预处理的 2D 绘图指令;
  4. Flush SkCanvas,相当于生成相应的 GL GPU 绘图指令请求 GPU 执行;
  5. 最后调用 eglSwapBuffers 请求 Android Native Window 交换前后台缓冲区,并触发 Android UI 的重绘(TextureView)或者 SurfaceFlinger 的窗口重新合成(SurfaceView);
发布于 09-10

文章被以下专栏收录