Render one scene to multiple windows with same context

Hello,

I read some articles how to draw for example in my case one triangle to two different top-level windows. They are both sharing context.
My hierarchy is following:
> Application > MainWindow > GraphicsWindow1 > Triangle
> GraphicsWindow2 (contains link to GraphicsWindow1)

Problem:
When GraphicsWindow2 renders he take Triangle from GW1 and calls Draw. I suppose that should be all. But there is not drawed triangle in GW2.

My goals: render in GW2 same thing as in GW1 but with different camera. Also I tried to build this with some efficient like few lines of code.

Source code is available on github: GitHub - glararan/Qt-OpenGL: Not yet nammed project for editing World of Warcraft files

GraphicsWindow inherits from GLWindow which inherits from QOpenGLWindow.

GraphicsWindow location: https://github.com/glararan/Qt-OpenGL/blob/master/src/ui/graphicswindow.cpp
Triangle location: https://github.com/glararan/Qt-OpenGL/blob/master/src/core/triangle.cpp
Shaders are located here: Qt-OpenGL/bin/shaders/basic at master · glararan/Qt-OpenGL · GitHub

I have to missing something, just I don’t know what.

Thanks for help!

Well I figured out, that problem is located in Triangle.cpp file in Draw function. It seems VAO can’t be shared for multiple windows?

void Triangle::Draw(const QMatrix4x4& mvp){
    qDebug() << "Triangle target1:	" << QOpenGLContext::currentContext()->surface() << "	" << functions->glGetError(); // returns Triangle target1:	 0x10d6a98 	 0


    shader->bind();
    qDebug() << "Triangle target2:	" << QOpenGLContext::currentContext()->surface() << "	" << functions->glGetError(); // returns Triangle target2:	 0x10d6a98 	 0
    {
        shader->setUniformValue(mvpLoc, mvp);


        qDebug() << "Triangle target3:	" << QOpenGLContext::currentContext()->surface() << "	" << functions->glGetError(); // returns Triangle target3:	 0x10d6a98 	 0
        //glBindVertexArray(vao);
        vao.bind(); // when I move qDebug() "target3" it returns glGetErrors() 1282


        functions->glDrawArrays(GL_TRIANGLES, 0, 3);
        vao.release();


        qDebug() << "Triangle target4:	" << QOpenGLContext::currentContext()->surface() << "	" << functions->glGetError(); // returns Triangle target4:	 0x10d6a98 	 1282
    }
    shader->release();
}

How would you solve this? :o

When I want to draw same scene in two windows… always create vao for each sub-object?

Thanks

VAOs aren’t shared between contexts. More generally, objects which contain references to other objects aren’t shared (more details here).

The simplest way is not to use a different context for each window. But that isn’t an option if the windows may be on different physical displays, or if you need to render from multiple threads concurrently.

If you need to use multiple contexts, then create a copy of the VAO for each context. Note that you can’t force them to have the same name (handle) in different contexts, so you’ll need some way to find the correct VAO for a given context.

[QUOTE=GClements;1283318]VAOs aren’t shared between contexts. More generally, objects which contain references to other objects aren’t shared (more details here).

The simplest way is not to use a different context for each window. But that isn’t an option if the windows may be on different physical displays, or if you need to render from multiple threads concurrently.

If you need to use multiple contexts, then create a copy of the VAO for each context. Note that you can’t force them to have the same name (handle) in different contexts, so you’ll need some way to find the correct VAO for a given context.[/QUOTE]

Ok so, I’m currently sharing context so I have to recreate for second window vao.

EDIT:

So for let this working I had to make this edit:

void Triangle::Draw(const QMatrix4x4& mvp, bool copy){
    shader->bind();
    {
        shader->setUniformValue(mvpLoc, mvp);


        if(!copy)
        {
            //glBindVertexArray(vao);
            vao.bind();


            functions->glDrawArrays(GL_TRIANGLES, 0, 3);
            vao.release();
        }
        else
        {
            vbo.bind();


            QOpenGLVertexArrayObject _vao;
            _vao.create();
            _vao.bind();


            shader->enableAttributeArray(0);
            shader->enableAttributeArray(1);
            shader->setAttributeBuffer(0, GL_FLOAT, 0, 2, 0);
            shader->setAttributeBuffer(1, GL_FLOAT, 6 * sizeof(GLfloat), 3, 0);


            _vao.bind();


            functions->glDrawArrays(GL_TRIANGLES, 0, 3);


            _vao.release();
            vbo.release();
        }
    }
    shader->release();
}

Any idea how to make this better?

