Flutter for Windows共享纹理的猜想

2,183 阅读4分钟

前言:Flutter官方给Texture的内容并不多,再加上for Desktop的,资料就不多了,正好最近比较有时间,研究了下Flutter for Windows底层的渲染逻辑。谷歌在IOSAndroid上的Engine处理外部Texture的代码和注释比较实在,Windows上的注释要么是还不支持,要么是很快支持,要么是将来可能有变动,所以至于实现,得自己定制Engine

这其中需要很多工程知识,比如gncmake的使用,我一个读书仔哪懂那么多,不过参考其他平台的外界纹理,总结了大概过程。

前情提要

image.png 在上一篇文章中,我们梳理了FlutterWindowsEngine的创建过程,发现了它创建了一个AngleSurfaceManager,打开文档,发现这是个重量级选手。

src\flutter\shell\platform\windows源码目录

AngleSurfaceManager

ANGLE是什么?

Flutter官方解释:

image.png

看得出是GL转DX11或DX12,让我们看看声明文件

angle_surface_manager.h

看得出ANGLE在这里使用EGL进行图形操作。

image.png

这些都是不暴露给外面的私有方法,要共享纹理,就得拿到contextFlutter内部自己使用了共享纹理,egl_resource_context_egl_context_,一个用来异步上传纹理,一个用来渲染

image.png

angle_surface_manager.cc

让我们看看这个东西初始GLcontext的内容

Initialize函数

这个函数内容太多了,我们就截取部分,Flutter内部通过eglCreateContext的第三个参数实现共享Context了。

image.png

Flutter Win平台下的纹理注册过程

一切源于这个issue

Issue #78648 · flutter/flutter (github.com)

提issue作者的demo flutter_windows_texture_bug 中,有关于如何在Windows下注册和上传使用纹理的内容,具体过程在gldraw_plugin.cppGLDrawPlugin::HandleMethodCall内。 他使用了官方目前实现的PixelBufferTextureTextureVariant,完成CPU->GPU的纹理上传方法,通过MarkTextureFrameAvailable触发回调函数更新纹理,像素格式为RGBA8。如果你的流仅在Flutter内部使用,那么这种方案可行。

image.png image.png

过程可以总结如下

和其他平台并没有区别

image.png

更深入一层

还记的我在前面几篇文章讲的flutter_windows.cc吗?

Flutter Engine在Windows上启动详解 - 掘金 (juejin.cn)

不管你上面是PixelBufferTexture还是什么的,他只是个接口,最后的数据还是经过层层处理,会储存在FlutterDesktopTextureInfo

image.png

src\flutter\shell\platform\common\public\flutter_texture_registrar.h包含FlutterDesktopTextureInfo定义,做了数据引用和类型定义

image.png

看到了本质,那我们顺着注册流程走一遍看看内部到底在作什么

1. FlutterWindowsTextureRegistrar

Win平台的RegistrarFlutterWindowsTextureRegistrar,实现为src\flutter\shell\platform\windows\flutter_windows_texture_registrar.cc

让我们看看他的RegisterTexture函数:

image.png

31行出现了!flutter::ExternalTextureGL,这个才是内部使用的外部纹理对象,构造函数参数包括我们最初设定的供MarkTextureFrameAvailable使用的函数。

2. ExternalTextureGL

实现文件src/flutter/shell/platform/windows/external_texture_gl.cc,一打开,亲切可爱。

ExternalTextureGLState存储的就是使用glGenTextures的第二个纹理标识符参数GLuint

image.png

PopulateTexture会调用CopyPixelBuffer,通过判断ExternalTextureGLStateGLunit是否为0,为0就初始和生成一份纹理,不为0直接glBindTexture

看到103行的texture_callback了吗,我们最上方写的函数就在这里被调用了,把数据生成并且传递了过来。

image.png

那么谁调用PopulateTexture呢?

嘿嘿,是Registrar!每个向他注册的纹理会被它储存,它从自己的textures_库中取出所需纹理,然后调用。

image.png

一个疑问:这里也许和MarkTextureFrameAvailable有着某种联系,好奇的可以自己去查查engine的这个函数如何调用。

异想天开

前置条件:你已经在Angle_Surface_Manager获得了egl_context,并且用它创建了属于你自己的egl_context

  1. 我们是不是可以向Registrar注册一个纹理,然后修改他的ExternalTextureGLState为你在其他egl_context自己生成的纹理的GLuint,并且对PopulateCopy这两个函数做一些修改,防止不必要的gl操作,或者定制成glTexSub等等,然后MarkAvaible......

  2. 你都拿到flutteregl_context了,你想把放到其他图形渲染引擎,夺舍它也不是不行.....

END

这可能是这个专栏的倒数第二篇文章了,engine研究那么多,还是没有直接写dart来得快乐。所以接下来停下engine的学习了,希望这篇文章对你有用!!

有错误,留个言,点个赞吧!

下一篇写个用Cmake和Vs2019启动engine的文章就完结吧。

Cmake和Vs2019启动Flutter Engine(还没写)