本文是《深入Linux GPU AMD GPU渲染与AI技术实践》读书笔记,以通俗简洁的方式重述知识。
干货长文预警,建议您做好准备。
关键词: Linux GPU 图形栈



导读

书接上文,我们已经讲完了图形API从接受应用程序发出的绘图命令,到向显卡发出具体指令的理论过程。本章内容将从Mesa的实现出发,看看这个过程在Linux中是如何实现的。
如果说前文讲的是标准,那么本章主要关注标准的实现

Mesa

Mesa是一个在开源环境下实现多种图形和计算 API 规范的软件库
核心角色: 它是 Linux、FreeBSD、Android 等系统上事实上的 OpenGL 和 Vulkan 驱动实现

Mesa 实现了 Khronos Group(科纳斯组织)发布的许多标准,包括:
OpenGL (所有版本,包括桌面版和 OpenGL ES 移动版)
Vulkan (作为可安装客户端驱动程序 ICD)
OpenCL (计算 API)
EGL (用于窗口系统接口的 API)
VDPAU/VA-API (视频加速 API)

简单来说,当一个 Linux 应用程序调用 glDraw*vkCreate* 函数时,它实际上是在调用 Mesa 库中的代码。

架构与组件

Mesa 的架构是分层的,主要由API 前端、核心抽象层和硬件后端组成。
简而言之,Vulkan调用走ICD,OpenGL调用走Gallium3D框架。

API 前端 (State Trackers / ICDs)

这层是面向应用程序的,负责解析上层 API 的调用。
因为OpenGL是基于状态机的,所以需要State Tracker隐式维护状态机。

  • State Trackers (状态跟踪器): 负责 OpenGL 和 OpenCL 等 API。它们将 API 调用(如状态设置、数据绑定、绘制指令)转换为 Mesa 内部的通用数据结构和命令。
  • Vulkan ICDs (可安装客户端驱动): Vulkan 驱动在 Mesa 中是独立实现的,如:
    • RADV: 针对 AMD Radeon GPU 的 Vulkan 驱动。
    • ANV: 针对 Intel GPU 的 Vulkan 驱动。

硬件后端 (Drivers)

这层是与特定 GPU 硬件交互的代码,向上提供抽象层所需的接口,向下生成控制硬件的命令。

驱动名称 对应硬件 抽象层
Iris / Crocus Intel Gen8+ GPU Gallium3D
RadeonSI AMD Radeon GCN/RDNA 架构 Gallium3D
RADV AMD Radeon GCN/RDNA 架构 Vulkan ICD
ANV Intel Gen8+ GPU Vulkan ICD
Nouveau NVIDIA GPU (性能受限) Gallium3D

4. 与内核的交互 (libdrm)

Mesa 位于用户空间,但 GPU 硬件控制、内存分配和命令提交等关键任务必须在内核空间完成。
libdrm 负责将 Mesa 生成的硬件命令和内存请求,通过系统调用传递给 Linux 内核中的 DRM 模块(例如:amdgpui915 驱动)。

Gallium3D

Gallium3D 是 Mesa 3D 图形库内部的一个图形驱动程序架构/框架。
它试图定义一套通用的、面向硬件的接口,让所有 OpenGL 驱动程序(无论是针对 AMD、Intel 还是其他硬件)都实现这套相同的接口。这样编写新的硬件驱动时,只需要编写新的硬件后端,而无需重新实现整个 OpenGL 状态机和优化逻辑。
简而言之,就是在硬件与OpenGL之间提供一层抽象,将硬件实现与OpenGL的逻辑解耦。

组件

State Trackers (状态跟踪器)

  1. 负责接收应用程序的 gl* 调用。
  2. 维护 OpenGL 的状态机(如当前的颜色、混合模式、深度测试设置等)。
  3. 负责将特定的 API(如 OpenGL、OpenCL、DirectX 9)的调用,转换为 Gallium3D 内部定义的通用图形原语和命令。
  4. 它将这些 Gallium3D 抽象接口可以理解的调用传递给下一层。

Hardware Drivers (硬件驱动/后端)

  1. 针对特定的 GPU 硬件,实现 Gallium3D 抽象层定义的所有接口。
  2. 负责与 libdrm 通信,并将 Gallium3D 抽象命令最终翻译成 GPU 硬件能理解的指令集和命令缓冲区。

