Qt界面底层实现浅谈: 多渲染后端的分层架构

目录

1.Qt的界面开发模式

1.1.传统 QtWidgets(C++ 控件):默认是 CPU 软件光栅化

2.Qt Quick(QML 界面):与 OpenGL 深度绑定,但 Qt6 已解耦

2.实现原理

2.1.整体分层

2.2.核心继承关系

2.3.核心组合与依赖关系

2.4.Windows 平台 QtWidgets 完整渲染调用链路

3.QOpenGLWidget 中使用 QPainter 进行 GPU 加速 2D 绘制

4.Qt RHI 渲染架构

4.1.诞生背景

4.2.分层架构

4.3.支持的图形后端

4.4.核心渲染流程与关键类

4.5.RHI 在 Qt 各模块中的应用

4.6.选型建议


1.Qt的界面开发模式

1.1.传统 QtWidgets(C++ 控件):默认是 CPU 软件光栅化

我们日常使用的QWidgetQPushButtonQTableView等标准控件,采用自绘机制,默认走CPU 软件光栅化,不依赖 OpenGL。

1.底层绘制流程

Qt 对外提供统一的 2D 绘制 APIQPainter,所有控件的外观都通过QPainter描述;QPainter底层由不同的Paint Engine(绘制引擎)真正执行光栅化计算:

  • 默认引擎QRasterPaintEngine,纯 CPU 软件光栅化,跨平台行为一致,兼容性最好。
  • 绘制完成后,最终将像素结果输出到操作系统的窗口表面:
    • Windows:输出到 GDI 设备上下文(HDC)
    • Linux X11/Wayland:输出到 XCB / Wayland 可绘表面
    • macOS:输出到 Core Graphics(Quartz)上下文

这也是 QtWidgets 能做到跨平台样式一致的核心原因:控件外观是 Qt 自己绘制的,而非调用系统原生控件。

2.QtWidgets 与 OpenGL 的结合方式

OpenGL 不是 QtWidgets 的默认渲染路径,但可以按需开启,常见有三种用法:

  • 局部嵌入 OpenGL 内容:使用QOpenGLWidget控件,在其中直接调用 OpenGL API 绘制 3D 场景、可视化图形等,这是 Qt 项目中最常用的 OpenGL 集成方式。
  • QPainter 走 GPU 加速:通过QOpenGLPaintDeviceQPainter绑定到 OpenGL 上下文,让原本 CPU 执行的 2D 绘制指令转由 GPU 完成,适合大量图元的高性能绘制场景。
  • 全局 OpenGL 窗口合成:Qt5 可通过Qt::AA_UseOpenGLES/Qt::AA_UseDesktopOpenGL等全局属性,让整个 Widget 窗口基于 OpenGL 表面渲染。但该方式兼容性问题较多,实际桌面项目很少全局开启。

2.Qt Quick(QML 界面):与 OpenGL 深度绑定,但 Qt6 已解耦

Qt Quick 是 Qt 新一代声明式 UI 框架,基于场景图(Scene Graph)渲染,从诞生起就依赖 GPU 硬件加速。

1.Qt5 时代:默认直接基于 OpenGL

Qt 5.x 版本中,Qt Quick 2 的场景图默认直接基于 OpenGL / OpenGL ES 2.0 实现,所有界面元素都会被转换成 OpenGL 渲染指令由 GPU 绘制 —— 这也是很多人 “Qt 用 OpenGL 画界面” 印象的主要来源。

2.Qt6 时代:通过 RHI 抽象层,OpenGL 仅为后端之一

Qt 6 引入了RHI(Rendering Hardware Interface,渲染硬件接口)作为统一的 GPU 抽象层,Qt Quick 不再直接调用 OpenGL,而是通过 RHI 间接调用底层图形 API,支持多后端自动切换:

平台默认后端可选后端
WindowsDirect3D 11Direct3D 12、Vulkan、OpenGL
macOS / iOSMetalVulkan、OpenGL
LinuxOpenGLVulkan
AndroidOpenGL ESVulkan

也就是说,Qt6 的 Qt Quick 界面,在 Windows 上默认由 D3D11 绘制,macOS 上默认由 Metal 绘制,只有 Linux 等平台默认继续使用 OpenGL。

2.实现原理

