[vlc-commits] [Git][videolan/vlc][master] 3 commits: qt: probe rhi asynchronously and cache the valid result for the next run

Steve Lhomme (@robUx4) gitlab at videolan.org
Mon Mar 24 10:17:46 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
22e9a827 by Fatih Uzunoglu at 2025-03-24T09:55:18+00:00
qt: probe rhi asynchronously and cache the valid result for the next run

Startup time is defined as the time it takes to start playing
the initial item when the application is opened.

Currently probing is done synchronously each time the interface
starts. This is not ideal, as we don't expect the system to
suddenly start supporting a particular graphics api. Obviously,
due to hardware change or driver update or any reason this may
change, so we still need to check that each time.

In this case, the worst can happen is that `QQuickWindow`
emits error and terminates the application (usually with an
error message box) when the api is no longer supported.
However, since the cached api is checked each time asynchronously,
the next startup would be fine.

This basically improves the startup performance, at the expense
of causing an error and terminating the application (in the
worst case) if the system suddenly starts not supporting once
valid api. The worst case is a rare case, so I believe that
this would be a good optimization.

- - - - -
0ba14517 by Fatih Uzunoglu at 2025-03-24T09:55:18+00:00
qt: add `Compositor::quickWindow()`

- - - - -
d668d601 by Fatih Uzunoglu at 2025-03-24T09:55:18+00:00
qt: graceful exit on scene graph error in all cases

Currently only `CompositorDirectComposition` does graceful
exit on scene graph error.

- - - - -


15 changed files:

- modules/gui/qt/maininterface/compositor.cpp
- modules/gui/qt/maininterface/compositor.hpp
- modules/gui/qt/maininterface/compositor_dcomp.cpp
- modules/gui/qt/maininterface/compositor_dcomp.hpp
- modules/gui/qt/maininterface/compositor_dummy.cpp
- modules/gui/qt/maininterface/compositor_dummy.hpp
- modules/gui/qt/maininterface/compositor_platform.cpp
- modules/gui/qt/maininterface/compositor_platform.hpp
- modules/gui/qt/maininterface/compositor_wayland.cpp
- modules/gui/qt/maininterface/compositor_wayland.hpp
- modules/gui/qt/maininterface/compositor_win7.cpp
- modules/gui/qt/maininterface/compositor_win7.hpp
- modules/gui/qt/maininterface/compositor_x11.cpp
- modules/gui/qt/maininterface/compositor_x11.hpp
- modules/gui/qt/qt.cpp


Changes:

=====================================
modules/gui/qt/maininterface/compositor.cpp
=====================================
@@ -457,3 +457,8 @@ bool CompositorVideo::setBlurBehind(QWindow *window, const bool enable)
 
     return true;
 }
+
+QQuickWindow *Compositor::quickWindow() const
+{
+    return qobject_cast<QQuickWindow*>(interfaceMainWindow());
+}


=====================================
modules/gui/qt/maininterface/compositor.hpp
=====================================
@@ -71,7 +71,7 @@ public:
 
     [[nodiscard]] virtual bool init() = 0;
 
