QtWebEngineProcess stays alive after QML scene destruction
-
Here is a fun one that I'm sure not many will know how to fix ;)
- Qt 6.8-beta4
- Ubuntu 22 and Vulkan
- Render a QML scene with rhi's rendercontrol
- The QML scene uses
WebEngineView{}
to render a website - Destroy the QML scene
- QtWebEngineProcess is not destroyed, and still viewing the website
I took the
rendercontrol_rhi
example project fromqtdeclarative.git
I replaced the QML with a
WebEngineView{}
and modified theCMakeLists.txt
accordingly.Steps to reproduce:
# get sources git clone https://code.qt.io/qt/qtdeclarative.git --branch 6.8.0 cd qtdeclarative/examples/quick/rendercontrol/rendercontrol_rhi # move to Mon Jun 3 14:55:17 2024 +0200 git checkout a268004c755828e19f398634548278f049af658d # patch git apply webview.patch # compile cmake -DCMAKE_PREFIX_PATH=/path/to/qt6/gcc_64/ -B build . make -Cbuild -j8 # run ./build/rendercontrol_rhi
After running, wait 5 seconds, click "next 10 frames" to verify the browser is rendering, then click
File -> Reset
which will:m_rpDesc.reset(); m_rt.reset(); m_ds.reset(); m_texture.reset(); m_qmlComponent.reset(); m_qmlEngine.reset(); m_scene.reset(); m_renderControl.reset();
And the QML scene is destroyed, however,
QtWebEngineProcess
is still active according to the process listps -xau
rob 2331577 0.1 0.0 378784 56064 pts/15 S+ 16:36 0:00 /path/to/qt/gcc_64/libexec/QtWebEngineProcess --type=zygote --no-zygote-sandbox --application-name=rendercontrol_rhi --webengine-schemes=qrc:sV --lang=en rob 2331578 0.1 0.0 378784 56256 pts/15 S+ 16:36 0:00 /path/to/qt/gcc_64/libexec/QtWebEngineProcess --type=zygote --application-name=rendercontrol_rhi --webengine-schemes=qrc:sV --lang=en rob 2331580 0.0 0.0 378812 11552 pts/15 S+ 16:36 0:00 /path/to/qt/gcc_64/libexec/QtWebEngineProcess --type=zygote --application-name=rendercontrol_rhi --webengine-schemes=qrc:sV --lang=en rob 2331602 1.6 0.1 1187592704 100864 pts/15 Sl+ 16:36 0:00 /path/to/qt/gcc_64/libexec/QtWebEngineProcess --type=renderer --webengine-schemes=qrc:sV --first-renderer-process --disable-gpu-memory-buffer-video-frames --disable-speech-api --disable-databases --disable-blink-features=EyeDropperAPI --lang=en --num-raster-threads=4 --enable-main-frame-before-activation --renderer-client-id=3 --time-ticks-at-unix-epoch=-1727719281365711 --launch-time-ticks=156883795394 --shared-files=v8_context_snapshot_data:100
To double verify, I made a website that creates a websocket connection to a websocket server, and observed it did not disconnect upon destroying the QML scene.
Does anyone know how to properly destroy the
WebEngineView
?Here is the
webview.patch
:diff --git a/CMakeLists.txt b/CMakeLists.txt index e152cde949..55102b1723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ project(QtDeclarative # special case ) find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core) # special case -find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Network Widgets OpenGL OpenGLWidgets Sql Concurrent Test LanguageServerPrivate LinguistTools Svg) +find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Network Widgets OpenGL OpenGLWidgets Sql Concurrent Test LanguageServerPrivate LinguistTools Svg WebEngine) qt_internal_project_setup() # Set up QT_HOST_PATH as an extra root path to look for the ShaderToolsTools package diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt b/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt index c38f26fcca..f744cc8ebd 100644 --- a/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt +++ b/examples/quick/rendercontrol/rendercontrol_rhi/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.16) project(rendercontrol_rhi LANGUAGES CXX) -find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Widgets) +find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick Widgets WebEngineQuick) qt_standard_project_setup(REQUIRES 6.5) @@ -17,6 +17,7 @@ target_link_libraries(rendercontrol_rhi PRIVATE Qt6::GuiPrivate Qt6::Quick Qt6::Widgets + Qt6::WebEngineQuick ) qt_add_qml_module(rendercontrol_rhi diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml b/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml index 707d9cb3a4..2ea2a94ea9 100644 --- a/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml +++ b/examples/quick/rendercontrol/rendercontrol_rhi/demo.qml @@ -3,154 +3,20 @@ import QtQuick import QtQuick.Particles +import QtWebEngine Rectangle { id: root + color: "red" - gradient: Gradient { - GradientStop { position: 0; color: "steelblue" } - GradientStop { position: 1; color: "black" } + Timer { + interval: 1000; running: true; repeat: true + onTriggered: console.log(Date().toString()); } - Text { - anchors.centerIn: parent - text: "Qt Quick in a texture" - font.pointSize: 40 - color: "white" - - SequentialAnimation on rotation { - PauseAnimation { duration: 2500 } - NumberAnimation { from: 0; to: 360; duration: 5000; easing.type: Easing.InOutCubic } - loops: Animation.Infinite - } - } - - ParticleSystem { - id: particles + WebEngineView { + id: webView anchors.fill: parent - - ImageParticle { - id: smoke - system: particles - anchors.fill: parent - groups: ["A", "B"] - source: "qrc:///particleresources/glowdot.png" - colorVariation: 0 - color: "#00111111" - } - ImageParticle { - id: flame - anchors.fill: parent - system: particles - groups: ["C", "D"] - source: "qrc:///particleresources/glowdot.png" - colorVariation: 0.1 - color: "#00ff400f" - } - - Emitter { - id: fire - system: particles - group: "C" - - y: parent.height - width: parent.width - - emitRate: 350 - lifeSpan: 3500 - - acceleration: PointDirection { y: -17; xVariation: 3 } - velocity: PointDirection {xVariation: 3} - - size: 24 - sizeVariation: 8 - endSize: 4 - } - - TrailEmitter { - id: fireSmoke - group: "B" - system: particles - follow: "C" - width: root.width - height: root.height - 68 - - emitRatePerParticle: 1 - lifeSpan: 2000 - - velocity: PointDirection {y:-17*6; yVariation: -17; xVariation: 3} - acceleration: PointDirection {xVariation: 3} - - size: 36 - sizeVariation: 8 - endSize: 16 - } - - TrailEmitter { - id: fireballFlame - anchors.fill: parent - system: particles - group: "D" - follow: "E" - - emitRatePerParticle: 120 - lifeSpan: 180 - emitWidth: TrailEmitter.ParticleSize - emitHeight: TrailEmitter.ParticleSize - emitShape: EllipseShape{} - - size: 16 - sizeVariation: 4 - endSize: 4 - } - - TrailEmitter { - id: fireballSmoke - anchors.fill: parent - system: particles - group: "A" - follow: "E" - - emitRatePerParticle: 128 - lifeSpan: 2400 - emitWidth: TrailEmitter.ParticleSize - emitHeight: TrailEmitter.ParticleSize - emitShape: EllipseShape{} - - velocity: PointDirection {yVariation: 16; xVariation: 16} - acceleration: PointDirection {y: -16} - - size: 24 - sizeVariation: 8 - endSize: 8 - } - - Emitter { - id: balls - system: particles - group: "E" - - y: parent.height - width: parent.width - - emitRate: 2 - lifeSpan: 7000 - - velocity: PointDirection {y:-17*4*2; xVariation: 6*6} - acceleration: PointDirection {y: 17*2; xVariation: 6*6} - - size: 8 - sizeVariation: 4 - } - - Turbulence { //A bit of turbulence makes the smoke look better - anchors.fill: parent - groups: ["A","B"] - strength: 32 - system: particles - } + url: "https://www.qt.io" } - - onWidthChanged: particles.reset() - onHeightChanged: particles.reset() } diff --git a/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp b/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp index 76f2ade0a9..47e6d06b79 100644 --- a/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp +++ b/examples/quick/rendercontrol/rendercontrol_rhi/main.cpp @@ -8,6 +8,7 @@ #include <QMenuBar> #include <QStatusBar> #include <QFileDialog> +#include <QtWebEngineQuick> #include <QMessageBox> #include <QLabel> #include <QScrollArea> @@ -86,7 +87,7 @@ private: void stepAnimations(); AnimationDriver *m_animationDriver = nullptr; - + bool loaded = false; std::unique_ptr<QQuickRenderControl> m_renderControl; std::unique_ptr<QQuickWindow> m_scene; std::unique_ptr<QQmlEngine> m_qmlEngine; @@ -205,6 +206,7 @@ MainWindow::MainWindow() QMenu *fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(tr("&Open"), this, &MainWindow::openRequested); + fileMenu->addAction(tr("Reset"), this, &MainWindow::reset); fileMenu->addAction(tr("E&xit"), qApp, &QCoreApplication::quit); m_statusMsg = new QLabel; @@ -556,6 +558,7 @@ void MainWindow::stepAnimations() int main(int argc, char **argv) { + QtWebEngineQuick::initialize(); QApplication app(argc, argv); #if QT_CONFIG(vulkan) @@ -570,24 +573,9 @@ int main(int argc, char **argv) selLayout->addWidget(new QLabel(QObject::tr("Select graphics API to use"))); QListWidget *apiList = new QListWidget; QVarLengthArray<QSGRendererInterface::GraphicsApi, 5> apiValues; -#ifdef Q_OS_WIN - apiList->addItem("Direct3D 11"); - apiValues.append(QSGRendererInterface::Direct3D11); - apiList->addItem("Direct3D 12"); - apiValues.append(QSGRendererInterface::Direct3D12); -#endif -#if QT_CONFIG(metal) - apiList->addItem("Metal"); - apiValues.append(QSGRendererInterface::Metal); -#endif -#if QT_CONFIG(vulkan) apiList->addItem("Vulkan"); apiValues.append(QSGRendererInterface::Vulkan); -#endif -#if QT_CONFIG(opengl) - apiList->addItem("OpenGL / OpenGL ES"); - apiValues.append(QSGRendererInterface::OpenGL); -#endif + if (apiValues.isEmpty()) { QMessageBox::critical(nullptr, QObject::tr("No 3D graphics API"), QObject::tr("No 3D graphics APIs are supported in this Qt build")); return 1;
-
I tried a simple QtWidgets application that creates a
QQuickWidget
, then later removing it viam_quickWidget->deleteLater();
- this seems to work; as it:- destroys the QML
- destroys WebEngineView (I can see the websocket connection being killed on the server side)
With rendercontrol, the websocket connection stays active, hence I think the page is still active in the background somehow.
-
Found the problem, turns out the
rendercontrol_rhi
example project in qtdeclarative has an issue.QObject *rootObject = m_qmlComponent->create();
but is never cleaned up.
it works when we clean it up inreset()
QQuickItem *rootItem = qobject_cast<QQuickItem *>(m_rootObject); rootItem.reset();