Qt Painting 模块实现代码在.\Qt5.12.12\5.12.12\Src\qtbase\src\gui\painting下面,整体遵循「统一接口 + 多后端引擎 + 底层光栅化组件」的分层架构。

2.1.整体分层

从上到下分为 5 层,上层依赖下层,下层对上层透明:

┌───────────────────────────────────────────────────┐ │ 接口层:QPainter + 绘制数据类(Pen/Brush/Path等) │ 用户直接使用 ├───────────────────────────────────────────────────┤ │ 引擎抽象层:QPaintEngine / QPaintEngineEx │ 统一绘制接口 ├───────────────────────────────────────────────────┤ │ 引擎实现层:QRasterPaintEngine │ 软件光栅化具体实现 ├───────────────────────────────────────────────────┤ │ 光栅化组件层:Stroker / OutlineMapper / DrawHelper│ 像素级计算 ├───────────────────────────────────────────────────┤ │ 设备层:QPaintDevice 基类 + QImage/QPixmap 等 │ 绘制目标载体 └───────────────────────────────────────────────────┘

2.2.核心继承关系

1.绘制引擎继承链(最核心的主干)

QPaintEngine (抽象基类) ├── QPaintEngineEx (扩展绘制引擎) │ ├── QRasterPaintEngine (光栅绘制引擎 - 主要实现) │ └── QEmulationPaintEngine (模拟绘制引擎 - 包装其他引擎) └── 其他平台特定引擎 [外部类] ├── QOpenGLPaintEngine ├── QWin32PaintEngine └── QX11PaintEngine

QPaintEngine(公有抽象基类)

  • 定位:所有绘制引擎的根接口,定义了全套 2D 绘制虚函数(drawRectsdrawLinesdrawPathdrawTextdrawImage等)
  • 职责:管理引擎生命周期、绑定绘制设备、存储基础绘制状态、提供引擎类型标识
  • 本目录内的直接子类:
    • QPaintEngineEx(私有扩展基类,_p后缀)
      • Qt5 引入的优化版 2D 引擎基类,新增批量绘制、状态缓存、统一抗锯齿处理能力
      • 对复杂路径、渐变、裁剪做了通用抽象,降低具体引擎的实现成本
      • 唯一核心子类:QRasterPaintEngine(私有类)
        • 纯 CPU 软件光栅化引擎的完整实现,是 QtWidgets 的默认渲染后端
        • 也是本目录最复杂、最核心的实现类
    • QPicturePaintEngine:录制绘制指令到QPicture,支持后续回放
    • QPrintEngine:打印引擎抽象基类

补充:OpenGL 绘制引擎、SVG 绘制引擎等不在本目录,分属gui/openglsvg等其他模块。

2.绘制设备继承链

QPaintDevice (抽象基类) ├── QPagedPaintDevice (分页绘制设备) │ └── QPdfWriter (PDF写入器) ├── QWindow (窗口) [外部类] ├── QImage (图像) [外部类] └── QPixmap (像素图) [外部类]

所有可被QPainter绘制的对象,都继承自QPaintDevice,核心能力是「创建并返回对应类型的绘制引擎」。

QPaintDevice(公有抽象基类)

  • 核心虚函数:paintEngine()→ 返回该设备对应的绘制引擎实例
  • 提供设备度量信息:逻辑像素尺寸、物理 DPI、颜色深度等
  • 本目录内的子类:
    • QImage:内存位图设备,像素可直接读写,纯软件绘制,跨平台行为一致
    • QPixmap:屏幕适配位图设备,平台相关,上屏效率更高
    • QBitmap:单色位图,QPixmap的子类
    • QPicture:绘制指令录制设备,不生成像素,只记录绘制命令
    • QPagedPaintDevice:分页设备基类(打印场景使用)

补充:QWidget(widgets 模块)、QOpenGLPaintDevice(opengl 模块)也是QPaintDevice的子类,但不在本目录

2.3.核心组合与依赖关系

1.QPainter:统一入口与调度者

QPainter是用户唯一直接使用的绘制入口,内部通过组合模式衔接设备与引擎。

  • 持有关系
    • 持有QPaintDevice*:当前绑定的绘制目标
    • 持有QPaintEngine*:当前使用的绘制引擎(由绘制设备创建)
    • 持有QPainterState状态栈:管理画笔、画刷、变换矩阵、裁剪区域、字体、渲染提示等状态,支持save()/restore()压栈弹栈
  • 工作逻辑: 用户所有drawXXX调用,先经过QPainter做坐标变换、裁剪校验、状态合并,再转发给底层QPaintEngine执行光栅化。

