From fe30bb10816dcdce99f282e9517fa513637a8019 Mon Sep 17 00:00:00 2001
From: Miikka Heikkinen <miikka.heikkinen@theqtcompany.com>
Date: Tue, 3 Nov 2015 16:16:07 +0200
Subject: [PATCH] Fix DEPTH_STENCIL buffers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

We can't assume combined depth/stencil buffer is supported,
so check for it, and initialize it correctly when it is
supported.

Change-Id: Id1792f51ef0a6c74437327e6308c88157fcc1124
Task-number: QTBUG-49182
Reviewed-by: Tomi Korpipää <tomi.korpipaa@theqtcompany.com>
Reviewed-by: Pasi Keränen <pasi.keranen@digia.com>
---
 src/imports/qtcanvas3d/canvas3d.cpp       |  9 ++-
 src/imports/qtcanvas3d/canvas3d_p.h       |  1 +
 src/imports/qtcanvas3d/canvasrenderer.cpp | 21 +++++-
 src/imports/qtcanvas3d/canvasrenderer_p.h |  4 +-
 src/imports/qtcanvas3d/context3d.cpp      | 88 ++++++++++++++++++-----
 src/imports/qtcanvas3d/context3d_p.h      |  6 +-
 src/imports/qtcanvas3d/renderbuffer3d.cpp | 19 ++++-
 src/imports/qtcanvas3d/renderbuffer3d_p.h |  5 +-
 8 files changed, 122 insertions(+), 31 deletions(-)

diff --git a/src/imports/qtcanvas3d/canvas3d.cpp b/src/imports/qtcanvas3d/canvas3d.cpp
index 825644d..f8294c3 100644
--- a/src/imports/qtcanvas3d/canvas3d.cpp
+++ b/src/imports/qtcanvas3d/canvas3d.cpp
@@ -90,6 +90,7 @@ 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),
@@ -370,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);
         }
 
@@ -381,7 +383,8 @@ 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.data(), &CanvasContext::handleTextureIdResolved,
@@ -609,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();
diff --git a/src/imports/qtcanvas3d/canvas3d_p.h b/src/imports/qtcanvas3d/canvas3d_p.h
index 14b6afa..1cf67ab 100644
--- a/src/imports/qtcanvas3d/canvas3d_p.h
+++ b/src/imports/qtcanvas3d/canvas3d_p.h
@@ -180,6 +180,7 @@ private:
     float m_devicePixelRatio;
 
     bool m_isOpenGLES2;
+    bool m_isCombinedDepthStencilSupported;
     bool m_isSoftwareRendered;
     bool m_runningInDesigner;
     CanvasContextAttributes m_contextAttribs;
diff --git a/src/imports/qtcanvas3d/canvasrenderer.cpp b/src/imports/qtcanvas3d/canvasrenderer.cpp
index c2b4e19..86ca838 100644
--- a/src/imports/qtcanvas3d/canvasrenderer.cpp
+++ b/src/imports/qtcanvas3d/canvasrenderer.cpp
@@ -158,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();
@@ -228,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) {
@@ -369,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())
@@ -458,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();
diff --git a/src/imports/qtcanvas3d/canvasrenderer_p.h b/src/imports/qtcanvas3d/canvasrenderer_p.h
index cf1a275..2f98974 100644
--- a/src/imports/qtcanvas3d/canvasrenderer_p.h
+++ b/src/imports/qtcanvas3d/canvasrenderer_p.h
@@ -84,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();
diff --git a/src/imports/qtcanvas3d/context3d.cpp b/src/imports/qtcanvas3d/context3d.cpp
index 014fe30..8700319 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
@@ -80,7 +84,8 @@ const int maxUniformAttributeNameLen = 512;
  */
 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)),
@@ -101,6 +106,7 @@ CanvasContext::CanvasContext(QQmlEngine *engine, bool isES2, int maxVertexAttrib
     m_maxVertexAttribs(maxVertexAttribs),
     m_contextVersion(contextVersion),
     m_isOpenGLES2(isES2),
+    m_isCombinedDepthStencilSupported(isCombinedDepthStencilSupported),
     m_commandQueue(0),
     m_contextLost(false),
     m_contextLostErrorReported(false),
@@ -1828,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 (!checkValidity(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));
+    }
 }
 
 /*!
@@ -2001,7 +2023,8 @@ QJSValue CanvasContext::createRenderbuffer()
     if (checkContextLost())
         return QJSValue(QJSValue::NullValue);
 
-    CanvasRenderBuffer *renderbuffer = new CanvasRenderBuffer(m_commandQueue, this);
+    CanvasRenderBuffer *renderbuffer =
+            new CanvasRenderBuffer(m_commandQueue, !m_isCombinedDepthStencilSupported, this);
     QJSValue value = m_engine->newQObject(renderbuffer);
     qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__
                                          << "():" << value.toString();
@@ -2048,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.
  */
@@ -2072,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));
+    }
 }
 
 /*!
diff --git a/src/imports/qtcanvas3d/context3d_p.h b/src/imports/qtcanvas3d/context3d_p.h
index 52cf800..165ec68 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);
@@ -1300,6 +1301,7 @@ private:
     int m_contextVersion;
     float **m_vertexAttribPointers;
     bool m_isOpenGLES2;
+    bool m_isCombinedDepthStencilSupported;
     CanvasGlCommandQueue *m_commandQueue; // Not owned
     QMutex m_renderJobMutex;
     QWaitCondition m_renderJobCondition;
diff --git a/src/imports/qtcanvas3d/renderbuffer3d.cpp b/src/imports/qtcanvas3d/renderbuffer3d.cpp
index 7514288..563ac4e 100644
--- a/src/imports/qtcanvas3d/renderbuffer3d.cpp
+++ b/src/imports/qtcanvas3d/renderbuffer3d.cpp
@@ -50,11 +50,17 @@ QT_CANVAS3D_BEGIN_NAMESPACE
  * 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)
 {
     queueCommand(CanvasGlCommandQueue::glGenRenderbuffers, m_renderbufferId);
+    if (initSecondaryId) {
+        m_secondaryId = queue->createResourceId();
+        queueCommand(CanvasGlCommandQueue::glGenRenderbuffers, m_secondaryId);
+    }
 }
 
 
@@ -72,6 +78,10 @@ void CanvasRenderBuffer::del()
 {
     if (m_renderbufferId) {
         queueCommand(CanvasGlCommandQueue::glDeleteRenderbuffers, m_renderbufferId);
+        if (m_secondaryId) {
+            queueCommand(CanvasGlCommandQueue::glDeleteRenderbuffers, m_secondaryId);
+            m_secondaryId = 0;
+        }
         m_renderbufferId = 0;
     }
 }
@@ -81,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 385ac6a..5b634e6 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
-- 
GitLab