GTK 的色彩

色彩让一切更美好。如果它是 HDR,那就更棒了。

1708 年的七色和十二色色环,归功于克劳德·布泰

在这篇文章中,我们将概述 GTK 中色彩方面的进展,而不会深入探讨色彩测量学和色彩科学的细节。

这项工作的高级目标是启用对 HDR 内容和色彩管理工作流程的正确处理。

最初,sRGB

到目前为止,GTK 中的颜色始终被假定为在 sRGB 色彩空间中。无处不在的 GdkRGBA 结构被定义为指定 sRGB 颜色。

sRGB 在 20 年前是一件伟大的事情,但世界正在转向其他色彩空间,这些色彩空间包括更广泛的色调范围(例如 Display-P3)或更大的动态范围(例如 BT.2100-PQ)。

在将此列入我们的议程相当长一段时间后(对该主题的初步调查发生在 2021 年),我们终于在为更丰富多彩的未来奠定基础方面取得了进展。

即将推出:GdkColorState

今年早些时候,我们合并了 Alice 的一些出色工作,以支持我们的 CSS 引擎中的现代色彩语法和色彩空间。在 CSS 中拥有富有表现力的颜色很棒,但如果我们的所有渲染机制仍然需要以 sRGB 指定颜色,它就无法真正闪耀。

因此,在过去的一两个月里,我们添加了在定义明确的色彩空间中进行渲染的支持,并引入了一个代表这些色彩空间的对象。它被称为 GdkColorState(别问我为什么名称从空间更改为状态)。

截至目前,GdkColorState 可以表示 sRGB 和 BT.2100-PQ,以及它们的线性化变体。您还不能用这些对象做太多事情,它们主要供我们的渲染器内部使用。

不过,您已经可以做一件事,那就是通过设置以下环境变量来尝试在线性色彩空间中渲染的效果:

GDK_DEBUG=linear

环境变量。

最终我们需要进行此项更改,才能产生正确且可理解的结果,尤其是在使用 HDR 内容时。

在线性色彩状态下进行所有合成看起来确实略有不同,因此我们希望将此更改推迟到完成所有周围的工作后,并在开发周期的开始进行此更改,以便让每个人都有时间进行调整。

新协议

Wayland 色彩管理协议已经酝酿了很长时间,但它有望很快退出实验阶段 — kwin 已经支持它,并且还有一个 mutter 分支。

我们添加了对 xx-color-management-v4 协议的支持,因此我们可以从合成器获取首选的色彩状态(如果它支持该协议),并且我们可以告知它我们产生的帧的色彩状态。

今天,您的合成器不太可能首选像 BT.2100-PQ 这样的 HDR 色彩状态(除非您找到打开实验性 HDR 支持的隐藏开关),但您可以尝试当 GTK 在该色彩状态下渲染时,通过设置以下环境变量来查看效果:

GDK_DEBUG=hdr

环境变量。

请注意,这不会使 HDR 内容显示在您的屏幕上 — 我们在 HDR 中进行渲染,并将最终帧转换回 sRGB 作为最后一步。要使 HDR 内容显示在您的屏幕上,需要一个接受此类内容的合成器。

未来:更多色彩状态和线性合成

我们仍然有一个很长的 待办事项列表,以在 GTK 中全面开发色彩支持。

重点包括

  • 更多色彩状态(包括用于更好渐变的 OKLCH 和用于视频内容的 YUV)
  • 色彩状态感知渲染 API(GdkColor 和新的 GtkSnapshot API)
  • 将 CSS 色彩状态信息传递给渲染器
  • 从 gstreamer 传播色彩状态信息(用于 HDR 等)
  • 切换到线性合成

其中一些有望及时纳入 GTK 4.16 版本。

总结

更好的色彩支持即将来到 GTK。

图形卸载继续

我们去年秋季首次引入了对 dmabuf 和图形卸载的支持,它包含在 GTK 4.14 中。自从我们上次 讨论以来,已经发生了很多改进,所以现在是时候进行另一次更新了。

转换

