diff --git a/modules/gui/qt/qt.cpp b/modules/gui/qt/qt.cpp
index 81b5f2e30ff024032a21f439cd233cd4c1ebcbdb..f76deadf2e7647566bf2c1c4d11d3645f1367aa2 100644
--- a/modules/gui/qt/qt.cpp
+++ b/modules/gui/qt/qt.cpp
@@ -920,45 +920,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);
@@ -995,6 +956,88 @@ 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 {
+        std::optional<QSGRendererInterface::GraphicsApi> graphicsApi;
+        // TODO: Probe D3D12 when it becomes the default.
+        {
+            // D3D11
+            QRhiD3D11InitParams params;
+            if (QRhi::probe(QRhi::D3D11, &params))
+                graphicsApi = QSGRendererInterface::Direct3D11;
+        }
+
+        if (!graphicsApi)
+        {
+            // Vulkan
+            QVulkanInstance inst;
+            if (inst.create())
+            {
+                QRhiVulkanInitParams params1;
+                params1.inst = &inst;
+                if (QRhi::probe(QRhi::Vulkan, &params1))
+                    graphicsApi = QSGRendererInterface::Vulkan;
+            }
+        }
+
+        if (!graphicsApi)
+        {
+            // OpenGL
+            QRhiGles2InitParams params2;
+            params2.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+            if (QRhi::probe(QRhi::OpenGLES2, &params2))
+                graphicsApi = QSGRendererInterface::OpenGL;
+            delete params2.fallbackSurface;
+        }
+
+        if (!graphicsApi)
+            graphicsApi = QSGRendererInterface::Software;
+
+        return *graphicsApi;
+    };
+
+    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, [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()));
+            }, 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));
+        }
+    }
+#endif
+
     app.setApplicationDisplayName( qtr("VLC media player") );
 
     if( QDate::currentDate().dayOfYear() >= QT_XMAS_JOKE_DAY && var_InheritBool( p_intf, "qt-icon-change" ) )