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 快照立即尝试。
请尝试一下,并告诉我们哪些对您有效,哪些无效。