2.QRasterPaintEngine:软件光栅引擎核心

QRasterPaintEngine是本目录的核心实现类,内部组合了多个专用组件,分工完成从「绘制指令」到「像素缓冲区」的完整转换。

QRasterPaintEngine的职责只有一个:QPainter的绘制指令(画线、填色、渐变、文字、图片合成等)通过 CPU 计算,转换成一张像素位图(QImage/QPixmap)

这一步是纯数学运算,不依赖任何操作系统图形 API,也不调用 GDI、OpenGL、Metal 等硬件接口,因此天然跨平台。这也是 QtWidgets 能做到 “全平台绘制效果完全一致” 的根本原因 —— 所有控件的外观都是 Qt 自己用 CPU 算出来的,而非调用系统原生控件渲染。

下面是涉及文件和一些内部组件介绍:

文件核心作用
qdrawhelper_p.h/qdrawhelper.cpp底层像素操作核心,包含所有 SIMD 加速的光栅化函数(矩形填充、线条绘制、渐变、图像混合、抗锯齿采样等)
qstroker_p.h/qstroker.cpp路径描边算法实现,负责将QPainterPath轮廓转换为可光栅化的扫描线
qpaintengineex_p.h/qpaintengineex.cpp优化版绘制引擎基类,提供通用绘制状态管理、路径裁剪等能力
qtextureglyphcache_p.h文字字形缓存,将字体字形光栅化后缓存为位图,大幅提升文字绘制性能
qoutlinemapper_p.h路径轮廓映射器,用于复杂路径的抗锯齿光栅化
内部组件类型核心职责
QRasterBuffer组合持有封装目标像素缓冲区,管理像素格式、行步长、脏区域,是所有绘制结果的最终写入目标
QStroker依赖调用路径描边计算器:输入QPainterPath+QPen参数,输出描边后的闭合轮廓多边形
QOutlineMapper依赖调用抗锯齿扫描线生成器:输入闭合路径,输出带亚像素精度的扫描线覆盖度数据,是 Qt 高质量抗锯齿的核心
QDrawHelper函数集依赖调用底层像素操作函数集合:所有像素填充、alpha 混合、渐变采样、图像缩放的最终执行者,带 SIMD 指令集加速
QTextureGlyphCache组合持有文字字形缓存:缓存已光栅化的字体字形位图,避免重复计算,大幅提升文字绘制性能

不同平台的差异:只在 “最后一步显示”

光栅化生成像素位图后,需要把这张图贴到系统窗口上,这一步由 Qt 的平台抽象层(QPA)负责,不同平台的提交方式不同:

平台光栅化引擎(统一)最终像素提交到窗口的方式
WindowsQRasterPaintEngine通过QBackingStore将位图 Blit 到 Win32 窗口 HDC,底层使用 GDI;Qt 5.15+ 可配合 DirectComposition 做窗口合成
Linux (X11)QRasterPaintEngine通过 XCB 协议将像素缓冲区提交给 X Server 绘制
Linux (Wayland)QRasterPaintEngine通过 Wayland 协议共享内存缓冲区提交合成
macOSQRasterPaintEngine渲染到CGContext(Core Graphics),最终上屏到 CALayer
嵌入式 Linux (Framebuffer/EGLFS)QRasterPaintEngine直接写入帧缓冲,或通过 EGL 表面上屏

从 Qt 5 开始,Qt 全面重构了绘制管线

  • Qt 4 存在多个平台相关绘制引擎:Windows 下有基于 GDI 的QWindowsPaintEngine,X11 下有基于 XRender 的引擎,macOS 下有基于 QuickDraw/CoreGraphics 的引擎;
  • 当时软件光栅化引擎只是备选,系统原生引擎才是默认,导致不同平台下绘制效果、性能差异很大。

什么时候不用 QRasterPaintEngine

只有主动切换到 GPU 加速渲染路径时,才会绕过软件光栅引擎:

  • 使用QOpenGLWidgetQQuickWidget等 GPU 加速控件;
  • 全局开启 OpenGL 渲染后端(如设置Qt::AA_UseOpenGLES);
  • 使用 Qt Quick(QML)界面,走 RHI + 原生 GPU API 渲染。

