From 12bbbdb904d047209fd8b175b87924db037b21d8 Mon Sep 17 00:00:00 2001
From: Pasi Keranen <pasi.keranen@digia.com>
Date: Wed, 12 Nov 2014 14:42:44 +0200
Subject: [PATCH] Fixed crash of jsonmodels example on OS X.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Added QTCANVAS3D_gl_state_dump extension for dumping the current GL state for debugging purposes and added example use to jsonmodels example.
Fixed documentation issues and removed invalid comments related to render thread.

Change-Id: Ib4a81370bf2673a5bfb412038148646638f723a9
Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
Reviewed-by: Pasi Keränen <pasi.keranen@digia.com>
---
 examples/canvas3d/jsonmodels/jsonmodels.js  |  19 +-
 examples/canvas3d/jsonmodels/jsonmodels.qml |   1 +
 src/canvas3d.cpp                            |   1 -
 src/canvas3dcommon_p.h                      |   2 +
 src/canvasglstatedump.cpp                   | 423 ++++++++++++++++++++
 src/canvasglstatedump_p.h                   |  91 +++++
 src/context3d.cpp                           |  99 +++--
 src/context3d_p.h                           |   7 +-
 src/contextattributes.cpp                   |   2 +-
 src/plugins.qmltypes                        |  40 +-
 src/qcanvas3d_plugin.cpp                    |   6 +
 src/qcanvas3d_plugin.h                      |   3 +
 src/src.pro                                 |   6 +-
 src/texture3d.cpp                           |   6 +-
 src/texture3d_p.h                           |   4 +-
 15 files changed, 656 insertions(+), 54 deletions(-)
 create mode 100644 src/canvasglstatedump.cpp
 create mode 100644 src/canvasglstatedump_p.h

diff --git a/examples/canvas3d/jsonmodels/jsonmodels.js b/examples/canvas3d/jsonmodels/jsonmodels.js
index 0e6c427..42a302c 100644
--- a/examples/canvas3d/jsonmodels/jsonmodels.js
+++ b/examples/canvas3d/jsonmodels/jsonmodels.js
@@ -74,6 +74,7 @@ var modelTwo = new Model();
 var modelThree = new Model();
 var modelFour = new Model();
 var modelFive = new Model();
