1

I am using QtQuick with a custom OpenGL renderer (custom from the point of view of QtQuick as it is simply OpenSceneGraph). In order to do so I create a custom QQuickItem inheriting from QQuickFramebufferObject and that in turns creates a custom renderer inheriting from QQuickFramebufferObject::Renderer in QQuickFramebufferObject::createRenderer(). This is well documented and there is no problem with these steps.

Now what happens is that in order to be later accessed, the renderer created in QQuickFramebufferObject::createRenderer() is actually cached (it is actually instantiated in QQuickFramebufferObject constructor and simply returned in QQuickFramebufferObject::createRenderer(). This works fine and I can see no straightforward other way to code it as the created renderer is later used to react to events such as geometryChanged or mousePressEvent, e.g.

////////////////////////////////////////////////////////////////////////////////
void OsgItem::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
    if (m_renderer)
        m_renderer->m_window->getEventQueue()->windowResize(newGeometry.x(), newGeometry.y(), newGeometry.width(), newGeometry.height());


    QQuickFramebufferObject::geometryChanged(newGeometry, oldGeometry);
}

////////////////////////////////////////////////////////////////////////////////
void OsgItem::mousePressEvent(QMouseEvent *event)
{
    m_renderer->m_window->getEventQueue()->mouseButtonPress(event->x(), event->y(), button(*event));

    update();
}

, where OsgItem is my custom QQuickFramebufferObject and m_renderer is my custom QQuickFramebufferObject::Renderer.

The problem is that createRenderer() is const (which is not really an invitation to cache things), and this paper clearly states that the renderer should not be cached -(although it is not stated in the official doc).

What's the catch here? Is there something that I have missed? Can you see another clean way I could code things?

genpfault
  • 51,148
  • 11
  • 85
  • 139
arennuit
  • 855
  • 1
  • 7
  • 23

2 Answers2

3

Velkan provided the principle of integrating Raw/Custom OpenGL with QQuickFramebufferObject. As we have just finished a project which also integrates OSG and QtQuick, I'd like to share some of our experiences.

Yes of course, you should never cache a renderer. The createRenderer is const for a reason, and that's how the Qt team designed the classes. According to our experiments, the createRenderer function may be called multiple times.

To sync between QQuickFramebufferObject and its renderer, we add a QQueue in QQuickFramebufferObject, and when any events we are interested happen, we put them in the queue. And in the renderer, when the synchronize is called, we copy the queue from the fbo to the renderer, and execute certain operations in the render function.

Jimmy Chen
  • 490
  • 3
  • 13
  • Unrelated but @Jimmy, you must have encountered the situation: did you have any problem with vertically flipped 3D scenes when coding this app you are mentioning? – arennuit Aug 25 '17 at 16:47
  • @arennuit Yes, we have. Call `setMirrorVertically(true)` which acts like a charm :) – Jimmy Chen Aug 26 '17 at 06:23
  • This works fine for the viewing and mouse interactions. Now for the picking I had to provide the intersector with "width - y" rather than "y". Is this also something you encountered? I cannot really understand what is going on... – arennuit Aug 26 '17 at 19:21
2

Well, renderer gets a non-const QQuickFramebufferObject in the synchronize(QQuickFramebufferObject *item).

It happens because you aren't supposed to touch the Renderer in the GUI thread with things like mousePressEvent (Renderer doesn't have any functions that can execute in the GUI thread, as I see). The Renderer::synchronize() is executed in the render thread while the GUI thread is blocked: so it's the place to do the data transfer.

In general, the GUI thread must demand sync when there is something to change because of the input. That causes it to stop, and then synchronize() for the scene-graph is called in the render thread. There is a sync diagram (doesn't mention the Renderer, but it must obey the same principle): http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html

Renderer::render() happens in the render thread while the GUI thread is running freely as I remember. So, no data can be passed there.

QQuickItem::geometryChanged is in the GUI thread (probably check with the debugger), so don't mix the render.

Velkan
  • 7,067
  • 6
  • 43
  • 87