简单理解:

  • 上层绘制逻辑全平台共用一套 QRaster 代码,保证效果一致;
  • 底层只封装了 “把像素贴到屏幕” 的平台接口,不参与绘制计算。

3.底层光栅化组件的协作流程

不同绘制图元会走不同的组件组合,典型链路:

  • 简单图元(矩形、水平线):直接调用QDrawHelper中的 SIMD 函数,批量填充像素,性能最高
  • 路径描边QPainterPathQStroker生成描边轮廓 →QOutlineMapper生成扫描线 →QDrawHelper逐行填充
  • 路径填充QPainterPathQOutlineMapper直接生成扫描线 →QDrawHelper逐行填充
  • 文字绘制:字符编码 → 字体引擎生成字形 →QTextureGlyphCache缓存命中 →QDrawHelper拷贝字形位图到目标缓冲区

4.绘制数据类(值类型)

这些类是绘制参数的载体,都是值语义,被QPainter状态栈持有,作为参数传递给绘制引擎。

  • QPen:描边参数,包含线宽、颜色、线型、端点样式、连接样式、虚线模式
  • QBrush:填充参数,支持纯色、渐变(QGradient)、纹理贴图三种填充模式
  • QPainterPath:路径数据容器,支持直线、贝塞尔曲线、圆弧、矩形等图元组合,支持路径布尔运算
  • QTransform:2D 仿射变换矩阵,支持平移、旋转、缩放、错切、透视变换
  • QColor:颜色类,支持 ARGB、HSV 等多种颜色空间
  • QGradient:渐变基类,子类包括线性渐变、径向渐变、锥形渐变

2.4.Windows 平台 QtWidgets 完整渲染调用链路

QtWidgets 在 Windows 上的渲染是典型的「软件光栅化 + 平台窗口上屏」架构,完整流程从触发重绘到像素上屏分为 4 个核心阶段,全程默认不依赖 OpenGL。

1.重绘触发阶段

重绘的源头分两类:

  • 主动触发:代码调用QWidget::update()/repaint(),Qt 内部标记脏区域(dirty region),向事件循环投递一个重绘事件。
  • 被动触发:窗口移动、缩放、被遮挡后露出、系统发送WM_PAINT消息,由 Qt 平台插件(QPA)接收并转化为重绘请求。

关键细节:

  • update()是异步合并的:多次调用会合并成一次重绘,在下一次事件循环时执行,避免重复绘制;
  • repaint()是同步立即重绘,会直接触发 paintEvent,常规场景不推荐频繁使用。

2.事件分发与脏区计算

  • Windows 消息循环接收到WM_PAINT,由QWindowsWindow平台类处理;
  • Qt 计算累计的脏区域(需要重绘的矩形集合),只重绘变化的部分,而非全窗口重绘,这是性能优化的核心之一;
  • 调用对应QWidgetpaintEvent(QPaintEvent*)虚函数,参数中携带脏区矩形。

3.光栅化绘制阶段(核心)

这是QRasterPaintEngine真正工作的阶段:

  • paintEvent中创建QPainter(this),Qt 自动为当前控件关联对应的绘制引擎;
  • 普通 QWidget 默认绑定QRasterPaintEngine,绘制目标是QBackingStore管理的一张内存位图(QImage,格式通常为ARGB32_Premultiplied);
  • 所有QPainter绘制指令(drawLine、drawRect、drawText、drawPath 等),都由QRasterPaintEngine拆解为底层像素操作:
    • 基础图元:走qdrawhelper模块的 SIMD 加速函数(SSE2/AVX2),批量处理像素;
    • 复杂路径:走QStroker描边 + 扫描线填充算法,逐行计算覆盖度实现抗锯齿;
    • 文字:先查字形缓存,命中直接拷贝位图,未命中则通过 FreeType 光栅化字形后缓存再绘制。
  • 所有绘制结果都写入内存中的像素缓冲区,全程在 CPU 执行,不涉及 GPU。

4.缓冲区提交与上屏(Windows 特有)

