Dynamic Qt Quick 3D creation and deletion
-
Hello all.
Short story, dynamically creating new items that contain Qt Quick 3D :: View3D crashes my application.
Seems like this crash happens when a new View3D is created; after the destruction of all other items with View3D.
Currently I am keeping an item with View3D alive, but does anybody know a proper solution, or a better workaround?A simple scenario:
* 1. Push a couple of items that contain View3D into a StackView ( item ownership doesn't matter as long as the object is detroyed after pop )
* 2. Pop until the stack is empty ( initial element is not a View3D )
* 3. Push another item that contains View3D
* 4. Expected application crashBelow is a minimum working example
main.qml
import QtQuick 2.15 import QtQuick3D 1.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 Window { visible: true width: 640 height: 480 title: qsTr("Crash Test") StackView { id: sv_main anchors.fill: parent /****************************************************** * * Disable below property and execute the * following to crash this example software * 1. Push a couple of Quick 3D elements into the stack * 2. Pop until the stack is empty * 3. Push another Quick 3D Element * 4. Expected crash * ******************************************************/ // initialItem: comp_initial } Row { spacing: 8 anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom margins: 16 } Button { text: "Push" onClicked: sv_main.push( "qrc:/Quick3dView.qml" ) } Button { enabled: sv_main.depth > 1 text: "Pop" onClicked: sv_main.pop() } } Component { id: comp_initial Label { text: "StackView is empty.\n Click \"Push\" to insert a Qt Quick 3D view into the stack." horizontalAlignment: Label.AlignHCenter verticalAlignment: Label.AlignVCenter background: Rectangle { color: "lightgreen" } Text { anchors.horizontalCenter: parent.horizontalCenter text: sv_main.depth font.pixelSize: 64 } } } }
Quick3dView.qml
import QtQuick 2.15 import QtQuick3D 1.15 import QtQuick.Controls 2.15 Item { id: root View3D { anchors.fill: root environment: SceneEnvironment { clearColor: Qt.rgba( Math.random(), Math.random(), Math.random(), 1.0 ) backgroundMode: SceneEnvironment.Color } camera: pcam Component.onCompleted: console.log( "Completed: " + this ) Component.onDestruction: console.log( "Destruction: " + this ) PerspectiveCamera { id: pcam position: Qt.vector3d(0, 0, 300) } DirectionalLight {} Model { id: mdl_cube position: Qt.vector3d(0, 0, 0) source: "#Cube" materials: [ DefaultMaterial { diffuseColor: "blue" } ] ParallelAnimation { running: true loops: Animation.Infinite NumberAnimation { target: mdl_cube properties: "eulerRotation.x" duration: 3000 from: 0 to: 360 easing.type:Easing.Linear } NumberAnimation { target: mdl_cube properties: "eulerRotation.y" duration: 3000 from: 0 to: 360 easing.type:Easing.Linear } } } Text { anchors.horizontalCenter: parent.horizontalCenter text: root.StackView.index font.pixelSize: 64 } } }
-
I've been blocked by this as well (Qt 6.1). I've managed to find a work around although it's not ideal. You can dynamically create the View3D but instead of deleting you can store it in a reuse buffer. Then if you need a View3D you can just grab one from the buffer or create it if empty. The one snag seems to be that the view requires a valid parent. I tried setting it to null but the crash happens when you re-parent or add the re-used view. I set the view to invisible and then re-parent. Here is a minimal working example.
import QtQuick import QtQuick3D import QtQuick.Controls import QtQuick.Layouts Window { id: window width: 1280 height: 720 visible: true color: "#848895" Item { id: root anchors.fill: parent ColumnLayout { anchors.fill: parent RowLayout { Button { text: "Remove" onClicked: keeper.removeView() } Button { text: "Add" onClicked: { var v = keeper.getView() if (v) { v.parent = flow v.visible = true } } } } Flow { id: flow Layout.fillWidth: true Layout.fillHeight: true } } property int viewCount: 0 Component { id: viewComponent Item { width: 200 height: 200 visible: false View3D { anchors.fill: parent environment: SceneEnvironment { clearColor: Qt.rgba (1, 0, 0, 1) backgroundMode: SceneEnvironment.Color } PerspectiveCamera { z: 200 } DirectionalLight { eulerRotation.x: -25 } Model { source: "#Cube" materials: DefaultMaterial { diffuseColor: Qt.rgba(0.8, 0.8, 0.8, 1.0) } PropertyAnimation on eulerRotation.y { loops: Animation.Infinite duration: 5000 to: 0 from: -360 } } Component.onCompleted: print ("View Created:", ++root.viewCount) Component.onDestruction: print ("View Destroyed", --root.viewCount) } } } } Item { id: keeper visible: false property var views: [] property var removed: [] function getView() { if (0 < removed.length) { views.push(removed.pop()) } else { views.push(viewComponent.createObject(flow)) } return views[views.length - 1] } function removeView() { if (0 < views.length) { var v = views.pop() v.visible = false v.parent = keeper removed.push(v) } } } }
-
The code in my previous post works in some cases but additional conditions must also be met. I eliminated crashes, I think, in Qt 6.1.2 with the following:
- A buffer to prevent deletion of the View3D in the code above is required.
- The parent hierarchy of the View3D must not have a null parent. Creating an Item with a null parent, adding a View3D, and then settings the item's parent can lead to a crash.
- Visibility is important. If adding a View3D to an Item, it seems best to create/add a View3D when the item becomes visible. I'm also releasing the view when the item becomes invisible. This seems to keep Qt happy and so far the crashes are gone.