Android Frame Pacing 库(也称为 Swappy)是 Android Game SDK 的一部分。它可帮助 OpenGL 和 Vulkan 游戏在 Android 上实现流畅的渲染和正确的帧同步。
帧同步是指游戏的逻辑和渲染循环与操作系统的显示子系统和底层显示硬件之间的同步。Android 显示子系统旨在避免某些视觉伪影,例如画面撕裂。显示子系统可通过执行以下操作避免画面撕裂:
- 在内部缓冲之前的帧
- 检测延迟帧的提交情况
- 当检测到延迟帧时继续显示当前帧
帧展示时间不一致是因为游戏渲染循环的运行速率与本机显示硬件支持的速率不同。如果底层显示硬件的游戏渲染循环运行速度过慢,会产生问题,导致展示时间不一致。例如,当运行速度为 30 FPS 的游戏尝试在原生支持 60 FPS 的设备上渲染时,游戏的渲染循环会导致同一帧在屏幕上又重复显示 16 毫秒。这种类型的中断会导致帧时间严重不一致,例如帧时间可能为 33 ms、16 ms、49 ms 等。过于复杂的场景会让该问题更复杂,因为它们会导致丢帧。
Frame Pacing 库可执行以下任务:
- 补偿由于游戏帧较短而出现的卡顿现象。
- 添加呈现时间戳,以便按时呈现帧,而不是提前呈现。
- 使用呈现时间戳扩展
EGL_ANDROID_presentation_time
和VK_GOOGLE_display_timing
。
- 对导致卡顿和延迟的长帧使用同步栅栏。
- 将 wait 注入应用,让显示管道能够跟上进度,而不会积累背压。
- 使用同步栅栏(
EGL_KHR_fence_sync
和VkFence
)。
- 如果设备支持多个刷新频率,会选择其中一个以便提供灵活流畅的呈现效果。
- 基于帧统计信息提供用于调试和性能分析的统计信息。
如需了解如何根据需求将库配置为以不同模式运行,请参阅支持的操作模式。
如需使用 OpenGL 渲染程序或 Vulkan 渲染程序实现,请参阅以下文章:
如需了解详情,请参阅实现适当的帧同步。
每秒帧数节流干预
借助每秒帧数 (FPS) 节流干预,游戏只需使用平台端更改即可保持适当的 FPS 速度,而无需开发者执行任何操作。
FPS 节流干预的实现使用以下组件:
GameManagerService
GameManagerService 组件会保留每位用户和每个游戏的游戏模式和游戏干预信息。FPS 信息存储在 GameManagerService 中,并在每个用户个人资料的 <PACKAGE_NAME, Interventions>
映射中存储其他干预信息(例如分辨率缩放系数)。
当游戏模式更改或干预更新时,可以访问 FPS 信息。UID
对于每个 PACKAGE_NAME
和用户都是唯一的,可以进一步转换为 <UID, Frame Rate>
对以发送到 SurfaceFlinger。
SurfaceFlinger
SurfaceFlinger 组件现已支持对应用的 FPS 进行节流,但前提是帧速率必须是显示刷新率的除数。 在发生 vsync 的情况下,SurfaceFlinger 会验证 vsync 时间戳是否与应用的帧速率处于同一相位,以检查受到节流的应用的 vsync 有效性。如果帧速率未与 vsync 处于同一相位,则 SurfaceFlinger 会保持该帧,直到帧速率和 vsync 处于同相位。
下图介绍了 GameManagerService 与 SurfaceFlinger 之间的交互:
SurfaceFinger 会维护一个 <UID, Frame Rate>
对映射,以设置新的帧速率节流优先级。UID
在用户和游戏之间是唯一的,因此同一台设备上的每位用户都可以对同一游戏采用不同的帧速率设置。为了对游戏的帧速率进行节流,GameServiceManager 会调用 SurfaceFlinger 来替换 UID 的帧速率。借助此机制,SurfaceFlinger 会在游戏模式更改或干预更新时更新映射。SurfaceFlinger 通过相应地锁定缓冲区来处理 FPS 更改。
如需详细了解 FPS 节流,请参阅 FPS 节流简介。