+var stateDumpExt;
 
 function initGL(canvas) {
     canvas3d = canvas
@@ -82,6 +83,12 @@ function initGL(canvas) {
         gl = canvas.getContext("canvas3d", {depth:true, antialias:true});
         log("   Received context: "+gl);
 
+        stateDumpExt = gl.getExtension("QTCANVAS3D_gl_state_dump");
+        if (stateDumpExt)
+            log("QTCANVAS3D_gl_state_dump extension found");
+        else
+            log("QTCANVAS3D_gl_state_dump extension NOT found");
+
         var contextConfig = gl.getContextAttributes();
         log("   Depth: "+contextConfig.alpha);
         log("   Stencil: "+contextConfig.stencil);
@@ -95,7 +102,7 @@ function initGL(canvas) {
         gl.enable(gl.DEPTH_TEST);
         gl.disable(gl.CULL_FACE);
         gl.enable(gl.BLEND);
-        gl.enable(gl.DEPTH_WRITE);
+        gl.enable(gl.DEPTH_TEST);
         gl.depthMask(true);
         gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
 
@@ -181,6 +188,7 @@ function renderGL(canvas) {
         // Calculate the modelview matrix
         mat4.identity(mMatrix);
         mat4.translate(mMatrix, mMatrix, posOne);
+
         // Calculate normal matrix before scaling, to keep lighting in order
         // Scale normal matrix with distance instead
         mat4.copy(nMatrix, mMatrix);
@@ -188,6 +196,7 @@ function renderGL(canvas) {
         mat4.invert(nMatrix, nMatrix);
         mat4.transpose(nMatrix, nMatrix);
         gl.uniformMatrix4fva(nMatrixUniform, false, nMatrix);
+
         // Scale the modelview matrix, and apply the matrix
         mat4.scale(mMatrix, mMatrix, [canvas.itemSize, canvas.itemSize, canvas.itemSize]);
         mat4.multiply(mvMatrix, vMatrix, mMatrix);
@@ -195,6 +204,10 @@ function renderGL(canvas) {
 
         // Draw the model
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, modelOne.indexVBO);
+
+        if (stateDumpExt)
+            log("GL STATE DUMP:\n"+stateDumpExt.getGLStateDump(stateDumpExt.DUMP_FULL));
+
         gl.drawElements(drawMode, modelOne.count, gl.UNSIGNED_SHORT, 0);
 
         // Calculate the modelview matrix
@@ -559,8 +572,10 @@ function fillModel(modelData, model) {
     gl.bufferData(gl.ARRAY_BUFFER,
                   Arrays.newFloat32Array(modelData.vertices),
                   gl.STATIC_DRAW);
-
     log("   "+model.normalsVBO.name);
+    if (stateDumpExt)
+        log("GL STATE DUMP:\n"+stateDumpExt.getGLStateDump(stateDumpExt.DUMP_VERTEX_ATTRIB_ARRAYS_BIT || stateDumpExt.DUMP_VERTEX_ATTRIB_ARRAYS_CONTENTS_BIT));
+
     gl.bindBuffer(gl.ARRAY_BUFFER, model.normalsVBO);
     gl.bufferData(gl.ARRAY_BUFFER,
                   Arrays.newFloat32Array(modelData.normals),
diff --git a/examples/canvas3d/jsonmodels/jsonmodels.qml b/examples/canvas3d/jsonmodels/jsonmodels.qml
index cdba0db..b7cb536 100644
--- a/examples/canvas3d/jsonmodels/jsonmodels.qml
+++ b/examples/canvas3d/jsonmodels/jsonmodels.qml
@@ -24,6 +24,7 @@ Window {
             Layout.fillWidth: true
             Canvas3D {
                 id: canvas3d
+
                 Layout.fillHeight: true
                 Layout.fillWidth: true
                 //! [1]
diff --git a/src/canvas3d.cpp b/src/canvas3d.cpp
index 5937bac..f03d122 100644
--- a/src/canvas3d.cpp
+++ b/src/canvas3d.cpp
@@ -678,7 +678,6 @@ void Canvas::renderNext()
 
     // Rebind default FBO
     QOpenGLFramebufferObject::bindDefault();
-    m_glContext->doneCurrent();
 
     // Notify the render node of new texture
     emit textureReady(m_displayFbo->texture(), m_initialisedSize, m_devicePixelRatio);
diff --git a/src/canvas3dcommon_p.h b/src/canvas3dcommon_p.h
index 76c85bf..4516de1 100644
--- a/src/canvas3dcommon_p.h
+++ b/src/canvas3dcommon_p.h
@@ -51,4 +51,6 @@
 
 #define VERBOSE_ALL_TYPED_ARRAY_CALLS false
 
+#define QT_CANVAS3D_GL_STATE_DUMP_EXT_NAME "QTCANVAS3D_gl_state_dump"
+
 #endif // CANVAS3DCOMMON_P_H
diff --git a/src/canvasglstatedump.cpp b/src/canvasglstatedump.cpp
new file mode 100644
index 0000000..7f7c9aa
--- /dev/null
+++ b/src/canvasglstatedump.cpp
@@ -0,0 +1,423 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 "canvasglstatedump_p.h"
+#include "enumtostringmap_p.h"
+
+#include <QDebug>
+#include <QColor>
+
+#define BOOL_TO_STR(a) a ? "true" : "false"
+
+/*!
+   \qmltype GLStateDumpExt
+   \since QtCanvas3D 1.0
+   \ingroup qtcanvas3d-qml-types
+   \brief Provides means to print current GL driver state info.
+
+   An uncreatable QML type that provides an extension API that can be used dump current OpenGL
+   driver state as string that can be then e.g. printed on console log. You can get it by
+   calling \l{Context3D::getExtension}{Context3D.getExtension} with "QTCANVAS3D_gl_state_dump"
+   as parameter.
+
+   Typical usage could be something like this:
+   \code
+    // Declare the variable to contain the extension
+    var stateDumpExt;
+    .
+    .
+    // After context has been created from Canvas3D get the extension
+    stateDumpExt = gl.getExtension("QTCANVAS3D_gl_state_dump");
+    .
+    .
+    // When you want to print the current GL state with everything enabled
+    // Check that you indeed have a valid extension (for portability) then use it
+    if (stateDumpExt)
+        log("GL STATE DUMP:\n"+stateDumpExt.getGLStateDump(stateDumpExt.DUMP_FULL));
+    \endcode
+
+   \sa Context3D
+ */
+CanvasGLStateDump::CanvasGLStateDump(QOpenGLContext *context, QObject *parent) :
+    QObject(parent),
+    QOpenGLFunctions(context),
+    m_map(EnumToStringMap::newInstance())
+{
+    m_isOpenGLES2 = context->isOpenGLES();
+    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &m_maxVertexAttribs);
+}
+
+/*!
+ * \internal
+ */
+CanvasGLStateDump::~CanvasGLStateDump()
+{
+    EnumToStringMap::deleteInstance();
+    m_map = 0;
+}
+
+/*!
+ * \internal
+ */
+QString CanvasGLStateDump::getGLArrayObjectDump(int target, int arrayObject, int type)
+{
+    QString stateDumpStr;
+    glBindBuffer(target, arrayObject);
+
+    GLint size;
+    glGetBufferParameteriv(target, GL_BUFFER_SIZE, &size);
+
+    if (type == GL_FLOAT) {
+        stateDumpStr.append("ARRAY_BUFFER_TYPE......................FLOAT\n");
+
+        stateDumpStr.append("ARRAY_BUFFER_SIZE......................");
+        stateDumpStr.append(QString::number(size));
+        stateDumpStr.append("\n");
+
+    } else if (type == GL_UNSIGNED_SHORT) {
+        stateDumpStr.append("ARRAY_BUFFER_TYPE......................UNSIGNED_SHORT\n");
+
+        stateDumpStr.append("ARRAY_BUFFER_SIZE......................");
+        stateDumpStr.append(QString::number(size));
+        stateDumpStr.append("\n");
+    }
+
+    return stateDumpStr;
+}
+
+/*!
+ * \qmlmethod string GLStateDumpExt::getGLStateDump(stateDumpEnums options)
+ * \return OpenGL driver state with given options as a human readable string that can be printed.
+ * Optional paremeter \a options may contain bitfields masked together from following options:
+ * \list
+ * \li \c{GLStateDumpExt.DUMP_BASIC_ONLY} Includes only very basic OpenGL state information.
+ * \li \c{GLStateDumpExt.DUMP_VERTEX_ATTRIB_ARRAYS_BIT} Includes all vertex attribute array
+ * information.
+ * \li \c{GLStateDumpExt.DUMP_VERTEX_ATTRIB_ARRAYS_BUFFERS_BIT} Includes size and type
+ * from all currently active vertex attribute arrays (including the currently bound element array)
+ * to verify that there are actual values in the array.
+ * \li \c{GLStateDumpExt.DUMP_FULL} Includes everything.
+ * \endlist
+ */
+QString CanvasGLStateDump::getGLStateDump(CanvasGLStateDump::stateDumpEnums options)
+{
+#if !defined(QT_OPENGL_ES_2)
+    GLint drawFramebuffer;
+    GLint readFramebuffer;
+    GLboolean polygonOffsetLineEnabled;
+    GLboolean polygonOffsetPointEnabled;
+    GLint boundVertexArray;
+#endif
+
+    QString stateDumpStr;
+    GLint renderbuffer;
+    GLfloat clearColor[4];
+    GLfloat clearDepth;
+    GLboolean isBlendingEnabled = glIsEnabled(GL_BLEND);
+    GLboolean isDepthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
+    GLint depthFunc;
+    GLboolean isDepthWriteEnabled;
+    GLint currentProgram;
+    GLint *vertexAttribArrayEnabledStates = new GLint[m_maxVertexAttribs];
+    GLint *vertexAttribArrayBoundBuffers = new GLint[m_maxVertexAttribs];
+    GLint *vertexAttribArraySizes = new GLint[m_maxVertexAttribs];
+    GLint *vertexAttribArrayTypes = new GLint[m_maxVertexAttribs];
+    GLint *vertexAttribArrayNormalized = new GLint[m_maxVertexAttribs];
+    GLint *vertexAttribArrayStrides = new GLint[m_maxVertexAttribs];
+    GLint activeTexture;
+    GLint texBinding2D;
+    GLint arrayBufferBinding;
+    GLint frontFace;
+    GLboolean isCullFaceEnabled = glIsEnabled(GL_CULL_FACE);
+    GLint cullFaceMode;
+    GLint blendEquationRGB;
+    GLint blendEquationAlpha;
+
+    GLint blendDestAlpha;
+    GLint blendDestRGB;
+    GLint blendSrcAlpha;
+    GLint blendSrcRGB;
+    GLint scissorBox[4];
+    GLboolean isScissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
+    GLint boundElementArrayBuffer;
+    GLboolean polygonOffsetFillEnabled;
+    GLfloat polygonOffsetFactor;
+    GLfloat polygonOffsetUnits;
+
+#if !defined(QT_OPENGL_ES_2)
+    if (!m_isOpenGLES2) {
+        glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFramebuffer);
+        glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFramebuffer);
+        glGetBooleanv(GL_POLYGON_OFFSET_LINE, &polygonOffsetLineEnabled);
+        glGetBooleanv(GL_POLYGON_OFFSET_POINT, &polygonOffsetPointEnabled);
+        glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &boundVertexArray);
+    }
+#endif
+
+    glGetBooleanv(GL_DEPTH_WRITEMASK, &isDepthWriteEnabled);
+    glGetIntegerv(GL_RENDERBUFFER_BINDING, &renderbuffer);
+    glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
+    glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth);
+    glGetIntegerv(GL_DEPTH_FUNC, &depthFunc);
+    glGetBooleanv(GL_POLYGON_OFFSET_FILL, &polygonOffsetFillEnabled);
+    glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &polygonOffsetFactor);
+    glGetFloatv(GL_POLYGON_OFFSET_UNITS, &polygonOffsetUnits);
+
+    glGetIntegerv(GL_CURRENT_PROGRAM, &currentProgram);
+    glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
+    glGetIntegerv(GL_TEXTURE_BINDING_2D, &texBinding2D );
+    glGetIntegerv(GL_FRONT_FACE, &frontFace);
+    glGetIntegerv(GL_CULL_FACE_MODE, &cullFaceMode);
+    glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRGB);
+    glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha);
+    glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha);
+    glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRGB);
+    glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha);
+    glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRGB);
+    glGetIntegerv(GL_SCISSOR_BOX, scissorBox);
+    glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &boundElementArrayBuffer);
+    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &arrayBufferBinding);
+
+
+#if !defined(QT_OPENGL_ES_2)
+    if (!m_isOpenGLES2) {
+        stateDumpStr.append("GL_DRAW_FRAMEBUFFER_BINDING.....");
+        stateDumpStr.append(QString::number(drawFramebuffer));
+        stateDumpStr.append("\n");
+
+        stateDumpStr.append("GL_READ_FRAMEBUFFER_BINDING.....");
+        stateDumpStr.append(QString::number(readFramebuffer));
+        stateDumpStr.append("\n");
+    }
+#endif
+
+    stateDumpStr.append("GL_RENDERBUFFER_BINDING.........");
+    stateDumpStr.append(QString::number(renderbuffer));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_SCISSOR_TEST.................");
+    stateDumpStr.append(BOOL_TO_STR(isScissorTestEnabled));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_SCISSOR_BOX..................");
+    stateDumpStr.append(QString::number(scissorBox[0]));
+    stateDumpStr.append(", ");
+    stateDumpStr.append(QString::number(scissorBox[1]));
+    stateDumpStr.append(", ");
+    stateDumpStr.append(QString::number(scissorBox[2]));
+    stateDumpStr.append(", ");
+    stateDumpStr.append(QString::number(scissorBox[3]));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_COLOR_CLEAR_VALUE............");
+    stateDumpStr.append("r:");
+    stateDumpStr.append(QString::number(clearColor[0]));
+    stateDumpStr.append(" g:");
+    stateDumpStr.append(QString::number(clearColor[1]));
+    stateDumpStr.append(" b:");
+    stateDumpStr.append(QString::number(clearColor[2]));
+    stateDumpStr.append(" a:");
+    stateDumpStr.append(QString::number(clearColor[3]));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_DEPTH_CLEAR_VALUE............");
+    stateDumpStr.append(QString::number(clearDepth));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_BLEND........................");
+    stateDumpStr.append(BOOL_TO_STR(isBlendingEnabled));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_BLEND_EQUATION_RGB...........");
+    stateDumpStr.append(m_map->lookUp(blendEquationRGB));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_BLEND_EQUATION_ALPHA.........");
+    stateDumpStr.append(m_map->lookUp(blendEquationAlpha));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_DEPTH_TEST...................");
+    stateDumpStr.append(BOOL_TO_STR(isDepthTestEnabled));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_DEPTH_FUNC...................");
+    stateDumpStr.append(m_map->lookUp(depthFunc));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_DEPTH_WRITEMASK..............");
+    stateDumpStr.append(BOOL_TO_STR(isDepthWriteEnabled));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_POLYGON_OFFSET_FILL..........");
+    stateDumpStr.append(BOOL_TO_STR(polygonOffsetFillEnabled));
+    stateDumpStr.append("\n");
+
+#if !defined(QT_OPENGL_ES_2)
+    if (!m_isOpenGLES2) {
+        stateDumpStr.append("GL_POLYGON_OFFSET_LINE..........");
+        stateDumpStr.append(BOOL_TO_STR(polygonOffsetLineEnabled));
+        stateDumpStr.append("\n");
+
+        stateDumpStr.append("GL_POLYGON_OFFSET_POINT.........");
+        stateDumpStr.append(BOOL_TO_STR(polygonOffsetPointEnabled));
+        stateDumpStr.append("\n");
+    }
+#endif
+
+    stateDumpStr.append("GL_POLYGON_OFFSET_FACTOR........");
+    stateDumpStr.append(QString::number(polygonOffsetFactor));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_POLYGON_OFFSET_UNITS.........");
+    stateDumpStr.append(QString::number(polygonOffsetUnits));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_CULL_FACE....................");
+    stateDumpStr.append(BOOL_TO_STR(isCullFaceEnabled));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_CULL_FACE_MODE...............");
+    stateDumpStr.append(m_map->lookUp(cullFaceMode));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_FRONT_FACE...................");
+    stateDumpStr.append(m_map->lookUp(frontFace));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_CURRENT_PROGRAM..............");
+    stateDumpStr.append(QString::number(currentProgram));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_ACTIVE_TEXTURE...............");
+    stateDumpStr.append(m_map->lookUp(activeTexture));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_TEXTURE_BINDING_2D...........");
+    stateDumpStr.append(QString::number(texBinding2D));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_ELEMENT_ARRAY_BUFFER_BINDING.");
+    stateDumpStr.append(QString::number(boundElementArrayBuffer));
+    stateDumpStr.append("\n");
+
+    stateDumpStr.append("GL_ARRAY_BUFFER_BINDING.........");
+    stateDumpStr.append(QString::number(arrayBufferBinding));
+    stateDumpStr.append("\n");
+
+#if !defined(QT_OPENGL_ES_2)
+    if (!m_isOpenGLES2) {
+        stateDumpStr.append("GL_VERTEX_ARRAY_BINDING.........");
+        stateDumpStr.append(QString::number(boundVertexArray));
+        stateDumpStr.append("\n");
+    }
+#endif
+
+    if (options && DUMP_VERTEX_ATTRIB_ARRAYS_BIT) {
+        for (int i = 0; i < m_maxVertexAttribs;i++) {
+            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertexAttribArrayEnabledStates[i]);
+            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vertexAttribArrayBoundBuffers[i]);
+            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &vertexAttribArraySizes[i]);
+            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &vertexAttribArrayTypes[i]);
+            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vertexAttribArrayNormalized[i]);
+            glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &vertexAttribArrayStrides[i]);
+        }
+
+
+        for (int i = 0; i < m_maxVertexAttribs;i++) {
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_");
+            stateDumpStr.append(QString::number(i));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_ENABLED.........");
+            stateDumpStr.append(BOOL_TO_STR(vertexAttribArrayEnabledStates[i]));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING..");
+            stateDumpStr.append(QString::number(vertexAttribArrayBoundBuffers[i]));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_SIZE............");
+            stateDumpStr.append(QString::number(vertexAttribArraySizes[i]));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_TYPE............");
+            stateDumpStr.append(m_map->lookUp(vertexAttribArrayTypes[i]));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_NORMALIZED......");
+            stateDumpStr.append(QString::number(vertexAttribArrayNormalized[i]));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append("GL_VERTEX_ATTRIB_ARRAY_STRIDE..........");
+            stateDumpStr.append(QString::number(vertexAttribArrayStrides[i]));
+            stateDumpStr.append("\n");
+        }
+    }
+
+    if (options && DUMP_VERTEX_ATTRIB_ARRAYS_BUFFERS_BIT) {
+        if (boundElementArrayBuffer != 0) {
+            stateDumpStr.append("GL_ELEMENT_ARRAY_BUFFER................");
+            stateDumpStr.append(QString::number(boundElementArrayBuffer));
+            stateDumpStr.append("\n");
+
+            stateDumpStr.append(getGLArrayObjectDump(GL_ELEMENT_ARRAY_BUFFER,
+                                                     boundElementArrayBuffer,
+                                                     GL_UNSIGNED_SHORT));
+        }
+
+        for (int i = 0; i < m_maxVertexAttribs;i++) {
+            if (vertexAttribArrayEnabledStates[i]) {
+                stateDumpStr.append("GL_ARRAY_BUFFER........................");
+                stateDumpStr.append(QString::number(vertexAttribArrayBoundBuffers[i]));
+                stateDumpStr.append("\n");
+
+                stateDumpStr.append(getGLArrayObjectDump(GL_ARRAY_BUFFER,
+                                                         vertexAttribArrayBoundBuffers[i],
+                                                         vertexAttribArrayTypes[i]));
+            }
+        }
+    }
+
+
+    delete[] vertexAttribArrayEnabledStates;
+    delete[] vertexAttribArrayBoundBuffers;
+    delete[] vertexAttribArraySizes;
+    delete[] vertexAttribArrayTypes;
+    delete[] vertexAttribArrayNormalized;
+    delete[] vertexAttribArrayStrides;
+
+    return stateDumpStr;
+}
diff --git a/src/canvasglstatedump_p.h b/src/canvasglstatedump_p.h
new file mode 100644
index 0000000..70110a7
--- /dev/null
+++ b/src/canvasglstatedump_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCanvas3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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$
+**
+****************************************************************************/
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the QtCanvas3D API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef CANVASGLSTATEDUMP_P_H
+#define CANVASGLSTATEDUMP_P_H
+
+#include "canvas3dcommon_p.h"
+
+#include <QObject>
+#include <QtGui/QOpenGLFunctions>
+
+#define DUMP_ENUM_AS_PROPERTY(A,B,C) Q_PROPERTY(A::B C READ C ## _read); inline A::B C ## _read() { return A::C; }
+
+class EnumToStringMap;
+
+class CanvasGLStateDump : public QObject, protected QOpenGLFunctions
+{
+    Q_OBJECT
+
+    Q_ENUMS(stateDumpEnums)
+
+public:
+    enum stateDumpEnums {
+        DUMP_BASIC_ONLY                        = 0x00,
+        DUMP_VERTEX_ATTRIB_ARRAYS_BIT          = 0x01,
+        DUMP_VERTEX_ATTRIB_ARRAYS_BUFFERS_BIT  = 0x02,
+        DUMP_FULL                              = 0x03
+    };
+
+    DUMP_ENUM_AS_PROPERTY(CanvasGLStateDump,stateDumpEnums,DUMP_BASIC_ONLY)
+    DUMP_ENUM_AS_PROPERTY(CanvasGLStateDump,stateDumpEnums,DUMP_VERTEX_ATTRIB_ARRAYS_BIT)
+    DUMP_ENUM_AS_PROPERTY(CanvasGLStateDump,stateDumpEnums,DUMP_VERTEX_ATTRIB_ARRAYS_BUFFERS_BIT)
+    DUMP_ENUM_AS_PROPERTY(CanvasGLStateDump,stateDumpEnums,DUMP_FULL)
+
+    CanvasGLStateDump(QOpenGLContext *context, QObject *parent = 0);
+    ~CanvasGLStateDump();
+
+    Q_INVOKABLE QString getGLStateDump(stateDumpEnums options = DUMP_BASIC_ONLY);
+
+    QString getGLArrayObjectDump(int target, int arrayObject, int type);
+
+private:
+    GLint m_maxVertexAttribs;
+    EnumToStringMap *m_map;
+    bool m_isOpenGLES2;
+};
+
+#endif // CANVASGLSTATEDUMP_P_H
diff --git a/src/context3d.cpp b/src/context3d.cpp
index 71dd317..2018656 100644
--- a/src/context3d.cpp
+++ b/src/context3d.cpp
@@ -34,6 +34,7 @@
 **
 ****************************************************************************/
 
