通过共享内存优化Flutter外接纹理的渲染性能,实时渲染不是梦

前言

看了咸鱼这篇《万万没想到——flutter这样外接纹理》的文章,我们了解到 Flutter 提供一种机制,可以将 Native 的纹理共享给 Flutter 来进行渲染。但是,由于 Flutter 获取 Native 纹理的数据类型是 CVPixelBuffer,导致 Native 纹理需要经过 GPU->CPU->GPU 的转换过程消耗额外性能,这对于需要实时渲染的音视频类需求,是不可接受的。

闲鱼这边的解决方案是修改了 Flutter Engine 的代码,将 FlutterGL 环境和 nativeGL 环境通过 ShareGroup 来联通,避免2个环境的纹理传递还要去 CPU 内存绕一圈。此方案能够解决内存拷贝的性能问题,但暴露 FlutterGL 环境,毕竟是一个存在风险的操作,给以后的 Flutter 渲染问题定位也增加了复杂度。所以,有没有一个完美、简便的方案呢?答案就是利用 CVPixelBuffer 的共享内存机制。

Flutter外接纹理的原理

先回顾下前置知识,看看官方提供的外接纹理机制究竟是怎样运行的。

图中红色块,是我们自己要编写的 Native 代码,黄色是 Flutter Engine 的内部代码逻辑。整体流程分为注册纹理,和整体的纹理渲染逻辑。

注册纹理
  1. 创建一个对象,实现FlutterTexture协议,该对象用来管理具体的纹理数据
  2. 通过FlutterTextureRegistry来注册第一步的FlutterTexture对象,获取一个flutter纹理id
  3. 将该id通过channel机制传递给dart侧,dart侧就能够通过Texture这个widget来使用纹理了,参数就是id
纹理渲染
  1. dart侧声明一个Texture widget,表明该widget实际渲染的是native提供的纹理
  2. engine侧拿到layerTree,layerTree的TextureLayer节点负责外接纹理的渲染
  3. 首先通过dart侧传递的id,找到先注册的FlutterTexture,该flutterTexture是我们自己用native代码实现的,其核心是实现了copyPixelBuffer方法
  4. flutter engine调用copyPixelBuffer拿到具体的纹理数据,然后交由底层进行gpu渲染

CVPixelBuffer格式分析

一切问题的根源就在这里了:CVPixelBuffer。从上面flutter外接纹理的渲染流程来看,native纹理到flutter纹理的数据交互,是通过 copyPixelBuffer 传递的,其参数就是 CVPixelBuffer。而前面咸鱼文章里面说的性能问题,就来自于纹理与 CVPixelBuffer 之间的转换。

那么,如果 CVPixelBuffer 能够和OpenGL的纹理同享同一份内存拷贝,GPU -> CPU -> GPU的性能瓶颈,是否就能够迎刃而解了呢?其实我们看一下flutter engine里面利用CVPixelBuffer来创建纹理的方法,就能够得到答案:

Flutter Engine是使用 CVOpenGLESTextureCacheCreateTextureFromImage 这个接口来从 CVPixelBuffer 对象创建OpenGL纹理的,那么这个接口实际上做了什么呢?我们来看一下官方文档

This function either creates a new or returns a cached CVOpenGLESTextureRef texture object mapped to the CVImageBufferRef and associated parameters. This operation creates a live binding between the image buffer and the underlying texture object. The EAGLContext associated with the cache may be modified to create, delete, or bind textures. When used as a source texture or GL_COLOR_ATTACHMENT, the image buffer must be unlocked before rendering. The source or render buffer texture should not be re-used until the rendering has completed. This can be guaranteed by calling glFlush().

从文档里面,我们了解到几个关键点:

  1. 返回的纹理对象,是直接映射到了CVPixelBufferRef对象的内存的
  2. 这块buffer内存,其实是可以同时被CPU和GPU访问的,我们只需要遵循如下的规则:
    • GPU访问的时候,该 CVPixelBuffer ,不能够处于lock状态。
      使用过pixelbuffer的同学应该都知道,通常CPU操作pixelbuffer对象的时候,要先进行lock操作,操作完毕再unlock。所以这里也容易理解,GPU使用纹理的时候,其必然不能够同时被CPU操作。
    • CPU访问的时候,要保证GPU已经渲染完成,通常是指在 glFlush() 调用之后。
      这里也容易理解,CPU要读写这个buffer的时候,要保证关联的纹理不能正在被OpenGL渲染。

我们用instrument的allocation来验证一下:

instrument的结果,也能够印证文档中的结论。 只有在创建pixelBuffer的时候,才分配了内存,而映射到纹理的时候,并没有新的内存分配。

