Linux的渲染与显示系统链路

Linux的渲染与显示系统链路

前言:

作者之前一直在做跨平台、全平台SDK(PC、移动端、web等),以前由于业务等在Linux上需求不是太多,所以在这一块的研究与总结较少。现在正好有一个实际来实现与总结Linux系统上的渲染与显示链路,包括linux内核中的各种各样的名称与概念。

传统Linux图形栈(X11/Wayland)的OpenGL实现存在两大痛点:

  • 权限控制颗粒度粗:X Server作为中间层持有所有GPU资源,应用只能通过间接请求获取渲染权限。
  • 合成器性能损耗:Wayland合成器需要额外拷贝帧数据,难以满足低延迟渲染需求。

EGL on DRM(或 DRM/KMS + EGL 直连)应用直接打开 DRM 设备、GBM 分配 Buffer、EGL 绑定 DRM 显示surface,opengl/opengles/vulkan等图形API渲染绘制帧,最后直接刷帧到 KMS上屏。

优点:

一、消耗低(X11、Wayland各种服务与进程消耗资源高)

二、链路短、延迟低:

Wayland链路:App → EGL → Mesa → 共享Buffer → 合成器拷贝/合成 → KMS

DRM+GBM+EGL 直出链路:App → EGL → GBM分配BO → DRM/KMS直接提交页翻转(PageFlip)

缺点:

一、只适用于单设备独占屏显示,KMS 的 CRTC / 显示输出同一时间只能由一个进程打开占用。

二、很原始没有生态工具,没有窗口组件,输入要全套接管,没有操作系统级别的图形兜底,比如开机、关机、休眠后唤醒等系统操作都要实现并且要与BSP、操作系统等衔接好。

窗口系统-X11:

GLX是OpenGL Extension to the X Window System的缩写。它作为x的扩展,是x协议和X server的一部分,已经包含在X server的代码中了。GLX提供了xwindow system使用的OpenGL接口,允许通过x调用OpenGL库。OpenGL 在使用时,需要与一个实际的窗口系统关联起来。其实现在GLX已经基本上很少使用了因为GLX必须依赖X11服务上,在不同平台上有不同的机制以关联窗口系统,在Windows上是WGL,在Linux上是GLX,在 Apple OS上是AGL等。

EGL则是OpenGL ES在嵌入式平台上(WGL,GLX,AGL)的等价物。EGL假设OS会提供窗口系统,但EGL与平台无关,并不局限于任何特定的窗口系统,所有用到本地窗口系统的地方都用屏蔽指针来处理,我觉得这就是它易于移植的关键。

X Window System(常简称 X11 或 X)是类 Unix 平台经典的窗口系统,采用 C/S(Client/Server)架构,X11 中的 X 指的就是 X 协议,11 指的是采用 X 协议的第 11 个版本。该架构天然具备网络透明性(Network Transparency),X 协议由 X server 和 X client 组成,配套组件包含窗口管理器(Window Manager)与显示管理器(Display Manager)。

核心架构组件

  • X Client(X 客户端)即各类图形应用程序(如 gedit、Firefox、终端模拟器等)。Client 本身不直接操作显示硬件,而是通过 X11 协议向 X Server 发送绘图指令(Drawing Requests)与输入请求。
  • X Server(X 服务端)运行在本地(或直接连接显示设备的机器上),是硬件资源的唯一管理者。其核心职责包括:
  • 资源管理:接管显卡、显示器、键盘、鼠标等 I/O 设备;
  • 渲染输出:解析 Client 的协议请求,完成像素级渲染并输出到屏幕;
  • 事件分发:将硬件输入事件(按键、鼠标移动等)路由回传给对应的 X Client。

Linux 本身是没有图形化界面的,所谓的图形化界面系统只不过中是Linux 下的应用程序(跟Win哲学不一样,win是图形界面写到内核中去捆绑在一起)。这一点和 Windows 不一样。Windows 从Windows 95 开始,图形界面就直接在系统内核中实现了,是操作系统不可或缺的一部分。

许多时候 X server 和 X client 在同一台主机上,台式机这看起来没什么。但是X server 和 X client 完全可以运行在不同的机器上,只要彼此通过 X 协议通信即可。于是,我们就可以做一些"神奇"的事情,比如像本文上面谈到的,在本地显示 (X server),运行在服务器上的 GUI 程序 (X client)。这样的操作可以通过 SSH X11 Forwarding (转发) 来实现。

窗口系统-Wayland