当您旋转显示器时,它的纵横比会从横向(例如,1920 x 1200)更改为纵向(1200 x 1920)。但是合成器处理的帧缓冲区保持为 1920 x 1200。

在某些其他情况下,内容可能需要在显示之前进行一些转换。例如,当显示来自前置摄像头的视频流时。

D4 cycle graph

到目前为止,GTK 将翻转或旋转的纹理视为不可卸载的,但在最近进行了一些改进之后,我们现在将在卸载内容时将此类转换传递给合成器。

快速绕行

这是一个很好的机会来解释 GTK 转换 API 依赖于来提供对有用类别的分类,例如 2D 平移、缩放、旋转或任意 3D 转换。

因此,最好使用特定方法,例如 gsk_transform_rotategsk_transform_translate,而不是自己计算转换矩阵并使用 gsk_transform_matrix,这不会给我们提供有关我们正在处理的转换类型的信息。如果没有此信息,我们就会错过像上面描述的转换卸载这样的优化。

本杰明 此处 简要概述了我们的转换方法。

纹理

卸载工作的最初目标是启用 dmabuf 内容的直通。但是,通常我们得到的不是这个。我们收到 GL 纹理中的内容(例如,来自 gstreamer)仍然更为常见。值得庆幸的是,mesa 具有 GL 扩展,可让我们将纹理导出为 dmabuf。我们最近使用此功能为 GL 纹理启用了卸载。

请注意,这些扩展可能并非在所有地方都可用,并且各个驱动程序在 dmabuf 支持方面可能会遇到问题。

总结

在即将发布的 GTK 4.16 版本中,图形卸载应该会更频繁地工作。

您今天可以尝试这些改进,例如在新的 Showtime 应用程序的 每日构建版本

Showtime

图形卸载回顾

我们去年秋季首次引入了对 dmabuf 和 图形卸载 的支持,它包含在 GTK 4.14 中。从那时起,发生了一些改进,所以现在是时候进行更新了。

堆栈中的改进

GStreamer 1.24 版本改进了对显式修改器的支持,并且 GTK 中的 GStreamer 媒体后端已更新为从 GStreamer 请求 dmabuf。

GStreamer 方面的另一件事是,dmabuf 有时会带有填充:在这种情况下,GStreamer 将为我们提供一个带有视口的缓冲区,并期望我们仅显示缓冲区的那一部分。有时,这是必要的,以适应硬件解码器的步幅和大小要求。

GTK 4.14 在卸载时支持此功能,并且仅显示 dmabuf 的视口指示的部分。

GTK 内部的改进

我们为 GTK 4.14 合并了新的 GSK 渲染器。新的渲染器以与旧的 gl 渲染器相同的方式支持 dmabuf。此外,新的 Vulkan 渲染器在渲染到纹理时会生成 dmabuf。

在 GTK 4.16 中,GtkGLArea 小部件如果可以,也会提供 dmabuf 纹理,因此您可以将其放入 GtkGraphicsOffload 小部件中以将其输出直接发送到合成器。

您可以在 git main 中的 gtk4-demo 中的 shadertoy 演示中看到这一点。

Shadertoy 演示,卸载的图形周围带有金色轮廓

改进的合成器交互

图形卸载的一个好处是,合成器可能能够将 dmabuf 传递给内核的 KMS API,而无需任何额外的副本或合成。这被称为直接扫描输出,它有助于降低功耗,因为不会使用 GPU 的大部分区域。

仅当 dmabuf 连接到全屏表面并且具有覆盖它的正确尺寸时,合成器才能执行此操作。如果它没有完全覆盖,则合成器需要一些保证,即可以将外部部分保留为黑色。

客户端提供该保证的一种方法是将 专门构造的黑色缓冲区附加到具有 dmabuf 附加的表面下方。如果 GSK 在 rendernode 树中找到黑色颜色节点,并且您设置了“black-background”属性,则 GtkGraphicsOffload 小部件现在将执行此操作。这应该会大大增加您在播放全屏视频时享受直接扫描输出优势的机会。

Developer trying to make sense of graphics offload
具有全屏黑色背景的卸载内容