工作过程

Gallium3D 的核心是定义了一个面向服务的架构,将复杂的 OpenGL 状态机拆解成一系列简单的、可插拔的服务或对象。这个架构的核心是 pipe_contextpipe_screen 接口。

  • pipe_screen (硬件抽象): 负责描述和查询 GPU 的静态能力(如支持的纹理格式、最大缓冲区大小、着色器模型版本等)。每个硬件驱动(后端)都必须实现这个接口。
  • pipe_context (渲染上下文): 负责描述和管理动态渲染状态。OpenGL 的所有绘制和状态设置指令最终都通过这个上下文接口下发。

一、状态跟踪与 API 转换(API 前端)

这是由 Mesa 中的 OpenGL State Tracker 完成的。

  1. 拦截 OpenGL 调用:
    应用程序调用标准的 OpenGL 函数(如 glBegin(), glColor(), glTexImage2D(), glDrawArrays() 等)。
  2. 维护抽象状态:
    State Tracker 不会将每个 gl* 调用立即转发给硬件。相反,它会维护一套抽象的、规范化的 OpenGL 状态
  3. 状态对象化 (State Object Generation):
    Gallium3D 鼓励将大量的 OpenGL 状态(如深度/模板/混合状态、光栅化状态、视口设置)打包成不可变的 Gallium3D 状态对象
    • 例如,OpenGL 中对混合、深度和模板测试的设置,会被 State Tracker 转换为一个统一的 Gallium3D pipe_blend_state 结构体。
  4. 调用抽象接口:
    当状态发生变化或需要绘制时,State Tracker 调用 pipe_context 提供的统一接口,而不是直接调用硬件特定的函数。
    • 设置状态: pipe_context->set_blend_state(pipe_blend_state)
    • 绘制: pipe_context->draw_vbo(...)

二、数据与资源抽象(资源管理)

Gallium3D 对所有图形资源进行了统一抽象,使得驱动程序无需关心 OpenGL 中复杂的资源绑定细节。

  1. 统一资源对象:
    无论是 OpenGL 的纹理、顶点缓冲区还是帧缓冲区,在 Gallium3D 内部都被抽象为通用的 pipe_resource 对象。
  2. 映射与传输:
    应用程序对资源的读写操作(如 glMapBuffer)被转换为 pipe_context->transfer_map()pipe_context->transfer_unmap() 等抽象接口。这允许驱动程序在不同的内存模型(主机内存、设备内存)之间进行高效的数据传输。
  3. 着色器抽象:
    • OpenGL 的 GLSL 着色器首先被编译成 Gallium3D 内部的通用中间表示 (Intermediate Representation)
    • 然后,由硬件后端驱动(如 RadeonSI)将这个中间表示编译成其 GPU 硬件的指令集架构 (ISA)

三、命令翻译与提交(硬件后端)

这是由特定硬件的 Gallium3D 驱动(如 RadeonSI)完成的。

  1. 接收抽象调用:
    驱动后端接收到来自 pipe_context 的抽象 API 调用(如 set_blend_state)。
  2. 翻译成硬件命令:
    驱动后端的核心任务是将抽象状态和命令翻译成该 GPU 硬件能够识别的寄存器设置、命令包 (Command Packet) 和指令流
    • 例如,一个抽象的 pipe_blend_state 对象会被 RadeonSI 驱动翻译成一系列写入 AMD GPU 硬件寄存器的值,以配置其融合单元 (Blend Unit)。
  3. 命令缓冲区生成:
    驱动将这些硬件命令写入一个或多个命令缓冲区
  4. 提交到内核:
    驱动程序通过 libdrm 接口,将完成的命令缓冲区提交给 Linux 内核的 DRM 模块,由内核调度到 GPU 硬件执行。

Vulkan ICD(RADV、ANV)

ICD (Installable Client Driver): 可安装客户端驱动程序。ICD 是 Vulkan API 的实际硬件实现。通常是以共享库(在 Windows 上是 .dll 文件,在 Linux 上是 .so 文件)的形式存在的,它包含了所有用于将 Vulkan API 调用转化为特定硬件指令的代码。

如果系统上有 NVIDIA、AMD 和 Intel 三张显卡,那么安装了三个独立的 Vulkan ICD。

架构

在 Vulkan 的运行时环境中,ICD 位于 Vulkan 加载器 (Vulkan Loader) 之下,位于硬件驱动之上。

