Qt and OpenGL, using one context for multiple widgets - c++

I recently asked a question about how to get around sharing issues with vertex array objects and frame buffer objects across multiple contexts, I was then convinced that using multiple contexts just caused more headaches then solutions.
I am using Qt and currently my setup is that I have one invisible QGLWidget which I then use in the constructor of my visible QGLWidget's in order to share resources, this works great accept that I cannot share certain things across the contexts.
I wish to find a solution where I am able to use a single context to render all of my different widget's, this question refers to using the QGLWidget constructor where you pass in the QGLContext you desire to be shared, however this does not seem to use one common context, but instead set the context to be used by one QGLWidget, when you try to use it on a second widget, a qWarning is called which informs you that the QGLContext must refer to the widget you are passing it to.
The goal of my application is to have 2 seperate GUI's which render different scenes, yet share the same context. Currently I have a 'World' editor which edits a scene and saves it to a file to be used in my game engine, and I also have a 'Material' editor which allows you to graphically edit a material similar to UDK's Material editor, there is a preview window which utilizes OpenGL.
Ideally I would like to keep my current design of having one unified game editor which is navigable by tabs, rather than having separate programs for each part of the editor.
The only thing that seemed like it was a decent solution was using the QGraphicsView and setting a QGLWidget as the viewport, however this does not seem to work at all. I can render basic primitives, however anything more and it falls apart.
Does anyone have experience dealing with this issue of multiple OpenGL Widgets, and if so could you explain the process you took to achieve your goal?

