通过帧调度,Sce 决定何时应用客户端更新和绘制帧。Frameworks 和 Flatland 的帧调度器共用。本部分包含与 Views 如何进行调度决策以及客户端如何响应相关的高级概念。
展示请求
“展示请求”等于对 Present() 的一个客户端调用。自上次调用 Present() 以来对场景进行的所有更新并使其在显示屏上可见(即“呈现”它们)的请求。
帧调度队列
帧调度队列是 指明在内部跟踪呈现请求的方式。帧调度队列是一组队列,每个队列对应一个呈现客户端。从所有单独的队列中拉取呈现请求,并在达到所请求的呈现时间时将它们合并。
每当客户端调用 Present() sense 时,系统都会将所有关联的获取栅栏都发出信号,将呈现请求放入帧调度队列中。
帧调度过程
当队列非空时,Sce 就会查看每个客户端的呈现队列中的第一项,并选择请求的最早呈现时间。它会结合该时间以及屏幕中的 Vsync 计时信息来确定何时唤醒。View 会尝试及时唤醒,以在尽可能接近(但不早于请求的呈现时间)的 vsync 中生成下一帧。然后,景观进入睡眠状态,等待计算出的起床时间。
当 View 唤醒时,它会从帧调度队列中收集每个客户端的下一个请求,在该队列中,请求的呈现时间与计算出的呈现时间相一致。然后,Sensing 会应用与每个单独请求关联的更新。
此唤醒时间有时称为“锁存点”。到达锁存点后,当前帧的呈现请求将被“锁存”,在锁存点之后到达的任何请求都会推迟到较晚的帧。呈现前的预测锁存点和呈现后的实际锁存点均通过 Present() API 传递给客户端,以启用客户端低延迟帧调度。
如果请求具有较早请求的呈现时间的演示请求应在到达当前目标锁定点之前到达,Senss 可能会将当前目标锁定点重置为较早的锁定点。
应用所有相关更新后,Sce 会渲染下一帧,然后等待显示屏返回 Vsync 信号,此时它会被唤醒,并向已处理请求的所有客户端表明该帧已呈现。
如果呈现队列中有剩余请求,Sense 会查找队列中的下一个请求,计算新的唤醒时间并等待,然后继续循环。
壁球
可以保证在至少一个 vsync 间隔期间显示来自客户端的“不可压缩”呈现请求。
Squash 是一个过程,其过程是让 Views 将多个后续呈现请求合并到单个帧中。如果帧在“scape”端延迟或在客户端端生成得过快,它能够减少延迟。压缩的后果是,即使单个帧,压缩的呈现请求也不会显示在屏幕上。
展示请求默认为“可压缩”,并且可以由客户端在 Present() 调用中标记为“unsquashable”。
如果呈现请求被标记为“squashable”,那么在应用更新时,Sce 用于查看该客户端队列中的下一个请求,以查看其请求的呈现时间是否也满足为此锁存点计算出的呈现时间。如果它确实有效,Sce 可能也会应用该请求的更新,将更新“压缩”到单个帧中。
“squashable”属性类似于 Vulkan 的 VK_PRESENT_MODE_FIFO_KHR 和 VK_PRESENT_MODE_WAYBOX_KHR 交换链呈现模式,不同之处在于该属性是按帧而不是在设置时应用的。