Qt 一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统

发布时间 2023-08-11 14:00:08作者: 一杯清酒邀明月

概述
  Qt 提供了多种渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统。选择哪种引擎主要取决于你的应用程序需求和你的硬件环境。

  • 栅格引擎(Raster):这是 Qt 的软件渲染引擎,它在 CPU 上执行所有的绘图操作。栅格引擎在所有平台上都提供了一致的视觉效果,但可能会比硬件加速的引擎慢。
  • OpenGL:这是一个跨平台的硬件加速渲染 API,它在 GPU 上执行绘图操作。OpenGL 可以提供更快的渲染速度和更丰富的视觉效果,但需要支持 OpenGL 的硬件和驱动程序。
  • 本地绘图系统:这是使用操作系统的绘图 API,如 Windows 的 GDI 或 Direct2D,macOS 的 Quartz,Linux 的 X11 或 Wayland。本地绘图系统可以提供与操作系统一致的视觉效果,但可能会受到操作系统和硬件的限制。

  如果你的应用程序需要最快的渲染速度和最丰富的视觉效果,你应该使用 OpenGL。如果你的应用程序需要与操作系统一致的视觉效果,你应该使用本地绘图系统。如果你的应用程序需要在所有平台上提供一致的视觉效果,或者你的硬件不支持 OpenGL,你应该使用栅格引擎。

引擎详解
栅格引擎(Raster)
  栅格引擎(Raster Engine)是 Qt 的一种渲染引擎,它在 CPU 上执行所有的绘图操作。这种引擎使用了一种称为栅格化(Rasterization)的技术,将矢量图形(如线、曲线、多边形)转换为像素网格(即栅格)。

  栅格引擎的主要优点是它在所有平台上都提供了一致的视觉效果,因为它完全由 Qt 控制,不依赖于操作系统或硬件的特性。这使得它在跨平台应用程序中非常有用,因为你可以确保你的应用程序在所有平台上看起来都一样。

  此外,栅格引擎不需要特殊的硬件支持,因此它可以在任何支持 Qt 的设备上运行,包括一些老旧或低端的设备,这些设备可能不支持硬件加速的渲染。

  然而,栅格引擎的主要缺点是它可能会比硬件加速的引擎慢,因为它在 CPU 上执行所有的绘图操作,而不是在 GPU 上。CPU 通常比 GPU 更擅长处理复杂的逻辑和控制流,而不是大量的并行计算,这是图形渲染所需要的。因此,如果你的应用程序需要进行大量的图形渲染,或者需要实现复杂的视觉效果,你可能会发现栅格引擎的性能不足。

  总的来说,栅格引擎是一种灵活且可靠的渲染引擎,适合于需要跨平台一致性或在低端硬件上运行的应用程序。然而,对于需要高性能图形渲染的应用程序,你可能需要考虑使用硬件加速的渲染引擎,如 OpenGL。
  Qt::AA_ForceRasterWidgets 属性可以强制 Qt 使用栅格引擎来绘制所有的窗口和控件,而不是使用本地的绘图系统。这可能会影响你的应用程序的性能和视觉效果,所以你应该根据你的应用程序需求和你的硬件环境来决定是否设置这个属性。

