diff --git a/examples/canvas3d/canvas3d/framebuffer/main.cpp b/examples/canvas3d/canvas3d/framebuffer/main.cpp
index dc9ef5c31f8c9a64201eada89be5316196b30c39..b516e40364ebc4aa56d0a9d8f137325a36d23722 100644
--- a/examples/canvas3d/canvas3d/framebuffer/main.cpp
+++ b/examples/canvas3d/canvas3d/framebuffer/main.cpp
@@ -59,6 +59,7 @@ int main(int argc, char *argv[])
 
     viewer.setTitle(QStringLiteral("Render into FrameBuffer"));
     viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+    viewer.setColor("#f2f2f2");
     viewer.show();
 
     return app.exec();
diff --git a/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/framebuffer.js b/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/framebuffer.js
index f268682a3d5b2a15111d05de96a77dd90f6e015d..397cca2d720a37c080e090e26e9d3459274fdb41 100644
--- a/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/framebuffer.js
+++ b/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/framebuffer.js
@@ -75,7 +75,7 @@ function initializeGL(canvas, textureLoader) {
     canvas3d = canvas
     try {
         // Get the OpenGL context object that represents the API we call
-        gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+        gl = canvas.getContext("canvas3d", {depth:true, antialias:true, alpha:false});
 
         // Setup the OpenGL state
         gl.enable(gl.DEPTH_TEST);
diff --git a/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/main.qml b/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/main.qml
index ba23f263d99def3033cb2f3ed536d5a77d23e3c5..68798cdf4e30169375eccbafd7e370875604b208 100644
--- a/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/main.qml
+++ b/examples/canvas3d/canvas3d/framebuffer/qml/framebuffer/main.qml
@@ -41,12 +41,11 @@ import QtQuick.Layouts 1.0
 
 import "framebuffer.js" as GLCode
 
-Rectangle {
+Item {
     id: mainview
     width: 1280
     height: 768
     visible: true
-    color: "#f2f2f2"
 
     Canvas3D {
         id: canvas3d
diff --git a/examples/canvas3d/canvas3d/interaction/main.cpp b/examples/canvas3d/canvas3d/interaction/main.cpp
index 05e00a0ca6be86cb753c1326e6c043d30b5c8d32..91da80e48081b291098b53ebd4a7a4cdf8cc5852 100644
--- a/examples/canvas3d/canvas3d/interaction/main.cpp
+++ b/examples/canvas3d/canvas3d/interaction/main.cpp
@@ -59,6 +59,7 @@ int main(int argc, char *argv[])
 
     viewer.setTitle(QStringLiteral("Interaction"));
     viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+    viewer.setColor("#fafafa");
     viewer.show();
 
     return app.exec();
diff --git a/examples/canvas3d/canvas3d/interaction/qml/interaction/interaction.js b/examples/canvas3d/canvas3d/interaction/qml/interaction/interaction.js
index e8ca513ebce13f1347f57fde074f6d27043ca702..11ff0796e67e86ff5036305627384d0fde4d5257 100644
--- a/examples/canvas3d/canvas3d/interaction/qml/interaction/interaction.js
+++ b/examples/canvas3d/canvas3d/interaction/qml/interaction/interaction.js
@@ -86,7 +86,7 @@ function initializeGL(canvas) {
 
         // Get the OpenGL context jsonObj that represents the API we call
         log("Getting Context");
-        gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+        gl = canvas.getContext("canvas3d", {depth:true, antialias:true, alpha:false});
         log("Context received "+gl);
 
         var contextConfig = gl.getContextAttributes();
diff --git a/examples/canvas3d/canvas3d/interaction/qml/interaction/main.qml b/examples/canvas3d/canvas3d/interaction/qml/interaction/main.qml
index 1836c47f481eb654bea8f62d11c3cf4544684da4..88e9b31d799c4a95e258496d6ce28ebb099174e2 100644
--- a/examples/canvas3d/canvas3d/interaction/qml/interaction/main.qml
+++ b/examples/canvas3d/canvas3d/interaction/qml/interaction/main.qml
@@ -43,12 +43,11 @@ import QtQuick.Layouts 1.0
 
 import "interaction.js" as GLCode
 
-Rectangle {
+Item {
     id: mainview
     width: 1280
     height: 768
     visible: true
-    color: "#fafafa"
 
     Canvas3D {
         id: canvas3d
diff --git a/examples/canvas3d/canvas3d/jsonmodels/qml/jsonmodels/jsonmodels.js b/examples/canvas3d/canvas3d/jsonmodels/qml/jsonmodels/jsonmodels.js
index 8a6dafe6c899bfe8caab496871581c355ba81770..2727cb82b58a3241f3def814a0b8e71634d8890a 100644
--- a/examples/canvas3d/canvas3d/jsonmodels/qml/jsonmodels/jsonmodels.js
+++ b/examples/canvas3d/canvas3d/jsonmodels/qml/jsonmodels/jsonmodels.js
@@ -117,7 +117,7 @@ function initializeGL(canvas) {
     canvas3d = canvas
     log("initializeGL...")
     try {
-        gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+        gl = canvas.getContext("canvas3d", {depth:true, antialias:true, alpha:false});
         log("   Received context: "+gl);
 
         stateDumpExt = gl.getExtension("QTCANVAS3D_gl_state_dump");
diff --git a/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml b/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml
index 3c16683e7612754df47ac21cbe1e2cefea18ce5e..433e226075e8b85eeba23ac9d055259e515a53fb 100644
--- a/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml
+++ b/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml
@@ -72,7 +72,7 @@ Window {
                     + "Z Rot:" + (canvas3d.zRotAnim | 0) + "\n"
                     + "FPS:" + canvas3d.fps
                 color: "red"
-                font.pointSize: 30
+                font.pointSize: 26
                 horizontalAlignment: Text.AlignLeft
                 verticalAlignment: Text.AlignVCenter
             }
diff --git a/examples/canvas3d/canvas3d/textureandlight/main.cpp b/examples/canvas3d/canvas3d/textureandlight/main.cpp
index d2472a242150756312c3c8a40bd43444e5ab3fde..9dfb4bfe14480be6d9d92ebecb8d7985708dd29c 100644
--- a/examples/canvas3d/canvas3d/textureandlight/main.cpp
+++ b/examples/canvas3d/canvas3d/textureandlight/main.cpp
@@ -69,6 +69,7 @@ int main(int argc, char *argv[])
 
     viewer.setTitle(QStringLiteral("Textured and Lit Cube"));
     viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+    viewer.setColor("#fafafa");
     viewer.show();
 
     return app.exec();
diff --git a/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/main.qml b/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/main.qml
index bb951b440ff739ae1f59e4839ca099c9e649a56f..cd308bf14806e26c1478c4c29fdcdd1345176eeb 100644
--- a/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/main.qml
+++ b/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/main.qml
@@ -41,12 +41,11 @@ import QtCanvas3D 1.0
 import "textureandlight.js" as GLCode
 //! [4]
 
-Rectangle {
+Item {
     id: mainview
     width: 1280
     height: 768
     visible: true
-    color: "#fafafa"
 
     //! [0]
     Canvas3D {
diff --git a/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/textureandlight.js b/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/textureandlight.js
index d80cb8fa54557870eac4c2d2e23927c712e74e30..4c1a5223323670a83e5a94af2069fdc8c7a0f876 100644
--- a/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/textureandlight.js
+++ b/examples/canvas3d/canvas3d/textureandlight/qml/textureandlight/textureandlight.js
@@ -63,7 +63,7 @@ function initializeGL(canvas) {
     canvas3d = canvas;
     //! [1]
     // Get the OpenGL context object that represents the API we call
-    gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+    gl = canvas.getContext("canvas3d", {depth:true, antialias:true, alpha:false});
     //! [1]
 
     //! [2]
diff --git a/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/cellphone.js b/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/cellphone.js
index 42c7e52deb3494192f6c564fc775efc2f4d2c1aa..160ae4e9d13e5c35b8ac9bf8355e02f56b427385 100644
--- a/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/cellphone.js
+++ b/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/cellphone.js
@@ -98,8 +98,7 @@ function initializeGL(canvas, textureSource) {
     }
 
     renderer = new THREE.Canvas3DRenderer(
-                { canvas: canvas, antialias: true, devicePixelRatio: canvas.devicePixelRatio,
-                  alpha: true});
+                { canvas: canvas, antialias: true, devicePixelRatio: canvas.devicePixelRatio });
     renderer.setSize( canvas.width, canvas.height );
 
     // The cellphone meshes were created using a third party tool (Blender).
diff --git a/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/main.qml b/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/main.qml
index 559847819731d8ce5ce82b1061cc32892269867d..ad27b16750362533dcd78a8cbadb99db91b0899e 100644
--- a/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/main.qml
+++ b/examples/canvas3d/canvas3d/threejs/cellphone/qml/cellphone/main.qml
@@ -45,6 +45,7 @@ Window {
     height: initialHeight
     visible: true
     title: "Interactive Mobile Phone Demo"
+    color: "black"
 
     //! [0]
     Item {
diff --git a/examples/canvas3d/canvas3d/threejs/oneqt/InfoSheet.qml b/examples/canvas3d/canvas3d/threejs/oneqt/InfoSheet.qml
index f35a9503b07087d67bb7bc7c2742be8975e79c58..2808f6a2b994f443130002739f6ce378440af999 100644
--- a/examples/canvas3d/canvas3d/threejs/oneqt/InfoSheet.qml
+++ b/examples/canvas3d/canvas3d/threejs/oneqt/InfoSheet.qml
@@ -95,7 +95,7 @@ Item {
             id: heading1
             text: ""
             font.family: "Helvetica"
-            font.pixelSize: 3.0 * 16
+            font.pointSize: 36
             font.weight: Font.Light
             color: "black"
 
@@ -105,7 +105,7 @@ Item {
                 anchors.left: heading1.right
                 text: ""
                 font.family: "Helvetica"
-                font.pixelSize: 3.0 * 16
+                font.pointSize: 36
                 font.weight: Font.Light
                 color: "#5caa15"
             }
@@ -123,10 +123,8 @@ Item {
             text: ""
             width: (infoSheet.width - infoSheet.anchors.leftMargin) * 0.3
             font.family: "Helvetica"
-            font.pixelSize: 16
+            font.pointSize: 16
             font.weight: Font.Light
-            lineHeight: 1.625 * 16
-            lineHeightMode: Text.FixedHeight
         }
     }
 }
diff --git a/examples/canvas3d/canvas3d/threejs/oneqt/Navibutton.qml b/examples/canvas3d/canvas3d/threejs/oneqt/Navibutton.qml
index ca9b02b98c946ef9c04b66024823226c661de1a7..80f3f0202d193ff384a5b0aa2fd9aed87f301160 100644
--- a/examples/canvas3d/canvas3d/threejs/oneqt/Navibutton.qml
+++ b/examples/canvas3d/canvas3d/threejs/oneqt/Navibutton.qml
@@ -41,8 +41,7 @@ Text {
     id: menubarItem
     text: ""
     font.family: "Helvetica"
-    font.pixelSize: 1.125 * 16
-    //font.pointSize: 20
+    font.pointSize: 20
     font.weight: Font.Light
     color: "#404244"
     Layout.alignment: Qt.AlignHCenter
diff --git a/examples/canvas3d/canvas3d/threejs/oneqt/main.cpp b/examples/canvas3d/canvas3d/threejs/oneqt/main.cpp
index 112cc20bf6ef3e631d51e33b936a6be8b43289b2..b68136aa28ec75ecc78ba5e07b8df82aae0053fe 100644
--- a/examples/canvas3d/canvas3d/threejs/oneqt/main.cpp
+++ b/examples/canvas3d/canvas3d/threejs/oneqt/main.cpp
@@ -59,6 +59,7 @@ int main(int argc, char *argv[])
 
     viewer.setTitle(QStringLiteral("One Qt"));
     viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+    viewer.setColor(QColor("#FCFCFC"));
     viewer.show();
 
     return app.exec();
diff --git a/examples/canvas3d/canvas3d/threejs/oneqt/oneqt.qml b/examples/canvas3d/canvas3d/threejs/oneqt/oneqt.qml
index cc1a16c1ec1b7ee64a12f5c4c0791132640422b4..8534f4cd5b60c4d92144d8889b3375cc7681d50d 100644
--- a/examples/canvas3d/canvas3d/threejs/oneqt/oneqt.qml
+++ b/examples/canvas3d/canvas3d/threejs/oneqt/oneqt.qml
@@ -38,13 +38,12 @@ import QtQuick 2.0
 import QtCanvas3D 1.0
 import QtQuick.Layouts 1.1
 
-Rectangle {
+Item {
     id: mainview
     width: 1280
     height: 768
     visible: true
     focus: true
-    color: "#FCFCFC"
 
     Keys.onPressed: {
         if (event.key === Qt.Key_1) imageCube.state = 'image1';
@@ -307,7 +306,7 @@ Rectangle {
         text: "Your productivity is at the core of what drives us. We made creating embedded<br>"+
               "devices agile and painless without sacrificing maximum native performance. You get<br>"+
               "to write your application using high level C++ libraries with no need to worry about<br>"+
-              "nasty platform details. Using Qt Creator IDE and with a variety of UI approaches to<br"+
+              "nasty platform details. Using Qt Creator IDE and with a variety of UI approaches to<br>"+
               "choose from you can create the optimal UX for your end users."
     }
 
@@ -322,7 +321,7 @@ Rectangle {
         headingText2: "is Our Specialty"
         text: "We make cross-platform application development easy. Target all the screens in your<br>"+
               "end users’ lives. You only need to write and maintain one code base regardless of<br>"+
-              "what kind of and how many target platforms you might have and we’re talking about<br"+
+              "what kind of and how many target platforms you might have and we’re talking about<br>"+
               "all major operating systems here. No need for separate implementations for<br>"+
               "different user devices. Qt makes your time-to-market faster, technology strategy<br>"+
               "simpler and future-proof, consequently reducing costs."
diff --git a/examples/canvas3d/canvas3d/threejs/planets/main.cpp b/examples/canvas3d/canvas3d/threejs/planets/main.cpp
index 59b10c6ee6c70fea156fef50544c67134e390cc6..61ca3fc83b83a34dcd46214bf9fc13279af19174 100644
--- a/examples/canvas3d/canvas3d/threejs/planets/main.cpp
+++ b/examples/canvas3d/canvas3d/threejs/planets/main.cpp
@@ -59,6 +59,7 @@ int main(int argc, char *argv[])
 
     viewer.setTitle(QStringLiteral("Qt Canvas 3D Examples - Planets"));
     viewer.setResizeMode(QQuickView::SizeRootObjectToView);
+    viewer.setColor(Qt::black);
     viewer.show();
 
     return app.exec();
diff --git a/examples/canvas3d/canvas3d/threejs/planets/planets.qml b/examples/canvas3d/canvas3d/threejs/planets/planets.qml
index e5c157848697e579dab3fefe40d38fc0ffbc6dca..9699590fb24dd7a485efc65cc490933e958366e0 100644
--- a/examples/canvas3d/canvas3d/threejs/planets/planets.qml
+++ b/examples/canvas3d/canvas3d/threejs/planets/planets.qml
@@ -39,12 +39,11 @@ import QtCanvas3D 1.0
 
 import "planets.js" as GLCode
 
-Rectangle {
+Item {
     id: mainview
     width: 1280
     height: 768
     visible: true
-    color: "black"
     property int focusedPlanet: 100
     property int oldPlanet: 0
     property real xLookAtOffset: 0
diff --git a/src/imports/qtcanvas3d/abstractobject3d.cpp b/src/imports/qtcanvas3d/abstractobject3d.cpp
index 794e9a423d5caf8dfc8f38cd81fe670d2a83b1c8..6023e1bf37fc0bbce930ec9ac852084c8cb34bc0 100644
--- a/src/imports/qtcanvas3d/abstractobject3d.cpp
+++ b/src/imports/qtcanvas3d/abstractobject3d.cpp
@@ -40,10 +40,22 @@
 QT_BEGIN_NAMESPACE
 QT_CANVAS3D_BEGIN_NAMESPACE
 
+/*!
+ * \qmltype Canvas3DAbstractObject
+ * \since QtCanvas3D 1.0
+ * \inqmlmodule QtCanvas3D
+ * \brief Base type for Canvas3D types representing OpenGL resources.
+ *
+ * An uncreatable QML type that is the base type for other Canvas3D types that represent
+ * OpenGL resources.
+ */
+
 CanvasAbstractObject::CanvasAbstractObject(CanvasGlCommandQueue *queue, QObject *parent) :
     QObject(parent),
     m_hasName(false),
+    m_invalidated(false),
     m_commandQueue(queue)
+
 {
     m_name = QString("0x%1").arg((long long) this, 0, 16);
 }
@@ -52,6 +64,10 @@ CanvasAbstractObject::~CanvasAbstractObject()
 {
 }
 
+/*!
+ * \qmlproperty string Canvas3DAbstractObject::name
+ * Name of the object.
+ */
 void CanvasAbstractObject::setName(const QString &name)
 {
     if (m_name == name)
@@ -73,5 +89,41 @@ bool CanvasAbstractObject::hasSpecificName() const
     return m_hasName;
 }
 
+/*!
+ * \qmlproperty bool Canvas3DAbstractObject::invalidated
+ * Indicates if this object has been invalidated. Invalidated objects cannot be valid parameters
+ * in Context3D methods and will result in \c{Context3D.INVALID_OPERATION} error if used.
+ * Objects are invalidated when context is lost and cannot be validated again.
+ *
+ * \sa {Canvas3D::contextLost}{Canvas3D.contextLost}
+ */
+bool CanvasAbstractObject::invalidated() const
+{
+    return m_invalidated;
+}
+
+void CanvasAbstractObject::setInvalidated(bool invalidated)
+{
+    m_invalidated = invalidated;
+}
+
+void CanvasAbstractObject::queueCommand(CanvasGlCommandQueue::GlCommandId id, GLint p1, GLint p2)
+{
+    if (!m_invalidated)
+        m_commandQueue->queueCommand(id, p1, p2);
+}
+
+void CanvasAbstractObject::queueCommand(CanvasGlCommandQueue::GlCommandId id, QByteArray *data,
+                                        GLint p1, GLint p2)
+{
+    if (!m_invalidated) {
+        GlCommand &command = m_commandQueue->queueCommand(id, p1, p2);
+        command.data = data;
+    } else {
+        // We need to delete the data since we are not passing it to a command
+        delete data;
+    }
+}
+
 QT_CANVAS3D_END_NAMESPACE
 QT_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/abstractobject3d_p.h b/src/imports/qtcanvas3d/abstractobject3d_p.h
index 988d2c001822813c4edb2574078780406b35f675..a1060f6e5261d415cffabde7c037cc367e5aa313 100644
--- a/src/imports/qtcanvas3d/abstractobject3d_p.h
+++ b/src/imports/qtcanvas3d/abstractobject3d_p.h
@@ -59,6 +59,7 @@ class CanvasAbstractObject : public QObject
 {
     Q_OBJECT
     Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+    Q_PROPERTY(bool invalidated READ invalidated NOTIFY invalidatedChanged)
 
 public:
     explicit CanvasAbstractObject(CanvasGlCommandQueue *queue, QObject *parent);
@@ -67,15 +68,23 @@ public:
     void setName(const QString &name);
     const QString &name() const;
     bool hasSpecificName() const;
+    bool invalidated() const;
+    void setInvalidated(bool invalidated); // Internal
 
 signals:
     void nameChanged(const QString &name);
+    void invalidatedChanged(bool invalidated);
+
+protected:
+    void queueCommand(CanvasGlCommandQueue::GlCommandId id, GLint p1, GLint p2 = 0);
+    void queueCommand(CanvasGlCommandQueue::GlCommandId id, QByteArray *data,
+                      GLint p1, GLint p2 = 0);
+    CanvasGlCommandQueue *commandQueue() const { return m_commandQueue; }
 
 private:
     QString m_name;
     bool m_hasName;
-
-protected:
+    bool m_invalidated;
     // Not owned. Can be null pointer if the object type doesn't need OpenGL commands
     CanvasGlCommandQueue *m_commandQueue;
 };
diff --git a/src/imports/qtcanvas3d/buffer3d.cpp b/src/imports/qtcanvas3d/buffer3d.cpp
index 46137baf7c6b5b3370b8df31054c06b7698dff69..9e2a9821766b1e048cbbafcac0a791aa73ecc68b 100644
--- a/src/imports/qtcanvas3d/buffer3d.cpp
+++ b/src/imports/qtcanvas3d/buffer3d.cpp
@@ -46,6 +46,7 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DBuffer
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains an OpenGL buffer.
  *
  * An uncreatable QML type that contains an OpenGL buffer. You can get it by calling the
@@ -65,13 +66,11 @@ CanvasBuffer::CanvasBuffer(CanvasGlCommandQueue *queue, QObject *parent) :
     m_bufferId(queue->createResourceId()),
     m_bindTarget(CanvasBuffer::UNINITIALIZED)
 {
-    Q_ASSERT(m_commandQueue);
-
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glGenBuffers, m_bufferId);
+    queueCommand(CanvasGlCommandQueue::glGenBuffers, m_bufferId);
 }
 
 CanvasBuffer::CanvasBuffer(const CanvasBuffer& other) :
-    CanvasAbstractObject(other.m_commandQueue, 0), // Copying a QObject, leave it parentless..
+    CanvasAbstractObject(other.commandQueue(), 0), // Copying a QObject, leave it parentless..
     m_bufferId(other.m_bufferId),
     m_bindTarget(other.m_bindTarget)
 {
@@ -85,9 +84,8 @@ CanvasBuffer::~CanvasBuffer()
 
 void CanvasBuffer::del()
 {
-    if (m_bufferId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteBuffers, m_bufferId);
-    }
+    if (m_bufferId)
+        queueCommand(CanvasGlCommandQueue::glDeleteBuffers, m_bufferId);
     m_bufferId = 0;
 }
 
diff --git a/src/imports/qtcanvas3d/canvas3d.cpp b/src/imports/qtcanvas3d/canvas3d.cpp
index 39acb80f18c17df34aab76cd1d20c087b93d43ca..f8294c396680afe4f68ee5b70a169c384b83e099 100644
--- a/src/imports/qtcanvas3d/canvas3d.cpp
+++ b/src/imports/qtcanvas3d/canvas3d.cpp
@@ -83,7 +83,6 @@ Canvas::Canvas(QQuickItem *parent):
     QQuickItem(parent),
     m_isNeedRenderQueued(false),
     m_rendererReady(false),
-    m_context3D(0),
     m_fboSize(0, 0),
     m_maxSize(0, 0),
     m_frameTimeMs(0),
@@ -91,24 +90,25 @@ Canvas::Canvas(QQuickItem *parent):
     m_maxSamples(0),
     m_devicePixelRatio(1.0f),
     m_isOpenGLES2(false),
+    m_isCombinedDepthStencilSupported(false),
     m_isSoftwareRendered(false),
     m_isContextAttribsSet(false),
     m_alphaChanged(false),
     m_resizeGLQueued(false),
-    m_firstSync(true),
+    m_allowRenderTargetChange(true),
+    m_renderTargetSyncConnected(false),
     m_renderTarget(RenderTargetOffscreenBuffer),
     m_renderOnDemand(false),
     m_renderer(0),
     m_maxVertexAttribs(0),
     m_contextVersion(0),
-    m_fps(0)
+    m_fps(0),
+    m_contextState(ContextNone)
 {
     connect(this, &QQuickItem::windowChanged, this, &Canvas::handleWindowChanged);
     connect(this, &Canvas::needRender, this, &Canvas::queueNextRender, Qt::QueuedConnection);
     connect(this, &QQuickItem::widthChanged, this, &Canvas::queueResizeGL, Qt::DirectConnection);
     connect(this, &QQuickItem::heightChanged, this, &Canvas::queueResizeGL, Qt::DirectConnection);
-    connect(this, &QQuickItem::widthChanged, this, &Canvas::widthChanged, Qt::DirectConnection);
-    connect(this, &QQuickItem::heightChanged, this, &Canvas::heightChanged, Qt::DirectConnection);
     setAntialiasing(false);
 
     // Set contents to false in case we are in qml designer to make component look nice
@@ -132,64 +132,34 @@ Canvas::Canvas(QQuickItem *parent):
  * Driven by the Qt Quick scenegraph loop.
  */
 
-Canvas::~Canvas()
-{
-    // Ensure that all JS objects have been destroyed before we destroy the command queue.
-    delete m_context3D;
-
-    if (m_renderer) {
-        if (m_renderer->thread() == QThread::currentThread())
-            delete m_renderer;
-        else
-            m_renderer->deleteLater();
-    }
-}
-
 /*!
- * Override QQuickItem's setWidth to be able to limit the maximum canvas size to maximum viewport
- * dimensions.
+ * \qmlsignal void Canvas3D::contextLost()
+ * Emitted when OpenGL context is lost. This happens whenever the parent window of the Canvas3D
+ * is destroyed (or otherwise loses its context), or Canvas3D is moved to a different window.
+ * Removing Canvas3D from a window and adding it back to the same window doesn't cause context
+ * loss, as long as the window itself stays alive.
+ *
+ * When context is lost, all objects created by Context3D are invalidated.
+ *
+ * \sa contextRestored
  */
-void Canvas::setWidth(int width)
-{
-    int newWidth = width;
-    int maxWidth = m_maxSize.width();
-    if (maxWidth && width > maxWidth) {
-        qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
-                                             << "():"
-                                             << "Maximum width exceeded. Limiting to "
-                                             << maxWidth;
-        newWidth = maxWidth;
-    }
-    QQuickItem::setWidth(qreal(newWidth));
-}
-
-int Canvas::width()
-{
-    return int(QQuickItem::width());
-}
 
 /*!
- * Override QQuickItem's setHeight to be able to limit the maximum canvas size to maximum viewport
- * dimensions.
+ * \qmlsignal void Canvas3D::contextRestored()
+ * Emitted when OpenGL context is restored after a loss of context occurred. The Context3D attached
+ * to the canvas needs to be reinitialized, so initializeGL is also emitted after this signal.
+ *
+ * \sa contextLost
  */
-void Canvas::setHeight(int height)
-{
-    int newHeight = height;
-    int maxHeight = m_maxSize.height();
-    if (maxHeight && height > maxHeight) {
-        qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
-                                             << "():"
-                                             << "Maximum height exceeded. Limiting to "
-                                             << maxHeight;
-        newHeight = maxHeight;
-    }
-
-    QQuickItem::setHeight(qreal(newHeight));
-}
 
-int Canvas::height()
+Canvas::~Canvas()
 {
-    return int(QQuickItem::height());
+    // Ensure that all JS objects have been destroyed before we destroy the command queue.
+    if (!m_context3D.isNull())
+        delete m_context3D.data();
+
+    if (m_renderer)
+        m_renderer->destroy();
 }
 
 /*!
@@ -265,7 +235,7 @@ int Canvas::height()
  */
 void Canvas::setRenderTarget(RenderTarget target)
 {
-    if (m_firstSync) {
+    if (m_allowRenderTargetChange) {
         RenderTarget oldTarget = m_renderTarget;
         m_renderTarget = target;
         if (m_renderTarget == RenderTargetOffscreenBuffer)
@@ -274,6 +244,13 @@ void Canvas::setRenderTarget(RenderTarget target)
             setFlag(ItemHasContents, false);
         if (oldTarget != m_renderTarget)
             emit renderTargetChanged();
+        if (!m_renderTargetSyncConnected && window()
+                && m_renderTarget != RenderTargetOffscreenBuffer) {
+            m_renderTargetSyncConnected = true;
+            connect(window(), &QQuickWindow::beforeSynchronizing,
+                    this, &Canvas::handleBeforeSynchronizing, Qt::DirectConnection);
+            window()->setClearBeforeRendering(false);
+        }
     } else {
         qCWarning(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
                                                << ": renderTarget property can only be "
@@ -394,7 +371,8 @@ QJSValue Canvas::getContext(const QString &type, const QVariantMap &options)
         updateWindowParameters();
 
         if (!m_renderer->createContext(window(), m_contextAttribs, m_maxVertexAttribs, m_maxSize,
-                                       m_contextVersion, m_extensions)) {
+                                       m_contextVersion, m_extensions,
+                                       m_isCombinedDepthStencilSupported)) {
             return QJSValue(QJSValue::NullValue);
         }
 
@@ -405,37 +383,21 @@ QJSValue Canvas::getContext(const QString &type, const QVariantMap &options)
         m_context3D = new CanvasContext(QQmlEngine::contextForObject(this)->engine(),
                                         m_isOpenGLES2, m_maxVertexAttribs,
                                         m_contextVersion, m_extensions,
-                                        m_renderer->commandQueue());
+                                        m_renderer->commandQueue(),
+                                        m_isCombinedDepthStencilSupported);
 
         connect(m_renderer, &CanvasRenderer::textureIdResolved,
-                m_context3D, &CanvasContext::handleTextureIdResolved,
+                m_context3D.data(), &CanvasContext::handleTextureIdResolved,
                 Qt::QueuedConnection);
 
-        // Verify that width and height are not initially too large, in case width and height
-        // were set before getting GL_MAX_VIEWPORT_DIMS
-        if (width() > m_maxSize.width()) {
-            qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
-                                                 << "():"
-                                                 << "Maximum width exceeded. Limiting to "
-                                                 << m_maxSize.width();
-            QQuickItem::setWidth(m_maxSize.width());
-        }
-        if (height() > m_maxSize.height()) {
-            qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
-                                                 << "():"
-                                                 << "Maximum height exceeded. Limiting to "
-                                                 << m_maxSize.height();
-            QQuickItem::setHeight(m_maxSize.height());
-        }
-
         m_context3D->setCanvas(this);
         m_context3D->setDevicePixelRatio(m_devicePixelRatio);
         m_context3D->setContextAttributes(m_contextAttribs);
 
-        emit contextChanged(m_context3D);
+        emit contextChanged(m_context3D.data());
     }
 
-    return QQmlEngine::contextForObject(this)->engine()->newQObject(m_context3D);
+    return QQmlEngine::contextForObject(this)->engine()->newQObject(m_context3D.data());
 }
 
 /*!
@@ -488,10 +450,52 @@ void Canvas::setPixelSize(QSize pixelSize)
 void Canvas::handleWindowChanged(QQuickWindow *window)
 {
     qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__ << "(" << window << ")";
-    if (!window)
+
+    if (!window) {
+        if (!m_contextWindow.isNull()) {
+            if (m_renderTarget != RenderTargetOffscreenBuffer) {
+                disconnect(m_contextWindow.data(), &QQuickWindow::beforeSynchronizing,
+                           this, &Canvas::handleBeforeSynchronizing);
+            }
+            if (m_renderer) {
+                if (m_renderTarget == RenderTargetForeground) {
+                    disconnect(m_contextWindow.data(), &QQuickWindow::beforeRendering,
+                            m_renderer, &CanvasRenderer::clearBackground);
+                    disconnect(m_contextWindow.data(), &QQuickWindow::afterRendering,
+                            m_renderer, &CanvasRenderer::render);
+                } else {
+                    disconnect(m_contextWindow.data(), &QQuickWindow::beforeRendering,
+                            m_renderer, &CanvasRenderer::render);
+                }
+            }
+        }
         return;
+    }
 
-    if (m_renderTarget != RenderTargetOffscreenBuffer) {
+    if (window != m_contextWindow.data()) {
+        handleContextLost();
+        m_contextWindow = window;
+    } else {
+        // Re-added to same window
+        if (!m_context3D.isNull())
+            m_context3D->markQuickTexturesDirty();
+
+        if (m_renderer) {
+            if (m_renderTarget == RenderTargetForeground) {
+                connect(window, &QQuickWindow::beforeRendering,
+                        m_renderer, &CanvasRenderer::clearBackground, Qt::DirectConnection);
+                connect(window, &QQuickWindow::afterRendering,
+                        m_renderer, &CanvasRenderer::render, Qt::DirectConnection);
+            } else {
+                connect(window, &QQuickWindow::beforeRendering,
+                        m_renderer, &CanvasRenderer::render, Qt::DirectConnection);
+            }
+        }
+    }
+
+    if ((!m_allowRenderTargetChange || !m_renderTargetSyncConnected)
+            && m_renderTarget != RenderTargetOffscreenBuffer) {
+        m_renderTargetSyncConnected = true;
         connect(window, &QQuickWindow::beforeSynchronizing,
                 this, &Canvas::handleBeforeSynchronizing, Qt::DirectConnection);
         window->setClearBeforeRendering(false);
@@ -530,7 +534,7 @@ void Canvas::itemChange(ItemChange change, const ItemChangeData &value)
 CanvasContext *Canvas::context()
 {
     qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__ << "()";
-    return m_context3D;
+    return m_context3D.data();
 }
 
 void Canvas::updateWindowParameters()
@@ -550,7 +554,7 @@ void Canvas::updateWindowParameters()
         }
     }
 
-    if (m_context3D) {
+    if (!m_context3D.isNull()) {
         if (m_context3D->devicePixelRatio() != m_devicePixelRatio)
             m_context3D->setDevicePixelRatio(m_devicePixelRatio);
     }
@@ -576,15 +580,26 @@ bool Canvas::firstSync()
 {
     qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__ << "()";
 
-    if (!m_renderer) {
-        m_renderer = new CanvasRenderer();
+    if (m_contextState == ContextLost || !m_renderer) {
+        if (m_renderer)
+            m_renderer->destroy();
 
+        m_renderer = new CanvasRenderer();
+        m_contextState = ContextRestoring;
+
+        // Update necessary things to m_context3D
+        if (!m_context3D.isNull()) {
+           m_context3D->setCommandQueue(m_renderer->commandQueue());
+           connect(m_renderer, &CanvasRenderer::textureIdResolved,
+                   m_context3D.data(), &CanvasContext::handleTextureIdResolved,
+                   Qt::QueuedConnection);
+        }
         connect(m_renderer, &CanvasRenderer::fpsChanged,
                 this, &Canvas::handleRendererFpsChange);
     }
 
     if (!m_renderer->qtContextResolved()) {
-        m_firstSync = false;
+        m_allowRenderTargetChange = false;
         QSize initializedSize = boundingRect().size().toSize();
         if (initializedSize.width() <= 0)
             initializedSize.setWidth(1);
@@ -597,7 +612,7 @@ bool Canvas::firstSync()
             m_renderer->getQtContextAttributes(m_contextAttribs);
             m_isContextAttribsSet = true;
             m_renderer->init(window(), m_contextAttribs, m_maxVertexAttribs, m_maxSize,
-                             m_contextVersion, m_extensions);
+                             m_contextVersion, m_extensions, m_isCombinedDepthStencilSupported);
             setPixelSize(m_renderer->fboSize());
         } else {
             m_renderer->createContextShare();
@@ -606,6 +621,9 @@ bool Canvas::firstSync()
 
         connect(window(), &QQuickWindow::sceneGraphInvalidated,
                 m_renderer, &CanvasRenderer::shutDown, Qt::DirectConnection);
+        connect(window(), &QQuickWindow::sceneGraphInvalidated,
+                this, &Canvas::handleContextLost, Qt::QueuedConnection);
+        connect(window(), &QObject::destroyed, this, &Canvas::handleContextLost);
 
         if (m_renderTarget == RenderTargetForeground) {
             connect(window(), &QQuickWindow::beforeRendering,
@@ -770,16 +788,23 @@ void Canvas::queueNextRender()
         return;
     }
 
-    if (!m_context3D) {
+    if (m_context3D.isNull() || m_contextState == ContextRestoring) {
         // Call the initialize function from QML/JavaScript. It'll call the getContext()
         // that in turn creates the renderer context.
         qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
                                              << " Emit initializeGL() signal";
 
+        if (!m_context3D.isNull()) {
+            m_context3D->setContextLostState(false);
+            emit contextRestored();
+        }
+
         // Call init on JavaScript side to queue the user's GL initialization commands.
         // The initial context creation get will also initialize the context command queue.
         emit initializeGL();
 
+        m_contextState = ContextAlive;
+
         if (!m_isContextAttribsSet) {
             qCDebug(canvas3drendering).nospace() << "Canvas3D::" << __FUNCTION__
                                                  << " Context attributes not set, returning";
@@ -882,5 +907,26 @@ void Canvas::handleRendererFpsChange(uint fps)
     }
 }
 
+void Canvas::handleContextLost()
+{
+    if (m_contextState == ContextAlive || m_contextState == ContextRestoring) {
+        m_contextState = ContextLost;
+        m_rendererReady = false;
+        m_fboSize = QSize(0, 0);
+
+        if (!m_contextWindow.isNull()) {
+            disconnect(m_contextWindow.data(), &QQuickWindow::sceneGraphInvalidated,
+                       this, &Canvas::handleContextLost);
+            disconnect(m_contextWindow.data(), &QObject::destroyed,
+                        this, &Canvas::handleContextLost);
+        }
+
+        if (!m_context3D.isNull())
+            m_context3D->setContextLostState(true);
+
+        emit contextLost();
+    }
+}
+
 QT_CANVAS3D_END_NAMESPACE
 QT_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/canvas3d_p.h b/src/imports/qtcanvas3d/canvas3d_p.h
index 9ce63e2fc5e46374ce44eb40d5cea3a89bc71f8d..1cf67ab3e32c7392a234f324aa9084b1dfbcdb17 100644
--- a/src/imports/qtcanvas3d/canvas3d_p.h
+++ b/src/imports/qtcanvas3d/canvas3d_p.h
@@ -54,6 +54,7 @@
 #include <QtQuick/QQuickWindow>
 #include <QtGui/QOpenGLFramebufferObject>
 #include <QtCore/QElapsedTimer>
+#include <QtCore/QPointer>
 
 QT_BEGIN_NAMESPACE
 
@@ -88,8 +89,6 @@ class QT_CANVAS3D_EXPORT Canvas : public QQuickItem
     Q_PROPERTY(float devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged)
     Q_PROPERTY(uint fps READ fps NOTIFY fpsChanged)
     Q_PROPERTY(QSize pixelSize READ pixelSize WRITE setPixelSize NOTIFY pixelSizeChanged)
-    Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged)
-    Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged)
     Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged REVISION 1)
     Q_PROPERTY(bool renderOnDemand READ renderOnDemand WRITE setRenderOnDemand NOTIFY renderOnDemandChanged REVISION 1)
 
@@ -100,6 +99,14 @@ public:
         RenderTargetForeground
     };
 
+    // internal
+    enum ContextState {
+        ContextNone,
+        ContextLost,
+        ContextRestoring,
+        ContextAlive
+    };
+
     Canvas(QQuickItem *parent = 0);
     ~Canvas();
 
@@ -107,10 +114,6 @@ public:
     float devicePixelRatio();
     QSize pixelSize();
     void setPixelSize(QSize pixelSize);
-    void setWidth(int width);
-    int width();
-    void setHeight(int height);
-    int height();
     void setRenderTarget(RenderTarget target);
     RenderTarget renderTarget() const;
     void setRenderOnDemand(bool enable);
@@ -126,12 +129,15 @@ public:
     CanvasRenderer *renderer();
 
 public slots:
+    void requestRender();
+
+private slots:
     void queueNextRender();
     void queueResizeGL();
-    void requestRender();
     void emitNeedRender();
     void handleBeforeSynchronizing();
     void handleRendererFpsChange(uint fps);
+    void handleContextLost();
 
 signals:
     void needRender();
@@ -139,10 +145,10 @@ signals:
     void contextChanged(CanvasContext *context);
     void fpsChanged(uint fps);
     void pixelSizeChanged(QSize pixelSize);
-    void widthChanged();
-    void heightChanged();
     void renderTargetChanged();
     void renderOnDemandChanged();
+    void contextLost();
+    void contextRestored();
 
     void initializeGL();
     void paintGL();
@@ -163,7 +169,7 @@ private:
 
     bool m_isNeedRenderQueued;
     bool m_rendererReady;
-    CanvasContext *m_context3D;
+    QPointer<CanvasContext> m_context3D;
     QSize m_fboSize;
     QSize m_maxSize;
 
@@ -174,13 +180,15 @@ private:
     float m_devicePixelRatio;
 
     bool m_isOpenGLES2;
+    bool m_isCombinedDepthStencilSupported;
     bool m_isSoftwareRendered;
     bool m_runningInDesigner;
     CanvasContextAttributes m_contextAttribs;
     bool m_isContextAttribsSet;
     bool m_alphaChanged;
     bool m_resizeGLQueued;
-    bool m_firstSync;
+    bool m_allowRenderTargetChange;
+    bool m_renderTargetSyncConnected;
     RenderTarget m_renderTarget;
     bool m_renderOnDemand;
 
@@ -191,6 +199,9 @@ private:
     QSet<QByteArray> m_extensions;
 
     uint m_fps;
+
+    ContextState m_contextState;
+    QPointer<QQuickWindow> m_contextWindow; // Not owned
 };
 
 QT_CANVAS3D_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/canvasglstatedump.cpp b/src/imports/qtcanvas3d/canvasglstatedump.cpp
index 7b46e4ecf1836cedfbac2f97a9c441d6f1432edf..35698484db7bd50e63b7ed94a4b5fbaa2d4bd3e3 100644
--- a/src/imports/qtcanvas3d/canvasglstatedump.cpp
+++ b/src/imports/qtcanvas3d/canvasglstatedump.cpp
@@ -433,6 +433,9 @@ void CanvasGLStateDump::doGLStateDump()
  */
 QString CanvasGLStateDump::getGLStateDump(CanvasGLStateDump::stateDumpEnums options)
 {
+    if (m_canvasContext->isContextLost())
+        return QString();
+
     m_options = options;
     m_stateDumpStr.clear();
 
diff --git a/src/imports/qtcanvas3d/canvasrenderer.cpp b/src/imports/qtcanvas3d/canvasrenderer.cpp
index 61778be55d1a9cbefc3c47113094d00f1ad39e77..86ca838e26b4106488985ba2504bd3d4fb406e0d 100644
--- a/src/imports/qtcanvas3d/canvasrenderer.cpp
+++ b/src/imports/qtcanvas3d/canvasrenderer.cpp
@@ -46,6 +46,7 @@
 #include <QtQml/QQmlEngine>
 #include <QtQml/QQmlContext>
 #include <QtQuick/QQuickWindow>
+#include <QtCore/QMutexLocker>
 
 QT_BEGIN_NAMESPACE
 QT_CANVAS3D_BEGIN_NAMESPACE
@@ -136,7 +137,7 @@ void CanvasRenderer::createContextShare()
     m_glContextQt->doneCurrent();
     if (!m_glContextShare->create()) {
         qCWarning(canvas3drendering).nospace() << "CanvasRenderer::" << __FUNCTION__
-                                               << "Failed to create share context";
+                                               << " Failed to create share context";
     }
     if (!m_glContextQt->makeCurrent(surface)) {
         qCWarning(canvas3drendering).nospace() << "CanvasRenderer::" << __FUNCTION__
@@ -157,7 +158,7 @@ void CanvasRenderer::getQtContextAttributes(CanvasContextAttributes &contextAttr
 
 void CanvasRenderer::init(QQuickWindow *window, const CanvasContextAttributes &contextAttributes,
                           GLint &maxVertexAttribs, QSize &maxSize, int &contextVersion,
-                          QSet<QByteArray> &extensions)
+                          QSet<QByteArray> &extensions, bool &isCombinedDepthStencilSupported)
 {
     m_antialias = contextAttributes.antialias();
     m_preserveDrawingBuffer = contextAttributes.preserveDrawingBuffer();
@@ -177,6 +178,10 @@ void CanvasRenderer::init(QQuickWindow *window, const CanvasContextAttributes &c
     maxSize.setHeight(viewportDims[1]);
 
     // Set the size
+    if (maxSize.width() < m_initializedSize.width())
+        m_initializedSize.setWidth(maxSize.width());
+    if (maxSize.height() < m_initializedSize.height())
+        m_initializedSize.setHeight(maxSize.height());
     setFboSize(m_initializedSize);
     m_forceViewportRect = QRect(0, 0, m_fboSize.width(), m_fboSize.height());
     glScissor(0, 0, m_fboSize.width(), m_fboSize.height());
@@ -223,6 +228,19 @@ void CanvasRenderer::init(QQuickWindow *window, const CanvasContextAttributes &c
 
     contextVersion = m_glContext->format().majorVersion();
 
+    if (contextVersion < 3) {
+        if (m_isOpenGLES2) {
+            isCombinedDepthStencilSupported =
+                    m_glContext->hasExtension(QByteArrayLiteral("GL_OES_packed_depth_stencil"));
+        } else {
+            isCombinedDepthStencilSupported =
+                    m_glContext->hasExtension(QByteArrayLiteral("GL_ARB_framebuffer_object"))
+                    || m_glContext->hasExtension(QByteArrayLiteral("GL_EXT_packed_depth_stencil"));
+        }
+    } else {
+        isCombinedDepthStencilSupported = true;
+    }
+
     extensions = m_glContext->extensions();
 
     if (!m_alphaMultiplierProgram) {
@@ -300,7 +318,7 @@ void CanvasRenderer::shutDown()
     if (!m_glContext)
         return;
 
-    disconnect(m_contextWindow, 0, this, 0);
+    QMutexLocker locker(&m_shutdownMutex);
 
     m_fps = 0;
 
@@ -364,7 +382,8 @@ void CanvasRenderer::shutDown()
 bool CanvasRenderer::createContext(QQuickWindow *window,
                                    const CanvasContextAttributes &contextAttributes,
                                    GLint &maxVertexAttribs, QSize &maxSize,
-                                   int &contextVersion, QSet<QByteArray> &extensions)
+                                   int &contextVersion, QSet<QByteArray> &extensions,
+                                   bool &isCombinedDepthStencilSupported)
 {
     // Initialize the swap buffer chain
     if (contextAttributes.depth() && contextAttributes.stencil() && !contextAttributes.antialias())
@@ -453,7 +472,8 @@ bool CanvasRenderer::createContext(QQuickWindow *window,
         return false;
     }
 
-    init(window, contextAttributes, maxVertexAttribs, maxSize, contextVersion, extensions);
+    init(window, contextAttributes, maxVertexAttribs, maxSize, contextVersion, extensions,
+         isCombinedDepthStencilSupported);
 
     if (m_glContext->thread() != contextThread) {
         m_glContext->doneCurrent();
@@ -1949,5 +1969,19 @@ qint64 CanvasRenderer::previousFrameTime()
     return m_frameTimeMs;
 }
 
+void CanvasRenderer::destroy()
+{
+    QMutexLocker locker(&m_shutdownMutex);
+
+    if (m_glContext) {
+        deleteLater();
+    } else {
+        // It is safe to delete even in another thread if we are already shut down or not yet
+        // started up.
+        locker.unlock();
+        delete this;
+    }
+}
+
 QT_CANVAS3D_END_NAMESPACE
 QT_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/canvasrenderer_p.h b/src/imports/qtcanvas3d/canvasrenderer_p.h
index 0740e9bb99c7da49bc51f4d32ad604a5628e0504..2f98974aa4dabdbf26548b31092635eb53381f89 100644
--- a/src/imports/qtcanvas3d/canvasrenderer_p.h
+++ b/src/imports/qtcanvas3d/canvasrenderer_p.h
@@ -58,6 +58,7 @@
 #include <QtGui/QOpenGLFunctions>
 #include <QtGui/QOpenGLFramebufferObject>
 #include <QtQuick/QQuickItem>
+#include <QtCore/QMutex>
 
 QT_BEGIN_NAMESPACE
 
@@ -83,10 +84,10 @@ public:
     void getQtContextAttributes(CanvasContextAttributes &contextAttributes);
     void init(QQuickWindow *window, const CanvasContextAttributes &contextAttributes,
               GLint &maxVertexAttribs, QSize &maxSize, int &contextVersion,
-              QSet<QByteArray> &extensions);
+              QSet<QByteArray> &extensions, bool &isCombinedDepthStencilSupported);
     bool createContext(QQuickWindow *window, const CanvasContextAttributes &contextAttributes,
                        GLint &maxVertexAttribs, QSize &maxSize, int &contextVersion,
-                       QSet<QByteArray> &extensions);
+                       QSet<QByteArray> &extensions, bool &isCombinedDepthStencilSupported);
 
     void createFBOs();
     void bindCurrentRenderTarget();
@@ -114,6 +115,7 @@ public:
     void deleteCommandData();
 
     qint64 previousFrameTime();
+    void destroy();
 
 public slots:
     void shutDown();
@@ -183,6 +185,7 @@ private:
     bool m_textureFinalized;
 
     GLbitfield m_clearMask;
+    QMutex m_shutdownMutex;
 };
 
 QT_CANVAS3D_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/context3d.cpp b/src/imports/qtcanvas3d/context3d.cpp
index 829d5a90bed6e293f8b629435327946c8d6e036b..8700319fea2fbb7c36d15d4ef3210b0d631e0a0d 100644
--- a/src/imports/qtcanvas3d/context3d.cpp
+++ b/src/imports/qtcanvas3d/context3d.cpp
@@ -66,6 +66,10 @@ QT_CANVAS3D_BEGIN_NAMESPACE
 
 const int maxUniformAttributeNameLen = 512;
 
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+
 /*!
  * \qmltype Context3D
  * \since QtCanvas3D 1.0
@@ -78,10 +82,10 @@ const int maxUniformAttributeNameLen = 512;
  *
  * \sa Canvas3D
  */
-
 CanvasContext::CanvasContext(QQmlEngine *engine, bool isES2, int maxVertexAttribs,
                              int contextVersion, const QSet<QByteArray> &extensions,
-                             CanvasGlCommandQueue *commandQueue, QObject *parent) :
+                             CanvasGlCommandQueue *commandQueue,
+                             bool isCombinedDepthStencilSupported, QObject *parent) :
     CanvasAbstractObject(0, parent),
     m_engine(engine),
     m_v4engine(QQmlEnginePrivate::getV4Engine(engine)),
@@ -102,16 +106,17 @@ CanvasContext::CanvasContext(QQmlEngine *engine, bool isES2, int maxVertexAttrib
     m_maxVertexAttribs(maxVertexAttribs),
     m_contextVersion(contextVersion),
     m_isOpenGLES2(isES2),
-    m_commandQueue(commandQueue),
+    m_isCombinedDepthStencilSupported(isCombinedDepthStencilSupported),
+    m_commandQueue(0),
+    m_contextLost(false),
+    m_contextLostErrorReported(false),
     m_stateDumpExt(0),
     m_textureProviderExt(0),
     m_standardDerivatives(0),
     m_compressedTextureS3TC(0),
     m_compressedTexturePVRTC(0)
 {
-    connect(m_commandQueue, &CanvasGlCommandQueue::queueFull,
-            this, &CanvasContext::handleFullCommandQueue, Qt::DirectConnection);
-
+    setCommandQueue(commandQueue);
 }
 
 CanvasContext::~CanvasContext()
@@ -257,13 +262,15 @@ QJSValue CanvasContext::getShaderPrecisionFormat(glEnums shadertype,
         break;
     }
 
-    // On desktop envs glGetShaderPrecisionFormat is part of OpenGL 4.x, so it is not necessarily
-    // available. Let's just return the default values if not ES2.
-    if (m_isOpenGLES2) {
-        GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetShaderPrecisionFormat,
-                                  GLint(shadertype), GLint(precisiontype));
-        syncCommand.returnValue = retval;
-        scheduleSyncCommand(&syncCommand);
+    if (!checkContextLost()) {
+        // On desktop envs glGetShaderPrecisionFormat is part of OpenGL 4.x, so it is not necessarily
+        // available. Let's just return the default values if not ES2.
+        if (m_isOpenGLES2) {
+            GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetShaderPrecisionFormat,
+                                      GLint(shadertype), GLint(precisiontype));
+            syncCommand.returnValue = retval;
+            scheduleSyncCommand(&syncCommand);
+        }
     }
 
     CanvasShaderPrecisionFormat *format = new CanvasShaderPrecisionFormat();
@@ -275,13 +282,15 @@ QJSValue CanvasContext::getShaderPrecisionFormat(glEnums shadertype,
 
 /*!
  * \qmlmethod bool Context3D::isContextLost()
- * Always returns false.
+ * Returns \c{true} if the context is currently lost.
+ *
+ * \sa {Canvas3D::contextLost}{Canvas3D.contextLost}
  */
 bool CanvasContext::isContextLost()
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "(): false";
-    return false;
+                                         << "(): " << m_contextLost;
+    return m_contextLost;
 }
 
 /*!
@@ -292,19 +301,23 @@ QJSValue CanvasContext::getContextAttributes()
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__ << "()";
 
-    CanvasContextAttributes *attributes = new CanvasContextAttributes();
-    attributes->setAlpha(m_contextAttributes.alpha());
-    attributes->setDepth(m_contextAttributes.depth());
-    attributes->setStencil(m_contextAttributes.stencil());
-    attributes->setAntialias(m_contextAttributes.antialias());
-    attributes->setPremultipliedAlpha(m_contextAttributes.premultipliedAlpha());
-    attributes->setPreserveDrawingBuffer(m_contextAttributes.preserveDrawingBuffer());
-    attributes->setPreferLowPowerToHighPerformance(
-                m_contextAttributes.preferLowPowerToHighPerformance());
-    attributes->setFailIfMajorPerformanceCaveat(
-                m_contextAttributes.failIfMajorPerformanceCaveat());
+    if (checkContextLost()) {
+        return QJSValue(QJSValue::NullValue);
+    } else {
+        CanvasContextAttributes *attributes = new CanvasContextAttributes();
+        attributes->setAlpha(m_contextAttributes.alpha());
+        attributes->setDepth(m_contextAttributes.depth());
+        attributes->setStencil(m_contextAttributes.stencil());
+        attributes->setAntialias(m_contextAttributes.antialias());
+        attributes->setPremultipliedAlpha(m_contextAttributes.premultipliedAlpha());
+        attributes->setPreserveDrawingBuffer(m_contextAttributes.preserveDrawingBuffer());
+        attributes->setPreferLowPowerToHighPerformance(
+                    m_contextAttributes.preferLowPowerToHighPerformance());
+        attributes->setFailIfMajorPerformanceCaveat(
+                    m_contextAttributes.failIfMajorPerformanceCaveat());
 
-    return m_engine->newQObject(attributes);
+        return m_engine->newQObject(attributes);
+    }
 }
 
 /*!
@@ -313,8 +326,10 @@ QJSValue CanvasContext::getContextAttributes()
  */
 void CanvasContext::flush()
 {
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "()";
+    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__ << "()";
+
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glFlush);
 }
@@ -326,8 +341,11 @@ void CanvasContext::flush()
  */
 void CanvasContext::finish()
 {
-    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                         << "()";
+    qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__ << "()";
+
+    if (checkContextLost())
+        return;
+
     GlSyncCommand syncCommand(CanvasGlCommandQueue::glFinish);
     scheduleSyncCommand(&syncCommand);
 }
@@ -338,10 +356,14 @@ void CanvasContext::finish()
  */
 QJSValue CanvasContext::createTexture()
 {
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     CanvasTexture *texture = new CanvasTexture(m_commandQueue, this);
     QJSValue value = m_engine->newQObject(texture);
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "():" << value.toString();
+    addObjectToValidList(texture);
     return value;
 }
 
@@ -359,7 +381,7 @@ void CanvasContext::deleteTexture(QJSValue texture3D)
 
     CanvasTexture *texture = getAsTexture3D(texture3D);
     if (texture) {
-        if (!checkParent(texture, __FUNCTION__))
+        if (!checkValidity(texture, __FUNCTION__))
             return;
         texture->del();
     } else {
@@ -387,6 +409,9 @@ void CanvasContext::scissor(int x, int y, int width, int height)
                                          << ", height:" << height
                                          << ")";
 
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glScissor,
                                  GLint(x), GLint(y), GLint(width), GLint(height));
 }
@@ -403,6 +428,9 @@ void CanvasContext::activeTexture(glEnums texture)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(texture:" << glEnumToString(texture)
                                          << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glActiveTexture, GLint(texture));
 }
 
@@ -433,7 +461,7 @@ void CanvasContext::bindTexture(glEnums target, QJSValue texture3D)
         return;
     }
 
-    if (texture && checkParent(texture, __FUNCTION__)) {
+    if (texture && checkValidity(texture, __FUNCTION__)) {
         if (target == TEXTURE_2D)
             m_currentTexture2D->bind(target);
         else if (target == TEXTURE_CUBE_MAP)
@@ -445,6 +473,9 @@ void CanvasContext::bindTexture(glEnums target, QJSValue texture3D)
 
 bool CanvasContext::isValidTextureBound(glEnums target, const QString &funcName, bool singleLayer)
 {
+    if (checkContextLost())
+        return false;
+
     switch (target) {
     case TEXTURE_2D:
         if (!m_currentTexture2D) {
@@ -503,16 +534,30 @@ bool CanvasContext::isValidTextureBound(glEnums target, const QString &funcName,
     return true;
 }
 
-bool CanvasContext::checkParent(QObject *obj, const char *function)
+bool CanvasContext::checkValidity(CanvasAbstractObject *obj, const char *function)
 {
-    if (obj && obj->parent() != this) {
-        m_error |= CANVAS_INVALID_OPERATION;
-        qCWarning(canvas3drendering).nospace() << "Context3D::" << function
-                                               << ":INVALID_OPERATION:"
-                                               << "Object from wrong context";
-        return false;
+    if (obj) {
+        if (obj->invalidated()) {
+            m_error |= CANVAS_INVALID_OPERATION;
+            qCWarning(canvas3drendering).nospace() << "Context3D::" << function
+                                                   << ":INVALID_OPERATION:"
+                                                   << "Object is invalid";
+            return false;
+        } else if (obj->parent() != this) {
+            m_error |= CANVAS_INVALID_OPERATION;
+            qCWarning(canvas3drendering).nospace() << "Context3D::" << function
+                                                   << ":INVALID_OPERATION:"
+                                                   << "Object from wrong context";
+            return false;
+        }
+        return true;
     }
-    return true;
+
+    m_error |= CANVAS_INVALID_OPERATION;
+    qCWarning(canvas3drendering).nospace() << "Context3D::" << function
+                                           << ":INVALID_OPERATION:"
+                                           << "Null object";
+    return false;
 }
 
 /*!
@@ -561,7 +606,7 @@ void CanvasContext::uniformMatrixNfv(int dim, const QJSValue &location3D, bool t
     CanvasUniformLocation *locationObj =
             static_cast<CanvasUniformLocation *>(location3D.toQObject());
 
-    if (!checkParent(locationObj, __FUNCTION__))
+    if (!checkValidity(locationObj, __FUNCTION__))
         return;
 
     // Check if we have a JavaScript array
@@ -695,7 +740,7 @@ bool CanvasContext::isTexture(QJSValue anyObject)
                                          << ")";
 
     CanvasTexture *texture = getAsTexture3D(anyObject);
-    if (texture && checkParent(texture, __FUNCTION__)) {
+    if (texture && checkValidity(texture, __FUNCTION__)) {
         GLboolean boolValue;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glIsTexture, texture->textureId());
         syncCommand.returnValue = &boolValue;
@@ -949,7 +994,7 @@ bool CanvasContext::checkTextureFormats(glEnums internalFormat, glEnums format)
     if (format != internalFormat) {
         qCWarning(canvas3drendering).nospace() << "Context3D::texImage2D()"
                                                << ":INVALID_OPERATION:"
-                                               << "internalFormat  doesn't match format";
+                                               << "internalFormat doesn't match format";
         m_error |= CANVAS_INVALID_OPERATION;
         return false;
     }
@@ -957,6 +1002,17 @@ bool CanvasContext::checkTextureFormats(glEnums internalFormat, glEnums format)
     return true;
 }
 
+bool CanvasContext::checkContextLost()
+{
+    if (m_contextLost) {
+        qCWarning(canvas3drendering).nospace() << "Context3D::checkContextValid()"
+                                               << ":CONTEXT LOST:"
+                                               << "Context has been lost";
+        return true;
+    }
+    return false;
+}
+
 /*!
  * \qmlmethod void Context3D::texImage2D(glEnums target, int level, glEnums internalformat, int width, int height, int border, glEnums format, glEnums type, TypedArray pixels)
  * Specify a 2D texture image.
@@ -1659,11 +1715,14 @@ int CanvasContext::getSufficientSize(glEnums internalFormat, int width, int heig
  */
 QJSValue CanvasContext::createFramebuffer()
 {
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     CanvasFrameBuffer *framebuffer = new CanvasFrameBuffer(m_commandQueue, this);
     QJSValue value = m_engine->newQObject(framebuffer);
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << ":" << value.toString();
-
+    addObjectToValidList(framebuffer);
     return value;
 }
 
@@ -1689,14 +1748,15 @@ void CanvasContext::bindFramebuffer(glEnums target, QJSValue buffer)
     CanvasFrameBuffer *framebuffer = getAsFramebuffer(buffer);
 
     GLint bindId = 0;
-    if (framebuffer && checkParent(framebuffer, __FUNCTION__)) {
+    if (framebuffer && checkValidity(framebuffer, __FUNCTION__)) {
         m_currentFramebuffer = framebuffer;
         bindId = framebuffer->id();
     } else {
         m_currentFramebuffer = 0;
     }
 
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindFramebuffer, bindId);
+    if (!checkContextLost())
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindFramebuffer, bindId);
 }
 
 /*!
@@ -1710,6 +1770,9 @@ CanvasContext::glEnums CanvasContext::checkFramebufferStatus(glEnums target)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(target:" << glEnumToString(target)
                                          << ")";
+    if (checkContextLost())
+        return FRAMEBUFFER_UNSUPPORTED;
+
     if (target != FRAMEBUFFER) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                                << ": INVALID_ENUM bind target, must be FRAMEBUFFER";
@@ -1771,22 +1834,38 @@ void CanvasContext::framebufferRenderbuffer(glEnums target, glEnums attachment,
     }
 
     CanvasRenderBuffer *renderbuffer = getAsRenderbuffer3D(renderbuffer3D);
-    if (renderbuffer && renderbuffertarget != RENDERBUFFER) {
-        qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                               << "(): INVALID_OPERATION renderbuffertarget must be"
-                                               << " RENDERBUFFER for non null renderbuffers";
-        m_error |= CANVAS_INVALID_OPERATION;
-        return;
+    if (renderbuffer) {
+        if (renderbuffertarget != RENDERBUFFER) {
+            qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                                   << "(): INVALID_OPERATION renderbuffertarget must be"
+                                                   << " RENDERBUFFER for non null renderbuffers";
+            m_error |= CANVAS_INVALID_OPERATION;
+            return;
+        }
+        if (!checkValidity(renderbuffer, __FUNCTION__))
+            return;
     }
-    if (!checkParent(renderbuffer, __FUNCTION__))
-        return;
 
     GLint renderbufferId = renderbuffer ? renderbuffer->id() : 0;
 
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glFramebufferRenderbuffer,
-                                 GLint(target), GLint(attachment),
-                                 GLint(renderbuffertarget),
-                                 GLint(renderbufferId));
+    if (attachment == DEPTH_STENCIL_ATTACHMENT) {
+        GLint secondaryId = m_isCombinedDepthStencilSupported
+                ? renderbufferId
+                : (renderbuffer ? renderbuffer->secondaryId() : 0);
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glFramebufferRenderbuffer,
+                                     GLint(GL_FRAMEBUFFER), GLint(DEPTH_ATTACHMENT),
+                                     GLint(GL_RENDERBUFFER),
+                                     GLint(renderbufferId));
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glFramebufferRenderbuffer,
+                                     GLint(GL_FRAMEBUFFER), GLint(STENCIL_ATTACHMENT),
+                                     GLint(GL_RENDERBUFFER),
+                                     GLint(secondaryId));
+    } else {
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glFramebufferRenderbuffer,
+                                     GLint(GL_FRAMEBUFFER), GLint(attachment),
+                                     GLint(GL_RENDERBUFFER),
+                                     GLint(renderbufferId));
+    }
 }
 
 /*!
@@ -1835,7 +1914,7 @@ void CanvasContext::framebufferTexture2D(glEnums target, glEnums attachment, glE
 
     CanvasTexture *texture = getAsTexture3D(texture3D);
     if (texture) {
-        if (!checkParent(texture, __FUNCTION__))
+        if (!checkValidity(texture, __FUNCTION__))
             return;
         if (textarget != TEXTURE_2D
                 && textarget != TEXTURE_CUBE_MAP_POSITIVE_X
@@ -1864,11 +1943,13 @@ void CanvasContext::framebufferTexture2D(glEnums target, glEnums attachment, glE
         }
     }
 
-    GLint textureId = texture ? texture->textureId() : 0;
-    m_currentFramebuffer->setTexture(texture);
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glFramebufferTexture2D,
-                                 GLint(target), GLint(attachment),
-                                 GLint(textarget), textureId, GLint(level));
+    if (!checkContextLost()) {
+        GLint textureId = texture ? texture->textureId() : 0;
+        m_currentFramebuffer->setTexture(texture);
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glFramebufferTexture2D,
+                                     GLint(target), GLint(attachment),
+                                     GLint(textarget), textureId, GLint(level));
+    }
 }
 
 /*!
@@ -1884,7 +1965,7 @@ bool CanvasContext::isFramebuffer(QJSValue anyObject)
                                          << ")";
 
     CanvasFrameBuffer *fbo = getAsFramebuffer(anyObject);
-    if (fbo && checkParent(fbo, __FUNCTION__)) {
+    if (fbo && checkValidity(fbo, __FUNCTION__)) {
         GLboolean boolValue;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glIsFramebuffer, fbo->id());
         syncCommand.returnValue = &boolValue;
@@ -1922,7 +2003,7 @@ void CanvasContext::deleteFramebuffer(QJSValue buffer)
 
     CanvasFrameBuffer *fbo = getAsFramebuffer(buffer);
     if (fbo) {
-        if (!checkParent(fbo, __FUNCTION__))
+        if (!checkValidity(fbo, __FUNCTION__))
             return;
         fbo->del();
     } else {
@@ -1939,10 +2020,15 @@ void CanvasContext::deleteFramebuffer(QJSValue buffer)
  */
 QJSValue CanvasContext::createRenderbuffer()
 {
-    CanvasRenderBuffer *renderbuffer = new CanvasRenderBuffer(m_commandQueue, this);
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
+    CanvasRenderBuffer *renderbuffer =
+            new CanvasRenderBuffer(m_commandQueue, !m_isCombinedDepthStencilSupported, this);
     QJSValue value = m_engine->newQObject(renderbuffer);
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "():" << value.toString();
+    addObjectToValidList(renderbuffer);
     return value;
 }
 
@@ -1967,14 +2053,16 @@ void CanvasContext::bindRenderbuffer(glEnums target, QJSValue renderbuffer3D)
 
     CanvasRenderBuffer *renderbuffer = getAsRenderbuffer3D(renderbuffer3D);
     GLint bindId = 0;
-    if (renderbuffer && checkParent(renderbuffer, __FUNCTION__)) {
+    if (renderbuffer && checkValidity(renderbuffer, __FUNCTION__)) {
         m_currentRenderbuffer = renderbuffer;
         bindId = renderbuffer->id();
     } else {
         m_currentRenderbuffer = 0;
     }
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindRenderbuffer,
-                                 GLint(GL_RENDERBUFFER), bindId);
+    if (!checkContextLost()) {
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindRenderbuffer,
+                                     GLint(GL_RENDERBUFFER), bindId);
+    }
 }
 
 /*!
@@ -1983,7 +2071,7 @@ void CanvasContext::bindRenderbuffer(glEnums target, QJSValue renderbuffer3D)
  * \a target must be \c Context3D.RENDERBUFFER.
  * \a internalformat specifies the color-renderable, depth-renderable or stencil-renderable format
  * of the renderbuffer. Must be one of \c{Context3D.RGBA4}, \c{Context3D.RGB565}, \c{Context3D.RGB5_A1},
- * \c{Context3D.DEPTH_COMPONENT16} or \c{Context3D.STENCIL_INDEX8}.
+ * \c{Context3D.DEPTH_COMPONENT16}, \c{Context3D.STENCIL_INDEX8}, or \c{Context3D.DEPTH_STENCIL}.
  * \a width specifies the renderbuffer width in pixels.
  * \a height specifies the renderbuffer height in pixels.
  */
@@ -1997,6 +2085,9 @@ void CanvasContext::renderbufferStorage(glEnums target, glEnums internalformat,
                                          << ", height:" << height
                                          << ")";
 
+    if (checkContextLost())
+        return;
+
     if (target != RENDERBUFFER) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                                << ": INVALID_ENUM target must be RENDERBUFFER";
@@ -2004,9 +2095,38 @@ void CanvasContext::renderbufferStorage(glEnums target, glEnums internalformat,
         return;
     }
 
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glRenderbufferStorage,
-                                 GLint(target), GLint(internalformat),
-                                 GLint(width), GLint(height));
+    if (!m_currentRenderbuffer) {
+        qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                               << ": INVALID_OPERATION no renderbuffer bound";
+        m_error |= CANVAS_INVALID_OPERATION;
+        return;
+    }
+
+    if (internalformat == DEPTH_STENCIL) {
+        if (m_isCombinedDepthStencilSupported) {
+            m_commandQueue->queueCommand(CanvasGlCommandQueue::glRenderbufferStorage,
+                                         GLint(target), GLint(GL_DEPTH24_STENCIL8),
+                                         GLint(width), GLint(height));
+        } else {
+            // Some platforms do not support combined DEPTH_STENCIL buffer natively, so create
+            // two separate render buffers for them. Depth buffer is the primary buffer
+            // and the stencil buffer is the secondary buffer.
+            m_commandQueue->queueCommand(CanvasGlCommandQueue::glRenderbufferStorage,
+                                         GLint(target), GLint(GL_DEPTH_COMPONENT16),
+                                         GLint(width), GLint(height));
+            m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindRenderbuffer,
+                                         GLint(target), m_currentRenderbuffer->secondaryId());
+            m_commandQueue->queueCommand(CanvasGlCommandQueue::glRenderbufferStorage,
+                                         GLint(target), GLint(GL_STENCIL_INDEX8),
+                                         GLint(width), GLint(height));
+            m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindRenderbuffer,
+                                         GLint(target), m_currentRenderbuffer->id());
+        }
+    } else {
+        m_commandQueue->queueCommand(CanvasGlCommandQueue::glRenderbufferStorage,
+                                     GLint(target), GLint(internalformat),
+                                     GLint(width), GLint(height));
+    }
 }
 
 /*!
@@ -2022,7 +2142,7 @@ bool CanvasContext::isRenderbuffer(QJSValue anyObject)
                                          << ")";
 
     CanvasRenderBuffer *rbo = getAsRenderbuffer3D(anyObject);
-    if (rbo && checkParent(rbo, __FUNCTION__)) {
+    if (rbo && checkValidity(rbo, __FUNCTION__)) {
         GLboolean boolValue;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glIsRenderbuffer, rbo->id());
         syncCommand.returnValue = &boolValue;
@@ -2059,7 +2179,7 @@ void CanvasContext::deleteRenderbuffer(QJSValue renderbuffer3D)
 
     CanvasRenderBuffer *renderbuffer = getAsRenderbuffer3D(renderbuffer3D);
     if (renderbuffer) {
-        if (!checkParent(renderbuffer, __FUNCTION__))
+        if (!checkValidity(renderbuffer, __FUNCTION__))
             return;
         renderbuffer->del();
     } else {
@@ -2082,6 +2202,9 @@ void CanvasContext::sampleCoverage(float value, bool invert)
                                          << "(value:" << value
                                          << ", invert:" << invert
                                          << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glSampleCoverage,
                                  GLint(invert), GLfloat(value));
 }
@@ -2093,11 +2216,14 @@ void CanvasContext::sampleCoverage(float value, bool invert)
  */
 QJSValue CanvasContext::createProgram()
 {
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     CanvasProgram *program = new CanvasProgram(m_commandQueue, this);
     QJSValue value = m_engine->newQObject(program);
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "():" << value.toString();
-
+    addObjectToValidList(program);
     return value;
 }
 
@@ -2115,7 +2241,7 @@ bool CanvasContext::isProgram(QJSValue anyObject)
 
     CanvasProgram *program = getAsProgram3D(anyObject);
 
-    if (program && checkParent(program, __FUNCTION__)) {
+    if (program && checkValidity(program, __FUNCTION__)) {
         GLboolean boolValue;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glIsProgram, program->id());
         syncCommand.returnValue = &boolValue;
@@ -2153,7 +2279,7 @@ void CanvasContext::deleteProgram(QJSValue program3D)
     CanvasProgram *program = getAsProgram3D(program3D, true);
 
     if (program) {
-        if (!checkParent(program, __FUNCTION__))
+        if (!checkValidity(program, __FUNCTION__))
             return;
         program->del();
     } else {
@@ -2195,7 +2321,7 @@ void CanvasContext::attachShader(QJSValue program3D, QJSValue shader3D)
         return;
     }
 
-    if (!checkParent(program, __FUNCTION__) || !checkParent(shader, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__) || !checkValidity(shader, __FUNCTION__))
         return;
 
     program->attach(shader);
@@ -2220,7 +2346,7 @@ QJSValue CanvasContext::getAttachedShaders(QJSValue program3D)
         return QJSValue(QJSValue::NullValue);
     }
 
-    if (!checkParent(program, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__))
         return QJSValue(QJSValue::NullValue);
 
     QList<CanvasShader *> shaders = program->attachedShaders();
@@ -2269,7 +2395,7 @@ void CanvasContext::detachShader(QJSValue program3D, QJSValue shader3D)
         return;
     }
 
-    if (!checkParent(program, __FUNCTION__) || !checkParent(shader, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__) || !checkValidity(shader, __FUNCTION__))
         return;
 
     program->detach(shader);
@@ -2288,7 +2414,7 @@ void CanvasContext::linkProgram(QJSValue program3D)
 
     CanvasProgram *program = getAsProgram3D(program3D);
 
-    if (!program || !checkParent(program, __FUNCTION__)) {
+    if (!program || !checkValidity(program, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
@@ -2306,6 +2432,9 @@ void CanvasContext::lineWidth(float width)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(width:" << width
                                          << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glLineWidth, GLfloat(width));
 }
 
@@ -2323,6 +2452,9 @@ void CanvasContext::polygonOffset(float factor, float units)
                                          << "(factor:" << factor
                                          << ", units:" << units
                                          << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glPolygonOffset,
                                  GLfloat(factor), GLfloat(units));
 }
@@ -2348,6 +2480,9 @@ void CanvasContext::pixelStorei(glEnums pname, int param)
                                          << ", param:" << param
                                          << ")";
 
+    if (checkContextLost())
+        return;
+
     switch (pname) {
     case UNPACK_FLIP_Y_WEBGL:
         m_unpackFlipYEnabled = (param != 0);
@@ -2384,6 +2519,9 @@ void CanvasContext::hint(glEnums target, glEnums mode)
                                          << "(target:" << glEnumToString(target)
                                          << ",mode:" << glEnumToString(mode) << ")";
 
+    if (checkContextLost())
+        return;
+
     switch (target) {
     case FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
         if (!m_standardDerivatives) {
@@ -2485,6 +2623,9 @@ void CanvasContext::blendColor(float red, float green, float blue, float alpha)
                                           << ", alpha:" << alpha
                                           << ")";
 
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glBlendColor,
                                  GLfloat(red), GLfloat(green),
                                  GLfloat(blue), GLfloat(alpha));
@@ -2492,6 +2633,9 @@ void CanvasContext::blendColor(float red, float green, float blue, float alpha)
 
 bool CanvasContext::checkBlendMode(glEnums mode)
 {
+    if (checkContextLost())
+        return false;
+
     switch (mode) {
     case FUNC_ADD:
     case FUNC_SUBTRACT:
@@ -2574,6 +2718,9 @@ void CanvasContext::blendFunc(glEnums sfactor, glEnums dfactor)
                                          << ", dfactor:" << glEnumToString(dfactor)
                                          << ")";
 
+    if (checkContextLost())
+        return;
+
     if (((sfactor == CONSTANT_COLOR || sfactor == ONE_MINUS_CONSTANT_COLOR)
          && (dfactor == CONSTANT_ALPHA || dfactor == ONE_MINUS_CONSTANT_ALPHA))
             || ((dfactor == CONSTANT_COLOR || dfactor == ONE_MINUS_CONSTANT_COLOR)
@@ -2634,6 +2781,9 @@ void CanvasContext::blendFuncSeparate(glEnums srcRGB, glEnums dstRGB, glEnums sr
                                          << ", dstAlpha:" << glEnumToString(dstAlpha)
                                          << ")";
 
+    if (checkContextLost())
+        return;
+
     if (((srcRGB == CONSTANT_COLOR || srcRGB == ONE_MINUS_CONSTANT_COLOR )
          && (dstRGB == CONSTANT_ALPHA || dstRGB == ONE_MINUS_CONSTANT_ALPHA ))
             || ((dstRGB == CONSTANT_COLOR || dstRGB == ONE_MINUS_CONSTANT_COLOR )
@@ -2667,7 +2817,7 @@ QJSValue CanvasContext::getProgramParameter(QJSValue program3D, glEnums paramNam
 
     CanvasProgram *program = getAsProgram3D(program3D);
 
-    if (!program || !checkParent(program, __FUNCTION__)) {
+    if (!program || !checkValidity(program, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return QJSValue(QJSValue::NullValue);
     }
@@ -2721,19 +2871,26 @@ QJSValue CanvasContext::getProgramParameter(QJSValue program3D, glEnums paramNam
  */
 QJSValue CanvasContext::createShader(glEnums type)
 {
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     switch (type) {
     case VERTEX_SHADER: // Intentional fall through
-    case FRAGMENT_SHADER:
+    case FRAGMENT_SHADER: {
         qCDebug(canvas3drendering).nospace() << "Context3D::createShader("
                                              << glEnumToString(type) << ")";
-        return m_engine->newQObject(new CanvasShader(GLenum(type), m_commandQueue, this));
-    default:
+        CanvasShader *shader = new CanvasShader(GLenum(type), m_commandQueue, this);
+        addObjectToValidList(shader);
+        return m_engine->newQObject(shader);
+    }
+    default: {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                                << ":INVALID_ENUM:unknown shader type:"
                                                << glEnumToString(type);
         m_error |= CANVAS_INVALID_ENUM;
         return QJSValue(QJSValue::NullValue);
     }
+    }
 }
 
 /*!
@@ -2749,7 +2906,7 @@ bool CanvasContext::isShader(QJSValue anyObject)
                                          << ")";
 
     CanvasShader *shader3D = getAsShader3D(anyObject);
-    if (shader3D && checkParent(shader3D, __FUNCTION__)) {
+    if (shader3D && checkValidity(shader3D, __FUNCTION__)) {
         GLboolean boolValue;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glIsShader, shader3D->id());
         syncCommand.returnValue = &boolValue;
@@ -2788,7 +2945,7 @@ void CanvasContext::deleteShader(QJSValue shader3D)
     CanvasShader *shader = getAsShader3D(shader3D, true);
 
     if (shader) {
-        if (!checkParent(shader, __FUNCTION__))
+        if (!checkValidity(shader, __FUNCTION__))
             return;
         shader->del();
     } else {
@@ -2825,7 +2982,7 @@ void CanvasContext::shaderSource(QJSValue shader3D, const QString &shaderSource)
                                                << "Invalid shader handle:" << shader3D.toString();
         return;
     }
-    if (!checkParent(shader, __FUNCTION__))
+    if (!checkValidity(shader, __FUNCTION__))
         return;
 
     shader->setSourceCode(modSource);
@@ -2850,7 +3007,7 @@ QJSValue CanvasContext::getShaderSource(QJSValue shader3D)
                                                << "Invalid shader handle:" << shader3D.toString();
         return QJSValue(QJSValue::NullValue);
     }
-    if (!checkParent(shader, __FUNCTION__))
+    if (!checkValidity(shader, __FUNCTION__))
         return false;
 
     return QJSValue(shader->sourceCode());
@@ -2873,7 +3030,7 @@ void CanvasContext::compileShader(QJSValue shader3D)
                                                << "Invalid shader handle:" << shader3D.toString();
         return;
     }
-    if (!checkParent(shader, __FUNCTION__))
+    if (!checkValidity(shader, __FUNCTION__))
         return;
 
     shader->compileShader();
@@ -3048,6 +3205,9 @@ void CanvasContext::vertexAttribNfv(int dim, unsigned int indx, const QJSValue &
                                                        << ")";
     }
 
+    if (checkContextLost())
+        return;
+
     CanvasGlCommandQueue::GlCommandId id(CanvasGlCommandQueue::internalNoCommand);
     switch (dim) {
     case 1:
@@ -3118,7 +3278,7 @@ void CanvasContext::uniformNf(int dim, const QJSValue &location, float x, float
 
     CanvasUniformLocation *locationObj = getAsUniformLocation3D(location);
 
-    if (!locationObj || !checkParent(locationObj, __FUNCTION__)) {
+    if (!locationObj || !checkValidity(locationObj, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
@@ -3167,7 +3327,7 @@ void CanvasContext::uniformNi(int dim, const QJSValue &location, int x, int y, i
 
     CanvasUniformLocation *locationObj = getAsUniformLocation3D(location);
 
-    if (!locationObj || !checkParent(locationObj, __FUNCTION__)) {
+    if (!locationObj || !checkValidity(locationObj, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
@@ -3228,7 +3388,7 @@ void CanvasContext::uniformNxv(int dim, bool typeFloat, const QJSValue &location
     }
 
     CanvasUniformLocation *locationObj = getAsUniformLocation3D(location);
-    if (!locationObj || !checkParent(locationObj, __FUNCTION__)) {
+    if (!locationObj || !checkValidity(locationObj, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
@@ -3282,25 +3442,27 @@ void CanvasContext::uniformNxv(int dim, bool typeFloat, const QJSValue &location
 bool CanvasContext::isCapabilityValid(CanvasContext::glEnums cap)
 {
     bool capValid = false;
-    switch (GLint(cap)) {
-    case CULL_FACE:
-    case BLEND:
-    case DITHER:
-    case STENCIL_TEST:
-    case DEPTH_TEST:
-    case SCISSOR_TEST:
-    case POLYGON_OFFSET_FILL:
-    case SAMPLE_ALPHA_TO_COVERAGE:
-    case SAMPLE_COVERAGE:
-        capValid = true;
-        break;
-    default:
-        qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
-                                               << ":INVALID_ENUM:"
-                                               << "Tried to enable, disable, or query an invalid capability:"
-                                               << glEnumToString(cap);
-        m_error |= CANVAS_INVALID_ENUM;
-        break;
+    if (!checkContextLost()) {
+        switch (GLint(cap)) {
+        case CULL_FACE:
+        case BLEND:
+        case DITHER:
+        case STENCIL_TEST:
+        case DEPTH_TEST:
+        case SCISSOR_TEST:
+        case POLYGON_OFFSET_FILL:
+        case SAMPLE_ALPHA_TO_COVERAGE:
+        case SAMPLE_COVERAGE:
+            capValid = true;
+            break;
+        default:
+            qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
+                                                   << ":INVALID_ENUM:"
+                                                   << "Tried to enable, disable, or query an invalid capability:"
+                                                   << glEnumToString(cap);
+            m_error |= CANVAS_INVALID_ENUM;
+            break;
+        }
     }
     return capValid;
 }
@@ -3316,6 +3478,8 @@ void CanvasContext::vertexAttrib1f(unsigned int indx, float x)
                                          << "(indx:" << indx
                                          << ", x:" << x
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glVertexAttrib1f, GLint(indx), GLfloat(x));
 }
@@ -3342,6 +3506,8 @@ void CanvasContext::vertexAttrib2f(unsigned int indx, float x, float y)
                                          << ", x:" << x
                                          << ", y:" << y
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glVertexAttrib2f,
                                  GLint(indx), GLfloat(x), GLfloat(y));
@@ -3370,6 +3536,8 @@ void CanvasContext::vertexAttrib3f(unsigned int indx, float x, float y, float z)
                                          << ", y:" << y
                                          << ", z:" << z
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glVertexAttrib3f,
                                  GLint(indx), GLfloat(x), GLfloat(y), GLfloat(z));
@@ -3399,6 +3567,8 @@ void CanvasContext::vertexAttrib4f(unsigned int indx, float x, float y, float z,
                                          << ", z:" << z
                                          << ", w:" << w
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glVertexAttrib4f,
                                  GLint(indx), GLfloat(x), GLfloat(y), GLfloat(z), GLfloat(w));
@@ -3435,7 +3605,7 @@ QJSValue CanvasContext::getShaderParameter(QJSValue shader3D, glEnums pname)
         m_error |= CANVAS_INVALID_OPERATION;
         return QJSValue(QJSValue::NullValue);
     }
-    if (!checkParent(shader, __FUNCTION__))
+    if (!checkValidity(shader, __FUNCTION__))
         return QJSValue(QJSValue::NullValue);
 
     GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetShaderiv,
@@ -3480,12 +3650,16 @@ QJSValue CanvasContext::getShaderParameter(QJSValue shader3D, glEnums pname)
  */
 QJSValue CanvasContext::createBuffer()
 {
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     CanvasBuffer *newBuffer = new CanvasBuffer(m_commandQueue, this);
     m_idToCanvasBufferMap.insert(newBuffer->id(), newBuffer);
 
     QJSValue value = m_engine->newQObject(newBuffer);
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << ":" << value.toString() << " = " << newBuffer;
+    addObjectToValidList(newBuffer);
     return value;
 }
 
@@ -3508,7 +3682,7 @@ QJSValue CanvasContext::getUniformLocation(QJSValue program3D, const QString &na
         m_error |= CANVAS_INVALID_OPERATION;
         return QJSValue(QJSValue::NullValue);
     }
-    if (!checkParent(program, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__))
         return QJSValue(QJSValue::NullValue);
 
     CanvasUniformLocation *location3D = new CanvasUniformLocation(m_commandQueue, this);
@@ -3518,6 +3692,7 @@ QJSValue CanvasContext::getUniformLocation(QJSValue program3D, const QString &na
                                          << "(program3D:" << program3D.toString()
                                          << ", name:" << value.toString()
                                          << "):" << location3D;
+    addObjectToValidList(location3D);
 
     GlCommand &command = m_commandQueue->queueCommand(CanvasGlCommandQueue::glGetUniformLocation,
                                                       location3D->id(), program->id());
@@ -3534,6 +3709,9 @@ QJSValue CanvasContext::getUniformLocation(QJSValue program3D, const QString &na
  */
 int CanvasContext::getAttribLocation(QJSValue program3D, const QString &name)
 {
+    if (checkContextLost())
+        return -1;
+
     CanvasProgram *program = getAsProgram3D(program3D);
     if (!program) {
         qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
@@ -3545,7 +3723,7 @@ int CanvasContext::getAttribLocation(QJSValue program3D, const QString &name)
         m_error |= CANVAS_INVALID_OPERATION;
         return -1;
     } else {
-        if (!checkParent(program, __FUNCTION__))
+        if (!checkValidity(program, __FUNCTION__))
             return -1;
         GLint attribLoc = -1;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetAttribLocation, program->id());
@@ -3584,7 +3762,7 @@ void CanvasContext::bindAttribLocation(QJSValue program3D, int index, const QStr
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
-    if (!checkParent(program, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__))
         return;
 
     program->bindAttributeLocation(index, name);
@@ -3599,6 +3777,8 @@ void CanvasContext::enableVertexAttribArray(int index)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(index:" << index
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glEnableVertexAttribArray, GLint(index));
 }
@@ -3612,6 +3792,8 @@ void CanvasContext::disableVertexAttribArray(int index)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(index:" << index
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glDisableVertexAttribArray, GLint(index));
 }
@@ -3673,6 +3855,8 @@ void CanvasContext::vertexAttribPointer(int indx, int size, glEnums type,
                                          << ", stride:" << stride
                                          << ", offset:" << offset
                                          << ")";
+    if (checkContextLost())
+        return;
 
     if (!m_currentArrayBuffer) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
@@ -3966,7 +4150,7 @@ bool CanvasContext::isBuffer(QJSValue anyObject)
                                          << ")";
 
     CanvasBuffer *buffer = getAsBuffer3D(anyObject);
-    if (buffer && checkParent(buffer, __FUNCTION__)) {
+    if (buffer && checkValidity(buffer, __FUNCTION__)) {
         GLboolean boolValue;
         GlSyncCommand syncCommand(CanvasGlCommandQueue::glIsBuffer, buffer->id());
         syncCommand.returnValue = &boolValue;
@@ -4007,7 +4191,7 @@ void CanvasContext::deleteBuffer(QJSValue buffer3D)
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
-    if (!checkParent(bufferObj, __FUNCTION__))
+    if (!checkValidity(bufferObj, __FUNCTION__))
         return;
 
     m_idToCanvasBufferMap.remove(bufferObj->id());
@@ -4023,33 +4207,40 @@ CanvasContext::glEnums CanvasContext::getError()
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__;
 
-    // Fetch GL errors synchronously
-    GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetError);
-    int canvasError = CANVAS_NO_ERRORS;
-    syncCommand.returnValue = &canvasError;
-    scheduleSyncCommand(&syncCommand);
-
-    m_error |= canvasError;
-
     glEnums retVal = NO_ERROR;
-    if (m_error != CANVAS_NO_ERRORS) {
-        // Return set error flags one by one and clear the flags.
-        // Note that stack overflow/underflow flags are never returned.
-        if ((m_error & CANVAS_INVALID_ENUM) != 0) {
-            retVal = INVALID_ENUM;
-            m_error &= ~(CANVAS_INVALID_ENUM);
-        } else if ((m_error & CANVAS_INVALID_VALUE) != 0) {
-            retVal = INVALID_VALUE;
-            m_error &= ~(CANVAS_INVALID_VALUE);
-        }else if ((m_error & CANVAS_INVALID_OPERATION) != 0) {
-            retVal = INVALID_OPERATION;
-            m_error &= ~(CANVAS_INVALID_OPERATION);
-        } else if ((m_error & CANVAS_OUT_OF_MEMORY) != 0) {
-            retVal = OUT_OF_MEMORY;
-            m_error &= ~(CANVAS_OUT_OF_MEMORY);
-        } else if ((m_error & CANVAS_INVALID_FRAMEBUFFER_OPERATION) != 0) {
-            retVal = INVALID_FRAMEBUFFER_OPERATION;
-            m_error &= ~(CANVAS_INVALID_FRAMEBUFFER_OPERATION);
+    if (m_contextLost) {
+        if (!m_contextLostErrorReported) {
+            m_contextLostErrorReported = true;
+            retVal = CONTEXT_LOST_WEBGL;
+        }
+    } else {
+        // Fetch GL errors synchronously
+        GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetError);
+        int canvasError = CANVAS_NO_ERRORS;
+        syncCommand.returnValue = &canvasError;
+        scheduleSyncCommand(&syncCommand);
+
+        m_error |= canvasError;
+
+        if (m_error != CANVAS_NO_ERRORS) {
+            // Return set error flags one by one and clear the flags.
+            // Note that stack overflow/underflow flags are never returned.
+            if ((m_error & CANVAS_INVALID_ENUM) != 0) {
+                retVal = INVALID_ENUM;
+                m_error &= ~(CANVAS_INVALID_ENUM);
+            } else if ((m_error & CANVAS_INVALID_VALUE) != 0) {
+                retVal = INVALID_VALUE;
+                m_error &= ~(CANVAS_INVALID_VALUE);
+            }else if ((m_error & CANVAS_INVALID_OPERATION) != 0) {
+                retVal = INVALID_OPERATION;
+                m_error &= ~(CANVAS_INVALID_OPERATION);
+            } else if ((m_error & CANVAS_OUT_OF_MEMORY) != 0) {
+                retVal = OUT_OF_MEMORY;
+                m_error &= ~(CANVAS_OUT_OF_MEMORY);
+            } else if ((m_error & CANVAS_INVALID_FRAMEBUFFER_OPERATION) != 0) {
+                retVal = INVALID_FRAMEBUFFER_OPERATION;
+                m_error &= ~(CANVAS_INVALID_FRAMEBUFFER_OPERATION);
+            }
         }
     }
 
@@ -4066,6 +4257,8 @@ QJSValue CanvasContext::getParameter(glEnums pname)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "( pname:" << glEnumToString(pname)
                                          << ")";
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
 
     GLint value = 0;
     GlSyncCommand syncCommand(CanvasGlCommandQueue::glGetIntegerv, GLint(pname));
@@ -4390,7 +4583,7 @@ QJSValue CanvasContext::getShaderInfoLog(QJSValue shader3D)
         m_error |= CANVAS_INVALID_OPERATION;
         return QJSValue(QJSValue::NullValue);
     }
-    if (!checkParent(shader, __FUNCTION__))
+    if (!checkValidity(shader, __FUNCTION__))
         return QJSValue(QJSValue::NullValue);
 
     QString log;
@@ -4423,7 +4616,7 @@ QJSValue CanvasContext::getProgramInfoLog(QJSValue program3D)
         m_error |= CANVAS_INVALID_OPERATION;
         return QJSValue(QJSValue::NullValue);
     }
-    if (!checkParent(program, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__))
         return QJSValue(QJSValue::NullValue);
 
     QString log;
@@ -4459,7 +4652,7 @@ void CanvasContext::bindBuffer(glEnums target, QJSValue buffer3D)
     }
 
     CanvasBuffer *buffer = getAsBuffer3D(buffer3D);
-    if (buffer && checkParent(buffer, __FUNCTION__)) {
+    if (buffer && checkValidity(buffer, __FUNCTION__)) {
         if (target == ARRAY_BUFFER) {
             if (buffer->target() == CanvasBuffer::UNINITIALIZED)
                 buffer->setTarget(CanvasBuffer::ARRAY_BUFFER);
@@ -4511,7 +4704,7 @@ void CanvasContext::validateProgram(QJSValue program3D)
         m_error |= CANVAS_INVALID_OPERATION;
         return;
     }
-    if (checkParent(program, __FUNCTION__))
+    if (checkValidity(program, __FUNCTION__))
         program->validateProgram();
 }
 
@@ -4532,7 +4725,7 @@ void CanvasContext::useProgram(QJSValue program3D)
         return;
     }
 
-    if (!checkParent(program, __FUNCTION__))
+    if (!checkValidity(program, __FUNCTION__))
         return;
     program->useProgram();
 }
@@ -4558,6 +4751,9 @@ void CanvasContext::clear(glEnums flags)
                                              << "(flags:" << flagStr << ")";
     }
 
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glClear, GLint(flags));
 
     // Set clear flags if the clear targets default framebuffer
@@ -4574,6 +4770,9 @@ void CanvasContext::cullFace(glEnums mode)
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(mode:" << glEnumToString(mode) << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glCullFace, GLint(mode));
 }
 
@@ -4586,6 +4785,9 @@ void CanvasContext::frontFace(glEnums mode)
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(mode:" << glEnumToString(mode) << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glFrontFace, GLint(mode));
 }
 
@@ -4597,6 +4799,9 @@ void CanvasContext::depthMask(bool flag)
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(flag:" << flag << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glDepthMask, GLint(flag));
 }
 
@@ -4610,6 +4815,9 @@ void CanvasContext::depthFunc(glEnums func)
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(func:" << glEnumToString(func) << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glDepthFunc, GLint(func));
 }
 
@@ -4623,6 +4831,8 @@ void CanvasContext::depthRange(float zNear, float zFar)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(zNear:" << zNear
                                          << ", zFar:" << zFar <<  ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glDepthRangef,
                                  GLfloat(zNear), GLfloat(zFar));
@@ -4636,6 +4846,9 @@ void CanvasContext::clearStencil(int stencil)
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(stencil:" << stencil << ")";
+    if (checkContextLost())
+        return;
+
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glClearStencil, GLint(stencil));
 }
 
@@ -4651,6 +4864,8 @@ void CanvasContext::colorMask(bool maskRed, bool maskGreen, bool maskBlue, bool
                                          << ", maskGreen:" << maskGreen
                                          << ", maskBlue:" << maskBlue
                                          << ", maskAlpha:" << maskAlpha  <<  ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glColorMask,
                                  GLint(maskRed), GLint(maskGreen),
@@ -4666,6 +4881,8 @@ void CanvasContext::clearDepth(float depth)
 {
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(depth:" << depth << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glClearDepthf, GLfloat(depth));
 }
@@ -4682,6 +4899,8 @@ void CanvasContext::clearColor(float red, float green, float blue, float alpha)
                                           << ", green:" << green
                                           << ", blue:" << blue
                                           << ", alpha:" << alpha << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glClearColor,
                                  GLfloat(red), GLfloat(green),
@@ -4704,6 +4923,8 @@ void CanvasContext::viewport(int x, int y, int width, int height)
                                           << ", y:" << y
                                           << ", width:" << width
                                           << ", height:" << height << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glViewport, GLint(x), GLint(y),
                                  GLint(width), GLint(height));
@@ -4729,6 +4950,8 @@ void CanvasContext::drawArrays(glEnums mode, int first, int count)
                                          << "(mode:" << glEnumToString(mode)
                                          << ", first:" << first
                                          << ", count:" << count << ")";
+    if (checkContextLost())
+        return;
 
     if (first < 0) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
@@ -4829,6 +5052,9 @@ void CanvasContext::drawElements(glEnums mode, int count, glEnums type, long off
 void CanvasContext::readPixels(int x, int y, long width, long height, glEnums format, glEnums type,
                                QJSValue pixels)
 {
+    if (checkContextLost())
+        return;
+
     if (format != RGBA) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                                << ":INVALID_ENUM:format must be RGBA.";
@@ -4881,7 +5107,7 @@ CanvasActiveInfo *CanvasContext::getActiveAttrib(QJSValue program3D, uint index)
                                          << ", index:" << index << ")";
 
     CanvasProgram *program = getAsProgram3D(program3D);
-    if (!program || !checkParent(program, __FUNCTION__)) {
+    if (!program || !checkValidity(program, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return 0;
     }
@@ -4916,7 +5142,7 @@ CanvasActiveInfo *CanvasContext::getActiveUniform(QJSValue program3D, uint index
                                          << ", index:" << index << ")";
 
     CanvasProgram *program = getAsProgram3D(program3D);
-    if (!program || !checkParent(program, __FUNCTION__)) {
+    if (!program || !checkValidity(program, __FUNCTION__)) {
         m_error |= CANVAS_INVALID_OPERATION;
         return 0;
     }
@@ -4960,6 +5186,9 @@ void CanvasContext::stencilFunc(glEnums func, int ref, uint mask)
                                          << ", ref:" << ref
                                          << ", mask:" << mask
                                          << ")";
+    if (checkContextLost())
+        return;
+
     // Clamp ref
     if (ref < 0)
         ref = 0;
@@ -4998,6 +5227,9 @@ void CanvasContext::stencilFuncSeparate(glEnums face, glEnums func, int ref, uin
                                          << ", ref:" << ref
                                          << ", mask:" << mask
                                          << ")";
+    if (checkContextLost())
+        return;
+
     // Clamp ref
     if (ref < 0)
         ref = 0;
@@ -5016,6 +5248,8 @@ void CanvasContext::stencilMask(uint mask)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(mask:" << mask
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glStencilMask, GLint(mask));
 }
@@ -5037,6 +5271,8 @@ void CanvasContext::stencilMaskSeparate(glEnums face, uint mask)
                                          << "(face:" <<  glEnumToString(face)
                                          << ", mask:" << mask
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glStencilMaskSeparate,
                                  GLint(face), GLint(mask));
@@ -5065,6 +5301,8 @@ void CanvasContext::stencilOp(glEnums sfail, glEnums zfail, glEnums zpass)
                                          << ", zfail:" <<  glEnumToString(zfail)
                                          << ", zpass:" << glEnumToString(zpass)
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glStencilOp,
                                  GLint(sfail), GLint(zfail), GLint(zpass));
@@ -5100,6 +5338,8 @@ void CanvasContext::stencilOpSeparate(glEnums face, glEnums fail, glEnums zfail,
                                          << ", zfail:" <<  glEnumToString(zfail)
                                          << ", zpass:" << glEnumToString(zpass)
                                          << ")";
+    if (checkContextLost())
+        return;
 
     m_commandQueue->queueCommand(CanvasGlCommandQueue::glStencilOpSeparate,
                                  GLint(face), GLint(fail), GLint(zfail), GLint(zpass));
@@ -5119,6 +5359,9 @@ QJSValue CanvasContext::getFramebufferAttachmentParameter(glEnums target, glEnum
                                          << ", attachment:" << glEnumToString(attachment)
                                          << ", pname:" << glEnumToString(pname)
                                          << ")";
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     if (target != FRAMEBUFFER) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                                << ":INVALID_ENUM:"
@@ -5193,6 +5436,8 @@ QJSValue CanvasContext::getRenderbufferParameter(glEnums target, glEnums pname)
                                          << "(target" << glEnumToString(target)
                                          << ", pname:" << glEnumToString(pname)
                                          << ")";
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
 
     if (target != RENDERBUFFER) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
@@ -5251,6 +5496,8 @@ QJSValue CanvasContext::getTexParameter(glEnums target, glEnums pname)
                                          << "(target" << glEnumToString(target)
                                          << ", pname:" << glEnumToString(pname)
                                          << ")";
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
 
     GLint parameter = 0;
     if (isValidTextureBound(target, __FUNCTION__, false)) {
@@ -5302,6 +5549,8 @@ uint CanvasContext::getVertexAttribOffset(uint index, glEnums pname)
                                          << "(index" << index
                                          << ", pname:" << glEnumToString(pname)
                                          << ")";
+    if (checkContextLost())
+        return 0;
 
     if (pname != VERTEX_ATTRIB_ARRAY_POINTER) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
@@ -5366,6 +5615,8 @@ QJSValue CanvasContext::getVertexAttrib(uint index, glEnums pname)
                                          << "(index" << index
                                          << ", pname:" << glEnumToString(pname)
                                          << ")";
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
 
     if (index >= MAX_VERTEX_ATTRIBS) {
         qCWarning(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
@@ -5439,10 +5690,15 @@ QJSValue CanvasContext::getVertexAttrib(uint index, glEnums pname)
  */
 QJSValue CanvasContext::createTextureFromSource(QQuickItem *item)
 {
+    if (checkContextLost())
+        return QJSValue(QJSValue::NullValue);
+
     // First check if we have a CanvasTexture already for this item
     CanvasTexture *texture = m_quickItemToTextureMap.value(item, 0);
-    if (!texture)
+    if (!texture) {
         texture = new CanvasTexture(m_commandQueue, this, item);
+        addObjectToValidList(texture);
+    }
 
     m_quickItemToTextureMap.insert(item, texture);
 
@@ -5547,7 +5803,7 @@ QJSValue CanvasContext::getUniform(QJSValue program3D, QJSValue location3D)
                                                << ":INVALID_OPERATION:No location3D was specified";
         m_error |= CANVAS_INVALID_OPERATION;
         return QJSValue(QJSValue::UndefinedValue);
-    } else if (!checkParent(program, __FUNCTION__) || !checkParent(location, __FUNCTION__)) {
+    } else if (!checkValidity(program, __FUNCTION__) || !checkValidity(location, __FUNCTION__)) {
         return QJSValue(QJSValue::UndefinedValue);
     }
 
@@ -5711,19 +5967,22 @@ QVariantList CanvasContext::getSupportedExtensions()
     qCDebug(canvas3drendering).nospace() << Q_FUNC_INFO;
 
     QVariantList list;
-    list.append(QVariant::fromValue(QStringLiteral("QTCANVAS3D_gl_state_dump")));
 
-    if (!m_isOpenGLES2 ||
-            (m_contextVersion >= 3
-             || m_extensions.contains("OES_standard_derivatives"))) {
-        list.append(QVariant::fromValue(QStringLiteral("OES_standard_derivatives")));
-    }
+    if (!checkContextLost()) {
+        list.append(QVariant::fromValue(QStringLiteral("QTCANVAS3D_gl_state_dump")));
+
+        if (!m_isOpenGLES2 ||
+                (m_contextVersion >= 3
+                 || m_extensions.contains("OES_standard_derivatives"))) {
+            list.append(QVariant::fromValue(QStringLiteral("OES_standard_derivatives")));
+        }
 
-    if (m_extensions.contains("GL_EXT_texture_compression_s3tc"))
-        list.append(QVariant::fromValue(QStringLiteral("WEBGL_compressed_texture_s3tc")));
+        if (m_extensions.contains("GL_EXT_texture_compression_s3tc"))
+            list.append(QVariant::fromValue(QStringLiteral("WEBGL_compressed_texture_s3tc")));
 
-    if (m_extensions.contains("IMG_texture_compression_pvrtc"))
-        list.append(QVariant::fromValue(QStringLiteral("WEBGL_compressed_texture_pvrtc")));
+        if (m_extensions.contains("IMG_texture_compression_pvrtc"))
+            list.append(QVariant::fromValue(QStringLiteral("WEBGL_compressed_texture_pvrtc")));
+    }
 
     return list;
 }
@@ -5811,36 +6070,97 @@ QVariant CanvasContext::getExtension(const QString &name)
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "(name:" << name
                                          << ")";
-
-    QString upperCaseName = name.toUpper();
-
-    if (upperCaseName == QStringLiteral("QTCANVAS3D_GL_STATE_DUMP")) {
-        if (!m_stateDumpExt)
-            m_stateDumpExt = new CanvasGLStateDump(this, m_isOpenGLES2, this);
-        return QVariant::fromValue(m_stateDumpExt);
-    } else if (upperCaseName == QStringLiteral("QTCANVAS3D_TEXTURE_PROVIDER")) {
-        if (!m_textureProviderExt)
-            m_textureProviderExt = new CanvasTextureProvider(this, this);
-        return QVariant::fromValue(m_textureProviderExt);
-    } else if (upperCaseName == QStringLiteral("OES_STANDARD_DERIVATIVES") &&
-               m_extensions.contains("OES_standard_derivatives")) {
-        if (!m_standardDerivatives)
-            m_standardDerivatives = new QObject(this);
-        return QVariant::fromValue(m_standardDerivatives);
-    } else if (upperCaseName == QStringLiteral("WEBGL_COMPRESSED_TEXTURE_S3TC") &&
-               m_extensions.contains("GL_EXT_texture_compression_s3tc")) {
-        if (!m_compressedTextureS3TC)
-            m_compressedTextureS3TC = new CompressedTextureS3TC(this);
-        return QVariant::fromValue(m_compressedTextureS3TC);
-    } else if (upperCaseName == QStringLiteral("WEBGL_COMPRESSED_TEXTURE_PVRTC") &&
-               m_extensions.contains("IMG_texture_compression_pvrtc")) {
-        if (!m_compressedTexturePVRTC)
-            m_compressedTexturePVRTC = new CompressedTexturePVRTC(this);
-        return QVariant::fromValue(m_compressedTexturePVRTC);
+    if (!checkContextLost()) {
+        QString upperCaseName = name.toUpper();
+
+        if (upperCaseName == QStringLiteral("QTCANVAS3D_GL_STATE_DUMP")) {
+            if (!m_stateDumpExt)
+                m_stateDumpExt = new CanvasGLStateDump(this, m_isOpenGLES2, this);
+            return QVariant::fromValue(m_stateDumpExt);
+        } else if (upperCaseName == QStringLiteral("QTCANVAS3D_TEXTURE_PROVIDER")) {
+            if (!m_textureProviderExt)
+                m_textureProviderExt = new CanvasTextureProvider(this, this);
+            return QVariant::fromValue(m_textureProviderExt);
+        } else if (upperCaseName == QStringLiteral("OES_STANDARD_DERIVATIVES") &&
+                   m_extensions.contains("OES_standard_derivatives")) {
+            if (!m_standardDerivatives)
+                m_standardDerivatives = new QObject(this);
+            return QVariant::fromValue(m_standardDerivatives);
+        } else if (upperCaseName == QStringLiteral("WEBGL_COMPRESSED_TEXTURE_S3TC") &&
+                   m_extensions.contains("GL_EXT_texture_compression_s3tc")) {
+            if (!m_compressedTextureS3TC)
+                m_compressedTextureS3TC = new CompressedTextureS3TC(this);
+            return QVariant::fromValue(m_compressedTextureS3TC);
+        } else if (upperCaseName == QStringLiteral("WEBGL_COMPRESSED_TEXTURE_PVRTC") &&
+                   m_extensions.contains("IMG_texture_compression_pvrtc")) {
+            if (!m_compressedTexturePVRTC)
+                m_compressedTexturePVRTC = new CompressedTexturePVRTC(this);
+            return QVariant::fromValue(m_compressedTexturePVRTC);
+        }
     }
 
     return QVariant(QVariant::Int);
 }
 
+void CanvasContext::setContextLostState(bool lost)
+{
+    if (lost != m_contextLost) {
+        m_contextLost = lost;
+
+        // Clear errors on lost state change
+        m_error = CANVAS_NO_ERRORS;
+
+        if (lost) {
+            foreach (CanvasAbstractObject *jsObj, m_validObjectMap.keys()) {
+                jsObj->setInvalidated(true);
+                disconnect(jsObj, &QObject::destroyed, this, &CanvasContext::handleObjectDeletion);
+            }
+            m_validObjectMap.clear();
+            m_quickItemToTextureMap.clear();
+            m_idToCanvasBufferMap.clear();
+
+            m_currentProgram = 0;
+            m_currentArrayBuffer = 0;
+            m_currentElementArrayBuffer = 0;
+            m_currentTexture2D = 0;
+            m_currentTextureCubeMap = 0;
+            m_currentFramebuffer = 0;
+            m_currentRenderbuffer = 0;
+            m_contextLostErrorReported = false;
+        }
+    }
+}
+
+void CanvasContext::setCommandQueue(CanvasGlCommandQueue *queue)
+{
+    m_commandQueue = queue;
+    connect(m_commandQueue, &CanvasGlCommandQueue::queueFull,
+            this, &CanvasContext::handleFullCommandQueue, Qt::DirectConnection);
+}
+
+void CanvasContext::markQuickTexturesDirty()
+{
+    if (m_quickItemToTextureMap.size()) {
+        QMap<QQuickItem *, CanvasTexture *>::iterator i = m_quickItemToTextureMap.begin();
+        while (i != m_quickItemToTextureMap.end()) {
+            m_commandQueue->addQuickItemAsTexture(i.key(), i.value()->textureId());
+            i++;
+        }
+    }
+}
+
+void CanvasContext::handleObjectDeletion(QObject *obj)
+{
+    CanvasAbstractObject *jsObj = qobject_cast<CanvasAbstractObject *>(obj);
+    if (jsObj)
+        m_validObjectMap.remove(jsObj);
+}
+
+void CanvasContext::addObjectToValidList(CanvasAbstractObject *jsObj)
+{
+    m_validObjectMap.insert(jsObj, 0);
+    connect(jsObj, &QObject::destroyed, this, &CanvasContext::handleObjectDeletion);
+}
+
 QT_CANVAS3D_END_NAMESPACE
 QT_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/context3d_p.h b/src/imports/qtcanvas3d/context3d_p.h
index ec3b4bcd87671d0d5755a9771348bfd0285f7fa4..165ec68271ea799aa0fb2a247bef11f187ab5d12 100644
--- a/src/imports/qtcanvas3d/context3d_p.h
+++ b/src/imports/qtcanvas3d/context3d_p.h
@@ -973,8 +973,9 @@ public:
     ENUM_AS_PROPERTY(UNPACK_COLORSPACE_CONVERSION_WEBGL)
     ENUM_AS_PROPERTY(BROWSER_DEFAULT_WEBGL)
 
-    CanvasContext(QQmlEngine *engine, bool isES2, int maxVertexAttribs, int contextVersion, const QSet<QByteArray> &extensions,
-                  CanvasGlCommandQueue *commandQueue, QObject *parent = 0);
+    CanvasContext(QQmlEngine *engine, bool isES2, int maxVertexAttribs, int contextVersion,
+                  const QSet<QByteArray> &extensions, CanvasGlCommandQueue *commandQueue,
+                  bool isCombinedDepthStencilSupported, QObject *parent = 0);
     ~CanvasContext();
 
     void setCanvas(Canvas *canvas);
@@ -1193,15 +1194,22 @@ public:
 
     void scheduleSyncCommand(GlSyncCommand *command);
 
-public slots:
-    void handleFullCommandQueue();
-    void handleTextureIdResolved(QQuickItem *item);
+    void setContextLostState(bool lost);
+
+    CanvasGlCommandQueue *commandQueue() { return m_commandQueue; }
+    void setCommandQueue(CanvasGlCommandQueue *queue);
+    void markQuickTexturesDirty();
 
 signals:
     void canvasChanged(Canvas *canvas);
     void drawingBufferWidthChanged();
     void drawingBufferHeightChanged();
 
+private slots:
+    void handleFullCommandQueue();
+    void handleTextureIdResolved(QQuickItem *item);
+    void handleObjectDeletion(QObject *obj);
+
 private:
     uchar *getTypedArrayAsRawDataPtr(const QJSValue &jsValue, int &byteLength,
                                      QV4::Heap::TypedArray::Type type);
@@ -1230,7 +1238,7 @@ private:
     bool isOfType(const QJSValue &value, const char *classname) const;
 
     bool isValidTextureBound(glEnums target, const QString &funcName, bool singleLayer = true);
-    bool checkParent(QObject *jsObj, const char *function);
+    bool checkValidity(CanvasAbstractObject *jsObj, const char *function);
 
     float *transposeMatrix(int dim, int count, float *src);
     void uniformMatrixNfv(int dim, const QJSValue &location3D, bool transpose,
@@ -1254,6 +1262,9 @@ private:
     bool checkBufferUsage(glEnums usage);
     bool checkTextureFormats(glEnums internalFormat, glEnums format);
     bool checkTextureTarget(glEnums target);
+    bool checkContextLost();
+
+    void addObjectToValidList(CanvasAbstractObject *jsObj);
 
     typedef enum {
         CANVAS_NO_ERRORS = 0,
@@ -1290,19 +1301,14 @@ private:
     int m_contextVersion;
     float **m_vertexAttribPointers;
     bool m_isOpenGLES2;
+    bool m_isCombinedDepthStencilSupported;
     CanvasGlCommandQueue *m_commandQueue; // Not owned
     QMutex m_renderJobMutex;
     QWaitCondition m_renderJobCondition;
     QMap<QQuickItem *, CanvasTexture *> m_quickItemToTextureMap;
-
-    bool invalidEnumFlag;
-    bool invalidValueFlag;
-    bool invalidOperationFlag;
-    bool invalidStackOverflowFlag;
-    bool invalidStackUnderflowFlag;
-    bool invalidOutOfMemoryFlag;
-    bool invalidFramebufferFlag;
-    bool invalidContextLostFlag;
+    bool m_contextLost;
+    QMap<CanvasAbstractObject *, int> m_validObjectMap;
+    bool m_contextLostErrorReported;
 
     // EXTENSIONS
     CanvasGLStateDump *m_stateDumpExt;
diff --git a/src/imports/qtcanvas3d/doc/qtcanvas3d.qdocconf b/src/imports/qtcanvas3d/doc/qtcanvas3d.qdocconf
index 20533f7378bbae9cf942f86cd3d49d47eb9adc4b..be8e805f06c5a935f83f42d7f52c4f590db5ddc7 100644
--- a/src/imports/qtcanvas3d/doc/qtcanvas3d.qdocconf
+++ b/src/imports/qtcanvas3d/doc/qtcanvas3d.qdocconf
@@ -11,7 +11,7 @@ imagedirs  += images
 headerdirs += ..
 sourcedirs += ..
 
-examplesinstallpath = canvas3d
+examplesinstallpath = qtcanvas3d/canvas3d
 
 depends     = qtcore qtgui qtqml qtquick qtquickcontrols qtquicklayouts qtdoc qtmultimedia
 
diff --git a/src/imports/qtcanvas3d/framebuffer3d.cpp b/src/imports/qtcanvas3d/framebuffer3d.cpp
index e89d998dbc92af8002b4a28d35dc8069d6e7c4e4..90e11b3253843a81ab5562da128ed7412e3e542e 100644
--- a/src/imports/qtcanvas3d/framebuffer3d.cpp
+++ b/src/imports/qtcanvas3d/framebuffer3d.cpp
@@ -43,6 +43,7 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DFrameBuffer
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains an OpenGL framebuffer.
  *
  * An uncreatable QML type that contains an OpenGL framebuffer object. You can get it by calling the
@@ -54,9 +55,7 @@ CanvasFrameBuffer::CanvasFrameBuffer(CanvasGlCommandQueue *queue, QObject *paren
     m_framebufferId(queue->createResourceId()),
     m_texture(0)
 {
-    Q_ASSERT(m_commandQueue);
-
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glGenFramebuffers, m_framebufferId);
+    queueCommand(CanvasGlCommandQueue::glGenFramebuffers, m_framebufferId);
 }
 
 CanvasFrameBuffer::~CanvasFrameBuffer()
@@ -72,7 +71,7 @@ bool CanvasFrameBuffer::isAlive()
 void CanvasFrameBuffer::del()
 {
     if (m_framebufferId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteFramebuffers, m_framebufferId);
+        queueCommand(CanvasGlCommandQueue::glDeleteFramebuffers, m_framebufferId);
         m_framebufferId = 0;
     }
 }
diff --git a/src/imports/qtcanvas3d/plugins.qmltypes b/src/imports/qtcanvas3d/plugins.qmltypes
index 4d0f3a8c4e9cc647a4497713f3892237109e6c30..cc04aafd8d582715db5f40479021a1d4b9fadd43 100644
--- a/src/imports/qtcanvas3d/plugins.qmltypes
+++ b/src/imports/qtcanvas3d/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
 // It is used for QML tooling purposes only.
 //
 // This file was auto-generated by:
-// 'qmlplugindump.exe -nonrelocatable QtCanvas3D 1.1'
+// 'qmlplugindump -nonrelocatable QtCanvas3D 1.1'
 
 Module {
     dependencies: []
@@ -201,6 +201,8 @@ Module {
             name: "pixelSizeChanged"
             Parameter { name: "pixelSize"; type: "QSize" }
         }
+        Signal { name: "contextLost" }
+        Signal { name: "contextRestored" }
         Signal { name: "initializeGL" }
         Signal { name: "paintGL" }
         Signal {
@@ -215,15 +217,7 @@ Module {
             Parameter { name: "size"; type: "QSize" }
             Parameter { name: "devicePixelRatio"; type: "float" }
         }
-        Method { name: "queueNextRender" }
-        Method { name: "queueResizeGL" }
         Method { name: "requestRender" }
-        Method { name: "emitNeedRender" }
-        Method { name: "handleBeforeSynchronizing" }
-        Method {
-            name: "handleRendererFpsChange"
-            Parameter { name: "fps"; type: "uint" }
-        }
         Method { name: "frameTimeMs"; type: "int" }
         Method { name: "frameSetupTimeMs"; revision: 1; type: "int" }
         Method {
@@ -242,10 +236,15 @@ Module {
         name: "QtCanvas3D::CanvasAbstractObject"
         prototype: "QObject"
         Property { name: "name"; type: "string" }
+        Property { name: "invalidated"; type: "bool"; isReadonly: true }
         Signal {
             name: "nameChanged"
             Parameter { name: "name"; type: "string" }
         }
+        Signal {
+            name: "invalidatedChanged"
+            Parameter { name: "invalidated"; type: "bool" }
+        }
     }
     Component {
         name: "QtCanvas3D::CanvasActiveInfo"
@@ -1465,11 +1464,6 @@ Module {
             name: "canvasChanged"
             Parameter { name: "canvas"; type: "Canvas"; isPointer: true }
         }
-        Method { name: "handleFullCommandQueue" }
-        Method {
-            name: "handleTextureIdResolved"
-            Parameter { name: "item"; type: "QQuickItem"; isPointer: true }
-        }
         Method { name: "getSupportedExtensions"; type: "QVariantList" }
         Method {
             name: "getExtension"
diff --git a/src/imports/qtcanvas3d/program3d.cpp b/src/imports/qtcanvas3d/program3d.cpp
index 0249f9f14795f07595f4f982ebaaf866f3d3758c..a4cd12ea25f5a6e9c315e39028108309ae2de07f 100644
--- a/src/imports/qtcanvas3d/program3d.cpp
+++ b/src/imports/qtcanvas3d/program3d.cpp
@@ -43,6 +43,7 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DProgram
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains a shader program.
  *
  * An uncreatable QML type that contains a compiled shader program. You can get it by calling
@@ -54,9 +55,7 @@ CanvasProgram::CanvasProgram(CanvasGlCommandQueue *queue, QObject *parent) :
     m_programId(queue->createResourceId()),
     m_linked(false)
 {
-    Q_ASSERT(m_commandQueue);
-
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glCreateProgram, m_programId);
+    queueCommand(CanvasGlCommandQueue::glCreateProgram, m_programId);
 }
 
 CanvasProgram::~CanvasProgram()
@@ -74,8 +73,7 @@ void CanvasProgram::attach(CanvasShader *shader)
     if (m_programId) {
         if (m_attachedShaders.count(shader) == 0) {
             m_attachedShaders.append(shader);
-            m_commandQueue->queueCommand(CanvasGlCommandQueue::glAttachShader,
-                                         m_programId, shader->id());
+            queueCommand(CanvasGlCommandQueue::glAttachShader, m_programId, shader->id());
         }
     }
 }
@@ -85,8 +83,7 @@ void CanvasProgram::detach(CanvasShader *shader)
     if (m_programId) {
         if (m_attachedShaders.count(shader) > 0) {
             m_attachedShaders.removeOne(shader);
-            m_commandQueue->queueCommand(CanvasGlCommandQueue::glDetachShader,
-                                         m_programId, shader->id());
+            queueCommand(CanvasGlCommandQueue::glDetachShader, m_programId, shader->id());
         }
     }
 }
@@ -99,7 +96,7 @@ const QList<CanvasShader *> &CanvasProgram::attachedShaders() const
 void CanvasProgram::link()
 {
     if (m_programId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glLinkProgram, m_programId);
+        queueCommand(CanvasGlCommandQueue::glLinkProgram, m_programId);
         m_linked = true;
     }
 }
@@ -113,24 +110,22 @@ bool CanvasProgram::isLinked()
 
 void CanvasProgram::useProgram()
 {
-    if (m_programId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glUseProgram, m_programId);
-    }
+    if (m_programId)
+        queueCommand(CanvasGlCommandQueue::glUseProgram, m_programId);
 }
 
 void CanvasProgram::bindAttributeLocation(int index, const QString &name)
 {
     if (m_programId) {
-        GlCommand &command = m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindAttribLocation,
-                                                          m_programId, GLint(index));
-        command.data = new QByteArray(name.toLatin1());
+        queueCommand(CanvasGlCommandQueue::glBindAttribLocation, new QByteArray(name.toLatin1()),
+                     m_programId, GLint(index));
     }
 }
 
 void CanvasProgram::del()
 {
     if (m_programId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteProgram, m_programId);
+        queueCommand(CanvasGlCommandQueue::glDeleteProgram, m_programId);
         m_programId = 0;
     }
     m_attachedShaders.clear();
@@ -139,7 +134,7 @@ void CanvasProgram::del()
 void CanvasProgram::validateProgram()
 {
     if (m_programId)
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glValidateProgram, m_programId);
+        queueCommand(CanvasGlCommandQueue::glValidateProgram, m_programId);
 }
 
 GLint CanvasProgram::id()
diff --git a/src/imports/qtcanvas3d/renderbuffer3d.cpp b/src/imports/qtcanvas3d/renderbuffer3d.cpp
index 362777b95ae457da069a8a20cf8ba101ad9029f1..563ac4ef0c6c22f488882c9c36c722a6ea2e80dd 100644
--- a/src/imports/qtcanvas3d/renderbuffer3d.cpp
+++ b/src/imports/qtcanvas3d/renderbuffer3d.cpp
@@ -43,19 +43,24 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DRenderBuffer
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains an OpenGL renderbuffer.
  *
  * An uncreatable QML type that contains an OpenGL renderbuffer. You can get it by calling
  * the \l{Context3D::createRenderbuffer()}{Context3D.createRenderbuffer()} method.
  */
 
-CanvasRenderBuffer::CanvasRenderBuffer(CanvasGlCommandQueue *queue, QObject *parent) :
+CanvasRenderBuffer::CanvasRenderBuffer(CanvasGlCommandQueue *queue,
+                                       bool initSecondaryId, QObject *parent) :
     CanvasAbstractObject(queue, parent),
-    m_renderbufferId(queue->createResourceId())
+    m_renderbufferId(queue->createResourceId()),
+    m_secondaryId(0)
 {
-    Q_ASSERT(m_commandQueue);
-
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glGenRenderbuffers, m_renderbufferId);
+    queueCommand(CanvasGlCommandQueue::glGenRenderbuffers, m_renderbufferId);
+    if (initSecondaryId) {
+        m_secondaryId = queue->createResourceId();
+        queueCommand(CanvasGlCommandQueue::glGenRenderbuffers, m_secondaryId);
+    }
 }
 
 
@@ -72,7 +77,11 @@ bool CanvasRenderBuffer::isAlive()
 void CanvasRenderBuffer::del()
 {
     if (m_renderbufferId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteRenderbuffers, m_renderbufferId);
+        queueCommand(CanvasGlCommandQueue::glDeleteRenderbuffers, m_renderbufferId);
+        if (m_secondaryId) {
+            queueCommand(CanvasGlCommandQueue::glDeleteRenderbuffers, m_secondaryId);
+            m_secondaryId = 0;
+        }
         m_renderbufferId = 0;
     }
 }
@@ -82,5 +91,10 @@ GLint CanvasRenderBuffer::id()
     return m_renderbufferId;
 }
 
+GLint CanvasRenderBuffer::secondaryId()
+{
+    return m_secondaryId;
+}
+
 QT_CANVAS3D_END_NAMESPACE
 QT_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/renderbuffer3d_p.h b/src/imports/qtcanvas3d/renderbuffer3d_p.h
index 385ac6a167add140cffdb4c7855f6010375e33ae..5b634e65aac9bd2e6ceb399e77f4fe82e5b5c4cf 100644
--- a/src/imports/qtcanvas3d/renderbuffer3d_p.h
+++ b/src/imports/qtcanvas3d/renderbuffer3d_p.h
@@ -58,14 +58,17 @@ class CanvasRenderBuffer : public CanvasAbstractObject
 {
     Q_OBJECT
 public:
-    explicit CanvasRenderBuffer(CanvasGlCommandQueue *queue, QObject *parent = 0);
+    explicit CanvasRenderBuffer(CanvasGlCommandQueue *queue, bool initSecondaryId,
+                                QObject *parent = 0);
     ~CanvasRenderBuffer();
     bool isAlive();
     void del();
     GLint id();
+    GLint secondaryId();
 
 private:
     GLint m_renderbufferId;
+    GLint m_secondaryId;
 };
 
 QT_CANVAS3D_END_NAMESPACE
diff --git a/src/imports/qtcanvas3d/shader3d.cpp b/src/imports/qtcanvas3d/shader3d.cpp
index 535e67afc7a93ddd491c6311e66f73b949424ad9..64ddde38203b9e9314b44e7eee7d0ebbcfa33ee6 100644
--- a/src/imports/qtcanvas3d/shader3d.cpp
+++ b/src/imports/qtcanvas3d/shader3d.cpp
@@ -43,6 +43,7 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DShader
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains a shader.
  *
  * An uncreatable QML type that contains a shader. You can get it by calling
@@ -54,9 +55,7 @@ CanvasShader::CanvasShader(GLenum type, CanvasGlCommandQueue *queue, QObject *pa
     m_shaderId(queue->createResourceId()),
     m_sourceCode("")
 {
-    Q_ASSERT(m_commandQueue);
-
-    m_commandQueue->queueCommand(CanvasGlCommandQueue::glCreateShader, GLint(type), m_shaderId);
+    queueCommand(CanvasGlCommandQueue::glCreateShader, GLint(type), m_shaderId);
 }
 
 CanvasShader::~CanvasShader()
@@ -77,7 +76,7 @@ bool CanvasShader::isAlive()
 void CanvasShader::del()
 {
     if (m_shaderId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteShader, m_shaderId);
+        queueCommand(CanvasGlCommandQueue::glDeleteShader, m_shaderId);
         m_shaderId = 0;
     }
 }
@@ -95,9 +94,8 @@ void CanvasShader::setSourceCode(const QString &source)
 void CanvasShader::compileShader()
 {
     if (m_shaderId) {
-        GlCommand &command = m_commandQueue->queueCommand(CanvasGlCommandQueue::glCompileShader,
-                                                          m_shaderId);
-        command.data = new QByteArray(m_sourceCode.toLatin1());
+        queueCommand(CanvasGlCommandQueue::glCompileShader, new QByteArray(m_sourceCode.toLatin1()),
+                     m_shaderId);
     }
 }
 
diff --git a/src/imports/qtcanvas3d/texture3d.cpp b/src/imports/qtcanvas3d/texture3d.cpp
index fafb5a7bc3dd9c97e22093451e6cbdd227e73749..6cc7aed7d2da2a81ed6597e311e81dc92e1afea3 100644
--- a/src/imports/qtcanvas3d/texture3d.cpp
+++ b/src/imports/qtcanvas3d/texture3d.cpp
@@ -44,6 +44,7 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DTexture
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains an OpenGL texture.
  *
  * An uncreatable QML type that contains an OpenGL texture. You can get it by calling
@@ -58,12 +59,10 @@ CanvasTexture::CanvasTexture(CanvasGlCommandQueue *queue, CanvasContext *context
     m_context(context),
     m_quickItem(quickItem)
 {
-    Q_ASSERT(m_commandQueue);
-
     if (m_quickItem)
         connect(m_quickItem, &QObject::destroyed, this, &CanvasTexture::handleItemDestroyed);
     else
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glGenTextures, m_textureId);
+        queueCommand(CanvasGlCommandQueue::glGenTextures, m_textureId);
 }
 
 CanvasTexture::~CanvasTexture()
@@ -73,10 +72,8 @@ CanvasTexture::~CanvasTexture()
 
 void CanvasTexture::bind(CanvasContext::glEnums target)
 {
-    if (m_textureId) {
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::glBindTexture, GLint(target),
-                                     m_textureId);
-    }
+    if (m_textureId)
+        queueCommand(CanvasGlCommandQueue::glBindTexture, GLint(target), m_textureId);
 }
 
 GLint CanvasTexture::textureId() const
@@ -99,14 +96,13 @@ bool CanvasTexture::isAlive() const
 
 void CanvasTexture::del()
 {
-    if (m_textureId) {
+    if (!invalidated() && m_textureId) {
         if (m_quickItem) {
             m_context->quickItemToTextureMap().remove(m_quickItem);
             m_quickItem = 0;
-            m_commandQueue->queueCommand(CanvasGlCommandQueue::internalClearQuickItemAsTexture,
-                                         m_textureId);
+            queueCommand(CanvasGlCommandQueue::internalClearQuickItemAsTexture, m_textureId);
         } else {
-            m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteTextures, m_textureId);
+            queueCommand(CanvasGlCommandQueue::glDeleteTextures, m_textureId);
         }
     }
     m_textureId = 0;
diff --git a/src/imports/qtcanvas3d/uniformlocation.cpp b/src/imports/qtcanvas3d/uniformlocation.cpp
index f76b0b2cb2e6774cbf37c7b20153d8d136a6f344..765b95a0bd011fe2059a306b2acd1bf66652745f 100644
--- a/src/imports/qtcanvas3d/uniformlocation.cpp
+++ b/src/imports/qtcanvas3d/uniformlocation.cpp
@@ -44,6 +44,7 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * \qmltype Canvas3DUniformLocation
  * \since QtCanvas3D 1.0
  * \inqmlmodule QtCanvas3D
+ * \inherits Canvas3DAbstractObject
  * \brief Contains uniform location id.
  *
  * An uncreatable QML type that contains an uniform location id. You can get it by calling
@@ -60,7 +61,7 @@ CanvasUniformLocation::CanvasUniformLocation(CanvasGlCommandQueue *queue, QObjec
 CanvasUniformLocation::~CanvasUniformLocation()
 {
     if (m_locationId)
-        m_commandQueue->queueCommand(CanvasGlCommandQueue::internalClearLocation, m_locationId);
+        queueCommand(CanvasGlCommandQueue::internalClearLocation, m_locationId);
 }
 
 GLint CanvasUniformLocation::id()
diff --git a/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml b/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml
index 2f4da23839d2bafbb3d2c8f57be486505bf08ca3..5ca74c47dcf18a32642ef795dc4d30c2de216343 100644
--- a/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml
+++ b/tests/auto/qmltest/canvas3d/tst_render_dynamic.qml
@@ -34,16 +34,17 @@
 **
 ****************************************************************************/
 
-import QtQuick 2.2
-import QtCanvas3D 1.0
-import QtTest 1.0
+import QtQuick 2.6
+import QtCanvas3D 1.1
+import QtTest 1.1
+import QtQuick.Window 2.0
 
 import "tst_render_simple.js" as Content
 
 Item {
     id: top
-    height: 300
-    width: 300
+    height: 200
+    width: 200
 
     property var canvas3d: null
     property var activeContent: Content
@@ -51,31 +52,33 @@ Item {
     property bool renderOk: false
     property var canvasWindow: null
     property bool windowHidden: false
+    property var window1: null
+    property var window2: null
 
-    function createCanvas() {
+    function createCanvas(parentItem) {
         canvas3d = Qt.createQmlObject("
         import QtQuick 2.2
-        import QtCanvas3D 1.0
+        import QtCanvas3D 1.1
         Canvas3D {
             onInitializeGL: initOk = activeContent.initializeGL(canvas3d)
             onPaintGL: {
                 renderOk = true
                 activeContent.paintGL(canvas3d)
             }
-        }", top)
-        canvas3d.anchors.fill = top
+        }", parentItem)
+        canvas3d.anchors.fill = parentItem
     }
 
     function createCanvasWindow() {
         canvasWindow = Qt.createQmlObject("
         import QtQuick 2.2
-        import QtCanvas3D 1.0
+        import QtCanvas3D 1.1
         import QtQuick.Window 2.0
         Window {
             property alias windowCanvas: theCanvas
             visible: true
-            width: 300
-            height: 300
+            width: 200
+            height: 200
             x: top.width
             y: top.height
             Canvas3D {
@@ -93,22 +96,69 @@ Item {
         }", top)
     }
 
+    function createCanvasNoParent() {
+        var canvasComponent = Qt.createComponent("tst_render_dynamic_canvas_component.qml");
+        if (canvasComponent.status === Component.Ready) {
+            canvas3d = canvasComponent.createObject(null)
+        }
+    }
+
+    function createWindow() {
+        return Qt.createQmlObject("
+        import QtQuick 2.2
+        import QtCanvas3D 1.0
+        import QtQuick.Window 2.0
+        Window {
+            property alias windowTop: windowTop
+            visible: true
+            width: 200
+            height: 200
+            Item {
+                id: windowTop
+                anchors.fill: parent
+            }
+        }", top)
+    }
+
+    function createWindow1() {
+        window1 = createWindow();
+        window1.x = top.x + top.width
+        window1.y = top.y
+    }
+    function createWindow2() {
+        window2 = createWindow();
+        window2.x = window1.x + window1.width
+        window2.y = window1.y
+    }
+
+    function checkCanvasCorrect(windowTop) {
+        var image = theTestCase.grabImage(windowTop);
+        theTestCase.verify(image.pixel(5, 10) === Qt.rgba(0, 0, 1, 1))
+        theTestCase.verify(image.pixel(5, 190) === Qt.rgba(0, 0, 1, 1))
+        theTestCase.verify(image.pixel(95, 100) === Qt.rgba(0, 0, 1, 1))
+        theTestCase.verify(image.pixel(195, 10) === Qt.rgba(0, 0, 0, 1))
+        theTestCase.verify(image.pixel(195, 190) === Qt.rgba(0, 0, 0, 1))
+        theTestCase.verify(image.pixel(105, 100) === Qt.rgba(0, 0, 0, 1))
+    }
+
     TestCase {
+        id: theTestCase
         name: "Canvas3D_render_dynamic"
         when: windowShown
 
-        function test_render_1_dynamic_creation() {
+        function test_render_01_dynamic_creation() {
             verify(canvas3d === null)
             verify(initOk === false)
             verify(renderOk === false)
-            createCanvas()
+            createCanvas(top)
             verify(canvas3d !== null)
             waitForRendering(canvas3d)
             tryCompare(top, "initOk", true)
             tryCompare(top, "renderOk", true)
+            checkCanvasCorrect(top)
         }
 
-        function test_render_2_dynamic_deletion() {
+        function test_render_02_dynamic_deletion() {
             verify(canvas3d !== null)
             verify(initOk === true)
             verify(renderOk === true)
@@ -117,14 +167,15 @@ Item {
             verify(canvas3d === null)
         }
 
-        function test_render_3_dynamic_recreation() {
+        function test_render_03_dynamic_recreation() {
             initOk = false
             renderOk = false
-            createCanvas()
+            createCanvas(top)
             tryCompare(top, "initOk", true)
             tryCompare(top, "renderOk", true)
             waitForRendering(canvas3d)
             verify(canvas3d !== null)
+            checkCanvasCorrect(top)
 
             canvas3d.destroy()
             waitForRendering(canvas3d)
@@ -132,50 +183,434 @@ Item {
 
             initOk = false
             renderOk = false
-            createCanvas()
+            createCanvas(top)
             waitForRendering(canvas3d)
             verify(canvas3d !== null)
             tryCompare(top, "initOk", true)
             tryCompare(top, "renderOk", true)
+            checkCanvasCorrect(top)
 
             canvas3d.destroy()
             waitForRendering(canvas3d)
             verify(canvas3d === null)
         }
 
-        function test_render_4_dynamic_window_creation() {
+        function test_render_04_dynamic_window_creation() {
             initOk = false
             renderOk = false
             verify(canvasWindow === null)
             createCanvasWindow()
             verify(canvasWindow !== null)
+            waitForRendering(canvasWindow.windowCanvas)
             tryCompare(top, "initOk", true)
             tryCompare(top, "renderOk", true)
             tryCompare(top, "windowHidden", false)
+            checkCanvasCorrect(canvasWindow.windowCanvas)
             canvasWindow.destroy()
             tryCompare(top, "canvasWindow", null)
         }
 
-        function test_render_5_dynamic_window_hide_and_reshow() {
+        function test_render_05_dynamic_window_hide_and_reshow() {
             initOk = false
             renderOk = false
             verify(canvasWindow === null)
             verify(windowHidden === false)
             createCanvasWindow()
             verify(canvasWindow !== null)
+            waitForRendering(canvasWindow.windowCanvas)
             tryCompare(top, "initOk", true)
             tryCompare(top, "renderOk", true)
             tryCompare(top, "windowHidden", false)
-            waitForRendering(canvasWindow.windowCanvas)
+            checkCanvasCorrect(canvasWindow.windowCanvas)
             canvasWindow.hide()
+            wait(1000) // Short wait to allow events to be processed
             tryCompare(top, "windowHidden", true)
             renderOk = false
             canvasWindow.show()
             waitForRendering(canvasWindow.windowCanvas)
             tryCompare(top, "renderOk", true)
             tryCompare(top, "windowHidden", false)
+            checkCanvasCorrect(canvasWindow.windowCanvas)
             canvasWindow.destroy()
             tryCompare(top, "canvasWindow", null)
         }
+
+        function test_render_06_dynamic_no_parent() {
+            verify(canvas3d === null)
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(canvas3d.parent === null)
+            canvas3d.destroy()
+            tryCompare(top, "canvas3d", null)
+        }
+
+        function test_render_07_dynamic_from_no_parent_to_parent() {
+            verify(canvas3d === null)
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(canvas3d.parent === null)
+            canvas3d.parent = top
+            waitForRendering(canvas3d)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            checkCanvasCorrect(top)
+            canvas3d.destroy()
+            tryCompare(top, "canvas3d", null)
+        }
+
+        function test_render_08_dynamic_from_no_parent_to_parent_and_back() {
+            verify(canvas3d === null)
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(canvas3d.parent === null)
+
+            // Canvas to window
+            canvas3d.parent = top
+            waitForRendering(canvas3d)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            checkCanvasCorrect(top)
+
+            // Remove canvas from window
+            canvas3d.parent = null
+            waitForRendering(top)
+
+            // Canvas back to same window - should not reinitialize
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.parent = top
+            waitForRendering(top)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "initOk", false)
+            tryCompare(canvas3d, "contextLostOk", false)
+            checkCanvasCorrect(top)
+
+            // Remove canvas from window
+            canvas3d.parent = null
+            waitForRendering(top)
+
+            // Destroy canvas
+            canvas3d.destroy()
+            tryCompare(top, "canvas3d", null)
+
+            // Create canvas again
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(canvas3d.parent === null)
+
+            // Canvas to window
+            canvas3d.parent = top
+            waitForRendering(canvas3d)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", false)
+            checkCanvasCorrect(top)
+
+            // Remove canvas from window
+            canvas3d.parent = null
+            waitForRendering(top)
+
+            // Canvas back to same window - should not reinitialize
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.parent = top
+            waitForRendering(top)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "initOk", false)
+            tryCompare(canvas3d, "contextLostOk", false)
+            checkCanvasCorrect(top)
+
+            // Destroy canvas without removing from window
+            canvas3d.destroy()
+            tryCompare(top, "canvas3d", null)
+        }
+
+        function test_render_09_dynamic_switch_between_two_windows() {
+            verify(canvas3d === null)
+            verify(window1 === null)
+            verify(window2 === null)
+            createCanvasNoParent()
+            createWindow1()
+            createWindow2()
+            verify(canvas3d !== null)
+            verify(window1 !== null)
+            verify(window2 !== null)
+            verify(canvas3d.parent === null)
+
+            // Canvas to first window
+            canvas3d.parent = window1.windowTop
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", false)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Canvas to second window
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.parent = window2.windowTop
+            waitForRendering(window2.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", true)
+            checkCanvasCorrect(window2.windowTop)
+
+            // Canvas back to first window
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.contextLostOk = false
+            canvas3d.contextRestoredOk = false
+            canvas3d.parent = window1.windowTop
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", true)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Canvas to no window, destroy canvas last
+            canvas3d.parent = null
+            waitForRendering(window1.windowTop)
+            window1.destroy()
+            window2.destroy()
+            canvas3d.destroy()
+            tryCompare(top, "canvas3d", null)
+            tryCompare(top, "window1", null)
+            tryCompare(top, "window2", null)
+        }
+
+        function test_render_10_dynamic_canvas_to_window_which_is_destroyed_1() {
+            verify(canvas3d === null)
+            verify(window1 === null)
+            verify(window2 === null)
+
+            // Canvas is created as child of the window
+            initOk = false
+            renderOk = false
+            createWindow1()
+            createCanvas(window1.windowTop)
+            verify(canvas3d !== null)
+            verify(window1 !== null)
+            verify(canvas3d.parent === window1.windowTop)
+            waitForRendering(window1.windowTop)
+            tryCompare(top, "initOk", true)
+            tryCompare(top, "renderOk", true)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Destroy window with canvas still on it - canvas should get destroyed also
+            window1.destroy()
+            tryCompare(top, "window1", null)
+            tryCompare(top, "canvas3d", null);
+        }
+
+        function test_render_11_dynamic_canvas_to_window_which_is_destroyed_2() {
+            verify(canvas3d === null)
+            verify(window1 === null)
+
+            // Create window, this time without canvas as child
+            createWindow1()
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(window1 !== null)
+            verify(canvas3d.parent === null)
+
+            // Set canvas as child of window
+            canvas3d.parent = window1.windowTop
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Destroy window with canvas as only visual child - canvas should not get autodestroyed
+            window1.destroy()
+            tryCompare(top, "window1", null)
+            verify(canvas3d !== null);
+            canvas3d.destroy();
+            tryCompare(top, "canvas3d", null);
+        }
+
+        function test_render_12_dynamic_canvas_to_window_which_is_destroyed_3() {
+            verify(canvas3d === null)
+            verify(window1 === null)
+
+            // Create first window, no child
+            createWindow1()
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(window1 !== null)
+            verify(canvas3d.parent === null)
+
+            // Set canvas as child of window
+            canvas3d.parent = window1.windowTop
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Remove canvas from window
+            canvas3d.parent = null
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "contextLostOk", false)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+
+            // Destroy window with canvas removed
+            window1.destroy()
+            tryCompare(top, "window1", null)
+            verify(canvas3d !== null);
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            var errorCheck = canvas3d.checkContextLostError()
+            verify(errorCheck === true)
+            canvas3d.destroy();
+            tryCompare(top, "canvas3d", null);
+        }
+
+        function test_render_13_dynamic_canvas_to_window_which_is_destroyed_4() {
+            verify(canvas3d === null)
+            verify(window1 === null)
+            verify(window2 === null)
+
+            // Create two windows, no child
+            createWindow1()
+            createWindow2()
+            createCanvasNoParent()
+            verify(canvas3d !== null)
+            verify(window1 !== null)
+            verify(window2 !== null)
+            verify(canvas3d.parent === null)
+
+            // Set canvas as child of first window
+            canvas3d.parent = window1.windowTop
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", false)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Remove canvas from first window
+            canvas3d.parent = null
+            waitForRendering(window1.windowTop)
+
+            // Set canvas as child of second window - should reinitialize
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.parent = window2.windowTop
+            waitForRendering(window2.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", true)
+            checkCanvasCorrect(window2.windowTop)
+
+            // Destroy first window - should not affect canvas
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.contextLostOk = false
+            canvas3d.contextRestoredOk = false
+            window1.destroy()
+            tryCompare(top, "window1", null)
+            waitForRendering(window2.windowTop)
+            verify(canvas3d !== null);
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "initOk", false)
+            tryCompare(canvas3d, "contextLostOk", false)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            checkCanvasCorrect(window2.windowTop)
+
+            // Destroy second window - canvas only visual child, so doesn't get destroyed with it
+            window2.destroy()
+            tryCompare(top, "window2", null)
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            verify(canvas3d !== null);
+            canvas3d.destroy();
+            tryCompare(top, "canvas3d", null);
+        }
+
+        function test_render_14_dynamic_change_window_without_render_inbetween() {
+            verify(canvas3d === null)
+            verify(window1 === null)
+            verify(window2 === null)
+            createCanvasNoParent()
+            createWindow1()
+            createWindow2()
+            verify(canvas3d !== null)
+            verify(window1 !== null)
+            verify(window2 !== null)
+            verify(canvas3d.parent === null)
+
+            // Canvas to first window
+            canvas3d.parent = window1.windowTop
+            waitForRendering(window1.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", false)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Toggle parent rapidly - should not affect canvas
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.contextLostOk = false
+            canvas3d.contextRestoredOk = false
+            canvas3d.parent = null
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = null
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = null
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = null
+            canvas3d.parent = window1.windowTop
+            tryCompare(canvas3d, "initOk", false)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", false)
+            tryCompare(canvas3d, "contextRestoredOk", false)
+            checkCanvasCorrect(window1.windowTop)
+
+            // Canvas to second window
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.parent = window2.windowTop
+            waitForRendering(window2.windowTop)
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", true)
+            checkCanvasCorrect(window2.windowTop)
+
+            // Change window rapidly - should cause context loss
+            canvas3d.initOk = false
+            canvas3d.renderOk = false
+            canvas3d.contextLostOk = false
+            canvas3d.contextRestoredOk = false
+            canvas3d.parent = window2.windowTop
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = null
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = window2.windowTop
+            canvas3d.parent = null
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = window2.windowTop
+            canvas3d.parent = null
+            canvas3d.parent = window2.windowTop
+            canvas3d.parent = window1.windowTop
+            canvas3d.parent = window2.windowTop
+            tryCompare(canvas3d, "initOk", true)
+            tryCompare(canvas3d, "renderOk", true)
+            tryCompare(canvas3d, "contextLostOk", true)
+            tryCompare(canvas3d, "contextRestoredOk", true)
+            checkCanvasCorrect(window2.windowTop)
+
+            // Destroy windows and canvas
+            window1.destroy()
+            window2.destroy()
+            canvas3d.destroy()
+            tryCompare(top, "canvas3d", null)
+            tryCompare(top, "window1", null)
+            tryCompare(top, "window2", null)
+        }
     }
 }
diff --git a/tests/auto/qmltest/canvas3d/tst_render_dynamic_canvas_component.qml b/tests/auto/qmltest/canvas3d/tst_render_dynamic_canvas_component.qml
new file mode 100644
index 0000000000000000000000000000000000000000..723187344c07dfa21a8df69f01cc0c91ba35cc06
--- /dev/null
+++ b/tests/auto/qmltest/canvas3d/tst_render_dynamic_canvas_component.qml
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtCanvas3D 1.1
+
+import "tst_render_simple.js" as Content
+
+Canvas3D {
+    id: canvas3d
+    anchors.fill: parent
+
+    property var activeContent: Content
+    property bool initOk: false
+    property bool renderOk: false
+    property bool contextLostOk: false
+    property bool contextRestoredOk: false
+
+    onInitializeGL: {
+        initOk = activeContent.initializeGL(canvas3d)
+    }
+    onPaintGL: {
+        renderOk = true
+        activeContent.paintGL(canvas3d)
+    }
+    onContextLost: {
+        contextLostOk = activeContent.checkContextLost();
+    }
+
+    onContextRestored: {
+        contextRestoredOk = activeContent.checkContextRestored();
+    }
+
+    function checkContextLostError() {
+        return activeContent.checkContextLostError();
+    }
+}
diff --git a/tests/auto/qmltest/canvas3d/tst_render_simple.js b/tests/auto/qmltest/canvas3d/tst_render_simple.js
index cd2c840f937fd31df6a37486fef3f111342c0c44..62e6186c7b1fa4f1826c452d1710bca2bce0b904 100644
--- a/tests/auto/qmltest/canvas3d/tst_render_simple.js
+++ b/tests/auto/qmltest/canvas3d/tst_render_simple.js
@@ -82,7 +82,11 @@ function initializeGL(canvas) {
 function paintGL(canvas) {
     gl.clear(gl.COLOR_BUFFER_BIT);
     gl.drawArrays(gl.TRIANGLES, 0, 3);
-    return (gl.getError() === 0);
+    var err = 0;
+    if (canvas.renderTarget === Canvas3D.RenderTargetOffscreenBuffer)
+        err = gl.getError();
+    return (err === 0);
+
 }
 
 function resizeGL(canvas)
@@ -144,3 +148,44 @@ function compileShader(str, type) {
 
     return shader;
 }
+
+function checkInvalidations() {
+    if (shaderProgram.invalidated === false)
+        return false;
+    if (buffer.invalidated === false)
+        return false;
+    if (vertexShader.invalidated === false)
+        return false;
+    if (fragmentShader.invalidated === false)
+        return false;
+    return true;
+}
+
+function checkContextLost() {
+    var lostStatus = gl.isContextLost();
+    if (lostStatus === true) {
+        return checkInvalidations();
+    } else {
+        return false;
+    }
+}
+
+function checkContextRestored() {
+    var lostStatus = gl.isContextLost();
+    if (lostStatus === false) {
+        // All old objects should still be invalid, initializeGL will validate them
+        return checkInvalidations();
+    } else {
+        return false;
+    }
+}
+
+function checkContextLostError() {
+    var err = gl.getError();
+    if (err === gl.CONTEXT_LOST_WEBGL) {
+        err = gl.getError();
+        if (err === gl.NO_ERROR)
+            return true;
+    }
+    return false;
+}
diff --git a/tests/manual/commandthroughput/qml/commandthroughput/main.qml b/tests/manual/commandthroughput/qml/commandthroughput/main.qml
index a234c0b695642c3fd245ba9fee4e0deb8d497fbd..cda57816866d5548fe7115b7ad97035fbf4c3de3 100644
--- a/tests/manual/commandthroughput/qml/commandthroughput/main.qml
+++ b/tests/manual/commandthroughput/qml/commandthroughput/main.qml
@@ -58,7 +58,6 @@ Item {
         property double xRotAnim: 0
         property double yRotAnim: 0
         property double zRotAnim: 0
-        property bool isRunning: true
         property int itemCount: 0
         property int maxCount: 5000
         property int frameTime: 0
@@ -87,19 +86,6 @@ Item {
         onDevicePixelRatioChanged: {
             GLCode.onCanvasResize(canvas3d);
         }
-
-        Keys.onSpacePressed: {
-            canvas3d.isRunning = !canvas3d.isRunning
-            if (canvas3d.isRunning) {
-                objAnimationX.pause();
-                objAnimationY.pause();
-                objAnimationZ.pause();
-            } else {
-                objAnimationX.resume();
-                objAnimationY.resume();
-                objAnimationZ.resume();
-            }
-        }
     }
 
     Timer {
@@ -125,7 +111,7 @@ Item {
             id: fpsLabel
             Layout.alignment: Qt.AlignRight
             Layout.fillWidth: false
-            Layout.preferredWidth : 180
+            Layout.preferredWidth : 200
             text: "Fps: " + canvas3d.fps + " GL:" + canvas3d.frameTime + " onPaintGL:" + canvas3d.frameSetupTime
             color: "#FFFFFF"
         }
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index fb886f381a807e7f7e11c953915358d30773ac01..b856c2d69a13546aa598126569e8188e0cf04ed4 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -1,2 +1,3 @@
 TEMPLATE = subdirs
-SUBDIRS = commandthroughput
+SUBDIRS = commandthroughput \
+    multiwindowtest
diff --git a/tests/manual/multiwindowtest/main.cpp b/tests/manual/multiwindowtest/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99470cd6e15af671b60db9f1bf959afaaea51985
--- /dev/null
+++ b/tests/manual/multiwindowtest/main.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+    QGuiApplication app(argc, argv);
+
+    QQmlApplicationEngine engine;
+
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+    return app.exec();
+}
diff --git a/tests/manual/multiwindowtest/multiwindowtest.pro b/tests/manual/multiwindowtest/multiwindowtest.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ab052312b0a0e7da6e54d8b875dae029abaad535
--- /dev/null
+++ b/tests/manual/multiwindowtest/multiwindowtest.pro
@@ -0,0 +1,17 @@
+TEMPLATE = app
+
+QT += qml quick
+
+SOURCES += main.cpp
+
+RESOURCES += multiwindowtest.qrc
+
+OTHER_FILES += qml/multiwindowtest/*
+
+DISTFILES += \
+    qml/multiwindowtest/framebuffer.js \
+    qml/multiwindowtest/quickitemtexture.js \
+    qml/multiwindowtest/qtlogo.png \
+    qml/multiwindowtest/framebuffer.qml \
+    qml/multiwindowtest/quickitemtexture.qml
+
diff --git a/tests/manual/multiwindowtest/multiwindowtest.qrc b/tests/manual/multiwindowtest/multiwindowtest.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..cb387e1903007246e31399b58a167dd81f59d75e
--- /dev/null
+++ b/tests/manual/multiwindowtest/multiwindowtest.qrc
@@ -0,0 +1,12 @@
+<RCC>
+    <qresource prefix="/">
+        <file alias="gl-matrix.js">../../../examples/canvas3d/canvas3d/3rdparty/gl-matrix.js</file>
+        <file alias="main.qml">qml/multiwindowtest/main.qml</file>
+        <file alias="framebuffer.js">qml/multiwindowtest/framebuffer.js</file>
+        <file alias="framebuffer.qml">qml/multiwindowtest/framebuffer.qml</file>
+        <file alias="qtlogo.png">qml/multiwindowtest/qtlogo.png</file>
+        <file alias="quickitemtexture.js">qml/multiwindowtest/quickitemtexture.js</file>
+        <file alias="quickitemtexture.qml">qml/multiwindowtest/quickitemtexture.qml</file>
+        <file alias="canvaswindow.qml">qml/multiwindowtest/canvaswindow.qml</file>
+    </qresource>
+</RCC>
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/canvaswindow.qml b/tests/manual/multiwindowtest/qml/multiwindowtest/canvaswindow.qml
new file mode 100644
index 0000000000000000000000000000000000000000..3ddca17950729e839258c0e627d23bcb7b5bf9fe
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/canvaswindow.qml
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Controls 1.0
+import QtQuick.Layouts 1.0
+import QtQuick.Window 2.2
+
+Window {
+    id: canvasWindow
+    width: 400
+    height: 600
+    visible: true
+
+    property alias canvasArea: canvasArea
+    property var managerObject: null
+    property var canvas: null
+    property var previousCanvas: null
+
+    function setManager(manager) {
+        managerObject = manager;
+    }
+
+    onClosing: {
+       managerObject.removeWindow(canvasWindow)
+    }
+
+    ColumnLayout {
+        spacing: 1
+        anchors.fill: parent
+        visible: true
+
+        Item {
+            id: canvasArea
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            Layout.preferredHeight: 400
+
+            onChildrenChanged: {
+                if (children.length === 0) {
+                    previousCanvas = canvas
+                    canvas = null
+                }
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "New QuickTexture canvas"
+            onClicked: {
+                previousCanvas = canvas
+                if (canvas)
+                    canvas.parent = null
+                canvas = managerObject.createItemTextureCanvas(canvasArea)
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "New framebuffer canvas"
+            onClicked: {
+                previousCanvas = canvas
+                if (canvas)
+                    canvas.parent = null
+                canvas = managerObject.createFboCanvas(canvasArea)
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Grab random canvas"
+            onClicked: {
+                previousCanvas = canvas
+                if (canvas)
+                    canvas.parent = null
+                canvas = chooseRandomCanvas()
+                if (canvas)
+                    canvas.parent = canvasArea
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Grab previous canvas"
+            onClicked: {
+                var previous = previousCanvas
+                previousCanvas = canvas
+                if (canvas)
+                    canvas.parent = null
+                canvas = previous
+                if (canvas)
+                    canvas.parent = canvasArea
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Delete canvas"
+            onClicked: {
+                managerObject.deleteCanvas(canvas)
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Release canvas"
+            onClicked: {
+                if (canvas)
+                    canvas.parent = null
+            }
+        }
+    }
+}
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/framebuffer.js b/tests/manual/multiwindowtest/qml/multiwindowtest/framebuffer.js
new file mode 100644
index 0000000000000000000000000000000000000000..644a2f2e6952c4b26ea1d9938afd425d8680415b
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/framebuffer.js
@@ -0,0 +1,460 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+Qt.include("gl-matrix.js")
+
+//
+// Draws a cube that has the Qt logo as decal texture on each face in to a texture.
+// That texture is used as the texture for drawing another cube on the screen.
+//
+
+var gl;
+
+var rttFramebuffer;
+var rttTexture;
+var rttWidth = 512;
+var rttHeight = 512;
+
+var cubeTexture = 0;
+
+var vertexPositionAttribute;
+var textureCoordAttribute;
+var vertexNormalAttribute;
+var vertexColorAttribute;
+var mvMatrix = mat4.create();
+var pMatrix  = mat4.create();
+var nMatrix  = mat4.create();
+var pMatrixUniform;
+var mvMatrixUniform;
+var nUniform;
+
+var canvas3d;
+var isLogEnabled = false;
+
+function log(message) {
+    if (isLogEnabled)
+        console.log(message)
+}
+
+function initializeGL(canvas, textureLoader) {
+    canvas3d = canvas
+    try {
+        // Get the OpenGL context object that represents the API we call
+        gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+
+        // Setup the OpenGL state
+        gl.enable(gl.DEPTH_TEST);
+        gl.enable(gl.CULL_FACE);
+        gl.cullFace(gl.BACK);
+        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+
+        // Initialize the shader program
+        initShaders();
+
+        // Initialize vertex and color buffers
+        initBuffers();
+
+        // Load the Qt logo as texture
+        var qtLogoImage = TextureImageFactory.newTexImage();
+        qtLogoImage.imageLoaded.connect(function() {
+            cubeTexture = gl.createTexture();
+            cubeTexture.name = "CubeTexture";
+            gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
+            gl.texImage2D(gl.TEXTURE_2D,    // target
+                          0,                // level
+                          gl.RGBA,          // internalformat
+                          gl.RGBA,          // format
+                          gl.UNSIGNED_BYTE, // type
+                          qtLogoImage);    // pixels
+
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+            gl.generateMipmap(gl.TEXTURE_2D);
+        });
+        qtLogoImage.imageLoadingFailed.connect(function() {
+            console.log("Texture load FAILED, "+qtLogoImage.errorString);
+        });
+        qtLogoImage.src = "qrc:/qtlogo.png";
+
+        // Create the framebuffer object
+        rttFramebuffer = gl.createFramebuffer();
+        rttFramebuffer.name = "OffscreenRenderTarget";
+        gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
+
+        // Create the texture
+        rttTexture = gl.createTexture();
+        rttTexture.name = "OffscreenRenderTargetTexture";
+        gl.bindTexture(gl.TEXTURE_2D, rttTexture);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+        gl.texImage2D(gl.TEXTURE_2D, 0,
+                      gl.RGBA, rttWidth, rttHeight,
+                      0, gl.RGBA, gl.UNSIGNED_BYTE,
+                      null);
+        gl.generateMipmap(gl.TEXTURE_2D);
+
+        // Bind the texture as color attachment, create and bind a depth buffer
+        gl.framebufferTexture2D(gl.FRAMEBUFFER,
+                                gl.COLOR_ATTACHMENT0,
+                                gl.TEXTURE_2D, rttTexture, 0);
+        var renderbuffer = gl.createRenderbuffer();
+        gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
+        gl.renderbufferStorage(gl.RENDERBUFFER,
+                               gl.DEPTH_COMPONENT16,
+                               rttWidth, rttHeight);
+        gl.framebufferRenderbuffer(gl.FRAMEBUFFER,
+                                   gl.DEPTH_ATTACHMENT,
+                                   gl.RENDERBUFFER, renderbuffer);
+        gl.bindTexture(gl.TEXTURE_2D, 0);
+        gl.bindRenderbuffer(gl.RENDERBUFFER, 0);
+        gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
+    } catch(e) {
+        console.log("initializeGL FAILURE!");
+        console.log(""+e);
+        console.log(""+e.message);
+    }
+}
+
+function handleContextLost() {
+    // Null all used textures so we don't get invalid operation on first few paints
+    // after context lost while the textures re-resolve.
+    rttTexture = null;
+    cubeTexture = null;
+}
+
+function degToRad(degrees) {
+    return degrees * Math.PI / 180;
+}
+
+function paintGL(canvas, clear) {
+    // bind the FBO and setup viewport
+    gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
+    gl.viewport(0, 0, rttWidth, rttHeight);
+
+    // Clear the cube buffer
+    gl.clearColor(0.95, 0.95, 0.95, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+    // Bind the loaded texture
+    gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
+
+    // Calculate and set matrix uniforms
+    mat4.perspective(pMatrix, degToRad(45), rttWidth / rttHeight, 0.1, 100.0);
+    gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix);
+
+    mat4.identity(mvMatrix);
+    mat4.translate(mvMatrix, mvMatrix, [0, 0, -5.0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.xRotSlider), [0, 1, 0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.yRotSlider), [1, 0, 0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.zRotSlider), [0, 0, 1]);
+    gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix);
+
+    mat4.invert(nMatrix, mvMatrix);
+    mat4.transpose(nMatrix, nMatrix);
+    gl.uniformMatrix4fv(nUniform, false, nMatrix);
+
+    // Draw the cube to the FBO
+    gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
+
+    // Bind the render-to-texture and generate mipmaps
+    gl.bindTexture(gl.TEXTURE_2D, rttTexture);
+    gl.generateMipmap(gl.TEXTURE_2D);
+
+    // Bind default framebuffer and setup viewport accordingly
+    gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
+    gl.viewport(0, 0,
+                canvas.width * canvas.devicePixelRatio,
+                canvas.height * canvas.devicePixelRatio);
+
+    // Clear the canvas buffer
+    gl.clearColor(0.0, 0.0, 0.0, 0.0);
+    if (clear)
+        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+    // Calculate and set matrix uniforms
+    mat4.perspective(pMatrix, degToRad(45), canvas.width / canvas.height, 0.1, 100.0);
+    gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix);
+
+    mat4.identity(mvMatrix);
+    mat4.translate(mvMatrix, mvMatrix, [(canvas.yRotAnim - 120.0) / 120.0,
+                                        (canvas.xRotAnim -  60.0) / 50.0,
+                                        -10.0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.xRotAnim), [0, 1, 0]);
+    gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix);
+
+    mat4.invert(nMatrix, mvMatrix);
+    mat4.transpose(nMatrix, nMatrix);
+    gl.uniformMatrix4fv(nUniform, false, nMatrix);
+
+    // Draw the on-screen cube
+    gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
+}
+
+function resizeGL(canvas)
+{
+    var pixelRatio = canvas.devicePixelRatio;
+    canvas.pixelSize = Qt.size(canvas.width * pixelRatio,
+                               canvas.height * pixelRatio);
+}
+
+function initBuffers()
+{
+    log("        cubeVertexPositionBuffer");
+    var cubeVertexPositionBuffer = gl.createBuffer();
+    cubeVertexPositionBuffer.name = "cubeVertexPositionBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
+    gl.bufferData(
+                gl.ARRAY_BUFFER,
+                new Float32Array([// Front face
+                                        -1.0, -1.0,  1.0,
+                                        1.0, -1.0,  1.0,
+                                        1.0,  1.0,  1.0,
+                                        -1.0,  1.0,  1.0,
+
+                                        // Back face
+                                        -1.0, -1.0, -1.0,
+                                        -1.0,  1.0, -1.0,
+                                        1.0,  1.0, -1.0,
+                                        1.0, -1.0, -1.0,
+
+                                        // Top face
+                                        -1.0,  1.0, -1.0,
+                                        -1.0,  1.0,  1.0,
+                                        1.0,  1.0,  1.0,
+                                        1.0,  1.0, -1.0,
+
+                                        // Bottom face
+                                        -1.0, -1.0, -1.0,
+                                        1.0, -1.0, -1.0,
+                                        1.0, -1.0,  1.0,
+                                        -1.0, -1.0,  1.0,
+
+                                        // Right face
+                                        1.0, -1.0, -1.0,
+                                        1.0,  1.0, -1.0,
+                                        1.0,  1.0,  1.0,
+                                        1.0, -1.0,  1.0,
+
+                                        // Left face
+                                        -1.0, -1.0, -1.0,
+                                        -1.0, -1.0,  1.0,
+                                        -1.0,  1.0,  1.0,
+                                        -1.0,  1.0, -1.0
+                                       ]),
+                gl.STATIC_DRAW);
+
+    gl.enableVertexAttribArray(vertexPositionAttribute);
+    gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
+
+    log("        cubeVertexIndexBuffer");
+    var cubeVertexIndexBuffer = gl.createBuffer();
+    cubeVertexIndexBuffer.name = "cubeVertexIndexBuffer";
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
+    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
+                  new Uint16Array([
+                                            0,  1,  2,      0,  2,  3,    // front
+                                            4,  5,  6,      4,  6,  7,    // back
+                                            8,  9,  10,     8,  10, 11,   // top
+                                            12, 13, 14,     12, 14, 15,   // bottom
+                                            16, 17, 18,     16, 18, 19,   // right
+                                            20, 21, 22,     20, 22, 23    // left
+                                        ]),
+                  gl.STATIC_DRAW);
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
+
+    log("        cubeVerticesTextureCoordBuffer");
+    var cubeVerticesTextureCoordBuffer = gl.createBuffer();
+    cubeVerticesTextureCoordBuffer.name = "cubeVerticesTextureCoordBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
+    var textureCoordinates = [
+                // Front
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Back
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Top
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Bottom
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Right
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Left
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0
+            ];
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
+                  gl.STATIC_DRAW);
+    gl.enableVertexAttribArray(textureCoordAttribute);
+    gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
+
+    var cubeVerticesNormalBuffer = gl.createBuffer();
+    cubeVerticesNormalBuffer.name = "cubeVerticesNormalBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+                                                              // Front
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+
+                                                              // Back
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+
+                                                              // Top
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+
+                                                              // Bottom
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+
+                                                              // Right
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+
+                                                              // Left
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0
+                                                          ]), gl.STATIC_DRAW);
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+    gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
+}
+
+function initShaders()
+{
+    var vertexShader = getShader(gl,
+                                 "attribute highp vec3 aVertexNormal;   \
+                                  attribute highp vec3 aVertexPosition; \
+                                  attribute highp vec2 aTextureCoord;   \
+
+                                  uniform highp mat4 uNormalMatrix;     \
+                                  uniform mat4 uMVMatrix;               \
+                                  uniform mat4 uPMatrix;                \
+
+                                  varying mediump vec4 vColor;          \
+                                  varying highp vec2 vTextureCoord;     \
+                                  varying highp vec3 vLighting;         \
+
+                                  void main(void) {                     \
+                                      gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);                      \
+                                      vTextureCoord = aTextureCoord;                                                        \
+                                      highp vec3 ambientLight = vec3(0.5, 0.5, 0.5);                                        \
+                                      highp vec3 directionalLightColor = vec3(0.75, 0.75, 0.75);                             \
+                                      highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);                                 \
+                                      highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);              \
+                                      highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);    \
+                                      vLighting = ambientLight + (directionalLightColor * directional);                     \
+                                  }", gl.VERTEX_SHADER);
+    var fragmentShader = getShader(gl,
+                                   "varying highp vec2 vTextureCoord;   \
+                                    varying highp vec3 vLighting;       \
+
+                                    uniform sampler2D uSampler;         \
+
+                                    void main(void) {                   \
+                                        mediump vec3 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)).rgb;  \
+                                        gl_FragColor = vec4(texelColor * vLighting, 1.0);                                       \
+                                    }", gl.FRAGMENT_SHADER);
+
+    var shaderProgram = gl.createProgram();
+    gl.attachShader(shaderProgram, vertexShader);
+    gl.attachShader(shaderProgram, fragmentShader);
+    gl.linkProgram(shaderProgram);
+
+    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+        console.log("Could not initialize shaders");
+        console.log(gl.getProgramInfoLog(shaderProgram));
+    }
+
+    gl.useProgram(shaderProgram);
+
+    // look up where the vertex data needs to go.
+    vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
+    gl.enableVertexAttribArray(vertexPositionAttribute);
+    textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
+    gl.enableVertexAttribArray(textureCoordAttribute);
+    vertexNormalAttribute =gl.getAttribLocation(shaderProgram, "aVertexNormal");
+    gl.enableVertexAttribArray(vertexNormalAttribute);
+
+    pMatrixUniform  = gl.getUniformLocation(shaderProgram, "uPMatrix");
+    mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
+    nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
+
+    var textureSamplerUniform = gl.getUniformLocation(shaderProgram, "uSampler")
+    gl.activeTexture(gl.TEXTURE0);
+    gl.uniform1i(textureSamplerUniform, 0);
+}
+
+function getShader(gl, str, type) {
+    var shader = gl.createShader(type);
+    gl.shaderSource(shader, str);
+    gl.compileShader(shader);
+
+    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+        console.log("JS:Shader compile failed");
+        console.log(gl.getShaderInfoLog(shader));
+        return null;
+    }
+
+    return shader;
+}
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/framebuffer.qml b/tests/manual/multiwindowtest/qml/multiwindowtest/framebuffer.qml
new file mode 100644
index 0000000000000000000000000000000000000000..3fe937826dd421db5068601d9cfe6bc30b3a7302
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/framebuffer.qml
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtCanvas3D 1.1
+import QtQuick.Controls 1.0
+import QtQuick.Layouts 1.0
+
+import "framebuffer.js" as GLCode
+
+Rectangle {
+    id: mainView
+    anchors.fill: parent
+    visible: true
+    color: "#f2f2f2"
+
+    property alias canvas3d: canvas3d
+    property string canvasName: ""
+    property var previousParent: null
+
+    onParentChanged: {
+        if (previousParent && previousParent.handleParentChange)
+            previousParent.handleParentChange()
+        previousParent = parent
+    }
+
+    Canvas3D {
+        id: canvas3d
+        anchors.fill: parent
+        property double xRotSlider: 0
+        property double yRotSlider: 0
+        property double zRotSlider: 0
+        property double xRotAnim: 0
+        property double yRotAnim: 0
+        property double zRotAnim: 0
+        property bool isRunning: true
+
+        // Emitted when one time initializations should happen
+        onInitializeGL: {
+            GLCode.initializeGL(canvas3d);
+        }
+
+        // Emitted each time Canvas3D is ready for a new frame
+        onPaintGL: {
+            if (canvas3d.renderTarget === Canvas3D.RenderTargetOffscreenBuffer)
+                GLCode.paintGL(canvas3d, true);
+            else
+                GLCode.paintGL(canvas3d, false);
+        }
+
+        onResizeGL: {
+            GLCode.resizeGL(canvas3d);
+        }
+
+        onContextLost: {
+            console.log("Context lost on: ", mainView.canvasName)
+            GLCode.handleContextLost();
+        }
+
+        onContextRestored: {
+            console.log("Context restored on: ", mainView.canvasName)
+        }
+
+        Keys.onSpacePressed: {
+            canvas3d.isRunning = !canvas3d.isRunning
+            if (canvas3d.isRunning) {
+                objAnimationX.pause();
+                objAnimationY.pause();
+                objAnimationZ.pause();
+            } else {
+                objAnimationX.resume();
+                objAnimationY.resume();
+                objAnimationZ.resume();
+            }
+        }
+
+        SequentialAnimation {
+            id: objAnimationX
+            loops: Animation.Infinite
+            running: true
+            NumberAnimation {
+                target: canvas3d
+                property: "xRotAnim"
+                from: 0.0
+                to: 120.0
+                duration: 7000
+                easing.type: Easing.InOutQuad
+            }
+            NumberAnimation {
+                target: canvas3d
+                property: "xRotAnim"
+                from: 120.0
+                to: 0.0
+                duration: 7000
+                easing.type: Easing.InOutQuad
+            }
+        }
+
+        SequentialAnimation {
+            id: objAnimationY
+            loops: Animation.Infinite
+            running: true
+            NumberAnimation {
+                target: canvas3d
+                property: "yRotAnim"
+                from: 0.0
+                to: 240.0
+                duration: 5000
+                easing.type: Easing.InOutCubic
+            }
+            NumberAnimation {
+                target: canvas3d
+                property: "yRotAnim"
+                from: 240.0
+                to: 0.0
+                duration: 5000
+                easing.type: Easing.InOutCubic
+            }
+        }
+
+        SequentialAnimation {
+            id: objAnimationZ
+            loops: Animation.Infinite
+            running: true
+            NumberAnimation {
+                target: canvas3d
+                property: "zRotAnim"
+                from: -100.0
+                to: 100.0
+                duration: 3000
+                easing.type: Easing.InOutSine
+            }
+            NumberAnimation {
+                target: canvas3d
+                property: "zRotAnim"
+                from: 100.0
+                to: -100.0
+                duration: 3000
+                easing.type: Easing.InOutSine
+            }
+        }
+    }
+
+    RowLayout {
+        id: controlLayout
+        spacing: 5
+        x: 12
+        y: parent.height - 100
+        width: parent.width - (x * 2)
+        height: 100
+        visible: true
+
+        Label {
+            id: xRotLabel
+            Layout.alignment: Qt.AlignRight
+            Layout.fillWidth: false
+            text: "X-axis:"
+        }
+
+        Slider {
+            id: xSlider
+            Layout.alignment: Qt.AlignLeft
+            Layout.fillWidth: true
+            minimumValue: 0;
+            maximumValue: 360;
+            onValueChanged: canvas3d.xRotSlider = value;
+        }
+
+        Label {
+            id: yRotLabel
+            Layout.alignment: Qt.AlignRight
+            Layout.fillWidth: false
+            text: "Y-axis:"
+        }
+
+        Slider {
+            id: ySlider
+            Layout.alignment: Qt.AlignLeft
+            Layout.fillWidth: true
+            minimumValue: 0;
+            maximumValue: 360;
+            onValueChanged: canvas3d.yRotSlider = value;
+        }
+
+        Label {
+            id: zRotLabel
+            Layout.alignment: Qt.AlignRight
+            Layout.fillWidth: false
+            text: "Z-axis:"
+        }
+
+        Slider {
+            id: zSlider
+            Layout.alignment: Qt.AlignLeft
+            Layout.fillWidth: true
+            minimumValue: 0;
+            maximumValue: 360;
+            onValueChanged: canvas3d.zRotSlider = value;
+        }
+    }
+}
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/main.qml b/tests/manual/multiwindowtest/qml/multiwindowtest/main.qml
new file mode 100644
index 0000000000000000000000000000000000000000..969c14c4f3e18ab8edb117577c72fe8460e2d291
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/main.qml
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 1.0
+import QtQuick.Layouts 1.0
+import QtQuick.Window 2.2
+import QtCanvas3D 1.1
+
+Window {
+    id: mainview
+    width: 400
+    height: 600
+    visible: true
+    title: "Windows:" + windowCount + " Canvases:" + canvasCount
+
+    property var windowList: []
+    property var canvasList: []
+    property var windowComponent: null
+    property var fboCanvasComponent: null
+    property var itemTextureCanvasComponent: null
+    property int windowCount: 0
+    property int canvasCount: 0
+    property string renderTarget: "Offscreen"
+
+    onClosing: {
+       for (var i = windowCount - 1; i >= 0; i--)
+           windowList[i].close()
+    }
+
+    ColumnLayout {
+        id: controlLayout
+        spacing: 1
+        anchors.fill: parent
+        visible: true
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "New window"
+            onClicked: {
+                createWindow()
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "New framebuffer canvas"
+            onClicked: {
+                createFboCanvas(null)
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "New QuickTexture  canvas"
+            onClicked: {
+                createItemTextureCanvas(null)
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Delete random canvas"
+            onClicked: {
+                deleteCanvas(chooseRandomCanvas())
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Delete first canvas"
+            onClicked: {
+                if (canvasCount > 0)
+                    deleteCanvas(canvasList[0])
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Delete last canvas"
+            onClicked: {
+                if (canvasCount > 0)
+                    deleteCanvas(canvasList[canvasCount - 1])
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "RenderTarget for new canvases: " + renderTarget
+            onClicked: {
+                if (renderTarget === "Offscreen") {
+                    renderTarget = "Background"
+                } else if (renderTarget === "Background") {
+                    renderTarget = "Foreground"
+                } else {
+                    renderTarget = "Offscreen"
+                }
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Run test: Swapping two canvas in one window"
+            onClicked: {
+                if (singleWindowSwapTestTimer.running) {
+                    if (singleWindowSwapTestCanvasFbo)
+                        singleWindowSwapTestCanvasFbo.destroy()
+                    if (singleWindowSwapTestCanvasQuickItem)
+                        singleWindowSwapTestCanvasQuickItem.destroy()
+                    if (singleWindowSwapTestWindow) {
+                        removeWindow(singleWindowSwapTestWindow)
+                        singleWindowSwapTestWindow.destroy()
+                    }
+                    singleWindowSwapTestTimer.stop()
+                } else {
+                    singleWindowSwapTestWindow = createWindow()
+                    singleWindowSwapTestCanvasFbo = createFboCanvas(null)
+                    singleWindowSwapTestCanvasQuickItem = createItemTextureCanvas(null)
+                    singleWindowSwapTestTimer.start()
+                }
+            }
+        }
+
+        Button {
+            Layout.fillHeight: true
+            Layout.fillWidth: true
+            text: "Run test: Swapping one canvas in two windows"
+            onClicked: {
+                if (doubleWindowSwapTestTimer.running) {
+                    if (doubleWindowSwapTestCanvas)
+                        doubleWindowSwapTestCanvas.destroy()
+                    if (doubleWindowSwapTestWindow1) {
+                        removeWindow(doubleWindowSwapTestWindow1)
+                        doubleWindowSwapTestWindow1.destroy()
+                    }
+                    if (doubleWindowSwapTestWindow2) {
+                        removeWindow(doubleWindowSwapTestWindow2)
+                        doubleWindowSwapTestWindow2.destroy()
+                    }
+                    doubleWindowSwapTestTimer.stop()
+                } else {
+                    doubleWindowSwapTestWindow1 = createWindow()
+                    doubleWindowSwapTestWindow2 = createWindow()
+                    doubleWindowSwapTestCanvas = createFboCanvas(null)
+
+                    doubleWindowSwapTestTimer.start()
+                }
+            }
+        }
+    }
+
+    property var singleWindowSwapTestWindow: null
+    property var singleWindowSwapTestCanvasFbo: null
+    property var singleWindowSwapTestCanvasQuickItem: null
+
+    Timer {
+        id: singleWindowSwapTestTimer
+        interval: 200
+        repeat: true
+
+        property bool fboCanvasShown: false
+        property int counter: 0
+
+        onTriggered: {
+            console.log("Count:", counter++)
+            if (fboCanvasShown) {
+                fboCanvasShown = false
+                singleWindowSwapTestCanvasQuickItem.parent = null
+                singleWindowSwapTestCanvasFbo.parent = singleWindowSwapTestWindow.canvasArea
+            } else {
+                fboCanvasShown = true
+                singleWindowSwapTestCanvasFbo.parent = null
+                singleWindowSwapTestCanvasQuickItem.parent = singleWindowSwapTestWindow.canvasArea
+            }
+        }
+    }
+
+    property var doubleWindowSwapTestWindow1: null
+    property var doubleWindowSwapTestWindow2: null
+    property var doubleWindowSwapTestCanvas: null
+
+    Timer {
+        id: doubleWindowSwapTestTimer
+        interval: 200
+        repeat: true
+
+        property bool firstWindow: false
+        property int counter: 0
+
+        onTriggered: {
+            console.log("Count:", counter++)
+            if (firstWindow) {
+                firstWindow = false
+                doubleWindowSwapTestCanvas.parent = doubleWindowSwapTestWindow2.canvasArea
+            } else {
+                firstWindow = true
+                doubleWindowSwapTestCanvas.parent = doubleWindowSwapTestWindow1.canvasArea
+            }
+        }
+    }
+
+    function createWindow() {
+        if (windowComponent === null)
+            windowComponent = Qt.createComponent("canvaswindow.qml")
+        var window = windowComponent.createObject(null)
+        window.setManager(mainview)
+        windowList[windowCount] = window
+        windowCount++
+        window.x = windowCount * 20
+        window.y = windowCount * 20
+        return window
+    }
+
+    function removeWindow(win) {
+        var found = false
+        for (var i = 0; i < windowCount; i++) {
+            if (windowList[i] === win) {
+                found = true
+                windowCount--
+            }
+            if (found) {
+                if (i < windowCount)
+                    windowList[i] = windowList[i + 1]
+                else
+                    windowList[i] = null
+            }
+        }
+    }
+
+    function deleteCanvas(canvas) {
+        var found = false
+        for (var i = 0; i < canvasCount; i++) {
+            if (canvasList[i] === canvas) {
+                found = true
+                canvasCount--
+                console.log("Canvas deleted: ", canvasList[i].canvasName)
+                canvasList[i].destroy()
+            }
+            if (found) {
+                if (i < canvasCount)
+                    canvasList[i] = canvasList[i + 1]
+                else
+                    canvasList[i] = null
+            }
+        }
+    }
+
+    function chooseRandomCanvas() {
+        if (canvasCount > 0) {
+            var index = Math.floor((Math.random() * canvasCount))
+            console.log("Random canvas selected: ", canvasList[index].canvasName)
+            return canvasList[index]
+        } else {
+            return null
+        }
+    }
+
+    function setupCanvas(canvas, canvasArea, canvasName) {
+        canvas.parent = canvasArea
+        canvasList[canvasCount] = canvas
+        canvasCount++
+        canvas.canvasName = canvasName
+        console.log("Created ", canvas.canvasName)
+        if (renderTarget === "Offscreen") {
+            canvas.canvas3d.renderTarget = Canvas3D.RenderTargetOffscreenBuffer
+        } else if (renderTarget === "Background") {
+            canvas.color = "transparent"
+            canvas.canvas3d.renderTarget = Canvas3D.RenderTargetBackground
+        } else {
+            canvas.canvas3d.renderTarget = Canvas3D.RenderTargetForeground
+        }
+        return canvas
+    }
+
+    function createFboCanvas(canvasArea) {
+        if (fboCanvasComponent === null)
+            fboCanvasComponent = Qt.createComponent("framebuffer.qml")
+        return setupCanvas(fboCanvasComponent.createObject(null), canvasArea,
+                           "FBO canvas " + canvasCount)
+    }
+
+    function createItemTextureCanvas(canvasArea) {
+        if (itemTextureCanvasComponent === null)
+            itemTextureCanvasComponent = Qt.createComponent("quickitemtexture.qml")
+        return setupCanvas(itemTextureCanvasComponent.createObject(null), canvasArea,
+                           "QuickItem canvas " + canvasCount)
+    }
+}
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/multiwindowtest.js b/tests/manual/multiwindowtest/qml/multiwindowtest/multiwindowtest.js
new file mode 100644
index 0000000000000000000000000000000000000000..8433ec2dda5c21356409f46f060d51d2c47ac68f
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/multiwindowtest.js
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+Qt.include("../../../examples/canvas3d/canvas3d/3rdparty/gl-matrix.js")
+
+//
+// Draws a cube that has the Qt logo as decal texture on each face in to a texture.
+// That texture is used as the texture for drawing another cube on the screen.
+//
+
+var gl;
+
+var cubeTexture = 0;
+
+var vertexPositionAttribute;
+var vertexNormalAttribute;
+var vertexColorAttribute;
+var mvMatrix = mat4.create();
+var pMatrix  = mat4.create();
+var nMatrix  = mat4.create();
+var pMatrixUniform;
+var mvMatrixUniform;
+var nUniform;
+
+var canvas3d;
+var isLogEnabled = false;
+
+function log(message) {
+    if (isLogEnabled)
+        console.log(message)
+}
+
+function initializeGL(canvas, textureLoader) {
+    canvas3d = canvas
+    try {
+        // Get the OpenGL context object that represents the API we call
+        gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+
+        // Setup the OpenGL state
+        gl.enable(gl.DEPTH_TEST);
+        gl.enable(gl.CULL_FACE);
+        gl.cullFace(gl.BACK);
+        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+
+        // Initialize the shader program
+        initShaders();
+
+        // Initialize vertex and color buffers
+        initBuffers();
+
+    } catch(e) {
+        console.log("initializeGL FAILURE!");
+        console.log(""+e);
+        console.log(""+e.message);
+    }
+}
+
+function degToRad(degrees) {
+    return degrees * Math.PI / 180;
+}
+
+function paintGL(canvas) {
+    gl.viewport(0, 0,
+                canvas.width * canvas.devicePixelRatio,
+                canvas.height * canvas.devicePixelRatio);
+
+    gl.clearColor(0.0, 0.0, 0.0, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+    var xStart = -6.0;
+    var yStart = -3.2;
+    var zStart = -15.0;
+    var xRange = 12.0;
+    var yRange = 7.0;
+    var zRange = 5.0;
+
+    for (var count = 0; count < canvas3d.itemCount; count++) {
+        // Calculate and set matrix uniforms
+        mat4.perspective(pMatrix, degToRad(45), canvas.width / canvas.height, 0.1, 100.0);
+        gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix);
+
+        mat4.identity(mvMatrix);
+        var xFrac = ((count % 200) / 200);
+        var yFrac = count / canvas3d.maxCount;
+        var zFrac = ((count % 12) / 12);
+        mat4.translate(mvMatrix, mvMatrix, [xStart + (xFrac * xRange),
+                                            yStart + (yFrac * yRange),
+                                            zStart + (zFrac * zRange)]);
+        mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.xRotSlider), [0, 1, 0]);
+        mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.yRotSlider), [1, 0, 0]);
+        mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.zRotSlider), [0, 0, 1]);
+        mat4.scale(mvMatrix, mvMatrix, [0.15, 0.15, 0.15])
+        gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix);
+
+        mat4.invert(nMatrix, mvMatrix);
+        mat4.transpose(nMatrix, nMatrix);
+        gl.uniformMatrix4fv(nUniform, false, nMatrix);
+
+        // Draw the on-screen cube
+        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
+    }
+}
+
+function onCanvasResize(canvas)
+{
+    var pixelRatio = canvas.devicePixelRatio;
+    canvas.pixelSize = Qt.size(canvas.width * pixelRatio,
+                               canvas.height * pixelRatio);
+}
+
+function initBuffers()
+{
+    log("        cubeVertexPositionBuffer");
+    var cubeVertexPositionBuffer = gl.createBuffer();
+    cubeVertexPositionBuffer.name = "cubeVertexPositionBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
+    gl.bufferData(
+                gl.ARRAY_BUFFER,
+                new Float32Array([// Front face
+                                        -1.0, -1.0,  1.0,
+                                        1.0, -1.0,  1.0,
+                                        1.0,  1.0,  1.0,
+                                        -1.0,  1.0,  1.0,
+
+                                        // Back face
+                                        -1.0, -1.0, -1.0,
+                                        -1.0,  1.0, -1.0,
+                                        1.0,  1.0, -1.0,
+                                        1.0, -1.0, -1.0,
+
+                                        // Top face
+                                        -1.0,  1.0, -1.0,
+                                        -1.0,  1.0,  1.0,
+                                        1.0,  1.0,  1.0,
+                                        1.0,  1.0, -1.0,
+
+                                        // Bottom face
+                                        -1.0, -1.0, -1.0,
+                                        1.0, -1.0, -1.0,
+                                        1.0, -1.0,  1.0,
+                                        -1.0, -1.0,  1.0,
+
+                                        // Right face
+                                        1.0, -1.0, -1.0,
+                                        1.0,  1.0, -1.0,
+                                        1.0,  1.0,  1.0,
+                                        1.0, -1.0,  1.0,
+
+                                        // Left face
+                                        -1.0, -1.0, -1.0,
+                                        -1.0, -1.0,  1.0,
+                                        -1.0,  1.0,  1.0,
+                                        -1.0,  1.0, -1.0
+                                       ]),
+                gl.STATIC_DRAW);
+    gl.enableVertexAttribArray(vertexPositionAttribute);
+    gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
+
+    log("        cubeVertexIndexBuffer");
+    var cubeVertexIndexBuffer = gl.createBuffer();
+    cubeVertexIndexBuffer.name = "cubeVertexIndexBuffer";
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
+    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
+                  new Uint16Array([
+                                            0,  1,  2,      0,  2,  3,    // front
+                                            4,  5,  6,      4,  6,  7,    // back
+                                            8,  9,  10,     8,  10, 11,   // top
+                                            12, 13, 14,     12, 14, 15,   // bottom
+                                            16, 17, 18,     16, 18, 19,   // right
+                                            20, 21, 22,     20, 22, 23    // left
+                                        ]),
+                  gl.STATIC_DRAW);
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
+
+    var colors = [
+                [0.0,  1.0,  1.0,  1.0],    // Front face: white
+                [1.0,  0.0,  0.0,  1.0],    // Back face: red
+                [0.0,  1.0,  0.0,  1.0],    // Top face: green
+                [0.0,  0.0,  1.0,  1.0],    // Bottom face: blue
+                [1.0,  1.0,  0.0,  1.0],    // Right face: yellow
+                [1.0,  0.0,  1.0,  1.0]     // Left face: purple
+            ];
+
+    var generatedColors = [];
+    for (var j = 0; j < 6; j++) {
+        var c = colors[j];
+
+        for (var i = 0; i < 4; i++) {
+            generatedColors = generatedColors.concat(c);
+        }
+    }
+    log("        cubeVertexColorBuffer");
+    var cubeVertexColorBuffer = gl.createBuffer();
+    cubeVertexColorBuffer.name = "cubeVertexColorBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);
+    gl.enableVertexAttribArray(vertexColorAttribute);
+    gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
+
+    log("        cubeVerticesTextureCoordBuffer");
+    var cubeVerticesTextureCoordBuffer = gl.createBuffer();
+    cubeVerticesTextureCoordBuffer.name = "cubeVerticesTextureCoordBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
+    var textureCoordinates = [
+                // Front
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Back
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Top
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Bottom
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Right
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Left
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0
+            ];
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
+                  gl.STATIC_DRAW);
+
+    var cubeVerticesNormalBuffer = gl.createBuffer();
+    cubeVerticesNormalBuffer.name = "cubeVerticesNormalBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+                                                              // Front
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+
+                                                              // Back
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+
+                                                              // Top
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+
+                                                              // Bottom
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+
+                                                              // Right
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+
+                                                              // Left
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0
+                                                          ]), gl.STATIC_DRAW);
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+    gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
+}
+
+function initShaders()
+{
+    var vertexShader = getShader(gl,
+                                 "attribute highp vec3 aVertexNormal;   \
+                                  attribute highp vec3 aVertexPosition; \
+                                  attribute mediump vec4 aVertexColor;  \
+                                  attribute highp vec2 aTextureCoord;   \
+
+                                  uniform highp mat4 uNormalMatrix;     \
+                                  uniform mat4 uMVMatrix;               \
+                                  uniform mat4 uPMatrix;                \
+
+                                  varying mediump vec4 vColor;          \
+                                  varying highp vec3 vLighting;         \
+
+                                  void main(void) {                     \
+                                      gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);                      \
+                                      vColor = aVertexColor;                                                                \
+                                      highp vec3 ambientLight = vec3(0.5, 0.5, 0.5);                                        \
+                                      highp vec3 directionalLightColor = vec3(0.15, 0.15, 0.15);                             \
+                                      highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);                                 \
+                                      highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);              \
+                                      highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);    \
+                                      vLighting = ambientLight + (directionalLightColor * directional);                     \
+                                  }", gl.VERTEX_SHADER);
+    var fragmentShader = getShader(gl,
+                                   "varying mediump vec4 vColor;        \
+                                    varying highp vec3 vLighting;       \
+
+                                    void main(void) {                   \
+                                        gl_FragColor = vec4(vColor.rgb * vLighting, 1.0);                                       \
+                                    }", gl.FRAGMENT_SHADER);
+
+    var shaderProgram = gl.createProgram();
+    gl.attachShader(shaderProgram, vertexShader);
+    gl.attachShader(shaderProgram, fragmentShader);
+    gl.linkProgram(shaderProgram);
+
+    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+        console.log("Could not initialize shaders");
+        console.log(gl.getProgramInfoLog(shaderProgram));
+    }
+
+    gl.useProgram(shaderProgram);
+
+    // look up where the vertex data needs to go.
+    vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
+    gl.enableVertexAttribArray(vertexPositionAttribute);
+    vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
+    gl.enableVertexAttribArray(vertexColorAttribute);
+    vertexNormalAttribute =gl.getAttribLocation(shaderProgram, "aVertexNormal");
+    gl.enableVertexAttribArray(vertexNormalAttribute);
+
+    pMatrixUniform  = gl.getUniformLocation(shaderProgram, "uPMatrix");
+    mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
+    nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
+}
+
+function getShader(gl, str, type) {
+    var shader = gl.createShader(type);
+    gl.shaderSource(shader, str);
+    gl.compileShader(shader);
+
+    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+        console.log("JS:Shader compile failed");
+        console.log(gl.getShaderInfoLog(shader));
+        return null;
+    }
+
+    return shader;
+}
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/qtlogo.png b/tests/manual/multiwindowtest/qml/multiwindowtest/qtlogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..f7276e2b9925b3a732bc0e28d1e1b36c7b6ed30b
Binary files /dev/null and b/tests/manual/multiwindowtest/qml/multiwindowtest/qtlogo.png differ
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/quickitemtexture.js b/tests/manual/multiwindowtest/qml/multiwindowtest/quickitemtexture.js
new file mode 100644
index 0000000000000000000000000000000000000000..308350454f1f3809d7a05cc31e3abd7bd603cb36
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/quickitemtexture.js
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+Qt.include("gl-matrix.js")
+
+//
+// Draws a cube that has a Qt Quick item as decal texture on each face.
+// A simple per vertex lighting equation is used to emulate light landing on the rotating cube.
+//
+
+var gl;
+var cubeTexture = 0;
+var vertexPositionAttribute;
+var textureCoordAttribute;
+var vertexNormalAttribute;
+var mvMatrix = mat4.create();
+var pMatrix  = mat4.create();
+var nMatrix  = mat4.create();
+var pMatrixUniform;
+var mvMatrixUniform;
+var nUniform;
+var width = 0;
+var height = 0;
+var canvas3d;
+var pixelSize;
+var canvasTextureProvider = null;
+var textureSourceItem = null;
+
+function initializeGL(canvas, textureSource) {
+    width = 0;
+    height = 0;
+    canvas3d = canvas;
+    textureSourceItem = textureSource;
+
+    // Get the OpenGL context object that represents the API we call
+    gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
+
+    // Setup the OpenGL state
+    gl.enable(gl.DEPTH_TEST);
+    gl.depthFunc(gl.LESS);
+    gl.enable(gl.CULL_FACE);
+    gl.cullFace(gl.BACK);
+    gl.clearDepth(1.0);
+    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
+
+    // Use fully transparent clear color to allow other QML components to be seen through
+    // Canvas3D where not obscured by the rotating cube.
+    gl.clearColor(0.0, 0.0, 0.0, 0.0);
+
+    // Set viewport
+    gl.viewport(0, 0, canvas.width, canvas.height);
+
+    // Initialize the shader program
+    initShaders();
+
+    // Initialize vertex and color buffers
+    initBuffers();
+
+    // Create a texture from the Qt Quick item
+    canvasTextureProvider = gl.getExtension("QTCANVAS3D_texture_provider");
+    cubeTexture = canvasTextureProvider.createTextureFromSource(textureSourceItem);
+}
+
+function resizeGL(canvas)
+{
+    var pixelRatio = canvas.devicePixelRatio;
+    canvas.pixelSize = Qt.size(canvas.width * pixelRatio,
+                               canvas.height * pixelRatio);
+}
+
+function degToRad(degrees) {
+    return degrees * Math.PI / 180;
+}
+
+function paintGL(canvas, clear) {
+    gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
+    var pixelRatio = canvas.devicePixelRatio;
+    var currentWidth = canvas.width * pixelRatio;
+    var currentHeight = canvas.height * pixelRatio;
+    if (currentWidth !== width || currentHeight !== height ) {
+        width = currentWidth;
+        height = currentHeight;
+        gl.viewport(0, 0, width, height);
+        mat4.perspective(pMatrix, degToRad(45), width / height, 0.1, 500.0);
+        gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix);
+    }
+
+    if (clear)
+        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+    mat4.identity(mvMatrix);
+    mat4.translate(mvMatrix, mvMatrix, [(canvas.yRotAnim - 120.0) / 120.0,
+                                        (canvas.xRotAnim -  60.0) / 50.0,
+                                        -7.0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.xRotAnim), [0, 1, 0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.yRotAnim), [1, 0, 0]);
+    mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.zRotAnim), [0, 0, 1]);
+    gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix);
+
+    mat4.invert(nMatrix, mvMatrix);
+    mat4.transpose(nMatrix, nMatrix);
+    gl.uniformMatrix4fv(nUniform, false, nMatrix);
+
+    gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
+}
+
+function initBuffers()
+{
+    var cubeVertexPositionBuffer = gl.createBuffer();
+    cubeVertexPositionBuffer.name = "cubeVertexPositionBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
+    gl.bufferData(
+                gl.ARRAY_BUFFER,
+                new Float32Array([// Front face
+                                        -1.0, -1.0,  1.0,
+                                        1.0, -1.0,  1.0,
+                                        1.0,  1.0,  1.0,
+                                        -1.0,  1.0,  1.0,
+
+                                        // Back face
+                                        -1.0, -1.0, -1.0,
+                                        -1.0,  1.0, -1.0,
+                                        1.0,  1.0, -1.0,
+                                        1.0, -1.0, -1.0,
+
+                                        // Top face
+                                        -1.0,  1.0, -1.0,
+                                        -1.0,  1.0,  1.0,
+                                        1.0,  1.0,  1.0,
+                                        1.0,  1.0, -1.0,
+
+                                        // Bottom face
+                                        -1.0, -1.0, -1.0,
+                                        1.0, -1.0, -1.0,
+                                        1.0, -1.0,  1.0,
+                                        -1.0, -1.0,  1.0,
+
+                                        // Right face
+                                        1.0, -1.0, -1.0,
+                                        1.0,  1.0, -1.0,
+                                        1.0,  1.0,  1.0,
+                                        1.0, -1.0,  1.0,
+
+                                        // Left face
+                                        -1.0, -1.0, -1.0,
+                                        -1.0, -1.0,  1.0,
+                                        -1.0,  1.0,  1.0,
+                                        -1.0,  1.0, -1.0
+                                       ]),
+                gl.STATIC_DRAW);
+    gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
+
+    var cubeVertexIndexBuffer = gl.createBuffer();
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
+    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
+                  new Uint16Array([
+                                            0,  1,  2,      0,  2,  3,    // front
+                                            4,  5,  6,      4,  6,  7,    // back
+                                            8,  9,  10,     8,  10, 11,   // top
+                                            12, 13, 14,     12, 14, 15,   // bottom
+                                            16, 17, 18,     16, 18, 19,   // right
+                                            20, 21, 22,     20, 22, 23    // left
+                                        ]),
+                  gl.STATIC_DRAW);
+
+    var cubeVerticesTextureCoordBuffer = gl.createBuffer();
+    cubeVerticesTextureCoordBuffer.name = "cubeVerticesTextureCoordBuffer";
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
+    var textureCoordinates = [
+                // Front
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Back
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Top
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Bottom
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Right
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0,
+                // Left
+                1.0,  0.0,
+                0.0,  0.0,
+                0.0,  1.0,
+                1.0,  1.0
+            ];
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
+                  gl.STATIC_DRAW);
+    gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
+
+    var cubeVerticesNormalBuffer = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+                                                              // Front
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+                                                              0.0,  0.0,  1.0,
+
+                                                              // Back
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+                                                              0.0,  0.0, -1.0,
+
+                                                              // Top
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+                                                              0.0,  1.0,  0.0,
+
+                                                              // Bottom
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+                                                              0.0, -1.0,  0.0,
+
+                                                              // Right
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+                                                              1.0,  0.0,  0.0,
+
+                                                              // Left
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0,
+                                                              -1.0,  0.0,  0.0
+                                                          ]), gl.STATIC_DRAW);
+    gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
+}
+
+function initShaders()
+{
+    var vertexShader = getShader(gl,
+                                 "attribute highp vec3 aVertexNormal;    \
+                                  attribute highp vec3 aVertexPosition;  \
+                                  attribute highp vec2 aTextureCoord;    \
+                                                                         \
+                                  uniform highp mat4 uNormalMatrix;      \
+                                  uniform mat4 uMVMatrix;                \
+                                  uniform mat4 uPMatrix;                 \
+                                                                         \
+                                  varying mediump vec4 vColor;           \
+                                  varying highp vec2 vTextureCoord;      \
+                                  varying highp vec3 vLighting;          \
+                                                                         \
+                                  void main(void) {                      \
+                                      gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); \
+                                      vTextureCoord = aTextureCoord;                                   \
+                                      highp vec3 ambientLight = vec3(0.5, 0.5, 0.5);                   \
+                                      highp vec3 directionalLightColor = vec3(0.75, 0.75, 0.75);       \
+                                      highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);            \
+                                      highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); \
+                                      highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); \
+                                      vLighting = ambientLight + (directionalLightColor * directional); \
+                                  }", gl.VERTEX_SHADER);
+
+    var fragmentShader = getShader(gl,
+                                   "varying highp vec2 vTextureCoord;  \
+                                    varying highp vec3 vLighting;      \
+                                                                       \
+                                    uniform sampler2D uSampler;        \
+                                                                       \
+                                    void main(void) {                  \
+                                        mediump vec3 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)).rgb; \
+                                        gl_FragColor = vec4(texelColor * vLighting, 1.0);                                      \
+                                    }", gl.FRAGMENT_SHADER);
+
+    // Create the Program3D for shader
+    var shaderProgram = gl.createProgram();
+
+    // Attach the shader sources to the shader program
+    gl.attachShader(shaderProgram, vertexShader);
+    gl.attachShader(shaderProgram, fragmentShader);
+
+    // Link the program
+    gl.linkProgram(shaderProgram);
+
+    // Check the linking status
+    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+        console.log("Could not initialize shaders");
+        console.log(gl.getProgramInfoLog(shaderProgram));
+    }
+
+    // Take the shader program into use
+    gl.useProgram(shaderProgram);
+
+    // Look up where the vertex data needs to go
+    vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
+    gl.enableVertexAttribArray(vertexPositionAttribute);
+    textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
+    gl.enableVertexAttribArray(textureCoordAttribute);
+    vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
+    gl.enableVertexAttribArray(vertexNormalAttribute);
+
+    // Get the uniform locations
+    pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
+    mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
+    nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
+
+    // Setup texture sampler uniform
+    var textureSamplerUniform = gl.getUniformLocation(shaderProgram, "uSampler")
+    gl.activeTexture(gl.TEXTURE0);
+    gl.uniform1i(textureSamplerUniform, 0);
+    gl.bindTexture(gl.TEXTURE_2D, 0);
+}
+
+function getShader(gl, str, type) {
+    var shader = gl.createShader(type);
+    gl.shaderSource(shader, str);
+    gl.compileShader(shader);
+
+    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+        console.log("JS:Shader compile failed");
+        console.log(gl.getShaderInfoLog(shader));
+        return null;
+    }
+
+    return shader;
+}
diff --git a/tests/manual/multiwindowtest/qml/multiwindowtest/quickitemtexture.qml b/tests/manual/multiwindowtest/qml/multiwindowtest/quickitemtexture.qml
new file mode 100644
index 0000000000000000000000000000000000000000..505fe5ca01f05d96e987411992bc93dccfd031b5
--- /dev/null
+++ b/tests/manual/multiwindowtest/qml/multiwindowtest/quickitemtexture.qml
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtCanvas3D 1.1
+import QtQuick.Controls 1.1
+import QtQuick.Layouts 1.1
+import QtQuick.Window 2.2
+
+import "quickitemtexture.js" as GLCode
+
+Rectangle {
+    id: mainView
+    anchors.fill: parent
+    visible: true
+    color: "#f9f9f9"
+
+    property alias canvas3d: canvas3d
+    property string canvasName: ""
+    property var previousParent: null
+
+    onParentChanged: {
+        if (previousParent && previousParent.handleParentChange)
+            previousParent.handleParentChange()
+        previousParent = parent
+    }
+
+    ColumnLayout {
+        Layout.fillWidth: true
+        x: 4
+        y: 4
+        Rectangle {
+            id: textureSource
+            color: "lightgreen"
+            width: 256
+            height: 256
+            border.color: "blue"
+            border.width: 4
+            layer.enabled: true
+            layer.smooth: true
+            Label {
+                anchors.fill: parent
+                anchors.margins: 16
+                text: "X Rot:" + (canvas3d.xRotAnim | 0) + "\n"
+                    + "Y Rot:" + (canvas3d.yRotAnim | 0) + "\n"
+                    + "Z Rot:" + (canvas3d.zRotAnim | 0) + "\n"
+                    + "FPS:" + canvas3d.fps
+                color: "red"
+                font.pointSize: 30
+                horizontalAlignment: Text.AlignLeft
+                verticalAlignment: Text.AlignVCenter
+            }
+        }
+        Button {
+            Layout.fillWidth: true
+            Layout.minimumWidth: 256
+            text: textureSource.visible ? "Hide texture source" : "Show texture source"
+            onClicked: textureSource.visible = !textureSource.visible
+        }
+        Button {
+            Layout.fillWidth: true
+            Layout.minimumWidth: 256
+            text: "Quit"
+            onClicked: Qt.quit()
+        }
+    }
+
+    Canvas3D {
+        id: canvas3d
+        anchors.fill:parent
+        focus: true
+        property double xRotAnim: 0
+        property double yRotAnim: 0
+        property double zRotAnim: 0
+        property bool isRunning: true
+
+        // Emitted when one time initializations should happen
+        onInitializeGL: {
+            GLCode.initializeGL(canvas3d, textureSource);
+        }
+
+        // Emitted each time Canvas3D is ready for a new frame
+        onPaintGL: {
+            if (canvas3d.renderTarget === Canvas3D.RenderTargetOffscreenBuffer)
+                GLCode.paintGL(canvas3d, true);
+            else
+                GLCode.paintGL(canvas3d, false);
+        }
+
+        onResizeGL: {
+            GLCode.resizeGL(canvas3d);
+        }
+
+        onContextLost: {
+            console.log("Context lost on: ", mainView.canvasName)
+        }
+
+        onContextRestored: {
+            console.log("Context restored on: ", mainView.canvasName)
+        }
+
+        Keys.onSpacePressed: {
+            canvas3d.isRunning = !canvas3d.isRunning
+            if (canvas3d.isRunning) {
+                objAnimationX.pause();
+                objAnimationY.pause();
+                objAnimationZ.pause();
+            } else {
+                objAnimationX.resume();
+                objAnimationY.resume();
+                objAnimationZ.resume();
+            }
+        }
+
+        SequentialAnimation {
+            id: objAnimationX
+            loops: Animation.Infinite
+            running: true
+            NumberAnimation {
+                target: canvas3d
+                property: "xRotAnim"
+                from: 0.0
+                to: 120.0
+                duration: 7000
+                easing.type: Easing.InOutQuad
+            }
+            NumberAnimation {
+                target: canvas3d
+                property: "xRotAnim"
+                from: 120.0
+                to: 0.0
+                duration: 7000
+                easing.type: Easing.InOutQuad
+            }
+        }
+
+        SequentialAnimation {
+            id: objAnimationY
+            loops: Animation.Infinite
+            running: true
+            NumberAnimation {
+                target: canvas3d
+                property: "yRotAnim"
+                from: 0.0
+                to: 240.0
+                duration: 5000
+                easing.type: Easing.InOutCubic
+            }
+            NumberAnimation {
+                target: canvas3d
+                property: "yRotAnim"
+                from: 240.0
+                to: 0.0
+                duration: 5000
+                easing.type: Easing.InOutCubic
+            }
+        }
+
+        SequentialAnimation {
+            id: objAnimationZ
+            loops: Animation.Infinite
+            running: true
+            NumberAnimation {
+                target: canvas3d
+                property: "zRotAnim"
+                from: -100.0
+                to: 100.0
+                duration: 3000
+                easing.type: Easing.InOutSine
+            }
+            NumberAnimation {
+                target: canvas3d
+                property: "zRotAnim"
+                from: 100.0
+                to: -100.0
+                duration: 3000
+                easing.type: Easing.InOutSine
+            }
+        }
+    }
+}