First, binding the VBO serves no purpose here, because attribute array bindings are stored in the current VAO.

Second, you shouldn’t be creating a new VAO for each call to the Draw method; you should be storing it. Currently, GLObject has a member variable, vao of type QOpenGLVertexArrayObject, whereas it should probably have a vector/map/etc of such, with the context (or some context-related property) as the key. Or the window class should have a vector/map/etc, with the object as a key.

But unless you actually need multiple contexts, you should just use a single context for all windows. Ignore the context which Qt creates and explicitly use makeCurrent() to make the global context current.

[QUOTE=GClements;1283322]First, binding the VBO serves no purpose here, because attribute array bindings are stored in the current VAO.

Second, you shouldn’t be creating a new VAO for each call to the Draw method; you should be storing it. Currently, GLObject has a member variable, vao of type QOpenGLVertexArrayObject, whereas it should probably have a vector/map/etc of such, with the context (or some context-related property) as the key. Or the window class should have a vector/map/etc, with the object as a key.

But unless you actually need multiple contexts, you should just use a single context for all windows. Ignore the context which Qt creates and explicitly use makeCurrent() to make the global context current.[/QUOTE]

  1. When I dont bind VBO while creating VAO app crashes. But I dont want to have multiple vao
  2. Yes I know but it was fast debug, sorry :frowning: GLObject variable vao is for main window where scene is rendered correctly, while I bind same vao while drawing on second window, nothing is rendered.
  3. I am using single context, the one created by main window. makeCurrent using in GLWindow::renderNow() look below.
void GLWindow::renderNow(){
    if(!isExposed())
        return;


    if(!painted)
        return;


    painted = false;


    QOpenGLWindow::makeCurrent();


    QOpenGLWindow::update();


    context()->swapBuffers(this);


    if(draw && !pendingUpdate)
    {
        pendingUpdate = false;


        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
    }
}

So if I correct understand, I should create one global context before I even create mainwindow and use it for both windows.

This just calls the superclass’ makeCurrent() method. you’d need to use something like


mainwindow->context()->makeCurrent(this);.

[QUOTE=glararan;1283323]
So if I correct understand, I should create one global context before I even create mainwindow and use it for both windows.[/QUOTE]
You should be able to use the context created for the main window everywhere. But you’ll need to ensure that you don’t implicitly use the context created for the second window. Personally, I’d suggest deriving from QPaintDeviceWindow rather than QOpenGLWindow so that you can do it right. That’s likely to be easier than trying to explicitly work around all of QOpenGLWindow’s mistakes.

[QUOTE=GClements;1283325]This just calls the superclass’ makeCurrent() method. you’d need to use something like


mainwindow->context()->makeCurrent(this);.

You should be able to use the context created for the main window everywhere. But you’ll need to ensure that you don’t implicitly use the context created for the second window. Personally, I’d suggest deriving from QPaintDeviceWindow rather than QOpenGLWindow so that you can do it right. That’s likely to be easier than trying to explicitly work around all of QOpenGLWindow’s mistakes.[/QUOTE]

Last night I read this article: https://blog.qt.io/blog/2014/11/20/qt-weekly-20-completing-the-offering-qopenglwindow-and-qrasterwindow/
If I would deriving just from QWindow this should work like QPaintDeviceWindow

EDIT: Ok I’m inheriting from QPaintDeviceWindow
EDIT2: Ok I basically took QOpenGLWindow code and removed creating context on init, instead I give him already created to use, working for now, just I have to clean code and fix swapBuffers error, so for now its solved, thank you!

VAOs can’t be shared between contexts (nor can FBOs, program pipeline objects or transform feedback objects).

You can use a single context on multiple windows, provided that all windows are compatible with the context (typically this means that they must be on the same screen or on screens which correspond to the same video device, and use the same or similar formats).

The issue here is with QOpenGLWindow, which “conveniently” creates a context for the window and ensures that it’s current before invoking methods which are likely to use the context. That approach may have been fine for OpenGL up to version 2 (where contexts which shared data shared all objects), but doesn’t really work for OpenGL 3 or later due to container objects being non-shareable.

Of course, you can just re-create any container objects for each context, but then an application-defined class can’t be associated with a single container (e.g. VAO), it needs one container per context.

Well, there is final code: https://github.com/glararan/Qt-OpenGL/commit/2b90e93829f294a0acb866b0eeaffc6e092530a8 but I forgot to remove isCopy… but compiler let you know what to remove.
There is currently just one more limitation, maximum fps is based on your monitor. But when you close second window, you regain power and FPS is around 2 thousand.