3. Vblank 机制详解:垂直消隐期的定义、硬件信号产生原理与软件中断处理

Vblank(垂直消隐期)是显示驱动性能优化的核心时间窗口。理解其硬件定义、信号产生机制以及软件中断处理流程,是进行 Vblank 与 DMA-BUF 联合调优的基础。本章将从物理原理到内核实现,逐层剖析 Vblank 机制。

3.1 垂直消隐期的定义与物理意义

在 CRT(阴极射线管)和现代 LCD/OLED 显示技术中,图像的显示是通过逐行扫描完成的。电子束(或等效的数据传输)从屏幕左上角开始,从左到右、从上到下扫描。

  • 水平消隐(Hblank):电子束完成一行扫描后,从右端回到左端开始下一行的时间间隔。
  • 垂直消隐(Vblank):电子束完成整个帧(所有行)的扫描后,从右下角回到左上角开始下一帧的时间间隔。在此期间,没有有效的图像数据被传输到显示面板。

Vblank 期间,显示控制器不读取帧缓冲区,因此这是更新帧缓冲区内容(如提交新帧、翻转 Buffer)的“安全窗口”。如果在 Vblank 期间之外更新正在被读取的缓冲区,会导致 Tearing(画面撕裂)。

关键指标: Vblank 持续时间 = (Vtotal - Vactive) × Htotal × Pixel Clock。例如,1080p@60Hz 的典型 Vblank 时长约为 1.2ms - 1.5ms,这是驱动进行 Buffer 翻转和提交的关键时间预算。
图 3-1:逐行扫描与 Vblank 示意图 电子束 Hblank Vblank 区域(安全更新窗口) 帧结束

3.2 硬件信号产生原理

Vblank 信号由显示控制器(Display Controller)中的时序发生器(Timing Generator)硬件产生。该硬件模块根据设定的显示模式参数(如分辨率、刷新率)生成精确的时序信号。

3.2.1 时序参数与信号生成

参数 描述 典型值 (1080p@60Hz)
Htotal 水平总像素(有效 + 消隐) 2200
Vtotal 垂直总行数(有效 + 消隐) 1125
Vblank Start Vblank 开始的扫描行号 1080
Vblank End Vblank 结束的扫描行号 1124
VSync Pulse 垂直同步脉冲宽度 4 行

硬件计数器(Line Counter)从 0 计数到 Vtotal-1。当计数值达到 Vblank Start 时,硬件自动断言 Vblank 中断信号(IRQ);当计数值达到 Vblank End 时,取消该信号。

图 3-2:Vblank 硬件信号产生时序 Pixel Clock ... Line Counter 0 1 ... 1079 1080 ... 1124 0 Vblank IRQ Vblank 窗口(IRQ 高电平) 触发点

3.2.2 硬件寄存器接口

显示驱动通过操作显示控制器的寄存器来配置和响应 Vblank 信号:

/* 典型的 Vblank 相关寄存器定义 */
#define DISP_VBLANK_START_REG  0x1004  /* Vblank 起始行 */
#define DISP_VBLANK_END_REG    0x1008  /* Vblank 结束行 */
#define DISP_VBLANK_IRQ_EN_REG 0x100C  /* 中断使能 */
#define DISP_VBLANK_IRQ_STS_REG 0x1010 /* 中断状态 */

/* 驱动初始化时配置 Vblank 窗口 */
void disp_vblank_config(struct disp_device *dev) {
    writel(1080, dev->base + DISP_VBLANK_START_REG);
    writel(1124, dev->base + DISP_VBLANK_END_REG);
    writel(0x1, dev->base + DISP_VBLANK_IRQ_EN_REG); /* 使能中断 */
}

3.3 软件中断处理流程

当硬件产生 Vblank 中断信号后,Linux 内核的显示驱动子系统(如 DRM/KMS)会执行一系列软件处理。该流程涉及中断控制器、DRM 核心框架以及用户空间。

