| RFC-0147:视图系统 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 介绍了 Fuchsia 平台的窗口系统,并确认了其在平台中的作用。 |
| 问题 | |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2021-11-03 |
| 审核日期(年-月-日) | 2022-01-05 |
摘要
此 RFC 概述了 Fuchsia 视图系统:一组用于推理和交互视觉区域(“视图”)及其生命周期的 API。这组功能在其他平台上通常也称为窗口系统。此 RFC 的范围仅限于具有单个显示屏的产品。为适应无头设备和/或多个显示屏而进行的更改将在未来的 RFC 中介绍。
视图是图形内容区域,也是 Fuchsia 上图形和用户互动的基础单元。视图连接起来形成视图树层次结构,其中包含一个独特的根视图。Fuchsia 的图形合成器 Scenic 会渲染视图树中每个视图的图形内容,以生成显示输出。Scenic 和输入流水线负责将以界面为目标的用户输入(例如键盘、鼠标和触控)路由到正确的 View。
视图系统是 Fuchsia 平台支持“自带运行时”的关键部分,也是产品开发者使用来自多个运行时的图形内容在 Fuchsia 上构建安全的可视化用户体验的机制。Fuchsia 的合成 API(Flatland 和 GFX)构建在视图系统之上。
设计初衷
此 RFC 的目标是记录并批准与视图系统相关的“世界状态”。具体而言,我们希望批准以下决定。
- 希望在 Fuchsia 上显示图形的组件必须使用视图系统创建视图。唯一的例外是单窗口实用程序界面(例如 Virtcon 和恢复界面),它们不使用窗口化功能,并且设计为在 Scenic 未运行的资源受限情况下运行。这些界面直接与显示控制器通信。
- 视图系统是组件与 Fuchsia 平台对用户触控、鼠标和键盘以及无障碍服务的支持进行集成的唯一方式。
- 视图系统与所用的合成策略(GFX 与 Flatland)无关,并为 Fuchsia 上的所有合成器实现提供共享基础。
如需修改这些决策,需要提交额外的 RFC 和/或更新此 RFC。
利益相关方
辅导员:
hjfreyer@google.com
审核者:
此部分预计会在审核期间更新
- 图形:reveman@google.com、jjosh@google.com、emircan@google.com
- 输入:quiche@google.com
- 组件:ypomortsev@google.com
- 安全性:kostyak@google.com
- 一般问题:wez@google.com
已咨询:
sanjayc@google.com, hjfreyer@google.com, jjosh@google.com, lindkvist@google.com, geb@google.com
共同化:
草稿文档已发送给 Scenic 和 Input 团队进行讨论。
术语库
- Scenic
- Fuchsia 平台中的图形合成组件
- View System API 的唯一实现者,包括其与图形合成 API 和 HCI API 的关联。
- 查看
- 图形内容的视觉区域。
- 它具有坐标系、边界框,并通过视图树与其祖先建立明确的空间关系。
- 查看树
- 系统上视图的结构,通过父子关系连接。视图树的根视图通常会附加到显示屏。
- 显示
- 一种基于像素的输出设备;也称为屏幕。视图系统目前支持一次将一个屏幕连接到系统。
- 屏幕通过显示控制器进行管理。
- 显示控制器
- 用于管理连接到计算机的屏幕的驱动程序。
- 仅与 Scenic 对话。
- 不适用于树外客户端。
- Scene Manager
- 一种将根视图附加到屏幕的平台组件。
- Accessibility Manager
- 实现无障碍功能 API 的平台组件。
- 它具有对视图系统的特权访问权限。
- 系统 shell(“系统界面”或“SysUI”)
- 开发者 shell(“功能块”“present_view”)
- 用于测试的系统 shell。通常,这允许开发者单独启动 Fuchsia 组件。
- 界面客户端
- 这是指视图所有者的通用方式,在本 RFC 中随处可见。
- 示例:一个 Flutter 应用,具有多个顶级视图的 Chromium。
设计
视图的定义
Fuchsia 视图是 Fuchsia 上图形和交互的基本单元。视图定义了一个用于向用户显示图形内容的视觉区域。并非所有视图都会在同一时间显示。每个视图的图形内容均由 Fuchsia 组件提供。Scenic 实现了对管理视图以及将视图内容合成到屏幕上的平台支持。
每个视图:
- 可以纳入另一个视图(其子视图)的内容,从而形成视图树。
- 定义用于放置子内容的坐标系。
- 具有一个用于定义相应视图的可见(以及可选的互动式)部分的边界框。
- 可以连接到风景树,也可以断开与风景树的连接。(可能存在未连接的视图,但它们的内容不会渲染到屏幕上,也不会接收输入。)
Scenic 支持的任何图形合成 API(例如 fuchsia.ui.composition 和 fuchsia.ui.scenic)都必须提供用于创建视图和管理其生命周期的方法。任何未来的合成 API 也必须在 View 系统之上运行。Fuchsia 平台 HCI API(例如 pointer 和 keyboard)使用视图来路由输入。(如需了解详情,请参阅下文中的输入部分。)
查看树状视图
有一个全局视图树,由 Scenic 拥有和实现。视图树具有一个与屏幕连接的独特根视图。Scenic 是唯一可以直接操纵所有视图及其位置的 Fuchsia 组件。
视图树中的每个视图都有在自己的坐标系中定义的边界,以及在父视图的坐标系中表达的位置和方向。这些共同决定了图形内容最终在屏幕上可见的区域,以及响应用户输入的区域。
儿童视角
视图可以在其坐标系中创建一个空占位符,从而嵌入来自另一个视图的其他图形内容;该占位符称为视口。这两个视图在视图树中形成父子关系,其中父视图的视口嵌入了子视图的图形内容。为了建立这种关系,家长和孩子在创建 View 和 Viewport 时必须提供匹配的 token。这些令牌以内核对象的形式实现,并且不可克隆。父级和子级可以通过多种方式(在视图系统外部)获取这些匹配令牌。
将父 View 附加到视图树或从视图树分离父 View 时,也会附加/分离其子 View。即使子树从全局视图树中分离,子树中视图之间的连接也会保留。
视图隔离
虽然一个 View 可以嵌入另一个 View,但 View 系统不会让一个 View 访问任何其他 View 的图形内容。这种隔离保证是视图安全性的基础之一。
将美景地图视图显示到屏幕上
Scenic 是唯一与显示控制器通信的组件。它会获取整个视图树的图形内容,以及树中编码的关系,然后创建一个单张图片(可能包含多个层)。然后,它会对硬件进行编程,以显示最终的屏幕图像。
查看所有权
视图中的内容通过单个 FIDL 渠道提供给由 Scenic 实现的图形合成 API。从 FIDL 渠道到 Scenic 的每个客户端端点都称为“界面客户端”,一个界面客户端最多可以创建一个视图。
视图可能来自各种组件,包括面向用户的组件(例如浏览器)、系统界面以及属于 Fuchsia 平台的组件(例如无障碍管理器)。一个组件可以创建多个渠道,从而创建多个视图。因此,在某些情况下,单个组件可能会同时提供父视图和该视图的子视图。
为了合并来自多个组件的图形,开发者应在每个组件中创建一个或多个视图,并使用子视图和视图树来合并它们。请务必注意,视图树层次结构不必与组件实例层次结构一致。在许多情况下,这些结构有意设计得截然不同。
Runtimes
对于使用更高级别的语言(例如 Dart 或 JavaScript)编写的组件,runner 实现通常负责创建和管理视图。使用这些语言的开发者可能不了解底层操作系统的许多细节;Runner 的责任是将 View 生命周期转换为特定于语言或运行时的机制。
窗口管理与窗口系统与合成
Fuchsia 分离了在其他平台上有时会组合在一起的三个功能。
- 窗口管理包含产品专用政策以及有关窗口行为方式的具体选择。在 Fuchsia 上,此任务由系统界面负责,完全在平台之外执行。
- 窗口系统是指低级窗口管理。在 Fuchsia 上,此操作由视图系统处理。
- 合成是指将来自多个来源的图片组合起来,以生成用于显示的图片。目前,这也是 Scenic 组件的责任,不过将来可能会发生变化。
选择将窗口系统和合成分开,可为图形呈现提供快速路径,并实现高效的实现。虽然视图系统和合成器目前主要在 Scenic 中实现,但它们在 API 和代码方面都是分开的。这种分离使视图系统能够支持多种合成策略(Flatland 和 GFX)。
通过将窗口管理作为产品级问题,我们将政策逻辑保留在平台之外,从而确保平台和产品之间的清晰分离。
输入
视图系统是 Fuchsia 平台确定如何路由用户输入(例如指针事件或键盘事件)的主要机制。Fuchsia 的用户输入 API 旨在将每个渠道限定在特定视图的范围内。输入将根据当前视图焦点(如果是键盘事件)和/或与输入事件位置对应的视图(如果是触摸或鼠标事件)路由到视图。只有在连接到视图树时,视图才能接收用户输入。
多个视图可能会参与同一事件的输入处理。特定产品的窗口管理器可能会根据产品政策配置此路由,例如向系统界面授予对鼠标事件的全局访问权限。如需了解更多详情,请参阅用户输入架构。该系统可在多个运行时(包括能够消除触控手势歧义)的视图之间实现顺畅的用户体验。
这方面的一个重要影响是,Fuchsia 平台之外的组件无法直接访问来自驱动程序的输入事件。它们必须通过视图系统接收此信息。
查看焦点
在任何给定时间,视图树都有一个突出的视图,称为已聚焦的视图。获得焦点的视图通常是用户希望接收用户输入的视图。Fuchsia 的输入子系统依赖于视图焦点来确定将输入路由到何处。如需了解详情,请参阅焦点链文档。父视图所有者可以控制其子树中的视图焦点。
ViewRef
平台使用称为 ViewRefs 的令牌来识别视图并就视图进行通信。ViewRef 是对特定 View 的唯一引用,在系统重新启动之前保持唯一性。它以内核对象的形式实现,其句柄可以随意复制,并通过任意协议发送给其他组件。
View 系统 API 大量使用 ViewRef,为每个 View 提供稳定的跨组件引用。ViewRef 还用于发出有关关联视图的生命周期事件信号。这样一来,平台内外的其他组件就可以就视图及其生命周期进行通信,并将用户输入和无障碍协议路由到视图。
实现
Scenic 是视图系统的权威来源,因为视图树和核心视图管理 API 都是在 Scenic 中实现的。每个客户端都使用其 ViewRef 注册以接收关联视图的用户输入和无障碍事件。Scenic 目前支持两种图形合成 API:fuchsia.ui.scenic(旧版)和 fuchsia.ui.composition(正在开发中)。View 系统独立于每个平台,但可与每个平台紧密配合使用。
API 实现细节不在本 RFC 的讨论范围内,但相关 API 可在下方找到。值得注意的是,这些 API 在过去几年中逐步发展。未来可能会简化某些方面。
涉及“查看系统”的平台组件
- 风景
- 输入管道
- 场景管理器(或旧版系统上的根演示器)
- Accessibility Manager
- 文本管理器
用于视图管理的 API
- fuchsia.ui.views
- 查看引用、查看 token 定义
- 查看焦点管理
- fuchsia.ui.composition
- 创建视图、移动视图、调整视图大小、连接/断开视图
- fuchsia.ui.policy
- 将系统界面视图连接到根视图
- fuchsia.ui.app.ViewProvider
- 从组件创建 SurfaceView
- fuchsia.web.Frame
- 创建 Web 客户端视图
用于用户输入的 API
- fuchsia.ui.pointer
- 指针事件
- fuchsia.ui.input3
- 键盘输入
- fuchsia.ui.shortcut
- 键盘快捷键
无障碍功能 API
- fuchsia.accessibility.semantics
- 允许视图发送语义信息以实现无障碍功能
- fuchsia.ui.annotation
- 允许无障碍服务突出显示获得焦点的界面组件
性能
视图系统的许多方面都会影响图形和输入性能。下面将介绍一些重要方面。
- 所有运行时都相同:Fuchsia 的自带运行时原则意味着,Fuchsia 上的所有运行时都使用相同的事件路由,并且没有运行时会获得优先处理。
- 一次渲染:Scenic 可以组合不同视图的图形内容,而无需重新渲染,因为它知道视图位置,并且可以正确地将图形内容转发到显示驱动程序。
- 高效调度:Scenic 会公开焦点信息,从而允许非图形用户输入(例如键盘事件)直接从参与处理它的组件调度。
安全性和隐私权注意事项
视图系统为在 Fuchsia 上安全地合成图形内容做出了多项保证,这些保证构成了安全的用户体验的基础。不过,视图系统本身无法保证每个用户体验都是安全的,只能保证视图系统遵守其自身的安全保证。
View 系统尝试通过以下方式启用安全的用户体验:
- 将拥有和操纵视图树的唯一责任委托给 Scenic。
- 将屏幕上显示图形内容的唯一责任委托给 Scenic。
- 禁止界面客户端在没有匹配的 Viewport 或 ViewRef 的情况下操纵另一个 View。
- 禁止界面客户端使用视图系统来检查任何其他界面客户端的视图的内容。
- 仅允许界面客户端将输入内容注入到其视图子树中。
- 仅允许 View 在连接到视图树时获得焦点并接收输入。
界面安全性依赖于组件框架在功能路由方面提供的保证。视图安全性的详细信息将在未来的 RFC 中介绍。
测试
视图系统是在多个抽象层实现的,因此需要使用分层方法进行测试。
在 fuchsia.git 中:
- Scenic 组件代码库中视图树和视图生命周期的单元测试。
- /src/ui/tests 中的界面集成测试,用于测试围绕 View 系统 API 的合约。
- 这些 API 由 Scenic、Scene Manager、输入流水线、Accessibility Manager 和 Text Manager 等平台组件提供和使用。
- fuchsia.ui.observation.test API 用于树内集成测试,以检查视图系统行为。
树外:
- Chromium 和 Flutter 等运行时会编写集成测试来运行视图系统 API
- 产品编写间接使用视图系统的端到端测试。
文档
此 RFC 简要介绍了视图系统的角色和职责。我们将撰写文档,并可能撰写其他 RFC 来解决相关细节问题。
根据此 RFC 和即将针对 Flatland 进行的更改,还需要更新部分 Scenic 文档。
缺点、替代方案和未知因素
已知限制
View 系统目前缺少在多个视图之间同步更新的机制。
视图系统部分负责将功能路由到公开视图(使用 ViewRef)的运行时。这在概念上与通过 Fuchsia 的组件框架进行的功能路由有一些重叠,但目前完全是分开的。未来,在组件框架中支持 View 功能可能是有利的。
目前的 API 仅允许视图所有者创建子视图。对于希望创建多个顶级窗口的应用,这并不是理想的解决方案。
由于视图系统 API 在多年内由许多不同的贡献者自然演变而来,因此运行时开发者的认知负担目前相当高。未来,我们将通过提供更好的文档和示例,并尽可能简化 API 来缓解此问题。
替代方案
视图系统架构做出了多项架构选择。
- 任何包含图形的产品都需要使用 View 系统。
- 或者,Scenic 可以是可选的,产品可以包含自己的合成器。这将需要从树外公开许多低级 API。
- 当前方法的优势:
- 运行时之间的同质行为(包括无障碍功能和输入)
- 一致的安全保证
- 显示 API 可以不断发展,而不会破坏客户端
- 当前方法的缺点:
- 减少了产品自定义选项
- 运行时集成的复杂性
- 如果基于 Fuchsia 构建的产品需要,这种情况可能会在将来发生变化。
- 视图系统分布在多个组件中,包括 Scenic 和输入流水线。
- 或者,我们也可以将整个 View 系统构建到 Scenic 中。
- 当前方法的优势:
- 关注点分离:图形合成、窗口管理、产品政策都是独立的。
- 允许通过委托合成实现高性能图形路径
- 与界面相关的组件可以独立发展
- 即使子系统(例如文本输入)崩溃,也能让图形继续正常运行
- 当前方法的缺点:
- 多个进程可以通过 IPC 增加延迟时间
- 增加了组件之间的协调/同步复杂性
在先技术和参考资料
- 视图系统和 Scenic 在架构上与 X 窗口系统有一些相似之处。
- 其他方面则与 Wayland 协议更为相似。
- 输入架构 RFC 详细介绍了视图系统如何与 Fuchsia 的用户输入子系统互动。