I don't quite understand why you are having so much trouble, I'm building a CAD-like app so share a few contexts, like this:
I use an application-wide hidden QGLWidget as a member of my main window class, this is the context shaders are loaded in.
For each document window, the window class has a hidden QGLWidget member, this is the context geometry is loaded in. The shader context is used as the 'shared' widget for it, allowing documents access to the application wide shaders.
Each of the 5 viewports in each document window is a visible QGLWidget, this is where the actual rendering takes place. The document window geometry QGLWidget is used as the 'shared' widget, so the viewports have access to the document-wide geometry data and the application-wide shaders.
The shared widget parameter allows you to create an 'inheritance' tree of contexts, every context has access to it's own and all it's ancestors data (but not it's childrens or siblings).

Related

Should I use a singleton.?

I am planning on making a 3D scene editor app using c++ and open gl. And I have to keep track of the current loaded project and different scenes it contains also the user preferences and other things. The best solution I can think of is to wrap them in a context class which will be a singleton. Is there a better way of doing this?
Not a good idea, probably. One reason is technical. The actual OpenGL context's lifetime is limited and is far shorter than app itself, usually related to surface you're outputting to. You would need to initialize context after your visualizing window is ready and de-initialize it before window is gone. Trying to do so when window gone may end to undefined behaviour depending on platform. In some cases you might need several contexts.
Another reason is, it doesn't look like proper separation of responsibilities. User settings aren't part of context, but some may affect only a single render pass (out of plural). You likely would have Preferences, Renderer which would be an interface to Context manager, Geometries, Textures (or materials) separate, an Scene Manager as well (think of scene tree in Blender or DAZ studio, each item in scene can have separate user settings, regarding how to visualize them).

QOpenGLWidget: retrieving window handle for 3rdparty library

I'm creating an application, which interacts with OpenGL via QOpenGL* classes. The graphics is shown through a QOpenGLWidget, which is placed in a UI-form.
Now, there is a library for CAD purposes (Open CASCADE), an OpenGL interface of which requires a handle to the render window. The question is: can I somehow say the library to render everything to the mentioned widget?
In other words, is there a way to interpret the widget as a native, probably, platform-specific (HWND here) window, so that the library renders its own stuff exactly there?
Thanks
QOpenGLWidget is not the same thing as QGLWidget.
The classical approach for embedding OCCT 3D viewer, which you can find in Qt IESample coming with OCCT, creates QWidget with unique window handle flag, takes this window handle and ask OCCT to take care about OpenGL context creation for this window. This is most straightforward, robust and portable way with one limitation - Qt will not be able drawing semitransparent widgets on top of this QWidget. This is not a limitation of OCCT, but rather limitation of Qt Widgets design.
QOpenGLWidget was intended to solve this limitation by allowing to mix custom OpenGL rendering and normal widgets. The integration of external OpenGL graphics engine, however, became more complicated and fragile. It is not very helpful stealing winId() from QOpenGLWidget, as rendering content is expected to be drawn not into window itself, but rather into OpenGL framebufer object (FBO), created by QOpenGLWidget - see QOpenGLWidget::defaultFramebufferObject() property.
External renderer is expected to render into this FBO for proper composition of Qt widgets. Luckily, OCCT is flexible enough to allow such integration. Unluckily, such integration requires some knowledge of OpenGL, as well as its usage by Qt and OCCT.
For that, you need to ask OCCT wrapping OpenGL context already created by Qt (for that V3d_View::SetWindow() provides an optional argument of type Aspect_RenderingContext, which corresponds to HGLRC on Windows and can be fetched using wglGetCurrentContext() within rendering thread) as well as FBO created by QOpenGLWidget (for that, OCCT provides OpenGl_FrameBuffer::InitWrapper() and OpenGl_Context::SetDefaultFrameBuffer() methods, as well as OpenGl_Caps::buffersNoSwap flag to leave window buffer swapping management to Qt).
OCCT doesn't come yet with a sample using QOpenGLWidget, but you can find also qt/AndroidQt sample implementing similar thing for embedding OCCT 3D Viewer into QtQuick application.
After some investigation, I found that method QOpenGLWidget::winId() returns the correct handle. It's been found out only now, because the rendered scene disappeared immediately, leaving a black picture instead. However, when the viewport is resized, the scene returns back (and disappears again, though). Looks like Open CASCADE has problems with Qt 5 OpenGL implementation, since QGLWidget didn't have such problems, as far as I know.

glCreateShader gives same ID

Background :
I have a Shader class in my c++/OpenGL3.1/GLSL/Qt program. My program uses several shaders based on different GLSL sources files.
My application can run many different 3D renderers based on the QGLWidget implementation and each one creates its own shaders.
When I create my first 3d renderer and initialize my shaders, shaders IDs are generated with the help of glCreateShader & glCreateProgram, without any problem.
Problem :
But when I create a second 3d renderer, the OGL functions retrieving the ID give exactly the same but I expect to have new ones. It means that my two renderers will send the data to the same GPU program...
It's obvious that in the GPU program, uniform variables are mixed and when running the second renderer, the first one displays a weird rendering.
Indeed, when I close one of the two renderer, all shaders are killed... and the second renderer cannot display anything.
Idea ?
I'm completely lost and my logical deduction is that glCreateShader & glCreateProgram give ID according to their own thread id. QGLWidget running probably its own thread to call the rendering functions, it may trouble the persistence...
Any idea of how to solve this problem ?
If both your QOpenGLWidget share the same parent window, then by default they share the same context. If you don't want that, the easier is probably to create a new top-level widget (any QWidget with no parent) with your second QOpenGLWidget.
Please note that this is different from the older QGLWidget class. From Qt documentation:
When multiple QOpenGLWidgets are added as children to the same top-level widget, their contexts will share with each other. This does not apply for QOpenGLWidget instances that belong to different windows.
This means that all QOpenGLWidgets in the same window can access each other's sharable resources, like textures, and there is no need for an extra "global share" context, as was the case with QGLWidget.
There is no problem to solve.
Each QGLWidget has its own OpenGL context. And, unless you are explicitly sharing objects between them, each context has its own separate list of objects.
You can only use an OpenGL object with the OpenGL context that created it. So long as you keep the objects separate, and only use them with the context that created it, you should be fine.

Get only one QOpenGLContext for different QT widgets

I've the following problem :
I want to get an application composed of many view which render a common OpenGL scene from a different point of view, illumination, and others options.
Basically, my question is what is the best way to do that with qt ?
My first attempt was to create multiple QOpenGLWidget and get a common QOpenGLContext where I stored the textures but also the meshes and shaders.
But it didn't work for meshes because Vertex Array Objects seem to not be shareable.
After lot of tries, a possible solution is to store one VAO for each widget that need the mesh but this look really awful.
So, I wonder if there is a good alternative for this kind of problem, or maybe a good documentation to understand how these QOpenGLContext work.
The simplest idea that I've imagined is to create only one QOpenGLContext and use it in the different widgets. But I don't know how to just create a QOpenGLContext alone nor what kind of QWidgets is able to display these renderings.
It's my first post so I don't know if it's clear enough or if I need to describe my whole architecture.
You already tried, so I pass the word about shared contexts.
An OpenGL context is bound to a window: if you want only one context, the straight answer is to have only one window.
Using the widgets module, you can have multiple views of a same scene using multiple viewports in a same QOpenGLWidget. Something like:
void myWidget::paintGL() {
//...
glViewport(
0, 0,
this->width()/2, this->height()/2
);
// draw scene from one point of view
glViewport(
this->width()/2, this->height()/2,
this->width()/2, this->height()/2
);
// draw scene from an other point of view
//...
}
You should probably design a viewport class to store and manage the rendering parameters for each viewport.
The drawback is that you will have to detect in which viewport the user is clicking to handle interactions: some kind of if event.pos.x is between 0 and this->width()/2 ....
An other way could be to let down the widgets module and use Qt Quick and QML: a quick window declares a unique OpenGL context, where each quick item is like a viewport, but encapsulated in its own object so you don't have to think about where the user is interacting.
Inherit QQuickItem instead of QOpenGLWidget and export your class to QML using the qmlRegisterType() macro. You can then create a QQuickView in your program to load a QML code where you declare your items. An example from Qt's documentation here.
I think since multiple views/surfces can update independently, unfortunately its not possible to have one single QOpenGLContext that does the job. And sharing contexts have the limitation you already point out in your question.
QOpenGLContext can be moved to a different thread with moveToThread().
Do not call makeCurrent() from a different thread than the one to
which the QOpenGLContext object belongs. A context can only be current
in one thread and against one surface at a time, and a thread only has
one context current at a time.
Link : http://doc.qt.io/qt-5/qopenglcontext.html
So one way you can get it working is have independent updates to your views in a sequential order and make the context current one by one and render before moving on to the next view. This will guarantee that the context is current in only one view at any given time. Perhaps use a QMutex to serialize the updates.
Alternatively you can also pass the context around among threads and serialize their updates, but this is a bad approach.

Issues creating independent OpenGL Viewports

Goal:
In my application I'm trying to implement multiple viewports to allow the user to view a scene from multiple perspectives. Each of my viewports need to be able to switch between wireframe, shaded, lighting, etc. I can currently render from different perspectives in each viewport, but I have issues.
Problem:
When I try to set various settings such as glPolygonMode() or qglClearColor() within any viewport, these settings only seem to apply to a single viewport, generally the very last viewport that was created. This isn't a signals/slots issue, since these connections are handled internally within each widget, and cannot be mixed up between widgets.
Attempts at solving the problem:
Since I'm using Qt as the library for managing all UI related things, I'm sure there are a lot of things Qt has taken care of for creating and setting up each OpenGL instance for me, so there may be things that I'm overlooking that I don't know about.
I've checked the constructors available for QGLWidgets, and seen that a QGLWidget can take in another QGLWidget as a "sharedwidget", and also a QGLContext object.
I currently use the "sharedwidget" route, because without it for some reason I can't get textures to bind for more than 1 viewport. However, this doesn't solve the problem of not being able to switch between wireframe or shaded in each QGLWidget instance.
I've also tried the QGLContext route. By default each QGLWidget
creates a new context anyways, but when trying to assign new ones or
sharing a single Context between all of them I would just get issues
with my shaders not linking (I believe the initializeGL slot is not
getting called in that case), leading to a crash every time a context is shared to another QGLWidget:
ASSERT: "QOpenGLFunctions::isInitialized(d_ptr)" in file
c:\work\build\qt5_workdir\w\s\qtbase\include\qtgui../../src/gui/opengl/qopenglfunctions.h,
line 2018
Details:
Currently, my application takes on the following hierarchy:
Application
Window
ViewportWidget [dynamic array]
QGLWidget (custom variation)
The only thing each QGLWidget needs to share is the pointer to the current "map", so that each can render the map based on whatever settings are set within that particular widget's instance.
I perform the following functions for setting up a viewport:
I create a new ViewportWidget, parent it and add it to the appropriate frame and Layout. If the viewport isn't the first one, then it also passes the very first QGLWidget to be used as a "sharedwidget"
The viewport then creates a QGLFormat with a swap interval of 1, and passes said format into the constructor of a new QGLWidget.
I then am forced to call "makeCurrent()" for the viewport, otherwise I crash with the reason:
ASSERT: "false" in file qgl.cpp, line 122
Is it even possible to have separate QGLWidgets with different "polygonMode"'s, or "clearColor"'s? I'm just worried that I'm doing something wrong that will bite me in the butt later on, which I want to avoid.