Wayland是轻量级窗口系统(无X Server中间层,由Compositor直接管理窗口合成)。Wayland原生支持EGL,流程更简洁表现在: X11 里角色是拆开的:X Server(管绘图原语、窗口树、输入)+ Window Manager(独立的普通客户端,管装饰和布局)+ Compositor(又是另一个客户端,借用 Composite 扩展把窗口重定向到离屏 pixmap 再合成上屏)。三者之间靠 X protocol 互相通信,全局状态一大堆。Wayland 把这三者合并成一个 Compositor:它既是显示服务器,又是窗口管理器,又是合成器。

一句话:Wayland是省掉了 X11 里那次 Composite 重定向 + 二次合成

Wayland 的渲染方式:

在 Wayland 架构中,客户端负责窗口内容的渲染,可以使用软件渲染或硬件渲染(如 OpenGL)。客户端将渲染结果存储在缓冲区中,并通知合成器使用该缓冲区进行屏幕合成。

Wayland 更安全:X11 假设所有客户端可信( 任何应用都可以注册全局键盘钩子),任意程序可监听/注入他人键鼠事件、截取全屏,键盘记录器几乎零门槛,所以在X11下实现录屏与共享桌面非常容易(跟linux的安全哲学不符合的)。

输入隔离:在 Wayland 中,只有 Compositor 知道哪个窗口获得了焦点。Compositor 只会把键盘事件发送给当前获得焦点的那个 Client。其他后台程序无法窃听的按键(做了隔离)。

内容隔离: 个客户端无法直接读取另一个客户端的窗口内容(无法随意截屏)。如果需要截屏,必须通过 Compositor 提供的特定 Portal 接口(如 XDG Desktop Portal),这通常会弹出一个系统级的确认框询问用户是否允许。

要想了解更多详细的Wayland 内容可以查看如下的文章:万字长文详解 Wayland 协议、架构-CSDN博客

EGL

EGL 是 Khronos Group 制定的跨平台图形标准接口,核心定位是作为 OpenGL ES、OpenVG 等渲染 API 与操作系统原生窗口系统之间的适配桥梁。

OpenGL 及 OpenGL ES 作为纯图形渲染 API,仅负责通过驱动向 GPU 下发渲染指令、控制图形渲染管线状态机的运行逻辑,本身不具备与原生窗口系统、显示硬件交互的能力。在 EGL 出现之前,不同平台各自实现了专属的渲染桥接层 ------ 例如 X11 平台的 GLX、Windows 平台的 WGL,均与特定系统深度绑定;而 EGL 以平台无关的设计目标,为渲染 API 对接原生窗口系统提供了统一的标准化方案。

EGL 提供了以下核心机制:

  • 对接设备原生窗口系统,管理显示连接的建立与销毁
  • 查询绘图表面的可用类型,配置帧缓冲像素格式与参数
  • 创建并维护可绘制表面,作为渲染输出的载体
  • 在 OpenGL ES 与其他图形渲染 API 之间实现渲染时序同步
  • 管理纹理贴图等共享渲染资源,支撑多上下文、多 API 资源交互

为了让OpenGL ES能够绘制在当前设备上,我们需要EGL作为OpenGL ES与设备的桥梁。

EGL API 是独立于 OpenGL ES 各版本标准的独立的一套 API,其主要作用是为 OpenGL 指令 创建 Context 、绘制目标 Surface 、配置 FrameBuffer 属性、Swap 提交绘制结果 等。

EGL 中大部分函数成功时都是返回 EGL_TRUE,失败返回 EGL_FALSE ;具体错误类型可通过 eglGetError() 接口查询。

创建:建立本地系统和 OpenGL ES 的连接 displayId 指定显示连接,一般使用默认的 EGL_DEFAULT_DISPLAY,即返回与默认原生窗口的连接。

EGL 三大核心抽象

EGL 渲染环境的构建围绕三个核心对象展开:

a:Display(EGLDisplay) 是对实际显示设备的抽象,代表 EGL 与原生窗口系统的一个连接实例。初始化的第一步便是建立与本地系统的显示连接,通常调用 eglGetDisplay() 获取句柄,传入 EGL_DEFAULT_DISPLAY 即可关联系统默认显示设备。

b:Surface(EGLSurface)是对用来存储图像的内存区域FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer,是 OpenGL ES 渲染指令的直接绘制目标。