这里也能印证我们的结论,创建pixelBuffer的时候,才分配了内存,映射到纹理的时候,并没有新的内存分配。

共享内存方案

既然了解到CVPixelBuffer对象,实际上是可以桥接一个OpenGL的纹理的,那我们的整体解决方案就水到渠成了,可以看看下面这个图

关键点在于,首先需要创建pixelBuffer对象,并分配内存。然后在native gl环境和flutter gl环境里面分别映射一个纹理对象。这样,在2个独立的gl环境里面,我们都有各自的纹理对象,但实际上其内存都被映射到同一个CVPixelBuffer上。在实际的每一帧渲染流程里面,native环境做渲染到纹理,而flutter环境里面则是从纹理读取数据。

Demo演示

这里我写了个小demo来验证下实际效果,demo的主要逻辑是以60FPS的帧率,渲染一个旋转的三角形到一个pixelBuffer映射的纹理上。然后每帧绘制完成之后,通知 Flutter 侧来读取这个pixelBuffer对象去做渲染。

核心代码展示如下:

关键代码都添加了注释,这里就不分析了

我们从上面的gif图上可以看到整个渲染过程是十分流畅的,最后看displayLink的帧率也能够达到60FPS。该demo是可以套用到其他的需要CPU与GPU共享内存的场景的。

完整的demo代码在这里flutter_texture

参考链接


通过共享内存优化flutter外接纹理的渲染性能,实时渲染不是梦

Flutter格式化排除代码段

前置条件
  • ubuntu 24.04.2 LTS
  • Visual Studio Code 1.98.2
  • Android Studio Meerkat | 2024.3.1
  • Flutter 3.29.1
问题描述

在使用 Flutter 编写代码的时候,如下代码进行格式化

可以发现格式化后的结果变成了如下的样子:

这部分代码我们不希望格式化,尤其是编写加解密算法的时候,里面的置换数组格式化之后会完全乱掉。

可以使用

阻止格式化工具对具体代码块格式化。

比如如下代码:

参考链接


在Visual Studio Code中调试Flutter Dart代码报错未验证断点(Unverified Breakpoint)

前置条件
  • ubuntu 24.04.2 LTS
  • Visual Studio Code 1.98.2
  • Flutter 3.29.1
问题描述

在使用 Visual Studio Code 调试 Flutter 代码的时候,如果在依赖库的代码中设置断点,会无法断点,并且提示断点未验证,"Unverified Breakpoint"。

如下图:

继续阅读在Visual Studio Code中调试Flutter Dart代码报错未验证断点(Unverified Breakpoint)

Flutter多平台打包发布

fastforge (原名 flutter_distributor)是一个强大的工具,支持跨平台发布和高级打包选项。

安装

确保 fastforge 已安装:

配置文件

为高级打包配置所需的文件:

  • macOS:

    • 如果打包 DMG 安装包:macos/packaging/dmg/make_config.yaml

    • 如果打包 PKG 安装包:macos/packaging/pkg/make_config.yaml

    • distribute_options.yaml

  • Windows:

    • 如果打包 exe 安装包:windows/packaging/exe/inno_setup.sas

      windows/packaging/exe/make_config.yaml

    • 如果打包 msix windows/packaging/msix/make_config.yaml

  • Linux:

    • linux/packaging/appimage

      有两个文件 linux/packaging/appimage/AppRun

      linux/packaging/appimage/make_config.yaml

    • linux/packaging/deb 文件 linux/packaging/deb/make_config.yaml

    • linux/packaging/rpm 文件 linux/packaging/rpm/make_config.yaml

命令示例

  1. 打包 macOS (DMG 和 PKG)

  2. 打包 Windows (EXE 和 MSIX)

  3. 打包 Linux (DEB、RPM 和 AppImage)

参考链接


Flutter 3.29.1-使用U盾完成数据的加解密(国密算法SKF接口)

参考 Python3-使用U盾完成数据的加解密(国密算法SKF接口),那么相同的功能如何使用 Flutter FFI 实现呢?

  • Flutter 3.29.1
  • ffi 2.1.4
  • pointycastle 3.6.0
  • pkcs7 1.0.5
  • convert 3.1.2

代码参考如下:

 

使用方式:

参考链接


VSCode GDB调试控制台报错"-var-create: unable to create variable object"

背景

在使用 Visual Studio Code (1.97.2) 进行 GDB 调试时,想使用 x 命令看一下某地址处的数值。出现如下报错:

解决方案

其实,在刚刚开始调试程序时,就以黄色字体给出了解决方案。

