图形卸载简介

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

什么是 dmabuf?

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

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

为什么 dmabufs 有用?

我已经提到,我们希望避免复制像素数据并将其输入到 GTK 合成管道中(对于 4k 视频,每个帧的数据量可能相当大)。

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

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

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

细节

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

但是,如果您的 UI 中唯一更改的内容是已经存在于 dmabuf 中的视频内容,那么最好避免通过 GL 的迂回方式,而是通过为 dmabuf 提供文件描述符,直接将数据交给合成器。

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

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

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

如果有一些内容被绘制在视频的顶部,我们可能无法实现这一点,但是我们仍然可以通过将包含视频的子曲面放置在主曲面的下方,并在主曲面上戳一个半透明的孔以使其透视,从而获得让合成器进行合成的好处。

圆形的播放按钮是强制子曲面放置在主曲面下面的原因。

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

GTK 检查器中的图形卸载可视化会显示这些更改的发生

最初,摄像机流没有卸载,因为圆角会对其进行裁剪。洋红色轮廓表示流已卸载到主曲面下方的子曲面(因为视频控件位于其上方)。金色轮廓表示子曲面位于主曲面的上方。

如何使用?

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

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

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

有哪些限制?

目前,图形卸载仅在 Linux 上的 Wayland 上可用。我们有望在 MacOS 上实现类似的功能,但目前,这仅适用于 Wayland。它还取决于内容是否在 dmabufs 中。

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

总结

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

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