在为 GTK 4.16 实现此功能时,我们发现 mutter 对单像素缓冲区的支持存在一些问题,但这些问题已得到快速修复。

要在 GTK4 视频播放器中查看图形卸载和直接扫描输出的效果,您可以尝试 Light Video Player

如果您想了解图形卸载是否在您的系统上工作或调试为什么它不起作用,本杰明最近的这篇 文章 非常有帮助。

总结

GTK 4 继续改进,以实现高效的视频播放,并推动该领域在整个堆栈中的改进。

非常感谢 Robert Mader 为推动这一切所做的努力。 ❤️

GTK 4.14 中的辅助功能改进

GTK 4.14 在辅助功能方面带来了各种改进,特别是对于显示复杂的格式化文本的应用程序;对于 WebKitGTK;以及对于通知。

可访问文本接口

4.0 的辅助功能重写为 GTK 提供的控件(如 GtkTextView)中的复杂、可选择和格式化文本提供了一个实现,但树外的控件无法执行相同的操作,因为在讨论 AT(辅助技术)实际需要什么以及我们正在研究非 Linux 实现时,API 被保持为私有。对于 GTK 4.14,我们最终有了一个公共接口,树外的控件可以实现该接口,以向 AT 提供复杂、格式化的文本:GtkAccessibleText

GtkAccessibleText 允许控件在给定偏移量处提供文本内容;应用于内容的文本属性;并向辅助技术通知文本、插入符位置或选择边界的变化。

实现 GtkAccessibleText 的文本控件应在以下情况下通知 AT

文本属性主要留给应用程序实现——包括命名和序列化;GTK 为各种工具包和辅助技术已经使用的常用文本属性提供支持,它们在 API 参考中以 GTK_ACCESSIBLE_ATTRIBUTE_* 前缀作为常量提供。

GtkAccessibleText 接口是实现虚拟终端辅助功能的必要条件;最常见的基于 GTK 的虚拟终端库 VTE 已被移植到 GTK4,这要归功于 Christian Hergert 的努力,并且在 GNOME 46 中将通过新的 GTK 接口支持辅助功能。

桥接 AT-SPI 树

在某些情况下,库或应用程序会使用 AT-SPI 实现自己的可访问树,无论是在同一进程中还是在进程外。其中一个库是 WebKitGTK,它从单独进程内的 Web 树生成可访问对象树。这些进程不使用 GTK,因此它们不能使用 GtkAccessible API 来描述其内容。

感谢 Georges Stavracas 的工作,GTK 现在可以将这些辅助功能对象树桥接到 GTK 控件自身下,从而允许 AT 从 UI 导航到使用 WebKit 的网页。

目前,与 GTK 中其余的辅助功能 API 一样,这仅限于 Linux 上的 AT-SPI 协议,这意味着希望利用它的库和应用程序需要确保在编译时 API 可用,通过使用 pkg-config 文件和单独的 C 头文件,类似于打印 API 的公开方式。

通知

使用与当前控件焦点分离的应用内通知的应用程序,例如 libadwaita 中的 AdwToast,现在可以通过 gtk_accessible_announce() 方法向 AT 发送通知消息,这要归功于 Lukáš Tyrychtr,其方式尊重当前的 AT 输出。

其他改进

GTK 4.12 确保计算出的可访问标签和描述与 ARIA 规范保持同步;GTK 4.14 通过删除特殊情况和重复项来迭代这些改进。

感谢 文档基金会的 Michael Weghorn 的工作,文本相关的可访问对象有了新的角色,例如段落和注释,以及辅助功能 API 的 AT-SPI 实现中的各种修复。

由于许多人的贡献,GTK4 中的辅助功能支持正在每个周期逐步改进;理想情况下,这些改进还应为工具包和辅助技术共享带来更好、更高效的协议。

我们仍在探索为其他辅助功能平台(如 UIAutomation)和其他库(如 AccessKit)添加后端的可能性。

关于分数缩放、字体和微调

GTK 4.14 即将发布,其中包含今年早些时候引入的新渲染器。

