关于QOpenGLWidget的多线程渲染
背景介绍
我们知道,QOpenGLWidget
是Qt中继承自QWidget的一个类,对OpenGL的渲染环境进行了很好的封装。用户在使用时只用继承并重写几个关键的虚函数即可完成OpenGL资源的准备和每帧画面的绘制。
但是,也正是因为QOpenGLWidget
是继承自QWidget的一个类,也决定了它的绘制函数是在UI线程调用的,也就是说,当我们重写了QOpenGLWidget
的paintGL
函数时,该函数是在UI线程被调用的。那么如果我们在paintGL
函数里花太多时间进行场景绘制的话,会直接导致UI线程的更新不及时,鼠标键盘交互事件被阻塞,造成界面卡死的现象。
所以当我们选择QOpenGLWidget
来在Qt界面中进行OpenGL场景绘制时,我们需要一种更高效的方式来保证场景能够正常渲染,且不会造成UI线程的阻塞。通常情况下,我们的可以选择的方式是在QOpenGLWidget
中应用多线程渲染。
方案介绍
下面,我来介绍一种在我们产品中被使用,证明有效且高效的一种QOpenGLWidget
多线程渲染处理方式。这套方案针对的应用场景是当我们的OpenGL场景需要按照一定的帧速率定时进行更新,且OpenGL场景的绘制需要消耗相当长的时间,可能超过16甚至33毫秒时。在这种场景下,OpenGL的渲染肯定会阻塞UI线程,造成画面卡点,交互卡顿。我们多线程OpenGL渲染的核心流程是这样的
首先,我们需要一个Renderer对象,专门负责核心的OpenGL场景渲染逻辑,并且我们需要一个Render Thread来和Renderer进行绑定,作为核心OpenGL场景的渲染线程。
其次,我们需要为Renderer创建一个和QOpenGLWidget
的QOpenGLContext
共享的新QOpenGLContext
对象,用于在Render Thread中作为渲染所使用的上下文。
我们在Renderer进行渲染时, 需要将场景的结果渲染到一个OpenGL纹理上,而不是直接绘制到Render Buffer上。由于渲染线程的上下文和UI线程的OpenGL上下文共享,这个纹理可以在QOpenGLWidget
被访问,并最终绘制到主FBO上用于显示。
方案流程
1. 下面是该方案主要使用的三个类的构成:
2. 下面是该方案的主要运行流程:
示例代码
头文件
#pragma once
#include <QtWidgets>
class RefTexture {
GLuint mTexture;
QAtomicInt mRefCount;
private:
~RefTexture();
public:
RefTexture(int width, int height);
void reta