Application → API Calls Vulkan Loader → Dispatch Vulkan ICD → Hardware I/O GPU Driver / Kernel \text{Application} \xrightarrow{\text{API Calls}} \text{Vulkan Loader} \xrightarrow{\text{Dispatch}} \text{Vulkan ICD} \xrightarrow{\text{Hardware I/O}} \text{GPU Driver / Kernel} ApplicationAPI Calls Vulkan LoaderDispatch Vulkan ICDHardware I/O GPU Driver / Kernel

Vulkan 加载器 (Vulkan Loader)

加载器(如 Khronos 维护的 v u l k a n vulkan vulkan-loader)是应用程序首次调用 Vulkan 函数时接触到的第一个组件。

功能: 加载器负责:
1. 发现 ICD: 扫描系统路径和注册表,找到所有已安装的 ICD(即,找到系统中所有可用的 GPU 驱动)。
2. 函数分发 (Dispatch): 维护一个巨大的函数指针表。当应用程序调用一个 Vulkan 函数时,加载器通过这个表将调用转发给正确的 ICD 实例。
3. 管理层 (Layers) 插入: 负责将验证层 (Validation Layers) 和工具层插入到调用链中。

Vulkan ICD

ICD 扮演了“翻译家”和“执行者”的角色。

  • 功能: 接收来自加载器的函数调用,并执行将 Vulkan 逻辑转化为 GPU 特定指令的实际工作。

工作原理

ICD 的实现原理基于 Vulkan 的模块化、可扩展和函数指针分发的设计。

函数指针分发机制 (Dispatch Table)

这是 Vulkan ICD 运行的核心机制,它允许 Vulkan API 在运行时动态地绑定到正确的驱动程序函数。

  1. 入口点 (Entry Points): 当应用程序调用一个 Vulkan 函数时,它首先调用的是加载器导出的函数(例如 $vkCreateInstance$)。
  2. 分发调用: 加载器维护着不同层次的分发表 (Dispatch Table)
    • Instance Dispatch Table ( V k I n s t a n c e VkInstance VkInstance): 包含全局函数和实例相关的函数(如层和扩展)。
    • Device Dispatch Table ( V k D e v i c e VkDevice VkDevice): 包含设备相关的函数(如命令提交、管线创建)。
  3. ICD 填充表格: 当加载器初始化一个 ICD 时,ICD 会将自己实现的 Vulkan 函数地址(如 $vkCreatePipeline$ 的驱动版本)填充到相应的分发表中。
  4. 最终调用: 当应用程序调用 $vkCreatePipeline$ 时,加载器通过设备分发表,直接调用 ICD 中对应的实现函数。

ICD 的核心内部工作

ICD 内部的代码负责将抽象的 Vulkan 概念转化为硬件命令。

  1. 对象转换: ICD 接收 Vulkan 抽象对象(如 V k P i p e l i n e VkPipeline VkPipeline V k C o m m a n d B u f f e r VkCommandBuffer VkCommandBuffer),并将其转换为硬件能够识别的私有驱动结构体
  2. 着色器编译: ICD 接收 SPIR-V 格式的着色器代码,并使用驱动内部的编译器将其翻译成 GPU 特定的 ISA (指令集架构)。例如,AMD 的 RADV ICD 会将 SPIR-V 编译成 RDNA/GCN 汇编指令。
  3. 命令缓冲区生成: ICD 将 Vulkan 绘制、调度和传输命令(如 v k C m d D r a w vkCmdDraw vkCmdDraw) 转化为硬件专用的命令流(或称为“环形缓冲区”中的数据包)。
  4. 内核通信: ICD 利用底层的系统接口(如 Linux 上的 libdrm 库)将生成的命令流提交给内核中的 GPU 驱动,最终由内核驱动程序将这些指令送入 GPU 的硬件队列执行。

现实中的ICD

ICD 名称 对应的 Mesa 驱动 目标硬件 特点
RADV Radeon Vulkan AMD Radeon GPU 非常成熟且性能优秀,是 Valve (Steam Deck) 等使用的主要驱动。
ANV Intel ANV Intel GPU 针对 Intel 核显的实现,同样成熟。
V3DV V3D Vulkan Raspberry Pi / Broadcom 针对嵌入式/低功耗硬件的 Vulkan 实现。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