新渲染器对分数缩放的支持得到了很大改进——在我的系统上,我现在使用 125% 的缩放而不是“大文本”设置,我发现它对我的需求来说效果很好。

魔法数字

自从 4.0 以来,GTK 一直倡导线性布局。

我们的想法是,我们只需将字形放置在坐标告诉我们的位置,如果这是一个介于像素之间的某个分数位置,那就这样吧,我们可以很好地在该偏移处渲染轮廓。这种方法可行——如果您的输出设备具有足够高的分辨率(任何高于 240 dpi 的分辨率都应该可以)。遗憾的是,我们生活在一个大多数笔记本电脑屏幕不具有这种分辨率的世界中,因此我们不能仅仅忽略像素。

因此,我们添加了 gtk-hint-font-metrics 设置,该设置强制文本布局将内容舍入为整数位置。这不太适合分数缩放,因为舍入发生在应用程序像素中,而我们真正需要的是整数设备像素位置才能产生清晰的结果。

应用程序像素与设备像素

常见的分数缩放比例为 125%、150%、175%、200% 和 225%。在这些比例下(200% 除外),大多数应用程序像素边界与设备像素边界不对齐。

现在怎么办?

新渲染器为我们提供了一个机会,可以重新审视字体渲染主题,并对微调选项的机制进行一些研究,以及它们如何从 GTK 通过 Pango 和 cairo 传递下来,然后最终在 freetype 中以渲染目标 + 加载标志的组合形式出现。

微调样式和抗锯齿选项转换为渲染模式和加载标志

新渲染器认识到,在字形方面有两种基本的操作模式

  • 优化统一间距
  • 优化清晰渲染

前者导致亚像素定位和无微调渲染,后者导致微调渲染和放置在整数像素位置的字形(因为这是自动微调器所期望的)。

我们通过查看字体选项来确定我们处于哪种情况。如果它们告诉我们进行微调,我们会将字形位置在 y 方向上舍入为整数设备像素。为什么只有 y?自动微调器仅在垂直方向上应用微调,而水平方向是亚像素位置的更高分辨率最有帮助的地方。如果我们不进行微调,那么我们会在 x 和 y 方向上都使用亚像素位置,就像旧渲染器一样(值得注意的是,新渲染器在设备像素中使用亚像素位置)。

对比

文本渲染的差异总是很微妙,并且在某种程度上是口味和偏好的问题。因此,这些屏幕截图应持保留态度——最好自己尝试新渲染器。

125% 渲染的文本,旧渲染器
125% 渲染的文本,新渲染器

这两个渲染都是在 125% 的比例下完成的,启用了微调(但请注意,旧渲染器通过以 200% 渲染并依靠合成器来缩小内容来处理 125%)。

以下是一些细节:T 和 e 的水平条在各行中保持一致,即使我们仍然允许字形在水平方向上按亚像素位置偏移。

一致的垂直放置
T 和 e 的实例,旧渲染器
T 和 e 的实例,新渲染器

总结

GTK 4.14 中的新渲染器应产生更清晰的字体渲染,尤其是在分数缩放的情况下。

请试用一下并告诉我们您的想法。

更新:关于亚像素渲染

我应该预料到会出现这个问题,所以这里有一个快速的答案

我们在 GTK 4 中没有使用子像素渲染(也称为 ClearType 或 rgb 抗锯齿),因为我们的合成没有组件 alpha。我们的字体抗锯齿始终是灰度。请注意,子像素渲染与子像素定位是不同的概念。

GTK 黑客马拉松更新

像往常一样,GTK 团队的一些成员和更广泛的 GNOME 社区在 FOSDEM 之前聚集在一起进行了为期两天的黑客马拉松。

今年,我们的目标是在可访问性和输入方面取得进展。以下是我们取得的成就的快速摘要。

可访问性

  • 我们同意合并 Georges 编写的用于 webkit 可访问性的套接字实现
  • 我们同意 Lukáš 建议的可访问通知 api 很好
  • 我们完成了 GtkAccessibleText 接口,并将我们的内部实现转移到了该接口
  • 我们讨论了基于 AccessKit 的 a11y 后端的 可能性

