RFC-0162:Flatland API

RFC-0162:Flatland API
状态已接受
区域
  • 图形
说明

介绍了 Flatland,即 Fuchsia 的 2D 合成 API。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-05-24
审核日期(年-月-日)2021-05-30

之前的 API 设计文档

此 RFC 之前以 API 设计文档的形式提交,后来在 API 设计文档模板被弃用后转换为 RFC。

摘要

本文档为 Fuchsia 图形客户端提出了一项 2D API。Flatland 为客户端提供类似于显示控制器的功能,其中资源是在 2D 世界中定义的。

目标和使用场景

Scenic 目前在 fuchsia.ui.gfx 命名空间下为图形客户端提供 3D API(即“gfx api”)。此 3D API 为客户端提供类似于视频游戏引擎或其他 3D 图形程序的场景模型。绘制顺序由 Z 深度处理,不透明度通过基于深度的 alpha 混合处理。遗憾的是,无论是从产品角度还是性能角度来看,gfx API 都不再适合 Scenic 的需求。无法实现群组不透明度等功能,因为如何处理一组半透明内容的阴影尚不清楚。

从产品角度来看,我们目前的客户是 2D 产品。没有深度和绘制顺序的概念。它们只是按照提交不同批次绘制几何图形的顺序排列。由于没有深度,透明度效果也由绘制顺序决定。“2D”客户端(通过 Flutter、Chromium 和会话框架)必须执行额外的工作,才能解决 Scenic 的 3D 场景表示与用户体验到的 2D 表示之间的不匹配问题。

从性能角度来看,现代视频显示控制器 (VDC) 硬件提供了多种加速功能,例如多个显示平面和硬件叠加层,Scenic 希望在未来利用这些功能来降低功耗和 GPU 使用率。不过,硬件严格按照 2D 范式运行,并且仅理解可放置在 X/Y 中的矩形层。Scenic 的当前 3D API 允许并鼓励客户提交不符合此范式的内容,这会阻碍优化尝试。

通过公开真正的 2D API,我们旨在让 2D 客户端的开发者更轻松地完成工作:

  • 2D API 更符合 2D 客户端的预期。
  • 尽可能将工作委托给 VDC,以减少 GPU 使用量。
  • 一种更轻量级的 Scenic,仅针对 2D 矩形层进行了优化。

设计

我们建议您从头开始编写 2D 协议,不要重复使用 fuchsia.ui.gfx 的任何部分。

建议的 2D API 的当前状态位于作为此审核的一部分提交的 fuchsia.ui.scenic.internal.flatland 库中。欢迎查看并发表评论。

以下是针对 Flatland API 做出的一些高级别决策的说明:

  • Flatland 密切遵循 fuchsia.hardware.display/Controller 上定义的显示控制器 API,并提供类似的功能。
    • 性能最佳的场景是,Scenic 中的 Flatland 实现将其资源传递给显示屏,而不进行合成。因此,显示控制器 API 具有以相同方式定义的常见资源。也就是说,用作栅栏的 zx.handle:EVENT(用于指示何时可以安全地访问资源)通过这些协议传输,并具有相同的含义和用途。
  • Flatland 旨在为客户端提供确定性的 CPU 费用。有一个截止时间调度的渲染线程。每个 Flatland 会话都在自己的调度程序上运行,这些调度程序在当前配置中位于自己的线程上。
    • 可能存在多个 Flatland 会话,每个会话都是一个与 Flatland 通信的渠道,目的是在显示屏上渲染到矩形层。这些会话可能不会影响彼此的展示或表演流程。
  • 强制要求通过 Scenic 的分配器协议(在 fuchsia.ui.composition/Allocator 下定义)完成图片分配。
    • 所有图片使用都是零复制,因为客户端和 Scenic 在使用 Sysmem 进行分配之前就分配和格式达成了一致。
    • 分配器允许在多个 Flatland 会话中使用映像。
  • Flatland 不提供命令联合模式。Present() 调用是处理已入队的命令(即各个方法)的标记。
    • Flatland 严格限制客户端调用 Present() 的次数。此限制通过响应 OnPresentProcessed() 进行传达。此限制用作节流机制。
    • Flatland 可能会在 OnPresentProcessed() 回调中返回错误,以告知客户端存在非法操作。之后,Flatland 渠道会关闭。
  • Flatland 要求客户端定义并跟踪唯一资源标识符。通过围绕这些标识符(例如 TransformId 和 ContentId)定义的结构体来强制执行类型安全。
  • Flatland 使用挂起式 GET 请求来通知客户端链接结构或属性的变化。这些信息通过单独的协议发出。

未知

扁平化设计可能会在某些方面进行改进,以更好地满足客户需求。

  • 客户端可能希望合成器处理显示硬件未处理的某些 2D 效果,例如模糊效果。这些操作的费用更高。我们计划将这两类操作分离,以明确区分它们。Flatland 的当前状态仅支持可由显示屏处理的操作。
  • 演示和反馈流程会根据客户的反馈不断改进。在定义当前的 Flatland 流程之前,gfx API 的呈现流程经历了三次迭代。因此,我们将 PresentArgs 定义为表格,以便在未来进行更改时具有一定的灵活性。
    • 我们在设计和演示反馈中重点关注低延迟客户端和高吞吐量客户端。OnPresentProcessed() 会在适合开始处理下一帧时通知客户端。OnFramePresented() 会告知高级客户端所呈现的帧到达屏幕的时间。

易用性

请查看 flatland_unittest 中的树内测试,以找到大量 Flatland API 用法示例。

下图说明了关联多个 Flatland 会话的连通图的运作方式。这是一个更复杂但很常见的用例。

