我试图学习如何用Qt做线程gl,以便在一个单独的线程中卸载大量的QPainter。我在主gui线程中有一个QGLWidget,从vbo中绘制一些线条并显示纹理。在QGLWidget的initializeGL()函数中,我创建了QOffscreenSurface、QOpenGLContext和QThread,然后将QOpenGLContext移动到新的QThread中。GLWidget的上下文和QOpenGLContext被设置为共享。QThread启动后,它创建了一个QGLFramebufferObject作为QPainter的绘画设备。当QPainter完成时,威胁会向QGLWidget发出一个信号,用fbo中完成的纹理重新绘制。
在Kubuntu 14.04机器上的nvidia开源nouveau驱动程序上,如果进行多次绘图,程序隔离故障并可以吐出视频驱动程序的东西。如果使用QPainter绘制很少,一切都很好。在Nexus 4, Kubuntu 14.04英特尔第二代核心和英特尔82945G/GZ Kubuntu 14.04上似乎没问题。我怀疑暴发户只是对我不为人知的错误不那么宽容?
nouveau: kernel rejected pushbuf: Invalid argument
nouveau: ch0: krec 0 pushes 3 bufs 12 relocs 0
nouveau: ch0: buf 00000000 00000002 00000004 00000004 00000000<br>
nouveau: ch0: buf 00000001 00000013 00000002 00000000 00000002
nouveau: ch0: buf 00000002 00000016 00000002 00000002 00000000
nouveau: ch0: buf 00000003 00000007 00000002 00000002 00000000
nouveau: ch0: buf 00000004 0000000a 00000002 00000002 00000000
nouveau: ch0: buf 00000005 0000000b 00000002 00000002 00000000
nouveau: ch0: buf 00000006 00000008 00000002 00000002 00000000
nouveau: ch0: buf 00000007 00000006 00000004 00000000 00000004
nouveau: ch0: buf 00000008 00000003 00000004 00000004 00000000
nouveau: ch0: buf 00000009 0000000e 00000002 00000002 00000000
nouveau: ch0: buf 0000000a 00000017 00000002 00000000 00000002
nouveau: ch0: buf 0000000b 00000018 00000002 00000000 00000002
Segmentation fault (core dumped)
test.pro
QT += core gui widgets opengl
TARGET = test
TEMPLATE = app
SOURCES = main.cpp textThread.cpp
HEADERS = main.h textThread.h
main.h
#include <QGLWidget>
#include <QGLFunctions>
#include <QGLShader>
#include <QTimer>
#include "textThread.h"
class glview : public QGLWidget, protected QGLFunctions
{
Q_OBJECT
public:
explicit glview(QWidget *parent = 0);
~glview();
QSize sizeHint() const;
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
private:
QTimer repaintTimer;
QGLShaderProgram *program, *txtovlp;
textThread *maketext;
quint32 vbo_id[2];
private slots:
void repaint(void);
};
main.cpp
#include <QApplication>
#include "main.h"
struct vrtx {
GLfloat x;
GLfloat y;
GLfloat z;
GLfloat r;
GLfloat g;
GLfloat b;
}__attribute__((packed)) line_geo[] = {
// x, y, z, r, g, b
{1, 1, 0, 1, 0, 0},
{1, 2, 0, 0, 1, 0},
{1, 2, 0, 0, 1, 0},
{2, 2, 0, 1, 0, 0},
{2, 2, 0, 1, 0, 0},
{2, 1, 0, 0, 1, 0},
{2, 1, 0, 0, 1, 0},
{1, 1, 0, 1, 0, 0},
};
struct txtr_vrtx {
GLfloat x;
GLfloat y;
GLfloat z;
GLfloat tx;
GLfloat ty;
}__attribute__((packed)) txtr_geo[] = {
// x, y, z, tx,ty
{3, 1, 0, 0, 0},
{3, 2, 0, 0, 1},
{4, 2, 0, 1, 1},
{4, 1, 0, 1, 0},
};
glview::glview(QWidget *parent) : QGLWidget(parent)
{
connect(&repaintTimer, SIGNAL(timeout()), this, SLOT(repaint()));
repaintTimer.start(20);
}
glview::~glview()
{
delete maketext->context;
delete maketext->offscrnsf;
delete maketext;
delete program;
delete txtovlp;
}
QSize glview::sizeHint() const
{
return QSize(500, 300);
}
void glview::initializeGL()
{
initializeGLFunctions();
qglClearColor(Qt::white);
QGLShader *vshader = new QGLShader(QGLShader::Vertex, this);
const char *vsrc =
"attribute highp vec4 vertex;n"
"attribute mediump vec4 colour;n"
"varying mediump vec4 f_colour;n"
"uniform mediump mat4 matrix;n"
"void main(void)n"
"{n"
" gl_Position = matrix * vertex;n"
" f_colour = colour;n"
"}n";
vshader->compileSourceCode(vsrc);
QGLShader *fshader = new QGLShader(QGLShader::Fragment, this);
const char *fsrc =
"varying mediump vec4 f_colour;n"
"void main(void)n"
"{n"
" gl_FragColor = f_colour;n"
"}n";
fshader->compileSourceCode(fsrc);
program = new QGLShaderProgram(this);
program->addShader(vshader);
program->addShader(fshader);
program->link();
QGLShader *txtovlp_vshader = new QGLShader(QGLShader::Vertex, this);
const char *txtovlp_vsrc =
"attribute highp vec4 vertex;n"
"attribute mediump vec2 texCoord;n"
"varying mediump vec2 texc;n"
"uniform mediump mat4 matrix;n"
"void main(void)n"
"{n"
" gl_Position = matrix * vertex;n"
" texc = texCoord;n"
"}n";
txtovlp_vshader->compileSourceCode(txtovlp_vsrc);
QGLShader *txtovlp_fshader = new QGLShader(QGLShader::Fragment, this);
const char *txtovlp_fsrc =
"uniform sampler2D texture;n"
"varying mediump vec2 texc;n"
"void main(void)n"
"{n"
" gl_FragColor = texture2D(texture, texc.st);n"
"}n";
txtovlp_fshader->compileSourceCode(txtovlp_fsrc);
txtovlp = new QGLShaderProgram(this);
txtovlp->addShader(txtovlp_vshader);
txtovlp->addShader(txtovlp_fshader);
txtovlp->link();
glGenBuffers(2, vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(line_geo), line_geo, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(txtr_geo), txtr_geo, GL_STATIC_DRAW);
glEnable(GL_DEPTH_TEST);
doneCurrent();
maketext = new textThread;
maketext->offscrnsf = new QOffscreenSurface();
maketext->offscrnsf->create();
if (!maketext->offscrnsf->isValid()) {
qDebug() << "Surface Failed";
exit(1);
}
maketext->context = new QOpenGLContext();
maketext->context->setShareContext(this->context()->contextHandle());
maketext->context->create();
if (!maketext->context->isValid()) {
qDebug() << "Context Failed";
exit(1);
}
if (!maketext->context->areSharing(this->context()->contextHandle(), maketext->context)) {
qDebug() << "Sharing Failed";
exit(1);
}
maketext->context->moveToThread(maketext);
//connect(maketext, SIGNAL(finished()), this, SLOT(repaint()));
connect(maketext, SIGNAL(finished()), this, SLOT(repaint()), Qt::QueuedConnection);
maketext->start();
}
void glview::resizeGL(int w, int h)
{
makeCurrent();
glViewport(0, 0, w, h);
}
void glview::repaint(void)
{
repaintTimer.start(20);
updateGL();
}
void glview::paintGL()
{
static quint32 i;
i++;
printf("Pa");
makeCurrent();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 matrix;
matrix.ortho(0, 5, 0, 3, -1, 1);
program->bind();
program->setUniformValue("matrix", matrix);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id[0]);
int vertexLocation = program->attributeLocation("vertex");
program->enableAttributeArray(vertexLocation);
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(struct vrtx), 0);
int colourLocation = program->attributeLocation("colour");
program->enableAttributeArray(colourLocation);
glVertexAttribPointer(colourLocation, 3, GL_FLOAT, GL_FALSE, sizeof(struct vrtx), ((char*)NULL + 12));
glDrawArrays(GL_LINES, 0, sizeof(line_geo) / sizeof(struct vrtx));
txtovlp->bind();
txtovlp->setUniformValue("matrix", matrix);
maketext->textLock.lock();
if (maketext->done) {
maketext->textLock.unlock();
//qDebug() << "Painting with text" << i;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id[1]);
glBindTexture(GL_TEXTURE_2D, maketext->texture_id);
int txtr_vertexLocation = txtovlp->attributeLocation("vertex");
txtovlp->enableAttributeArray(txtr_vertexLocation);
glVertexAttribPointer(txtr_vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(struct txtr_vrtx), 0);
int texCoordLocation = txtovlp->attributeLocation("texCoord");
txtovlp->enableAttributeArray(texCoordLocation);
glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(struct txtr_vrtx), ((char*)NULL + 12));
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
}
else {
maketext->textLock.unlock();
//qDebug() << "Painting" << i;
}
glFlush();
printf("intn");
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv);
glview widget;
widget.show();
return app.exec();
}
textThread.h
#ifndef textThread_h
#define textThread_h
#include <QThread>
#include <QMutex>
#include <QOpenGLContext>
#include <QOffscreenSurface>
#include <QGLFramebufferObject>
#include <QFont>
#include <QDebug>
class textThread : public QThread
{
Q_OBJECT
public:
textThread();
~textThread();
QMutex textLock;
QOffscreenSurface *offscrnsf;
QOpenGLContext *context;
bool done;
quint32 texture_id;
signals:
void finished(void);
protected:
void run();
private:
QGLFramebufferObject *fbo;
QFont font;
};
#endif
textThread.cpp
#include <QPainter>
#include "textThread.h"
#define QPAINT_A_LOT
textThread::textThread()
{
done = 0;
fbo = NULL;
font.setFamily("Helvetica");
}
textThread::~textThread()
{
delete fbo;
}
void textThread::run()
{
context->makeCurrent(offscrnsf);
qDebug() << "Thread";
if (!fbo)
fbo = new QGLFramebufferObject(100, 100, GL_TEXTURE_2D);
fbo->bind();
texture_id = fbo->texture();
QPainter painter(fbo);
font.setPointSize(20);
painter.setFont(font);
painter.eraseRect(0,0,100,100);
painter.setPen(Qt::blue);
#ifdef QPAINT_A_LOT
quint32 i;
for (i=0; i<140000; i++) {
//if (!(i%32768))
if (!(i%1024))
qDebug() << i;
painter.drawText(0, 60, "FBO");
}
#else
painter.drawText(0, 60, "FBO");
sleep(1);
#endif
painter.end();
fbo->release();
context->doneCurrent();
textLock.lock();
done = 1;
textLock.unlock();
emit finished();
}
我切换到这段代码段故障所在的同一台机器上的闭源nvidia驱动程序,它工作正常。我也在窗户上试过,效果很好。我很确定问题是开源的nouveau驱动程序。