输入

  • Carlos 审查了将未处理的事件传递回系统(在 macOS 上)的合并请求
  • 我们检查了输入 API 中 X11 样式时间戳的残余,并决定提供接受事件的替代方案

Wayland

  • Carlos 开始将私有的 gtk-shell 协议转换为单独的协议

感谢 GNOME 基金会对本次活动的支持。 ❤️

GTK 的新渲染器

最近,GTK 获得了两个新的渲染器:一个用于 GL,一个用于 Vulkan。

由于命名困难,我们重用了现有的名称,并将其称为“ngl”和“vulkan”。它们是从相同的 源代码构建的,因此我们也称它们为“统一”渲染器。

但它们令人兴奋的地方是什么呢?

单一来源

如前所述,两个渲染器都是从相同的源代码构建的。它被建模为遵循 Vulkan API,并进行了一些抽象以涵盖 Vulkan 和 GL(更具体地说,GL 3.3+ 和 GLES 3.0+)之间的差异。这使我们可以共享大部分用于遍历场景图、维护转换和其他状态、缓存纹理和字形的架构,并且可以更轻松地使两个渲染器保持最新和同步。

这种统一的方法是否可以进一步扩展,以涵盖 macOS 上的基于 Metal 的渲染器或 Windows 上的基于 DirectX 的渲染器?有可能。Vulkan/GL 组合的优势在于它们基本上共享相同的着色器语言(GLSL,有一些变体)。Metal 或 DirectX 情况并非如此。对于这些平台,我们需要复制着色器或使用像 SPIRV-Cross 这样的翻译工具。

如果这正是您感兴趣的事情,欢迎提供帮助。

实现细节

旧的 GL 渲染器为每个渲染节点类型使用简单的着色器,并且经常采用离屏渲染来处理更复杂的内容。统一渲染器也具有(功能更强大的)每个节点的着色器,但它们不会依赖于离屏,而是会使用一个复杂的着色器来解释缓冲区中的数据。在游戏编程中,这种方法被称为 ubershader

统一渲染器的实现不如旧的 GL 渲染器优化,并且在编写时侧重于正确性和可维护性。因此,它可以正确地处理更多不同的渲染节点树。

这是一个看起来无害的示例

repeat {
  bounds: 0 0 50 50;
  child: border {
    outline: 0 0 4.3 4.3;
    widths: 1.3;
  }
}
gl(左)ngl(右)
特写视图

新功能

如果没有一些切实的好处,我们就不会完成所有这些工作。当然,还有新的特性和功能。让我们看一些

抗锯齿。 旧的 GL 渲染器的一个大问题是它会丢失精细的细节。如果某个东西足够小,以至于落在单行像素的边界之间,它就会直接消失。特别是,这可能会影响下划线,例如助记符。统一渲染器通过进行抗锯齿来更好地处理这种情况。这不仅有助于保留精细细节,还可以防止基元的锯齿状轮廓。

GL 与 NGL 的特写视图

分数缩放。 抗锯齿也是让我们正确处理分数缩放的基础。如果您的 1200 × 800 窗口设置为缩放到 125%,使用统一渲染器,我们将使用 1500 × 1000 大小的帧缓冲区,而不是让合成器缩小 2400 × 1600 的图像。更少的像素,更清晰的图像。

任意渐变。旧的 GL 渲染器处理具有最多 6 个颜色停止点的线性、径向和锥形渐变。统一渲染器允许无限数量的颜色停止点。新的渲染器还将抗锯齿应用于渐变,因此锐利的边缘将具有平滑的线条。

具有 64 个颜色停止点的线性渐变

Dmabufs。简要偏离新渲染器,我们致力于 dmabuf 支持和图形卸载 去年秋天。新的渲染器支持这一点,并将其扩展为在请求通过 render_texture API 生成纹理时创建 dmabuf(目前,仅限 Vulkan 渲染器)。

有任何锐利边缘吗?