图 1 - 平面国 此图显示了 Flatland API 之间的关联。

测试

Flatland 目前定义为内部 API,测试由树内单元测试提供。由于除了测试之外,没有其他运行 Flatland 代码的方法,因此我们一直在使用自动化测试广泛覆盖每段代码。我们计划保持此测试覆盖率和质量。

我们计划最终将所有图形示例转换为 Flatland。我们没有任何必须生活在 3D 世界中的客户。我们目前正在努力让 Flatland 演示者成为添加和迁移树内集成测试的基础 https://fxbug.dev/42156206。这将为我们将来迁移一些关键的外部客户端(例如 Flutter 和 Chromium)做好准备。

Flatland 旨在允许运行业务逻辑,而无需依赖硬件功能(例如 Vulkan 和显示屏)。Vulkan 可由跳过合成的 null 渲染器实现替换。显示可以替换现有的虚假显示实现。

我们将在兼容性测试套件下提供更多 Flatland API 集成测试。

性能考虑因素

Flatland 旨在允许多个客户端同时运行,而不会影响彼此的工作。

  • 每种方法都是一种单向 FIDL 方法,表示客户端向显示屏的矩形部分发出渲染命令。Present() 表示来自客户端的命令序列结束,以及 Scenic 开始处理下一个显示更新。
  • 大多数客户端在主动更新内容时,会在每个垂直同步点至少进行一次调用。对于 60 fps 的显示场景,该时间约为 16 毫秒。在每个 Vsync 间隔中:
    • 客户端可以进行 N 次调用来修改场景图,然后进行一次 Present() 调用。
    • Flatland 会发出 OnPresentProcessed(),告知客户端操作已排队,客户端应开始生成下一帧。 目前,我们的客户不知道何时是开始工作的理想时间,他们依靠硬编码的偏移量来避免重叠,而 OnPresentProcessed() 可以解决这个问题。此反馈还提供了有关未来理想的 Present() 调用(针对客户端)的提示,并告知客户其 Present() 许可。
    • 客户端可能会忽略 OnPresentProcessed() 中给定的时间和提示,并根据其内部时钟继续呈现。此方法仍然有效,但无法保证避免资源争用。
    • Flatland 会发出 OnFramePresented() 来告知客户端内容已实际显示在屏幕上。对于高级客户端,此反馈对于同步(例如音频/视频)是必需的。
  • Flatland 通过明确定义 num_presents_returned 来阻止恶意客户端排队过多的 Present() 调用。客户端调用 Present() 的次数不得超过 OnPresentProcessed() 允许的次数。每个客户端最初都允许调用一次 Present()。
  • 请注意,Flatland 会在自己的调度程序上运行每个渠道连接。 这些反馈机制是异步的。

安全注意事项

Flatland 用户彼此隔离。每个设备都通过自己的渠道连接到 Scenic。唯一资源标识符仅在其渠道范围内定义。它们只能定位到由其父级 Flatland 会话定义的部分屏幕。

每种错误情况都会导致 Flatland 渠道关闭。在这些错误状态下,不清楚屏幕上应绘制什么内容,因此我们认为没有必要允许客户端继续呈现。

隐私注意事项

Flatland 不会公开任何设备标识符或隐私敏感信息。客户端不直接与硬件互动。

Flatland 允许客户端通过 SetDebugName() 设置可识别的调试字符串。在打印有关错误的详细系统日志时,此字符串会用作前缀,以帮助客户端区分哪些是自己的错误。客户端完全可以控制在此处设置的内容,如果未设置任何内容,系统日志中将不包含任何前缀。

缺点和替代方案

我们从 fuchsia.ui.gfx 下的现有 3D API 中获得的经验是决定 Flatland 的 2D API 的基础。在做出设计决策时,我们考虑了所有用户反馈、bug 和经验教训。

  • 我们可以选择在现有 3D API 下发展 2D API。不过,两者之间存在一些根本性差异,这会给客户端和实现带来不必要的复杂性。
  • Flatland 可以使用与 fuchsia.ui.gfx 类似的命令联合模式。目前,每个 Flatland 命令都映射为一个 FIDL 方法。做出此决定的原因是,我们发现现有 3D API 命令模式存在一些负面影响。我们必须为客户端提供并维护不同语言的封装容器。不过,将每个注释都映射为一种方法也有一些缺点。在支持单次写入多条消息之前,此设计可防止批处理。不过,我们预计客户端不会经常操纵场景图,因此认为这不会产生过高的费用。

未来的工作

我们计划在以下方面改进 Flatland API:

  • 每个 Flatland 实例都将关联一个 ViewRef,并提供用于获取自身及其子级的 ViewRef 的方法。请参阅 https://fxbug.dev/42159888
  • 一种“工厂函数”,用于将输入协议绑定到特定的 Flatland 实例,从而将其范围限制为该实例的视图子树。请参阅 https://fxbug.dev/42159922
  • 尺寸和指标从父级流向子级,但父级(和中介服务器)不知道这些尺寸和指标在哪个帧生效,第一个帧除外。这会产生连锁效应:用户可能会看到不完美的帧,并且其他 API 会受到客户端逻辑中延迟的影响。请参阅 https://fxbug.dev/42156345
  • 正在考虑跨多个实例同步 Present(),但尚未解决。请参阅 https://fxbug.dev/42159935
  • Flatland 将与其依赖项一起位于 fuchsia.ui.composition 命名空间下。fuchsia.scenic.allocation 和 fuchsia.scenic.scheduling 也将移至该命名空间下。请参阅 https://fxbug.dev/42158797