绘制完成后,需要把内存位图的脏区拷贝到屏幕上,这一步是 Windows 平台相关的:

  • QBackingStore::flush()被调用,将 backing store 中脏区的像素提交到窗口表面;
  • 传统兼容路径:通过 Windows GDI 的BitBlt/StretchBlt函数,将内存 DIB 位图拷贝到窗口 HDC 上,由 DWM(桌面窗口管理器)最终合成到屏幕;
  • 优化路径(Qt 5.15+ / Qt6):启用 DirectComposition 支持后,Qt 会将 backing store 的内容通过 DirectComposition 表面提交,直接交给 DWM 合成,减少一次 GDI 拷贝,显著提升缩放、动画场景的性能。

补充:Qt Windows 平台的 backing store 是双缓冲结构,绘制在后台缓冲区进行,完成后一次性 flush 到前台,因此默认不会出现闪烁,不需要手动实现双缓冲。

2.5.QRaster 软件光栅化 vs OpenGL GPU 渲染 性能对比

维度QRaster 软件光栅化OpenGL GPU 渲染
计算主体CPU 执行扫描线、像素混合算法GPU 并行执行顶点着色、片元着色
数据路径内存生成位图 → 拷贝到窗口顶点 / 纹理上传 GPU → GPU 计算 → 帧缓冲直接上屏
并行方式主要依靠 SIMD 指令级并行,多线程优化有限天然大规模并行,成百上千核心同时计算
变换开销缩放、旋转、透视需逐像素重算,开销极大矩阵变换由顶点着色器完成,几乎零开销

两者没有绝对的 “谁更快”,优势场景完全不同。以下基于 Windows 桌面平台的常规硬件环境,从多个维度做详细对比。

1.核心原理差异

维度QRaster 软件光栅化OpenGL GPU 渲染
计算主体CPU 执行扫描线、像素混合算法GPU 并行执行顶点着色、片元着色
数据路径内存生成位图 → 拷贝到窗口顶点 / 纹理上传 GPU → GPU 计算 → 帧缓冲直接上屏
并行方式主要依靠 SIMD 指令级并行,多线程优化有限天然大规模并行,成百上千核心同时计算
变换开销缩放、旋转、透视需逐像素重算,开销极大矩阵变换由顶点着色器完成,几乎零开销

2.分场景性能对比

(1)常规桌面 UI(几十个控件、少量文字与图标)

  • QRaster 更快
  • 原因:OpenGL 需要创建上下文、切换状态、上传数据,有固定的驱动开销;而 CPU 光栅化直接内存操作,小数据量下延迟更低、响应更快。
  • 这也是 QtWidgets 默认不用 OpenGL 的核心原因:普通业务界面,软件渲染性能完全够用,且兼容性更好。

(2)大量 2D 图元(上千条线、矩形、点,如曲线图表、仿真轨迹)

  • 千级以内:两者差距不大,QRaster 凭借 SIMD 优化依然够用。
  • 万级以上、且每帧动态更新:OpenGL 反超并拉开差距
  • 原因:CPU 串行处理万个图元会线性增长耗时;GPU 并行处理下,图元数量增长带来的性能下降平缓很多。

(3)频繁缩放、旋转、透明混合(如地图、画布类应用)

  • OpenGL 压倒性优势
  • 原因:仿射变换、透视变换在 GPU 中是硬件级支持,只需要修改顶点矩阵即可;而软件光栅化下,每次变换都要重新计算所有像素的位置、做插值重采样,CPU 开销成倍增长。

(4)文字密集型界面(报表、表格、大量文本)

  • QRaster 通常更优
  • 原因:文字光栅化本质是字形位图拷贝 + 灰度抗锯齿,CPU 缓存命中率高,FreeType + Qt 字形缓存已经做了深度优化;而 GPU 渲染文字需要先把字形上传纹理,小文字频繁切换字形时,纹理切换开销反而更高。

(5)图片与视频渲染

  • 大图缩放、视频帧连续播放:OpenGL 优势明显,纹理采样、颜色空间转换都可由 GPU 硬件加速。
  • 静态小图标展示:两者无明显差异。