与通常情况一样,新功能带来了新陷阱的可能性。作为应用程序开发人员,您应该注意以下几点

不再有 glshader 节点。是的,它们为 4.0 制作了一些花哨的演示,但它们与旧的 GL 渲染器紧密相关,因为它们对该渲染器公开的 GLSL API 做出假设。因此,新的渲染器不支持它们。

文档中已警告过您

如果出现问题,此函数将返回 FALSE 并报告错误。您应该在依赖着色器进行渲染之前使用此函数,如果失败,则使用具有更简单着色器的回退或不使用着色器的回退。

值得庆幸的是,自 4.0 以来 GTK 获得了新功能,例如蒙版节点和对直接 alpha 纹理的支持,因此不再需要 glshader 节点的许多用途。

分数位置。旧的 GL 渲染器会四舍五入,因此您可以处理分数位置。新的渲染器会将事物放置在您告诉它的位置。这有时会产生意想不到的 后果,因此您应该注意并确保您的位置在它们应该在的位置。

特别是,请注意 cairo 样式的绘图,其中将线条放置在半像素位置,以便它们精确填充一行像素。

驱动程序问题。新的渲染器以新的和不同的方式使用图形驱动程序,因此可能会触发该方面的问题。

请针对 GTK 提交您看到的问题,即使它们看起来像是驱动程序问题,因为对于我们来说,了解新代码在各种驱动程序和硬件上的工作效果如何(或有多糟糕)很有用。

但它更快吗?

不,新的渲染器(尚未)没有更快。

旧的 GL 渲染器针对速度进行了高度优化。它还使用更简单的着色器,并且不进行抗锯齿等功能所需的计算。我们希望最终使新的渲染器更快,但是新的功能和正确性使它们非常令人兴奋,即使在我们达到该目标之前也是如此。所有基于 GPU 的渲染器都足够快,可以以 60 或 144 fps 的速度渲染今天的 GTK 应用程序。

话虽如此,在一些非科学的基准测试中,Vulkan 渲染器已经接近匹配甚至超越了旧的 GL 渲染器。新的 GL 渲染器由于我们尚未追踪到的原因而速度较慢。

新默认值

在刚刚发布的 4.13.6 快照中,我们已将 ngl 渲染器设为新的默认值。这是一个试探性的措施 — 渲染器需要通过不同的应用程序进行更广泛的测试,以验证它们是否已准备好投入生产。如果出现重大问题,我们可以将 4.14 恢复为 gl 渲染器。

我们决定暂时不将 Vulkan 渲染器设置为默认值,因为它在一些应用程序集成方面落后于 GL 渲染器:webkit GTK4 端口适用于 GL,而不适用于 Vulkan,GtkGLArea 和 GtkMediaStream 目前都生成 Vulkan 渲染器无法直接导入的 GL 纹理。所有这些问题有望在不久的将来得到解决,然后我们将重新考虑默认渲染器的决定。

如果您在非常旧的硬件上使用 GTK,您可能更喜欢旧的 GL 渲染器,因为它对 GPU 的要求较低。您可以使用 GSK_RENDERER 环境变量覆盖渲染器选择

GSK_RENDERER=gl

未来的计划和可能性

新的渲染器是实现我们长期以来一直想拥有的功能的良好基础,例如

  • 正确的颜色处理(包括 HDR)
  • 在 GPU 上进行路径渲染
  • 可能包括字形渲染
  • 在主线程外渲染
  • 性能(在旧的和性能较低的设备上)

其中一些将是我们近期和中期工作的重点。

总结

新的渲染器具有一些令人兴奋的功能,而且未来还会有更多功能。

请尝试使用它们,并告诉我们哪些对您有效,哪些对您无效。

介绍图形卸载

GTK 团队的一些成员在过去一个月左右的时间里,一直在探索 Linux 内核图形 API 的世界,特别是 dmabuf。我们带着一些挫折和一些成功从这次冒险中归来。

什么是 dmabuf?

dmabuf 是内核空间中的一个内存缓冲区,由文件描述符标识。其理念是,您不必复制大量的像素数据,而只需在内核子系统之间传递文件描述符。