3.3.1 中断处理路径

  1. 硬件中断触发:显示控制器将 Vblank IRQ 线拉高,通知中断控制器(如 GIC)。
  2. 内核中断处理:CPU 执行中断服务例程(ISR),调用驱动注册的 irq_handler。
  3. DRM Vblank 事件处理:ISR 调用 drm_crtc_handle_vblank(),更新内核中的 Vblank 计数器。
  4. 唤醒等待队列:唤醒所有在 drm_vblank_get() 上等待的用户空间进程。
  5. Page Flip 完成回调:如果之前有异步 Page Flip 请求,此时触发完成事件,释放旧 Buffer。
图 3-3:Vblank 软件中断处理流程 硬件 Vblank IRQ ISR: irq_handler() drm_crtc_handle_vblank() 唤醒等待队列 Page Flip 完成回调 用户态 vsync 事件 释放旧 Buffer

3.3.2 关键内核 API 与数据结构

DRM 子系统提供了标准化的 Vblank 管理接口,驱动开发者需要理解以下核心 API:

API 函数 功能 调用时机
drm_vblank_init() 初始化 Vblank 相关数据结构 驱动加载时
drm_crtc_vblank_on() 使能 CRTC 的 Vblank 计数 CRTC 使能后
drm_crtc_vblank_off() 关闭 CRTC 的 Vblank 计数 CRTC 关闭前
drm_crtc_handle_vblank() 处理 Vblank 中断,更新计数器 ISR 中
drm_crtc_vblank_get() 获取 Vblank 计数并增加引用 用户态/内核态请求
性能提示: Vblank ISR 必须尽可能短。不要在 ISR 中执行 Buffer 拷贝或复杂的 DMA 操作。通常只做计数器更新和唤醒操作,实际的 Buffer 提交工作放在下半部(如 Tasklet 或 Workqueue)中完成,以避免阻塞中断控制器。

3.4 Vblank 与 Tearing 的关系

Vblank 机制的核心价值在于防止画面撕裂。当应用程序在非 Vblank 期间更新帧缓冲区时,显示控制器可能正在读取同一行数据,导致屏幕上半部分显示旧帧、下半部分显示新帧。

常见误区: 即使启用了 Vblank 同步,如果驱动在 Vblank 中断处理中延迟过高(超过 Vblank 窗口时长),会导致 Frame Drop(丢帧)。此时用户会感知到画面卡顿,而非撕裂。丢帧的典型表现为:dmesg 中出现 "vblank wait timed out" 或 "flip queue overflow" 错误。
图 3-4:Vblank 同步 vs 非同步对比 非同步(Tearing) 旧帧 (Frame N) 新帧 (Frame N+1) 撕裂线 Vblank 同步(无撕裂) 完整新帧 (Frame N+1) Vblank 同步

3.5 多显示器场景下的 Vblank 挑战

在多显示器(多 CRTC)系统中,每个显示控制器独立产生 Vblank 信号。驱动需要为每个 CRTC 维护独立的 Vblank 计数器和中断处理路径。

  • 独立中断线:每个 CRTC 通常使用独立的中断号,或通过共享中断线 + 状态寄存器区分。
  • 同步问题:不同显示器的 Vblank 窗口可能不同步(相位差),导致全局 Buffer 翻转策略复杂化。
  • 原子翻转:DRM 的 Atomic API 允许驱动在多个 CRTC 的 Vblank 窗口内同步提交更新,避免多屏撕裂。
/* 多 CRTC Vblank 中断处理示例 */
static irqreturn_t disp_irq_handler(int irq, void *dev_id) {
    struct disp_device *dev = dev_id;
    u32 status = readl(dev->base + DISP_IRQ_STS_REG);
    
    if (status & CRTC0_VBLANK_IRQ) {
        drm_crtc_handle_vblank(&dev->crtc[0]);
    }
    if (status & CRTC1_VBLANK_IRQ) {
        drm_crtc_handle_vblank(&dev->crtc[1]);
    }
    
    writel(status, dev->base + DISP_IRQ_STS_REG); /* 清除中断 */
    return IRQ_HANDLED;
}