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
命名空间下(“gfx API”)为图形客户端提供 3D 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 的 Allocator 协议(在
fuchsia.ui.composition/Allocator
下定义)进行的映像分配。- 由于客户端和 Scenic 会在使用 Sysmem 进行分配之前就分配和格式达成一致,因此所有 Image 用法都是零拷贝。
- 借助分配器,您可以在多个 Flatland 会话中使用图片。
- Flatland 不提供命令联合模式。Present() 调用是用于处理已加入队列的命令(即各个方法)的标记。
- Flatland 会严格限制客户端调用 Present() 的次数。这通过响应 OnPresentProcessed() 进行传达。这用作节流机制。
- Flatland 可能会在 OnPresentProcessed() 回调中返回错误,以告知客户端存在非法操作。此后,Flatland 频道将关闭。
- Flatland 要求客户定义和跟踪唯一资源标识符。类型安全由围绕这些标识符(例如 TransformId 和 ContentId)定义的结构体强制执行。
- Flatland 使用悬挂 get 来通知客户端有关链接结构或属性的更改。这些事件是通过单独的协议发出的。
未知
Flatland 设计可能会在某些方面进行演变,以更好地满足客户的需求。
- 客户端可能希望合成器处理显示硬件无法处理的一些 2D 效果,例如模糊处理。这些操作的费用较高。我们计划通过解耦这两类操作来明确这一区别。Flatland 的当前状态仅支持显示屏可以处理的操作。
- 演示和反馈流程会根据客户的反馈不断改进。在定义当前 Flatland 流程之前,我们对 gfx API 的呈现流程进行了三次迭代。因此,PresentArgs 被定义为表,以便日后更灵活地进行更改。
- 在设计和演示反馈中,我们会重点关注低延迟客户端和高吞吐量客户端。OnPresentProcessed() 会在适合开始处理下一帧时通知客户端。OnFramePresented() 会告知高级客户端所呈现帧到达屏幕的时间。
易用性
请查看 flatland_unittest 中的树内测试,找到大量 Flatland API 使用示例。
下图说明了用于关联多个 Flatland 会话的连接图的工作原理。这是一个更复杂但常见的用例。
图 1 - 平面国
测试
Flatland 目前被定义为内部 API,测试由树内单元测试提供。由于除了测试之外,没有其他方法可以运行 Flatland 代码,因此我们一直在使用自动化测试全面涵盖每段代码。我们计划保持这种测试覆盖率和质量。
我们计划最终将所有图形示例都转换为 Flatland。我们没有任何客户需要严格地生活在 3D 世界中。我们目前正在努力使 Flatland 演示者成为添加和迁移树内集成测试的基础https://fxbug.dev/42156206。这将为我们日后迁移一些关键的外部客户端(例如 Flutter 和 Chromium)做好准备。
Flatland 旨在让您能够在不依赖硬件功能(例如 Vulkan 和显示屏)的情况下运行业务逻辑。Vulkan 可以与 null 渲染程序实现进行交换,后者会跳过合成。显示屏可以与现有的虚拟显示屏实现进行切换。
我们将在兼容性测试套件下提供其他 Flatland API 集成测试。
性能注意事项
Flatland 的设计允许多个客户端同时运行,而不会影响彼此的工作。
- 每个方法都是单向 FIDL 方法,表示客户端将内容呈现到显示屏的矩形部分的命令。Present() 表示来自客户端的命令序列已结束,并表示 Scenic 开始处理下一次显示更新。
- 在积极更新内容时,大多数客户端都会在每个 vsync 调用至少一次。对于 60 fps 显示场景,这大约为 16 毫秒。在每个 vsync 间隔中:
- 客户端可以执行 N 次调用来修改场景图,然后执行一次 Present() 调用。
- Flatland 会发出 OnPresentProcessed() 以告知客户操作已加入队列,客户应开始生成其下一帧。目前,我们的客户无法确定何时是开始工作的理想时间,并且依赖于硬编码的偏移量来避免重叠,而 OnPresentProcessed() 会解决此问题。此反馈还会提示客户未来对 Present() 的理想调用次数,并告知其 Present() 配额。
- 客户端可能会忽略 OnPresentProcessed() 中提供的时间和提示,并根据其内部时钟继续呈现。这仍然有效,但无法保证能避免资源争用。
- Flatland 会发出 OnFramePresented() 以告知客户端内容已实际显示在屏幕上。此反馈对于高级客户端的同步(例如音频/视频)至关重要。
- Flatland 通过显式定义
num_presents_returned
,阻止恶意客户端将过多的 Present() 调用加入队列。客户端调用 Present() 的次数不得超过 OnPresentProcessed() 允许的次数。每个客户端开始时都有 1 次显示许可。 - 请注意,Flatland 会在自己的调度程序上运行每个渠道连接。 这些反馈机制是异步的。
安全注意事项
Flatland 用户彼此之间是隔离的。它们各自通过自己的渠道连接到 Scenic。唯一资源标识符仅在其渠道范围内定义。它们只能定位到其父级 Flatland 会话定义的屏幕部分。
每种错误都会导致 Flatland 渠道关闭。在这些错误状态下,我们无法确定应在屏幕上绘制什么内容,因此我们认为没有必要允许客户端继续呈现。
隐私注意事项
Flatland 不会公开任何设备标识符或隐私敏感信息。客户端不会直接与硬件交互。
Flatland 允许客户端通过 SetDebugName() 设置可识别的调试字符串。在输出有关错误的详细系统日志时,此字符串会用作前缀,以帮助客户端区分各自的日志。客户端可以完全控制要在此处设置的内容,如果未设置任何内容,系统日志中将不显示任何前缀。
缺点和替代方案
我们从 fuchsia.ui.gfx
下的现有 3D API 中获得的经验,为我们做出 Flatland 2D API 决策奠定了基础。在制定设计决策时,我们考虑了所有用户反馈、错误和教训。
- 我们可以选择在现有 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。