也就是说:

  • 如果是想查看某个变量的值,直接在输入框里输入变量名就好了;

  • 如果要执行GDB命令,则需要加-exec前缀,如下图:

参考链接


解决在macOS上使用Flutter项目访问网络报错 "SocketException: Connection failed (OS Error: Operation not permitted, errno = 1)"

在Flutter应用中添加网络支持是一个非常常见的需求。

这主要是按照以下步骤进行。

  • pub.dev中安装一个网络包,如httpdio
  • 在您的 Flutter 应用程序中添加所有必要的网络代码
  • 如果需要,添加任何所需的API密钥

但是,如果你在macOS上运行该应用程序,你会被这个可怕的错误所欢迎。

SocketException错误已经来拜访你了

这就是它,以更可读的格式。

所以,让我们弄清楚为什么会发生这种情况,以及如何修复它。👇

注意:如果你要给一个旧的Flutter应用添加macOS支持,你可能需要运行flutter create --platforms macos . ,把macOS添加为构建目标。我在macOS上运行我的大部分应用程序,所以在构建响应式UI时,我可以轻松地调整窗口的大小。

macOS上的客户端联网权限

macOS应用程序默认为沙盒,如果你没有添加所需的权限,就会发生SocketException 的错误。

要解决这个问题,请打开名为macos/Runner/DebugProfile.entitlements 的文件并添加以下内容。

然后,打开macos/Runner/Release.entitlements ,做同样的事情。

那么iOS呢?

在iOS上,只要你连接到一个安全的https 终端,该应用就可以正常运行(不需要额外的配置)。

但请记住,在某些情况下,你可能需要定制应用程序的传输安全设置,如这里所解释的。

安卓的情况如何?

在安卓系统中,曾经需要AndroidManifest.xml 文件中添加INTERNET权限。

但根据这个答案,现在已经不需要了,因为大多数应用程序都需要互联网访问。

参考链接


如何解决 "SocketException:连接失败(操作不允许)",在macOS上使用Flutter

Error: Gradle build failed to produce an .apk file. It's likely that this file was generated under /xxxx/build, but the tool couldn't find it.

Flutter 升级到 3.27.3 版本,然后升级 Android 构建工具到最新的 AGP 8.8.0 版本,然后编译报错:

于是在项目的配置文件中寻找配置 "build" 目录的地方,于是在 Flutter 项目的 Android 工程根目录下找到,如下:

注意 getLayout().setBuildDirectory('../build') ,以前的 Gradle 版本是可以正常编译的,现在的版本需要需要改为 rootProject.buildDir = "../build" 。

修改后的结果如下:

Flutter各版本对操作系统的支持情况

Flutter 3.3

32-bit iOS deprecation

As we announced earlier with the 3.0 stable release, due to decreased usage, that release was the last one to support 32-bit iOS devices and iOS versions 9 and 10. This change affects the iPhone 4S, iPhone 5, iPhone 5C, and the 2nd, 3d, and 4th generation iPad devices. The 3.3 stable version of Flutter and all following stable releases no longer support 32-bit iOS devices and iOS versions 9 and 10. This means that apps built against Flutter 3.3 and later won’t run on those devices.

Sunsetting macOS 10.11 and 10.12

In the Q4 2022 stable release, we expect to drop support for macOS versions 10.11 and 10.12. This means that apps built against stable Flutter SDKs after that point will no longer work on these versions, and the minimum macOS version supported by Flutter will increase to 10.13 High Sierra.

Bitcode deprecation

Bitcode will no longer be accepted for iOS app submission in the upcoming Xcode 14 release, and projects with bitcode enabled will emit a build warning in this version of Xcode. In light of this, Flutter will drop support for bitcode in a future stable release.

By default, Flutter apps don’t have bitcode enabled, and we don’t expect this to impact many developers. However, if you have enabled bitcode manually in your Xcode project, disable it as soon as you upgrade to Xcode 14. You can do so by opening ios/Runner.xcworkspace and setting the build setting Enable Bitcode to No. Add-to-app developers should disable it in the host Xcode project.

See Apple’s documentation to learn more about bitcode distribution.

Flutter 3.7

Supported platforms

As of the current release, Flutter supports the following platforms as part of Google-tested and best-effort platform tier:

Platform Version Channels
Android API 16 (Android 4.1) & above All
iOS iOS 11 & above All
Linux Debian, 64-bit All
macOS El Capitan (10.11) & above All
Web Chrome 84 & above All
Web Firefox 72.0 & above All
Web Safari on El Capitan & above All
Web Edge 1.2.0 & above All
Windows Windows 7 & above, 64-bit All

All channels include master, beta, and stable channels.

