基于Qt Widgets的Qt程序,控件的刷新默认状况下都是在UI线程中依次进行的,换言之,各个控件的QWidget::paintEvent方法会在UI线程中串行地被调用。若是某个控件的paintEvent很是耗时(等待数据时间+CPU处理时间+GPU渲染时间),会致使刷新帧率降低,界面的响应速度变慢。
假如这个paintEvent耗时的控件没有使用OpenGL渲染,彻底使用CPU渲染。这种状况处理起来比较简单,只须要另外开一个线程用CPU往QImage里面渲染,当主线程调用到这个控件的paintEvent时,再把渲染好的QImage画出来就能够了,单纯绘制一个QImage仍是很快的。
若是这个paintEvent耗时的控件使用了OpenGL渲染,状况会复杂一些,由于想要把OpenGL渲染过程搬到另一个线程中并非直接把OpenGL调用从UI线程搬到渲染线程就能够的,是须要作一些准备工做的。另外,UI线程如何使用渲染线程的渲染结果也是一个须要思考的问题。
以绘制一个迭代了15次的Sierpinski三角形为例,它总共有3^15=14348907个三角形,在个人MX150显卡上绘制一次需要30ms左右的时间。所以若是我在UI线程渲染这些顶点的话,UI线程的刷新帧率就会掉到30帧左右。如今咱们来看一下如何在另外一个线程中渲染这些三角形。
软硬件环境
CPU:Intel® Core™ i5-8250U CPU @ 1.60GHz多线程
GPU:NVIDIA GeForce MX150(Driver:388.19)svg
OS:Microsoft Windows 10 Home 10.0.18362性能
Compiler:MSVC 2017测试
Optimization flag:O2this
Qt version:5.12.1spa
OpenGL version:4.6.0
概述
有如下主要的类或方法:
- GLWidget
这个类在UI线程中使用,继承了QOpenGLWidget,负责将渲染线程渲染结果绘制到屏幕上。
- Renderer
这个类在渲染线程中使用,负责将三角形渲染到离屏framebuffer中。
- RenderThread
渲染线程管理类,负责初始化渲染线程OpenGL的context。
- TextureBuffer
纹理缓存类,负责将Renderer渲染好的图像缓存到纹理中,供UI线程绘制使用。
- RenderThread::run
渲染线程的例程,负责调用Renderer的方法渲染图像,在Renderer渲染好一帧图像后将图像