c:Context (EGLContext) 存储 OpenGL ES 渲染管线全部状态信息的上下文容器,涵盖当前着色器程序、纹理绑定状态、顶点数组配置、渲染参数等所有运行时状态。EGLContext 必须绑定到有效 EGLSurface 上,才能正常执行渲染指令。

内核之DRM&KMS&GDM:

在 Linux 图形显示栈中,DRM、KMS 与 GDM 分处内核态 与用户态的不同层级,串联起从硬件驱动到用户登录会话的完整链路:

  • DRM(Direct Rendering Manager):内核级图形子系统,统一管理 GPU 显存、渲染命令调度与设备访问,是整个图形栈的基石。
  • KMS(Kernel Mode Setting):DRM 的显示分支,以 framebuffer、plane、CRTC、encoder、connector 等对象抽象输出管线,分辨率、刷新率等显示模式的配置与提交。
  • GDM(GNOME Display Manager):用户态显示管理器,负责图形登录界面的呈现、PAM 身份认证,以及用户会话的创建、切换与销毁。

三者的调用关系是严格分层的:内核层 DRM/KMS 提供硬件抽象 → 用户态显示服务器(Mutter、Xorg 等)封装驱动接口并独占 DRM Master → GDM 拉起并管理显示服务器会话,在其上实现登录交互与会话生命周期管理。

DRM (Direct Rendering Manager)

DRM的核心功能模块

模块 功能描述
GEM (Graphics Execution Manager) GPU 内存管理器,负责显存分配、缓冲对象 (BufferObject) 管理、内存映射与释放,是硬件加速渲染的基础
KMS (Kernel Mode Setting) 内核态显示模式设置,接管显示输出的全流程配置
PRIME / DMA-BUF 跨设备缓冲共享机制,支持 GPU 与其他硬件(如摄像头、编解码器)之间零拷贝数据传输
GPU 调度器 统一管理 GPU 命令提交、任务队列与优先级调度(drm_gpu_scheduler)
同步原语 Fence、VBlank 等同步机制, 解决的是"CPU 和 GPU 谁等谁、什么时候等"的时序问题,目的是对齐渲染节奏与显示刷新,避免撕裂和卡顿。保证渲染与显示时序一致,避免画面撕裂

设备节点与权限模型:

DRM 通过 /dev/dri/ 目录下的设备节点向用户态暴露接口:

  • /dev/dri/cardX:主设备节点(Primary Node),用于模式设置与特权操作,只有 DRM Master 进程(只有一个)可执行 KMS 配置。可以是card0 card1 、card2等
  • /dev/dri/renderDX:渲染节点(Render Node),非特权渲染接口,普通用户可直接调用进行 3D 渲染、视频编解码等操作,无需 DRM Master 权限。

KMS (Kernel Mode Setting)

KMS 是 DRM 框架内专门负责显示输出的子系统,将原本运行在用户态的显示模式配置逻辑移入内核,由内核统一管理显示硬件资源,实现:

对象 本质 核心职责 关键约束
Framebuffer 一块像素存储 承载像素数据,绑定 format / modifier / stride / 宽高,作为 Plane 的数据源 对一块显存的视图/元数据描述(drm_framebuffer),底层可以来自 GEM BO,也可以是导入的 dma-buf。
Plane 硬件图层 从 Framebuffer 取像素,决定位置、尺寸、缩放、Z 序、透明度 分 Primary(基础画面)、Cursor(硬件鼠标)、Overlay(视频/UI 叠加);多 Plane 可挂同一 CRTC 由硬件合成
CRTC 显示管线调度器 生成像素时钟与 HSync/VSync 时序,决定分辨率与刷新率,驱动扫描输出 数量决定可同时驱动的独立显示管线数
Encoder 信号编码器 把 CRTC 的并行像素时序转成 HDMI/DP/VGA 的串行电气信号 硬件中间层,用户态一般不直接配置,由驱动自动路由
Connector 物理端口抽象 检测热插拔、读取 EDID、枚举支持的显示模式 Connector 通过一个 Encoder 连接到 CRTC;一个 Connector 在硬件上可能支持多个可选 Encoder,但同一时刻只使用一个。

GDM (GNOME Display Manager)

定位与职责:GDM 是 GNOME 桌面环境的官方显示管理器,属于用户态系统服务,运行在 DRM/KMS 与显示服务器之上,核心职责包括:

  • 提供图形化登录界面(Greeter),处理用户身份认证
  • 管理 X11 与 Wayland 显示会话的启动、切换与销毁
  • 控制用户登录后的桌面环境加载流程
  • 支持多用户切换、远程桌面接入、自动登录等高级功能