当然,现实比这美好的景象更复杂:内存可能是设备内存,其访问方式与“普通”内存不同,并且可能存在多个缓冲区(和多个文件描述符),因为图形数据通常会分割成多个平面(例如,RGB 和 A 可能是分开的,或者 Y 和 UV)。

为什么 dmabuf 有用?

我已经提到,我们希望避免复制像素数据,并将其馈送到 GTK 合成管道中(对于 4K 视频,这可能是每帧相当多的数据)。

这种优化很重要的用例是那些长时间显示频繁变化的内容的情况,例如

  • 视频播放器
  • 虚拟机
  • 流媒体
  • 屏幕录制
  • 游戏

在最佳情况下,如果合成器支持直接扫描输出并且 dmabuf 适合,我们或许还可以避免将数据馈送到合成器的合成管道中。特别是在移动系统上,这可以完全避免使用 GPU,从而降低功耗。

详细信息

自 4.0 版本以来,GTK 已经在使用 dmabuf:在合成帧时,GTK 将所有渲染节点(通常每个小部件有多个)转换为 GL 命令,将其发送到 GPU,然后 mesa 将生成的纹理导出为 dmabuf 并将其附加到我们的 Wayland 表面。

但是,如果您的 UI 中唯一变化的内容是已经在 dmabuf 中的视频内容,那么最好避免通过 GL 进行绕行,只需将 dmabuf 的文件描述符直接传递给合成器即可。

Wayland 具有子表面的概念,允许应用程序将其一些合成需求推迟到合成器:应用程序将缓冲区附加到每个(子)表面,而合成器的工作是将它们组合在一起。

通过现在 git main 中的内容,GTK 将根据需要创建子表面,以便将 dmabuf 直接传递给合成器。我们可以通过两种不同的方式来实现:如果没有任何内容绘制在 dmabuf 之上(没有圆角或覆盖的控件),那么我们可以将子表面堆叠在主表面之上,而无需更改任何视觉效果。

这是理想的情况,因为它使合成器能够设置直接扫描输出,从而为我们提供了从视频解码器到显示器的零拷贝路径。

如果有些内容绘制在视频之上,我们可能无法实现这一点,但我们仍然可以通过将带有视频的子表面放置在主表面下方并在主表面上戳一个半透明的孔来让它透出来,从而获得让合成器进行合成的好处。

圆形播放按钮是此处强制将子表面放置在主表面下方的因素。

GTK 会为每一帧自动透明地选择这些模式,而无需应用程序开发人员执行任何操作。一旦播放按钮出现在帧中,我们会将子表面放置在下方,一旦视频被圆角剪裁,我们将完全停止卸载。当然,卸载的优势也会消失。

GTK 检查器中的图形卸载可视化会显示这些变化

最初,相机流不会被卸载,因为圆角会对其进行裁剪。洋红色轮廓表示该流已卸载到主表面下方的子表面(因为视频控件位于其顶部)。金色轮廓表示子表面位于主表面之上。

如何使用?

GTK 4.14 将引入一个 GtkGraphicsOffload 小部件,其唯一的工作是提示 GTK 应尝试通过将其附加到子表面而不是像通常那样让 GSK 处理其子小部件的内容来卸载其子小部件的内容。

为了创建适合卸载的内容,新的 GdkDmabufTextureBuilder 将 dmabuf 封装在 GdkTexture 对象中。dmabuf 的典型来源是 pipewire、video4linux 或 gstreamer。在即将发布的 1.24 版本中,gstreamer 中的 dmabuf 支持将更加稳定。

在测试此代码时,我们使用了 Georges Basile Stavracas Neto 的 pipewire 的 GtkMediaStream 实现,该实现可以在 pipewire-media-stream 和 Christian Hergert 和 Bilal Elmoussaoui 的 libmks 中找到。

有哪些限制?

目前,图形卸载仅在 Linux 上的 Wayland 上有效。我们或许可以在 MacOS 上实现类似的功能,但目前,这仅限于 Wayland。它还取决于内容是否在 dmabuf 中。