Google-tested platforms
Platform Version
Android Android SDK 19–30*
iOS 14-15
Linux Debian 10
Linux Ubuntu 18.04 LTS
macOS Monterey (12) & above
Web Chrome 84
Web Firefox 72.0
Web Safari / Catalina
Web Edge 1.2.0
Windows Windows 10

* Passing tests on Android SDK 19 also confers a passing result on SDK 20. This is because Android SDK 20 has additional support for Android Wear, but otherwise no new or deprecated API.

Best-effort platforms
Platform Version
Android Android SDK 16–18
iOS iOS 11-13
Linux Debian 11
Linux Debian 9 & below
Linux Ubuntu 20.04
Linux Ubuntu 22.04 (Aspirational Google-tested platform)
macOS El Capitan (10.11) - Big Sur (11)*
Windows Windows 11 (Aspirational Google-tested platform)
Windows Windows 8
Windows Windows 7

* Flutter 3.3 is the last stable release with macOS 10.11 through 10.13 best-effort support.

Unsupported platforms
Platform Version
Android Android SDK 15 & below
iOS iOS 10 & below and arm7v 32-bit iOS
Linux Any 32-bit platform
macOS Yosemite (10.10) & below
Windows Windows Vista & below
Windows Any 32-bit platform

Flutter 3.24.5

目标平台 目标架构 支持版本 CI 测试版本 不支持版本
Android SDK x64, Arm32, Arm64 21 至 34 21 至 34 20 和更早版本
iOS Arm64 12 至 18 17 11 和更早版本
macOS x64, Arm64 Mojave (10.14) 至 Sequoia (15) Ventura (13), Sonoma (14) High Sierra (10.13) 和更早版本
Windows x64, Arm64 10, 11 10 8 和更早版本
Debian (Linux) x64, Arm64 10, 11, 12 11, 12 9 和更早版本
Ubuntu (Linux) x64, Arm64 20.04 LTS 至 24.04 LTS 20.04 LTS, 22.04 LTS 23.10 和更早的非 LTS 版本
Chrome (Web) JavaScript, WebAssembly 最新的 2 个 119, 125 95 和更早版本
Firefox (Web) JavaScript 最新的 2 个 132 98 和更早版本
Safari (Web) JavaScript 15.6 和更新版本 15.6 15.5 和更早版本
Edge (Web) JavaScript, WebAssembly 最新的 2 个 119, 125 95 和更早版本

Flutter 3.27.0

目标平台 目标架构 支持版本 CI测试版本 不支持版本
Android SDK x64, Arm32, Arm64 21 to 34 21 to 34 20 and earlier
iOS Arm64 12 to 18 17 11 and earlier
macOS x64, Arm64 Mojave (10.14) to Sequoia (15) Ventura (13), Sonoma (14) High Sierra (10.13) and earlier
Windows x64, Arm64 10, 11 10 8 and earlier
Debian (Linux) x64, Arm64 10, 11, 12 11, 12 9 and earlier
Ubuntu (Linux) x64, Arm64 20.04 LTS to 24.04 LTS 20.04 LTS, 22.04 LTS 23.10 and earlier non-LTS
Chrome (Web) JavaScript, WebAssembly Latest 2 119, 125 95 and earlier
Firefox (Web) JavaScript Latest 2 132 98 and earlier
Safari (Web) JavaScript 15.6 and newer 15.6 15.5 and earlier
Edge (Web) JavaScript, WebAssembly Latest 2 119, 125 95 and earlier

参考链接


Flutter Web 调试问题: Assertion Failed on Text Input Click in Chrome (Flutter 3.27.0, Dart 3.6.0)

前置条件

  • Flutter 版本: 3.27.0 / 3.27.1 / 3.27.2
  • Dart 版本: 3.6.0
  • 编译器: Android Studio Ladybug | 2024.2.1 Patch 3
  • 操作系统: macOS Sequoia 15.2 (Apple M2) / Windows 11 专业版 23H2
  • 浏览器: Chrome (131.0.6778.205)

问题描述

直接从 Flutter API 官方页面(Form Class Documentation)复制 Form 示例,并使用 Flutter 的 Web 调试模式运行。

尝试用鼠标单击文本输入时,遇到以下错误:

这个问题是 Flutter 3.27.0 /  3.27.1 / 3.27.2 的缺陷,官方已经在 Flutter 3.27.3 / 3.28 版本修复,后续升级到 Flutter 3.27.3 / 3.28 即可解决此问题。

目前(2024/12/23master 通道已经修复这个问题,可以通过切换到 master 通道来解决:

目前(2025/01/25Flutter 3.27.3 已经修复这个问题,升级到此版本可以修复问题。

参考链接