一次转换,两条去处

解码器交出来的是外部纹理,只有 GL 能采样它。渲染这一侧的做法是先把它转成一张 RGBA 纹理:平台渲染器用外部纹理采样器把这一帧画进自己的图,这张图就同时是它对外交出的 GPU 上下文,两条路都从它身上取东西。
转换这一层在平台渲染器里,两条路都要走它。互斥的是后面呈现那一步。
去处一:渲染器自建一个面,直接上屏
开关关着的时候,窗口那一侧交给渲染器的是一个原生窗口。渲染器按它建面:有窗口时建的是窗口面,没有窗口时建的是一个离屏对接面(pbuffer)。
每帧的动作很短:把上下文设为当前、按画面真实比例算一次视口(不是全屏时留黑边)、把那张 RGBA 纹理画成一个全屏四边形,然后交换上屏。
交换带垂直同步,不过这一层把交换间隔设成了 0。屏幕那一侧的同步留给系统的合成器,这一侧不等。
这条路与片段着色器里那几个变体有关(直通态走另一套采样),那几个变体怎么分在渲染分层那一篇里讲过,这里不重复。
去处二:把这块显存交给 Vulkan
开关开着的时候,窗口那一侧换成 Vulkan 的窗口对象,平台渲染器改成离屏——它的那张 RGBA 纹理不再上屏,而是交给 VK 的输入层。
交接处共享的不是纹理对象,是一块显存。做法是三步:
先分配一个缓冲,格式是 8bit RGBA,用途标明这块仅供 GPU 写与采样。再用 EGL 把它包成一个 EGLImage,并把这个 image 绑成一张 GL 纹理——到这一步,GL 侧能直接读写它。

VK 侧反过来做一遍:查这块缓冲的属性,建图时声明这是一张外部图、外部类型是这种缓冲,再把缓冲导入并绑定内存。同一个缓冲于是有了两个 API 各自的视图。
输入层每帧做的事:先从 GL 那一侧取那张 RGBA 纹理的编号、尺寸与上下文,尺寸或纹理换了新才重建图;然后把导入进来的 VkImage 当传输源,拷进管线自己的纹理。
重建的条件不能只看纹理编号。GL 的上下文重建之后,纹理编号常常被复用,只看编号会拿到旧尺寸的图,画面从第一帧起就是错的——所以要连上下文一起判。
反方向也有一条:VK 合成完的输出面同样绑回一张 GL 纹理,给不走 VK 的那条呈现路用。
Vulkan 开关:窗口对象与渲染器取面两处跟着换
开关只有一个(设 Vulkan 那个接口)。它一改,两处跟着换:窗口对象换成 VK 的窗口,平台渲染器那边改成离屏取面。界面上的默认关着,要走 VK 就显式开。
两条路互斥,互斥的是呈现那一步——同一张输出纹理只能是一种格式,不能一边上屏一边交给 VK。转换那一步两条都要走,上屏那一步只有一条做。
那块缓冲的格式与用途,和一次能力探测
分配那块显存的时候有两个参数要定:格式是 8bit RGBA,用途是「只给 GPU 写、只给 GPU 采样」。用途这一项影响驱动怎么摆放这块内存——不做 CPU 读的那一档,驱动可以把它放在更合适的位置。反过来说,如果哪一步需要 CPU 去读这块缓冲,这条路就不适用,得另找一条。
互操作能力要先探一次:启动时判断这套设备与系统版本支不支持这种缓冲,不支持就不走这条绑定,落回由渲染器自己出图那条更普通的路。
系统版本还要照顾一档:较老的 NDK 头文件里没有那几个缓冲函数,代码是按名字动态取出来用的,取不到按不支持处理。这与解码那边取候选名是同一类写法——能力探测放在入口,而不是等到某一帧才失败。
那条离屏面,与交换间隔的取舍
渲染器建面分两种:给窗口时建窗口面,不给窗口时建离屏面。离屏面这一档是给「不出窗口但要出图」的用法留的:导出、抓帧,或者当一个对接面交给别人——编码器要的输入面就是这一类用法。
交换间隔设成 0 是个取舍:渲染完立刻交换,不等垂直同步,延迟更低,屏幕那一侧的同步交给系统合成器;代价是可能出现撕裂。这一版按低延迟优先选。
三个平台摆在一起看
同一件事在另外两个平台上换了显存对象,骨架没变。
Windows 上是 NT 句柄加一道共享栅栏,读方要在 CPU 上等写方一手;Android 上是一块缓冲,两个 API 各挂一个视图,中间没有显式等待;Apple 上是一个系统面,靠约定顺序(Metal 先出、VK 接手)。
三处的共同点:一次 RGBA 转换、一个能跨 API 共享的显存对象、以及互斥的那一步呈现。
与硬编共用同一套设施
这一套 EGL 上下文与面,硬编那一侧也在用:编码器要一个能收纹理的输入面,渲染就往那个面里画。所以这一篇里的「自建一个面、画进去、然后交换」这个动作,换个面就是编码的输入。
Android 上硬解帧的两条去处,一个落在平台渲染器自己建的那条链上,一个落在「共享显存 → VkImage」这条交接上;共用的是那次转换和那块显存,不同的是谁做呈现。
引擎 avox 在 GitHub 开源:github.com/xxxzhou/avox,外部纹理采样、那块共享显存与 VK 输入层都在里面。
音视频开发 播放器 硬件解码 OpenGL ES Vulkan AHardwareBuffer