QPainter在QThread中使用共享QOpenGLContext - 在nouveau上发生错误

时间:2014-08-12 00:53:36

标签: multithreading qt opengl qt5

我试图学习如何使用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 Intel第二代Core和Intel 82945G / GZ Kubuntu 14.04上看起来很好。我怀疑nouveau对我未知的错误的容忍度较低?

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("int\n");
}

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();
}

1 个答案:

答案 0 :(得分:1)

我切换到该代码段故障的同一台机器上的闭源nvidia驱动程序,它工作正常。我也在Windows上尝试过它很好。我很确定这个问题是开源的nouveau驱动程序。