3.其他维度对比

  • 内存占用:QRaster 需要在内存中保留完整窗口大小的位图,高分辨率下内存占用高(4K 屏约 32MB 单缓冲);OpenGL 帧缓冲在显存中,系统内存占用更低。
  • 启动与首次绘制开销:QRaster 几乎零启动成本;OpenGL 需要初始化驱动、创建上下文、编译着色器,首帧延迟高。
  • 兼容性与稳定性:QRaster 纯软件实现,几乎不会出现驱动兼容问题;OpenGL 受显卡驱动影响大,不同厂商、不同版本驱动可能出现渲染异常、崩溃。
  • 功耗:轻量界面下,CPU 渲染功耗更低;重度绘制场景下,GPU 并行效率更高,单位算力功耗更优。
  • 开发调试成本:QRaster 基于 QPainter 原生 API,开发简单,调试方便;OpenGL 需要学习着色器、管线、资源管理,开发与调试门槛高很多。

4.选型建议

  • 普通业务桌面软件、表单、管理系统:默认用 QRaster 软件渲染即可,稳定、兼容、开发成本低。
  • 大数据量可视化、动态轨迹、实时图表、2D 游戏画布:优先考虑 OpenGL 加速。
  • 3D 场景、三维仿真:必须使用 OpenGL/Vulkan 等 GPU 渲染方案。
  • 嵌入式低性能设备:若 GPU 算力弱、驱动不完善,软件光栅化往往比 GPU 渲染更流畅稳定。

3.QOpenGLWidget 中使用 QPainter 进行 GPU 加速 2D 绘制

QOpenGLWidget中直接使用QPainter绘制时,Qt 会自动切换到OpenGL 绘制引擎(底层为QOpenGLPaintDevice),所有 2D 绘制指令都会被转换成 OpenGL 调用,由 GPU 完成光栅化,完全替代默认的QRasterPaintEngine软件渲染。

它最大的优势是:

  • API 零成本迁移:和普通 QWidget 中QPainter的用法完全一致,现有绘制代码几乎不用改
  • 变换性能质变:平移、缩放、旋转等操作由 GPU 矩阵硬件运算完成,几乎零开销
  • 天然抗锯齿:GPU 多重采样抗锯齿开销远低于软件抗锯齿,画面更流畅

项目配置(.pro 文件)

QT += core gui widgets opengl CONFIG += c++17 TARGET = OpenGLPainterDemo TEMPLATE = app SOURCES += main.cpp glpaintwidget.cpp HEADERS += glpaintwidget.h

头文件glpaintwidget.h:

#ifndef GLPAINTWIDGET_H #define GLPAINTWIDGET_H #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QMouseEvent> #include <QWheelEvent> #include <QPainter> class GLPaintWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: explicit GLPaintWidget(QWidget *parent = nullptr); ~GLPaintWidget() override; protected: // OpenGL 生命周期 void initializeGL() override; void resizeGL(int w, int h) override; void paintGL() override; // 交互事件 void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: qreal m_zoom = 1.0; // 缩放比例 QPoint m_panOffset; // 平移偏移量 QPoint m_lastMousePos; // 上次鼠标位置 bool m_panning = false; // 是否处于拖动状态 }; #endif // GLPAINTWIDGET_H

源文件glpaintwidget.cpp