Mesa:

在linux的图形就不得不提起:Mesa(全称 Mesa 3D Graphics Library)是一个开源的用户空间图形库,它的核心作用是在操作系统中实现各种图形 API,比如 OpenGL、Vulkan、OpenGL ES、EGL,以及早期的 Direct3D 9(通过 Gallium Nine)等。简单来说,当你的程序调用 glDrawArrays 或 vkQueueSubmit 时,真正把这些高级 API 指令翻译成 GPU 能理解的硬件命令的,往往就是 Mesa。

支持各大平台,其中linux上是主流的开源图形栈,Windows 和 macOS 上也能跑 Mesa,但市场占有率很低------不过这并不妨碍 Mesa 的软光栅实现(当然不妨碍Mesa的软光栅LLVpipe、Lavapipe实现vulkan、opengl在win上做兜底方案)

Linux 上开源显卡驱动的用户态部分UMD,内核是 DRM,Mesa 运行在用户空间,是图形 API 与内核图形驱动之间的转换层。

UMD(User Mode Driver,用户模式驱动):位于 Mesa 中,负责 API 状态机、着色器编译等。

KMD(Kernel Mode Driver,内核模式驱动):位于 Linux 内核中,如 amdgpu、i915 等,负责硬件资源管理。

Mesa的软件架构:

Mesa 内部有两套驱动框架目前主流的开源驱动基本都基于 Gallium3D。Mesa Classic:经典驱动架构,各驱动独立实现较多逻辑

Gallium3D:现代驱动架构,多个驱动共享通用中间层,便于维护和功能统一。

在编译管线中,Mesa 还涉及多种中间表示(IR),整个流程可以理解为:高级着色器语言->某IR(GLSL IR/SPIR-v)->驱动特定优化->GPU机器码。

Mesa 的两套纯软光栅实现

Mesa 内置的两套软件光栅化器,都是纯 CPU 实现+SIMD(加速)、不依赖任何 GPU 硬件,作为无显卡 / 驱动异常时的兜底方案:

  • llvmpipe:面向 OpenGL / OpenGL ES 的软件渲染器,基于 LLVM JIT 编译着色器,利用CPU SIMD指令加速,是目前 Linux 无 GPU 环境下的标准 GL 兜底实现,替代了更早的 softpipe。
  • lavapipe:面向 Vulkan 的软件渲染器,可理解为 llvmpipe 的 Vulkan 版本,同样基于 LLVM 后端,用于在无 GPU 环境下兼容 Vulkan 应用。

对于PC端:

Intel:全面拥抱 Mesa 主线;( Intel 核显(UHD/Iris Xe)的官方主力驱动就是 Mesa 主线的开源实现)

AMD:Mesa 开源驱动为消费级主流

NVIDIA:开源社区驱动为 Nouveau,NVIDIA 专有驱动则绕过 Mesa,提供独立的 OpenGL/Vulkan 实现

Home --- The Mesa 3D Graphics Library

参考资料:

https://www.cnblogs.com/yongdaimi/p/11244950.html

https://blog.csdn.net/afei__/article/details/84867104

https://blog.csdn.net/u011330638/article/details/81591490?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=1619753972152_78156&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

OpenGL DRM实战:从零构建安全渲染管线的避坑指南

OpenGL与X11/Wayland/DRM/KMS/EGL的关系_opengl x11-CSDN博客

DRM显示系统分析

Linux 图形栈全景解析:从 OpenGL 到 DRM/KMS 的完整链路_drm opengl-CSDN博客

万字长文详解 Wayland 协议、架构-CSDN博客

相关推荐
lisanmengmeng33 分钟前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
若亦_1 小时前
Java中关于this的三种用法
java·开发语言·intellij-idea
山甫aa1 小时前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
软件技术NINI1 小时前
基于SpringBoot和Vue的博物馆预约平台管理系统的设计与实现
java·毕业设计·毕设
Android系统攻城狮1 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
独泪了无痕11 小时前
探索Pinyin4j-Java中的拼音转换利器
java·后端
道尔柯南12 小时前
C++ string 类详解:从常用接口到深拷贝,彻底掌握字符串处理
android·java·c++
唐诺13 小时前
5、Android JNI 动态注册中的“签名“详解
android·jni
kingx313 小时前
Bugku 3122 谷歌小恐龙题解:纯离线逆向 Vault 的 AES-GCM 加密资源(附 Python 解密器)
java·逆向·ctf·bugku·aes-gcm