сбой из общего контекста QOpenGLWidget

У меня есть два QOpenGLWidget (view1, view2) как дочерние элементы в виджете верхнего уровня. В документе Qt говорится: «Когда несколько QOpenGLWidgets добавляются в качестве дочерних к одному и тому же виджету верхнего уровня, их контексты будут совместно использоваться друг с другом». Итак, view1 и view2 совместно используют контекст OpenGL. Я попытался визуализировать ту же сцену, которая инициализирована в контексте view1, и приложение вылетает в paintGL() view2. Что я сделал не так?

Вот упрощенный код:

#include <QtGui/QtGui>
#include <QtWidgets/QOpenGLWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>

static const char *vertexShaderSource =
    "attribute vec4 posAttr;\n"
    "void main() {\n"
    "   gl_Position = posAttr;\n"
    "}\n";

static const char *fragmentShaderSource =
    "void main() {\n"
    "   gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
    "}\n";

QOpenGLShaderProgram *program = nullptr;
QOpenGLBuffer arrayBuf;
int posAttr = -1;

class View3D : public QOpenGLWidget {
public:
    View3D()
    {
        setMinimumSize(300, 200);
    }
private:
    auto initializeGL() -> void override
    {
        if (program)
            return;

        program = new QOpenGLShaderProgram(this);
        program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
        program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
        program->link();
        posAttr = program->attributeLocation("posAttr");

        program->bind();

        GLfloat vertices[] = {
            0.0f, 0.707f, 0.f,
            -0.5f, -0.5f, 0.f,
            0.5f, -0.5f, 0.f,
        };

        arrayBuf.create();
        arrayBuf.bind();
        arrayBuf.allocate(vertices, sizeof(vertices));

        program->enableAttributeArray(posAttr);
        program->setAttributeBuffer(posAttr, GL_FLOAT, 0, 3, 0);

        program->release();
    }

    auto paintGL() -> void override
    {
        auto f = context()->functions();

        const auto dpr = devicePixelRatio();
        f->glViewport(0, 0, width() * dpr, height() * dpr);

        f->glClear(GL_COLOR_BUFFER_BIT);

        program->bind();
        arrayBuf.bind();

        program->enableAttributeArray(posAttr);
        f->glDrawArrays(GL_TRIANGLES, 0, 3);
        program->disableAttributeArray(posAttr);

        arrayBuf.release();
        program->release();
    }

};

auto main(int argc, char **argv) -> int
{
    QApplication app{argc, argv};

    QWidget w;
    auto hbox = new QHBoxLayout{&w};
    hbox->addWidget(new View3D); // view1
    hbox->addWidget(new View3D); // view2
    w.show();

    return app.exec();
}

person xylosper    schedule 16.03.2020    source источник


Ответы (1)


Кажется, что состояние вершины не должно совместно использоваться между контекстами, даже если эти контексты являются общими.

Вот исправленная версия:

#include <QtGui/QtGui>
#include <QtWidgets/QOpenGLWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>

static const char *vertexShaderSource =
    "attribute vec4 posAttr;\n"
    "void main() {\n"
    "   gl_Position = posAttr;\n"
    "}\n";

static const char *fragmentShaderSource =
    "void main() {\n"
    "   gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
    "}\n";

QOpenGLBuffer *arrayBuf = nullptr;
QOpenGLShaderProgram *program = nullptr;

class View3D : public QOpenGLWidget {
public:
    View3D()
    {
        setMinimumSize(300, 200);
    }
private:
    int posAttr = -1;
    auto initializeGL() -> void override
    {
        if (!arrayBuf) {
            arrayBuf = new QOpenGLBuffer;
            arrayBuf->create();
            arrayBuf->bind();
            GLfloat vertices[] = {
                0.0f, 0.707f, 0.f,
                -0.5f, -0.5f, 0.f,
                0.5f, -0.5f, 0.f,
            };
            arrayBuf->allocate(vertices, sizeof(vertices));
        }

        if (!program) {
            program = new QOpenGLShaderProgram(this);
            program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
            program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
            program->link();
        }

        posAttr = program->attributeLocation("posAttr");
        program->bind();
        arrayBuf->bind();
        program->enableAttributeArray(posAttr);
        program->setAttributeBuffer(posAttr, GL_FLOAT, 0, 3, 0);
        program->release();
    }

    auto paintGL() -> void override
    {
        auto f = context()->functions();

        const auto dpr = devicePixelRatio();
        f->glViewport(0, 0, width() * dpr, height() * dpr);

        f->glClear(GL_COLOR_BUFFER_BIT);

        program->bind();
        arrayBuf->bind();

        program->enableAttributeArray(posAttr);
        f->glDrawArrays(GL_TRIANGLES, 0, 3);
        program->disableAttributeArray(posAttr);

        arrayBuf->release();
        program->release();
    }

};

auto main(int argc, char **argv) -> int
{
    QApplication app{argc, argv};

    QWidget w;
    auto hbox = new QHBoxLayout{&w};
    hbox->addWidget(new View3D); // view1
    hbox->addWidget(new View3D); // view2
    w.show();

    return app.exec();
}

По наблюдениям, совместное использование состояния атрибута вершины также может вызвать проблему. Я знал, что не могу поделиться VAO по спецификации OpenGL, но я никогда не использовал VAO здесь явно.

Я не уверен, что это запрещено спецификацией или ошибкой драйвера. Однако, по крайней мере, для драйвера NVidia, очевидно, что вы не должны совместно использовать состояние атрибута вершины для любых случаев, включая общие контексты.

person xylosper    schedule 19.03.2020