#include "glpaintwidget.h" GLPaintWidget::GLPaintWidget(QWidget *parent) : QOpenGLWidget(parent) { // 配置 OpenGL 表面:3.3 核心模式 + 4倍抗锯齿 QSurfaceFormat format; format.setVersion(3, 3); format.setProfile(QSurfaceFormat::CoreProfile); format.setSamples(4); format.setDepthBufferSize(24); setFormat(format); setWindowTitle("QPainter OpenGL GPU 加速绘制"); resize(900, 600); } GLPaintWidget::~GLPaintWidget() { // 资源释放:QPainter 相关资源由 Qt 内部管理,无需手动释放 } void GLPaintWidget::initializeGL() { // 初始化 OpenGL 函数指针,必须最先调用 initializeOpenGLFunctions(); // 设置清屏背景色 glClearColor(0.95f, 0.95f, 0.98f, 1.0f); } void GLPaintWidget::resizeGL(int w, int h) { // 视口跟随窗口尺寸 glViewport(0, 0, w, h); } void GLPaintWidget::paintGL() { // 清屏 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // ====================== 核心:QPainter GPU 绘制 ====================== // 直接在 QOpenGLWidget 上创建 QPainter,自动使用 OpenGL 引擎 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::TextAntialiasing, true); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); // 应用全局变换:平移 + 缩放(GPU 硬件加速,零开销) painter.translate(m_panOffset); painter.scale(m_zoom, m_zoom); // 1. 绘制网格背景(千级线条,GPU 下依然流畅) painter.setPen(QPen(QColor(220, 220, 230), 1)); for (int x = -2000; x < 2000; x += 50) { painter.drawLine(x, -2000, x, 2000); } for (int y = -2000; y < 2000; y += 50) { painter.drawLine(-2000, y, 2000, y); } // 2. 绘制矩形 painter.setPen(QPen(QColor(64, 158, 255), 2)); painter.setBrush(QColor(64, 158, 255, 80)); painter.drawRect(QRect(0, 0, 200, 150)); // 3. 绘制圆形 painter.setPen(QPen(QColor(255, 103, 103), 2)); painter.setBrush(QColor(255, 103, 103, 80)); painter.drawEllipse(QPoint(320, 100), 80, 80); // 4. 渐变填充圆角矩形 QLinearGradient gradient(0, 200, 200, 350); gradient.setColorAt(0, QColor(0, 200, 150)); gradient.setColorAt(1, QColor(0, 150, 200)); painter.setBrush(gradient); painter.setPen(Qt::NoPen); painter.drawRoundedRect(0, 200, 200, 150, 12, 12); // 5. 贝塞尔曲线路径 QPainterPath path; path.moveTo(260, 250); path.cubicTo(360, 200, 360, 360, 460, 310); painter.setPen(QPen(QColor(155, 89, 182), 3)); painter.setBrush(Qt::NoBrush); painter.drawPath(path); // 6. 文字绘制 painter.setPen(QColor(40, 40, 40)); painter.setFont(QFont("Microsoft YaHei", 16, QFont::Bold)); painter.drawText(20, 420, "GPU加速2D绘制 | 左键拖动平移 | 滚轮缩放"); // 7. 大量随机点(性能测试:10000 个点依然流畅) painter.setPen(QPen(QColor(255, 193, 7), 2)); for (int i = 0; i < 10000; ++i) { int x = qrand() % 800 + 50; int y = qrand() % 400 + 50; painter.drawPoint(x, y); } // ================================================================== } // ------------------------------ 交互逻辑 ------------------------------ void GLPaintWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_panning = true; m_lastMousePos = event->pos(); } } void GLPaintWidget::mouseMoveEvent(QMouseEvent *event) { if (m_panning) { m_panOffset += event->pos() - m_lastMousePos; m_lastMousePos = event->pos(); update(); // 触发重绘 } } void GLPaintWidget::wheelEvent(QWheelEvent *event) { // 滚轮缩放:每步缩放 10% qreal factor = event->angleDelta().y() > 0 ? 1.1 : 0.9; m_zoom *= factor; update(); }

主函数main.cpp

#include <QApplication> #include "glpaintwidget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); GLPaintWidget w; w.show(); return a.exec(); }

4.Qt RHI 渲染架构

RHI(Rendering Hardware Interface,渲染硬件接口)是 Qt 6 最核心的底层重构,彻底改变了 Qt 的 GPU 渲染体系,也是理解现代 Qt 渲染机制的关键。

4.1.诞生背景

Qt 5 时代,Qt Quick 2 的场景图直接基于 OpenGL ES 2.0 开发,所有渲染指令硬编码调用 OpenGL API。随着图形行业发展,这套架构出现了致命短板:

  • 苹果在 macOS/iOS 全面弃用 OpenGL,主推 Metal,OpenGL 驱动停止更新,性能与兼容性持续退化
  • Windows 平台 Direct3D 驱动更稳定、功耗更低,是游戏与工业软件的主流选择
  • Vulkan 作为新一代跨平台图形 API,在 Linux、Android 快速普及
  • 不同厂商的 OpenGL 驱动差异巨大,适配与调试成本极高

因此 Qt 6 从零构建了 RHI 抽象层:上层只调用统一的 RHI API,底层自动适配各平台原生图形 API,设计理念与 Unity、Unreal 等游戏引擎的渲染抽象层一致。

4.2.分层架构

RHI 是纯 GPU 渲染抽象层,不负责窗口、事件、输入,只专注于 GPU 资源管理与绘制指令提交。整体架构分层如下:

