diff --git a/tests/manual/3dcompositor/3dcompositor.pro b/tests/manual/3dcompositor/3dcompositor.pro
new file mode 100644
index 0000000000000000000000000000000000000000..88794a8b5c9e44ed0adbf22c99d1a9e01c90b343
--- /dev/null
+++ b/tests/manual/3dcompositor/3dcompositor.pro
@@ -0,0 +1,29 @@
+QT += quick quick3d waylandcompositor
+CONFIG += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+        main.cpp
+
+RESOURCES += qml.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/3dcompositor/main.cpp b/tests/manual/3dcompositor/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a5a6606ad1fd2128563e8111c329c95405ec08c
--- /dev/null
+++ b/tests/manual/3dcompositor/main.cpp
@@ -0,0 +1,19 @@
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QtQuick3D/QQuick3DViewport>
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+
+    QGuiApplication app(argc, argv);
+
+    QSurfaceFormat::setDefaultFormat(QQuick3DViewport::idealSurfaceFormat());
+
+    QQmlApplicationEngine engine;
+    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
+    if (engine.rootObjects().isEmpty())
+        return -1;
+
+    return app.exec();
+}
diff --git a/tests/manual/3dcompositor/main.qml b/tests/manual/3dcompositor/main.qml
new file mode 100644
index 0000000000000000000000000000000000000000..8704df7a430ad060ed1de14021ea6966e759e1c1
--- /dev/null
+++ b/tests/manual/3dcompositor/main.qml
@@ -0,0 +1,83 @@
+import QtQuick 2.12
+import QtQuick.Window 2.12
+import QtQuick3D 1.12
+import QtWayland.Compositor 1.3
+import QtQuick.Controls 2.12
+
+WaylandCompositor {
+    WaylandOutput {
+        window: Window {
+            id: window
+            width: 1280
+            height: 720
+            visible: true
+            View3D {
+                id: view
+                anchors.fill: parent
+                focus: true
+
+                environment: SceneEnvironment {
+                    id: environment
+                    probeBrightness: 5
+                    clearColor: "#cceeff"
+                    backgroundMode: SceneEnvironment.Color
+                }
+
+                Node {
+                    id: shapeSpawner
+                }
+
+                Camera {
+                    id: camera
+                    position: Qt.vector3d(0, 0, 0)
+                }
+
+            }
+        }
+    }
+    Component {
+        id: toplevelComponent
+        Model {
+            id: model
+            NumberAnimation on rotation.y {
+                duration: 6000
+                from: 0
+                to: 360
+                loops: Animation.Infinite
+            }
+            NumberAnimation on rotation.x {
+                duration: 12000
+                from: 0
+                to: 360
+                loops: Animation.Infinite
+            }
+            property alias shellSurface: ssItem.shellSurface
+            Component.onCompleted: console.log("loaded model")
+            position: Qt.vector3d(0, 0, 300)
+            source: "#Cube"
+            materials: [
+                DefaultMaterial {
+                    lighting: DefaultMaterial.NoLighting
+                    diffuseMap: Texture {
+                        sourceItem: ShellSurfaceItem {
+                            id: ssItem
+                            autoCreatePopupItems: true
+                            shellSurface: xdgSurface
+                            onSurfaceDestroyed: model.destroy()
+                        }
+                        scaleU: 1
+                        scaleV: -1 // TODO: shouldn't be needed
+                        tilingModeHorizontal: Texture.Repeat // TODO: shouldn't be needed
+                        tilingModeVertical: Texture.Repeat
+                    }
+                }
+            ]
+        }
+    }
+    XdgShell {
+        onToplevelCreated: {
+            toplevelComponent.createObject(shapeSpawner, {shellSurface: xdgSurface});
+            toplevel.sendMaximized(Qt.size(256, 256))
+        }
+    }
+}
diff --git a/tests/manual/3dcompositor/qml.qrc b/tests/manual/3dcompositor/qml.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..5f6483ac33f1881cc59e080e69bb033ebe2a7829
--- /dev/null
+++ b/tests/manual/3dcompositor/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>