-    [[nodiscard]] virtual bool makeMainInterface(MainCtx* intf) = 0;
+    [[nodiscard]] virtual bool makeMainInterface(MainCtx* intf, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback = {}) = 0;
 
     /**
      * @brief destroyMainInterface must released all resources, this is called before
@@ -96,6 +96,10 @@ public:
 
     virtual QWindow* interfaceMainWindow() const = 0;
 
+    // Override this to return the quick window if quick window
+    // is not the interface main window:
+    virtual QQuickWindow* quickWindow() const;
+
     virtual QQuickItem * activeFocusItem() const = 0;
 };
 


=====================================
modules/gui/qt/maininterface/compositor_dcomp.cpp
=====================================
@@ -236,7 +236,7 @@ void CompositorDirectComposition::setup()
     }
 }
 
-bool CompositorDirectComposition::makeMainInterface(MainCtx* mainCtx)
+bool CompositorDirectComposition::makeMainInterface(MainCtx* mainCtx, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback)
 {
     assert(mainCtx);
     m_mainCtx = mainCtx;
@@ -314,6 +314,9 @@ bool CompositorDirectComposition::makeMainInterface(MainCtx* mainCtx)
             m_mainCtx,
             &MainCtx::askToQuit);
 
+    if (aboutToShowQuickWindowCallback)
+        aboutToShowQuickWindowCallback(quickViewPtr);
+
     m_quickView->setVisible(true);
     return true;
 }


=====================================
modules/gui/qt/maininterface/compositor_dcomp.hpp
=====================================
@@ -53,7 +53,7 @@ public:
 
     bool init() override;
 
-    bool makeMainInterface(MainCtx*) override;
+    bool makeMainInterface(MainCtx*, std::function<void (QQuickWindow *)> aboutToShowQuickWindowCallback = {}) override;
     void destroyMainInterface() override;
     void unloadGUI() override;
 


=====================================
modules/gui/qt/maininterface/compositor_dummy.cpp
=====================================
@@ -42,7 +42,7 @@ bool CompositorDummy::init()
     return true;
 }
 
-bool CompositorDummy::makeMainInterface(MainCtx* mainCtx)
+bool CompositorDummy::makeMainInterface(MainCtx* mainCtx, std::function<void (QQuickWindow *)> aboutToShowQuickWindowCallback)
 {
     m_mainCtx = mainCtx;
 
@@ -68,6 +68,9 @@ bool CompositorDummy::makeMainInterface(MainCtx* mainCtx)
     if (m_qmlWidget->status() != QQuickView::Ready)
         return false;
 
+    if (aboutToShowQuickWindowCallback)
+        aboutToShowQuickWindowCallback(m_qmlWidget.get());
+
     m_qmlWidget->setVisible(true);
 
     return true;


=====================================
modules/gui/qt/maininterface/compositor_dummy.hpp
=====================================
@@ -40,7 +40,7 @@ public:
 
     bool init() override;
 
-    bool makeMainInterface(MainCtx*) override;
+    bool makeMainInterface(MainCtx*, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback = {}) override;
 
     /**
      * @brief release all resources used by the compositor.


=====================================
modules/gui/qt/maininterface/compositor_platform.cpp
=====================================
@@ -62,7 +62,7 @@ bool CompositorPlatform::init()
     return false;
 }
 
-bool CompositorPlatform::makeMainInterface(MainCtx *mainCtx)
+bool CompositorPlatform::makeMainInterface(MainCtx *mainCtx, std::function<void (QQuickWindow *)> aboutToShowQuickWindowCallback)
 {
     m_mainCtx = mainCtx;
 
@@ -97,6 +97,9 @@ bool CompositorPlatform::makeMainInterface(MainCtx *mainCtx)
 
     m_rootWindow->installEventFilter(this);
 
+    if (aboutToShowQuickWindowCallback)
+        aboutToShowQuickWindowCallback(m_quickWindow);
+
     m_rootWindow->setVisible(true);
     m_videoWindow->setVisible(true);
     m_quickWindow->setVisible(true);
@@ -148,6 +151,11 @@ QWindow *CompositorPlatform::interfaceMainWindow() const
     return m_rootWindow.get();
 }
 
+QQuickWindow *CompositorPlatform::quickWindow() const
+{
+    return m_quickWindow.get();
+}
+
 Compositor::Type CompositorPlatform::type() const
 {
     return Compositor::PlatformCompositor;


=====================================
modules/gui/qt/maininterface/compositor_platform.hpp
=====================================
@@ -38,11 +38,12 @@ public:
 
     bool init() override;
 
-    bool makeMainInterface(MainCtx *) override;
+    bool makeMainInterface(MainCtx *, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback = {}) override;
     void destroyMainInterface() override;
     void unloadGUI() override;
     bool setupVoutWindow(vlc_window_t*, VoutDestroyCb destroyCb) override;
     QWindow* interfaceMainWindow() const override;
+    QQuickWindow* quickWindow() const override;
     Type type() const override;
     QQuickItem * activeFocusItem() const override;
 


=====================================
modules/gui/qt/maininterface/compositor_wayland.cpp
=====================================
@@ -97,7 +97,7 @@ bool CompositorWayland::init()
     return m_waylandImpl->init(m_waylandImpl, qpni_display);
 }
 
-bool CompositorWayland::makeMainInterface(MainCtx* mainCtx)
+bool CompositorWayland::makeMainInterface(MainCtx* mainCtx, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback)
 {
     assert(mainCtx);
     m_mainCtx = mainCtx;
@@ -136,7 +136,11 @@ bool CompositorWayland::makeMainInterface(MainCtx* mainCtx)
     const bool ret = commonGUICreate(m_qmlView.get(), m_qmlView.get(), flags);
 
     if (ret)
+    {
+        if (aboutToShowQuickWindowCallback)
+            aboutToShowQuickWindowCallback(m_qmlView.get());
         m_qmlView->setVisible(true);
+    }
 
     return ret;
 }


=====================================
modules/gui/qt/maininterface/compositor_wayland.hpp
=====================================
@@ -48,7 +48,7 @@ public:
 
     bool init() override;
 
-    bool makeMainInterface(MainCtx*) override;
+    bool makeMainInterface(MainCtx*, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback = {}) override;
 
     /**
      * @brief release all resources used by the compositor.


=====================================
modules/gui/qt/maininterface/compositor_win7.cpp
=====================================
@@ -61,7 +61,7 @@ bool CompositorWin7::init()
     return true;
 }
 
-bool CompositorWin7::makeMainInterface(MainCtx* mainCtx)
+bool CompositorWin7::makeMainInterface(MainCtx* mainCtx, std::function<void (QQuickWindow *)> aboutToShowQuickWindowCallback)
 {
     m_mainCtx = mainCtx;
 
@@ -106,7 +106,11 @@ bool CompositorWin7::makeMainInterface(MainCtx* mainCtx)
     const bool ret = commonGUICreate(m_qmlView.get(), m_qmlView.get(), CompositorVideo::CAN_SHOW_PIP);
 
     if (ret)
+    {
+        if (aboutToShowQuickWindowCallback)
+            aboutToShowQuickWindowCallback(m_qmlView.get());
         m_qmlView->setVisible(true);
+    }
 
     return ret;
 }


=====================================
modules/gui/qt/maininterface/compositor_win7.hpp
=====================================
@@ -50,7 +50,7 @@ public:
 
     bool init() override;
 
-    bool makeMainInterface(MainCtx*) override;
+    bool makeMainInterface(MainCtx*, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback = {}) override;
     void destroyMainInterface() override;
     void unloadGUI() override;
     bool setupVoutWindow(vlc_window_t*, VoutDestroyCb destroyCb) override;


=====================================
modules/gui/qt/maininterface/compositor_x11.cpp
=====================================
@@ -170,7 +170,7 @@ bool CompositorX11::init()
     return true;
 }
 
-bool CompositorX11::makeMainInterface(MainCtx* mainCtx)
+bool CompositorX11::makeMainInterface(MainCtx* mainCtx, std::function<void (QQuickWindow *)> aboutToShowQuickWindowCallback)
 {
     m_mainCtx = mainCtx;
 
@@ -211,6 +211,9 @@ bool CompositorX11::makeMainInterface(MainCtx* mainCtx)
     if (!commonGUICreate(m_renderWindow.get(), m_qmlView.get(), flags))
         return false;
 
+    if (aboutToShowQuickWindowCallback)
+        aboutToShowQuickWindowCallback(m_qmlView->getOffscreenWindow());
+
     m_qmlView->setVisible(true);
 
     if (m_mainCtx->hasAcrylicSurface())
@@ -270,6 +273,12 @@ bool CompositorX11::setupVoutWindow(vlc_window_t* p_wnd, VoutDestroyCb destroyCb
 
 QWindow* CompositorX11::interfaceMainWindow() const { return m_renderWindow.get(); }
 
+QQuickWindow *CompositorX11::quickWindow() const
+{
+    assert(m_qmlView);
+    return m_qmlView->getOffscreenWindow();
+}
+
 QQuickItem * CompositorX11::activeFocusItem() const /* override */
 {
     return m_qmlView->activeFocusItem();


=====================================
modules/gui/qt/maininterface/compositor_x11.hpp
=====================================
@@ -42,7 +42,7 @@ public:
 
     bool init() override;
 
-    bool makeMainInterface(MainCtx*) override;
+    bool makeMainInterface(MainCtx*, std::function<void(QQuickWindow*)> aboutToShowQuickWindowCallback = {}) override;
     void destroyMainInterface() override;
     void unloadGUI() override;
 
@@ -51,6 +51,7 @@ public:
     inline Type type() const override { return X11Compositor; }
 
     QWindow* interfaceMainWindow() const override;
+    QQuickWindow* quickWindow() const override;
 
     QQuickItem * activeFocusItem() const override;
 


=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -909,45 +909,6 @@ static void *Thread( void *obj )
     QApplication app( argc, argv );
     app.setProperty("initialStyle", app.style()->objectName());
 
-#if defined(_WIN32)
-    // NOTE: Qt Quick does not have a cross-API RHI fallback procedure (as of Qt 6.7.1).
-    //       We have to manually pick a graphics api here, since the default graphics
-    //       api (Direct3D 11.2) may not be supported.
-
-    if (qEnvironmentVariableIsEmpty("QSG_RHI_BACKEND") &&
-        qEnvironmentVariableIsEmpty("QT_QUICK_BACKEND") &&
-        (QT_VERSION < QT_VERSION_CHECK(6, 4, 0) || !uint(qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"))))
-    {
-        // If OS version is lower than Windows 8.1, and Qt version is lower than
-        // 6.6.0, do not take risk and use OpenGL (since probing is not available).
-        // If OS version is greater than or equal to Windows 8.1, and Qt version is
-        // lower than 6.6.0, take risk and do not use the fallback procedure.
-        // Qt in the contribs is already version 6.7.0, so this should not be a
-        // concern.
-        if ((QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) ||
-            (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)))
-        {
-            // TODO: Probe D3D12 when it becomes the default.
-            // If probing is not available, use OpenGL as a compromise:
-            QSGRendererInterface::GraphicsApi graphicsApi = QSGRendererInterface::OpenGL;
-#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
-            QRhiD3D11InitParams params;
-            if (QRhi::probe(QRhi::D3D11, &params))
-                graphicsApi = QSGRendererInterface::Direct3D11;
-            else
-            {
-                QRhiGles2InitParams params1;
-                params1.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
-                if (!QRhi::probe(QRhi::OpenGLES2, &params1))
-                    graphicsApi = QSGRendererInterface::Software;
-                delete params1.fallbackSurface;
-            }
-#endif
-            QQuickWindow::setGraphicsApi(graphicsApi);
-        }
-    }
-#endif
-
     {
         // Install custom translator:
         const auto translator = new Translator(&app);
@@ -984,6 +945,70 @@ static void *Thread( void *obj )
 #endif
             QSettings::UserScope, "vlc", "vlc-qt-interface" );
 
+#if defined(_WIN32)
+    // NOTE: Qt Quick does not have a cross-API RHI fallback procedure (as of Qt 6.7.1).
+    //       We have to manually pick a graphics api here, since the default graphics
+    //       api (Direct3D 11.2) may not be supported.
+    static const auto probeRhi = []() -> QSGRendererInterface::GraphicsApi {
+        QSGRendererInterface::GraphicsApi graphicsApi = QSGRendererInterface::OpenGL;
+        // TODO: Probe D3D12 when it becomes the default.
+        QRhiD3D11InitParams params;
+        if (QRhi::probe(QRhi::D3D11, &params))
+            graphicsApi = QSGRendererInterface::Direct3D11;
+        else
+        {
+            QRhiGles2InitParams params1;
+            params1.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+            if (!QRhi::probe(QRhi::OpenGLES2, &params1))
+                graphicsApi = QSGRendererInterface::Software;
+            delete params1.fallbackSurface;
+        }
+        return graphicsApi;
+    };
+
+    static const char* const asyncRhiProbeCompletedProperty = "asyncRhiProbeCompleted";
+    if (qEnvironmentVariableIsEmpty("QSG_RHI_BACKEND") &&
+        qEnvironmentVariableIsEmpty("QT_QUICK_BACKEND") &&
+        (QT_VERSION < QT_VERSION_CHECK(6, 4, 0) || !uint(qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"))))
+    {
+        static const char* const graphicsApiKey = "graphics-api";
+        const QVariant graphicsApiValue = p_intf->mainSettings->value(graphicsApiKey);
+        // 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>()));
+            // 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).
+            QMetaObject::invokeMethod(&app, [&app, 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);
+                settings->setValue(graphicsApiKey, static_cast<int>(probeRhi()));
+                settings->sync();
+                app.setProperty(asyncRhiProbeCompletedProperty, true);
+            }, Qt::QueuedConnection); // Asynchronous, so probing here does not cause start-up slowdown.
+        }
+        else
+        {
+            const QSGRendererInterface::GraphicsApi graphicsApi = probeRhi();
+            QQuickWindow::setGraphicsApi(graphicsApi);
+            p_intf->mainSettings->setValue(graphicsApiKey, static_cast<int>(graphicsApi));
+            p_intf->mainSettings->sync();
+        }
+    }
+#endif
+
     app.setApplicationDisplayName( qtr("VLC media player") );
 
     if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )
@@ -1019,12 +1044,38 @@ static void *Thread( void *obj )
 
     if( !p_intf->b_isDialogProvider )
     {
+        static const auto gracefulExitHandler = [&app, mainCtx = QPointer(p_intf->p_mi), settings = QPointer(p_intf->mainSettings)](QQuickWindow *window) {
+            // Graceful exit on error, as if this signal is not connected
+            // Qt uses `qFatal()`. With `MainCtx::askToQuit()`, the event loop
+            // would be cleared first:
+            assert(window);
+            QObject::connect(window,
+                             &QQuickWindow::sceneGraphError,
+                             &app,
+                             [&app, mainCtx, settings](QQuickWindow::SceneGraphError error, const QString &message) {
+                                 qWarning() << "Compositor: Scene Graph Error: " << error << ", Message: " << message;
+                                 assert(mainCtx);
+#ifdef _WIN32
+                                // This is not really important, as with graceful exit the events in the queue should
+                                // be cleared first. This means that in the worst case the application should wait
+                                // until asynchronous probing is completed before exiting.
+                                 if (!app.property(asyncRhiProbeCompletedProperty).toBool())
+                                 {
+                                     assert(settings);
+                                     settings->remove(asyncRhiProbeCompletedProperty);
+                                     settings->sync();
+                                 }
+#endif
+                                 mainCtx->askToQuit();
+                             });
+        };
+
         bool ret = false;
         do {
             p_intf->p_compositor.reset(compositorFactory.createCompositor());
             if (! p_intf->p_compositor)
                 break;
-            ret = p_intf->p_compositor->makeMainInterface(p_intf->p_mi);
+            ret = p_intf->p_compositor->makeMainInterface(p_intf->p_mi, gracefulExitHandler);
             if (!ret)
             {
                 p_intf->p_compositor->destroyMainInterface();



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/eb1155a2d70eccded0abf2defe17830f46762d37...d668d601905b945a00c3e92740c7d505126a1c0f

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/eb1155a2d70eccded0abf2defe17830f46762d37...d668d601905b945a00c3e92740c7d505126a1c0f
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list