背景
随着虚拟化技术如模拟器,容器化等技术等发展,在安卓云游戏/云手机场景中,可以在服务宿主侧虚拟出更多更小颗粒度的 Android 实例。其中比较核心的技术是图形虚拟化技术,如何最大限度利用宿主侧的 GPU 资源进行渲染和编码,不考虑软编等利用 CPU 资源进行渲染编码是因为效率带来的延迟问题。
Linux 图形栈
先看一个比较通用的 linux 图形栈:
- X 协议:比较早的协议,X server 直接管理 GPU 内的 framebuffer 和 X Client 提交命令,通过 XClient(Xlib 或 XCB)向 Xserver(Xorg)提交相关命令实现,且有很多扩展协议,但是弊端需要一个额外的 Windows Manager 来处理多个应用。目前已经被 Wayland 这种扩展协议取代,composer 处理输入,窗口,合成显示等功能。
- GLX:因为是用来做间接渲染,做了两个工作:1)将 OpenGL 和 X window API 绑定 2)通过 X server 转发 GL 的调用。本质还是 X 协议那一套。
- FB driver:历史遗留显示子系统,提供了 Framebuffer 获取,图像操作原语,电源管理等功能。
- OpenGL:统一的 3D 图形渲染 API 接口,各主流厂商(Intel、 Nvidia、AMD、Qualcomm 等)都支持的接口,主流实现的是开源的 mesa。Mesa 3D 是其最主流的开源实现,值得注意的是 Mesa 不仅支持 OpenGL,还支持 Vulkan, Direct 3D 等渲染 API。
- DRM:Direct Rendering Manager, 目前主流的 GPU 显示子系统,用户态使用 libDRM 的 DRM API 来操作 DRM 设备,对 GPU 通过 ioctl 等标准文件操作来通信,实现:
- framebuffer 管理。
- 用户态抽象渲染能力:如 Buffer Object 管理,GPU 作业命令提交等,一般和具体 driver 相关。
- Virtual Driver 支持:包含 vmwgfx(VMware 桥接设备)和 virgl(Virto 桥接设备)
- Prime Zero-copy memory,buffer 通过用户态的 fd 文件描述符代表了实际显存中的 DMA buffer,通过 Prime API 导出 FD,可以在 IPC 之间传递。
包含 Wayland 比较主流的所有图像栈变得异常复杂:
每种应用的图像数据流都比较复杂,但大致流线是:应用(显示 Client)->(显示 Server)->OpenGL/EGL->Mesa 3D->libDRM->(内核)DRM->GPU 驱动。
Android 图形栈
以 SurfaceFlinger 为核心,维护了所有 app 窗口的交叠覆盖关系:
- OpenGL ES:2D/3D 渲染走的路径,使用 drm 所有功能进行绘制渲染。
- Gralloc FB: 使用 drm 为 app 提供显存管理等功能。
- HWComposer: 调用 openGL 窗口合成 RGB 或者 YUV,实现屏幕绘制。
综合 Linux 图形栈和 Android 图形栈可以发现在底层都是基于 drm 实现,实现硬编方案的核心思想就是渲染和编码都利用宿主侧的 GPU,并且渲染和编码 Zero-copy,所以有两类技术:
- virto-gpu 技术将 OpenGLES 命令导出(virgl)之后再反过来调用宿主侧的 virglrenderer,又将其翻译回 OpenGL 和 GLSL,然后再调用宿主的 OpenGL,这部分技术代表是 Qemu 方案。使用假的抽象 GPU。在抽象层 GPU 层进行拦截,并调用宿主侧的 GPU。