OpenGL
  Qt 对 OpenGL 的支持是内建的,也就是说,只要你的 Qt 版本支持 OpenGL,你就可以在你的 Qt 应用程序中使用 OpenGL,无需额外安装 OpenGL。

  然而,要在你的 Qt 应用程序中使用 OpenGL,你的系统需要有一个支持 OpenGL 的图形驱动。这通常意味着你需要在你的系统上安装一个支持 OpenGL 的图形卡驱动。大多数现代的桌面系统(包括 Windows、macOS 和大多数 Linux 发行版)都自带了支持 OpenGL 的图形驱动,所以你通常不需要手动安装。

  如果你的系统没有支持 OpenGL 的图形驱动,或者你的图形驱动不支持你需要的 OpenGL 版本,你可能需要手动安装一个新的图形驱动。这通常涉及到从你的图形卡制造商的网站下载驱动程序,并按照他们的指示进行安装。

  请注意,虽然 Qt 支持 OpenGL,但并不是所有的 Qt 功能都需要 OpenGL。大多数 Qt 功能(包括 Qt Widgets 和 Qt Quick 2D)都可以在没有 OpenGL 的系统上运行。只有一些特定的功能(如 Qt Quick 3D 和一些 Qt 3D 功能)需要 OpenGL。

  Qt 提供了对 OpenGL 的深度集成,使得开发者可以利用 OpenGL 的强大功能来创建具有丰富视觉效果的应用程序。以下是一些关于 Qt 中 OpenGL 的关键点:

  1. QOpenGLWidget 和 QOpenGLWindow: 这两个类提供了一个可以使用 OpenGL 进行绘制的窗口或者控件。你可以重写它们的 initializeGL、resizeGL 和 paintGL 方法来执行你的 OpenGL 代码。
  2. QOpenGLFunctions: 这个类提供了跨平台的 OpenGL API。你可以使用这个类来调用 OpenGL 函数,而不需要直接包含 OpenGL 头文件或者链接 OpenGL 库。
  3. QOpenGLBuffer, QOpenGLVertexArrayObject, QOpenGLFramebufferObject: 这些类提供了对 OpenGL 的缓冲区、顶点数组对象和帧缓冲对象的封装。你可以使用这些类来管理你的 OpenGL 数据。
  4. QOpenGLShader and QOpenGLShaderProgram: 这两个类提供了对 OpenGL 的着色器和着色器程序的封装。你可以使用这些类来编译、链接和使用你的着色器。
  5. QOpenGLTexture: 这个类提供了对 OpenGL 的纹理的封装。你可以使用这个类来加载、生成和使用你的纹理。
  6. Qt Quick and QML: Qt Quick 是 Qt 的一个模块,它使用 QML 语言和 OpenGL 来创建动态的、硬件加速的用户界面。你可以使用 Qt Quick 和 QML 来创建具有丰富动画和视觉效果的应用程序,而不需要直接编写 OpenGL 代码。
  7. Thread Support: Qt 支持在多线程环境中使用 OpenGL。你可以在不同的线程中创建和使用 OpenGL 上下文和资源,但你需要注意 OpenGL 的线程安全问题。
  8. Extension Support: Qt 支持 OpenGL 的扩展。你可以使用 QOpenGLContext::hasExtension 和 QOpenGLContext::getProcAddress 方法来检查和使用 OpenGL 的扩展。
  9. Debug Support: Qt 支持 OpenGL 的调试。你可以使用 QOpenGLDebugLogger 类来获取 OpenGL 的调试消息。

  总的来说,Qt 提供了一套完整的工具和类来使用 OpenGL,使得开发者可以更容易地创建具有丰富视觉效果的应用程序。

本地绘图系统
  在 Qt 中,"本地绘图系统"通常指的是使用操作系统的原生绘图 API 来渲染用户界面。这些 API 可以提供与操作系统一致的视觉效果,但可能会受到操作系统和硬件的限制。

  Qt 支持多种操作系统,因此它的本地绘图系统也有多种。以下是一些例子:

  在 Windows 上,Qt 可以使用 GDI(Graphics Device Interface)或 Direct2D。GDI 是 Windows 的传统绘图 API,它在 CPU 上执行所有的绘图操作。Direct2D 是 Windows 的新绘图 API,它在 GPU 上执行绘图操作,可以提供更快的渲染速度和更丰富的视觉效果。

  在 macOS 上,Qt 可以使用 Quartz。Quartz 是 macOS 的绘图 API,它在 GPU 上执行绘图操作,可以提供与 macOS 一致的视觉效果。

  在 Linux 上,Qt 可以使用 X11 或 Wayland。X11 是 Linux 的传统绘图 API,它在 CPU 上执行所有的绘图操作。Wayland 是 Linux 的新绘图 API,它在 GPU 上执行绘图操作,可以提供更快的渲染速度和更丰富的视觉效果。

  Qt 的本地绘图系统通常用于绘制窗口和控件。但是,Qt 也提供了一些更高级的绘图功能,如 QPainter,它可以在任何绘图设备上绘制复杂的 2D 图形。QPainter 可以使用本地绘图系统,也可以使用其他的渲染引擎,如栅格引擎或 OpenGL。

  如果你的应用程序需要与操作系统一致的视觉效果,你应该使用本地绘图系统。但是,如果你的应用程序需要在所有平台上提供一致的视觉效果,或者你的应用程序需要复杂的 2D 图形,你可能需要使用 QPainter 或其他的渲染引擎。

通过策略模式来选择不同的引擎
  在 C++ 中,你可以通过继承和重写虚函数来实现一个抽象基类。在这个例子中,RasterEngineStrategy、OpenGLEngineStrategy 和 NativeEngineStrategy 都是 ApplicationEngineStrategy 的子类,它们都重写了 setEngine 函数。以下是具体的实现:

 1 #include <QApplication>
 2 #include <QSurfaceFormat>
 3 #include <memory>
 4 
 5 class ApplicationEngineStrategy {
 6 public:
 7     virtual void setEngine(QApplication& app) = 0;
 8 };
 9 