想要利用这一点的应用程序需要配合,并避免执行干扰子表面使用的事情,例如将视频内容的角变圆。GtkGraphicsOffload 文档为开发人员提供了有关约束以及如何调试图形卸载问题的更多详细信息。

总结

GTK 4.14 版本将为媒体播放带来一些有趣的新功能。您可以使用刚刚发布的 4.13.3 快照立即尝试。

请尝试一下,并告诉我们哪些对您有效,哪些对您无效。

GTK 中的路径,第 2 部分

在本系列的第一部分中,我们介绍了路径的概念,并研究了如何创建 GskPath。但是,路径的含义不止于此。

路径点

路径的许多有趣属性会随着您沿路径的轨迹移动而变化。要查询此类属性,我们首先需要一种方法来确定我们感兴趣的路径上的点。

GTK 为此目的提供了 GskPathPoint 结构,并提供了许多函数来获取它们,例如 gsk_path_get_closest_point(),它允许您查找路径上最接近给定点的点。

获得 GskPathPoint 后,您可以查询该点路径的属性。最基本的属性是位置,但您也可以获得切线、曲率或距路径起点的距离。

输入

在用户界面中使用路径时,另一个有趣的问题是

鼠标指针是否悬停在路径上?

如果要突出显示指针悬停的路径,或者要响应用户单击路径,则需要此问题的答案。

对于填充的路径,GTK 通过 gsk_path_in_fill() 方法提供答案。

对于描边路径,提供 100% 准确的答案要复杂得多(特别是,如果描边使用虚线图案),但我们可以提供一个通常足够好的近似答案:如果到路径上最近点的距离小于线宽的一半,则该点位于描边内部。

展望

本系列的下一部分将介绍使用路径进行渲染。

GTK 中的路径

我们希望摆脱 cairo 作为 GTK 中的绘图 API 已经不是什么秘密了,这样我们就可以将更多的绘图移到 GPU 上。

虽然人们已经找到了使用渲染节点绘制事物的创造性方法,但它们没有提供像 Skia 或 cairo 这样的全面的绘图 API。这是一种非常令人不满意的状态。

几年前,我们开始研究如何改变这种情况,方法是将路径作为 GTK 中的一等对象提供。这项努力终于开始取得成果,您可以在 GTK 4.13.0 中看到初步结果。

路径

那么,什么是路径?一个粗略的定义可以是

一系列线段或曲线,它们的端点可能连接也可能不连接。

当我们说曲线时,我们特指二次或三次贝塞尔曲线。在 cairo 之上,我们还支持有理二次贝塞尔曲线(或者 Skia 称之为:圆锥曲线),因为它们可以让我们精确地模拟圆形和圆角矩形。

这张图显示了一个典型的路径,由 4 条曲线和 2 条直线组成,其中一些是连接的。正如您所看到的,路径可以是闭合的(如这里的 4 条曲线)或开放的(如 2 条直线),具有起点和终点。

那么,路径在绘图中有什么用呢?首先,您可以使用路径来定义一个区域(路径内部的部分),并用颜色、渐变或更复杂的内容填充它。

或者,您可以使用各种属性(如线宽、颜色或虚线图案)描边路径。

GTK 中的路径

我们在 GTK 中用于路径的对象是 GskPath。它是一种紧凑、不可变的表示形式,针对渲染进行了优化。要创建 GskPath,您需要使用 GskPathBuilder,它有许多方便的方法来创建路径,无论是从单独的曲线还是从预定义的形状创建。

此示例创建一个闭合三角形的路径

builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 0, 50);
gsk_path_builder_line_to (builder, 100, 50);
gsk_path_builder_line_to (builder, 50, 0);
gsk_path_builder_close (builder);
path = gsk_path_builder_free_to_path (builder);

这个例子创建一个具有给定中心和半径的圆形路径

builder = gsk_path_builder_new ();
gsk_path_builder_add_circle (builder, center, radius);
path = gsk_path_builder_free_to_path (builder);

展望

在下一篇文章中,我们将研究路径的属性以及如何查询它们。