Qt6 + QML / OpenCascade 7.6 Problem

I'm trying to build an application using Qt 6.2.3 with QML and OpenCascade 7.6. I encountered a problem of program crashing just after the start. After several debugging sessions I found that the problem is related with OpenGL as attempts to get current OpenGL context and device weren't succesful (wglCurrentContext() and wglCurrentDC() both returned nothing as I got from memory inspection). I referred to Anton Shabalin's example and AndroidQt sample in code writing process, also tried to build and run the first, with the same result. I tried to build and run my program with Qt 5.15 toolkit, I had semi-success - the program just didn't draw anything (in Application Output I saw "TKOpenGL.WinSystem error - wglMakeCurrent() has failed"). I'm not really proficient in OpenGL so I would really appreciate a help on this issue.
The code where error occurs is at the slot for synchronization, particularly the function for a viewer initialization. I request device and render contexts with aforementioned functions, set up display driver, viewer, context and try to initialize a window through Handle(WNT_Window) window = new WNT_Window(drawable, render_context); - that's where I get an exception.

Kirill Gavrilov's picture

Qt 6 implements several new backends for QtQuick, and OpenGL is not default one on Windows platform. You might want explicitly asking Qt to load OpenGL backend (something like QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL)) or build Qt with OpenGL backend (configure -opengl desktop). The same applies to Qt 5, though there OpenGL implementation is usually selected by default, save some exceptions where Qt decides that OpenGL drivers are not good enough (have no idea by what criteria). Just as starting point.

Ekaterina Evdokimova's picture

So, after some research I found the solution. The key is to be aware of where you are working at - at GUI thread or rendering thread as they are separated in QtQuick Qt6 by default. Indeed, setting explicitly the usage of OpenGl as a rendering backend should be done and it's done for the rendering thread, but classes derived from QQuickItem are considered by Qt as being at GUI thread so when the called slots for synchronization and rendering are from QQuickItem there is no any existing OpenGl context. So, the solution is to separate classes for rendering and for GUI.
More detailed explanations can be found at
https://doc.qt.io/qt-6/qtquick-scenegraph-openglunderqml-example.html (also a practical example - the reason why I don't provide my own code)
https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph.html

Ekaterina Evdokimova's picture

Also you can set the usage of a single thread for both GUI and rendering but it's not recommended due to portability issues.

Kirill Gavrilov's picture

 Ekaterina Evdokimova wrote:

Also you can set the usage of a single thread for both GUI and rendering but it's not recommended due to portability issues.

In contrary - dedicated rendering OpenGL thread used by Qt5 / QtQuick by default is a non-portable way. The only platform that I known that might have problems with single-threaded implementations is Android, where the key class GLSurfaceView is always expected to perform rendering in non-GUI thread. So I guess yes, if you are targeting Android platform as well, then you would certainly want debugging multi-threading issues in a desktop build - for portability and convenience reasons.

For instance, new macOS versions suddenly decided to "deprecate" usage of OpenGL from non-GUI threads, making existing multi-threaded OpenGL applications (including Qt5) broken just by upgrade of macOS SDK. And for this reason Qt 5.14 changed this default behavior to single-threaded on macOS platform:

https://www.qt.io/blog/qt-quick-on-vulkan-metal-and-direct3d-part-2
https://bugreports.qt.io/browse/QTBUG-79315

One important feature here is the ability to run with the threaded render loop of Qt Quick. This is very welcome because due to threading problems of certain NSOpenGLContext and related APIs in macOS 10.14, Qt disables the threaded loop with OpenGL on macOS, defaulting to the 'basic' loop instead. This leads to a somewhat reduced smoothness in Qt Quick animations.

With Metal we do not have any issues with the threaded setup, so we can once again default to the threaded render loop. (the default choice of the render loop can be overridden by setting the QSG_RENDER_LOOP environment variable; this variable is supported also in combination with QSG_RHI)

The main drawback of a single-threaded implementation is potentially slower performance / larger GUI lag.

//! Setup QtQuick Scene Graph rendering parameters.
static void setupQtRenderParams()
{

#if defined(_WIN32)
  // never use ANGLE on Windows, since OCCT 3D Viewer does not expect this
  QCoreApplication::setAttribute (Qt::AA_UseDesktopOpenGL);
  //QCoreApplication::setAttribute (Qt::AA_UseOpenGLES);
#endif

#if !defined(__ANDROID__)
  {
    // Workaround broken glyphs on Apple M1 and on Windows 10 (should be fixed in Qt6):
    // https://bugreports.qt.io/browse/QTBUG-86429
    OSD_Environment aVarGlyph ("QT_ENABLE_GLYPH_CACHE_WORKAROUND");
    if (aVarGlyph.Value().IsEmpty())
    {
      aVarGlyph.SetValue ("1");
      aVarGlyph.Build();
    }
  }
#endif

#if defined(__APPLE__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
  {
    // macOS 10.14 SDK deprecated usage of OpenGL from non-GUI thread in a weird way:
    // - Applications built against older SDKs work well with multiple threads.
    // - Applications built against 10.14+ SDK crash on attempt to use OpenGL from non-GUI thread.
    // This limitation has been introduced only for OpenGL and not Metal.
    //
    // Qt 5.14 has changed defaults and switched to single-threaded OpenGL implementation with statement
    // "this leads to a somewhat reduced smoothness in Qt Quick animations".
    // To work with older Qt, application should be either built using older SDK
    // or to set QSG_RENDER_LOOP=basic environment variable instead of default QSG_RENDER_LOOP=threaded.
    // https://bugreports.qt.io/browse/QTBUG-79315
    // https://tracker.dev.opencascade.org/view.php?id=31391
    //
    // As alternative to using older SDK, it is also possible modifying SDK info
    // within Mach-O header of already built application by tools like vtool:
    // > vtool ./myapp -set-build-version 1 10.0 10.0 -output ./myapptest
    // Only executable should be modified.
    //
    // It would be graceful reading SDK version from Mach-O header at application startup
    // to conditionally enable workaround, but this would require a plenty of code
    // as there is no simple function in macOS SDK for that.
    // So let's enable it unconditionally and make users suffer from their vendor policy.
    // Performance difference wasn't tested  deeply - probably not a big loss anyway.
    OSD_Environment aVarRendLoop ("QSG_RENDER_LOOP");
    if (aVarRendLoop.Value().IsEmpty())
    {
      aVarRendLoop.SetValue ("basic");
      aVarRendLoop.Build();
    }
  }
#endif
}