+#include "canvasglstatedump_p.h"
 #include "activeinfo3d_p.h"
 #include "canvas3d_p.h"
 #include "context3d_p.h"
@@ -75,7 +76,6 @@
  * \sa Canvas3D
  */
 
-// Owned by the SG Render Thread!
 CanvasContext::CanvasContext(QOpenGLContext *context, QSurface *surface,
                              int width, int height, bool isES2, QObject *parent) :
     CanvasAbstractObject(parent),
@@ -96,7 +96,8 @@ CanvasContext::CanvasContext(QOpenGLContext *context, QSurface *surface,
     m_map(EnumToStringMap::newInstance()),
     m_canvas(0),
     m_maxVertexAttribs(0),
-    m_isOpenGLES2(isES2)
+    m_isOpenGLES2(isES2),
+    m_stateDumpExt(0)
 {
     int value = 0;
     glGetIntegerv(MAX_VERTEX_ATTRIBS, &value);
@@ -322,40 +323,6 @@ CanvasShaderPrecisionFormat *CanvasContext::getShaderPrecisionFormat(glEnums sha
     return format;
 }
 
-/*!
- * \qmlmethod list<variant> Context3D::getSupportedExtensions()
- * Returns an array of the extension strings supported by this implementation
- */
-/*!
- * \internal
- */
-QVariantList CanvasContext::getSupportedExtensions()
-{
-    if (m_logAllCalls) qDebug() << Q_FUNC_INFO;
-
-    // No extensions supported at the moment
-    return QVariantList();
-}
-
-/*!
- * \qmlmethod variant Context3D::getExtension(string name)
- * Returns an object if given \a name matches a supported extension.
- * Otherwise returns \c{null}. The returned object may contain constants and/or functions provided
- * by the extension, but at minimum a unique object is returned.
- * \a name is the case-insensitive name of the extension to be returned.
- */
-/*!
- * \internal
- */
-QVariant CanvasContext::getExtension(const QString &name)
-{
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
-                                << "(name:" << name
-                                << ")";
-
-    return QVariant();
-}
-
 /*!
  * \qmlmethod bool Context3D::isContextLost()
  * Always returns false.
@@ -4014,8 +3981,20 @@ void CanvasContext::useProgram(CanvasProgram *program)
  */
 void CanvasContext::clear(glEnums flags)
 {
-    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
-                                << "(flags:" << glEnumToString(flags) << ")";
+    if (m_logAllCalls) {
+        QString flagStr;
+        if (flags && COLOR_BUFFER_BIT != 0)
+            flagStr.append(" COLOR_BUFFER_BIT ");
+
+        if (flags && DEPTH_BUFFER_BIT != 0)
+            flagStr.append(" DEPTH_BUFFER_BIT ");
+
+        if (flags && STENCIL_BUFFER_BIT != 0)
+            flagStr.append(" STENCIL_BUFFER_BIT ");
+
+        qDebug() << "Context3D::" << __FUNCTION__ << "(flags:" << flagStr << ")";
+    }
+
     glClear(flags);
 }
 
@@ -5052,3 +5031,47 @@ QVariant CanvasContext::getUniform(CanvasProgram *program, CanvasUniformLocation
 
     return QVariant();
 }
+
+/*!
+ * \qmlmethod list<variant> Context3D::getSupportedExtensions()
+ * Returns an array of the extension strings supported by this implementation
+ */
+/*!
+ * \internal
+ */
+QVariantList CanvasContext::getSupportedExtensions()
+{
+    if (m_logAllCalls) qDebug() << Q_FUNC_INFO;
+
+    // No extensions supported at the moment
+    QVariantList list;
+    list.append(QVariant::fromValue(QStringLiteral(QT_CANVAS3D_GL_STATE_DUMP_EXT_NAME)));
+    return list;
+}
+
+/*!
+ * \qmlmethod variant Context3D::getExtension(string name)
+ * \return object if given \a name matches a supported extension.
+ * Otherwise returns \c{null}. The returned object may contain constants and/or functions provided
+ * by the extension, but at minimum a unique object is returned.
+ * Case-insensitive \a name of the extension to be returned.
+ */
+/*!
+ * \internal
+ */
+QVariant CanvasContext::getExtension(const QString &name)
+{
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(name:" << name
+                                << ")";
+
+    QString upperCaseName = name.toUpper();
+
+    if (upperCaseName == QStringLiteral(QT_CANVAS3D_GL_STATE_DUMP_EXT_NAME).toUpper()) {
+        if (!m_stateDumpExt)
+            m_stateDumpExt = new CanvasGLStateDump(m_context, this);
+        return QVariant::fromValue(m_stateDumpExt);
+    }
+
+    return QVariant(QVariant::Int);
+}
diff --git a/src/context3d_p.h b/src/context3d_p.h
index a18207b..411dc3d 100644
--- a/src/context3d_p.h
+++ b/src/context3d_p.h
@@ -50,6 +50,7 @@
 #include "canvas3dcommon_p.h"
 #include "contextattributes_p.h"
 #include "abstractobject3d_p.h"
+#include "canvasglstatedump_p.h"
 
 #include <QtGui/QOpenGLFunctions>
 #include <QString>
@@ -95,9 +96,6 @@ class QT_CANVAS3D_EXPORT CanvasContext : public CanvasAbstractObject, protected
     Q_PROPERTY(uint drawingBufferWidth READ drawingBufferWidth NOTIFY drawingBufferWidthChanged)
     Q_PROPERTY(uint drawingBufferHeight READ drawingBufferHeight NOTIFY drawingBufferHeightChanged)
 
-    Q_PROPERTY(bool logAllCalls READ logAllCalls WRITE setLogAllCalls NOTIFY logAllCallsChanged)
-    Q_PROPERTY(bool logAllErrors READ logAllErrors WRITE setLogAllErrors NOTIFY logAllErrorsChanged)
-
 public:
     enum glEnums {
         /* ClearBufferMask */
@@ -1232,6 +1230,9 @@ private:
     uint m_maxVertexAttribs;
     float **m_vertexAttribPointers;
     bool m_isOpenGLES2;
+
+    // EXTENSIONS
+    CanvasGLStateDump *m_stateDumpExt;
 };
 
 #endif // CONTEXT3D_P_H
diff --git a/src/contextattributes.cpp b/src/contextattributes.cpp
index a6a11e0..e2f4ffa 100644
--- a/src/contextattributes.cpp
+++ b/src/contextattributes.cpp
@@ -46,7 +46,7 @@
  * \brief Attribute set for Context3D
  *
  * ContextAttributes is an attribute set that can be given as parameter on first call to
- * Canvas3D object's \l{Canvas3D::getContext()}{getContext(string type, ContextAttributes options)}
+ * Canvas3D object's \l{Canvas3D::getContext}{getContext(string type, ContextAttributes options)}
  * method call. It can also be requested from the Context3D later on to verify what exact
  * attributes are in fact enabled/disabled in the created context.
  *
diff --git a/src/plugins.qmltypes b/src/plugins.qmltypes
index d93efa6..e96591e 100644
--- a/src/plugins.qmltypes
+++ b/src/plugins.qmltypes
@@ -462,8 +462,6 @@ Module {
         Property { name: "canvas"; type: "Canvas"; isReadonly: true; isPointer: true }
         Property { name: "drawingBufferWidth"; type: "uint"; isReadonly: true }
         Property { name: "drawingBufferHeight"; type: "uint"; isReadonly: true }
-        Property { name: "logAllCalls"; type: "bool" }
-        Property { name: "logAllErrors"; type: "bool" }
         Property { name: "DEPTH_BUFFER_BIT"; type: "CanvasContext::glEnums"; isReadonly: true }
         Property { name: "STENCIL_BUFFER_BIT"; type: "CanvasContext::glEnums"; isReadonly: true }
         Property { name: "COLOR_BUFFER_BIT"; type: "CanvasContext::glEnums"; isReadonly: true }
@@ -1837,6 +1835,44 @@ Module {
         isCreatable: false
         exportMetaObjectRevisions: [0]
     }
+    Component {
+        name: "CanvasGLStateDump"
+        prototype: "QObject"
+        exports: ["QtCanvas3D/GLStateDumpExt 1.0"]
+        isCreatable: false
+        exportMetaObjectRevisions: [0]
+        Enum {
+            name: "stateDumpEnums"
+            values: {
+                "DUMP_BASIC_ONLY_BIT": 0,
+                "DUMP_VERTEX_ATTRIB_ARRAYS_BIT": 1,
+                "DUMP_VERTEX_ATTRIB_ARRAYS_CONTENTS_BIT": 2,
+                "DUMP_FULL": 3
+            }
+        }
+        Property {
+            name: "DUMP_BASIC_ONLY_BIT"
+            type: "CanvasGLStateDump::stateDumpEnums"
+            isReadonly: true
+        }
+        Property {
+            name: "DUMP_VERTEX_ATTRIB_ARRAYS_BIT"
+            type: "CanvasGLStateDump::stateDumpEnums"
+            isReadonly: true
+        }
+        Property {
+            name: "DUMP_VERTEX_ATTRIB_ARRAYS_CONTENTS_BIT"
+            type: "CanvasGLStateDump::stateDumpEnums"
+            isReadonly: true
+        }
+        Property { name: "DUMP_FULL"; type: "CanvasGLStateDump::stateDumpEnums"; isReadonly: true }
+        Method {
+            name: "getGLStateDump"
+            type: "string"
+            Parameter { name: "options"; type: "stateDumpEnums" }
+        }
+        Method { name: "getGLStateDump"; type: "string" }
+    }
     Component {
         name: "CanvasInt16Array"
         prototype: "CanvasTypedArray"
diff --git a/src/qcanvas3d_plugin.cpp b/src/qcanvas3d_plugin.cpp
index 703af02..56d79b1 100644
--- a/src/qcanvas3d_plugin.cpp
+++ b/src/qcanvas3d_plugin.cpp
@@ -148,6 +148,12 @@ void QtCanvas3DPlugin::registerTypes(const char *uri)
                                                       1, 0,
                                                       "UniformLocation",
                                                       QLatin1String("Trying to create uncreatable: UniformLocation, use Context3D.getUniformLocation() instead."));
+
+    // EXTENSIONS
+    qmlRegisterUncreatableType<CanvasGLStateDump>(uri,
+                                                  1, 0,
+                                                  "GLStateDumpExt",
+                                                  QLatin1String("Trying to create uncreatable: GLStateDumpExt, use Context3D.getExtension(\"" QT_CANVAS3D_GL_STATE_DUMP_EXT_NAME "\") instead."));
 }
 
 
diff --git a/src/qcanvas3d_plugin.h b/src/qcanvas3d_plugin.h
index 3cd0e9e..2bda1e7 100644
--- a/src/qcanvas3d_plugin.h
+++ b/src/qcanvas3d_plugin.h
@@ -63,6 +63,7 @@
 #include "renderbuffer3d_p.h"
 #include "shaderprecisionformat_p.h"
 #include "activeinfo3d_p.h"
+#include "canvasglstatedump_p.h"
 
 #include <QQmlExtensionPlugin>
 
@@ -96,6 +97,8 @@ QML_DECLARE_TYPE(CanvasFrameBuffer)
 QML_DECLARE_TYPE(CanvasRenderBuffer)
 QML_DECLARE_TYPE(CanvasShaderPrecisionFormat)
 QML_DECLARE_TYPE(CanvasActiveInfo)
+QML_DECLARE_TYPE(CanvasGLStateDump)
+
 class QtCanvas3DPlugin : public QQmlExtensionPlugin
 {
     Q_OBJECT
diff --git a/src/src.pro b/src/src.pro
index 5231741..1f842d5 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -45,7 +45,8 @@ SOURCES += qcanvas3d_plugin.cpp \
     teximage3d.cpp \
     texture3d.cpp \
     uniformlocation.cpp \
-    activeinfo3d.cpp
+    activeinfo3d.cpp \
+    canvasglstatedump.cpp
 
 HEADERS += qcanvas3d_plugin.h \
     arraybuffer_p.h \
@@ -79,7 +80,8 @@ HEADERS += qcanvas3d_plugin.h \
     teximage3d_p.h \
     texture3d_p.h \
     uniformlocation_p.h \
-    activeinfo3d_p.h
+    activeinfo3d_p.h \
+    canvasglstatedump_p.h
 
 OTHER_FILES = qmldir \
     doc/* \
diff --git a/src/texture3d.cpp b/src/texture3d.cpp
index 1a482db..69db079 100644
--- a/src/texture3d.cpp
+++ b/src/texture3d.cpp
@@ -81,7 +81,7 @@ void CanvasTexture::bind(CanvasContext::glEnums target)
 /*!
  * \internal
  */
-GLuint CanvasTexture::textureId()
+GLuint CanvasTexture::textureId() const
 {
     if (!m_isAlive)
         return 0;
@@ -92,7 +92,7 @@ GLuint CanvasTexture::textureId()
 /*!
  * \internal
  */
-bool CanvasTexture::isAlive()
+bool CanvasTexture::isAlive() const
 {
     return bool(m_textureId);
 }
@@ -113,7 +113,7 @@ void CanvasTexture::del()
 QDebug operator<<(QDebug dbg, const CanvasTexture *texture)
 {
     if (texture)
-        dbg.nospace() << "Texture3D("<< ((void*) texture) << ", name:" << texture->name() << ")";
+        dbg.nospace() << "Texture3D("<< ((void*) texture) << ", name:" << texture->name() << ", id:" << texture->textureId() << ")";
     else
         dbg.nospace() << "Texture3D("<< ((void*) texture) <<")";
     return dbg.maybeSpace();
diff --git a/src/texture3d_p.h b/src/texture3d_p.h
index 6c821b1..f328685 100644
--- a/src/texture3d_p.h
+++ b/src/texture3d_p.h
@@ -64,8 +64,8 @@ public:
     void bind(CanvasContext::glEnums target);
 
     void del();
-    bool isAlive();
-    GLuint textureId();
+    bool isAlive() const;
+    GLuint textureId() const;
 
     friend QDebug operator<< (QDebug d, const CanvasTexture *texture);
 
-- 
GitLab