[vlc-commits] [Git][videolan/vlc][master] qt: fix race condition while probing RHI backend

Steve Lhomme (@robUx4) gitlab at videolan.org
Sun Oct 4 06:58:48 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
ac939dde by Pierre Lamot at 2026-10-04T06:13:48+00:00
qt: fix race condition while probing RHI backend

The RHI probing is started in a background thread which requires to run some
callbacks in the main Qt event loop. If the RHI fails to initialize, then we
have an early return and the qApp is never run leading to the RHI probing task
failing to access the qApp pointer.

To avoid this, we defer the execution the probling in a Qt slot (to ensure that
the main loop is running) and we use a QEventLoopLocker to prevent the mainloop
from early returns.

fix: #30155

- - - - -


1 changed file:

- modules/gui/qt/qt.cpp


Changes:

=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -1172,69 +1172,79 @@ static void *Thread( void *obj )
         // settings value can be string (ini file), do not use `typeId()`:
         if (graphicsApiValue.isValid() && Q_LIKELY(graphicsApiValue.canConvert<int>()))
         {
-            // A cached (by then) valid graphics api is found, use it:
-            QQuickWindow::setGraphicsApi(static_cast<QSGRendererInterface::GraphicsApi>(graphicsApiValue.value<int>()));
-            if (p_intf->mainSettings->value(graphicsApiRhiSoftwareKey).value<bool>())
-                enableRhiSoftwareRenderer();
 
-            // Asynchronous re-probe to see if the cached graphics api is still applicable.
-            // If not, QQuickWindow is going to emit scene graph error, and the application is
-            // likely going to terminate. However, when the user starts the application again
-            // there will not be an error thanks to this. We can not prevent the error, as
-            // it is decided to not make compositor initializaation wait to not reduce the startup
-            // speed. Startup time is defined as the time it takes to start playing the initial
-            // item. Currently the player waits for the interface, and QQuickWindow initialization
-            // is therefore not enforced to be synchronous (`QWindow::setVisible(true)` which
-            // initializes the scene graph thus rhi is asynchronous).
-
-            class RhiProbeTask : public AsyncTask<QPair<QSGRendererInterface::GraphicsApi, bool>>
-            {
-            public:
-                RhiProbeTask() = default;
+            //some part of the probing needs to be executed on the GUI thread and some part will be ran in a background thread,
+            //so we defer the execution of this once the app is started. We can't start the task directly as qApp isn't set yet
+            //and there are scenarios where the main loop will never be run.
+            QMetaObject::invokeMethod(&app, [&](){
+                // A cached (by then) valid graphics api is found, use it:
+                QQuickWindow::setGraphicsApi(static_cast<QSGRendererInterface::GraphicsApi>(graphicsApiValue.value<int>()));
+                if (p_intf->mainSettings->value(graphicsApiRhiSoftwareKey).value<bool>())
+                    enableRhiSoftwareRenderer();
+
+                // Asynchronous re-probe to see if the cached graphics api is still applicable.
+                // If not, QQuickWindow is going to emit scene graph error, and the application is
+                // likely going to terminate. However, when the user starts the application again
+                // there will not be an error thanks to this. We can not prevent the error, as
+                // it is decided to not make compositor initializaation wait to not reduce the startup
+                // speed. Startup time is defined as the time it takes to start playing the initial
+                // item. Currently the player waits for the interface, and QQuickWindow initialization
+                // is therefore not enforced to be synchronous (`QWindow::setVisible(true)` which
+                // initializes the scene graph thus rhi is asynchronous).
+
+                class RhiProbeTask : public AsyncTask<QPair<QSGRendererInterface::GraphicsApi, bool>>
+                {
+                public:
+                    RhiProbeTask() = default;
+
+                    QPair<QSGRendererInterface::GraphicsApi, bool> execute() override
+                    {
+                        return probeRhi();
+                    }
 
-                QPair<QSGRendererInterface::GraphicsApi, bool> execute() override
+                    //prevent the main loop (qApp) from exiting until the task is complete
+                    //the lock is taken on RhiProbeTask creation which occurs in the main loop
+                    QEventLoopLocker m_applocker;
+                };
+
+                const auto rhiProbeTask = QPointer(new RhiProbeTask);
+                QObject::connect(rhiProbeTask, &BaseAsyncTask::result, qApp, [rhiProbeTask, settings = QPointer(p_intf->mainSettings)]() {
+                    // We can not use `QQuickWindow::setGraphicsApi()` here, as QQuickWindow
+                    // may have already tried to initialize the scene graph hence rhi. If the
+                    // cached graphics api is not optimal or not available anymore, the next
+                    // startup will use the refreshed value. That's the best we can do here
+                    // without forcing QQuickWindow to wait (hence delaying startup).
+                    assert(settings);
+                    const QPair<QSGRendererInterface::GraphicsApi, bool> rhiResult = rhiProbeTask->takeResult();
+                    settings->setValue(graphicsApiKey, static_cast<int>(rhiResult.first));
+                    settings->setValue(graphicsApiRhiSoftwareKey, rhiResult.second);
+                    settings->sync();
+                    qApp->setProperty(asyncRhiProbeCompletedProperty, true);
+                    qDebug() << "Asynchronous rhi re-probe completed. Graphics api (QSGRendererInterface::GraphicsApi):"
+                             << rhiResult.first
+                             << "Prefer software renderer:"
+                             << rhiResult.second;
+                    rhiProbeTask->abandon();
+                }, Qt::QueuedConnection);
+
+                const auto globalThreadPool = QThreadPool::globalInstance();
+                assert(globalThreadPool);
+
+                if (graphicsApiValue.toInt() == QSGRendererInterface::OpenGL)
                 {
-                    return probeRhi();
+                    // Due to asynchronous probing, we need to set the default
+                    // format before the main interface window's (`::create()`)
+                    // is called. Since previous probe result succeeded with
+                    // OpenGL and required version, we can assume that the there
+                    // will be again a compatible OpenGL format, rather than
+                    // storing and re-using the version (considering that it
+                    // takes negligible time to re-create the compatible format):
+                    if (const auto format = createCompatibleOpenGLFormat()) /* [[likely]] */
+                        QSurfaceFormat::setDefaultFormat(*format);
                 }
-            };
 
-            const auto rhiProbeTask = QPointer(new RhiProbeTask);
-            QObject::connect(rhiProbeTask, &BaseAsyncTask::result, qApp, [rhiProbeTask, settings = QPointer(p_intf->mainSettings)]() {
-                // We can not use `QQuickWindow::setGraphicsApi()` here, as QQuickWindow
-                // may have already tried to initialize the scene graph hence rhi. If the
-                // cached graphics api is not optimal or not available anymore, the next
-                // startup will use the refreshed value. That's the best we can do here
-                // without forcing QQuickWindow to wait (hence delaying startup).
-                assert(settings);
-                const QPair<QSGRendererInterface::GraphicsApi, bool> rhiResult = rhiProbeTask->takeResult();
-                settings->setValue(graphicsApiKey, static_cast<int>(rhiResult.first));
-                settings->setValue(graphicsApiRhiSoftwareKey, rhiResult.second);
-                settings->sync();
-                qApp->setProperty(asyncRhiProbeCompletedProperty, true);
-                qDebug() << "Asynchronous rhi re-probe completed. Graphics api (QSGRendererInterface::GraphicsApi):"
-                         << rhiResult.first
-                         << "Prefer software renderer:"
-                         << rhiResult.second;
-                rhiProbeTask->abandon();
+                rhiProbeTask->start(*globalThreadPool, -1);
             }, Qt::QueuedConnection);
-
-            const auto globalThreadPool = QThreadPool::globalInstance();
-            assert(globalThreadPool);
-
-            if (graphicsApiValue.toInt() == QSGRendererInterface::OpenGL)
-            {
-                // Due to asynchronous probing, we need to set the default
-                // format before the main interface window's (`::create()`)
-                // is called. Since previous probe result succeeded with
-                // OpenGL and required version, we can assume that the there
-                // will be again a compatible OpenGL format, rather than
-                // storing and re-using the version (considering that it
-                // takes negligible time to re-create the compatible format):
-                if (const auto format = createCompatibleOpenGLFormat()) /* [[likely]] */
-                    QSurfaceFormat::setDefaultFormat(*format);
-            }
-
-            rhiProbeTask->start(*globalThreadPool, -1);
         }
         else
         {



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/ac939dde997ba1a13e454cf6b96bfd4e2079b33f

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/ac939dde997ba1a13e454cf6b96bfd4e2079b33f
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list