┌─────────────────────────────────────┐ │ Qt Quick / Qt 3D / 自定义渲染代码 │ 上层业务 ├─────────────────────────────────────┤ │ Qt Scene Graph(场景图) │ 场景裁剪、批处理 ├─────────────────────────────────────┤ │ Qt RHI API │ 统一抽象层 ★ ├──────┬──────┬───────┬───────────────┤ │D3D11 │D3D12 │ Metal │ Vulkan / GL │ 平台图形后端 └──────┴──────┴───────┴───────────────┘

核心价值:

  • 一次编写,多后端运行:同一份渲染代码无需修改,即可在不同图形 API 上执行
  • 自动最优适配:根据操作系统自动选择原生图形 API,发挥最佳性能与兼容性
  • 屏蔽底层差异:统一的资源、管线、着色器模型,开发者无需学习多套图形 API

4.3.支持的图形后端

Qt 6 目前支持 6 种图形后端,不同平台默认值不同:

运行平台默认后端可选后端说明
WindowsDirect3D 11Direct3D 12、Vulkan、OpenGLD3D11 兼容性最广,D3D12 性能更强
macOS / iOSMetalVulkan、OpenGLMetal 为苹果官方推荐,功耗与性能最优
Linux (X11/Wayland)OpenGLVulkan开源驱动生态下 Vulkan 性能更优
AndroidOpenGL ES 3.0+Vulkan中高端设备支持 Vulkan
嵌入式 LinuxOpenGL ESVulkan依硬件驱动支持而定

可通过环境变量QSG_RHI_BACKEND手动强制指定后端(用于调试或特殊场景):

# Windows 强制使用 Vulkan set QSG_RHI_BACKEND=vulkan # macOS 强制使用 OpenGL export QSG_RHI_BACKEND=opengl

4.4.核心渲染流程与关键类

RHI 采用基于命令缓冲区的延迟渲染模型,与现代图形 API(D3D12、Vulkan、Metal)设计理念对齐。核心流程分为三个阶段:

  • 初始化:创建 RHI 实例、交换链、全局资源
  • 资源准备:创建缓冲区、纹理、图形管线、资源绑定
  • 帧渲染:录制绘制命令 → 提交到 GPU → 呈现到屏幕

核心类与作用:

  • QRhi:RHI 核心实例,代表一个 GPU 逻辑设备,是所有 GPU 资源的工厂
  • QRhiSwapChain:交换链,对应窗口的可显示表面,管理双缓冲 / 三缓冲与垂直同步
  • QRhiBuffer:GPU 缓冲区,存储顶点、索引、Uniform 常量等数据
  • QRhiTexture:GPU 纹理资源,支持 2D、立方图、数组纹理等
  • QRhiGraphicsPipeline:图形管线,封装着色器、深度测试、混合模式、光栅化等所有固定管线状态
  • QRhiShaderResourceBindings:着色器资源绑定,关联 Uniform 缓冲区、纹理采样器与着色器输入槽
  • QRhiCommandBuffer:命令缓冲区,录制所有绘制、资源拷贝、状态切换指令,最后批量提交给 GPU

4.5.RHI 在 Qt 各模块中的应用

  • Qt Quick(QML):Qt 6 中场景图已完全基于 RHI 重写,是 RHI 最大的使用方。所有 QML 界面元素最终都会被转换成 RHI 绘制指令,由底层 GPU API 执行。
  • Qt 3D:Qt 3D 渲染后端已迁移至 RHI,支持多图形 API 切换。
  • Qt Multimedia:视频解码后的帧渲染通过 RHI 实现 GPU 加速输出。
  • Qt Widgets:传统 Widgets 默认仍使用 CPU 软件光栅化,可通过两种方式接入 RHI:
    1. 使用QQuickWidget嵌入 QML 界面,间接使用 RHI 渲染
    2. Qt 6.4+ 提供QRhiWidget控件,可直接在 Widget 窗口中使用 RHI API 自定义绘制,是替代QOpenGLWidget的新一代方案

4.6.选型建议

  • 开发传统 QtWidgets 桌面应用,仅需嵌入 3D 场景或高性能 2D 绘制:QOpenGLWidget仍是最成熟、资料最多、上手最快的方案。
  • 开发新一代跨平台应用,基于 Qt Quick / QML 架构:默认基于 RHI,无需关心底层,自动享受最优性能与平台适配。
  • 追求极致性能、面向未来图形 API,或针对特定平台深度优化:可基于QRhiWidget开发自定义渲染逻辑,逐步替代 OpenGL。