10 class RasterEngineStrategy : public ApplicationEngineStrategy {
11 public:
12     void setEngine(QApplication& app) override {
13         app.setAttribute(Qt::AA_ForceRasterWidgets, true);
14     }
15 };
16 
17 class OpenGLEngineStrategy : public ApplicationEngineStrategy {
18 public:
19     void setEngine(QApplication& app) override {
20         QSurfaceFormat format;
21         format.setVersion(3, 2);
22         format.setProfile(QSurfaceFormat::CoreProfile);
23         QSurfaceFormat::setDefaultFormat(format);
24     }
25 };
26 
27 class NativeEngineStrategy : public ApplicationEngineStrategy {
28 public:
29     void setEngine(QApplication& app) override {
30         app.setAttribute(Qt::AA_ForceRasterWidgets, false);
31     }
32 };
33 
34 class Application {
35 private:
36     std::unique_ptr<ApplicationEngineStrategy> strategy;
37 public:
38     void setEngineStrategy(std::unique_ptr<ApplicationEngineStrategy> newStrategy) {
39         strategy = std::move(newStrategy);
40     }
41     void applyEngineStrategy(QApplication& app) {
42         if (strategy) {
43             strategy->setEngine(app);
44         }
45     }
46 };
47 
48 int main(int argc, char *argv[])
49 {
50     QApplication a(argc, argv);
51 
52     Application app;
53     // 根据需要选择不同的策略
54     app.setEngineStrategy(std::make_unique<RasterEngineStrategy>());
55     app.applyEngineStrategy(a);
56 
57     // ...
58     return a.exec();
59 }

  在这个例子中,RasterEngineStrategy::setEngine 函数设置了 Qt::AA_ForceRasterWidgets 属性为 true,这会强制 Qt 使用栅格引擎。OpenGLEngineStrategy::setEngine 函数设置了默认的 QSurfaceFormat,这会使 Qt 使用 OpenGL 引擎。NativeEngineStrategy::setEngine 函数设置了 Qt::AA_ForceRasterWidgets 属性为 false,这会使 Qt 使用本地绘图系统。

  在这个例子中,ApplicationEngineStrategy 是一个抽象基类,它定义了一个虚函数 setEngine。这个函数接受一个 QApplication 引用,并设置其渲染引擎。

  Application 类有一个 ApplicationEngineStrategy 的唯一指针成员 strategy。它有一个 setEngineStrategy 函数,用于设置策略,和一个 applyEngineStrategy 函数,用于应用策略。

  这样,你可以在运行时根据需要选择不同的渲染引擎。例如,你可以这样使用这个策略模式:

 1 int main(int argc, char *argv[])
 2 {
 3     QApplication a(argc, argv);
 4 
 5     Application app;
 6     if (useRasterEngine) {
 7         app.setEngineStrategy(std::make_unique<RasterEngineStrategy>());
 8     } else if (useOpenGLEngine) {
 9         app.setEngineStrategy(std::make_unique<OpenGLEngineStrategy>());
10     } else {
11         app.setEngineStrategy(std::make_unique<NativeEngineStrategy>());
12     }
13     app.applyEngineStrategy(a);
14 
15     // ...
16 }

  在这个例子中,useRasterEngine、useOpenGLEngine 和 useNativeEngine 是你的应用程序的配置选项,你可以根据需要设置它们的值。

默认行为
  Qt 默认会尝试使用最佳的渲染引擎,这通常是本地绘图系统或者 OpenGL,取决于你的硬件和操作系统。如果你没有特别的需求,你通常不需要手动设置渲染引擎。

  然而,有些情况下你可能需要手动设置渲染引擎。例如,如果你的应用程序需要一些特殊的渲染效果,你可能需要强制使用 OpenGL。或者,如果你的硬件不支持 OpenGL,你可能需要强制使用栅格引擎。

  至于获取当前的渲染引擎,Qt 没有直接的 API 可以做到这一点。但你可以通过检查一些相关的属性或状态来推测出当前的渲染引擎。例如,你可以检查 Qt::AA_ForceRasterWidgets 属性,如果它是 true,那么 Qt 应该正在使用栅格引擎。或者,你可以检查一个 QWidget 的 paintEngine 方法返回的 QPaintEngine 对象的类型,如果它是 QOpenGLPaintEngine,那么 Qt 应该正在使用 OpenGL。但请注意,这些方法都不是 100% 可靠的,因为 Qt 可能会在不同的窗口或控件中使用不同的渲染引擎。

结语
  在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

  这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

  我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。