[Solved] Question about OpenGL Under QML example
-
Hi devs
In the example OpenGL Under QML they split the Squircle in 2 separate classes: Squircle and SquircleRenderer. "Don't be tempted to merge the two objects into one. QQuickItems may be deleted on the GUI thread while the render thread is rendering." I dont understand why this is necessary and what magic happens under the wood, maybe because I have limiited knowledge of threads and on the scene graph.The reason I'm asking is because I'm struggling with a bug in the last few days, I have created similar classes, Graph2dOpenGL and Graph2dOpenGLRenderer.
class Graph2dOpenGLRenderer : public QObject, protected QOpenGLFunctions { ... QList<Graph2D*> m_graph2DList; Graph2D *m_graph1; Graph2D *m_graph2; }; Graph2dOpenGLRenderer::Graph2dOpenGLRenderer() : ..... { ... defaultGraphs(); ... } void Graph2dOpenGLRenderer::defaultGraphs() { m_graph1 = new Graph2D; m_graph2 = new Graph2D; m_graph1->setInterval(-50,50); m_graph1->setDelta(0.1); m_graph1->setGraph2DExpression("12*cos(x+t)"); m_graph1->setupGraph(); m_graph1->prepareBuffers(); m_graph1->setBufferData(); m_graph2DList.append(m_graph1); m_graph2->setInterval("-50","50"); m_graph2->setDelta("0.1"); m_graph2->setGraph2DExpression("25*sin(3*x+t)"); m_graph2->setColor(0.25,0.78,0.89); m_graph2->setPolarGraph(true); m_graph2->setupGraph(); m_graph2->prepareBuffers(); m_graph2->setBufferData(); m_graph2DList.append(m_graph2); } void Graph2dOpenGLRenderer::paint() { ... for (int i=0;i < m_graph2DList.size();i++) { m_graph2DList[i]->draw(*m_shaderProgram); } ... }
The code above works fine, but if I need to move defaultGraphs() out of the Ctor and called it from qml side so that the user can customize the graphs, something like:
void Graph2dOpenGL::defaultGraphs() { //emit defaultGraphsChanged(); m_renderer->defaultGraphs(); }
and that's when everything breaks and I get weird openg trash lines.
What am I missing ?
-
The only thing which has any business invoking methods on your Renderer is the QSG system itself. Use the synchronization method to copy state necessary for rendering from the non-Renderer part to the Renderer part (if it's too big to copy by value, copy a reference - preferably by reference counted shared pointer, as either side can be destroyed first - and then beware of the supplier side writing shared state while the Renderer is thread is still reading it to render it). Yes it feels awkward at first, but the payoff is immense in terms of exploiting the full power of devices with a couple of feeble CPUs and a relative beast of a GPU which needs to be kept fed.
Be sure to read and understand http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html !
-
I got it working, that link you provide is handsome, cleared a lot of douts I had.
About passing the data, I just created a QList pointer in the render side, and in the sync method I assign it the adress of my QList, it's so cheap and works like charm.
Note that I have to do all the vbo handling in the render side, else with will fail to bind because not being in the opengl context, that's why I was getting all the opengl trash because the vbos being messed up.
Thanks again -
@johngod Be slightly wary that if the QList isn't actually owned by the Renderer... there's a chance it could be deleted when the non-Render bit of your application is being torn down but the QSG render thread is still doing its last frame. Using a QSharedPointer<QList> should keep the data around until everyone's done with it.
Yes having to do everything OpenGL-ey in the render thread can be a bit of a pain... especially for resources which might be shared by multiple items and you just want to initialize them and move on. (Took me a while to realize that... the platform I started off on - Linux+Nvidia - seemed quite tolerant of abuse, but then the Mac port didn't like it at all).