导读:
- drm/crtc: 关于使用 kthread worker 来实现 non-blocking commit 的讨论
- drm/cma-helper: 默认使用 drm_gem_cma_default_funcs callbacks
- drm/atomic: 让 CRTC & Connector atomic 函数统一使用 drm_atomic_state 参数
- libdmabufheap: 允许 ION 和 DMA-BUF Heaps 混合使用
- Intel 开源 Vulkan 驱动 ANV 已合入对光线追踪的支持
- 关于在 Mesa 中添加 Rust 驱动的提案
- LLVMpipe 已支持 OpenGL 4.5
- Vulkan 1.2.158 新增 VK_KHR_fragment_shading_rate 扩展
DRM
1. drm/crtc: 关于使用 kthread worker 来实现 non-blocking commit 的讨论
熟悉 DRM 框架的小伙伴都知道,DRM atomic commit 操作支持两种模式:blocking 和 non-blocking。顾名思义,blocking commit ioctl 调用会一直等到 CRTC 硬件彻底切换到新的 buffer 上才返回,而 non-blocking commit ioctl 则无需等待硬件更新完成,只需要把 plane 参数提交给底层驱动的 workqueue 就可以立即返回,而真正的硬件更新则由 workqueue 系统调度来完成。blocking commit 主要用于显示的休眠唤醒操作,而 non-blocking commit 则用于显示的内容更新,以便能达到最高刷新率。
但是这里面有个细节,就是用于实现 non-blocking commit 的 workqueue 是 system_unbound_wq,这种 workqueue 是以普通优先级(SCHED_NORMAL)运行在系统上的,且没有绑定到固定的 CPU 上,这样在多核系统上可以实现最大化的调度。但因为它运行在普通优先级上,系统采用的调度器为 CFS (Complete Fair Scheduler) ,在这种调度策略下,每一个线程都有机会得到执行,workqueue 工作的线程则极容易被其它线程抢占,导致 commit_work 执行被打断。这种现象在 Android 系统 中极为常见,因为 Android 中的 SurfaceFlinger 主线程为 RT (Real-Time) 级别,运行在实时优先级(SCHED_FIFO),该优先级高于 SCHED_NORMAL,而且只会被更高优先级的 RT 线程抢占,否则将永远执行下去。因此 SurfaceFlinger 的 RT 线程可以随时抢占 commit work 的执行 线程,这就导致 commit_work() 的执行时间被拉长,最终阻碍了上层 SurfaceFlinger 的合成流程,于是出现丢帧或延迟现象。
为了解决上述问题,Rob Clark (Google Chromium) 提交了一组 Patch,不再使用 system_unbound_wq 来完成 non-blocking commit 操作,取而代之的则是为每个 CRTC 创建一个 kthread worker 来实现同样的行为。kthread worker 与系统自带的 worker 相比:1. 每个 CRTC 都有专属的内核线程来处理 work,多个 CRTC 不用阻塞在一个公共的系统 work 队列中,提高了多屏显示的实时性;2. 用户可以调整 kthread 优先级,以便让 worker 线程运行在 RT 级别,这样 drm commit work 就不会被其它 RT 级别的线程抢占了。但如果将 commit work 默认设置到 RT 级别,则可能会对原来系统中的其它线程造成影响,因此为了降低风险,Rob 决定将 commit work 设置优先级的权利交给上层应用,底层驱动只负责提供设置优先级的能力即可。在 v1 patch 中,commit work 优先级的设置是通过 DRM_CLIENT_CAP_SCHED_MODE 来设置的,而到了 v2 patch 则换成了 “KWORK_TID” 这个 property 来实现,应用程序通过该 Property 来获取当前执行 commit_work 的线程 ID,然后通过 sched_setscheduler() 来设置 SCHED_FIFO 优先级别。
其实当我第一眼看到这个 patch 的时候,我的第一反应是 ———— 这不就和 ADF 一样了嘛!ADF (Atomic Framework Display) 是 Google 于 2013 年推出的一套 kernel 显示框架,转为 Android 系统设计,本质上仍然是 DRM 的阉割版本。因为那时候 DRM 还没有引入 Atomic Modeseting 框架,只适应 PC 平台,无法适应低功耗高实时性的移动设备,于是 Google 取长补短,设计出自己的一套显示框架 ADF,在那时可谓是相当超前的。而 ADF 中也有一个类似于 commit work 的东西,叫 post work,该 work 就是基于 kthread worker 实现的。而从 Android8.0 开始,Google 又抛弃了 ADF,选择 DRM 作为默认的 kernel 显示驱动,这很大一部分原因和 2015 年 DRM 合入 Atomic Modeseting 框架有关。
回到该 patch 上来,目前该 patch 仍然处于 review 阶段,并且 DRM Maintainer Daniel Vetter 似乎对该 patch 不是很满意,来来回回加了不少 comment,看起来这组 patch 离进社区应该还有很长一段路要走,但如果真的合入,将会对 Android 显示性能(尤其是多屏显示)有极大提升。
详情:[PATCH v2 0/3] drm: commit_work scheduling
扩展阅读:
2. drm/cma-helper: 默认使用 drm_gem_cma_default_funcs callbacks
在上一期中报道了 [drm/gem: 全面启用 GEM object functions] 的提交,该提交彻底删除了 drm_driver 中与 prime export 相关的 callbacks,取而代之的则是 drm_gem_object_funcs,该 funcs 会绑定到 drm gem object 上,这是一种面向对象的编程思想,使得代码更加模块化,可读性更强。而本次的 cma-helper 提交则是在前面 patch 的基础上,对 cma helper 的进一步优化。简单说,cma helper 提供了一个默认 drm_gem_object_funcs,即 drm_gem_cma_default_funcs:
static const struct drm_gem_object_funcs drm_gem_cma_default_funcs = {
.free = drm_gem_cma_free_object,
.print_info = drm_gem_cma_print_info,
.get_sg_table = drm_gem_cma_prime_get_sg_table,
.vmap = drm_gem_cma_prime_vmap,
.vm_ops = &drm_gem_cma_vm_ops,
};
但该 funcs 只能通过 drm_gem_cma_create_object_default_funcs() 函数来实现与 drm gem object 的绑定,显得很啰嗦。于是 Thomas Zimmermann( drm-misc maintainer, SUSE)对此做了 cleanup,当 Vendor 驱动在使用 cma helper 时,如果没有实现自己的 drm_gem_object_funcs,则 cma helper 在创建 gem object 时会使用默认的 drm_gem_cma_default_funcs 作为该 object 的 callbacks。这样 vendor 驱动也就不必再啰嗦的调用 drm_gem_cma_create_object_default_funcs() 了,整体代码风格会更加简练。
详情:[PATCH] drm/cma-helper: Make default object functions the default
3. drm/atomic: 让 CRTC & Connector atomic 函数统一使用 drm_atomic_state 参数
DRM 框架中经常会碰到各种 state,如 drm_atomic_state、drm_crtc_state、drm_connector_state、drm_plane_state 等,这些 state 构成了 atomic commit 的核心数据结构,并且它们是随着每次 commit 操作动态创建和销毁的,对于初学者而言不是很好理解,看代码时最容易眼花缭乱。其中 drm_atomic_state 是所有其它 state 的集合,代表了本次 commit 的完整信息。而 crtc/connector/plane state 则代表了分发到单个 object 要设置(或已设置)的硬件参数信息,它们和 drm_atomic_state 通过内部指针紧密联系,因此可以相互之间来回转换。这些 state 作为参数在各种 atomic helper/callbacks 中进行传递,最终实现硬件状态的更新。
Maxime Ripard 为了给树莓派4添加 HDMI HDR10/12 bit 支持的功能,不得不修改 drm core 中 atomic helper/callbacks 的传递参数,因为现有的 atomic callbacks 中都是以 object 所对应的 state 作为参数进行传递的,比如 crtc 的 atomic_enable/disable 是采用 drm_crtc_state 作为入参的,connector 的 atomic_enable/disable 则是以 drm_connector_state 作为入参的,如果开发人员想要在 crtc->atomic_enable() 中访问 drm_atomic_state,理论上只需要通过 drm_crtc_state->state 指针就可以获取到,但实际上是做不到的,因为 crtc->atomic_enable/disable 是在 drm_atomic_helper_swap_state() 之后调用的,而该函数会将 drm_crtc_state->state 指针给清空,这就导致 vc4 驱动无法在 crtc->atomic_enable/disable() 中获取 drm_atomic_state 参数。为了解决这个问题,Maxime 调整了 crtc/connector atomic callbacks 的参数,将原来的 drm_{crtc|connector}_state 入参直接替换成了 drm_atomic_state 参数,然后函数内部再根据各自需要决定是使用 drm_atomic_state 还是 object state。这样做有两个好处:1. 提高了驱动设计的灵活性;2. 统一风格,增强了代码的可读性。该提交已 review 通过,预计将被合入 linux-5.10-rc3。
详情:
- [PATCH v2 1/6] drm/atomic: Pass the full state to CRTC atomic enable/disable
- [PATCH 1/2] drm/atomic: Pass the full state to CRTC atomic_check
- [PATCH 3/3] drm: Use the state pointer directly in atomic_check
- [PATCH] drm: Pass the full state to connectors atomic functions
AOSP
libdmabufheap: 允许 ION 和 DMA-BUF Heaps 混合使用
之前曾在第3期中报道过 kernel mainline 移除 ION 的消息,取而代之的则是 DMA-BUF Heaps。而 Google 将在 AndroidS 中全面启用 DMA-BUF Heap,当然 ION 仍然会被继续支持,它的代码也一直保留在 Google 独立的 kernel common 仓库中。
libdmabufheap 是 Google 实现的一套与 kernel DMA-BUF Heap 交互的 userspace library,为应用层屏蔽了 dma-buf heap 操作的细节。libdmabufheap 除了包含 dma-buf heap 相关的操作接口外,还封装了 ION 的操作接口,用于兼容那些暂时还没有迁移到 dma-buf heap 的 vendor 驱动。如下 patch 让 libdmabufheap 可以混合着使用 ION 和 DMA-BUF 驱动,既可以从 ION 中分配 buffer,又可以从 DMA-BUF Heap 中分配 buffer,极大的方便了 vendor 驱动的过渡。
详情:aosp/libdmabufheap[master]: Allow libdmabufheap to use a mix of ION and DMA-BUF heaps
Mesa
1. Intel 开源 Vulkan 驱动 ANV 已合入对光线追踪的支持
在 10 月份的 XDC2020 大会上,Intel 工程师 Jason Ekstrand 为大家分享了光线追踪(Ray-Tracing)的基本原理以及 Vulkan 新引入的一个跨平台光线追踪扩展 API ———— VK_KHR_ray_tracing。VK_KHR_ray_tracing 是由 NVIDIA、AMD、Intel 等多家巨头联合制定,用于实现跨平台的光线追踪标准 API,它于今年年初被作为临时提案合入到了 Vulkan 标准中。而在 10 月 28 日,Jason 则向 Mesa 开发者社区发送邮件宣布,Intel 开源 Vulkan 驱动 ANV 已正式支持 VK_KHR_ray_tracing 扩展 API,即开发者们可以在最新的 ANV 驱动上使用基于 GPU 的光线追踪功能了。在该邮件列表中,Jason 还回答了一些开发者们比较感兴趣的问题:
- 这些 patch 是否能正常工作?是否有经过测试?
- 这些 patch 能否兼容老的 Intel GPU 硬件?
- 哪些 Intel GPU 支持 ray-tracing?在市面上何时能够买到?
- 关于 ray-tracing 编译器相关的代码目前都被放在了 src/intel 目录下,这些代码能否用于 AMD RADV 或其它 vulkan 驱动?
- 那些已经使用 NVIDIA VK_NV_ray_tracing 扩展的游戏该如何处理?是否有考虑过支持这些游戏?
详情:[Mesa-dev] Landing ray-tracing support in ANV
2. 关于在 Mesa 中添加 Rust 驱动的提案
早前,Linux kernel 社区就有人提出过关于“在 kernel 中添加对 Rust 支持”的想法,并且该想法也得到了 Linus 的认可。而近日,来自 Collabora 公司的 Alyssa Rosenzweig (ARM GPU 开源驱动 Panfrost 的主要开发人员)则在 Mesa 社区发起了一场关于“在 Mesa 中添加 Rust 驱动”的讨论,该讨论引来了众多开发者的关注,讨论也很激烈。以下是来自 Alyssa 的观点:
首先,引入 Rust 的原因并不是因为目前使用 C 实现的 Mesa 代码不够安全,而是因为通过 OpenGL 暴露给用户层的 WebGL 代码不够安全,WebGL 会引入不被信任的代码,这使得 OpenGL 驱动成为了黑客攻击的入口点。而 Alyssa 并不是要号召大家重写整个 Mesa 框架,而是希望对于那些使用 Rust 具有天然优势的驱动则尽量采用 Rust 编写,如编译器后端。她认为将 Rust 用在编译器后端上,最大的好处不是内存安全,而是增强了代码的错误校验与鲁棒性。同时她还对 Rust 代码编译和运行时的依赖条件做了评估,如工具链、标准库等,并且不排除是否需要将 Rust 构建框架 Cargo 集成到 Meson 环境中的可能性。
详情:[Mesa-dev] Rust drivers in Mesa
3. LLVMpipe 已支持 OpenGL 4.5
LLVMpipe 是 Mesa Gallium3D 驱动中基于 LLVM 编译架构实现的一套软件光栅化驱动,得益于强大的 LLVM 编译架构的优化功能,不管是动态的在线编译(如shader),还是静态代码执行(如光栅化),它都能生成最终在目标机上高效执行的机器指令,因此速度相当快,它是 Mesa 推荐的主流 CPU 光栅化驱动。除此之外,它也是 Mesa 中唯一实现了多线程能力的 OpenGL 驱动,因此可以充分利用 CPU 的多核资源。它由 Dave Airlie (Red Hat,DRM Maintainer) 主导开发。
10 月 30 日,Dave Airlie 在社区宣布 LLVMpipe 已正式支持 OpenGL 4.5 标准,并通过了相关的符合性测试。同时他还在 clover/llvmpipe 上跑了一遍 OpenCL 3.0 的符合性测试 case,并期望后期能够对其进一步完善。他的下一步计划则是将 LAVApipe 驱动实现 Vulkan 1.0 的 patch 提交到 Mesa 中,目前 LAVApipe 已经通过了 Vulkan 1.0 99% 的 CTS 测试 case,剩余一些采样精度以及 snorm blending 相关的 fail 项需要解决。
详情:[Mesa-dev] llvmpipe is OpenGL 4.5 conformant.
Vulkan
Vulkan 1.2.158 新增 VK_KHR_fragment_shading_rate 扩展
随着 GPU 硬件性能的不断增强,目前市面上已经开始出现支持可变着色率 (Variable Rate Shading) 的 GPU,如 NVIDIA Turing/Ampere 架构的 GPU。所谓可变着色率是指 fragment shader 在工作时不再是按每个像素单独执行一次着色程序,而是将多个像素当成一个像素来执行着色程序,并将执行的结果统一返回个这几个像素。因此对于那些大面积相同色块的场景而言,可以极大的提高 GPU 渲染的效率。
针对以上这种能力的 GPU 硬件,vulkan 1.2.158 引入了 VK_KHR_fragment_shading_rate 扩展,该扩展使得应用程序可以通过设置 fragment shader 的着色率来灵活控制 GPU 渲染的效率。例如对于一个复杂的场景,我们可以根据场景内容将渲染目标划分成不同区域,那些需要大量细节的区域则采用普通的单像素着色,而那些不太需要大量细节的区域,可以根据色块面积选择不同的着色率,如每 4x4 个像素执行一次 fragment shader,或者 8x8 个像素执行一次 shader。该扩展新增 vkCmdSetFragmentShadingRateKHR() 和 vkGetPhysicalDeviceFragmentShadingRatesKHR() 接口,允许用户设置最大 16x16 像素着色率,如果设置的着色率不支持,则会自动选用最接近的着色率。
随着 VK_KHR_fragment_shading_rate 扩展一起添加的,还有 SPIR-V 的 SPV_KHR_fragment_shading_rate 扩展,该扩展允许设置/查询某个 fragment shader 的着色率。