diff options
Diffstat (limited to 'src/opengl')
72 files changed, 19035 insertions, 21058 deletions
diff --git a/src/opengl/.prev_CMakeLists.txt b/src/opengl/.prev_CMakeLists.txt new file mode 100644 index 0000000000..eb125273bc --- /dev/null +++ b/src/opengl/.prev_CMakeLists.txt @@ -0,0 +1,62 @@ +# Generated from opengl.pro. + +##################################################################### +## OpenGL Module: +##################################################################### + +qt_add_module(OpenGL + SOURCES + qopengl2pexvertexarray.cpp qopengl2pexvertexarray_p.h + qopenglcustomshaderstage.cpp qopenglcustomshaderstage_p.h + qopengldebug.cpp qopengldebug.h + qopenglengineshadermanager.cpp qopenglengineshadermanager_p.h + qopenglengineshadersource_p.h + qopenglgradientcache.cpp qopenglgradientcache_p.h + qopenglpaintdevice.cpp qopenglpaintdevice.h qopenglpaintdevice_p.h + qopenglpaintengine.cpp qopenglpaintengine_p.h + qopenglpixeltransferoptions.cpp qopenglpixeltransferoptions.h + qopenglshadercache_p.h + qopengltexture.cpp qopengltexture.h qopengltexture_p.h + qopengltexturecache.cpp qopengltexturecache_p.h + qopengltextureglyphcache.cpp qopengltextureglyphcache_p.h + qopengltexturehelper.cpp qopengltexturehelper_p.h + qopengltextureuploader.cpp qopengltextureuploader_p.h + qopenglwindow.cpp qopenglwindow.h + qtopenglglobal.h + DEFINES + QT_NO_FOREACH + QT_NO_USING_NAMESPACE + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + PRIVATE_MODULE_INTERFACE + Qt::CorePrivate + Qt::GuiPrivate +) + +## Scopes: +##################################################################### + +qt_extend_target(OpenGL CONDITION QT_FEATURE_widgets + SOURCES + qopenglwidget.cpp qopenglwidget.h + LIBRARIES + Qt::WidgetsPrivate + PUBLIC_LIBRARIES + Qt::Widgets + PRIVATE_MODULE_INTERFACE + Qt::WidgetsPrivate +) + +qt_extend_target(OpenGL CONDITION NOT QT_FEATURE_opengles2 + SOURCES + qopenglqueryhelper_p.h + qopengltimerquery.cpp qopengltimerquery.h +) +qt_add_docs(OpenGL + doc/qtopengl.qdocconf +) + diff --git a/src/opengl/CMakeLists.txt b/src/opengl/CMakeLists.txt new file mode 100644 index 0000000000..acad98a681 --- /dev/null +++ b/src/opengl/CMakeLists.txt @@ -0,0 +1,63 @@ +# Generated from opengl.pro. + +##################################################################### +## OpenGL Module: +##################################################################### + +qt_add_module(OpenGL + SOURCES + qopengl2pexvertexarray.cpp qopengl2pexvertexarray_p.h + qopenglcustomshaderstage.cpp qopenglcustomshaderstage_p.h + qopengldebug.cpp qopengldebug.h + qopenglengineshadermanager.cpp qopenglengineshadermanager_p.h + qopenglengineshadersource_p.h + qopenglgradientcache.cpp qopenglgradientcache_p.h + qopenglpaintdevice.cpp qopenglpaintdevice.h qopenglpaintdevice_p.h + qopenglpaintengine.cpp qopenglpaintengine_p.h + qopenglpixeltransferoptions.cpp qopenglpixeltransferoptions.h + qopenglshadercache_p.h + qopengltexture.cpp qopengltexture.h qopengltexture_p.h + qopengltextureblitter.cpp qopengltextureblitter.h + qopengltexturecache.cpp qopengltexturecache_p.h + qopengltextureglyphcache.cpp qopengltextureglyphcache_p.h + qopengltexturehelper.cpp qopengltexturehelper_p.h + qopengltextureuploader.cpp qopengltextureuploader_p.h + qopenglwindow.cpp qopenglwindow.h + qtopenglglobal.h + DEFINES + QT_NO_FOREACH + QT_NO_USING_NAMESPACE + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + PRIVATE_MODULE_INTERFACE + Qt::CorePrivate + Qt::GuiPrivate +) + +## Scopes: +##################################################################### + +qt_extend_target(OpenGL CONDITION QT_FEATURE_widgets + SOURCES + qopenglwidget.cpp qopenglwidget.h + LIBRARIES + Qt::WidgetsPrivate + PUBLIC_LIBRARIES + Qt::Widgets + PRIVATE_MODULE_INTERFACE + Qt::WidgetsPrivate +) + +qt_extend_target(OpenGL CONDITION NOT QT_FEATURE_opengles2 + SOURCES + qopenglqueryhelper_p.h + qopengltimerquery.cpp qopengltimerquery.h +) +qt_add_docs(OpenGL + doc/qtopengl.qdocconf +) + diff --git a/src/opengl/doc/snippets/code/src_opengl_qgl.cpp b/src/opengl/doc/snippets/code/src_opengl_qgl.cpp deleted file mode 100644 index a73ee2cdc5..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qgl.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] -QGLFormat fmt; -fmt.setAlpha(true); -fmt.setStereo(true); -QGLFormat::setDefaultFormat(fmt); -//! [0] - - -//! [1] -QGLFormat fmt; -fmt.setDoubleBuffer(false); // single buffer -fmt.setDirectRendering(false); // software rendering -MyGLWidget* myWidget = new MyGLWidget(fmt, ...); -//! [1] - - -//! [2] -QGLFormat fmt; -fmt.setOverlay(true); -fmt.setStereo(true); -MyGLWidget* myWidget = new MyGLWidget(fmt, ...); -if (!myWidget->format().stereo()) { - // ok, goggles off - if (!myWidget->format().hasOverlay()) { - qFatal("Cool hardware required"); - } -} -//! [2] - - -//! [3] -// The rendering in MyGLWidget depends on using -// stencil buffer and alpha channel -MyGLWidget::MyGLWidget(QWidget* parent) - : QGLWidget(QGLFormat(QGL::StencilBuffer | QGL::AlphaChannel), parent) -{ - if (!format().stencil()) - qWarning("Could not get stencil buffer; results will be suboptimal"); - if (!format().alpha()) - qWarning("Could not get alpha channel; results will be suboptimal"); - ... -} -//! [3] - - -//! [4] -QApplication a(argc, argv); -QGLFormat f; -f.setDoubleBuffer(false); -QGLFormat::setDefaultFormat(f); -//! [4] - - -//! [5] -QGLFormat f = QGLFormat::defaultOverlayFormat(); -f.setDoubleBuffer(true); -QGLFormat::setDefaultOverlayFormat(f); -//! [5] - - -//! [6] -// ...continued from above -MyGLWidget* myWidget = new MyGLWidget(QGLFormat(QGL::HasOverlay), ...); -if (myWidget->format().hasOverlay()) { - // Yes, we got an overlay, let's check _its_ format: - QGLContext* olContext = myWidget->overlayContext(); - if (olContext->format().doubleBuffer()) - ; // yes, we got a double buffered overlay - else - ; // no, only single buffered overlays are available -} -//! [6] - - -//! [7] -QGLContext *cx; -// ... -QGLFormat f; -f.setStereo(true); -cx->setFormat(f); -if (!cx->create()) - exit(); // no OpenGL support, or cannot render on the specified paintdevice -if (!cx->format().stereo()) - exit(); // could not create stereo context -//! [7] - - -//! [8] -class MyGLDrawer : public QGLWidget -{ - Q_OBJECT // must include this if you use Qt signals/slots - -public: - MyGLDrawer(QWidget *parent) - : QGLWidget(parent) {} - -protected: - - void initializeGL() override - { - // Set up the rendering context, define display lists etc.: - ... - glClearColor(0.0, 0.0, 0.0, 0.0); - glEnable(GL_DEPTH_TEST); - ... - } - - void resizeGL(int w, int h) override - { - // setup viewport, projection etc.: - glViewport(0, 0, (GLint)w, (GLint)h); - ... - glFrustum(...); - ... - } - - void paintGL() override - { - // draw the scene: - ... - glRotatef(...); - glMaterialfv(...); - glBegin(GL_QUADS); - glVertex3f(...); - glVertex3f(...); - ... - glEnd(); - ... - } - -}; -//! [8] diff --git a/src/opengl/doc/snippets/code/src_opengl_qglbuffer.cpp b/src/opengl/doc/snippets/code/src_opengl_qglbuffer.cpp deleted file mode 100644 index 11ef88ab37..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qglbuffer.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] - QGLBuffer buffer1(QGLBuffer::IndexBuffer); - buffer1.create(); - - QGLBuffer buffer2 = buffer1; -//! [0] - -//! [1] - QGLBuffer::release(QGLBuffer::VertexBuffer); -//! [1] diff --git a/src/opengl/doc/snippets/code/src_opengl_qglcolormap.cpp b/src/opengl/doc/snippets/code/src_opengl_qglcolormap.cpp deleted file mode 100644 index 12a089944f..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qglcolormap.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] -#include <QApplication> -#include <QGLColormap> - -int main() -{ - QApplication app(argc, argv); - - MySuperGLWidget widget; // a QGLWidget in color-index mode - QGLColormap colormap; - - // This will fill the colormap with colors ranging from - // black to white. - for (int i = 0; i < colormap.size(); i++) - colormap.setEntry(i, qRgb(i, i, i)); - - widget.setColormap(colormap); - widget.show(); - return app.exec(); -} -//! [0] diff --git a/src/opengl/doc/snippets/code/src_opengl_qglfunctions.cpp b/src/opengl/doc/snippets/code/src_opengl_qglfunctions.cpp deleted file mode 100644 index c848ae7ede..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qglfunctions.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] - class MyGLWidget : public QGLWidget, protected QGLFunctions - { - Q_OBJECT - public: - MyGLWidget(QWidget *parent = 0) : QGLWidget(parent) {} - - protected: - void initializeGL(); - void paintGL(); - }; - - void MyGLWidget::initializeGL() - { - initializeGLFunctions(); - } -//! [0] - -//! [1] - void MyGLWidget::paintGL() - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, textureId); - ... - } -//! [1] - -//! [2] - QGLFunctions glFuncs(QGLContext::currentContext()); - glFuncs.glActiveTexture(GL_TEXTURE1); -//! [2] - -//! [3] - QGLFunctions funcs(QGLContext::currentContext()); - bool npot = funcs.hasOpenGLFeature(QGLFunctions::NPOTTextures); -//! [3] diff --git a/src/opengl/doc/snippets/code/src_opengl_qglpixelbuffer.cpp b/src/opengl/doc/snippets/code/src_opengl_qglpixelbuffer.cpp deleted file mode 100644 index f6fbe6ddb4..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qglpixelbuffer.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] -QGLPixelBuffer pbuffer(...); -... -pbuffer.makeCurrent(); -GLuint dynamicTexture = pbuffer.generateDynamicTexture(); -pbuffer.bindToDynamicTexture(dynamicTexture); -... -pbuffer.releaseFromDynamicTexture(); -//! [0] - - -//! [1] -QGLPixelBuffer pbuffer(...); -... -pbuffer.makeCurrent(); -GLuint dynamicTexture = pbuffer.generateDynamicTexture(); -... -pbuffer.updateDynamicTexture(dynamicTexture); -//! [1] diff --git a/src/opengl/doc/snippets/code/src_opengl_qglshaderprogram.cpp b/src/opengl/doc/snippets/code/src_opengl_qglshaderprogram.cpp deleted file mode 100644 index 04492499e8..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qglshaderprogram.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] -QGLShader shader(QGLShader::Vertex); -shader.compileSourceCode(code); - -QGLShaderProgram program(context); -program.addShader(shader); -program.link(); - -program.bind(); -//! [0] - -//! [1] -program.addShaderFromSourceCode(QGLShader::Vertex, - "attribute highp vec4 vertex;\n" - "uniform highp mat4 matrix;\n" - "void main(void)\n" - "{\n" - " gl_Position = matrix * vertex;\n" - "}"); -program.addShaderFromSourceCode(QGLShader::Fragment, - "uniform mediump vec4 color;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = color;\n" - "}"); -program.link(); -program.bind(); - -int vertexLocation = program.attributeLocation("vertex"); -int matrixLocation = program.uniformLocation("matrix"); -int colorLocation = program.uniformLocation("color"); -//! [1] - -//! [2] -static GLfloat const triangleVertices[] = { - 60.0f, 10.0f, 0.0f, - 110.0f, 110.0f, 0.0f, - 10.0f, 110.0f, 0.0f -}; - -QColor color(0, 255, 0, 255); - -QMatrix4x4 pmvMatrix; -pmvMatrix.ortho(rect()); - -program.enableAttributeArray(vertexLocation); -program.setAttributeArray(vertexLocation, triangleVertices, 3); -program.setUniformValue(matrixLocation, pmvMatrix); -program.setUniformValue(colorLocation, color); - -glDrawArrays(GL_TRIANGLES, 0, 3); - -program.disableAttributeArray(vertexLocation); -//! [2] diff --git a/src/opengl/doc/snippets/code/src_opengl_qgraphicsshadereffect.cpp b/src/opengl/doc/snippets/code/src_opengl_qgraphicsshadereffect.cpp deleted file mode 100644 index 461e96e094..0000000000 --- a/src/opengl/doc/snippets/code/src_opengl_qgraphicsshadereffect.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [0] - static char const colorizeShaderCode[] = - "uniform lowp vec4 effectColor;\n" - "lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) {\n" - " vec4 src = texture2D(imageTexture, textureCoords);\n" - " float gray = dot(src.rgb, vec3(0.212671, 0.715160, 0.072169));\n" - " vec4 colorize = 1.0-((1.0-gray)*(1.0-effectColor));\n" - " return vec4(colorize.rgb, src.a);\n" - "}"; -//! [0] - -//! [1] - class ColorizeEffect : public QGraphicsShaderEffect - { - Q_OBJECT - public: - ColorizeEffect(QObject *parent = 0) - : QGraphicsShaderEffect(parent), color(Qt::black) - { - setPixelShaderFragment(colorizeShaderCode); - } - - QColor effectColor() const { return color; } - void setEffectColor(const QColor& c) - { - color = c; - setUniformsDirty(); - } - - protected: - void setUniforms(QGLShaderProgram *program) - { - program->setUniformValue("effectColor", color); - } - - private: - QColor color; - }; -//! [1] - -//! [2] - lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) { - return texture2D(imageTexture, textureCoords); - } -//! [2] diff --git a/src/opengl/doc/src/qtopengl-examples.qdoc b/src/opengl/doc/src/qtopengl-examples.qdoc index de80c92883..8770d6247e 100644 --- a/src/opengl/doc/src/qtopengl-examples.qdoc +++ b/src/opengl/doc/src/qtopengl-examples.qdoc @@ -33,12 +33,11 @@ \image opengl-examples.png - These examples describe how to use the \l {Qt OpenGL} module. For new code, - please use the OpenGL classes in the \l {Qt GUI} module. + These examples describe how to use the \l {Qt OpenGL} module. - Qt provides support for integration with OpenGL implementations on all - platforms, giving developers the opportunity to display hardware accelerated - 3D graphics alongside a more conventional user interface. + Qt provides support for integration with OpenGL implementations, giving + developers the opportunity to display hardware accelerated 3D graphics + alongside a more conventional user interface. These examples demonstrate the basic techniques used to take advantage of OpenGL in Qt applications. diff --git a/src/opengl/doc/src/qtopengl-module.qdoc b/src/opengl/doc/src/qtopengl-module.qdoc index 336d37c73f..24df4aec76 100644 --- a/src/opengl/doc/src/qtopengl-module.qdoc +++ b/src/opengl/doc/src/qtopengl-module.qdoc @@ -34,8 +34,7 @@ \brief The Qt OpenGL module offers classes that make it easy to use OpenGL in Qt applications. - \warning This module should not be used anymore for new code. Please - use the corresponding OpenGL classes in \l{Qt GUI}. + \warning This module should not be used anymore for new code. OpenGL is a standard API for rendering 3D graphics. OpenGL only deals with 3D rendering and provides little or no support for GUI @@ -48,9 +47,6 @@ the United States and other countries. The Qt OpenGL module makes it easy to use OpenGL in Qt applications. - It provides an OpenGL widget class that can be used just like any - other Qt widget, except that it opens an OpenGL display buffer where - you can use the OpenGL API to render the contents. To include the definitions of the module's classes, use the following directive: @@ -63,11 +59,4 @@ \snippet code/doc_src_qtopengl.pro 1 \endif - - The Qt OpenGL module is implemented as a platform-independent Qt/C++ - wrapper around the platform-dependent GLX (version 1.3 or later), - WGL, or AGL C APIs. Although the basic functionality provided is very - similar to Mark Kilgard's GLUT library, applications using the Qt - OpenGL module can take advantage of the whole Qt API for - non-OpenGL-specific GUI functionality. */ diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp deleted file mode 100644 index 47e8531959..0000000000 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ /dev/null @@ -1,875 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qglengineshadermanager_p.h" -#include "qglengineshadersource_p.h" -#include "qpaintengineex_opengl2_p.h" -#include "qglshadercache_p.h" - -#include <QtGui/private/qopenglcontext_p.h> - -#if defined(QT_DEBUG) -#include <QMetaEnum> -#endif - -#include <algorithm> - -// #define QT_GL_SHARED_SHADER_DEBUG - -QT_BEGIN_NAMESPACE - -class QGLEngineSharedShadersResource : public QOpenGLSharedResource -{ -public: - QGLEngineSharedShadersResource(QOpenGLContext *ctx) - : QOpenGLSharedResource(ctx->shareGroup()) - , m_shaders(new QGLEngineSharedShaders(QGLContext::fromOpenGLContext(ctx))) - { - } - - ~QGLEngineSharedShadersResource() - { - delete m_shaders; - } - - void invalidateResource() override - { - delete m_shaders; - m_shaders = 0; - } - - void freeResource(QOpenGLContext *) override - { - } - - QGLEngineSharedShaders *shaders() const { return m_shaders; } - -private: - QGLEngineSharedShaders *m_shaders; -}; - -class QGLShaderStorage -{ -public: - QGLEngineSharedShaders *shadersForThread(const QGLContext *context) { - QOpenGLMultiGroupSharedResource *&shaders = m_storage.localData(); - if (!shaders) - shaders = new QOpenGLMultiGroupSharedResource; - QGLEngineSharedShadersResource *resource = - shaders->value<QGLEngineSharedShadersResource>(context->contextHandle()); - return resource ? resource->shaders() : 0; - } - -private: - QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage; -}; - -Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage); - -QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context) -{ - return qt_shader_storage()->shadersForThread(context); -} - -const char* QGLEngineSharedShaders::qShaderSnippets[] = { - 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0 -}; - -QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) - : blitShaderProg(0) - , simpleShaderProg(0) -{ - -/* - Rather than having the shader source array statically initialised, it is initialised - here instead. This is to allow new shader names to be inserted or existing names moved - around without having to change the order of the glsl strings. It is hoped this will - make future hard-to-find runtime bugs more obvious and generally give more solid code. -*/ - static bool snippetsPopulated = false; - if (!snippetsPopulated) { - - const char** code = qShaderSnippets; // shortcut - - code[MainVertexShader] = qglslMainVertexShader; - code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader; - code[MainWithTexCoordsAndOpacityVertexShader] = qglslMainWithTexCoordsAndOpacityVertexShader; - - code[UntransformedPositionVertexShader] = qglslUntransformedPositionVertexShader; - code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader; - code[ComplexGeometryPositionOnlyVertexShader] = qglslComplexGeometryPositionOnlyVertexShader; - code[PositionWithPatternBrushVertexShader] = qglslPositionWithPatternBrushVertexShader; - code[PositionWithLinearGradientBrushVertexShader] = qglslPositionWithLinearGradientBrushVertexShader; - code[PositionWithConicalGradientBrushVertexShader] = qglslPositionWithConicalGradientBrushVertexShader; - code[PositionWithRadialGradientBrushVertexShader] = qglslPositionWithRadialGradientBrushVertexShader; - code[PositionWithTextureBrushVertexShader] = qglslPositionWithTextureBrushVertexShader; - code[AffinePositionWithPatternBrushVertexShader] = qglslAffinePositionWithPatternBrushVertexShader; - code[AffinePositionWithLinearGradientBrushVertexShader] = qglslAffinePositionWithLinearGradientBrushVertexShader; - code[AffinePositionWithConicalGradientBrushVertexShader] = qglslAffinePositionWithConicalGradientBrushVertexShader; - code[AffinePositionWithRadialGradientBrushVertexShader] = qglslAffinePositionWithRadialGradientBrushVertexShader; - code[AffinePositionWithTextureBrushVertexShader] = qglslAffinePositionWithTextureBrushVertexShader; - - code[MainFragmentShader_CMO] = qglslMainFragmentShader_CMO; - code[MainFragmentShader_CM] = qglslMainFragmentShader_CM; - code[MainFragmentShader_MO] = qglslMainFragmentShader_MO; - code[MainFragmentShader_M] = qglslMainFragmentShader_M; - code[MainFragmentShader_CO] = qglslMainFragmentShader_CO; - code[MainFragmentShader_C] = qglslMainFragmentShader_C; - code[MainFragmentShader_O] = qglslMainFragmentShader_O; - code[MainFragmentShader] = qglslMainFragmentShader; - code[MainFragmentShader_ImageArrays] = qglslMainFragmentShader_ImageArrays; - - code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader; - code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader; - code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader; - code[CustomImageSrcFragmentShader] = qglslCustomSrcFragmentShader; // Calls "customShader", which must be appended - code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader; - if (!context->contextHandle()->isOpenGLES()) - code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader_desktop; - else - code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader_ES; - code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader; - code[PatternBrushSrcFragmentShader] = qglslPatternBrushSrcFragmentShader; - code[LinearGradientBrushSrcFragmentShader] = qglslLinearGradientBrushSrcFragmentShader; - code[RadialGradientBrushSrcFragmentShader] = qglslRadialGradientBrushSrcFragmentShader; - code[ConicalGradientBrushSrcFragmentShader] = qglslConicalGradientBrushSrcFragmentShader; - code[ShockingPinkSrcFragmentShader] = qglslShockingPinkSrcFragmentShader; - - code[NoMaskFragmentShader] = ""; - code[MaskFragmentShader] = qglslMaskFragmentShader; - code[RgbMaskFragmentShaderPass1] = qglslRgbMaskFragmentShaderPass1; - code[RgbMaskFragmentShaderPass2] = qglslRgbMaskFragmentShaderPass2; - code[RgbMaskWithGammaFragmentShader] = ""; //### - - code[NoCompositionModeFragmentShader] = ""; - code[MultiplyCompositionModeFragmentShader] = ""; //### - code[ScreenCompositionModeFragmentShader] = ""; //### - code[OverlayCompositionModeFragmentShader] = ""; //### - code[DarkenCompositionModeFragmentShader] = ""; //### - code[LightenCompositionModeFragmentShader] = ""; //### - code[ColorDodgeCompositionModeFragmentShader] = ""; //### - code[ColorBurnCompositionModeFragmentShader] = ""; //### - code[HardLightCompositionModeFragmentShader] = ""; //### - code[SoftLightCompositionModeFragmentShader] = ""; //### - code[DifferenceCompositionModeFragmentShader] = ""; //### - code[ExclusionCompositionModeFragmentShader] = ""; //### - -#if defined(QT_DEBUG) - // Check that all the elements have been filled: - for (int i = 0; i < TotalSnippetCount; ++i) { - if (Q_UNLIKELY(!qShaderSnippets[i])) { - qFatal("Shader snippet for %s (#%d) is missing!", - snippetNameStr(SnippetName(i)).constData(), i); - } - } -#endif - snippetsPopulated = true; - } - - QGLShader* fragShader; - QGLShader* vertexShader; - QByteArray vertexSource; - QByteArray fragSource; - - // Compile up the simple shader: - vertexSource.append(qShaderSnippets[MainVertexShader]); - vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]); - - fragSource.append(qShaderSnippets[MainFragmentShader]); - fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); - - simpleShaderProg = new QGLShaderProgram(context, 0); - - CachedShader simpleShaderCache(fragSource, vertexSource); - - bool inCache = simpleShaderCache.load(simpleShaderProg, context); - - if (!inCache) { - vertexShader = new QGLShader(QGLShader::Vertex, context, 0); - shaders.append(vertexShader); - if (!vertexShader->compileSourceCode(vertexSource)) - qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); - - fragShader = new QGLShader(QGLShader::Fragment, context, 0); - shaders.append(fragShader); - if (!fragShader->compileSourceCode(fragSource)) - qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); - - simpleShaderProg->addShader(vertexShader); - simpleShaderProg->addShader(fragShader); - - simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); - simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); - simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); - } - - simpleShaderProg->link(); - - if (Q_UNLIKELY(!simpleShaderProg->isLinked())) { - qCritical("Errors linking simple shader: %s", qPrintable(simpleShaderProg->log())); - } else { - if (!inCache) - simpleShaderCache.store(simpleShaderProg, context); - } - - // Compile the blit shader: - vertexSource.clear(); - vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]); - vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]); - - fragSource.clear(); - fragSource.append(qShaderSnippets[MainFragmentShader]); - fragSource.append(qShaderSnippets[ImageSrcFragmentShader]); - - blitShaderProg = new QGLShaderProgram(context, 0); - - CachedShader blitShaderCache(fragSource, vertexSource); - - inCache = blitShaderCache.load(blitShaderProg, context); - - if (!inCache) { - vertexShader = new QGLShader(QGLShader::Vertex, context, 0); - shaders.append(vertexShader); - if (!vertexShader->compileSourceCode(vertexSource)) - qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); - - fragShader = new QGLShader(QGLShader::Fragment, context, 0); - shaders.append(fragShader); - if (!fragShader->compileSourceCode(fragSource)) - qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); - - blitShaderProg->addShader(vertexShader); - blitShaderProg->addShader(fragShader); - - blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - } - - blitShaderProg->link(); - if (Q_UNLIKELY(!blitShaderProg->isLinked())) { - qCritical("Errors linking blit shader: %s", qPrintable(blitShaderProg->log())); - } else { - if (!inCache) - blitShaderCache.store(blitShaderProg, context); - } - -#ifdef QT_GL_SHARED_SHADER_DEBUG - qDebug(" -> QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); -#endif -} - -QGLEngineSharedShaders::~QGLEngineSharedShaders() -{ -#ifdef QT_GL_SHARED_SHADER_DEBUG - qDebug(" -> ~QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); -#endif - qDeleteAll(shaders); - shaders.clear(); - - qDeleteAll(cachedPrograms); - cachedPrograms.clear(); - - if (blitShaderProg) { - delete blitShaderProg; - blitShaderProg = 0; - } - - if (simpleShaderProg) { - delete simpleShaderProg; - simpleShaderProg = 0; - } -} - -#if defined (QT_DEBUG) -QByteArray QGLEngineSharedShaders::snippetNameStr(SnippetName name) -{ - QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName")); - return QByteArray(m.valueToKey(name)); -} -#endif - -// The address returned here will only be valid until next time this function is called. -// The program is return bound. -QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog) -{ - for (int i = 0; i < cachedPrograms.size(); ++i) { - QGLEngineShaderProg *cachedProg = cachedPrograms.at(i); - if (*cachedProg == prog) { - // Move the program to the top of the list as a poor-man's cache algo - cachedPrograms.move(i, 0); - cachedProg->program->bind(); - return cachedProg; - } - } - - QScopedPointer<QGLEngineShaderProg> newProg; - - do { - QByteArray fragSource; - // Insert the custom stage before the srcPixel shader to work around an ATI driver bug - // where you cannot forward declare a function that takes a sampler as argument. - if (prog.srcPixelFragShader == CustomImageSrcFragmentShader) - fragSource.append(prog.customStageSource); - fragSource.append(qShaderSnippets[prog.mainFragShader]); - fragSource.append(qShaderSnippets[prog.srcPixelFragShader]); - if (prog.compositionFragShader) - fragSource.append(qShaderSnippets[prog.compositionFragShader]); - if (prog.maskFragShader) - fragSource.append(qShaderSnippets[prog.maskFragShader]); - - QByteArray vertexSource; - vertexSource.append(qShaderSnippets[prog.mainVertexShader]); - vertexSource.append(qShaderSnippets[prog.positionVertexShader]); - - QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram); - - CachedShader shaderCache(fragSource, vertexSource); - bool inCache = shaderCache.load(shaderProgram.data(), QGLContext::currentContext()); - - if (!inCache) { - - QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment)); - QByteArray description; -#if defined(QT_DEBUG) - // Name the shader for easier debugging - description.append("Fragment shader: main="); - description.append(snippetNameStr(prog.mainFragShader)); - description.append(", srcPixel="); - description.append(snippetNameStr(prog.srcPixelFragShader)); - if (prog.compositionFragShader) { - description.append(", composition="); - description.append(snippetNameStr(prog.compositionFragShader)); - } - if (prog.maskFragShader) { - description.append(", mask="); - description.append(snippetNameStr(prog.maskFragShader)); - } - fragShader->setObjectName(QString::fromLatin1(description)); -#endif - if (!fragShader->compileSourceCode(fragSource)) { - qWarning() << "Warning:" << description << "failed to compile!"; - break; - } - - QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex)); -#if defined(QT_DEBUG) - // Name the shader for easier debugging - description.clear(); - description.append("Vertex shader: main="); - description.append(snippetNameStr(prog.mainVertexShader)); - description.append(", position="); - description.append(snippetNameStr(prog.positionVertexShader)); - vertexShader->setObjectName(QString::fromLatin1(description)); -#endif - if (!vertexShader->compileSourceCode(vertexSource)) { - qWarning() << "Warning:" << description << "failed to compile!"; - break; - } - - shaders.append(vertexShader.data()); - shaders.append(fragShader.data()); - shaderProgram->addShader(vertexShader.take()); - shaderProgram->addShader(fragShader.take()); - - // We have to bind the vertex attribute names before the program is linked: - shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - if (prog.useTextureCoords) - shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - if (prog.useOpacityAttribute) - shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); - if (prog.usePmvMatrixAttribute) { - shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); - shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); - shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); - } - } - - newProg.reset(new QGLEngineShaderProg(prog)); - newProg->program = shaderProgram.take(); - - newProg->program->link(); - if (newProg->program->isLinked()) { - if (!inCache) - shaderCache.store(newProg->program, QGLContext::currentContext()); - } else { - QString error; - error = QLatin1String("Shader program failed to link,"); -#if defined(QT_DEBUG) - QLatin1String br("\n"); - error += QLatin1String("\n Shaders Used:\n"); - for (int i = 0; i < newProg->program->shaders().count(); ++i) { - QGLShader *shader = newProg->program->shaders().at(i); - error += QLatin1String(" ") + shader->objectName() + QLatin1String(": \n") - + QLatin1String(shader->sourceCode()) + br; - } -#endif - error += QLatin1String(" Error Log:\n") - + QLatin1String(" ") + newProg->program->log(); - qWarning() << error; - break; - } - - newProg->program->bind(); - - if (newProg->maskFragShader != QGLEngineSharedShaders::NoMaskFragmentShader) { - GLuint location = newProg->program->uniformLocation("maskTexture"); - newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT); - } - - if (cachedPrograms.count() > 30) { - // The cache is full, so delete the last 5 programs in the list. - // These programs will be least used, as a program us bumped to - // the top of the list when it's used. - for (int i = 0; i < 5; ++i) { - delete cachedPrograms.last(); - cachedPrograms.removeLast(); - } - } - - cachedPrograms.insert(0, newProg.data()); - } while (false); - - return newProg.take(); -} - -void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage) -{ - auto hasStageAsCustomShaderSouce = [stage](QGLEngineShaderProg *cachedProg) -> bool { - if (cachedProg->customStageSource == stage->source()) { - delete cachedProg; - return true; - } - return false; - }; - cachedPrograms.erase(std::remove_if(cachedPrograms.begin(), cachedPrograms.end(), - hasStageAsCustomShaderSouce), - cachedPrograms.end()); -} - - -QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) - : ctx(context), - shaderProgNeedsChanging(true), - complexGeometry(false), - srcPixelType(Qt::NoBrush), - opacityMode(NoOpacity), - maskType(NoMask), - compositionMode(QPainter::CompositionMode_SourceOver), - customSrcStage(0), - currentShaderProg(0) -{ - sharedShaders = QGLEngineSharedShaders::shadersForContext(context); -} - -QGLEngineShaderManager::~QGLEngineShaderManager() -{ - //### - removeCustomStage(); -} - -GLuint QGLEngineShaderManager::getUniformLocation(Uniform id) -{ - if (!currentShaderProg) - return 0; - - QVector<uint> &uniformLocations = currentShaderProg->uniformLocations; - if (uniformLocations.isEmpty()) - uniformLocations.fill(GLuint(-1), NumUniforms); - - static const char *const uniformNames[] = { - "imageTexture", - "patternColor", - "globalOpacity", - "depth", - "maskTexture", - "fragmentColor", - "linearData", - "angle", - "halfViewportSize", - "fmp", - "fmp2_m_radius2", - "inverse_2_fmp2_m_radius2", - "sqrfr", - "bradius", - "invertedTextureSize", - "brushTransform", - "brushTexture", - "matrix", - "translateZ" - }; - - if (uniformLocations.at(id) == GLuint(-1)) - uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]); - - return uniformLocations.at(id); -} - - -void QGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType) -{ - Q_UNUSED(transformType); // Currently ignored -} - -void QGLEngineShaderManager::setDirty() -{ - shaderProgNeedsChanging = true; -} - -void QGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style) -{ - Q_ASSERT(style != Qt::NoBrush); - if (srcPixelType == PixelSrcType(style)) - return; - - srcPixelType = style; - shaderProgNeedsChanging = true; //### -} - -void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type) -{ - if (srcPixelType == type) - return; - - srcPixelType = type; - shaderProgNeedsChanging = true; //### -} - -void QGLEngineShaderManager::setOpacityMode(OpacityMode mode) -{ - if (opacityMode == mode) - return; - - opacityMode = mode; - shaderProgNeedsChanging = true; //### -} - -void QGLEngineShaderManager::setMaskType(MaskType type) -{ - if (maskType == type) - return; - - maskType = type; - shaderProgNeedsChanging = true; //### -} - -void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) -{ - if (compositionMode == mode) - return; - - compositionMode = mode; - shaderProgNeedsChanging = true; //### -} - -void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage) -{ - if (customSrcStage) - removeCustomStage(); - customSrcStage = stage; - shaderProgNeedsChanging = true; -} - -void QGLEngineShaderManager::removeCustomStage() -{ - if (customSrcStage) - customSrcStage->setInactive(); - customSrcStage = 0; - shaderProgNeedsChanging = true; -} - -QGLShaderProgram* QGLEngineShaderManager::currentProgram() -{ - if (currentShaderProg) - return currentShaderProg->program; - else - return sharedShaders->simpleProgram(); -} - -void QGLEngineShaderManager::useSimpleProgram() -{ - sharedShaders->simpleProgram()->bind(); - QGLContextPrivate* ctx_d = ctx->d_func(); - ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); - ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false); - ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); - shaderProgNeedsChanging = true; -} - -void QGLEngineShaderManager::useBlitProgram() -{ - sharedShaders->blitProgram()->bind(); - QGLContextPrivate* ctx_d = ctx->d_func(); - ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); - ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true); - ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); - shaderProgNeedsChanging = true; -} - -QGLShaderProgram* QGLEngineShaderManager::simpleProgram() -{ - return sharedShaders->simpleProgram(); -} - -QGLShaderProgram* QGLEngineShaderManager::blitProgram() -{ - return sharedShaders->blitProgram(); -} - - - -// Select & use the correct shader program using the current state. -// Returns \c true if program needed changing. -bool QGLEngineShaderManager::useCorrectShaderProg() -{ - if (!shaderProgNeedsChanging) - return false; - - bool useCustomSrc = customSrcStage != 0; - if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) { - useCustomSrc = false; - qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src"); - } - - QGLEngineShaderProg requiredProgram; - - bool texCoords = false; - - // Choose vertex shader shader position function (which typically also sets - // varyings) and the source pixel (srcPixel) fragment shader function: - requiredProgram.positionVertexShader = QGLEngineSharedShaders::InvalidSnippetName; - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::InvalidSnippetName; - bool isAffine = brushTransform.isAffine(); - if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) { - if (isAffine) - requiredProgram.positionVertexShader = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader; - else - requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader; - - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::PatternBrushSrcFragmentShader; - } - else switch (srcPixelType) { - default: - case Qt::NoBrush: - qFatal("QGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set"); - break; - case QGLEngineShaderManager::ImageSrc: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcFragmentShader; - requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; - texCoords = true; - break; - case QGLEngineShaderManager::NonPremultipliedImageSrc: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader; - requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; - texCoords = true; - break; - case QGLEngineShaderManager::PatternSrc: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader; - requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; - texCoords = true; - break; - case QGLEngineShaderManager::TextureSrcWithPattern: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader; - requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader - : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader; - break; - case Qt::SolidPattern: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::SolidBrushSrcFragmentShader; - requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; - break; - case Qt::LinearGradientPattern: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader; - requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader - : QGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader; - break; - case Qt::ConicalGradientPattern: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader; - requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader - : QGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader; - break; - case Qt::RadialGradientPattern: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader; - requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader - : QGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader; - break; - case Qt::TexturePattern: - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcFragmentShader; - requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader - : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader; - break; - }; - - if (useCustomSrc) { - requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::CustomImageSrcFragmentShader; - requiredProgram.customStageSource = customSrcStage->source(); - } - - const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; - const bool hasMask = maskType != QGLEngineShaderManager::NoMask; - - // Choose fragment shader main function: - if (opacityMode == AttributeOpacity) { - Q_ASSERT(!hasCompose && !hasMask); - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_ImageArrays; - } else { - bool useGlobalOpacity = (opacityMode == UniformOpacity); - if (hasCompose && hasMask && useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CMO; - if (hasCompose && hasMask && !useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CM; - if (!hasCompose && hasMask && useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_MO; - if (!hasCompose && hasMask && !useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_M; - if (hasCompose && !hasMask && useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CO; - if (hasCompose && !hasMask && !useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_C; - if (!hasCompose && !hasMask && useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_O; - if (!hasCompose && !hasMask && !useGlobalOpacity) - requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader; - } - - if (hasMask) { - if (maskType == PixelMask) { - requiredProgram.maskFragShader = QGLEngineSharedShaders::MaskFragmentShader; - texCoords = true; - } else if (maskType == SubPixelMaskPass1) { - requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1; - texCoords = true; - } else if (maskType == SubPixelMaskPass2) { - requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2; - texCoords = true; - } else if (maskType == SubPixelWithGammaMask) { - requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader; - texCoords = true; - } else { - qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type"); - } - } else { - requiredProgram.maskFragShader = QGLEngineSharedShaders::NoMaskFragmentShader; - } - - if (hasCompose) { - switch (compositionMode) { - case QPainter::CompositionMode_Multiply: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_Screen: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_Overlay: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_Darken: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_Lighten: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::LightenCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_ColorDodge: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_ColorBurn: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_HardLight: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_SoftLight: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_Difference: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader; - break; - case QPainter::CompositionMode_Exclusion: - requiredProgram.compositionFragShader = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader; - break; - default: - qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode"); - } - } else { - requiredProgram.compositionFragShader = QGLEngineSharedShaders::NoCompositionModeFragmentShader; - } - - // Choose vertex shader main function - if (opacityMode == AttributeOpacity) { - Q_ASSERT(texCoords); - requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader; - } else if (texCoords) { - requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsVertexShader; - } else { - requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainVertexShader; - } - requiredProgram.useTextureCoords = texCoords; - requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity); - if (complexGeometry && srcPixelType == Qt::SolidPattern) { - requiredProgram.positionVertexShader = QGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader; - requiredProgram.usePmvMatrixAttribute = false; - } else { - requiredProgram.usePmvMatrixAttribute = true; - - // Force complexGeometry off, since we currently don't support that mode for - // non-solid brushes - complexGeometry = false; - } - - // At this point, requiredProgram is fully populated so try to find the program in the cache - currentShaderProg = sharedShaders->findProgramInCache(requiredProgram); - - if (currentShaderProg && useCustomSrc) { - customSrcStage->setUniforms(currentShaderProg->program); - } - - // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it - // doesn't use are disabled) - QGLContextPrivate* ctx_d = ctx->d_func(); - ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); - ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords); - ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute); - - shaderProgNeedsChanging = false; - return true; -} - -QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h deleted file mode 100644 index a667af9b96..0000000000 --- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h +++ /dev/null @@ -1,523 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt 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 QGL_ENGINE_SHADER_SOURCE_H -#define QGL_ENGINE_SHADER_SOURCE_H - -#include "qglengineshadermanager_p.h" - -QT_BEGIN_NAMESPACE - - - -static const char* const qglslMainVertexShader = "\n\ - void setPosition(); \n\ - void main(void) \n\ - { \n\ - setPosition(); \n\ - }\n"; - -static const char* const qglslMainWithTexCoordsVertexShader = "\n\ - attribute highp vec2 textureCoordArray; \n\ - varying highp vec2 textureCoords; \n\ - void setPosition(); \n\ - void main(void) \n\ - { \n\ - setPosition(); \n\ - textureCoords = textureCoordArray; \n\ - }\n"; - -static const char* const qglslMainWithTexCoordsAndOpacityVertexShader = "\n\ - attribute highp vec2 textureCoordArray; \n\ - attribute lowp float opacityArray; \n\ - varying highp vec2 textureCoords; \n\ - varying lowp float opacity; \n\ - void setPosition(); \n\ - void main(void) \n\ - { \n\ - setPosition(); \n\ - textureCoords = textureCoordArray; \n\ - opacity = opacityArray; \n\ - }\n"; - -// NOTE: We let GL do the perspective correction so texture lookups in the fragment -// shader are also perspective corrected. -static const char* const qglslPositionOnlyVertexShader = "\n\ - attribute highp vec2 vertexCoordsArray; \n\ - attribute highp vec3 pmvMatrix1; \n\ - attribute highp vec3 pmvMatrix2; \n\ - attribute highp vec3 pmvMatrix3; \n\ - void setPosition(void) \n\ - { \n\ - highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ - vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ - gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\ - }\n"; - -static const char* const qglslComplexGeometryPositionOnlyVertexShader = "\n\ - uniform highp mat3 matrix; \n\ - uniform highp float translateZ; \n\ - attribute highp vec2 vertexCoordsArray; \n\ - void setPosition(void) \n\ - { \n\ - vec3 v = matrix * vec3(vertexCoordsArray, 1.0); \n\ - vec4 vz = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, translateZ, 1) * vec4(v, 1.0); \n\ - gl_Position = vec4(vz.xyz, 1.0);\n\ - } \n"; - -static const char* const qglslUntransformedPositionVertexShader = "\n\ - attribute highp vec4 vertexCoordsArray; \n\ - void setPosition(void) \n\ - { \n\ - gl_Position = vertexCoordsArray; \n\ - }\n"; - -// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125 -static const char* const qglslPositionWithPatternBrushVertexShader = "\n\ - attribute highp vec2 vertexCoordsArray; \n\ - attribute highp vec3 pmvMatrix1; \n\ - attribute highp vec3 pmvMatrix2; \n\ - attribute highp vec3 pmvMatrix3; \n\ - uniform mediump vec2 halfViewportSize; \n\ - uniform highp vec2 invertedTextureSize; \n\ - uniform highp mat3 brushTransform; \n\ - varying highp vec2 patternTexCoords; \n\ - void setPosition(void) \n\ - { \n\ - highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ - vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ - gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \n\ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ - gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ - patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \n\ - }\n"; - -static const char* const qglslAffinePositionWithPatternBrushVertexShader - = qglslPositionWithPatternBrushVertexShader; - -static const char* const qglslPatternBrushSrcFragmentShader = "\n\ - uniform sampler2D brushTexture; \n\ - uniform lowp vec4 patternColor; \n\ - varying highp vec2 patternTexCoords;\n\ - lowp vec4 srcPixel() \n\ - { \n\ - return patternColor * (1.0 - texture2D(brushTexture, patternTexCoords).r); \n\ - }\n"; - - -// Linear Gradient Brush -static const char* const qglslPositionWithLinearGradientBrushVertexShader = "\n\ - attribute highp vec2 vertexCoordsArray; \n\ - attribute highp vec3 pmvMatrix1; \n\ - attribute highp vec3 pmvMatrix2; \n\ - attribute highp vec3 pmvMatrix3; \n\ - uniform mediump vec2 halfViewportSize; \n\ - uniform highp vec3 linearData; \n\ - uniform highp mat3 brushTransform; \n\ - varying mediump float index; \n\ - void setPosition() \n\ - { \n\ - highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ - vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ - gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ - gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ - index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \n\ - }\n"; - -static const char* const qglslAffinePositionWithLinearGradientBrushVertexShader - = qglslPositionWithLinearGradientBrushVertexShader; - -static const char* const qglslLinearGradientBrushSrcFragmentShader = "\n\ - uniform sampler2D brushTexture; \n\ - varying mediump float index; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - mediump vec2 val = vec2(index, 0.5); \n\ - return texture2D(brushTexture, val); \n\ - }\n"; - - -// Conical Gradient Brush -static const char* const qglslPositionWithConicalGradientBrushVertexShader = "\n\ - attribute highp vec2 vertexCoordsArray; \n\ - attribute highp vec3 pmvMatrix1; \n\ - attribute highp vec3 pmvMatrix2; \n\ - attribute highp vec3 pmvMatrix3; \n\ - uniform mediump vec2 halfViewportSize; \n\ - uniform highp mat3 brushTransform; \n\ - varying highp vec2 A; \n\ - void setPosition(void) \n\ - { \n\ - highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ - vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ - gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ - gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ - A = hTexCoords.xy * invertedHTexCoordsZ; \n\ - }\n"; - -static const char* const qglslAffinePositionWithConicalGradientBrushVertexShader - = qglslPositionWithConicalGradientBrushVertexShader; - -static const char* const qglslConicalGradientBrushSrcFragmentShader = "\n\ - #define INVERSE_2PI 0.1591549430918953358 \n\ - uniform sampler2D brushTexture; \n\ - uniform mediump float angle; \n\ - varying highp vec2 A; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - highp float t; \n\ - if (abs(A.y) == abs(A.x)) \n\ - t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \n\ - else \n\ - t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \n\ - return texture2D(brushTexture, vec2(t - floor(t), 0.5)); \n\ - }\n"; - - -// Radial Gradient Brush -static const char* const qglslPositionWithRadialGradientBrushVertexShader = "\n\ - attribute highp vec2 vertexCoordsArray;\n\ - attribute highp vec3 pmvMatrix1; \n\ - attribute highp vec3 pmvMatrix2; \n\ - attribute highp vec3 pmvMatrix3; \n\ - uniform mediump vec2 halfViewportSize; \n\ - uniform highp mat3 brushTransform; \n\ - uniform highp vec2 fmp; \n\ - uniform mediump vec3 bradius; \n\ - varying highp float b; \n\ - varying highp vec2 A; \n\ - void setPosition(void) \n\ - {\n\ - highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ - vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ - gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ - gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ - A = hTexCoords.xy * invertedHTexCoordsZ; \n\ - b = bradius.x + 2.0 * dot(A, fmp); \n\ - }\n"; - -static const char* const qglslAffinePositionWithRadialGradientBrushVertexShader - = qglslPositionWithRadialGradientBrushVertexShader; - -static const char* const qglslRadialGradientBrushSrcFragmentShader = "\n\ - uniform sampler2D brushTexture; \n\ - uniform highp float fmp2_m_radius2; \n\ - uniform highp float inverse_2_fmp2_m_radius2; \n\ - uniform highp float sqrfr; \n\ - varying highp float b; \n\ - varying highp vec2 A; \n\ - uniform mediump vec3 bradius; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - highp float c = sqrfr-dot(A, A); \n\ - highp float det = b*b - 4.0*fmp2_m_radius2*c; \n\ - lowp vec4 result = vec4(0.0); \n\ - if (det >= 0.0) { \n\ - highp float detSqrt = sqrt(det); \n\ - highp float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\ - if (bradius.y + w * bradius.z >= 0.0) \n\ - result = texture2D(brushTexture, vec2(w, 0.5)); \n\ - } \n\ - return result; \n\ - }\n"; - - -// Texture Brush -static const char* const qglslPositionWithTextureBrushVertexShader = "\n\ - attribute highp vec2 vertexCoordsArray; \n\ - attribute highp vec3 pmvMatrix1; \n\ - attribute highp vec3 pmvMatrix2; \n\ - attribute highp vec3 pmvMatrix3; \n\ - uniform mediump vec2 halfViewportSize; \n\ - uniform highp vec2 invertedTextureSize; \n\ - uniform highp mat3 brushTransform; \n\ - varying highp vec2 brushTextureCoords; \n\ - void setPosition(void) \n\ - { \n\ - highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ - vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ - gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ - gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ - brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\ - }\n"; - -static const char* const qglslAffinePositionWithTextureBrushVertexShader - = qglslPositionWithTextureBrushVertexShader; - -// OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead, -// we emulate GL_REPEAT by only taking the fractional part of the texture coords. -// TODO: Special case POT textures which don't need this emulation -static const char* const qglslTextureBrushSrcFragmentShader_ES = "\n\ - varying highp vec2 brushTextureCoords; \n\ - uniform sampler2D brushTexture; \n\ - lowp vec4 srcPixel() { \n\ - return texture2D(brushTexture, fract(brushTextureCoords)); \n\ - }\n"; - -static const char* const qglslTextureBrushSrcFragmentShader_desktop = "\n\ - varying highp vec2 brushTextureCoords; \n\ - uniform sampler2D brushTexture; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - return texture2D(brushTexture, brushTextureCoords); \n\ - }\n"; - -static const char* const qglslTextureBrushSrcWithPatternFragmentShader = "\n\ - varying highp vec2 brushTextureCoords; \n\ - uniform lowp vec4 patternColor; \n\ - uniform sampler2D brushTexture; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \n\ - }\n"; - -// Solid Fill Brush -static const char* const qglslSolidBrushSrcFragmentShader = "\n\ - uniform lowp vec4 fragmentColor; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - return fragmentColor; \n\ - }\n"; - -static const char* const qglslImageSrcFragmentShader = "\n\ - varying highp vec2 textureCoords; \n\ - uniform sampler2D imageTexture; \n\ - lowp vec4 srcPixel() \n\ - { \n" - "return texture2D(imageTexture, textureCoords); \n" - "}\n"; - -static const char* const qglslCustomSrcFragmentShader = "\n\ - varying highp vec2 textureCoords; \n\ - uniform sampler2D imageTexture; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - return customShader(imageTexture, textureCoords); \n\ - }\n"; - -static const char* const qglslImageSrcWithPatternFragmentShader = "\n\ - varying highp vec2 textureCoords; \n\ - uniform lowp vec4 patternColor; \n\ - uniform sampler2D imageTexture; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - return patternColor * (1.0 - texture2D(imageTexture, textureCoords).r); \n\ - }\n"; - -static const char* const qglslNonPremultipliedImageSrcFragmentShader = "\n\ - varying highp vec2 textureCoords; \n\ - uniform sampler2D imageTexture; \n\ - lowp vec4 srcPixel() \n\ - { \n\ - lowp vec4 sample = texture2D(imageTexture, textureCoords); \n\ - sample.rgb = sample.rgb * sample.a; \n\ - return sample; \n\ - }\n"; - -static const char* const qglslShockingPinkSrcFragmentShader = "\n\ - lowp vec4 srcPixel() \n\ - { \n\ - return vec4(0.98, 0.06, 0.75, 1.0); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_ImageArrays = "\n\ - varying lowp float opacity; \n\ - lowp vec4 srcPixel(); \n\ - void main() \n\ - { \n\ - gl_FragColor = srcPixel() * opacity; \n\ - }\n"; - -static const char* const qglslMainFragmentShader_CMO = "\n\ - uniform lowp float globalOpacity; \n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 applyMask(lowp vec4); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_CM = "\n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 applyMask(lowp vec4); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = applyMask(compose(srcPixel())); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_MO = "\n\ - uniform lowp float globalOpacity; \n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 applyMask(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = applyMask(srcPixel()*globalOpacity); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_M = "\n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 applyMask(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = applyMask(srcPixel()); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_CO = "\n\ - uniform lowp float globalOpacity; \n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = compose(srcPixel()*globalOpacity); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_C = "\n\ - lowp vec4 srcPixel(); \n\ - lowp vec4 compose(lowp vec4); \n\ - void main() \n\ - { \n\ - gl_FragColor = compose(srcPixel()); \n\ - }\n"; - -static const char* const qglslMainFragmentShader_O = "\n\ - uniform lowp float globalOpacity; \n\ - lowp vec4 srcPixel(); \n\ - void main() \n\ - { \n\ - gl_FragColor = srcPixel()*globalOpacity; \n\ - }\n"; - -static const char* const qglslMainFragmentShader = "\n\ - lowp vec4 srcPixel(); \n\ - void main() \n\ - { \n\ - gl_FragColor = srcPixel(); \n\ - }\n"; - -static const char* const qglslMaskFragmentShader = "\n\ - varying highp vec2 textureCoords;\n\ - uniform sampler2D maskTexture;\n\ - lowp vec4 applyMask(lowp vec4 src) \n\ - {\n\ - lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ - return src * mask.a; \n\ - }\n"; - -// For source over with subpixel antialiasing, the final color is calculated per component as follows -// (.a is alpha component, .c is red, green or blue component): -// alpha = src.a * mask.c * opacity -// dest.c = dest.c * (1 - alpha) + src.c * alpha -// -// In the first pass, calculate: dest.c = dest.c * (1 - alpha) with blend funcs: zero, 1 - source color -// In the second pass, calculate: dest.c = dest.c + src.c * alpha with blend funcs: one, one -// -// If source is a solid color (src is constant), only the first pass is needed, with blend funcs: constant, 1 - source color - -// For source composition with subpixel antialiasing, the final color is calculated per component as follows: -// alpha = src.a * mask.c * opacity -// dest.c = dest.c * (1 - mask.c) + src.c * alpha -// - -static const char* const qglslRgbMaskFragmentShaderPass1 = "\n\ - varying highp vec2 textureCoords;\n\ - uniform sampler2D maskTexture;\n\ - lowp vec4 applyMask(lowp vec4 src) \n\ - { \n\ - lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ - return src.a * mask; \n\ - }\n"; - -static const char* const qglslRgbMaskFragmentShaderPass2 = "\n\ - varying highp vec2 textureCoords;\n\ - uniform sampler2D maskTexture;\n\ - lowp vec4 applyMask(lowp vec4 src) \n\ - { \n\ - lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ - return src * mask; \n\ - }\n"; - -/* - Left to implement: - RgbMaskFragmentShader, - RgbMaskWithGammaFragmentShader, - - MultiplyCompositionModeFragmentShader, - ScreenCompositionModeFragmentShader, - OverlayCompositionModeFragmentShader, - DarkenCompositionModeFragmentShader, - LightenCompositionModeFragmentShader, - ColorDodgeCompositionModeFragmentShader, - ColorBurnCompositionModeFragmentShader, - HardLightCompositionModeFragmentShader, - SoftLightCompositionModeFragmentShader, - DifferenceCompositionModeFragmentShader, - ExclusionCompositionModeFragmentShader, -*/ - -QT_END_NAMESPACE - -#endif // GLGC_SHADER_SOURCE_H diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp deleted file mode 100644 index d5ce4efd1a..0000000000 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtextureglyphcache_gl_p.h" -#include "qpaintengineex_opengl2_p.h" -#include "qglfunctions.h" -#include "private/qglengineshadersource_p.h" - -QT_BEGIN_NAMESPACE - - -static int next_qgltextureglyphcache_serial_number() -{ - static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0); - return 1 + serial.fetchAndAddRelaxed(1); -} - -QGLTextureGlyphCache::QGLTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix) - : QImageTextureGlyphCache(format, matrix) - , m_textureResource(0) - , pex(0) - , m_blitProgram(0) - , m_filterMode(Nearest) - , m_serialNumber(next_qgltextureglyphcache_serial_number()) -{ -#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG - qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, QOpenGLContext::currentContext()); -#endif - m_vertexCoordinateArray[0] = -1.0f; - m_vertexCoordinateArray[1] = -1.0f; - m_vertexCoordinateArray[2] = 1.0f; - m_vertexCoordinateArray[3] = -1.0f; - m_vertexCoordinateArray[4] = 1.0f; - m_vertexCoordinateArray[5] = 1.0f; - m_vertexCoordinateArray[6] = -1.0f; - m_vertexCoordinateArray[7] = 1.0f; - - m_textureCoordinateArray[0] = 0.0f; - m_textureCoordinateArray[1] = 0.0f; - m_textureCoordinateArray[2] = 1.0f; - m_textureCoordinateArray[3] = 0.0f; - m_textureCoordinateArray[4] = 1.0f; - m_textureCoordinateArray[5] = 1.0f; - m_textureCoordinateArray[6] = 0.0f; - m_textureCoordinateArray[7] = 1.0f; -} - -QGLTextureGlyphCache::~QGLTextureGlyphCache() -{ -#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG - qDebug(" -> ~QGLTextureGlyphCache() %p.", this); -#endif - delete m_blitProgram; - if (m_textureResource) - m_textureResource->free(); -} - -void QGLTextureGlyphCache::createTextureData(int width, int height) -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (ctx == 0) { - qWarning("QGLTextureGlyphCache::createTextureData: Called with no context"); - return; - } - QOpenGLFunctions *funcs = ctx->contextHandle()->functions(); - - // create in QImageTextureGlyphCache baseclass is meant to be called - // only to create the initial image and does not preserve the content, - // so we don't call when this function is called from resize. - if ((!QGLFramebufferObject::hasOpenGLFramebufferObjects() || ctx->d_ptr->workaround_brokenFBOReadBack) && image().isNull()) - QImageTextureGlyphCache::createTextureData(width, height); - - // Make the lower glyph texture size 16 x 16. - if (width < 16) - width = 16; - if (height < 16) - height = 16; - - if (m_textureResource && !m_textureResource->m_texture) { - delete m_textureResource; - m_textureResource = 0; - } - - if (!m_textureResource) - m_textureResource = new QGLGlyphTexture(ctx); - - funcs->glGenTextures(1, &m_textureResource->m_texture); - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - - m_textureResource->m_width = width; - m_textureResource->m_height = height; - - if (m_format == QFontEngine::Format_A32) { - QVarLengthArray<uchar> data(width * height * 4); - for (int i = 0; i < data.size(); ++i) - data[i] = 0; - funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); - } else { - QVarLengthArray<uchar> data(width * height); - for (int i = 0; i < data.size(); ++i) - data[i] = 0; - funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &data[0]); - } - - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_filterMode = Nearest; -} - -void QGLTextureGlyphCache::resizeTextureData(int width, int height) -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (ctx == 0) { - qWarning("QGLTextureGlyphCache::resizeTextureData: Called with no context"); - return; - } - QOpenGLFunctions *funcs = ctx->contextHandle()->functions(); - - int oldWidth = m_textureResource->m_width; - int oldHeight = m_textureResource->m_height; - - // Make the lower glyph texture size 16 x 16. - if (width < 16) - width = 16; - if (height < 16) - height = 16; - - GLuint oldTexture = m_textureResource->m_texture; - createTextureData(width, height); - - if (!QGLFramebufferObject::hasOpenGLFramebufferObjects() || ctx->d_ptr->workaround_brokenFBOReadBack) { - QImageTextureGlyphCache::resizeTextureData(width, height); - Q_ASSERT(image().depth() == 8); - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits()); - funcs->glDeleteTextures(1, &oldTexture); - return; - } - - // ### the QTextureGlyphCache API needs to be reworked to allow - // ### resizeTextureData to fail - - ctx->d_ptr->refreshCurrentFbo(); - - funcs->glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_fbo); - - GLuint tmp_texture; - funcs->glGenTextures(1, &tmp_texture); - funcs->glBindTexture(GL_TEXTURE_2D, tmp_texture); - funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_filterMode = Nearest; - funcs->glBindTexture(GL_TEXTURE_2D, 0); - funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, tmp_texture, 0); - - funcs->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - funcs->glBindTexture(GL_TEXTURE_2D, oldTexture); - - if (pex != 0) - pex->transferMode(BrushDrawingMode); - - funcs->glDisable(GL_STENCIL_TEST); - funcs->glDisable(GL_DEPTH_TEST); - funcs->glDisable(GL_SCISSOR_TEST); - funcs->glDisable(GL_BLEND); - - funcs->glViewport(0, 0, oldWidth, oldHeight); - - QGLShaderProgram *blitProgram = 0; - if (pex == 0) { - if (m_blitProgram == 0) { - m_blitProgram = new QGLShaderProgram(ctx); - - { - QString source; - source.append(QLatin1String(qglslMainWithTexCoordsVertexShader)); - source.append(QLatin1String(qglslUntransformedPositionVertexShader)); - - QGLShader *vertexShader = new QGLShader(QGLShader::Vertex, m_blitProgram); - vertexShader->compileSourceCode(source); - - m_blitProgram->addShader(vertexShader); - } - - { - QString source; - source.append(QLatin1String(qglslMainFragmentShader)); - source.append(QLatin1String(qglslImageSrcFragmentShader)); - - QGLShader *fragmentShader = new QGLShader(QGLShader::Fragment, m_blitProgram); - fragmentShader->compileSourceCode(source); - - m_blitProgram->addShader(fragmentShader); - } - - m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - - m_blitProgram->link(); - } - - funcs->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_vertexCoordinateArray); - funcs->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureCoordinateArray); - - m_blitProgram->bind(); - m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); - m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); - m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR)); - - blitProgram = m_blitProgram; - - } else { - pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray); - pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray); - - pex->shaderManager->useBlitProgram(); - blitProgram = pex->shaderManager->blitProgram(); - } - - blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT); - - funcs->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - - funcs->glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); - - funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, 0); - funcs->glDeleteTextures(1, &tmp_texture); - funcs->glDeleteTextures(1, &oldTexture); - - funcs->glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_ptr->current_fbo); - - if (pex != 0) { - funcs->glViewport(0, 0, pex->width, pex->height); - pex->updateClipScissorTest(); - } -} - -void QGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (ctx == 0) { - qWarning("QGLTextureGlyphCache::fillTexture: Called with no context"); - return; - } - QOpenGLFunctions *funcs = ctx->contextHandle()->functions(); - - if (!QGLFramebufferObject::hasOpenGLFramebufferObjects() || ctx->d_ptr->workaround_brokenFBOReadBack) { - QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - const QImage &texture = image(); - const uchar *bits = texture.constBits(); - bits += c.y * texture.bytesPerLine() + c.x; - for (int i=0; i<c.h; ++i) { - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits); - bits += texture.bytesPerLine(); - } - return; - } - - QImage mask = textureMapForGlyph(glyph, subPixelPosition); - const int maskWidth = mask.width(); - const int maskHeight = mask.height(); - - if (mask.format() == QImage::Format_Mono) { - mask = mask.convertToFormat(QImage::Format_Indexed8); - for (int y = 0; y < maskHeight; ++y) { - uchar *src = (uchar *) mask.scanLine(y); - for (int x = 0; x < maskWidth; ++x) - src[x] = -src[x]; // convert 0 and 1 into 0 and 255 - } - } else if (mask.depth() == 32) { - // Make the alpha component equal to the average of the RGB values. - // This is needed when drawing sub-pixel antialiased text on translucent targets. - for (int y = 0; y < maskHeight; ++y) { - quint32 *src = (quint32 *) mask.scanLine(y); - for (int x = 0; x < maskWidth; ++x) { - int r = qRed(src[x]); - int g = qGreen(src[x]); - int b = qBlue(src[x]); - int avg; - if (mask.format() == QImage::Format_RGB32) - avg = (r + g + b + 1) / 3; // "+1" for rounding. - else // Format_ARGB_Premultiplied - avg = qAlpha(src[x]); - if (ctx->contextHandle()->isOpenGLES()) { - // swizzle the bits to accommodate for the GL_RGBA upload. - src[x] = (avg << 24) | (r << 0) | (g << 8) | (b << 16); - } else { - src[x] = (src[x] & 0x00ffffff) | (avg << 24); - } - } - } - } - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - if (mask.depth() == 32) { - GLenum format = GL_RGBA; -#if !defined(QT_OPENGL_ES_2) - if (!ctx->contextHandle()->isOpenGLES()) - format = GL_BGRA; -#endif - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, format, GL_UNSIGNED_BYTE, mask.bits()); - } else { - // glTexSubImage2D() might cause some garbage to appear in the texture if the mask width is - // not a multiple of four bytes. The bug appeared on a computer with 32-bit Windows Vista - // and nVidia GeForce 8500GT. GL_UNPACK_ALIGNMENT is set to four bytes, 'mask' has a - // multiple of four bytes per line, and most of the glyph shows up correctly in the - // texture, which makes me think that this is a driver bug. - // One workaround is to make sure the mask width is a multiple of four bytes, for instance - // by converting it to a format with four bytes per pixel. Another is to copy one line at a - // time. - - if (!ctx->d_ptr->workaround_brokenAlphaTexSubImage_init) { - // don't know which driver versions exhibit this bug, so be conservative for now - const QByteArray vendorString(reinterpret_cast<const char*>(funcs->glGetString(GL_VENDOR))); - ctx->d_ptr->workaround_brokenAlphaTexSubImage = vendorString.indexOf("NVIDIA") >= 0; - ctx->d_ptr->workaround_brokenAlphaTexSubImage_init = true; - } - - if (ctx->d_ptr->workaround_brokenAlphaTexSubImage) { - for (int i = 0; i < maskHeight; ++i) - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, maskWidth, 1, GL_ALPHA, GL_UNSIGNED_BYTE, mask.scanLine(i)); - } else { - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, GL_ALPHA, GL_UNSIGNED_BYTE, mask.bits()); - } - } -} - -int QGLTextureGlyphCache::glyphPadding() const -{ - return 1; -} - -int QGLTextureGlyphCache::maxTextureWidth() const -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (ctx == 0) - return QImageTextureGlyphCache::maxTextureWidth(); - else - return ctx->d_ptr->maxTextureSize(); -} - -int QGLTextureGlyphCache::maxTextureHeight() const -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (ctx == 0) - return QImageTextureGlyphCache::maxTextureHeight(); - - if (ctx->d_ptr->workaround_brokenTexSubImage) - return qMin(1024, ctx->d_ptr->maxTextureSize()); - else - return ctx->d_ptr->maxTextureSize(); -} - -void QGLTextureGlyphCache::clear() -{ - m_textureResource->free(); - m_textureResource = 0; - - m_w = 0; - m_h = 0; - m_cx = 0; - m_cy = 0; - m_currentRowHeight = 0; - coords.clear(); -} - -QT_END_NAMESPACE diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 8b2349ff2f..2a5d7edbee 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -1,57 +1,64 @@ TARGET = QtOpenGL -QT = core-private gui-private widgets-private +QT = core-private gui-private +qtConfig(widgets): QT += widgets widgets-private DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH -msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x63000000 -solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 - QMAKE_DOCS = $$PWD/doc/qtopengl.qdocconf qtConfig(opengl): CONFIG += opengl qtConfig(opengles2): CONFIG += opengles2 -HEADERS += qgl.h \ - qgl_p.h \ - qglcolormap.h \ - qglfunctions.h \ - qglpixelbuffer.h \ - qglpixelbuffer_p.h \ - qglframebufferobject.h \ - qglframebufferobject_p.h \ - qglpaintdevice_p.h \ - qglbuffer.h \ - qtopenglglobal.h - -SOURCES += qgl.cpp \ - qglcolormap.cpp \ - qglfunctions.cpp \ - qglpixelbuffer.cpp \ - qglframebufferobject.cpp \ - qglpaintdevice.cpp \ - qglbuffer.cpp \ - -HEADERS += qglshaderprogram.h \ - gl2paintengineex/qglgradientcache_p.h \ - gl2paintengineex/qglengineshadermanager_p.h \ - gl2paintengineex/qgl2pexvertexarray_p.h \ - gl2paintengineex/qpaintengineex_opengl2_p.h \ - gl2paintengineex/qglengineshadersource_p.h \ - gl2paintengineex/qglcustomshaderstage_p.h \ - gl2paintengineex/qtextureglyphcache_gl_p.h \ - gl2paintengineex/qglshadercache_p.h - -SOURCES += qglshaderprogram.cpp \ - gl2paintengineex/qglgradientcache.cpp \ - gl2paintengineex/qglengineshadermanager.cpp \ - gl2paintengineex/qgl2pexvertexarray.cpp \ - gl2paintengineex/qpaintengineex_opengl2.cpp \ - gl2paintengineex/qglcustomshaderstage.cpp \ - gl2paintengineex/qtextureglyphcache_gl.cpp - -qtConfig(graphicseffect) { - HEADERS += qgraphicsshadereffect_p.h - SOURCES += qgraphicsshadereffect.cpp +HEADERS += \ + qopengl2pexvertexarray_p.h \ + qopenglcustomshaderstage_p.h \ + qopengldebug.h \ + qopenglengineshadermanager_p.h \ + qopenglengineshadersource_p.h \ + qopenglgradientcache_p.h \ + qopenglpaintdevice.h \ + qopenglpaintdevice_p.h \ + qopenglpaintengine_p.h \ + qopenglpixeltransferoptions.h \ + qopenglshadercache_p.h \ + qopengltexture.h \ + qopengltexture_p.h \ + qopengltexturehelper_p.h \ + qopengltextureblitter.h \ + qopengltexturecache_p.h \ + qopengltextureglyphcache_p.h \ + qopengltextureuploader_p.h \ + qopenglwindow.h \ + qtopenglglobal.h + +SOURCES += \ + qopengl2pexvertexarray.cpp \ + qopenglcustomshaderstage.cpp \ + qopenglengineshadermanager.cpp \ + qopenglgradientcache.cpp \ + qopenglpaintdevice.cpp \ + qopenglpaintengine.cpp \ + qopenglpixeltransferoptions.cpp \ + qopengltexture.cpp \ + qopengltexturehelper.cpp \ + qopengltextureblitter.cpp \ + qopengltexturecache.cpp \ + qopengltextureglyphcache.cpp \ + qopengltextureuploader.cpp \ + qopenglwindow.cpp \ + qopengldebug.cpp + +!qtConfig(opengles2) { + HEADERS += \ + qopenglqueryhelper_p.h \ + qopengltimerquery.h + + SOURCES += qopengltimerquery.cpp +} + +qtConfig(widgets) { + HEADERS += qopenglwidget.h + SOURCES += qopenglwidget.cpp } load(qt_module) diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp deleted file mode 100644 index 4108b70094..0000000000 --- a/src/opengl/qgl.cpp +++ /dev/null @@ -1,5558 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qapplication.h" -#include "qplatformdefs.h" -#include "qgl.h" -#include <qdebug.h> -#include <qglfunctions.h> - -#include <qdatetime.h> - -#include <stdlib.h> // malloc - -#include "qpixmap.h" -#include "qimage.h" -#include "qgl_p.h" - -#include "gl2paintengineex/qpaintengineex_opengl2_p.h" - -#include <qpa/qplatformopenglcontext.h> - -#include <qglpixelbuffer.h> -#include <qglframebufferobject.h> -#include <private/qopenglextensions_p.h> - -#include <private/qimage_p.h> -#include <qpa/qplatformpixmap.h> -#include <private/qglpixelbuffer_p.h> -#include <private/qimagepixmapcleanuphooks_p.h> -#include "qcolormap.h" -#include "qfile.h" -#include <qmutex.h> - -#include "qsurfaceformat.h" -#include <private/qapplication_p.h> -#include <qpa/qplatformopenglcontext.h> -#include <qpa/qplatformwindow.h> - -#ifndef QT_OPENGL_ES_2 -#include <qopenglfunctions_1_1.h> -#endif - -// #define QT_GL_CONTEXT_RESOURCE_DEBUG - -QT_BEGIN_NAMESPACE - -class QGLDefaultExtensions -{ -public: - QGLDefaultExtensions() - { - QGLTemporaryContext tempContext; - Q_ASSERT(QOpenGLContext::currentContext()); - QOpenGLExtensions *ext = qgl_extensions(); - Q_ASSERT(ext); - extensions = ext->openGLExtensions(); - features = ext->openGLFeatures(); - } - - QOpenGLFunctions::OpenGLFeatures features; - QOpenGLExtensions::OpenGLExtensions extensions; -}; - -Q_GLOBAL_STATIC(QGLDefaultExtensions, qtDefaultExtensions) - -bool qgl_hasFeature(QOpenGLFunctions::OpenGLFeature feature) -{ - if (QOpenGLContext::currentContext()) - return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(feature); - return qtDefaultExtensions()->features & feature; -} - -bool qgl_hasExtension(QOpenGLExtensions::OpenGLExtension extension) -{ - if (QOpenGLContext::currentContext()) - return qgl_extensions()->hasOpenGLExtension(extension); - return qtDefaultExtensions()->extensions & extension; -} - -QOpenGLExtensions::OpenGLExtensions extensions; - -/* - Returns the GL extensions for the current QOpenGLContext. If there is no - current QOpenGLContext, a default context will be created and the extensions - for that context will be returned instead. -*/ -QOpenGLExtensions* qgl_extensions() -{ - if (QOpenGLContext *context = QOpenGLContext::currentContext()) - return static_cast<QOpenGLExtensions *>(context->functions()); - - Q_ASSERT(false); - return 0; -} - -QOpenGLFunctions *qgl_functions() -{ - return qgl_extensions(); // QOpenGLExtensions is just a subclass of QOpenGLFunctions -} - -#ifndef QT_OPENGL_ES_2 -QOpenGLFunctions_1_1 *qgl1_functions() -{ - QOpenGLFunctions_1_1 *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_1_1>(); - f->initializeOpenGLFunctions(); - return f; -} -#endif - -struct QGLThreadContext { - ~QGLThreadContext() { - if (context) - context->doneCurrent(); - } - QGLContext *context; -}; - -Q_GLOBAL_STATIC(QGLFormat, qgl_default_format) - -class QGLDefaultOverlayFormat: public QGLFormat -{ -public: - inline QGLDefaultOverlayFormat() - { - setOption(QGL::FormatOption(0xffffU << 16)); // turn off all options - setOption(QGL::DirectRendering); - setPlane(1); - } -}; -Q_GLOBAL_STATIC(QGLDefaultOverlayFormat, defaultOverlayFormatInstance) - -Q_GLOBAL_STATIC(QGLSignalProxy, theSignalProxy) -QGLSignalProxy *QGLSignalProxy::instance() -{ - QGLSignalProxy *proxy = theSignalProxy(); - if (proxy && qApp && proxy->thread() != qApp->thread()) { - if (proxy->thread() == QThread::currentThread()) - proxy->moveToThread(qApp->thread()); - } - return proxy; -} - - -/*! - \namespace QGL - \inmodule QtOpenGL - - \brief The QGL namespace specifies miscellaneous identifiers used - in the Qt OpenGL module. -*/ - -/*! - \enum QGL::FormatOption - - This enum specifies the format options that can be used to configure an OpenGL - context. These are set using QGLFormat::setOption(). - - \value DoubleBuffer Specifies the use of double buffering. - \value DepthBuffer Enables the use of a depth buffer. - \value Rgba Specifies that the context should use RGBA as its pixel format. - \value AlphaChannel Enables the use of an alpha channel. - \value AccumBuffer Enables the use of an accumulation buffer. - \value StencilBuffer Enables the use of a stencil buffer. - \value StereoBuffers Enables the use of a stereo buffers for use with visualization hardware. - \value DirectRendering Specifies that the context is used for direct rendering to a display. - \value HasOverlay Enables the use of an overlay. - \value SampleBuffers Enables the use of sample buffers. - \value DeprecatedFunctions Enables the use of deprecated functionality for OpenGL 3.x - contexts. A context with deprecated functionality enabled is - called a full context in the OpenGL specification. - \value SingleBuffer Specifies the use of a single buffer, as opposed to double buffers. - \value NoDepthBuffer Disables the use of a depth buffer. - \value ColorIndex Specifies that the context should use a color index as its pixel format. - \value NoAlphaChannel Disables the use of an alpha channel. - \value NoAccumBuffer Disables the use of an accumulation buffer. - \value NoStencilBuffer Disables the use of a stencil buffer. - \value NoStereoBuffers Disables the use of stereo buffers. - \value IndirectRendering Specifies that the context is used for indirect rendering to a buffer. - \value NoOverlay Disables the use of an overlay. - \value NoSampleBuffers Disables the use of sample buffers. - \value NoDeprecatedFunctions Disables the use of deprecated functionality for OpenGL 3.x - contexts. A context with deprecated functionality disabled is - called a forward compatible context in the OpenGL specification. -*/ - -/***************************************************************************** - QGLFormat implementation - *****************************************************************************/ - - -/*! - \class QGLFormat - \inmodule QtOpenGL - \obsolete - - \brief The QGLFormat class specifies the display format of an OpenGL - rendering context. - - A display format has several characteristics: - \list - \li \l{setDoubleBuffer()}{Double or single buffering.} - \li \l{setDepth()}{Depth buffer.} - \li \l{setRgba()}{RGBA or color index mode.} - \li \l{setAlpha()}{Alpha channel.} - \li \l{setAccum()}{Accumulation buffer.} - \li \l{setStencil()}{Stencil buffer.} - \li \l{setStereo()}{Stereo buffers.} - \li \l{setDirectRendering()}{Direct rendering.} - \li \l{setOverlay()}{Presence of an overlay.} - \li \l{setPlane()}{Plane of an overlay.} - \li \l{setSampleBuffers()}{Multisample buffers.} - \endlist - - You can also specify preferred bit depths for the color buffer, - depth buffer, alpha buffer, accumulation buffer and the stencil - buffer with the functions: setRedBufferSize(), setGreenBufferSize(), - setBlueBufferSize(), setDepthBufferSize(), setAlphaBufferSize(), - setAccumBufferSize() and setStencilBufferSize(). - - Note that even if you specify that you prefer a 32 bit depth - buffer (e.g. with setDepthBufferSize(32)), the format that is - chosen may not have a 32 bit depth buffer, even if there is a - format available with a 32 bit depth buffer. The main reason for - this is how the system dependant picking algorithms work on the - different platforms, and some format options may have higher - precedence than others. - - You create and tell a QGLFormat object what rendering options you - want from an OpenGL rendering context. - - OpenGL drivers or accelerated hardware may or may not support - advanced features such as alpha channel or stereographic viewing. - If you request some features that the driver/hardware does not - provide when you create a QGLWidget, you will get a rendering - context with the nearest subset of features. - - There are different ways to define the display characteristics of - a rendering context. One is to create a QGLFormat and make it the - default for the entire application: - \snippet code/src_opengl_qgl.cpp 0 - - Or you can specify the desired format when creating an object of - your QGLWidget subclass: - \snippet code/src_opengl_qgl.cpp 1 - - After the widget has been created, you can find out which of the - requested features the system was able to provide: - \snippet code/src_opengl_qgl.cpp 2 - - \legalese - OpenGL is a trademark of Silicon Graphics, Inc. in the - United States and other countries. - \endlegalese - - \sa QGLContext, QGLWidget -*/ - -#ifndef QT_OPENGL_ES - -static inline void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4]) -{ -#define M(row,col) m[col*4+row] - out[0] = - M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3]; - out[1] = - M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3]; - out[2] = - M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3]; - out[3] = - M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3]; -#undef M -} - -static inline GLint qgluProject(GLdouble objx, GLdouble objy, GLdouble objz, - const GLdouble model[16], const GLdouble proj[16], - const GLint viewport[4], - GLdouble * winx, GLdouble * winy, GLdouble * winz) -{ - GLdouble in[4], out[4]; - - in[0] = objx; - in[1] = objy; - in[2] = objz; - in[3] = 1.0; - transform_point(out, model, in); - transform_point(in, proj, out); - - if (in[3] == 0.0) - return GL_FALSE; - - in[0] /= in[3]; - in[1] /= in[3]; - in[2] /= in[3]; - - *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2; - *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2; - - *winz = (1 + in[2]) / 2; - return GL_TRUE; -} - -#endif // !QT_OPENGL_ES - -/*! - Constructs a QGLFormat object with the following default settings: - \list - \li \l{setDoubleBuffer()}{Double buffer:} Enabled. - \li \l{setDepth()}{Depth buffer:} Enabled. - \li \l{setRgba()}{RGBA:} Enabled (i.e., color index disabled). - \li \l{setAlpha()}{Alpha channel:} Disabled. - \li \l{setAccum()}{Accumulator buffer:} Disabled. - \li \l{setStencil()}{Stencil buffer:} Enabled. - \li \l{setStereo()}{Stereo:} Disabled. - \li \l{setDirectRendering()}{Direct rendering:} Enabled. - \li \l{setOverlay()}{Overlay:} Disabled. - \li \l{setPlane()}{Plane:} 0 (i.e., normal plane). - \li \l{setSampleBuffers()}{Multisample buffers:} Disabled. - \endlist -*/ - -QGLFormat::QGLFormat() -{ - d = new QGLFormatPrivate; -} - - -/*! - Creates a QGLFormat object that is a copy of the current - defaultFormat(). - - If \a options is not 0, the default format is modified by the - specified format options. The \a options parameter should be - QGL::FormatOption values OR'ed together. - - This constructor makes it easy to specify a certain desired format - in classes derived from QGLWidget, for example: - \snippet code/src_opengl_qgl.cpp 3 - - Note that there are QGL::FormatOption values to turn format settings - both on and off, e.g. QGL::DepthBuffer and QGL::NoDepthBuffer, - QGL::DirectRendering and QGL::IndirectRendering, etc. - - The \a plane parameter defaults to 0 and is the plane which this - format should be associated with. Not all OpenGL implementations - supports overlay/underlay rendering planes. - - \sa defaultFormat(), setOption(), setPlane() -*/ - -QGLFormat::QGLFormat(QGL::FormatOptions options, int plane) -{ - d = new QGLFormatPrivate; - QGL::FormatOptions newOpts = options; - d->opts = defaultFormat().d->opts; - d->opts |= (newOpts & 0xffff); - d->opts &= ~(newOpts >> 16); - d->pln = plane; -} - -/*! - \internal -*/ -void QGLFormat::detach() -{ - if (d->ref.loadRelaxed() != 1) { - QGLFormatPrivate *newd = new QGLFormatPrivate(d); - if (!d->ref.deref()) - delete d; - d = newd; - } -} - -/*! - Constructs a copy of \a other. -*/ - -QGLFormat::QGLFormat(const QGLFormat &other) -{ - d = other.d; - d->ref.ref(); -} - -/*! - Assigns \a other to this object. -*/ - -QGLFormat &QGLFormat::operator=(const QGLFormat &other) -{ - if (d != other.d) { - other.d->ref.ref(); - if (!d->ref.deref()) - delete d; - d = other.d; - } - return *this; -} - -/*! - Destroys the QGLFormat. -*/ -QGLFormat::~QGLFormat() -{ - if (!d->ref.deref()) - delete d; -} - -/*! - Returns an OpenGL format for the window format specified by \a format. -*/ -QGLFormat QGLFormat::fromSurfaceFormat(const QSurfaceFormat &format) -{ - QGLFormat retFormat; - if (format.alphaBufferSize() >= 0) - retFormat.setAlphaBufferSize(format.alphaBufferSize()); - if (format.blueBufferSize() >= 0) - retFormat.setBlueBufferSize(format.blueBufferSize()); - if (format.greenBufferSize() >= 0) - retFormat.setGreenBufferSize(format.greenBufferSize()); - if (format.redBufferSize() >= 0) - retFormat.setRedBufferSize(format.redBufferSize()); - if (format.depthBufferSize() >= 0) - retFormat.setDepthBufferSize(format.depthBufferSize()); - if (format.samples() > 1) { - retFormat.setSampleBuffers(true); - retFormat.setSamples(format.samples()); - } - if (format.stencilBufferSize() > 0) { - retFormat.setStencil(true); - retFormat.setStencilBufferSize(format.stencilBufferSize()); - } - retFormat.setSwapInterval(format.swapInterval()); - retFormat.setDoubleBuffer(format.swapBehavior() != QSurfaceFormat::SingleBuffer); - retFormat.setStereo(format.stereo()); - retFormat.setVersion(format.majorVersion(), format.minorVersion()); - retFormat.setProfile(static_cast<QGLFormat::OpenGLContextProfile>(format.profile())); - return retFormat; -} - -/*! - Returns a window format for the OpenGL format specified by \a format. -*/ -QSurfaceFormat QGLFormat::toSurfaceFormat(const QGLFormat &format) -{ - QSurfaceFormat retFormat; - if (format.alpha()) - retFormat.setAlphaBufferSize(format.alphaBufferSize() == -1 ? 1 : format.alphaBufferSize()); - if (format.blueBufferSize() >= 0) - retFormat.setBlueBufferSize(format.blueBufferSize()); - if (format.greenBufferSize() >= 0) - retFormat.setGreenBufferSize(format.greenBufferSize()); - if (format.redBufferSize() >= 0) - retFormat.setRedBufferSize(format.redBufferSize()); - if (format.depth()) - retFormat.setDepthBufferSize(format.depthBufferSize() == -1 ? 1 : format.depthBufferSize()); - retFormat.setSwapBehavior(format.doubleBuffer() ? QSurfaceFormat::DoubleBuffer : QSurfaceFormat::SingleBuffer); - if (format.sampleBuffers()) - retFormat.setSamples(format.samples() == -1 ? 4 : format.samples()); - if (format.stencil()) - retFormat.setStencilBufferSize(format.stencilBufferSize() == -1 ? 1 : format.stencilBufferSize()); - retFormat.setSwapInterval(format.swapInterval()); - retFormat.setStereo(format.stereo()); - retFormat.setMajorVersion(format.majorVersion()); - retFormat.setMinorVersion(format.minorVersion()); - retFormat.setProfile(static_cast<QSurfaceFormat::OpenGLContextProfile>(format.profile())); - // QGLFormat has no way to set DeprecatedFunctions, that is, to tell that forward - // compatibility should not be requested. Some drivers fail to ignore the fwdcompat - // bit with compatibility profiles so make sure it is not set. - if (format.profile() == QGLFormat::CompatibilityProfile) - retFormat.setOption(QSurfaceFormat::DeprecatedFunctions); - return retFormat; -} - -void QGLContextPrivate::setupSharing() { - Q_Q(QGLContext); - QOpenGLContext *sharedContext = guiGlContext->shareContext(); - if (sharedContext) { - QGLContext *actualSharedContext = QGLContext::fromOpenGLContext(sharedContext); - sharing = true; - QGLContextGroup::addShare(q, actualSharedContext); - } -} - -void QGLContextPrivate::refreshCurrentFbo() -{ - QOpenGLContextPrivate *guiGlContextPrivate = - guiGlContext ? QOpenGLContextPrivate::get(guiGlContext) : 0; - - // if QOpenGLFramebufferObjects have been used in the mean-time, we've lost our cached value - if (guiGlContextPrivate && guiGlContextPrivate->qgl_current_fbo_invalid) { - GLint current; - QOpenGLFunctions *funcs = qgl_functions(); - funcs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤t); - - current_fbo = current; - - guiGlContextPrivate->qgl_current_fbo_invalid = false; - } -} - -void QGLContextPrivate::setCurrentFbo(GLuint fbo) -{ - current_fbo = fbo; - - QOpenGLContextPrivate *guiGlContextPrivate = - guiGlContext ? QOpenGLContextPrivate::get(guiGlContext) : 0; - - if (guiGlContextPrivate) - guiGlContextPrivate->qgl_current_fbo_invalid = false; -} - - -/*! - \fn bool QGLFormat::doubleBuffer() const - - Returns \c true if double buffering is enabled; otherwise returns - false. Double buffering is enabled by default. - - \sa setDoubleBuffer() -*/ - -/*! - If \a enable is true sets double buffering; otherwise sets single - buffering. - - Double buffering is enabled by default. - - Double buffering is a technique where graphics are rendered on an - off-screen buffer and not directly to the screen. When the drawing - has been completed, the program calls a swapBuffers() function to - exchange the screen contents with the buffer. The result is - flicker-free drawing and often better performance. - - Note that single buffered contexts are currently not supported - with EGL. - - \sa doubleBuffer(), QGLContext::swapBuffers(), - QGLWidget::swapBuffers() -*/ - -void QGLFormat::setDoubleBuffer(bool enable) -{ - setOption(enable ? QGL::DoubleBuffer : QGL::SingleBuffer); -} - - -/*! - \fn bool QGLFormat::depth() const - - Returns \c true if the depth buffer is enabled; otherwise returns - false. The depth buffer is enabled by default. - - \sa setDepth(), setDepthBufferSize() -*/ - -/*! - If \a enable is true enables the depth buffer; otherwise disables - the depth buffer. - - The depth buffer is enabled by default. - - The purpose of a depth buffer (or Z-buffering) is to remove hidden - surfaces. Pixels are assigned Z values based on the distance to - the viewer. A pixel with a high Z value is closer to the viewer - than a pixel with a low Z value. This information is used to - decide whether to draw a pixel or not. - - \sa depth(), setDepthBufferSize() -*/ - -void QGLFormat::setDepth(bool enable) -{ - setOption(enable ? QGL::DepthBuffer : QGL::NoDepthBuffer); -} - - -/*! - \fn bool QGLFormat::rgba() const - - Returns \c true if RGBA color mode is set. Returns \c false if color - index mode is set. The default color mode is RGBA. - - \sa setRgba() -*/ - -/*! - If \a enable is true sets RGBA mode. If \a enable is false sets - color index mode. - - The default color mode is RGBA. - - RGBA is the preferred mode for most OpenGL applications. In RGBA - color mode you specify colors as red + green + blue + alpha - quadruplets. - - In color index mode you specify an index into a color lookup - table. - - \sa rgba() -*/ - -void QGLFormat::setRgba(bool enable) -{ - setOption(enable ? QGL::Rgba : QGL::ColorIndex); -} - - -/*! - \fn bool QGLFormat::alpha() const - - Returns \c true if the alpha buffer in the framebuffer is enabled; - otherwise returns \c false. The alpha buffer is disabled by default. - - \sa setAlpha(), setAlphaBufferSize() -*/ - -/*! - If \a enable is true enables the alpha buffer; otherwise disables - the alpha buffer. - - The alpha buffer is disabled by default. - - The alpha buffer is typically used for implementing transparency - or translucency. The A in RGBA specifies the transparency of a - pixel. - - \sa alpha(), setAlphaBufferSize() -*/ - -void QGLFormat::setAlpha(bool enable) -{ - setOption(enable ? QGL::AlphaChannel : QGL::NoAlphaChannel); -} - - -/*! - \fn bool QGLFormat::accum() const - - Returns \c true if the accumulation buffer is enabled; otherwise - returns \c false. The accumulation buffer is disabled by default. - - \sa setAccum(), setAccumBufferSize() -*/ - -/*! - If \a enable is true enables the accumulation buffer; otherwise - disables the accumulation buffer. - - The accumulation buffer is disabled by default. - - The accumulation buffer is used to create blur effects and - multiple exposures. - - \sa accum(), setAccumBufferSize() -*/ - -void QGLFormat::setAccum(bool enable) -{ - setOption(enable ? QGL::AccumBuffer : QGL::NoAccumBuffer); -} - - -/*! - \fn bool QGLFormat::stencil() const - - Returns \c true if the stencil buffer is enabled; otherwise returns - false. The stencil buffer is enabled by default. - - \sa setStencil(), setStencilBufferSize() -*/ - -/*! - If \a enable is true enables the stencil buffer; otherwise - disables the stencil buffer. - - The stencil buffer is enabled by default. - - The stencil buffer masks certain parts of the drawing area so that - masked parts are not drawn on. - - \sa stencil(), setStencilBufferSize() -*/ - -void QGLFormat::setStencil(bool enable) -{ - setOption(enable ? QGL::StencilBuffer: QGL::NoStencilBuffer); -} - - -/*! - \fn bool QGLFormat::stereo() const - - Returns \c true if stereo buffering is enabled; otherwise returns - false. Stereo buffering is disabled by default. - - \sa setStereo() -*/ - -/*! - If \a enable is true enables stereo buffering; otherwise disables - stereo buffering. - - Stereo buffering is disabled by default. - - Stereo buffering provides extra color buffers to generate left-eye - and right-eye images. - - \sa stereo() -*/ - -void QGLFormat::setStereo(bool enable) -{ - setOption(enable ? QGL::StereoBuffers : QGL::NoStereoBuffers); -} - - -/*! - \fn bool QGLFormat::directRendering() const - - Returns \c true if direct rendering is enabled; otherwise returns - false. - - Direct rendering is enabled by default. - - \sa setDirectRendering() -*/ - -/*! - If \a enable is true enables direct rendering; otherwise disables - direct rendering. - - Direct rendering is enabled by default. - - Enabling this option will make OpenGL bypass the underlying window - system and render directly from hardware to the screen, if this is - supported by the system. - - \sa directRendering() -*/ - -void QGLFormat::setDirectRendering(bool enable) -{ - setOption(enable ? QGL::DirectRendering : QGL::IndirectRendering); -} - -/*! - \fn bool QGLFormat::sampleBuffers() const - - Returns \c true if multisample buffer support is enabled; otherwise - returns \c false. - - The multisample buffer is disabled by default. - - \sa setSampleBuffers() -*/ - -/*! - If \a enable is true, a GL context with multisample buffer support - is picked; otherwise ignored. - - \sa sampleBuffers(), setSamples(), samples() -*/ -void QGLFormat::setSampleBuffers(bool enable) -{ - setOption(enable ? QGL::SampleBuffers : QGL::NoSampleBuffers); -} - -/*! - Returns the number of samples per pixel when multisampling is - enabled. By default, the highest number of samples that is - available is used. - - \sa setSampleBuffers(), sampleBuffers(), setSamples() -*/ -int QGLFormat::samples() const -{ - return d->numSamples; -} - -/*! - Set the preferred number of samples per pixel when multisampling - is enabled to \a numSamples. By default, the highest number of - samples available is used. - - \sa setSampleBuffers(), sampleBuffers(), samples() -*/ -void QGLFormat::setSamples(int numSamples) -{ - detach(); - if (numSamples < 0) { - qWarning("QGLFormat::setSamples: Cannot have negative number of samples per pixel %d", numSamples); - return; - } - d->numSamples = numSamples; - setSampleBuffers(numSamples > 0); -} - -/*! - \since 4.2 - - Set the preferred swap interval. This can be used to sync the GL - drawing into a system window to the vertical refresh of the screen. - Setting an \a interval value of 0 will turn the vertical refresh syncing - off, any value higher than 0 will turn the vertical syncing on. - - Under Windows and under X11, where the \c{WGL_EXT_swap_control} - and \c{GLX_SGI_video_sync} extensions are used, the \a interval - parameter can be used to set the minimum number of video frames - that are displayed before a buffer swap will occur. In effect, - setting the \a interval to 10, means there will be 10 vertical - retraces between every buffer swap. - - Under Windows the \c{WGL_EXT_swap_control} extension has to be present, - and under X11 the \c{GLX_SGI_video_sync} extension has to be present. -*/ -void QGLFormat::setSwapInterval(int interval) -{ - detach(); - d->swapInterval = interval; -} - -/*! - \since 4.2 - - Returns the currently set swap interval. -1 is returned if setting - the swap interval isn't supported in the system GL implementation. -*/ -int QGLFormat::swapInterval() const -{ - return d->swapInterval; -} - -/*! - \fn bool QGLFormat::hasOverlay() const - - Returns \c true if overlay plane is enabled; otherwise returns \c false. - - Overlay is disabled by default. - - \sa setOverlay() -*/ - -/*! - If \a enable is true enables an overlay plane; otherwise disables - the overlay plane. - - Enabling the overlay plane will cause QGLWidget to create an - additional context in an overlay plane. See the QGLWidget - documentation for further information. - - \sa hasOverlay() -*/ - -void QGLFormat::setOverlay(bool enable) -{ - setOption(enable ? QGL::HasOverlay : QGL::NoOverlay); -} - -/*! - Returns the plane of this format. The default for normal formats - is 0, which means the normal plane. The default for overlay - formats is 1, which is the first overlay plane. - - \sa setPlane(), defaultOverlayFormat() -*/ -int QGLFormat::plane() const -{ - return d->pln; -} - -/*! - Sets the requested plane to \a plane. 0 is the normal plane, 1 is - the first overlay plane, 2 is the second overlay plane, etc.; -1, - -2, etc. are underlay planes. - - Note that in contrast to other format specifications, the plane - specifications will be matched exactly. This means that if you - specify a plane that the underlying OpenGL system cannot provide, - an \l{QGLWidget::isValid()}{invalid} QGLWidget will be - created. - - \sa plane() -*/ -void QGLFormat::setPlane(int plane) -{ - detach(); - d->pln = plane; -} - -/*! - Sets the format option to \a opt. - - \sa testOption() -*/ - -void QGLFormat::setOption(QGL::FormatOptions opt) -{ - detach(); - if (opt & 0xffff) - d->opts |= opt; - else - d->opts &= ~(opt >> 16); -} - - - -/*! - Returns \c true if format option \a opt is set; otherwise returns \c false. - - \sa setOption() -*/ - -bool QGLFormat::testOption(QGL::FormatOptions opt) const -{ - if (opt & 0xffff) - return (d->opts & opt) != 0; - else - return (d->opts & (opt >> 16)) == 0; -} - -/*! - Set the minimum depth buffer size to \a size. - - \sa depthBufferSize(), setDepth(), depth() -*/ -void QGLFormat::setDepthBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size %d", size); - return; - } - d->depthSize = size; - setDepth(size > 0); -} - -/*! - Returns the depth buffer size. - - \sa depth(), setDepth(), setDepthBufferSize() -*/ -int QGLFormat::depthBufferSize() const -{ - return d->depthSize; -} - -/*! - \since 4.2 - - Set the preferred red buffer size to \a size. - - \sa setGreenBufferSize(), setBlueBufferSize(), setAlphaBufferSize() -*/ -void QGLFormat::setRedBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setRedBufferSize: Cannot set negative red buffer size %d", size); - return; - } - d->redSize = size; -} - -/*! - \since 4.2 - - Returns the red buffer size. - - \sa setRedBufferSize() -*/ -int QGLFormat::redBufferSize() const -{ - return d->redSize; -} - -/*! - \since 4.2 - - Set the preferred green buffer size to \a size. - - \sa setRedBufferSize(), setBlueBufferSize(), setAlphaBufferSize() -*/ -void QGLFormat::setGreenBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setGreenBufferSize: Cannot set negative green buffer size %d", size); - return; - } - d->greenSize = size; -} - -/*! - \since 4.2 - - Returns the green buffer size. - - \sa setGreenBufferSize() -*/ -int QGLFormat::greenBufferSize() const -{ - return d->greenSize; -} - -/*! - \since 4.2 - - Set the preferred blue buffer size to \a size. - - \sa setRedBufferSize(), setGreenBufferSize(), setAlphaBufferSize() -*/ -void QGLFormat::setBlueBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size %d", size); - return; - } - d->blueSize = size; -} - -/*! - \since 4.2 - - Returns the blue buffer size. - - \sa setBlueBufferSize() -*/ -int QGLFormat::blueBufferSize() const -{ - return d->blueSize; -} - -/*! - Set the preferred alpha buffer size to \a size. - This function implicitly enables the alpha channel. - - \sa setRedBufferSize(), setGreenBufferSize(), alphaBufferSize() -*/ -void QGLFormat::setAlphaBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size %d", size); - return; - } - d->alphaSize = size; - setAlpha(size > 0); -} - -/*! - Returns the alpha buffer size. - - \sa alpha(), setAlpha(), setAlphaBufferSize() -*/ -int QGLFormat::alphaBufferSize() const -{ - return d->alphaSize; -} - -/*! - Set the preferred accumulation buffer size, where \a size is the - bit depth for each RGBA component. - - \sa accum(), setAccum(), accumBufferSize() -*/ -void QGLFormat::setAccumBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size %d", size); - return; - } - d->accumSize = size; - setAccum(size > 0); -} - -/*! - Returns the accumulation buffer size. - - \sa setAccumBufferSize(), accum(), setAccum() -*/ -int QGLFormat::accumBufferSize() const -{ - return d->accumSize; -} - -/*! - Set the preferred stencil buffer size to \a size. - - \sa stencilBufferSize(), setStencil(), stencil() -*/ -void QGLFormat::setStencilBufferSize(int size) -{ - detach(); - if (size < 0) { - qWarning("QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size %d", size); - return; - } - d->stencilSize = size; - setStencil(size > 0); -} - -/*! - Returns the stencil buffer size. - - \sa stencil(), setStencil(), setStencilBufferSize() -*/ -int QGLFormat::stencilBufferSize() const -{ - return d->stencilSize; -} - -/*! - \since 4.7 - - Set the OpenGL version to the \a major and \a minor numbers. If a - context compatible with the requested OpenGL version cannot be - created, a context compatible with version 1.x is created instead. - - \sa majorVersion(), minorVersion() -*/ -void QGLFormat::setVersion(int major, int minor) -{ - if (major < 1 || minor < 0) { - qWarning("QGLFormat::setVersion: Cannot set zero or negative version number %d.%d", major, minor); - return; - } - detach(); - d->majorVersion = major; - d->minorVersion = minor; -} - -/*! - \since 4.7 - - Returns the OpenGL major version. - - \sa setVersion(), minorVersion() -*/ -int QGLFormat::majorVersion() const -{ - return d->majorVersion; -} - -/*! - \since 4.7 - - Returns the OpenGL minor version. - - \sa setVersion(), majorVersion() -*/ -int QGLFormat::minorVersion() const -{ - return d->minorVersion; -} - -/*! - \enum QGLFormat::OpenGLContextProfile - \since 4.7 - - This enum describes the OpenGL context profiles that can be - specified for contexts implementing OpenGL version 3.2 or - higher. These profiles are different from OpenGL ES profiles. - - \value NoProfile OpenGL version is lower than 3.2. - \value CoreProfile Functionality deprecated in OpenGL version 3.0 is not available. - \value CompatibilityProfile Functionality from earlier OpenGL versions is available. -*/ - -/*! - \since 4.7 - - Set the OpenGL context profile to \a profile. The \a profile is - ignored if the requested OpenGL version is less than 3.2. - - \sa profile() -*/ -void QGLFormat::setProfile(OpenGLContextProfile profile) -{ - detach(); - d->profile = profile; -} - -/*! - \since 4.7 - - Returns the OpenGL context profile. - - \sa setProfile() -*/ -QGLFormat::OpenGLContextProfile QGLFormat::profile() const -{ - return d->profile; -} - - -/*! - \fn bool QGLFormat::hasOpenGL() - - Returns \c true if the window system has any OpenGL support; - otherwise returns \c false. - - \warning This function must not be called until the QApplication - object has been created. -*/ -bool QGLFormat::hasOpenGL() -{ - return QApplicationPrivate::platformIntegration() - ->hasCapability(QPlatformIntegration::OpenGL); -} - -/*! - \fn bool QGLFormat::hasOpenGLOverlays() - - Returns \c true if the window system supports OpenGL overlays; - otherwise returns \c false. - - \warning This function must not be called until the QApplication - object has been created. -*/ -bool QGLFormat::hasOpenGLOverlays() -{ - return false; -} - -QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(const QString &versionString) -{ - QGLFormat::OpenGLVersionFlags versionFlags = QGLFormat::OpenGL_Version_None; - - if (versionString.startsWith(QLatin1String("OpenGL ES"))) { - const auto parts = versionString.splitRef(QLatin1Char(' ')); - if (parts.size() >= 3) { - if (parts[2].startsWith(QLatin1String("1."))) { - if (parts[1].endsWith(QLatin1String("-CM"))) { - versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_0 | - QGLFormat::OpenGL_ES_CommonLite_Version_1_0; - if (parts[2].startsWith(QLatin1String("1.1"))) - versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_1 | - QGLFormat::OpenGL_ES_CommonLite_Version_1_1; - } else { - // Not -CM, must be CL, CommonLite - versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_0; - if (parts[2].startsWith(QLatin1String("1.1"))) - versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_1; - } - } else { - // OpenGL ES version 2.0 or higher - versionFlags |= QGLFormat::OpenGL_ES_Version_2_0; - } - } else { - // if < 3 parts to the name, it is an unrecognised OpenGL ES - qWarning("Unrecognised OpenGL ES version"); - } - } else { - // not ES, regular OpenGL, the version numbers are first in the string - if (versionString.startsWith(QLatin1String("1."))) { - switch (versionString[2].toLatin1()) { - case '5': - versionFlags |= QGLFormat::OpenGL_Version_1_5; - Q_FALLTHROUGH(); - case '4': - versionFlags |= QGLFormat::OpenGL_Version_1_4; - Q_FALLTHROUGH(); - case '3': - versionFlags |= QGLFormat::OpenGL_Version_1_3; - Q_FALLTHROUGH(); - case '2': - versionFlags |= QGLFormat::OpenGL_Version_1_2; - Q_FALLTHROUGH(); - case '1': - versionFlags |= QGLFormat::OpenGL_Version_1_1; - Q_FALLTHROUGH(); - default: - break; - } - } else if (versionString.startsWith(QLatin1String("2."))) { - versionFlags |= QGLFormat::OpenGL_Version_1_1 | - QGLFormat::OpenGL_Version_1_2 | - QGLFormat::OpenGL_Version_1_3 | - QGLFormat::OpenGL_Version_1_4 | - QGLFormat::OpenGL_Version_1_5 | - QGLFormat::OpenGL_Version_2_0; - if (versionString[2].toLatin1() == '1') - versionFlags |= QGLFormat::OpenGL_Version_2_1; - } else if (versionString.startsWith(QLatin1String("3."))) { - versionFlags |= QGLFormat::OpenGL_Version_1_1 | - QGLFormat::OpenGL_Version_1_2 | - QGLFormat::OpenGL_Version_1_3 | - QGLFormat::OpenGL_Version_1_4 | - QGLFormat::OpenGL_Version_1_5 | - QGLFormat::OpenGL_Version_2_0 | - QGLFormat::OpenGL_Version_2_1 | - QGLFormat::OpenGL_Version_3_0; - switch (versionString[2].toLatin1()) { - case '3': - versionFlags |= QGLFormat::OpenGL_Version_3_3; - Q_FALLTHROUGH(); - case '2': - versionFlags |= QGLFormat::OpenGL_Version_3_2; - Q_FALLTHROUGH(); - case '1': - versionFlags |= QGLFormat::OpenGL_Version_3_1; - Q_FALLTHROUGH(); - case '0': - break; - default: - versionFlags |= QGLFormat::OpenGL_Version_3_1 | - QGLFormat::OpenGL_Version_3_2 | - QGLFormat::OpenGL_Version_3_3; - break; - } - } else if (versionString.startsWith(QLatin1String("4."))) { - versionFlags |= QGLFormat::OpenGL_Version_1_1 | - QGLFormat::OpenGL_Version_1_2 | - QGLFormat::OpenGL_Version_1_3 | - QGLFormat::OpenGL_Version_1_4 | - QGLFormat::OpenGL_Version_1_5 | - QGLFormat::OpenGL_Version_2_0 | - QGLFormat::OpenGL_Version_2_1 | - QGLFormat::OpenGL_Version_3_0 | - QGLFormat::OpenGL_Version_3_1 | - QGLFormat::OpenGL_Version_3_2 | - QGLFormat::OpenGL_Version_3_3 | - QGLFormat::OpenGL_Version_4_0; - switch (versionString[2].toLatin1()) { - case '3': - versionFlags |= QGLFormat::OpenGL_Version_4_3; - Q_FALLTHROUGH(); - case '2': - versionFlags |= QGLFormat::OpenGL_Version_4_2; - Q_FALLTHROUGH(); - case '1': - versionFlags |= QGLFormat::OpenGL_Version_4_1; - Q_FALLTHROUGH(); - case '0': - break; - default: - versionFlags |= QGLFormat::OpenGL_Version_4_1 | - QGLFormat::OpenGL_Version_4_2 | - QGLFormat::OpenGL_Version_4_3; - break; - } - } else { - versionFlags |= QGLFormat::OpenGL_Version_1_1 | - QGLFormat::OpenGL_Version_1_2 | - QGLFormat::OpenGL_Version_1_3 | - QGLFormat::OpenGL_Version_1_4 | - QGLFormat::OpenGL_Version_1_5 | - QGLFormat::OpenGL_Version_2_0 | - QGLFormat::OpenGL_Version_2_1 | - QGLFormat::OpenGL_Version_3_0 | - QGLFormat::OpenGL_Version_3_1 | - QGLFormat::OpenGL_Version_3_2 | - QGLFormat::OpenGL_Version_3_3 | - QGLFormat::OpenGL_Version_4_0 | - QGLFormat::OpenGL_Version_4_1 | - QGLFormat::OpenGL_Version_4_2 | - QGLFormat::OpenGL_Version_4_3; - } - } - return versionFlags; -} - -/*! - \enum QGLFormat::OpenGLVersionFlag - \since 4.2 - - This enum describes the various OpenGL versions that are - recognized by Qt. Use the QGLFormat::openGLVersionFlags() function - to identify which versions that are supported at runtime. - - \value OpenGL_Version_None If no OpenGL is present or if no OpenGL context is current. - - \value OpenGL_Version_1_1 OpenGL version 1.1 or higher is present. - - \value OpenGL_Version_1_2 OpenGL version 1.2 or higher is present. - - \value OpenGL_Version_1_3 OpenGL version 1.3 or higher is present. - - \value OpenGL_Version_1_4 OpenGL version 1.4 or higher is present. - - \value OpenGL_Version_1_5 OpenGL version 1.5 or higher is present. - - \value OpenGL_Version_2_0 OpenGL version 2.0 or higher is present. - Note that version 2.0 supports all the functionality of version 1.5. - - \value OpenGL_Version_2_1 OpenGL version 2.1 or higher is present. - - \value OpenGL_Version_3_0 OpenGL version 3.0 or higher is present. - - \value OpenGL_Version_3_1 OpenGL version 3.1 or higher is present. - Note that OpenGL version 3.1 or higher does not necessarily support all the features of - version 3.0 and lower. - - \value OpenGL_Version_3_2 OpenGL version 3.2 or higher is present. - - \value OpenGL_Version_3_3 OpenGL version 3.3 or higher is present. - - \value OpenGL_Version_4_0 OpenGL version 4.0 or higher is present. - - \value OpenGL_Version_4_1 OpenGL version 4.1 or higher is present. - - \value OpenGL_Version_4_2 OpenGL version 4.2 or higher is present. - - \value OpenGL_Version_4_3 OpenGL version 4.3 or higher is present. - - \value OpenGL_ES_CommonLite_Version_1_0 OpenGL ES version 1.0 Common Lite or higher is present. - - \value OpenGL_ES_Common_Version_1_0 OpenGL ES version 1.0 Common or higher is present. - The Common profile supports all the features of Common Lite. - - \value OpenGL_ES_CommonLite_Version_1_1 OpenGL ES version 1.1 Common Lite or higher is present. - - \value OpenGL_ES_Common_Version_1_1 OpenGL ES version 1.1 Common or higher is present. - The Common profile supports all the features of Common Lite. - - \value OpenGL_ES_Version_2_0 OpenGL ES version 2.0 or higher is present. - Note that OpenGL ES version 2.0 does not support all the features of OpenGL ES 1.x. - So if OpenGL_ES_Version_2_0 is returned, none of the ES 1.x flags are returned. - - See also \l{http://www.opengl.org} for more information about the different - revisions of OpenGL. - - \sa openGLVersionFlags() -*/ - -/*! - \since 4.2 - - Identifies, at runtime, which OpenGL versions that are supported - by the current platform. - - Note that if OpenGL version 1.5 is supported, its predecessors - (i.e., version 1.4 and lower) are also supported. To identify the - support of a particular feature, like multi texturing, test for - the version in which the feature was first introduced (i.e., - version 1.3 in the case of multi texturing) to adapt to the largest - possible group of runtime platforms. - - This function needs a valid current OpenGL context to work; - otherwise it will return OpenGL_Version_None. - - \sa hasOpenGL(), hasOpenGLOverlays() -*/ -QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags() -{ - static bool cachedDefault = false; - static OpenGLVersionFlags defaultVersionFlags = OpenGL_Version_None; - QGLContext *currentCtx = const_cast<QGLContext *>(QGLContext::currentContext()); - QGLTemporaryContext *tmpContext = 0; - - if (currentCtx && currentCtx->d_func()->version_flags_cached) - return currentCtx->d_func()->version_flags; - - if (!currentCtx) { - if (cachedDefault) { - return defaultVersionFlags; - } else { - if (!hasOpenGL()) - return defaultVersionFlags; - tmpContext = new QGLTemporaryContext; - cachedDefault = true; - } - } - - QString versionString(QLatin1String(reinterpret_cast<const char*>(qgl_functions()->glGetString(GL_VERSION)))); - OpenGLVersionFlags versionFlags = qOpenGLVersionFlagsFromString(versionString); - if (currentCtx) { - currentCtx->d_func()->version_flags_cached = true; - currentCtx->d_func()->version_flags = versionFlags; - } - if (tmpContext) { - defaultVersionFlags = versionFlags; - delete tmpContext; - } - - return versionFlags; -} - - -/*! - Returns the default QGLFormat for the application. All QGLWidget - objects that are created use this format unless another format is - specified, e.g. when they are constructed. - - If no special default format has been set using - setDefaultFormat(), the default format is the same as that created - with QGLFormat(). - - \sa setDefaultFormat() -*/ - -QGLFormat QGLFormat::defaultFormat() -{ - return *qgl_default_format(); -} - -/*! - Sets a new default QGLFormat for the application to \a f. For - example, to set single buffering as the default instead of double - buffering, your main() might contain code like this: - \snippet code/src_opengl_qgl.cpp 4 - - \sa defaultFormat() -*/ - -void QGLFormat::setDefaultFormat(const QGLFormat &f) -{ - *qgl_default_format() = f; -} - - -/*! - Returns the default QGLFormat for overlay contexts. - - The default overlay format is: - \list - \li \l{setDoubleBuffer()}{Double buffer:} Disabled. - \li \l{setDepth()}{Depth buffer:} Disabled. - \li \l{setRgba()}{RGBA:} Disabled (i.e., color index enabled). - \li \l{setAlpha()}{Alpha channel:} Disabled. - \li \l{setAccum()}{Accumulator buffer:} Disabled. - \li \l{setStencil()}{Stencil buffer:} Disabled. - \li \l{setStereo()}{Stereo:} Disabled. - \li \l{setDirectRendering()}{Direct rendering:} Enabled. - \li \l{setOverlay()}{Overlay:} Disabled. - \li \l{setSampleBuffers()}{Multisample buffers:} Disabled. - \li \l{setPlane()}{Plane:} 1 (i.e., first overlay plane). - \endlist - - \sa setDefaultFormat() -*/ - -QGLFormat QGLFormat::defaultOverlayFormat() -{ - return *defaultOverlayFormatInstance(); -} - -/*! - Sets a new default QGLFormat for overlay contexts to \a f. This - format is used whenever a QGLWidget is created with a format that - hasOverlay() enabled. - - For example, to get a double buffered overlay context (if - available), use code like this: - - \snippet code/src_opengl_qgl.cpp 5 - - As usual, you can find out after widget creation whether the - underlying OpenGL system was able to provide the requested - specification: - - \snippet code/src_opengl_qgl.cpp 6 - - \sa defaultOverlayFormat() -*/ - -void QGLFormat::setDefaultOverlayFormat(const QGLFormat &f) -{ - QGLFormat *defaultFormat = defaultOverlayFormatInstance(); - *defaultFormat = f; - // Make sure the user doesn't request that the overlays themselves - // have overlays, since it is unlikely that the system supports - // infinitely many planes... - defaultFormat->setOverlay(false); -} - - -/*! - Returns \c true if all the options of the two QGLFormat objects - \a a and \a b are equal; otherwise returns \c false. - - \relates QGLFormat -*/ - -bool operator==(const QGLFormat& a, const QGLFormat& b) -{ - return (a.d == b.d) || ((int) a.d->opts == (int) b.d->opts - && a.d->pln == b.d->pln - && a.d->alphaSize == b.d->alphaSize - && a.d->accumSize == b.d->accumSize - && a.d->stencilSize == b.d->stencilSize - && a.d->depthSize == b.d->depthSize - && a.d->redSize == b.d->redSize - && a.d->greenSize == b.d->greenSize - && a.d->blueSize == b.d->blueSize - && a.d->numSamples == b.d->numSamples - && a.d->swapInterval == b.d->swapInterval - && a.d->majorVersion == b.d->majorVersion - && a.d->minorVersion == b.d->minorVersion - && a.d->profile == b.d->profile); -} - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug dbg, const QGLFormat &f) -{ - const QGLFormatPrivate * const d = f.d; - - QDebugStateSaver saver(dbg); - dbg.nospace() << "QGLFormat(" - << "options " << d->opts - << ", plane " << d->pln - << ", depthBufferSize " << d->depthSize - << ", accumBufferSize " << d->accumSize - << ", stencilBufferSize " << d->stencilSize - << ", redBufferSize " << d->redSize - << ", greenBufferSize " << d->greenSize - << ", blueBufferSize " << d->blueSize - << ", alphaBufferSize " << d->alphaSize - << ", samples " << d->numSamples - << ", swapInterval " << d->swapInterval - << ", majorVersion " << d->majorVersion - << ", minorVersion " << d->minorVersion - << ", profile " << d->profile - << ')'; - - return dbg; -} -#endif - - -/*! - Returns \c false if all the options of the two QGLFormat objects - \a a and \a b are equal; otherwise returns \c true. - - \relates QGLFormat -*/ - -bool operator!=(const QGLFormat& a, const QGLFormat& b) -{ - return !(a == b); -} - -struct QGLContextGroupList { - void append(QGLContextGroup *group) { - QMutexLocker locker(&m_mutex); - m_list.append(group); - } - - void remove(QGLContextGroup *group) { - QMutexLocker locker(&m_mutex); - m_list.removeOne(group); - } - - QList<QGLContextGroup *> m_list; - QRecursiveMutex m_mutex; -}; - -Q_GLOBAL_STATIC(QGLContextGroupList, qt_context_groups) - -/***************************************************************************** - QGLContext implementation - *****************************************************************************/ - -QGLContextGroup::QGLContextGroup(const QGLContext *context) - : m_context(context), m_refs(1) -{ - qt_context_groups()->append(this); -} - -QGLContextGroup::~QGLContextGroup() -{ - qt_context_groups()->remove(this); -} - -const QGLContext *qt_gl_transfer_context(const QGLContext *ctx) -{ - if (!ctx) - return 0; - QList<const QGLContext *> shares - (QGLContextPrivate::contextGroup(ctx)->shares()); - if (shares.size() >= 2) - return (ctx == shares.at(0)) ? shares.at(1) : shares.at(0); - else - return 0; -} - -QGLContextPrivate::QGLContextPrivate(QGLContext *context) - : internal_context(false) - , q_ptr(context) - , texture_destroyer(0) - , functions(0) -{ - group = new QGLContextGroup(context); - - texture_destroyer = new QGLTextureDestroyer; -} - -QGLContextPrivate::~QGLContextPrivate() -{ - delete functions; - - if (!group->m_refs.deref()) { - Q_ASSERT(group->context() == q_ptr); - delete group; - } - - delete texture_destroyer; -} - -void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format) -{ - Q_Q(QGLContext); - glFormat = reqFormat = format; - valid = false; - q->setDevice(dev); - - guiGlContext = 0; - ownContext = false; - fbo = 0; - crWin = false; - initDone = false; - sharing = false; - max_texture_size = -1; - version_flags_cached = false; - version_flags = QGLFormat::OpenGL_Version_None; - current_fbo = 0; - default_fbo = 0; - active_engine = 0; - workaround_needsFullClearOnEveryFrame = false; - workaround_brokenFBOReadBack = false; - workaround_brokenTexSubImage = false; - workaroundsCached = false; - - workaround_brokenTextureFromPixmap = false; - workaround_brokenTextureFromPixmap_init = false; - - workaround_brokenAlphaTexSubImage = false; - workaround_brokenAlphaTexSubImage_init = false; - - for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) - vertexAttributeArraysEnabledState[i] = false; -} - -QGLContext* QGLContext::currentCtx = 0; - -/* - QGLTemporaryContext implementation -*/ -class QGLTemporaryContextPrivate -{ -public: - QWindow *window; - QOpenGLContext *context; - - QGLContext *oldContext; -}; - -QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *) - : d(new QGLTemporaryContextPrivate) -{ - d->oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - - d->window = new QWindow; - d->window->setSurfaceType(QWindow::OpenGLSurface); - d->window->setGeometry(QRect(0, 0, 3, 3)); - d->window->create(); - - d->context = new QOpenGLContext; -#if !defined(QT_OPENGL_ES) - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { - // On desktop, request latest released version - QSurfaceFormat format; -#if defined(Q_OS_MAC) - // OS X is limited to OpenGL 3.2 Core Profile at present - // so set that here. If we use compatibility profile it - // only reports 2.x contexts. - format.setMajorVersion(3); - format.setMinorVersion(2); - format.setProfile(QSurfaceFormat::CoreProfile); -#else - format.setMajorVersion(4); - format.setMinorVersion(3); -#endif - d->context->setFormat(format); - } -#endif // QT_OPENGL_ES - d->context->create(); - d->context->makeCurrent(d->window); -} - -QGLTemporaryContext::~QGLTemporaryContext() -{ - if (d->oldContext) - d->oldContext->makeCurrent(); - - delete d->context; - delete d->window; -} - -/* - Read back the contents of the currently bound framebuffer, used in - QGLWidget::grabFrameBuffer(), QGLPixelbuffer::toImage() and - QGLFramebufferObject::toImage() -*/ - -static void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha) -{ - Q_ASSERT(!img.isNull()); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - // OpenGL gives RGBA; Qt wants ARGB - uint *p = (uint*)img.bits(); - uint *end = p + w*h; - if (alpha_format && include_alpha) { - while (p < end) { - uint a = *p << 24; - *p = (*p >> 8) | a; - p++; - } - } else { - // This is an old legacy fix for PowerPC based Macs, which - // we shouldn't remove - while (p < end) { - *p = 0xff000000 | (*p>>8); - ++p; - } - } - } else { - // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB - for (int y = 0; y < h; y++) { - uint *q = (uint*)img.scanLine(y); - for (int x=0; x < w; ++x) { - const uint pixel = *q; - if (alpha_format && include_alpha) { - *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) - | (pixel & 0xff00ff00); - } else { - *q = 0xff000000 | ((pixel << 16) & 0xff0000) - | ((pixel >> 16) & 0xff) | (pixel & 0x00ff00); - } - - q++; - } - } - - } - img = img.mirrored(); -} - -QImage qt_gl_read_frame_buffer(const QSize &size, bool alpha_format, bool include_alpha) -{ - QImage img(size, (alpha_format && include_alpha) ? QImage::Format_ARGB32_Premultiplied - : QImage::Format_RGB32); - if (img.isNull()) - return QImage(); - int w = size.width(); - int h = size.height(); - qgl_functions()->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); - convertFromGLImage(img, w, h, alpha_format, include_alpha); - return img; -} - -QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha) -{ - QImage img(size, alpha_format ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); - if (img.isNull()) - return QImage(); - int w = size.width(); - int h = size.height(); -#ifndef QT_OPENGL_ES - if (!QOpenGLContext::currentContext()->isOpenGLES()) { - - qgl1_functions()->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); - } -#endif // QT_OPENGL_ES - convertFromGLImage(img, w, h, alpha_format, include_alpha); - return img; -} - -Q_GLOBAL_STATIC(QGLTextureCache, qt_gl_texture_cache) - -QGLTextureCache::QGLTextureCache() - : m_cache(64*1024) // cache ~64 MB worth of textures - this is not accurate though -{ - QImagePixmapCleanupHooks::instance()->addPlatformPixmapModificationHook(cleanupTexturesForPixampData); - QImagePixmapCleanupHooks::instance()->addPlatformPixmapDestructionHook(cleanupBeforePixmapDestruction); - QImagePixmapCleanupHooks::instance()->addImageHook(cleanupTexturesForCacheKey); -} - -QGLTextureCache::~QGLTextureCache() -{ - QImagePixmapCleanupHooks::instance()->removePlatformPixmapModificationHook(cleanupTexturesForPixampData); - QImagePixmapCleanupHooks::instance()->removePlatformPixmapDestructionHook(cleanupBeforePixmapDestruction); - QImagePixmapCleanupHooks::instance()->removeImageHook(cleanupTexturesForCacheKey); -} - -void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost) -{ - QWriteLocker locker(&m_lock); - const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)}; - const bool inserted = m_cache.insert(cacheKey, texture, cost); - Q_UNUSED(inserted) Q_ASSERT(inserted); -} - -void QGLTextureCache::remove(qint64 key) -{ - QWriteLocker locker(&m_lock); - QMutexLocker groupLocker(&qt_context_groups()->m_mutex); - QList<QGLContextGroup *>::const_iterator it = qt_context_groups()->m_list.constBegin(); - while (it != qt_context_groups()->m_list.constEnd()) { - const QGLTextureCacheKey cacheKey = {key, *it}; - m_cache.remove(cacheKey); - ++it; - } -} - -bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId) -{ - QWriteLocker locker(&m_lock); - QList<QGLTextureCacheKey> keys = m_cache.keys(); - for (int i = 0; i < keys.size(); ++i) { - QGLTexture *tex = m_cache.object(keys.at(i)); - if (tex->id == textureId && tex->context == ctx) { - tex->options |= QGLContext::MemoryManagedBindOption; // forces a glDeleteTextures() call - m_cache.remove(keys.at(i)); - return true; - } - } - return false; -} - -void QGLTextureCache::removeContextTextures(QGLContext* ctx) -{ - QWriteLocker locker(&m_lock); - QList<QGLTextureCacheKey> keys = m_cache.keys(); - for (int i = 0; i < keys.size(); ++i) { - const QGLTextureCacheKey &key = keys.at(i); - if (m_cache.object(key)->context == ctx) - m_cache.remove(key); - } -} - -/* - a hook that removes textures from the cache when a pixmap/image - is deref'ed -*/ -void QGLTextureCache::cleanupTexturesForCacheKey(qint64 cacheKey) -{ - qt_gl_texture_cache()->remove(cacheKey); -} - - -void QGLTextureCache::cleanupTexturesForPixampData(QPlatformPixmap* pmd) -{ - cleanupTexturesForCacheKey(pmd->cacheKey()); -} - -void QGLTextureCache::cleanupBeforePixmapDestruction(QPlatformPixmap* pmd) -{ - // Remove any bound textures first: - cleanupTexturesForPixampData(pmd); -} - -QGLTextureCache *QGLTextureCache::instance() -{ - return qt_gl_texture_cache(); -} - -// DDS format structure -struct DDSFormat { - quint32 dwSize; - quint32 dwFlags; - quint32 dwHeight; - quint32 dwWidth; - quint32 dwLinearSize; - quint32 dummy1; - quint32 dwMipMapCount; - quint32 dummy2[11]; - struct { - quint32 dummy3[2]; - quint32 dwFourCC; - quint32 dummy4[5]; - } ddsPixelFormat; -}; - -// compressed texture pixel formats -#define FOURCC_DXT1 0x31545844 -#define FOURCC_DXT2 0x32545844 -#define FOURCC_DXT3 0x33545844 -#define FOURCC_DXT4 0x34545844 -#define FOURCC_DXT5 0x35545844 - -// ####TODO Properly #ifdef this class to use #define symbols actually defined -// by system GL includes -#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT -#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 -#endif - -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT -#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 -#endif - -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT -#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 -#endif - -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT -#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 -#endif - -#ifndef GL_GENERATE_MIPMAP_SGIS -#define GL_GENERATE_MIPMAP_SGIS 0x8191 -#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 -#endif - -/*! - \class QGLContext - \inmodule QtOpenGL - \obsolete - - \brief The QGLContext class encapsulates an OpenGL rendering context. - - An OpenGL rendering context is a complete set of OpenGL state - variables. The rendering context's \l {QGL::FormatOption} {format} - is set in the constructor, but it can also be set later with - setFormat(). The format options that are actually set are returned - by format(); the options you asked for are returned by - requestedFormat(). Note that after a QGLContext object has been - constructed, the actual OpenGL context must be created by - explicitly calling the \l{create()} - function. The makeCurrent() function makes this context the - current rendering context. You can make \e no context current - using doneCurrent(). The reset() function will reset the context - and make it invalid. - - You can examine properties of the context with, e.g. isValid(), - isSharing(), initialized(), windowCreated() and - overlayTransparentColor(). - - If you're using double buffering you can swap the screen contents - with the off-screen buffer using swapBuffers(). - - Please note that QGLContext is not thread safe. -*/ - -/*! - \enum QGLContext::BindOption - \since 4.6 - - A set of options to decide how to bind a texture using bindTexture(). - - \value NoBindOption Don't do anything, pass the texture straight - through. - - \value InvertedYBindOption Specifies that the texture should be flipped - over the X axis so that the texture coordinate 0,0 corresponds to - the top left corner. Inverting the texture implies a deep copy - prior to upload. - - \value MipmapBindOption Specifies that bindTexture() should try - to generate mipmaps. If the GL implementation supports the \c - GL_SGIS_generate_mipmap extension, mipmaps will be automatically - generated for the texture. Mipmap generation is only supported for - the \c GL_TEXTURE_2D target. - - \value PremultipliedAlphaBindOption Specifies that the image should be - uploaded with premultiplied alpha and does a conversion accordingly. - - \value LinearFilteringBindOption Specifies that the texture filtering - should be set to GL_LINEAR. Default is GL_NEAREST. If mipmap is - also enabled, filtering will be set to GL_LINEAR_MIPMAP_LINEAR. - - \value DefaultBindOption In Qt 4.5 and earlier, bindTexture() - would mirror the image and automatically generate mipmaps. This - option helps preserve this default behavior. - - \omitvalue CanFlipNativePixmapBindOption \omit Used by x11 from pixmap to choose - whether or not it can bind the pixmap upside down or not. \endomit - - \omitvalue MemoryManagedBindOption \omit Used by paint engines to - indicate that the pixmap should be memory managed along side with - the pixmap/image that it stems from, e.g. installing destruction - hooks in them. \endomit - - \omitvalue TemporarilyCachedBindOption \omit Used by paint engines on some - platforms to indicate that the pixmap or image texture is possibly - cached only temporarily and must be destroyed immediately after the use. \endomit - - \omitvalue InternalBindOption -*/ - -/*! - \obsolete - - Constructs an OpenGL context for the given paint \a device, which - can be a widget or a pixmap. The \a format specifies several - display options for the context. - - If the underlying OpenGL/Window system cannot satisfy all the - features requested in \a format, the nearest subset of features - will be used. After creation, the format() method will return the - actual format obtained. - - Note that after a QGLContext object has been constructed, \l - create() must be called explicitly to create the actual OpenGL - context. The context will be \l {isValid()}{invalid} if it was not - possible to obtain a GL context at all. -*/ - -QGLContext::QGLContext(const QGLFormat &format, QPaintDevice *device) - : d_ptr(new QGLContextPrivate(this)) -{ - Q_D(QGLContext); - d->init(device, format); -} - -/*! - Constructs an OpenGL context with the given \a format which - specifies several display options for the context. - - If the underlying OpenGL/Window system cannot satisfy all the - features requested in \a format, the nearest subset of features - will be used. After creation, the format() method will return the - actual format obtained. - - Note that after a QGLContext object has been constructed, \l - create() must be called explicitly to create the actual OpenGL - context. The context will be \l {isValid()}{invalid} if it was not - possible to obtain a GL context at all. - - \sa format(), isValid() -*/ -QGLContext::QGLContext(const QGLFormat &format) - : d_ptr(new QGLContextPrivate(this)) -{ - Q_D(QGLContext); - d->init(0, format); -} - -static void qDeleteQGLContext(void *handle) -{ - QGLContext *context = static_cast<QGLContext *>(handle); - delete context; -} - -QGLContext::QGLContext(QOpenGLContext *context) - : d_ptr(new QGLContextPrivate(this)) -{ - Q_D(QGLContext); - d->init(0, QGLFormat::fromSurfaceFormat(context->format())); - d->guiGlContext = context; - d->guiGlContext->setQGLContextHandle(this, qDeleteQGLContext); - d->ownContext = false; - d->valid = context->isValid(); - d->setupSharing(); -} - -/*! - Returns the OpenGL context handle. -*/ -QOpenGLContext *QGLContext::contextHandle() const -{ - Q_D(const QGLContext); - return d->guiGlContext; -} - -/*! - Returns an OpenGL context for the window context specified by the \a context - parameter. -*/ -QGLContext *QGLContext::fromOpenGLContext(QOpenGLContext *context) -{ - if (!context) - return 0; - if (context->qGLContextHandle()) { - return reinterpret_cast<QGLContext *>(context->qGLContextHandle()); - } - QGLContext *glContext = new QGLContext(context); - //Don't call create on context. This can cause the platformFormat to be set on the widget, which - //will cause the platformWindow to be recreated. - return glContext; -} - -/*! - Destroys the OpenGL context and frees its resources. -*/ - -QGLContext::~QGLContext() -{ - // remove any textures cached in this context - QGLTextureCache::instance()->removeContextTextures(this); - - // clean up resources specific to this context - d_ptr->cleanup(); - - QGLSignalProxy::instance()->emitAboutToDestroyContext(this); - reset(); -} - -void QGLContextPrivate::cleanup() -{ -} - -#define ctx q_ptr -void QGLContextPrivate::setVertexAttribArrayEnabled(int arrayIndex, bool enabled) -{ - Q_Q(QGLContext); - Q_ASSERT(arrayIndex < QT_GL_VERTEX_ARRAY_TRACKED_COUNT); -#ifdef glEnableVertexAttribArray - Q_ASSERT(glEnableVertexAttribArray); -#endif - - if (vertexAttributeArraysEnabledState[arrayIndex] && !enabled) - q->functions()->glDisableVertexAttribArray(arrayIndex); - - if (!vertexAttributeArraysEnabledState[arrayIndex] && enabled) - q->functions()->glEnableVertexAttribArray(arrayIndex); - - vertexAttributeArraysEnabledState[arrayIndex] = enabled; -} - -void QGLContextPrivate::syncGlState() -{ - Q_Q(QGLContext); -#ifdef glEnableVertexAttribArray - Q_ASSERT(glEnableVertexAttribArray); -#endif - for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) { - if (vertexAttributeArraysEnabledState[i]) - q->functions()->glEnableVertexAttribArray(i); - else - q->functions()->glDisableVertexAttribArray(i); - } - -} -#undef ctx - -void QGLContextPrivate::swapRegion(const QRegion &) -{ - Q_Q(QGLContext); - q->swapBuffers(); -} - -/*! - \overload - - Reads the compressed texture file \a fileName and generates a 2D GL - texture from it. - - This function can load DirectDrawSurface (DDS) textures in the - DXT1, DXT3 and DXT5 DDS formats if the \c GL_ARB_texture_compression - and \c GL_EXT_texture_compression_s3tc extensions are supported. - - Since 4.6.1, textures in the ETC1 format can be loaded if the - \c GL_OES_compressed_ETC1_RGB8_texture extension is supported - and the ETC1 texture has been encapsulated in the PVR container format. - Also, textures in the PVRTC2 and PVRTC4 formats can be loaded - if the \c GL_IMG_texture_compression_pvrtc extension is supported. - - \sa deleteTexture() -*/ - -GLuint QGLContext::bindTexture(const QString &fileName) -{ - QGLTexture texture(this); - QSize size = texture.bindCompressedTexture(fileName); - if (!size.isValid()) - return 0; - return texture.id; -} - -static inline QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format) -{ - if (texture_format == GL_BGRA) { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return ((src_pixel << 24) & 0xff000000) - | ((src_pixel >> 24) & 0x000000ff) - | ((src_pixel << 8) & 0x00ff0000) - | ((src_pixel >> 8) & 0x0000ff00); - } else { - return src_pixel; - } - } else { // GL_RGBA - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (src_pixel << 8) | ((src_pixel >> 24) & 0xff); - } else { - return ((src_pixel << 16) & 0xff0000) - | ((src_pixel >> 16) & 0xff) - | (src_pixel & 0xff00ff00); - } - } -} - -static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format) -{ - Q_ASSERT(dst.depth() == 32); - Q_ASSERT(img.depth() == 32); - - if (dst.size() != img.size()) { - int target_width = dst.width(); - int target_height = dst.height(); - qreal sx = target_width / qreal(img.width()); - qreal sy = target_height / qreal(img.height()); - - quint32 *dest = (quint32 *) dst.scanLine(0); // NB! avoid detach here - const uchar *srcPixels = img.constScanLine(img.height() - 1); - int sbpl = img.bytesPerLine(); - int dbpl = dst.bytesPerLine(); - - int ix = int(0x00010000 / sx); - int iy = int(0x00010000 / sy); - - quint32 basex = int(0.5 * ix); - quint32 srcy = int(0.5 * iy); - - // scale, swizzle and mirror in one loop - while (target_height--) { - const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl); - int srcx = basex; - for (int x=0; x<target_width; ++x) { - dest[x] = qt_gl_convertToGLFormatHelper(src[srcx >> 16], texture_format); - srcx += ix; - } - dest = (quint32 *)(((uchar *) dest) + dbpl); - srcy += iy; - } - } else { - const int width = img.width(); - const int height = img.height(); - const uint *p = (const uint*) img.scanLine(img.height() - 1); - uint *q = (uint*) dst.scanLine(0); - - if (texture_format == GL_BGRA) { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - // mirror + swizzle - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = ((*p << 24) & 0xff000000) - | ((*p >> 24) & 0x000000ff) - | ((*p << 8) & 0x00ff0000) - | ((*p >> 8) & 0x0000ff00); - p++; - q++; - } - p -= 2 * width; - } - } else { - const uint bytesPerLine = img.bytesPerLine(); - for (int i=0; i < height; ++i) { - memcpy(q, p, bytesPerLine); - q += width; - p -= width; - } - } - } else { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = (*p << 8) | ((*p >> 24) & 0xff); - p++; - q++; - } - p -= 2 * width; - } - } else { - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); - p++; - q++; - } - p -= 2 * width; - } - } - } - } -} - -/*! \internal */ -QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, - QGLContext::BindOptions options) -{ - Q_Q(QGLContext); - - const qint64 key = image.cacheKey(); - QGLTexture *texture = textureCacheLookup(key, target); - if (texture) { - if (image.paintingActive()) { - // A QPainter is active on the image - take the safe route and replace the texture. - q->deleteTexture(texture->id); - texture = 0; - } else { - qgl_functions()->glBindTexture(target, texture->id); - return texture; - } - } - - if (!texture) - texture = bindTexture(image, target, format, key, options); - // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null - Q_ASSERT(texture); - - // Enable the cleanup hooks for this image so that the texture cache entry is removed when the - // image gets deleted: - QImagePixmapCleanupHooks::enableCleanupHooks(image); - - return texture; -} - -// #define QGL_BIND_TEXTURE_DEBUG - -// ####TODO Properly #ifdef this file to use #define symbols actually defined -// by OpenGL/ES includes -#ifndef GL_UNSIGNED_INT_8_8_8_8_REV -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 -#endif - -// map from Qt's ARGB endianness-dependent format to GL's big-endian RGBA layout -static inline void qgl_byteSwapImage(QImage &img, GLenum pixel_type) -{ - const int width = img.width(); - const int height = img.height(); - - if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV - || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) - { - for (int i = 0; i < height; ++i) { - uint *p = (uint *) img.scanLine(i); - for (int x = 0; x < width; ++x) - p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); - } - } else { - for (int i = 0; i < height; ++i) { - uint *p = (uint *) img.scanLine(i); - for (int x = 0; x < width; ++x) - p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff); - } - } -} - -QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint internalFormat, - const qint64 key, QGLContext::BindOptions options) -{ - Q_Q(QGLContext); - QOpenGLFunctions *funcs = qgl_functions(); - -#ifdef QGL_BIND_TEXTURE_DEBUG - printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x, key=%llx\n", - image.width(), image.height(), internalFormat, int(options), key); - QTime time; - time.start(); -#endif - -#ifndef QT_NO_DEBUG - // Reset the gl error stack...git - while (funcs->glGetError() != GL_NO_ERROR) ; -#endif - - // Scale the pixmap if needed. GL textures needs to have the - // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL - // 2.0 or use the GL_TEXTURE_RECTANGLE texture target - int tx_w = qNextPowerOfTwo(image.width() - 1); - int tx_h = qNextPowerOfTwo(image.height() - 1); - - QImage img = image; - - if (!qgl_extensions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures) - && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0) - && (target == GL_TEXTURE_2D && (tx_w != image.width() || tx_h != image.height()))) - { - img = img.scaled(tx_w, tx_h); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - upscaled to %dx%d (%d ms)\n", tx_w, tx_h, time.elapsed()); - -#endif - } - - GLuint filtering = options & QGLContext::LinearFilteringBindOption ? GL_LINEAR : GL_NEAREST; - - GLuint tx_id; - funcs->glGenTextures(1, &tx_id); - funcs->glBindTexture(target, tx_id); - funcs->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filtering); - - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - bool genMipmap = !ctx->isOpenGLES(); - if (glFormat.directRendering() - && (qgl_extensions()->hasOpenGLExtension(QOpenGLExtensions::GenerateMipmap)) - && target == GL_TEXTURE_2D - && (options & QGLContext::MipmapBindOption)) - { -#if !defined(QT_OPENGL_ES_2) - if (genMipmap) { - funcs->glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); - funcs->glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); - } else { - funcs->glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); - genMipmap = true; - } -#else - funcs->glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); - genMipmap = true; -#endif - funcs->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, options & QGLContext::LinearFilteringBindOption - ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - generating mipmaps (%d ms)\n", time.elapsed()); -#endif - } else { - funcs->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filtering); - } - - QImage::Format target_format = img.format(); - bool premul = options & QGLContext::PremultipliedAlphaBindOption; - bool needsbyteswap = true; - GLenum externalFormat; - GLuint pixel_type; - if (target_format == QImage::Format_RGBA8888 - || target_format == QImage::Format_RGBA8888_Premultiplied - || target_format == QImage::Format_RGBX8888) { - externalFormat = GL_RGBA; - pixel_type = GL_UNSIGNED_BYTE; - needsbyteswap = false; - } else if (qgl_extensions()->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat)) { - externalFormat = GL_BGRA; - needsbyteswap = false; - if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) - pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV; - else - pixel_type = GL_UNSIGNED_BYTE; - } else { - externalFormat = GL_RGBA; - pixel_type = GL_UNSIGNED_BYTE; - } - - switch (target_format) { - case QImage::Format_ARGB32: - if (premul) { - img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converted ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed()); -#endif - } - break; - case QImage::Format_ARGB32_Premultiplied: - if (!premul) { - img = img.convertToFormat(target_format = QImage::Format_ARGB32); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converted ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed()); -#endif - } - break; - case QImage::Format_RGBA8888: - if (premul) { - img = img.convertToFormat(target_format = QImage::Format_RGBA8888_Premultiplied); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converted RGBA8888 -> RGBA8888_Premultiplied (%d ms) \n", time.elapsed()); -#endif - } - break; - case QImage::Format_RGBA8888_Premultiplied: - if (!premul) { - img = img.convertToFormat(target_format = QImage::Format_RGBA8888); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converted RGBA8888_Premultiplied -> RGBA8888 (%d ms) \n", time.elapsed()); -#endif - } - break; - case QImage::Format_RGB16: - pixel_type = GL_UNSIGNED_SHORT_5_6_5; - externalFormat = GL_RGB; - internalFormat = GL_RGB; - needsbyteswap = false; - break; - case QImage::Format_RGB32: - case QImage::Format_RGBX8888: - break; - default: - // Ideally more formats would be converted directly to an RGBA8888 format, - // but we are only guaranteed to have a fast conversion to an ARGB format. - if (img.hasAlphaChannel()) { - img = img.convertToFormat(premul - ? QImage::Format_ARGB32_Premultiplied - : QImage::Format_ARGB32); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converted to 32-bit alpha format (%d ms)\n", time.elapsed()); -#endif - } else { - img = img.convertToFormat(QImage::Format_RGB32); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converted to 32-bit (%d ms)\n", time.elapsed()); -#endif - } - } - - if (options & QGLContext::InvertedYBindOption) { - if (img.isDetached()) { - int ipl = img.bytesPerLine() / 4; - int h = img.height(); - for (int y=0; y<h/2; ++y) { - int *a = (int *) img.scanLine(y); - int *b = (int *) img.scanLine(h - y - 1); - for (int x=0; x<ipl; ++x) - qSwap(a[x], b[x]); - } - } else { - // Create a new image and copy across. If we use the - // above in-place code then a full copy of the image is - // made before the lines are swapped, which processes the - // data twice. This version should only do it once. - img = img.mirrored(); - } -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - flipped bits over y (%d ms)\n", time.elapsed()); -#endif - } - - if (needsbyteswap) { - // The only case where we end up with a depth different from - // 32 in the switch above is for the RGB16 case, where we do - // not need a byteswap. - Q_ASSERT(img.depth() == 32); - qgl_byteSwapImage(img, pixel_type); -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - did byte swapping (%d ms)\n", time.elapsed()); -#endif - } - if (ctx->isOpenGLES()) { - // OpenGL/ES requires that the internal and external formats be - // identical. - internalFormat = externalFormat; - } -#ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - uploading, image.format=%d, externalFormat=0x%x, internalFormat=0x%x, pixel_type=0x%x\n", - img.format(), externalFormat, internalFormat, pixel_type); -#endif - - const QImage &constRef = img; // to avoid detach in bits()... - funcs->glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat, - pixel_type, constRef.bits()); - if (genMipmap && ctx->isOpenGLES()) - q->functions()->glGenerateMipmap(target); -#ifndef QT_NO_DEBUG - GLenum error = funcs->glGetError(); - if (error != GL_NO_ERROR) { - qWarning(" - texture upload failed, error code 0x%x, enum: %d (%x)\n", error, target, target); - } -#endif - -#ifdef QGL_BIND_TEXTURE_DEBUG - static int totalUploadTime = 0; - totalUploadTime += time.elapsed(); - printf(" - upload done in %d ms, (accumulated: %d ms)\n", time.elapsed(), totalUploadTime); -#endif - - - // this assumes the size of a texture is always smaller than the max cache size - int cost = img.width()*img.height()*4/1024; - QGLTexture *texture = new QGLTexture(q, tx_id, target, options); - QGLTextureCache::instance()->insert(q, key, texture, cost); - - return texture; -} - -QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target) -{ - Q_Q(QGLContext); - QGLTexture *texture = QGLTextureCache::instance()->getTexture(q, key); - if (texture && texture->target == target - && (texture->context == q || QGLContext::areSharing(q, texture->context))) - { - return texture; - } - return 0; -} - -/*! \internal */ -QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, QGLContext::BindOptions options) -{ - Q_Q(QGLContext); - QPlatformPixmap *pd = pixmap.handle(); - Q_UNUSED(pd); - - const qint64 key = pixmap.cacheKey(); - QGLTexture *texture = textureCacheLookup(key, target); - if (texture) { - if (pixmap.paintingActive()) { - // A QPainter is active on the pixmap - take the safe route and replace the texture. - q->deleteTexture(texture->id); - texture = 0; - } else { - qgl_functions()->glBindTexture(target, texture->id); - return texture; - } - } - - if (!texture) { - QImage image; - QPaintEngine* paintEngine = pixmap.paintEngine(); - if (!paintEngine || paintEngine->type() != QPaintEngine::Raster) - image = pixmap.toImage(); - else { - // QRasterPixmapData::toImage() will deep-copy the backing QImage if there's an active QPainter on it. - // For performance reasons, we don't want that here, so we temporarily redirect the paint engine. - QPaintDevice* currentPaintDevice = paintEngine->paintDevice(); - paintEngine->setPaintDevice(0); - image = pixmap.toImage(); - paintEngine->setPaintDevice(currentPaintDevice); - } - - // If the system depth is 16 and the pixmap doesn't have an alpha channel - // then we convert it to RGB16 in the hope that it gets uploaded as a 16 - // bit texture which is much faster to access than a 32-bit one. - if (pixmap.depth() == 16 && !image.hasAlphaChannel() ) - image = image.convertToFormat(QImage::Format_RGB16); - texture = bindTexture(image, target, format, key, options); - } - // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null - Q_ASSERT(texture); - - if (texture->id > 0) - QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); - - return texture; -} - -/*! \internal */ -int QGLContextPrivate::maxTextureSize() -{ - if (max_texture_size != -1) - return max_texture_size; - - QOpenGLFunctions *funcs = qgl_functions(); - funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); - -#ifndef QT_OPENGL_ES - Q_Q(QGLContext); - if (!q->contextHandle()->isOpenGLES()) { - GLenum proxy = GL_PROXY_TEXTURE_2D; - - GLint size; - GLint next = 64; - funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - gl1funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &size); - if (size == 0) { - return max_texture_size; - } - do { - size = next; - next = size * 2; - - if (next > max_texture_size) - break; - funcs->glTexImage2D(proxy, 0, GL_RGBA, next, next, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - gl1funcs->glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &next); - } while (next > size); - - max_texture_size = size; - } -#endif - - return max_texture_size; -} - -/*! - Returns a QGLFunctions object that is initialized for this context. - */ -QGLFunctions *QGLContext::functions() const -{ - QGLContextPrivate *d = const_cast<QGLContextPrivate *>(d_func()); - if (!d->functions) { - d->functions = new QGLFunctions(this); - d->functions->initializeGLFunctions(this); - } - return d->functions; -} - -/*! - Generates and binds a 2D GL texture to the current context, based - on \a image. The generated texture id is returned and can be used in - later \c glBindTexture() calls. - - \overload -*/ -GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) -{ - if (image.isNull()) - return 0; - - Q_D(QGLContext); - QGLTexture *texture = d->bindTexture(image, target, format, DefaultBindOption); - return texture->id; -} - -/*! - \since 4.6 - - Generates and binds a 2D GL texture to the current context, based - on \a image. The generated texture id is returned and can be used - in later \c glBindTexture() calls. - - The \a target parameter specifies the texture target. The default - target is \c GL_TEXTURE_2D. - - The \a format parameter sets the internal format for the - texture. The default format is \c GL_RGBA. - - The binding \a options are a set of options used to decide how to - bind the texture to the context. - - The texture that is generated is cached, so multiple calls to - bindTexture() with the same QImage will return the same texture - id. - - Note that we assume default values for the glPixelStore() and - glPixelTransfer() parameters. - - \sa deleteTexture() -*/ -GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, BindOptions options) -{ - if (image.isNull()) - return 0; - - Q_D(QGLContext); - QGLTexture *texture = d->bindTexture(image, target, format, options); - return texture->id; -} - -/*! \overload - - Generates and binds a 2D GL texture based on \a pixmap. -*/ -GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) -{ - if (pixmap.isNull()) - return 0; - - Q_D(QGLContext); - QGLTexture *texture = d->bindTexture(pixmap, target, format, DefaultBindOption); - return texture->id; -} - -/*! - \overload - \since 4.6 - - Generates and binds a 2D GL texture to the current context, based - on \a pixmap. -*/ -GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, BindOptions options) -{ - if (pixmap.isNull()) - return 0; - - Q_D(QGLContext); - QGLTexture *texture = d->bindTexture(pixmap, target, format, options); - return texture->id; -} - -/*! - Removes the texture identified by \a id from the texture cache, - and calls glDeleteTextures() to delete the texture from the - context. - - \sa bindTexture() -*/ -void QGLContext::deleteTexture(GLuint id) -{ - if (QGLTextureCache::instance()->remove(this, id)) - return; - qgl_functions()->glDeleteTextures(1, &id); -} - -void qt_add_rect_to_array(const QRectF &r, GLfloat *array) -{ - qreal left = r.left(); - qreal right = r.right(); - qreal top = r.top(); - qreal bottom = r.bottom(); - - array[0] = left; - array[1] = top; - array[2] = right; - array[3] = top; - array[4] = right; - array[5] = bottom; - array[6] = left; - array[7] = bottom; -} - -void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, GLfloat *array) -{ - array[0] = x1; - array[1] = y1; - array[2] = x2; - array[3] = y1; - array[4] = x2; - array[5] = y2; - array[6] = x1; - array[7] = y2; -} - -#if !defined(QT_OPENGL_ES_2) - -static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget) -{ - QOpenGLFunctions *funcs = qgl_functions(); - GLfloat tx = 1.0f; - GLfloat ty = 1.0f; - -#ifdef QT_OPENGL_ES - Q_UNUSED(textureWidth); - Q_UNUSED(textureHeight); - Q_UNUSED(textureTarget); -#else - if (textureTarget != GL_TEXTURE_2D && !QOpenGLContext::currentContext()->isOpenGLES()) { - if (textureWidth == -1 || textureHeight == -1) { - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - gl1funcs->glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth); - gl1funcs->glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight); - } - - tx = GLfloat(textureWidth); - ty = GLfloat(textureHeight); - } -#endif - - GLfloat texCoordArray[4*2] = { - 0, ty, tx, ty, tx, 0, 0, 0 - }; - - GLfloat vertexArray[4*2]; - qt_add_rect_to_array(target, vertexArray); - - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - gl1funcs->glVertexPointer(2, GL_FLOAT, 0, vertexArray); - gl1funcs->glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray); - - gl1funcs->glEnableClientState(GL_VERTEX_ARRAY); - gl1funcs->glEnableClientState(GL_TEXTURE_COORD_ARRAY); - funcs->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - gl1funcs->glDisableClientState(GL_VERTEX_ARRAY); - gl1funcs->glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -#endif // !QT_OPENGL_ES_2 - -/*! - \since 4.4 - - This function supports the following use cases: - - \list - \li On OpenGL and OpenGL ES 1.x it draws the given texture, \a textureId, - to the given target rectangle, \a target, in OpenGL model space. The - \a textureTarget should be a 2D texture target. - \li On OpenGL and OpenGL ES 2.x, if a painter is active, not inside a - beginNativePainting / endNativePainting block, and uses the - engine with type QPaintEngine::OpenGL2, the function will draw the given - texture, \a textureId, to the given target rectangle, \a target, - respecting the current painter state. This will let you draw a texture - with the clip, transform, render hints, and composition mode set by the - painter. Note that the texture target needs to be GL_TEXTURE_2D for this - use case, and that this is the only supported use case under OpenGL ES 2.x. - \endlist - -*/ -void QGLContext::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) -{ -#if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_ES_2) - if (d_ptr->active_engine && - d_ptr->active_engine->type() == QPaintEngine::OpenGL2) { - QGL2PaintEngineEx *eng = static_cast<QGL2PaintEngineEx*>(d_ptr->active_engine); - if (!eng->isNativePaintingActive()) { - QRectF src(0, 0, target.width(), target.height()); - QSize size(target.width(), target.height()); - if (eng->drawTexture(target, textureId, size, src)) - return; - } - } -#endif - -#ifndef QT_OPENGL_ES_2 - QOpenGLFunctions *funcs = qgl_functions(); - if (!contextHandle()->isOpenGLES()) { -#ifdef QT_OPENGL_ES - if (textureTarget != GL_TEXTURE_2D) { - qWarning("QGLContext::drawTexture(): texture target must be GL_TEXTURE_2D on OpenGL ES"); - return; - } -#else - const bool wasEnabled = funcs->glIsEnabled(GL_TEXTURE_2D); - GLint oldTexture; - funcs->glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture); -#endif - - funcs->glEnable(textureTarget); - funcs->glBindTexture(textureTarget, textureId); - - qDrawTextureRect(target, -1, -1, textureTarget); - -#ifdef QT_OPENGL_ES - funcs->glDisable(textureTarget); -#else - if (!wasEnabled) - funcs->glDisable(textureTarget); - funcs->glBindTexture(textureTarget, oldTexture); -#endif - return; - } -#else - Q_UNUSED(target); - Q_UNUSED(textureId); - Q_UNUSED(textureTarget); -#endif - qWarning("drawTexture() with OpenGL ES 2.0 requires an active OpenGL2 paint engine"); -} - -/*! - \since 4.4 - - This function supports the following use cases: - - \list - \li By default it draws the given texture, \a textureId, - at the given \a point in OpenGL model space. The - \a textureTarget should be a 2D texture target. - \li If a painter is active, not inside a - beginNativePainting / endNativePainting block, and uses the - engine with type QPaintEngine::OpenGL2, the function will draw the given - texture, \a textureId, at the given \a point, - respecting the current painter state. This will let you draw a texture - with the clip, transform, render hints, and composition mode set by the - painter. Note that the texture target needs to be GL_TEXTURE_2D for this - use case. - \endlist - - \note This function is not supported under any version of OpenGL ES. -*/ -void QGLContext::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) -{ -#ifdef QT_OPENGL_ES - Q_UNUSED(point); - Q_UNUSED(textureId); - Q_UNUSED(textureTarget); -#else - if (!contextHandle()->isOpenGLES()) { - QOpenGLFunctions *funcs = qgl_functions(); - const bool wasEnabled = funcs->glIsEnabled(GL_TEXTURE_2D); - GLint oldTexture; - funcs->glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture); - - funcs->glEnable(textureTarget); - funcs->glBindTexture(textureTarget, textureId); - - GLint textureWidth; - GLint textureHeight; - - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - gl1funcs->glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_WIDTH, &textureWidth); - gl1funcs->glGetTexLevelParameteriv(textureTarget, 0, GL_TEXTURE_HEIGHT, &textureHeight); - - if (d_ptr->active_engine && - d_ptr->active_engine->type() == QPaintEngine::OpenGL2) { - QGL2PaintEngineEx *eng = static_cast<QGL2PaintEngineEx*>(d_ptr->active_engine); - if (!eng->isNativePaintingActive()) { - QRectF dest(point, QSizeF(textureWidth, textureHeight)); - QRectF src(0, 0, textureWidth, textureHeight); - QSize size(textureWidth, textureHeight); - if (eng->drawTexture(dest, textureId, size, src)) - return; - } - } - - qDrawTextureRect(QRectF(point, QSizeF(textureWidth, textureHeight)), textureWidth, textureHeight, textureTarget); - - if (!wasEnabled) - funcs->glDisable(textureTarget); - funcs->glBindTexture(textureTarget, oldTexture); - return; - } -#endif - qWarning("drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) not supported with OpenGL ES, use rect version instead"); -} - -/*! - This function sets the limit for the texture cache to \a size, - expressed in kilobytes. - - By default, the cache limit is approximately 64 MB. - - \sa textureCacheLimit() -*/ -void QGLContext::setTextureCacheLimit(int size) -{ - QGLTextureCache::instance()->setMaxCost(size); -} - -/*! - Returns the current texture cache limit in kilobytes. - - \sa setTextureCacheLimit() -*/ -int QGLContext::textureCacheLimit() -{ - return QGLTextureCache::instance()->maxCost(); -} - - -/*! - \fn QGLFormat QGLContext::format() const - - Returns the frame buffer format that was obtained (this may be a - subset of what was requested). - - \sa requestedFormat() -*/ - -/*! - \fn QGLFormat QGLContext::requestedFormat() const - - Returns the frame buffer format that was originally requested in - the constructor or setFormat(). - - \sa format() -*/ - -/*! - Sets a \a format for this context. The context is \l{reset()}{reset}. - - Call create() to create a new GL context that tries to match the - new format. - - \snippet code/src_opengl_qgl.cpp 7 - - \sa format(), reset(), create() -*/ - -void QGLContext::setFormat(const QGLFormat &format) -{ - Q_D(QGLContext); - reset(); - d->glFormat = d->reqFormat = format; -} - -/*! - \internal -*/ -void QGLContext::setDevice(QPaintDevice *pDev) -{ - Q_D(QGLContext); - // Do not touch the valid flag here. The context is either a new one and - // valid is not yet set or it is adapted from a valid QOpenGLContext in which - // case it must remain valid. - d->paintDevice = pDev; - if (d->paintDevice && (d->paintDevice->devType() != QInternal::Widget - && d->paintDevice->devType() != QInternal::Pixmap - && d->paintDevice->devType() != QInternal::Pbuffer)) { - qWarning("QGLContext: Unsupported paint device type"); - } -} - -/*! - \fn bool QGLContext::isValid() const - - Returns \c true if a GL rendering context has been successfully - created; otherwise returns \c false. -*/ - -/*! - \fn void QGLContext::setValid(bool valid) - \internal - - Forces the GL rendering context to be valid. -*/ - -/*! - \fn bool QGLContext::isSharing() const - - Returns \c true if this context is sharing its GL context with - another QGLContext, otherwise false is returned. Note that context - sharing might not be supported between contexts with different - formats. -*/ - -/*! - Returns \c true if \a context1 and \a context2 are sharing their - GL resources such as textures, shader programs, etc; - otherwise returns \c false. - - \since 4.6 -*/ -bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *context2) -{ - if (!context1 || !context2) - return false; - return context1->d_ptr->group == context2->d_ptr->group; -} - -/*! - \fn bool QGLContext::deviceIsPixmap() const - - Returns \c true if the paint device of this context is a pixmap; - otherwise returns \c false. - - Since Qt 5 the paint device is never actually a pixmap. renderPixmap() is - however still simulated using framebuffer objects and readbacks, and this - function will return \c true in this case. -*/ - -/*! - \fn bool QGLContext::windowCreated() const - - Returns \c true if a window has been created for this context; - otherwise returns \c false. - - \sa setWindowCreated() -*/ - -/*! - \fn void QGLContext::setWindowCreated(bool on) - - If \a on is true the context has had a window created for it. If - \a on is false no window has been created for the context. - - \sa windowCreated() -*/ - -/*! - \fn uint QGLContext::colorIndex(const QColor& c) const - - \internal - - Returns a colormap index for the color c, in ColorIndex mode. Used - by qglColor() and qglClearColor(). -*/ -uint QGLContext::colorIndex(const QColor&) const -{ - return 0; -} - -/*! - \fn bool QGLContext::initialized() const - - Returns \c true if this context has been initialized, i.e. if - QGLWidget::initializeGL() has been performed on it; otherwise - returns \c false. - - \sa setInitialized() -*/ - -/*! - \fn void QGLContext::setInitialized(bool on) - - If \a on is true the context has been initialized, i.e. - QGLContext::setInitialized() has been called on it. If \a on is - false the context has not been initialized. - - \sa initialized() -*/ - -/*! - \fn const QGLContext* QGLContext::currentContext() - - Returns the current context, i.e. the context to which any OpenGL - commands will currently be directed. Returns 0 if no context is - current. - - \sa makeCurrent() -*/ - -/*! - \fn QColor QGLContext::overlayTransparentColor() const - - If this context is a valid context in an overlay plane, returns - the plane's transparent color. Otherwise returns an \l{QColor::isValid()}{invalid} color. - - The returned color's \l{QColormap::pixel()}{pixel} value is - the index of the transparent color in the colormap of the overlay - plane. (Naturally, the color's RGB values are meaningless.) - - The returned QColor object will generally work as expected only - when passed as the argument to QGLWidget::qglColor() or - QGLWidget::qglClearColor(). Under certain circumstances it can - also be used to draw transparent graphics with a QPainter. -*/ -QColor QGLContext::overlayTransparentColor() const -{ - return QColor(); // Invalid color -} - -/*! - Creates the GL context. Returns \c true if it was successful in - creating a valid GL rendering context on the paint device - specified in the constructor; otherwise returns \c false (i.e. the - context is invalid). - - If the OpenGL implementation on your system does not support the requested - version of OpenGL context, then QGLContext will try to create the closest - matching version. The actual created context properties can be queried - using the QGLFormat returned by the format() function. For example, if - you request a context that supports OpenGL 4.3 Core profile but the driver - and/or hardware only supports version 3.2 Core profile contexts then you will - get a 3.2 Core profile context. - - After successful creation, format() returns the set of features of - the created GL rendering context. - - If \a shareContext points to a valid QGLContext, this method will - try to establish OpenGL display list and texture object sharing - between this context and the \a shareContext. Note that this may - fail if the two contexts have different \l {format()} {formats}. - Use isSharing() to see if sharing is in effect. - - \warning Implementation note: initialization of C++ class - members usually takes place in the class constructor. QGLContext - is an exception because it must be simple to customize. The - virtual functions chooseContext() (and chooseVisual() for X11) can - be reimplemented in a subclass to select a particular context. The - problem is that virtual functions are not properly called during - construction (even though this is correct C++) because C++ - constructs class hierarchies from the bottom up. For this reason - we need a create() function. - - \sa chooseContext(), format(), isValid() -*/ - -bool QGLContext::create(const QGLContext* shareContext) -{ - Q_D(QGLContext); - if (!d->paintDevice && !d->guiGlContext) - return false; - - reset(); - d->valid = chooseContext(shareContext); - if (d->valid && d->paintDevice && d->paintDevice->devType() == QInternal::Widget) { - QWidgetPrivate *wd = qt_widget_private(static_cast<QWidget *>(d->paintDevice)); - wd->usesDoubleBufferedGLContext = d->glFormat.doubleBuffer(); - } - return d->valid; -} - -bool QGLContext::isValid() const -{ - Q_D(const QGLContext); - return d->valid; -} - -void QGLContext::setValid(bool valid) -{ - Q_D(QGLContext); - d->valid = valid; -} - -bool QGLContext::isSharing() const -{ - Q_D(const QGLContext); - return d->group->isSharing(); -} - -QGLFormat QGLContext::format() const -{ - Q_D(const QGLContext); - return d->glFormat; -} - -QGLFormat QGLContext::requestedFormat() const -{ - Q_D(const QGLContext); - return d->reqFormat; -} - - QPaintDevice* QGLContext::device() const -{ - Q_D(const QGLContext); - return d->paintDevice; -} - -bool QGLContext::deviceIsPixmap() const -{ - Q_D(const QGLContext); - return !d->readback_target_size.isEmpty(); -} - - -bool QGLContext::windowCreated() const -{ - Q_D(const QGLContext); - return d->crWin; -} - - -void QGLContext::setWindowCreated(bool on) -{ - Q_D(QGLContext); - d->crWin = on; -} - -bool QGLContext::initialized() const -{ - Q_D(const QGLContext); - return d->initDone; -} - -void QGLContext::setInitialized(bool on) -{ - Q_D(QGLContext); - d->initDone = on; -} - -const QGLContext* QGLContext::currentContext() -{ - if (const QOpenGLContext *threadContext = QOpenGLContext::currentContext()) { - return QGLContext::fromOpenGLContext(const_cast<QOpenGLContext *>(threadContext)); - } - return 0; -} - -void QGLContextPrivate::setCurrentContext(QGLContext *context) -{ - Q_UNUSED(context); -} - -/*! - Moves the QGLContext to the given \a thread. - - Enables calling swapBuffers() and makeCurrent() on the context in - the given thread. -*/ -void QGLContext::moveToThread(QThread *thread) -{ - Q_D(QGLContext); - if (d->guiGlContext) - d->guiGlContext->moveToThread(thread); -} - -/*! - \fn bool QGLContext::chooseContext(const QGLContext* shareContext = 0) - - This semi-internal function is called by create(). It creates a - system-dependent OpenGL handle that matches the format() of \a - shareContext as closely as possible, returning true if successful - or false if a suitable handle could not be found. - - On Windows, it calls the virtual function choosePixelFormat(), - which finds a matching pixel format identifier. On X11, it calls - the virtual function chooseVisual() which finds an appropriate X - visual. On other platforms it may work differently. -*/ -bool QGLContext::chooseContext(const QGLContext* shareContext) -{ - Q_D(QGLContext); - if(!d->paintDevice || d->paintDevice->devType() != QInternal::Widget) { - // Unlike in Qt 4, the only possible target is a widget backed by an OpenGL-based - // QWindow. Pixmaps in particular are not supported anymore as paint devices since - // starting from Qt 5 QPixmap is raster-backed on almost all platforms. - d->valid = false; - }else { - QWidget *widget = static_cast<QWidget *>(d->paintDevice); - QGLFormat glformat = format(); - QSurfaceFormat winFormat = QGLFormat::toSurfaceFormat(glformat); - if (widget->testAttribute(Qt::WA_TranslucentBackground)) - winFormat.setAlphaBufferSize(qMax(winFormat.alphaBufferSize(), 8)); - - QWindow *window = widget->windowHandle(); - if (!window->handle() - || window->surfaceType() != QWindow::OpenGLSurface - || window->requestedFormat() != winFormat) - { - window->setSurfaceType(QWindow::OpenGLSurface); - window->setFormat(winFormat); - window->destroy(); - window->create(); - } - - if (d->ownContext) - delete d->guiGlContext; - d->ownContext = true; - QOpenGLContext *shareGlContext = shareContext ? shareContext->d_func()->guiGlContext : 0; - d->guiGlContext = new QOpenGLContext; - d->guiGlContext->setFormat(winFormat); - d->guiGlContext->setShareContext(shareGlContext); - d->valid = d->guiGlContext->create(); - - if (d->valid) - d->guiGlContext->setQGLContextHandle(this, 0); - - d->glFormat = QGLFormat::fromSurfaceFormat(d->guiGlContext->format()); - d->setupSharing(); - } - - - return d->valid; -} - -/*! - \fn void QGLContext::reset() - - Resets the context and makes it invalid. - - \sa create(), isValid() -*/ -void QGLContext::reset() -{ - Q_D(QGLContext); - if (!d->valid) - return; - d->cleanup(); - - d->crWin = false; - d->sharing = false; - d->valid = false; - d->transpColor = QColor(); - d->initDone = false; - QGLContextGroup::removeShare(this); - if (d->guiGlContext) { - if (QOpenGLContext::currentContext() == d->guiGlContext) - doneCurrent(); - if (d->ownContext) { - if (d->guiGlContext->thread() == QThread::currentThread()) - delete d->guiGlContext; - else - d->guiGlContext->deleteLater(); - } else - d->guiGlContext->setQGLContextHandle(0,0); - d->guiGlContext = 0; - } - d->ownContext = false; -} - -/*! - \fn void QGLContext::makeCurrent() - - Makes this context the current OpenGL rendering context. All GL - functions you call operate on this context until another context - is made current. - - In some very rare cases the underlying call may fail. If this - occurs an error message is output to stderr. - - If you call this from a thread other than the main UI thread, - make sure you've first pushed the context to the relevant thread - from the UI thread using moveToThread(). -*/ -void QGLContext::makeCurrent() -{ - Q_D(QGLContext); - if (!d->paintDevice || d->paintDevice->devType() != QInternal::Widget) - return; - - QWidget *widget = static_cast<QWidget *>(d->paintDevice); - if (!widget->windowHandle()) - return; - - if (d->guiGlContext->makeCurrent(widget->windowHandle())) { - if (!d->workaroundsCached) { - d->workaroundsCached = true; - const char *renderer = reinterpret_cast<const char *>(d->guiGlContext->functions()->glGetString(GL_RENDERER)); - if (renderer && strstr(renderer, "Mali")) { - d->workaround_brokenFBOReadBack = true; - } - } - } -} - -/*! - \fn void QGLContext::swapBuffers() const - - Call this to finish a frame of OpenGL rendering, and make sure to - call makeCurrent() again before issuing any further OpenGL commands, - for example as part of a new frame. -*/ -void QGLContext::swapBuffers() const -{ - Q_D(const QGLContext); - if (!d->paintDevice || d->paintDevice->devType() != QInternal::Widget) - return; - - QWidget *widget = static_cast<QWidget *>(d->paintDevice); - if (!widget->windowHandle()) - return; - - d->guiGlContext->swapBuffers(widget->windowHandle()); -} - -/*! - \fn void QGLContext::doneCurrent() - - Makes no GL context the current context. Normally, you do not need - to call this function; QGLContext calls it as necessary. -*/ -void QGLContext::doneCurrent() -{ - Q_D(QGLContext); - d->guiGlContext->doneCurrent(); -} - -/*! - \fn QPaintDevice* QGLContext::device() const - - Returns the paint device set for this context. - - \sa QGLContext::QGLContext() -*/ - -/***************************************************************************** - QGLWidget implementation - *****************************************************************************/ - - -/*! - \class QGLWidget - \inmodule QtOpenGL - \obsolete - - \brief The QGLWidget class is a widget for rendering OpenGL graphics. - - QGLWidget provides functionality for displaying OpenGL graphics - integrated into a Qt application. It is very simple to use. You - inherit from it and use the subclass like any other QWidget, - except that you have the choice between using QPainter and - standard OpenGL rendering commands. - - \note This class is part of the legacy \l {Qt OpenGL} module and, - like the other \c QGL classes, should be avoided in the new - applications. Instead, starting from Qt 5.4, prefer using - QOpenGLWidget and the \c QOpenGL classes. - - QGLWidget provides three convenient virtual functions that you can - reimplement in your subclass to perform the typical OpenGL tasks: - - \list - \li paintGL() - Renders the OpenGL scene. Gets called whenever the widget - needs to be updated. - \li resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets - called whenever the widget has been resized (and also when it - is shown for the first time because all newly created widgets get a - resize event automatically). - \li initializeGL() - Sets up the OpenGL rendering context, defines display - lists, etc. Gets called once before the first time resizeGL() or - paintGL() is called. - \endlist - - Here is a rough outline of how a QGLWidget subclass might look: - - \snippet code/src_opengl_qgl.cpp 8 - - If you need to trigger a repaint from places other than paintGL() - (a typical example is when using \l{QTimer}{timers} to - animate scenes), you should call the widget's updateGL() function. - - Your widget's OpenGL rendering context is made current when - paintGL(), resizeGL(), or initializeGL() is called. If you need to - call the standard OpenGL API functions from other places (e.g. in - your widget's constructor or in your own paint functions), you - must call makeCurrent() first. - - QGLWidget provides functions for requesting a new display - \l{QGLFormat}{format} and you can also create widgets with - customized rendering \l{QGLContext}{contexts}. - - You can also share OpenGL display lists between QGLWidget objects (see - the documentation of the QGLWidget constructors for details). - - Note that under Windows, the QGLContext belonging to a QGLWidget - has to be recreated when the QGLWidget is reparented. This is - necessary due to limitations on the Windows platform. This will - most likely cause problems for users that have subclassed and - installed their own QGLContext on a QGLWidget. It is possible to - work around this issue by putting the QGLWidget inside a dummy - widget and then reparenting the dummy widget, instead of the - QGLWidget. This will side-step the issue altogether, and is what - we recommend for users that need this kind of functionality. - - On \macos, when Qt is built with Cocoa support, a QGLWidget - can't have any sibling widgets placed ontop of itself. This is due - to limitations in the Cocoa API and is not supported by Apple. - - \section1 Overlays - - The QGLWidget creates a GL overlay context in addition to the - normal context if overlays are supported by the underlying system. - - If you want to use overlays, you specify it in the - \l{QGLFormat}{format}. (Note: Overlay must be requested in the format - passed to the QGLWidget constructor.) Your GL widget should also - implement some or all of these virtual methods: - - \list - \li paintOverlayGL() - \li resizeOverlayGL() - \li initializeOverlayGL() - \endlist - - These methods work in the same way as the normal paintGL() etc. - functions, except that they will be called when the overlay - context is made current. You can explicitly make the overlay - context current by using makeOverlayCurrent(), and you can access - the overlay context directly (e.g. to ask for its transparent - color) by calling overlayContext(). - - On X servers in which the default visual is in an overlay plane, - non-GL Qt windows can also be used for overlays. - - \section1 Painting Techniques - - As described above, subclass QGLWidget to render pure 3D content in the - following way: - - \list - \li Reimplement the QGLWidget::initializeGL() and QGLWidget::resizeGL() to - set up the OpenGL state and provide a perspective transformation. - \li Reimplement QGLWidget::paintGL() to paint the 3D scene, calling only - OpenGL functions to draw on the widget. - \endlist - - It is also possible to draw 2D graphics onto a QGLWidget subclass, it is necessary - to reimplement QGLWidget::paintEvent() and do the following: - - \list - \li Construct a QPainter object. - \li Initialize it for use on the widget with the QPainter::begin() function. - \li Draw primitives using QPainter's member functions. - \li Call QPainter::end() to finish painting. - \endlist - - \section1 Threading - - As of Qt version 4.8, support for doing threaded GL rendering has - been improved. There are three scenarios that we currently support: - \list - \li 1. Buffer swapping in a thread. - - Swapping buffers in a double buffered context may be a - synchronous, locking call that may be a costly operation in some - GL implementations. Especially so on embedded devices. It's not - optimal to have the CPU idling while the GPU is doing a buffer - swap. In those cases it is possible to do the rendering in the - main thread and do the actual buffer swap in a separate - thread. This can be done with the following steps: - - 1. Call doneCurrent() in the main thread when the rendering is - finished. - - 2. Call QGLContext::moveToThread(swapThread) to transfer ownership - of the context to the swapping thread. - - 3. Notify the swapping thread that it can grab the context. - - 4. Make the rendering context current in the swapping thread with - makeCurrent() and then call swapBuffers(). - - 5. Call doneCurrent() in the swapping thread. - - 6. Call QGLContext::moveToThread(qApp->thread()) and notify the - main thread that swapping is done. - - Doing this will free up the main thread so that it can continue - with, for example, handling UI events or network requests. Even if - there is a context swap involved, it may be preferable compared to - having the main thread wait while the GPU finishes the swap - operation. Note that this is highly implementation dependent. - - \li 2. Texture uploading in a thread. - - Doing texture uploads in a thread may be very useful for - applications handling large amounts of images that needs to be - displayed, like for instance a photo gallery application. This is - supported in Qt through the existing bindTexture() API. A simple - way of doing this is to create two sharing QGLWidgets. One is made - current in the main GUI thread, while the other is made current in - the texture upload thread. The widget in the uploading thread is - never shown, it is only used for sharing textures with the main - thread. For each texture that is bound via bindTexture(), notify - the main thread so that it can start using the texture. - - \li 3. Using QPainter to draw into a QGLWidget in a thread. - - In Qt 4.8, it is possible to draw into a QGLWidget using a - QPainter in a separate thread. Note that this is also possible for - QGLPixelBuffers and QGLFramebufferObjects. Since this is only - supported in the GL 2 paint engine, OpenGL 2.0 or OpenGL ES 2.0 is - required. - - QGLWidgets can only be created in the main GUI thread. This means - a call to doneCurrent() is necessary to release the GL context - from the main thread, before the widget can be drawn into by - another thread. You then need to call QGLContext::moveToThread() - to transfer ownership of the context to the thread in which you - want to make it current. - Also, the main GUI thread will dispatch resize and - paint events to a QGLWidget when the widget is resized, or parts - of it becomes exposed or needs redrawing. It is therefore - necessary to handle those events because the default - implementations inside QGLWidget will try to make the QGLWidget's - context current, which again will interfere with any threads - rendering into the widget. Reimplement QGLWidget::paintEvent() and - QGLWidget::resizeEvent() to notify the rendering thread that a - resize or update is necessary, and be careful not to call the base - class implementation. If you are rendering an animation, it might - not be necessary to handle the paint event at all since the - rendering thread is doing regular updates. Then it would be enough - to reimplement QGLWidget::paintEvent() to do nothing. - - \endlist - - As a general rule when doing threaded rendering: be aware that - binding and releasing contexts in different threads have to be - synchronized by the user. A GL rendering context can only be - current in one thread at any time. If you try to open a QPainter - on a QGLWidget and the widget's rendering context is current in - another thread, it will fail. - - In addition to this, rendering using raw GL calls in a separate - thread is supported. - - \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other - countries.} - - \sa QOpenGLWidget, QGLPixelBuffer -*/ - -/*! - Constructs an OpenGL widget with a \a parent widget. - - The \l{QGLFormat::defaultFormat()}{default format} is - used. The widget will be \l{isValid()}{invalid} if the - system has no \l{QGLFormat::hasOpenGL()}{OpenGL support}. - - The \a parent and widget flag, \a f, arguments are passed - to the QWidget constructor. - - If \a shareWidget is a valid QGLWidget, this widget will share - OpenGL display lists and texture objects with \a shareWidget. But - if \a shareWidget and this widget have different \l {format()} - {formats}, sharing might not be possible. You can check whether - sharing is in effect by calling isSharing(). - - The initialization of OpenGL rendering state, etc. should be done - by overriding the initializeGL() function, rather than in the - constructor of your QGLWidget subclass. - - \sa QGLFormat::defaultFormat(), {Textures Example} -*/ - -QGLWidget::QGLWidget(QWidget *parent, const QGLWidget* shareWidget, Qt::WindowFlags f) - : QWidget(*(new QGLWidgetPrivate), parent, f) -{ - Q_D(QGLWidget); - d->init(new QGLContext(QGLFormat::defaultFormat(), this), shareWidget); -} - -/*! - \internal - */ -QGLWidget::QGLWidget(QGLWidgetPrivate &dd, const QGLFormat &format, QWidget *parent, const QGLWidget *shareWidget, Qt::WindowFlags f) - : QWidget(dd, parent, f) -{ - Q_D(QGLWidget); - d->init(new QGLContext(format, this), shareWidget); - -} - - -/*! - Constructs an OpenGL widget with parent \a parent. - - The \a format argument specifies the desired - \l{QGLFormat}{rendering options}. - If the underlying OpenGL/Window system - cannot satisfy all the features requested in \a format, the - nearest subset of features will be used. After creation, the - format() method will return the actual format obtained. - - The widget will be \l{isValid()}{invalid} if the system - has no \l{QGLFormat::hasOpenGL()}{OpenGL support}. - - The \a parent and widget flag, \a f, arguments are passed - to the QWidget constructor. - - If \a shareWidget is a valid QGLWidget, this widget will share - OpenGL display lists and texture objects with \a shareWidget. But - if \a shareWidget and this widget have different \l {format()} - {formats}, sharing might not be possible. You can check whether - sharing is in effect by calling isSharing(). - - The initialization of OpenGL rendering state, etc. should be done - by overriding the initializeGL() function, rather than in the - constructor of your QGLWidget subclass. - - \sa QGLFormat::defaultFormat(), isValid() -*/ - -QGLWidget::QGLWidget(const QGLFormat &format, QWidget *parent, const QGLWidget* shareWidget, - Qt::WindowFlags f) - : QWidget(*(new QGLWidgetPrivate), parent, f) -{ - Q_D(QGLWidget); - d->init(new QGLContext(format, this), shareWidget); -} - -/*! - Constructs an OpenGL widget with parent \a parent. - - The \a context argument is a pointer to the QGLContext that - you wish to be bound to this widget. This allows you to pass in - your own QGLContext sub-classes. - - The widget will be \l{isValid()}{invalid} if the system - has no \l{QGLFormat::hasOpenGL()}{OpenGL support}. - - The \a parent and widget flag, \a f, arguments are passed - to the QWidget constructor. - - If \a shareWidget is a valid QGLWidget, this widget will share - OpenGL display lists and texture objects with \a shareWidget. But - if \a shareWidget and this widget have different \l {format()} - {formats}, sharing might not be possible. You can check whether - sharing is in effect by calling isSharing(). - - The initialization of OpenGL rendering state, etc. should be done - by overriding the initializeGL() function, rather than in the - constructor of your QGLWidget subclass. - - \sa QGLFormat::defaultFormat(), isValid() -*/ -QGLWidget::QGLWidget(QGLContext *context, QWidget *parent, const QGLWidget *shareWidget, - Qt::WindowFlags f) - : QWidget(*(new QGLWidgetPrivate), parent, f) -{ - Q_D(QGLWidget); - d->init(context, shareWidget); -} - -/*! - Destroys the widget. -*/ - -QGLWidget::~QGLWidget() -{ - Q_D(QGLWidget); - delete d->glcx; - d->glcx = 0; - d->cleanupColormaps(); -} - -/*! - \fn QGLFormat QGLWidget::format() const - - Returns the format of the contained GL rendering context. -*/ - -/*! - \fn bool QGLWidget::doubleBuffer() const - - Returns \c true if the contained GL rendering context has double - buffering; otherwise returns \c false. - - \sa QGLFormat::doubleBuffer() -*/ - -/*! - \fn void QGLWidget::setAutoBufferSwap(bool on) - - If \a on is true automatic GL buffer swapping is switched on; - otherwise it is switched off. - - If \a on is true and the widget is using a double-buffered format, - the background and foreground GL buffers will automatically be - swapped after each paintGL() call. - - The buffer auto-swapping is on by default. - - \sa autoBufferSwap(), doubleBuffer(), swapBuffers() -*/ - -/*! - \fn bool QGLWidget::autoBufferSwap() const - - Returns \c true if the widget is doing automatic GL buffer swapping; - otherwise returns \c false. - - \sa setAutoBufferSwap() -*/ - -/*! - \fn QFunctionPointer QGLContext::getProcAddress(const QString &proc) const - - Returns a function pointer to the GL extension function passed in - \a proc. \nullptr is returned if a pointer to the function could not be - obtained. -*/ -QFunctionPointer QGLContext::getProcAddress(const QString &procName) const -{ - Q_D(const QGLContext); - return d->guiGlContext->getProcAddress(procName.toLatin1()); -} - -/*! - \fn bool QGLWidget::isValid() const - - Returns \c true if the widget has a valid GL rendering context; - otherwise returns \c false. A widget will be invalid if the system - has no \l{QGLFormat::hasOpenGL()}{OpenGL support}. -*/ - -bool QGLWidget::isValid() const -{ - Q_D(const QGLWidget); - return d->glcx && d->glcx->isValid(); -} - -/*! - \fn bool QGLWidget::isSharing() const - - Returns \c true if this widget's GL context is shared with another GL - context, otherwise false is returned. Context sharing might not be - possible if the widgets use different formats. - - \sa format() -*/ - -bool QGLWidget::isSharing() const -{ - Q_D(const QGLWidget); - return d->glcx->isSharing(); -} - -/*! - \fn void QGLWidget::makeCurrent() - - Makes this widget the current widget for OpenGL operations, i.e. - makes the widget's rendering context the current OpenGL rendering - context. -*/ - -void QGLWidget::makeCurrent() -{ - Q_D(QGLWidget); - d->makeCurrent(); -} - -bool QGLWidgetPrivate::makeCurrent() -{ - glcx->makeCurrent(); - return QGLContext::currentContext() == glcx; -} - -/*! - \fn void QGLWidget::doneCurrent() - - Makes no GL context the current context. Normally, you do not need - to call this function; QGLContext calls it as necessary. However, - it may be useful in multithreaded environments. -*/ - -void QGLWidget::doneCurrent() -{ - Q_D(QGLWidget); - d->glcx->doneCurrent(); -} - -/*! - \fn void QGLWidget::swapBuffers() - - Swaps the screen contents with an off-screen buffer. This only - works if the widget's format specifies double buffer mode. - - Normally, there is no need to explicitly call this function - because it is done automatically after each widget repaint, i.e. - each time after paintGL() has been executed. - - \sa doubleBuffer(), setAutoBufferSwap(), QGLFormat::setDoubleBuffer() -*/ - -void QGLWidget::swapBuffers() -{ - Q_D(QGLWidget); - d->glcx->swapBuffers(); -} - - -/*! - \fn const QGLContext* QGLWidget::overlayContext() const - - Returns the overlay context of this widget, or \nullptr if this - widget has no overlay. - - \sa context() -*/ -const QGLContext* QGLWidget::overlayContext() const -{ - return nullptr; -} - -/*! - \fn void QGLWidget::makeOverlayCurrent() - - Makes the overlay context of this widget current. Use this if you - need to issue OpenGL commands to the overlay context outside of - initializeOverlayGL(), resizeOverlayGL(), and paintOverlayGL(). - - Does nothing if this widget has no overlay. - - \sa makeCurrent() -*/ -void QGLWidget::makeOverlayCurrent() -{ -} - -/*! - \obsolete - - Sets a new format for this widget. - - If the underlying OpenGL/Window system cannot satisfy all the - features requested in \a format, the nearest subset of features will - be used. After creation, the format() method will return the actual - rendering context format obtained. - - The widget will be assigned a new QGLContext, and the initializeGL() - function will be executed for this new context before the first - resizeGL() or paintGL(). - - This method will try to keep display list and texture object sharing - in effect with other QGLWidget objects, but changing the format might make - sharing impossible. Use isSharing() to see if sharing is still in - effect. - - \sa format(), isSharing(), isValid() -*/ - -void QGLWidget::setFormat(const QGLFormat &format) -{ - setContext(new QGLContext(format,this)); -} - - - - -/*! - \fn QGLContext *QGLWidget::context() const - - Returns the context of this widget. - - It is possible that the context is not valid (see isValid()), for - example, if the underlying hardware does not support the format - attributes that were requested. -*/ - -/*! - \fn void QGLWidget::setContext(QGLContext *context, - const QGLContext* shareContext, - bool deleteOldContext) - \obsolete - - Sets a new context for this widget. The QGLContext \a context must - be created using \e new. QGLWidget will delete \a context when - another context is set or when the widget is destroyed. - - If \a context is invalid, QGLContext::create() is performed on - it. The initializeGL() function will then be executed for the new - context before the first resizeGL() or paintGL(). - - If \a context is invalid, this method will try to keep display list - and texture object sharing in effect, or (if \a shareContext points - to a valid context) start display list and texture object sharing - with that context, but sharing might be impossible if the two - contexts have different \l {format()} {formats}. Use isSharing() to - see whether sharing is in effect. - - If \a deleteOldContext is true (the default), the existing context - will be deleted. You may use false here if you have kept a pointer - to the old context (as returned by context()), and want to restore - that context later. - - \note This function is obsolete and should no longer be used. If you were - using it to recreate the context for a QGLWidget, you should instead create a - new QGLWidget or use the QOpenGLContext API in conjunction with QWindow. - There is currently no officially supported way to substitute QGLWidget's - context with your own implementation of QGLContext. - - \sa context(), isSharing() -*/ -void QGLWidget::setContext(QGLContext *context, - const QGLContext* shareContext, - bool deleteOldContext) -{ - Q_D(QGLWidget); - if (context == 0) { - qWarning("QGLWidget::setContext: Cannot set null context"); - return; - } - - if (context->device() == 0) // a context may refere to more than 1 window. - context->setDevice(this); //but its better to point to 1 of them than none of them. - - QGLContext* oldcx = d->glcx; - d->glcx = context; - - if (!d->glcx->isValid()) - d->glcx->create(shareContext ? shareContext : oldcx); - - if (deleteOldContext) - delete oldcx; -} - -/*! - \fn void QGLWidget::updateGL() - - Updates the widget by calling glDraw(). -*/ - -void QGLWidget::updateGL() -{ - Q_D(QGLWidget); - const bool targetIsOffscreen = !d->glcx->d_ptr->readback_target_size.isEmpty(); - if (updatesEnabled() && (testAttribute(Qt::WA_Mapped) || targetIsOffscreen)) - glDraw(); -} - - -/*! - \fn void QGLWidget::updateOverlayGL() - - Updates the widget's overlay (if any). Will cause the virtual - function paintOverlayGL() to be executed. - - The widget's rendering context will become the current context and - initializeGL() will be called if it hasn't already been called. -*/ -void QGLWidget::updateOverlayGL() -{ -} - -/*! - This virtual function is called once before the first call to - paintGL() or resizeGL(), and then once whenever the widget has - been assigned a new QGLContext. Reimplement it in a subclass. - - This function should set up any required OpenGL context rendering - flags, defining display lists, etc. - - There is no need to call makeCurrent() because this has already - been done when this function is called. -*/ - -void QGLWidget::initializeGL() -{ -} - - -/*! - This virtual function is called whenever the widget needs to be - painted. Reimplement it in a subclass. - - There is no need to call makeCurrent() because this has already - been done when this function is called. -*/ - -void QGLWidget::paintGL() -{ - qgl_functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - - -/*! - \fn void QGLWidget::resizeGL(int width , int height) - - This virtual function is called whenever the widget has been - resized. The new size is passed in \a width and \a height. - Reimplement it in a subclass. - - There is no need to call makeCurrent() because this has already - been done when this function is called. -*/ - -void QGLWidget::resizeGL(int, int) -{ -} - - - -/*! - This virtual function is used in the same manner as initializeGL() - except that it operates on the widget's overlay context instead of - the widget's main context. This means that initializeOverlayGL() - is called once before the first call to paintOverlayGL() or - resizeOverlayGL(). Reimplement it in a subclass. - - This function should set up any required OpenGL context rendering - flags, defining display lists, etc. for the overlay context. - - There is no need to call makeOverlayCurrent() because this has - already been done when this function is called. -*/ - -void QGLWidget::initializeOverlayGL() -{ -} - - -/*! - This virtual function is used in the same manner as paintGL() - except that it operates on the widget's overlay context instead of - the widget's main context. This means that paintOverlayGL() is - called whenever the widget's overlay needs to be painted. - Reimplement it in a subclass. - - There is no need to call makeOverlayCurrent() because this has - already been done when this function is called. -*/ - -void QGLWidget::paintOverlayGL() -{ -} - - -/*! - \fn void QGLWidget::resizeOverlayGL(int width , int height) - - This virtual function is used in the same manner as paintGL() - except that it operates on the widget's overlay context instead of - the widget's main context. This means that resizeOverlayGL() is - called whenever the widget has been resized. The new size is - passed in \a width and \a height. Reimplement it in a subclass. - - There is no need to call makeOverlayCurrent() because this has - already been done when this function is called. -*/ - -void QGLWidget::resizeOverlayGL(int, int) -{ -} - -/*!\reimp -*/ -bool QGLWidget::event(QEvent *e) -{ - Q_D(QGLWidget); - - // A re-parent will destroy the window and re-create it. We should not reset the context while it happens. - if (e->type() == QEvent::ParentAboutToChange) - d->parent_changing = true; - - if (e->type() == QEvent::ParentChange) - d->parent_changing = false; - - return QWidget::event(e); -} - -/*! - \fn void QGLWidget::paintEvent(QPaintEvent *event) - - Handles paint events passed in the \a event parameter. Will cause - the virtual paintGL() function to be called. - - The widget's rendering context will become the current context and - initializeGL() will be called if it hasn't already been called. -*/ - -void QGLWidget::paintEvent(QPaintEvent *) -{ - if (updatesEnabled()) { - glDraw(); - updateOverlayGL(); - } -} - - -/*! - \fn void QGLWidget::resizeEvent(QResizeEvent *event) - - Handles resize events that are passed in the \a event parameter. - Calls the virtual function resizeGL(). -*/ -void QGLWidget::resizeEvent(QResizeEvent *e) -{ - Q_D(QGLWidget); - - QWidget::resizeEvent(e); - if (!isValid()) - return; - if (!d->makeCurrent()) - return; - if (!d->glcx->initialized()) - glInit(); - const qreal scaleFactor = (window() && window()->windowHandle()) ? - window()->windowHandle()->devicePixelRatio() : 1.0; - - resizeGL(width() * scaleFactor, height() * scaleFactor); -} - -/*! - Renders the current scene on a pixmap and returns the pixmap. - - You can use this method on both visible and invisible QGLWidget objects. - - Internally the function renders into a framebuffer object and performs pixel - readback. This has a performance penalty, meaning that this function is not - suitable to be called at a high frequency. - - After creating and binding the framebuffer object, the function will call - initializeGL(), resizeGL(), and paintGL(). On the next normal update - initializeGL() and resizeGL() will be triggered again since the size of the - destination pixmap and the QGLWidget's size may differ. - - The size of the pixmap will be \a w pixels wide and \a h pixels high unless - one of these parameters is 0 (the default), in which case the pixmap will - have the same size as the widget. - - Care must be taken when using framebuffer objects in paintGL() in - combination with this function. To switch back to the default framebuffer, - use QGLFramebufferObject::bindDefault(). Binding FBO 0 is wrong since - renderPixmap() uses a custom framebuffer instead of the one provided by the - windowing system. - - \a useContext is ignored. Historically this parameter enabled the usage of - the existing GL context. This is not supported anymore since additional - contexts are never created. - - Overlays are not rendered onto the pixmap. - - If the GL rendering context and the desktop have different bit - depths, the result will most likely look surprising. - - Note that the creation of display lists, modifications of the view - frustum etc. should be done from within initializeGL(). If this is - not done, the temporary QGLContext will not be initialized - properly, and the rendered pixmap may be incomplete/corrupted. -*/ - -QPixmap QGLWidget::renderPixmap(int w, int h, bool useContext) -{ - Q_UNUSED(useContext); - Q_D(QGLWidget); - - QSize sz = size(); - if ((w > 0) && (h > 0)) - sz = QSize(w, h); - - QPixmap pm; - if (d->glcx->isValid()) { - d->glcx->makeCurrent(); - QGLFramebufferObject fbo(sz, QGLFramebufferObject::CombinedDepthStencil); - fbo.bind(); - d->glcx->setInitialized(false); - uint prevDefaultFbo = d->glcx->d_ptr->default_fbo; - d->glcx->d_ptr->default_fbo = fbo.handle(); - d->glcx->d_ptr->readback_target_size = sz; - updateGL(); - fbo.release(); - pm = QPixmap::fromImage(fbo.toImage()); - d->glcx->d_ptr->default_fbo = prevDefaultFbo; - d->glcx->setInitialized(false); - d->glcx->d_ptr->readback_target_size = QSize(); - } - - return pm; -} - -/*! - Returns an image of the frame buffer. If \a withAlpha is true the - alpha channel is included. - - Depending on your hardware, you can explicitly select which color - buffer to grab with a glReadBuffer() call before calling this - function. - - On QNX the back buffer is not preserved when swapBuffers() is called. The back buffer - where this function reads from, might thus not contain the same content as the front buffer. - In order to retrieve what is currently visible on the screen, swapBuffers() - has to be executed prior to this function call. -*/ -QImage QGLWidget::grabFrameBuffer(bool withAlpha) -{ - makeCurrent(); - QImage res; - qreal pixelRatio = devicePixelRatioF(); - int w = pixelRatio * width(); - int h = pixelRatio * height(); - if (format().rgba()) - res = qt_gl_read_frame_buffer(QSize(w, h), format().alpha(), withAlpha); - res.setDevicePixelRatio(pixelRatio); - return res; -} - - - -/*! - Initializes OpenGL for this widget's context. Calls the virtual - function initializeGL(). -*/ - -void QGLWidget::glInit() -{ - Q_D(QGLWidget); - if (!isValid()) - return; - if (!d->makeCurrent()) - return; - initializeGL(); - d->glcx->setInitialized(true); -} - - -/*! - Executes the virtual function paintGL(). - - The widget's rendering context will become the current context and - initializeGL() will be called if it hasn't already been called. -*/ - -void QGLWidget::glDraw() -{ - Q_D(QGLWidget); - if (!isValid()) - return; - if (!d->makeCurrent()) - return; -#ifndef QT_OPENGL_ES - if (d->glcx->deviceIsPixmap() && !d->glcx->contextHandle()->isOpenGLES()) - qgl1_functions()->glDrawBuffer(GL_FRONT); -#endif - QSize readback_target_size = d->glcx->d_ptr->readback_target_size; - if (!d->glcx->initialized()) { - glInit(); - const qreal scaleFactor = (window() && window()->windowHandle()) ? - window()->windowHandle()->devicePixelRatio() : 1.0; - int w, h; - if (readback_target_size.isEmpty()) { - w = d->glcx->device()->width() * scaleFactor; - h = d->glcx->device()->height() * scaleFactor; - } else { - w = readback_target_size.width(); - h = readback_target_size.height(); - } - resizeGL(w, h); // New context needs this "resize" - } - paintGL(); - if (doubleBuffer() && readback_target_size.isEmpty()) { - if (d->autoSwap) - swapBuffers(); - } else { - qgl_functions()->glFlush(); - } -} - -/*! - Convenience function for specifying a drawing color to OpenGL. - Calls glColor4 (in RGBA mode) or glIndex (in color-index mode) - with the color \a c. Applies to this widgets GL context. - - \note This function is not supported on OpenGL/ES 2.0 systems. - - \sa qglClearColor(), QGLContext::currentContext(), QColor -*/ - -void QGLWidget::qglColor(const QColor& c) const -{ -#if !defined(QT_OPENGL_ES_2) -#ifdef QT_OPENGL_ES - qgl_functions()->glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); -#else - Q_D(const QGLWidget); - const QGLContext *ctx = QGLContext::currentContext(); - if (ctx && !ctx->contextHandle()->isOpenGLES()) { - if (ctx->format().rgba()) - qgl1_functions()->glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - else if (!d->cmap.isEmpty()) { // QGLColormap in use? - int i = d->cmap.find(c.rgb()); - if (i < 0) - i = d->cmap.findNearest(c.rgb()); - qgl1_functions()->glIndexi(i); - } else - qgl1_functions()->glIndexi(ctx->colorIndex(c)); - } -#endif //QT_OPENGL_ES -#else - Q_UNUSED(c); -#endif //QT_OPENGL_ES_2 -} - -/*! - Convenience function for specifying the clearing color to OpenGL. - Calls glClearColor (in RGBA mode) or glClearIndex (in color-index - mode) with the color \a c. Applies to this widgets GL context. - - \sa qglColor(), QGLContext::currentContext(), QColor -*/ - -void QGLWidget::qglClearColor(const QColor& c) const -{ -#ifdef QT_OPENGL_ES - qgl_functions()->glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); -#else - Q_D(const QGLWidget); - const QGLContext *ctx = QGLContext::currentContext(); - if (ctx && !ctx->contextHandle()->isOpenGLES()) { - if (ctx->format().rgba()) - qgl_functions()->glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - else if (!d->cmap.isEmpty()) { // QGLColormap in use? - int i = d->cmap.find(c.rgb()); - if (i < 0) - i = d->cmap.findNearest(c.rgb()); - qgl1_functions()->glClearIndex(i); - } else { - qgl1_functions()->glClearIndex(ctx->colorIndex(c)); - } - } else { - qgl_functions()->glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - } -#endif -} - - -/*! - Converts the image \a img into the unnamed format expected by - OpenGL functions such as glTexImage2D(). The returned image is not - usable as a QImage, but QImage::width(), QImage::height() and - QImage::bits() may be used with OpenGL. The GL format used is - \c GL_RGBA. - - \omit ### - - \l opengl/texture example - The following few lines are from the texture example. Most of the - code is irrelevant, so we just quote the relevant bits: - - \quotefromfile opengl/texture/gltexobj.cpp - \skipto tex1 - \printline tex1 - \printline gllogo.bmp - - We create \e tex1 (and another variable) for OpenGL, and load a real - image into \e buf. - - \skipto convertToGLFormat - \printline convertToGLFormat - - A few lines later, we convert \e buf into OpenGL format and store it - in \e tex1. - - \skipto glTexImage2D - \printline glTexImage2D - \printline tex1.bits - - Note the dimension restrictions for texture images as described in - the glTexImage2D() documentation. The width must be 2^m + 2*border - and the height 2^n + 2*border where m and n are integers and - border is either 0 or 1. - - Another function in the same example uses \e tex1 with OpenGL. - - \endomit -*/ - -QImage QGLWidget::convertToGLFormat(const QImage& img) -{ - QImage res(img.size(), QImage::Format_ARGB32); - convertToGLFormatHelper(res, img.convertToFormat(QImage::Format_ARGB32), GL_RGBA); - return res; -} - - -/*! - \fn QGLColormap & QGLWidget::colormap() const - - Returns the colormap for this widget. - - Usually it is only top-level widgets that can have different - colormaps installed. Asking for the colormap of a child widget - will return the colormap for the child's top-level widget. - - If no colormap has been set for this widget, the QGLColormap - returned will be empty. - - \sa setColormap(), QGLColormap::isEmpty() -*/ -const QGLColormap & QGLWidget::colormap() const -{ - Q_D(const QGLWidget); - return d->cmap; -} - -/*! - \fn void QGLWidget::setColormap(const QGLColormap & cmap) - - Set the colormap for this widget to \a cmap. Usually it is only - top-level widgets that can have colormaps installed. - - \sa colormap() -*/ -void QGLWidget::setColormap(const QGLColormap & c) -{ - Q_UNUSED(c); -} - -#ifndef QT_OPENGL_ES - -static void qt_save_gl_state() -{ - QOpenGLFunctions *funcs = qgl_functions(); - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - - gl1funcs->glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); - gl1funcs->glPushAttrib(GL_ALL_ATTRIB_BITS); - gl1funcs->glMatrixMode(GL_TEXTURE); - gl1funcs->glPushMatrix(); - gl1funcs->glLoadIdentity(); - gl1funcs->glMatrixMode(GL_PROJECTION); - gl1funcs->glPushMatrix(); - gl1funcs->glMatrixMode(GL_MODELVIEW); - gl1funcs->glPushMatrix(); - - gl1funcs->glShadeModel(GL_FLAT); - funcs->glDisable(GL_CULL_FACE); - funcs->glDisable(GL_LIGHTING); - funcs->glDisable(GL_STENCIL_TEST); - funcs->glDisable(GL_DEPTH_TEST); - funcs->glEnable(GL_BLEND); - funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -} - -static void qt_restore_gl_state() -{ - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - - gl1funcs->glMatrixMode(GL_TEXTURE); - gl1funcs->glPopMatrix(); - gl1funcs->glMatrixMode(GL_PROJECTION); - gl1funcs->glPopMatrix(); - gl1funcs->glMatrixMode(GL_MODELVIEW); - gl1funcs->glPopMatrix(); - gl1funcs->glPopAttrib(); - gl1funcs->glPopClientAttrib(); -} - -static void qt_gl_draw_text(QPainter *p, int x, int y, const QString &str, - const QFont &font) -{ - GLfloat color[4]; - qgl_functions()->glGetFloatv(GL_CURRENT_COLOR, &color[0]); - - QColor col; - col.setRgbF(color[0], color[1], color[2],color[3]); - QPen old_pen = p->pen(); - QFont old_font = p->font(); - - p->setPen(col); - p->setFont(font); - p->drawText(x, y, str); - - p->setPen(old_pen); - p->setFont(old_font); -} - -#endif // !QT_OPENGL_ES - -/*! - Renders the string \a str into the GL context of this widget. - - \a x and \a y are specified in window coordinates, with the origin - in the upper left-hand corner of the window. If \a font is not - specified, the currently set application font will be used to - render the string. To change the color of the rendered text you can - use the glColor() call (or the qglColor() convenience function), - just before the renderText() call. - - \note This function clears the stencil buffer. - - \note This function is not supported on OpenGL/ES systems. - - \note This function temporarily disables depth-testing when the - text is drawn. - - \note This function can only be used inside a - QPainter::beginNativePainting()/QPainter::endNativePainting() block - if a painter is active on the QGLWidget. -*/ - -void QGLWidget::renderText(int x, int y, const QString &str, const QFont &font) -{ -#ifndef QT_OPENGL_ES - Q_D(QGLWidget); - if (!d->glcx->contextHandle()->isOpenGLES()) { - Q_D(QGLWidget); - if (str.isEmpty() || !isValid()) - return; - - QOpenGLFunctions *funcs = qgl_functions(); - GLint view[4]; - bool use_scissor_testing = funcs->glIsEnabled(GL_SCISSOR_TEST); - if (!use_scissor_testing) - funcs->glGetIntegerv(GL_VIEWPORT, &view[0]); - int width = d->glcx->device()->width(); - int height = d->glcx->device()->height(); - bool auto_swap = autoBufferSwap(); - - QPaintEngine *engine = paintEngine(); - - qt_save_gl_state(); - - QPainter *p; - bool reuse_painter = false; - if (engine->isActive()) { - reuse_painter = true; - p = engine->painter(); - - funcs->glDisable(GL_DEPTH_TEST); - funcs->glViewport(0, 0, width, height); - } else { - setAutoBufferSwap(false); - // disable glClear() as a result of QPainter::begin() - d->disable_clear_on_painter_begin = true; - p = new QPainter(this); - } - - QRect viewport(view[0], view[1], view[2], view[3]); - if (!use_scissor_testing && viewport != rect()) { - // if the user hasn't set a scissor box, we set one that - // covers the current viewport - funcs->glScissor(view[0], view[1], view[2], view[3]); - funcs->glEnable(GL_SCISSOR_TEST); - } else if (use_scissor_testing) { - // use the scissor box set by the user - funcs->glEnable(GL_SCISSOR_TEST); - } - - qt_gl_draw_text(p, x, y, str, font); - - if (!reuse_painter) { - p->end(); - delete p; - setAutoBufferSwap(auto_swap); - d->disable_clear_on_painter_begin = false; - } - - qt_restore_gl_state(); - - return; - } -#else // QT_OPENGL_ES - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(str); - Q_UNUSED(font); -#endif - qWarning("QGLWidget::renderText is not supported under OpenGL/ES"); -} - -/*! \overload - - \a x, \a y and \a z are specified in scene or object coordinates - relative to the currently set projection and model matrices. This - can be useful if you want to annotate models with text labels and - have the labels move with the model as it is rotated etc. - - \note This function is not supported on OpenGL/ES systems. - - \note If depth testing is enabled before this function is called, - then the drawn text will be depth-tested against the models that - have already been drawn in the scene. Use \c{glDisable(GL_DEPTH_TEST)} - before calling this function to annotate the models without - depth-testing the text. - - \note This function can only be used inside a - QPainter::beginNativePainting()/QPainter::endNativePainting() block - if a painter is active on the QGLWidget. -*/ -void QGLWidget::renderText(double x, double y, double z, const QString &str, const QFont &font) -{ -#ifndef QT_OPENGL_ES - Q_D(QGLWidget); - if (!d->glcx->contextHandle()->isOpenGLES()) { - Q_D(QGLWidget); - if (str.isEmpty() || !isValid()) - return; - - QOpenGLFunctions *funcs = qgl_functions(); - bool auto_swap = autoBufferSwap(); - - int width = d->glcx->device()->width(); - int height = d->glcx->device()->height(); - GLdouble model[4 * 4], proj[4 * 4]; - GLint view[4]; - QOpenGLFunctions_1_1 *gl1funcs = qgl1_functions(); - gl1funcs->glGetDoublev(GL_MODELVIEW_MATRIX, &model[0]); - gl1funcs->glGetDoublev(GL_PROJECTION_MATRIX, &proj[0]); - funcs->glGetIntegerv(GL_VIEWPORT, &view[0]); - GLdouble win_x = 0, win_y = 0, win_z = 0; - qgluProject(x, y, z, &model[0], &proj[0], &view[0], - &win_x, &win_y, &win_z); - const int dpr = d->glcx->device()->devicePixelRatioF(); - win_x /= dpr; - win_y /= dpr; - win_y = height - win_y; // y is inverted - - QPaintEngine *engine = paintEngine(); - - QPainter *p; - bool reuse_painter = false; - bool use_depth_testing = funcs->glIsEnabled(GL_DEPTH_TEST); - bool use_scissor_testing = funcs->glIsEnabled(GL_SCISSOR_TEST); - - qt_save_gl_state(); - - if (engine->isActive()) { - reuse_painter = true; - p = engine->painter(); - } else { - setAutoBufferSwap(false); - // disable glClear() as a result of QPainter::begin() - d->disable_clear_on_painter_begin = true; - p = new QPainter(this); - } - - QRect viewport(view[0], view[1], view[2], view[3]); - if (!use_scissor_testing && viewport != rect()) { - funcs->glScissor(view[0], view[1], view[2], view[3]); - funcs->glEnable(GL_SCISSOR_TEST); - } else if (use_scissor_testing) { - funcs->glEnable(GL_SCISSOR_TEST); - } - funcs->glViewport(0, 0, width * dpr, height * dpr); - gl1funcs->glAlphaFunc(GL_GREATER, 0.0); - funcs->glEnable(GL_ALPHA_TEST); - if (use_depth_testing) - funcs->glEnable(GL_DEPTH_TEST); - - // The only option in Qt 5 is the shader-based OpenGL 2 paint engine. - // Setting fixed pipeline transformations is futile. Instead, pass the - // extra values directly and let the engine figure the matrices out. - static_cast<QGL2PaintEngineEx *>(p->paintEngine())->setTranslateZ(-2 * win_z); - - qt_gl_draw_text(p, qRound(win_x), qRound(win_y), str, font); - - static_cast<QGL2PaintEngineEx *>(p->paintEngine())->setTranslateZ(0); - - if (!reuse_painter) { - p->end(); - delete p; - setAutoBufferSwap(auto_swap); - d->disable_clear_on_painter_begin = false; - } - - qt_restore_gl_state(); - - return; - } -#else // QT_OPENGL_ES - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(z); - Q_UNUSED(str); - Q_UNUSED(font); -#endif - qWarning("QGLWidget::renderText is not supported under OpenGL/ES"); -} - -QGLFormat QGLWidget::format() const -{ - Q_D(const QGLWidget); - return d->glcx->format(); -} - -QGLContext *QGLWidget::context() const -{ - Q_D(const QGLWidget); - return d->glcx; -} - -bool QGLWidget::doubleBuffer() const -{ - Q_D(const QGLWidget); - return d->glcx->d_ptr->glFormat.testOption(QGL::DoubleBuffer); -} - -void QGLWidget::setAutoBufferSwap(bool on) -{ - Q_D(QGLWidget); - d->autoSwap = on; -} - -bool QGLWidget::autoBufferSwap() const -{ - Q_D(const QGLWidget); - return d->autoSwap; -} - -/*! - Calls QGLContext:::bindTexture(\a image, \a target, \a format) on the currently - set context. - - \sa deleteTexture() -*/ -GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format) -{ - if (image.isNull()) - return 0; - - Q_D(QGLWidget); - return d->glcx->bindTexture(image, target, format, QGLContext::DefaultBindOption); -} - -/*! - \overload - \since 4.6 - - The binding \a options are a set of options used to decide how to - bind the texture to the context. - */ -GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options) -{ - if (image.isNull()) - return 0; - - Q_D(QGLWidget); - return d->glcx->bindTexture(image, target, format, options); -} - - -/*! - Calls QGLContext:::bindTexture(\a pixmap, \a target, \a format) on the currently - set context. - - \sa deleteTexture() -*/ -GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) -{ - if (pixmap.isNull()) - return 0; - - Q_D(QGLWidget); - return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption); -} - -/*! - \overload - \since 4.6 - - Generates and binds a 2D GL texture to the current context, based - on \a pixmap. The generated texture id is returned and can be used in - - The binding \a options are a set of options used to decide how to - bind the texture to the context. - */ -GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, - QGLContext::BindOptions options) -{ - Q_D(QGLWidget); - return d->glcx->bindTexture(pixmap, target, format, options); -} - -/*! \overload - - Calls QGLContext::bindTexture(\a fileName) on the currently set context. - - \sa deleteTexture() -*/ -GLuint QGLWidget::bindTexture(const QString &fileName) -{ - Q_D(QGLWidget); - return d->glcx->bindTexture(fileName); -} - -/*! - Calls QGLContext::deleteTexture(\a id) on the currently set - context. - - \sa bindTexture() -*/ -void QGLWidget::deleteTexture(GLuint id) -{ - Q_D(QGLWidget); - d->glcx->deleteTexture(id); -} - -/*! - \since 4.4 - - Calls the corresponding QGLContext::drawTexture() with - \a target, \a textureId, and \a textureTarget for this - widget's context. -*/ -void QGLWidget::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) -{ - Q_D(QGLWidget); - d->glcx->drawTexture(target, textureId, textureTarget); -} - -/*! - \since 4.4 - - Calls the corresponding QGLContext::drawTexture() with - \a point, \a textureId, and \a textureTarget for this - widget's context. -*/ -void QGLWidget::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) -{ - Q_D(QGLWidget); - d->glcx->drawTexture(point, textureId, textureTarget); -} - -Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_gl_2_engine) - -Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine() -{ - return qt_gl_2_engine()->engine(); -} - -/*! - \internal - - Returns the GL widget's paint engine. -*/ -QPaintEngine *QGLWidget::paintEngine() const -{ - return qt_qgl_paint_engine(); -} - -void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget *shareWidget) -{ - Q_Q(QGLWidget); - q->setAttribute(Qt::WA_PaintOnScreen); - q->setAttribute(Qt::WA_NoSystemBackground); - q->setAutoFillBackground(true); // for compatibility - - mustHaveWindowHandle = 1; - q->setAttribute(Qt::WA_NativeWindow); - q->setWindowFlag(Qt::MSWindowsOwnDC); - - initContext(context, shareWidget); -} - -/* - This is the shared initialization for all platforms. Called from QGLWidgetPrivate::init() -*/ -void QGLWidgetPrivate::initContext(QGLContext *context, const QGLWidget* shareWidget) -{ - Q_Q(QGLWidget); - - glDevice.setWidget(q); - - glcx = 0; - autoSwap = true; - - if (context && !context->device()) - context->setDevice(q); - q->setContext(context, shareWidget ? shareWidget->context() : 0); - - if (!glcx) - glcx = new QGLContext(QGLFormat::defaultFormat(), q); -} - -bool QGLWidgetPrivate::renderCxPm(QPixmap*) -{ - return false; -} - -/*! \internal - Free up any allocated colormaps. This fn is only called for - top-level widgets. -*/ -void QGLWidgetPrivate::cleanupColormaps() -{ -} - -void QGLContextGroup::addShare(const QGLContext *context, const QGLContext *share) { - Q_ASSERT(context && share); - if (context->d_ptr->group == share->d_ptr->group) - return; - - // Make sure 'context' is not already shared with another group of contexts. - Q_ASSERT(context->d_ptr->group->m_refs.loadRelaxed() == 1); - - // Free 'context' group resources and make it use the same resources as 'share'. - QGLContextGroup *group = share->d_ptr->group; - delete context->d_ptr->group; - context->d_ptr->group = group; - group->m_refs.ref(); - - // Maintain a list of all the contexts in each group of sharing contexts. - // The list is empty if the "share" context wasn't sharing already. - if (group->m_shares.isEmpty()) - group->m_shares.append(share); - group->m_shares.append(context); -} - -void QGLContextGroup::removeShare(const QGLContext *context) { - // Remove the context from the group. - QGLContextGroup *group = context->d_ptr->group; - if (group->m_shares.isEmpty()) - return; - group->m_shares.removeAll(context); - - // Update context group representative. - Q_ASSERT(group->m_shares.size() != 0); - if (group->m_context == context) - group->m_context = group->m_shares.at(0); - - // If there is only one context left, then make the list empty. - if (group->m_shares.size() == 1) - group->m_shares.clear(); -} - -QSize QGLTexture::bindCompressedTexture - (const QString& fileName, const char *format) -{ - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) - return QSize(); - QByteArray contents = file.readAll(); - file.close(); - return bindCompressedTexture - (contents.constData(), contents.size(), format); -} - -// PVR header format for container files that store textures compressed -// with the ETC1, PVRTC2, and PVRTC4 encodings. Format information from the -// PowerVR SDK at http://www.imgtec.com/powervr/insider/powervr-sdk.asp -// "PVRTexTool Reference Manual, version 1.11f". -struct PvrHeader -{ - quint32 headerSize; - quint32 height; - quint32 width; - quint32 mipMapCount; - quint32 flags; - quint32 dataSize; - quint32 bitsPerPixel; - quint32 redMask; - quint32 greenMask; - quint32 blueMask; - quint32 alphaMask; - quint32 magic; - quint32 surfaceCount; -}; - -#define PVR_MAGIC 0x21525650 // "PVR!" in little-endian - -#define PVR_FORMAT_MASK 0x000000FF -#define PVR_FORMAT_PVRTC2 0x00000018 -#define PVR_FORMAT_PVRTC4 0x00000019 -#define PVR_FORMAT_ETC1 0x00000036 - -#define PVR_HAS_MIPMAPS 0x00000100 -#define PVR_TWIDDLED 0x00000200 -#define PVR_NORMAL_MAP 0x00000400 -#define PVR_BORDER_ADDED 0x00000800 -#define PVR_CUBE_MAP 0x00001000 -#define PVR_FALSE_COLOR_MIPMAPS 0x00002000 -#define PVR_VOLUME_TEXTURE 0x00004000 -#define PVR_ALPHA_IN_TEXTURE 0x00008000 -#define PVR_VERTICAL_FLIP 0x00010000 - -#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG -#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 -#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 -#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 -#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 -#endif - -#ifndef GL_ETC1_RGB8_OES -#define GL_ETC1_RGB8_OES 0x8D64 -#endif - -bool QGLTexture::canBindCompressedTexture - (const char *buf, int len, const char *format, bool *hasAlpha) -{ - if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) { - // Compressed texture loading only supported on little-endian - // systems such as x86 and ARM at the moment. - return false; - } - if (!format) { - // Auto-detect the format from the header. - if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) { - *hasAlpha = true; - return true; - } else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) { - const PvrHeader *pvrHeader = - reinterpret_cast<const PvrHeader *>(buf); - *hasAlpha = (pvrHeader->alphaMask != 0); - return true; - } - } else { - // Validate the format against the header. - if (!qstricmp(format, "DDS")) { - if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) { - *hasAlpha = true; - return true; - } - } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) { - if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) { - const PvrHeader *pvrHeader = - reinterpret_cast<const PvrHeader *>(buf); - *hasAlpha = (pvrHeader->alphaMask != 0); - return true; - } - } - } - return false; -} - -#define ctx QGLContext::currentContext() - -QSize QGLTexture::bindCompressedTexture - (const char *buf, int len, const char *format) -{ - if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) { - // Compressed texture loading only supported on little-endian - // systems such as x86 and ARM at the moment. - return QSize(); - } - if (!format) { - // Auto-detect the format from the header. - if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) - return bindCompressedTextureDDS(buf, len); - else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) - return bindCompressedTexturePVR(buf, len); - } else { - // Validate the format against the header. - if (!qstricmp(format, "DDS")) { - if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) - return bindCompressedTextureDDS(buf, len); - } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) { - if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) - return bindCompressedTexturePVR(buf, len); - } - } - return QSize(); -} - -QSize QGLTexture::bindCompressedTextureDDS(const char *buf, int len) -{ - // We only support 2D texture loading at present. - if (target != GL_TEXTURE_2D) - return QSize(); - - // Bail out if the necessary extension is not present. - if (!qgl_extensions()->hasOpenGLExtension(QOpenGLExtensions::DDSTextureCompression)) { - qWarning("QGLContext::bindTexture(): DDS texture compression is not supported."); - return QSize(); - } - - const DDSFormat *ddsHeader = reinterpret_cast<const DDSFormat *>(buf + 4); - if (!ddsHeader->dwLinearSize) { - qWarning("QGLContext::bindTexture(): DDS image size is not valid."); - return QSize(); - } - - int blockSize = 16; - GLenum format; - - switch(ddsHeader->ddsPixelFormat.dwFourCC) { - case FOURCC_DXT1: - format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - blockSize = 8; - break; - case FOURCC_DXT3: - format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - break; - case FOURCC_DXT5: - format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - default: - qWarning("QGLContext::bindTexture(): DDS image format not supported."); - return QSize(); - } - - const GLubyte *pixels = - reinterpret_cast<const GLubyte *>(buf + ddsHeader->dwSize + 4); - - QOpenGLFunctions *funcs = qgl_functions(); - funcs->glGenTextures(1, &id); - funcs->glBindTexture(GL_TEXTURE_2D, id); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - int size; - int offset = 0; - int available = len - int(ddsHeader->dwSize + 4); - int w = ddsHeader->dwWidth; - int h = ddsHeader->dwHeight; - - // load mip-maps - for(int i = 0; i < (int) ddsHeader->dwMipMapCount; ++i) { - if (w == 0) w = 1; - if (h == 0) h = 1; - - size = ((w+3)/4) * ((h+3)/4) * blockSize; - if (size > available) - break; - qgl_extensions()->glCompressedTexImage2D(GL_TEXTURE_2D, i, format, w, h, 0, - size, pixels + offset); - offset += size; - available -= size; - - // half size for each mip-map level - w = w/2; - h = h/2; - } - - // DDS images are not inverted. - options &= ~QGLContext::InvertedYBindOption; - - return QSize(ddsHeader->dwWidth, ddsHeader->dwHeight); -} - -QSize QGLTexture::bindCompressedTexturePVR(const char *buf, int len) -{ - // We only support 2D texture loading at present. Cube maps later. - if (target != GL_TEXTURE_2D) - return QSize(); - - // Determine which texture format we will be loading. - const PvrHeader *pvrHeader = reinterpret_cast<const PvrHeader *>(buf); - GLenum textureFormat; - quint32 minWidth, minHeight; - switch (pvrHeader->flags & PVR_FORMAT_MASK) { - case PVR_FORMAT_PVRTC2: - if (pvrHeader->alphaMask) - textureFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - else - textureFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - minWidth = 16; - minHeight = 8; - break; - - case PVR_FORMAT_PVRTC4: - if (pvrHeader->alphaMask) - textureFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - else - textureFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - minWidth = 8; - minHeight = 8; - break; - - case PVR_FORMAT_ETC1: - textureFormat = GL_ETC1_RGB8_OES; - minWidth = 4; - minHeight = 4; - break; - - default: - qWarning("QGLContext::bindTexture(): PVR image format 0x%x not supported.", int(pvrHeader->flags & PVR_FORMAT_MASK)); - return QSize(); - } - - // Bail out if the necessary extension is not present. - if (textureFormat == GL_ETC1_RGB8_OES) { - if (!qgl_extensions()->hasOpenGLExtension(QOpenGLExtensions::ETC1TextureCompression)) { - qWarning("QGLContext::bindTexture(): ETC1 texture compression is not supported."); - return QSize(); - } - } else { - if (!qgl_extensions()->hasOpenGLExtension(QOpenGLExtensions::PVRTCTextureCompression)) { - qWarning("QGLContext::bindTexture(): PVRTC texture compression is not supported."); - return QSize(); - } - } - - // Boundary check on the buffer size. - quint32 bufferSize = pvrHeader->headerSize + pvrHeader->dataSize; - if (bufferSize > quint32(len)) { - qWarning("QGLContext::bindTexture(): PVR image size is not valid."); - return QSize(); - } - - // Create the texture. - QOpenGLFunctions *funcs = qgl_functions(); - funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - funcs->glGenTextures(1, &id); - funcs->glBindTexture(GL_TEXTURE_2D, id); - if (pvrHeader->mipMapCount) { - if ((options & QGLContext::LinearFilteringBindOption) != 0) { - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } else { - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - } - } else if ((options & QGLContext::LinearFilteringBindOption) != 0) { - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - - // Load the compressed mipmap levels. - const GLubyte *buffer = - reinterpret_cast<const GLubyte *>(buf + pvrHeader->headerSize); - bufferSize = pvrHeader->dataSize; - quint32 level = 0; - quint32 width = pvrHeader->width; - quint32 height = pvrHeader->height; - while (bufferSize > 0 && level <= pvrHeader->mipMapCount) { - quint32 size = - (qMax(width, minWidth) * qMax(height, minHeight) * - pvrHeader->bitsPerPixel) / 8; - if (size > bufferSize) - break; - qgl_extensions()->glCompressedTexImage2D(GL_TEXTURE_2D, GLint(level), textureFormat, - GLsizei(width), GLsizei(height), 0, - GLsizei(size), buffer); - width /= 2; - height /= 2; - buffer += size; - ++level; - } - - // Restore the default pixel alignment for later texture uploads. - funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - - // Set the invert flag for the texture. The "vertical flip" - // flag in PVR is the opposite sense to our sense of inversion. - options.setFlag(QGLContext::InvertedYBindOption, !(pvrHeader->flags & PVR_VERTICAL_FLIP)); - - return QSize(pvrHeader->width, pvrHeader->height); -} - -#undef ctx - -QT_END_NAMESPACE diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h deleted file mode 100644 index f5accbeb3c..0000000000 --- a/src/opengl/qgl.h +++ /dev/null @@ -1,524 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGL_H -#define QGL_H - -#ifndef QT_NO_OPENGL - -#include <QtGui/qopengl.h> -#include <QtWidgets/qwidget.h> -#include <QtGui/qpaintengine.h> -#include <QtOpenGL/qglcolormap.h> -#include <QtCore/qmap.h> -#include <QtCore/qscopedpointer.h> - -#include <QtGui/QSurfaceFormat> - -QT_BEGIN_NAMESPACE - - - - -class QPixmap; -class QGLWidgetPrivate; -class QGLContextPrivate; - -// Namespace class: -namespace QGL -{ - enum FormatOption { - DoubleBuffer = 0x0001, - DepthBuffer = 0x0002, - Rgba = 0x0004, - AlphaChannel = 0x0008, - AccumBuffer = 0x0010, - StencilBuffer = 0x0020, - StereoBuffers = 0x0040, - DirectRendering = 0x0080, - HasOverlay = 0x0100, - SampleBuffers = 0x0200, - DeprecatedFunctions = 0x0400, - SingleBuffer = DoubleBuffer << 16, - NoDepthBuffer = DepthBuffer << 16, - ColorIndex = Rgba << 16, - NoAlphaChannel = AlphaChannel << 16, - NoAccumBuffer = AccumBuffer << 16, - NoStencilBuffer = StencilBuffer << 16, - NoStereoBuffers = StereoBuffers << 16, - IndirectRendering = DirectRendering << 16, - NoOverlay = HasOverlay << 16, - NoSampleBuffers = SampleBuffers << 16, - NoDeprecatedFunctions = DeprecatedFunctions << 16 - }; - Q_DECLARE_FLAGS(FormatOptions, FormatOption) -} - -Q_DECLARE_OPERATORS_FOR_FLAGS(QGL::FormatOptions) - -class QGLFormatPrivate; - -class Q_OPENGL_EXPORT QGLFormat -{ -public: - QGLFormat(); - QGLFormat(QGL::FormatOptions options, int plane = 0); - QGLFormat(const QGLFormat &other); - QGLFormat &operator=(const QGLFormat &other); - ~QGLFormat(); - - void setDepthBufferSize(int size); - int depthBufferSize() const; - - void setAccumBufferSize(int size); - int accumBufferSize() const; - - void setRedBufferSize(int size); - int redBufferSize() const; - - void setGreenBufferSize(int size); - int greenBufferSize() const; - - void setBlueBufferSize(int size); - int blueBufferSize() const; - - void setAlphaBufferSize(int size); - int alphaBufferSize() const; - - void setStencilBufferSize(int size); - int stencilBufferSize() const; - - void setSampleBuffers(bool enable); - bool sampleBuffers() const; - - void setSamples(int numSamples); - int samples() const; - - void setSwapInterval(int interval); - int swapInterval() const; - - bool doubleBuffer() const; - void setDoubleBuffer(bool enable); - bool depth() const; - void setDepth(bool enable); - bool rgba() const; - void setRgba(bool enable); - bool alpha() const; - void setAlpha(bool enable); - bool accum() const; - void setAccum(bool enable); - bool stencil() const; - void setStencil(bool enable); - bool stereo() const; - void setStereo(bool enable); - bool directRendering() const; - void setDirectRendering(bool enable); - bool hasOverlay() const; - void setOverlay(bool enable); - - int plane() const; - void setPlane(int plane); - - void setOption(QGL::FormatOptions opt); - bool testOption(QGL::FormatOptions opt) const; - - static QGLFormat defaultFormat(); - static void setDefaultFormat(const QGLFormat& f); - - static QGLFormat defaultOverlayFormat(); - static void setDefaultOverlayFormat(const QGLFormat& f); - - static bool hasOpenGL(); - static bool hasOpenGLOverlays(); - - void setVersion(int major, int minor); - int majorVersion() const; - int minorVersion() const; - - enum OpenGLContextProfile { - NoProfile, - CoreProfile, - CompatibilityProfile - }; - - void setProfile(OpenGLContextProfile profile); - OpenGLContextProfile profile() const; - - enum OpenGLVersionFlag { - OpenGL_Version_None = 0x00000000, - OpenGL_Version_1_1 = 0x00000001, - OpenGL_Version_1_2 = 0x00000002, - OpenGL_Version_1_3 = 0x00000004, - OpenGL_Version_1_4 = 0x00000008, - OpenGL_Version_1_5 = 0x00000010, - OpenGL_Version_2_0 = 0x00000020, - OpenGL_Version_2_1 = 0x00000040, - OpenGL_ES_Common_Version_1_0 = 0x00000080, - OpenGL_ES_CommonLite_Version_1_0 = 0x00000100, - OpenGL_ES_Common_Version_1_1 = 0x00000200, - OpenGL_ES_CommonLite_Version_1_1 = 0x00000400, - OpenGL_ES_Version_2_0 = 0x00000800, - OpenGL_Version_3_0 = 0x00001000, - OpenGL_Version_3_1 = 0x00002000, - OpenGL_Version_3_2 = 0x00004000, - OpenGL_Version_3_3 = 0x00008000, - OpenGL_Version_4_0 = 0x00010000, - OpenGL_Version_4_1 = 0x00020000, - OpenGL_Version_4_2 = 0x00040000, - OpenGL_Version_4_3 = 0x00080000 - }; - Q_DECLARE_FLAGS(OpenGLVersionFlags, OpenGLVersionFlag) - - static OpenGLVersionFlags openGLVersionFlags(); - - static QGLFormat fromSurfaceFormat(const QSurfaceFormat &format); - static QSurfaceFormat toSurfaceFormat(const QGLFormat &format); -private: - QGLFormatPrivate *d; - - void detach(); - - friend Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&); - friend Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&); -#ifndef QT_NO_DEBUG_STREAM - friend Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QGLFormat &); -#endif -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFormat::OpenGLVersionFlags) - -Q_OPENGL_EXPORT bool operator==(const QGLFormat&, const QGLFormat&); -Q_OPENGL_EXPORT bool operator!=(const QGLFormat&, const QGLFormat&); - -#ifndef QT_NO_DEBUG_STREAM -Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QGLFormat &); -#endif - -class QGLFunctions; - -class Q_OPENGL_EXPORT QGLContext -{ - Q_DECLARE_PRIVATE(QGLContext) -public: - QGLContext(const QGLFormat& format, QPaintDevice* device); - QGLContext(const QGLFormat& format); - virtual ~QGLContext(); - - virtual bool create(const QGLContext* shareContext = nullptr); - bool isValid() const; - bool isSharing() const; - void reset(); - - static bool areSharing(const QGLContext *context1, const QGLContext *context2); - - QGLFormat format() const; - QGLFormat requestedFormat() const; - void setFormat(const QGLFormat& format); - - void moveToThread(QThread *thread); - - virtual void makeCurrent(); - virtual void doneCurrent(); - - virtual void swapBuffers() const; - - QGLFunctions *functions() const; - - enum BindOption { - NoBindOption = 0x0000, - InvertedYBindOption = 0x0001, - MipmapBindOption = 0x0002, - PremultipliedAlphaBindOption = 0x0004, - LinearFilteringBindOption = 0x0008, - - MemoryManagedBindOption = 0x0010, // internal flag - CanFlipNativePixmapBindOption = 0x0020, // internal flag - TemporarilyCachedBindOption = 0x0040, // internal flag - - DefaultBindOption = LinearFilteringBindOption - | InvertedYBindOption - | MipmapBindOption, - InternalBindOption = MemoryManagedBindOption - | PremultipliedAlphaBindOption - }; - Q_DECLARE_FLAGS(BindOptions, BindOption) - - GLuint bindTexture(const QImage &image, GLenum target, GLint format, - BindOptions options); - GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format, - BindOptions options); - - GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D, - GLint format = GL_RGBA); - GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D, - GLint format = GL_RGBA); - GLuint bindTexture(const QString &fileName); - - void deleteTexture(GLuint tx_id); - - void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - - static void setTextureCacheLimit(int size); - static int textureCacheLimit(); - - QFunctionPointer getProcAddress(const QString &proc) const; - QPaintDevice* device() const; - QColor overlayTransparentColor() const; - - static const QGLContext* currentContext(); - - static QGLContext *fromOpenGLContext(QOpenGLContext *platformContext); - QOpenGLContext *contextHandle() const; - -protected: - virtual bool chooseContext(const QGLContext* shareContext = nullptr); - - bool deviceIsPixmap() const; - bool windowCreated() const; - void setWindowCreated(bool on); - bool initialized() const; - void setInitialized(bool on); - - uint colorIndex(const QColor& c) const; - void setValid(bool valid); - void setDevice(QPaintDevice *pDev); - -protected: - static QGLContext* currentCtx; - -private: - QGLContext(QOpenGLContext *windowContext); - - QScopedPointer<QGLContextPrivate> d_ptr; - - friend class QGLPixelBuffer; - friend class QGLPixelBufferPrivate; - friend class QGLWidget; - friend class QGLWidgetPrivate; - friend class QGLGlyphCache; - friend class QGL2PaintEngineEx; - friend class QGL2PaintEngineExPrivate; - friend class QGLEngineShaderManager; - friend class QGLTextureGlyphCache; - friend struct QGLGlyphTexture; - friend class QGLContextGroup; - friend class QGLPixmapBlurFilter; - friend class QGLTexture; - friend QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags(); - friend class QGLFramebufferObject; - friend class QGLFramebufferObjectPrivate; - friend class QGLFBOGLPaintDevice; - friend class QGLPaintDevice; - friend class QGLWidgetGLPaintDevice; - friend class QX11GLSharedContexts; - friend class QGLContextResourceBase; - friend class QSGDistanceFieldGlyphCache; -private: - Q_DISABLE_COPY(QGLContext) -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QGLContext::BindOptions) - -class Q_OPENGL_EXPORT QGLWidget : public QWidget -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QGLWidget) -public: - explicit QGLWidget(QWidget* parent=nullptr, - const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f=Qt::WindowFlags()); - explicit QGLWidget(QGLContext *context, QWidget* parent=nullptr, - const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f=Qt::WindowFlags()); - explicit QGLWidget(const QGLFormat& format, QWidget* parent=nullptr, - const QGLWidget* shareWidget = nullptr, Qt::WindowFlags f=Qt::WindowFlags()); - ~QGLWidget(); - - void qglColor(const QColor& c) const; - void qglClearColor(const QColor& c) const; - - bool isValid() const; - bool isSharing() const; - - void makeCurrent(); - void doneCurrent(); - - bool doubleBuffer() const; - void swapBuffers(); - - QGLFormat format() const; - void setFormat(const QGLFormat& format); - - QGLContext* context() const; - void setContext(QGLContext* context, const QGLContext* shareContext = nullptr, - bool deleteOldContext = true); - - QPixmap renderPixmap(int w = 0, int h = 0, bool useContext = false); - QImage grabFrameBuffer(bool withAlpha = false); - - void makeOverlayCurrent(); - const QGLContext* overlayContext() const; - - static QImage convertToGLFormat(const QImage& img); - - const QGLColormap & colormap() const; - void setColormap(const QGLColormap & map); - - void renderText(int x, int y, const QString & str, - const QFont & fnt = QFont()); - void renderText(double x, double y, double z, const QString & str, - const QFont & fnt = QFont()); - QPaintEngine *paintEngine() const override; - - GLuint bindTexture(const QImage &image, GLenum target, GLint format, - QGLContext::BindOptions options); - GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format, - QGLContext::BindOptions options); - - GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D, - GLint format = GL_RGBA); - GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D, - GLint format = GL_RGBA); - - GLuint bindTexture(const QString &fileName); - - void deleteTexture(GLuint tx_id); - - void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - -public Q_SLOTS: - virtual void updateGL(); - virtual void updateOverlayGL(); - -protected: - bool event(QEvent *) override; - virtual void initializeGL(); - virtual void resizeGL(int w, int h); - virtual void paintGL(); - - virtual void initializeOverlayGL(); - virtual void resizeOverlayGL(int w, int h); - virtual void paintOverlayGL(); - - void setAutoBufferSwap(bool on); - bool autoBufferSwap() const; - - void paintEvent(QPaintEvent*) override; - void resizeEvent(QResizeEvent*) override; - - virtual void glInit(); - virtual void glDraw(); - - QGLWidget(QGLWidgetPrivate &dd, - const QGLFormat &format = QGLFormat(), - QWidget *parent = nullptr, - const QGLWidget* shareWidget = nullptr, - Qt::WindowFlags f = Qt::WindowFlags()); -private: - Q_DISABLE_COPY(QGLWidget) - - friend class QGLDrawable; - friend class QGLPixelBuffer; - friend class QGLPixelBufferPrivate; - friend class QGLContext; - friend class QGLContextPrivate; - friend class QGLOverlayWidget; - friend class QGLPaintDevice; - friend class QGLWidgetGLPaintDevice; -}; - - -// -// QGLFormat inline functions -// - -inline bool QGLFormat::doubleBuffer() const -{ - return testOption(QGL::DoubleBuffer); -} - -inline bool QGLFormat::depth() const -{ - return testOption(QGL::DepthBuffer); -} - -inline bool QGLFormat::rgba() const -{ - return testOption(QGL::Rgba); -} - -inline bool QGLFormat::alpha() const -{ - return testOption(QGL::AlphaChannel); -} - -inline bool QGLFormat::accum() const -{ - return testOption(QGL::AccumBuffer); -} - -inline bool QGLFormat::stencil() const -{ - return testOption(QGL::StencilBuffer); -} - -inline bool QGLFormat::stereo() const -{ - return testOption(QGL::StereoBuffers); -} - -inline bool QGLFormat::directRendering() const -{ - return testOption(QGL::DirectRendering); -} - -inline bool QGLFormat::hasOverlay() const -{ - return testOption(QGL::HasOverlay); -} - -inline bool QGLFormat::sampleBuffers() const -{ - return testOption(QGL::SampleBuffers); -} - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL -#endif // QGL_H diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h deleted file mode 100644 index 4e52c0f5e9..0000000000 --- a/src/opengl/qgl_p.h +++ /dev/null @@ -1,556 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGL_P_H -#define QGL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of the QGLWidget class. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "QtOpenGL/qgl.h" -#include "QtOpenGL/qglcolormap.h" -#include "QtCore/qmap.h" -#include "QtCore/qthread.h" -#include "QtCore/qthreadstorage.h" -#include "QtCore/qhashfunctions.h" -#include "QtCore/qatomic.h" -#include "QtWidgets/private/qwidget_p.h" -#include "QtGui/private/qopenglcontext_p.h" -#include "QtGui/private/qopenglextensions_p.h" -#include "qcache.h" -#include "qglpaintdevice_p.h" - -#include <QtGui/QOpenGLContext> - -QT_BEGIN_NAMESPACE - -class QGLContext; -class QGLOverlayWidget; -class QPixmap; -class QOpenGLExtensions; - -class QGLFormatPrivate -{ -public: - QGLFormatPrivate() - : ref(1) - { - opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering - | QGL::StencilBuffer | QGL::DeprecatedFunctions; - pln = 0; - depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1; - numSamples = -1; - swapInterval = -1; - majorVersion = 2; - minorVersion = 0; - profile = QGLFormat::NoProfile; - } - QGLFormatPrivate(const QGLFormatPrivate *other) - : ref(1), - opts(other->opts), - pln(other->pln), - depthSize(other->depthSize), - accumSize(other->accumSize), - stencilSize(other->stencilSize), - redSize(other->redSize), - greenSize(other->greenSize), - blueSize(other->blueSize), - alphaSize(other->alphaSize), - numSamples(other->numSamples), - swapInterval(other->swapInterval), - majorVersion(other->majorVersion), - minorVersion(other->minorVersion), - profile(other->profile) - { - } - QAtomicInt ref; - QGL::FormatOptions opts; - int pln; - int depthSize; - int accumSize; - int stencilSize; - int redSize; - int greenSize; - int blueSize; - int alphaSize; - int numSamples; - int swapInterval; - int majorVersion; - int minorVersion; - QGLFormat::OpenGLContextProfile profile; -}; - -class Q_OPENGL_EXPORT QGLWidgetPrivate : public QWidgetPrivate -{ - Q_DECLARE_PUBLIC(QGLWidget) -public: - QGLWidgetPrivate() : QWidgetPrivate() - , disable_clear_on_painter_begin(false) - , parent_changing(false) - { - } - - ~QGLWidgetPrivate() {} - - void init(QGLContext *context, const QGLWidget* shareWidget); - void initContext(QGLContext *context, const QGLWidget* shareWidget); - bool renderCxPm(QPixmap *pixmap); - void cleanupColormaps(); - void aboutToDestroy() override { - if (glcx && !parent_changing) - glcx->reset(); - } - - bool makeCurrent(); - - QGLContext *glcx; - QGLWidgetGLPaintDevice glDevice; - bool autoSwap; - - QGLColormap cmap; - - bool disable_clear_on_painter_begin; - bool parent_changing; -}; - -// QGLContextPrivate has the responsibility of creating context groups. -// QGLContextPrivate maintains the reference counter and destroys -// context groups when needed. -class QGLContextGroup -{ -public: - ~QGLContextGroup(); - - const QGLContext *context() const {return m_context;} - bool isSharing() const { return m_shares.size() >= 2; } - QList<const QGLContext *> shares() const { return m_shares; } - - static void addShare(const QGLContext *context, const QGLContext *share); - static void removeShare(const QGLContext *context); - -private: - QGLContextGroup(const QGLContext *context); - - const QGLContext *m_context; // context group's representative - QList<const QGLContext *> m_shares; - QAtomicInt m_refs; - - friend class QGLContext; - friend class QGLContextPrivate; - friend class QGLContextGroupResourceBase; -}; - -// Get the context that resources for "ctx" will transfer to once -// "ctx" is destroyed. Returns null if nothing is sharing with ctx. -const QGLContext *qt_gl_transfer_context(const QGLContext *); - -/* - QGLTemporaryContext - the main objective of this class is to have a way of - creating a GL context and making it current, without going via QGLWidget - and friends. At certain points during GL initialization we need a current - context in order decide what GL features are available, and to resolve GL - extensions. Having a light-weight way of creating such a context saves - initial application startup time, and it doesn't wind up creating recursive - conflicts. - The class currently uses a private d pointer to hide the platform specific - types. This could possibly been done inline with #ifdef'ery, but it causes - major headaches on e.g. X11 due to namespace pollution. -*/ -class QGLTemporaryContextPrivate; -class QGLTemporaryContext { -public: - explicit QGLTemporaryContext(bool directRendering = true, QWidget *parent = nullptr); - ~QGLTemporaryContext(); - -private: - QScopedPointer<QGLTemporaryContextPrivate> d; -}; - -class QGLTexture; -class QGLTextureDestroyer; - -// This probably needs to grow to GL_MAX_VERTEX_ATTRIBS, but 3 is ok for now as that's -// all the GL2 engine uses: -#define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3 - -class QGLContextResourceBase; - -class QGLContextPrivate -{ - Q_DECLARE_PUBLIC(QGLContext) -public: - explicit QGLContextPrivate(QGLContext *context); - ~QGLContextPrivate(); - QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, - QGLContext::BindOptions options); - QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key, - QGLContext::BindOptions options); - QGLTexture *bindTexture(const QPixmap &pixmap, GLenum target, GLint format, - QGLContext::BindOptions options); - QGLTexture *textureCacheLookup(const qint64 key, GLenum target); - void init(QPaintDevice *dev, const QGLFormat &format); - int maxTextureSize(); - - void cleanup(); - - void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true); - void syncGlState(); // Makes sure the GL context's state is what we think it is - void swapRegion(const QRegion ®ion); - - QOpenGLContext *guiGlContext; - // true if QGLContext owns the QOpenGLContext (for who deletes who) - bool ownContext; - - void setupSharing(); - void refreshCurrentFbo(); - void setCurrentFbo(GLuint fbo); - - QGLFormat glFormat; - QGLFormat reqFormat; - GLuint fbo; - - uint valid : 1; - uint sharing : 1; - uint initDone : 1; - uint crWin : 1; - uint internal_context : 1; - uint version_flags_cached : 1; - - // workarounds for driver/hw bugs on different platforms - uint workaround_needsFullClearOnEveryFrame : 1; - uint workaround_brokenFBOReadBack : 1; - uint workaround_brokenTexSubImage : 1; - uint workaroundsCached : 1; - - uint workaround_brokenTextureFromPixmap : 1; - uint workaround_brokenTextureFromPixmap_init : 1; - - uint workaround_brokenAlphaTexSubImage : 1; - uint workaround_brokenAlphaTexSubImage_init : 1; - - QPaintDevice *paintDevice; - QSize readback_target_size; - QColor transpColor; - QGLContext *q_ptr; - QGLFormat::OpenGLVersionFlags version_flags; - - QGLContextGroup *group; - GLint max_texture_size; - - GLuint current_fbo; - GLuint default_fbo; - QPaintEngine *active_engine; - QGLTextureDestroyer *texture_destroyer; - - QGLFunctions *functions; - - bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT]; - - static inline QGLContextGroup *contextGroup(const QGLContext *ctx) { return ctx->d_ptr->group; } - - static void setCurrentContext(QGLContext *context); -}; - -// Temporarily make a context current if not already current or -// shared with the current contex. The previous context is made -// current when the object goes out of scope. -class Q_OPENGL_EXPORT QGLShareContextScope -{ -public: - QGLShareContextScope(const QGLContext *ctx) - : m_oldContext(nullptr) - { - QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (currentContext != ctx && !QGLContext::areSharing(ctx, currentContext)) { - m_oldContext = currentContext; - m_ctx = const_cast<QGLContext *>(ctx); - m_ctx->makeCurrent(); - } else { - m_ctx = currentContext; - } - } - - operator QGLContext *() - { - return m_ctx; - } - - QGLContext *operator->() - { - return m_ctx; - } - - ~QGLShareContextScope() - { - if (m_oldContext) - m_oldContext->makeCurrent(); - } - -private: - QGLContext *m_oldContext; - QGLContext *m_ctx; -}; - -QT_END_NAMESPACE -Q_DECLARE_METATYPE(GLuint) -QT_BEGIN_NAMESPACE - -class Q_OPENGL_EXPORT QGLTextureDestroyer -{ -public: - void emitFreeTexture(QGLContext *context, QPlatformPixmap *, GLuint id) { - if (context->contextHandle()) - (new QOpenGLSharedResourceGuard(context->contextHandle(), id, freeTextureFunc))->free(); - } - -private: - static void freeTextureFunc(QOpenGLFunctions *, GLuint id) { - QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &id); - } -}; - -class Q_OPENGL_EXPORT QGLSignalProxy : public QObject -{ - Q_OBJECT -public: - void emitAboutToDestroyContext(const QGLContext *context) { - emit aboutToDestroyContext(context); - } - static QGLSignalProxy *instance(); -Q_SIGNALS: - void aboutToDestroyContext(const QGLContext *context); -}; - -class QGLTexture { -public: - explicit QGLTexture(QGLContext *ctx = nullptr, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D, - QGLContext::BindOptions opt = QGLContext::DefaultBindOption) - : context(ctx), - id(tx_id), - target(tx_target), - options(opt) - {} - - ~QGLTexture() { - if (options & QGLContext::MemoryManagedBindOption) { - Q_ASSERT(context); - QPlatformPixmap *boundPixmap = nullptr; - context->d_ptr->texture_destroyer->emitFreeTexture(context, boundPixmap, id); - } - } - - QGLContext *context; - GLuint id; - GLenum target; - - QGLContext::BindOptions options; - - bool canBindCompressedTexture - (const char *buf, int len, const char *format, bool *hasAlpha); - QSize bindCompressedTexture - (const QString& fileName, const char *format = nullptr); - QSize bindCompressedTexture - (const char *buf, int len, const char *format = nullptr); - QSize bindCompressedTextureDDS(const char *buf, int len); - QSize bindCompressedTexturePVR(const char *buf, int len); -}; - -struct QGLTextureCacheKey { - qint64 key; - QGLContextGroup *group; -}; - -inline bool operator==(const QGLTextureCacheKey &a, const QGLTextureCacheKey &b) -{ - return a.key == b.key && a.group == b.group; -} - -inline uint qHash(const QGLTextureCacheKey &key) -{ - return qHash(key.key) ^ qHash(key.group); -} - - -class Q_AUTOTEST_EXPORT QGLTextureCache { -public: - QGLTextureCache(); - ~QGLTextureCache(); - - void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost); - void remove(qint64 key); - inline int size(); - inline void setMaxCost(int newMax); - inline int maxCost(); - inline QGLTexture* getTexture(QGLContext *ctx, qint64 key); - - bool remove(QGLContext *ctx, GLuint textureId); - void removeContextTextures(QGLContext *ctx); - static QGLTextureCache *instance(); - static void cleanupTexturesForCacheKey(qint64 cacheKey); - static void cleanupTexturesForPixampData(QPlatformPixmap* pixmap); - static void cleanupBeforePixmapDestruction(QPlatformPixmap* pixmap); - -private: - QCache<QGLTextureCacheKey, QGLTexture> m_cache; - QReadWriteLock m_lock; -}; - -int QGLTextureCache::size() { - QReadLocker locker(&m_lock); - return m_cache.size(); -} - -void QGLTextureCache::setMaxCost(int newMax) -{ - QWriteLocker locker(&m_lock); - m_cache.setMaxCost(newMax); -} - -int QGLTextureCache::maxCost() -{ - QReadLocker locker(&m_lock); - return m_cache.maxCost(); -} - -QGLTexture* QGLTextureCache::getTexture(QGLContext *ctx, qint64 key) -{ - // Can't be a QReadLocker since QCache::object() modifies the cache (reprioritizes the object) - QWriteLocker locker(&m_lock); - const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)}; - return m_cache.object(cacheKey); -} - -Q_OPENGL_EXPORT extern QPaintEngine* qt_qgl_paint_engine(); - -QOpenGLExtensions* qgl_extensions(); -bool qgl_hasFeature(QOpenGLFunctions::OpenGLFeature feature); -bool qgl_hasExtension(QOpenGLExtensions::OpenGLExtension extension); - -// Put a guard around a GL object identifier and its context. -// When the context goes away, a shared context will be used -// in its place. If there are no more shared contexts, then -// the identifier is returned as zero - it is assumed that the -// context destruction cleaned up the identifier in this case. -class QGLSharedResourceGuardBase : public QOpenGLSharedResource -{ -public: - QGLSharedResourceGuardBase(QGLContext *context, GLuint id) - : QOpenGLSharedResource(context->contextHandle()->shareGroup()) - , m_id(id) - { - } - - GLuint id() const - { - return m_id; - } - -protected: - void invalidateResource() override - { - m_id = 0; - } - - void freeResource(QOpenGLContext *context) override - { - if (m_id) { - freeResource(QGLContext::fromOpenGLContext(context), m_id); - } - } - - virtual void freeResource(QGLContext *ctx, GLuint id) = 0; - -private: - GLuint m_id; -}; - -template <typename Func> -class QGLSharedResourceGuard : public QGLSharedResourceGuardBase -{ -public: - QGLSharedResourceGuard(QGLContext *context, GLuint id, Func func) - : QGLSharedResourceGuardBase(context, id) - , m_func(func) - { - } - -protected: - void freeResource(QGLContext *ctx, GLuint id) override - { - m_func(ctx, id); - } - -private: - Func m_func; -}; - -template <typename Func> -QGLSharedResourceGuardBase *createSharedResourceGuard(QGLContext *context, GLuint id, Func cleanupFunc) -{ - return new QGLSharedResourceGuard<Func>(context, id, cleanupFunc); -} - -// this is a class that wraps a QThreadStorage object for storing -// thread local instances of the GL 1 and GL 2 paint engines - -template <class T> -class QGLEngineThreadStorage -{ -public: - QPaintEngine *engine() { - QPaintEngine *&localEngine = storage.localData(); - if (!localEngine) - localEngine = new T; - return localEngine; - } - -private: - QThreadStorage<QPaintEngine *> storage; -}; -QT_END_NAMESPACE - -#endif // QGL_P_H diff --git a/src/opengl/qglbuffer.cpp b/src/opengl/qglbuffer.cpp deleted file mode 100644 index bb9c2affbd..0000000000 --- a/src/opengl/qglbuffer.cpp +++ /dev/null @@ -1,568 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtOpenGL/qgl.h> -#include <QtOpenGL/private/qgl_p.h> -#include <private/qopenglextensions_p.h> -#include <QtCore/qatomic.h> -#include "qglbuffer.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QGLBuffer - \inmodule QtOpenGL - \brief The QGLBuffer class provides functions for creating and managing GL buffer objects. - \since 4.7 - \obsolete - \ingroup painting-3D - - Buffer objects are created in the GL server so that the - client application can avoid uploading vertices, indices, - texture image data, etc every time they are needed. - - QGLBuffer objects can be copied around as a reference to the - underlying GL buffer object: - - \snippet code/src_opengl_qglbuffer.cpp 0 - - QGLBuffer performs a shallow copy when objects are copied in this - manner, but does not implement copy-on-write semantics. The original - object will be affected whenever the copy is modified. - - \note This class has been deprecated in favor of QOpenGLBuffer. -*/ - -/*! - \enum QGLBuffer::Type - This enum defines the type of GL buffer object to create with QGLBuffer. - - \value VertexBuffer Vertex buffer object for use when specifying - vertex arrays. - \value IndexBuffer Index buffer object for use with \c{glDrawElements()}. - \value PixelPackBuffer Pixel pack buffer object for reading pixel - data from the GL server (for example, with \c{glReadPixels()}). - Not supported under OpenGL/ES. - \value PixelUnpackBuffer Pixel unpack buffer object for writing pixel - data to the GL server (for example, with \c{glTexImage2D()}). - Not supported under OpenGL/ES. -*/ - -/*! - \enum QGLBuffer::UsagePattern - This enum defines the usage pattern of a QGLBuffer object. - - \value StreamDraw The data will be set once and used a few times - for drawing operations. Under OpenGL/ES 1.1 this is identical - to StaticDraw. - \value StreamRead The data will be set once and used a few times - for reading data back from the GL server. Not supported - under OpenGL/ES. - \value StreamCopy The data will be set once and used a few times - for reading data back from the GL server for use in further - drawing operations. Not supported under OpenGL/ES. - \value StaticDraw The data will be set once and used many times - for drawing operations. - \value StaticRead The data will be set once and used many times - for reading data back from the GL server. Not supported - under OpenGL/ES. - \value StaticCopy The data will be set once and used many times - for reading data back from the GL server for use in further - drawing operations. Not supported under OpenGL/ES. - \value DynamicDraw The data will be modified repeatedly and used - many times for drawing operations. - \value DynamicRead The data will be modified repeatedly and used - many times for reading data back from the GL server. - Not supported under OpenGL/ES. - \value DynamicCopy The data will be modified repeatedly and used - many times for reading data back from the GL server for - use in further drawing operations. Not supported under OpenGL/ES. -*/ - -/*! - \enum QGLBuffer::Access - This enum defines the access mode for QGLBuffer::map(). - - \value ReadOnly The buffer will be mapped for reading only. - \value WriteOnly The buffer will be mapped for writing only. - \value ReadWrite The buffer will be mapped for reading and writing. -*/ - -class QGLBufferPrivate -{ -public: - QGLBufferPrivate(QGLBuffer::Type t) - : ref(1), - type(t), - guard(0), - usagePattern(QGLBuffer::StaticDraw), - actualUsagePattern(QGLBuffer::StaticDraw), - funcs(0) - { - } - - QAtomicInt ref; - QGLBuffer::Type type; - QGLSharedResourceGuardBase *guard; - QGLBuffer::UsagePattern usagePattern; - QGLBuffer::UsagePattern actualUsagePattern; - QOpenGLExtensions *funcs; -}; - -/*! - Constructs a new buffer object of type QGLBuffer::VertexBuffer. - - Note: this constructor just creates the QGLBuffer instance. The actual - buffer object in the GL server is not created until create() is called. - - \sa create() -*/ -QGLBuffer::QGLBuffer() - : d_ptr(new QGLBufferPrivate(QGLBuffer::VertexBuffer)) -{ -} - -/*! - Constructs a new buffer object of \a type. - - Note: this constructor just creates the QGLBuffer instance. The actual - buffer object in the GL server is not created until create() is called. - - \sa create() -*/ -QGLBuffer::QGLBuffer(QGLBuffer::Type type) - : d_ptr(new QGLBufferPrivate(type)) -{ -} - -/*! - Constructs a shallow copy of \a other. - - Note: QGLBuffer does not implement copy-on-write semantics, - so \a other will be affected whenever the copy is modified. -*/ -QGLBuffer::QGLBuffer(const QGLBuffer &other) - : d_ptr(other.d_ptr) -{ - d_ptr->ref.ref(); -} - -#define ctx QGLContext::currentContext(); - -/*! - Destroys this buffer object, including the storage being - used in the GL server. -*/ -QGLBuffer::~QGLBuffer() -{ - if (!d_ptr->ref.deref()) { - destroy(); - delete d_ptr; - } -} - -/*! - Assigns a shallow copy of \a other to this object. - - Note: QGLBuffer does not implement copy-on-write semantics, - so \a other will be affected whenever the copy is modified. -*/ -QGLBuffer &QGLBuffer::operator=(const QGLBuffer &other) -{ - if (d_ptr != other.d_ptr) { - other.d_ptr->ref.ref(); - if (!d_ptr->ref.deref()) { - destroy(); - delete d_ptr; - } - d_ptr = other.d_ptr; - } - return *this; -} - -/*! - Returns the type of buffer represented by this object. -*/ -QGLBuffer::Type QGLBuffer::type() const -{ - Q_D(const QGLBuffer); - return d->type; -} - -/*! - Returns the usage pattern for this buffer object. - The default value is StaticDraw. - - \sa setUsagePattern() -*/ -QGLBuffer::UsagePattern QGLBuffer::usagePattern() const -{ - Q_D(const QGLBuffer); - return d->usagePattern; -} - -/*! - Sets the usage pattern for this buffer object to \a value. - This function must be called before allocate() or write(). - - \sa usagePattern(), allocate(), write() -*/ -void QGLBuffer::setUsagePattern(QGLBuffer::UsagePattern value) -{ - Q_D(QGLBuffer); - d->usagePattern = d->actualUsagePattern = value; -} - -#undef ctx - -namespace { - void freeBufferFunc(QGLContext *ctx, GLuint id) - { - Q_ASSERT(ctx); - ctx->contextHandle()->functions()->glDeleteBuffers(1, &id); - } -} - -/*! - Creates the buffer object in the GL server. Returns \c true if - the object was created; false otherwise. - - This function must be called with a current QGLContext. - The buffer will be bound to and can only be used in - that context (or any other context that is shared with it). - - This function will return false if the GL implementation - does not support buffers, or there is no current QGLContext. - - \sa isCreated(), allocate(), write(), destroy() -*/ -bool QGLBuffer::create() -{ - Q_D(QGLBuffer); - if (d->guard && d->guard->id()) - return true; - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - if (ctx) { - delete d->funcs; - d->funcs = new QOpenGLExtensions(ctx->contextHandle()); - if (!d->funcs->hasOpenGLFeature(QOpenGLFunctions::Buffers)) - return false; - - GLuint bufferId = 0; - d->funcs->glGenBuffers(1, &bufferId); - if (bufferId) { - if (d->guard) - d->guard->free(); - - d->guard = createSharedResourceGuard(ctx, bufferId, freeBufferFunc); - return true; - } - } - return false; -} - -#define ctx QGLContext::currentContext() - -/*! - Returns \c true if this buffer has been created; false otherwise. - - \sa create(), destroy() -*/ -bool QGLBuffer::isCreated() const -{ - Q_D(const QGLBuffer); - return d->guard && d->guard->id(); -} - -/*! - Destroys this buffer object, including the storage being - used in the GL server. All references to the buffer will - become invalid. -*/ -void QGLBuffer::destroy() -{ - Q_D(QGLBuffer); - if (d->guard) { - d->guard->free(); - d->guard = 0; - } -} - -/*! - Reads the \a count bytes in this buffer starting at \a offset - into \a data. Returns \c true on success; false if reading from - the buffer is not supported. Buffer reading is not supported - under OpenGL/ES. - - It is assumed that this buffer has been bound to the current context. - - \sa write(), bind() -*/ -bool QGLBuffer::read(int offset, void *data, int count) -{ -#if !defined(QT_OPENGL_ES) - Q_D(QGLBuffer); - if (!d->funcs->hasOpenGLFeature(QOpenGLFunctions::Buffers) || !d->guard->id() || !d->funcs->d()->GetBufferSubData) - return false; - while (d->funcs->glGetError() != GL_NO_ERROR) ; // Clear error state. - d->funcs->glGetBufferSubData(d->type, offset, count, data); - return d->funcs->glGetError() == GL_NO_ERROR; -#else - Q_UNUSED(offset); - Q_UNUSED(data); - Q_UNUSED(count); - return false; -#endif -} - -/*! - Replaces the \a count bytes of this buffer starting at \a offset - with the contents of \a data. Any other bytes in the buffer - will be left unmodified. - - It is assumed that create() has been called on this buffer and that - it has been bound to the current context. - - \sa create(), read(), allocate() -*/ -void QGLBuffer::write(int offset, const void *data, int count) -{ -#ifndef QT_NO_DEBUG - if (!isCreated()) - qWarning("QGLBuffer::allocate(): buffer not created"); -#endif - Q_D(QGLBuffer); - if (d->guard && d->guard->id()) - d->funcs->glBufferSubData(d->type, offset, count, data); -} - -/*! - Allocates \a count bytes of space to the buffer, initialized to - the contents of \a data. Any previous contents will be removed. - - It is assumed that create() has been called on this buffer and that - it has been bound to the current context. - - \sa create(), read(), write() -*/ -void QGLBuffer::allocate(const void *data, int count) -{ -#ifndef QT_NO_DEBUG - if (!isCreated()) - qWarning("QGLBuffer::allocate(): buffer not created"); -#endif - Q_D(QGLBuffer); - if (d->guard && d->guard->id()) - d->funcs->glBufferData(d->type, count, data, d->actualUsagePattern); -} - -/*! - \fn void QGLBuffer::allocate(int count) - \overload - - Allocates \a count bytes of space to the buffer. Any previous - contents will be removed. - - It is assumed that create() has been called on this buffer and that - it has been bound to the current context. - - \sa create(), write() -*/ - -/*! - Binds the buffer associated with this object to the current - GL context. Returns \c false if binding was not possible, usually because - type() is not supported on this GL implementation. - - The buffer must be bound to the same QGLContext current when create() - was called, or to another QGLContext that is sharing with it. - Otherwise, false will be returned from this function. - - \sa release(), create() -*/ -bool QGLBuffer::bind() -{ -#ifndef QT_NO_DEBUG - if (!isCreated()) - qWarning("QGLBuffer::bind(): buffer not created"); -#endif - Q_D(const QGLBuffer); - GLuint bufferId = d->guard ? d->guard->id() : 0; - if (bufferId) { - if (d->guard->group() != QOpenGLContextGroup::currentContextGroup()) { -#ifndef QT_NO_DEBUG - qWarning("QGLBuffer::bind: buffer is not valid in the current context"); -#endif - return false; - } - d->funcs->glBindBuffer(d->type, bufferId); - return true; - } else { - return false; - } -} - -/*! - Releases the buffer associated with this object from the - current GL context. - - This function must be called with the same QGLContext current - as when bind() was called on the buffer. - - \sa bind() -*/ -void QGLBuffer::release() -{ -#ifndef QT_NO_DEBUG - if (!isCreated()) - qWarning("QGLBuffer::release(): buffer not created"); -#endif - Q_D(const QGLBuffer); - if (d->guard && d->guard->id()) - d->funcs->glBindBuffer(d->type, 0); -} - -#undef ctx - -/*! - Releases the buffer associated with \a type in the current - QGLContext. - - This function is a direct call to \c{glBindBuffer(type, 0)} - for use when the caller does not know which QGLBuffer has - been bound to the context but wants to make sure that it - is released. - - \snippet code/src_opengl_qglbuffer.cpp 1 -*/ -void QGLBuffer::release(QGLBuffer::Type type) -{ - if (QOpenGLContext *ctx = QOpenGLContext::currentContext()) - ctx->functions()->glBindBuffer(GLenum(type), 0); -} - -#define ctx QGLContext::currentContext() - -/*! - Returns the GL identifier associated with this buffer; zero if - the buffer has not been created. - - \sa isCreated() -*/ -GLuint QGLBuffer::bufferId() const -{ - Q_D(const QGLBuffer); - return d->guard ? d->guard->id() : 0; -} - -#ifndef GL_BUFFER_SIZE -#define GL_BUFFER_SIZE 0x8764 -#endif - -/*! - Returns the size of the data in this buffer, for reading operations. - Returns -1 if fetching the buffer size is not supported, or the - buffer has not been created. - - It is assumed that this buffer has been bound to the current context. - - \sa isCreated(), bind() -*/ -int QGLBuffer::size() const -{ - Q_D(const QGLBuffer); - if (!d->guard || !d->guard->id()) - return -1; - GLint value = -1; - d->funcs->glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value); - return value; -} - -/*! - Maps the contents of this buffer into the application's memory - space and returns a pointer to it. Returns null if memory - mapping is not possible. The \a access parameter indicates the - type of access to be performed. - - It is assumed that create() has been called on this buffer and that - it has been bound to the current context. - - This function is only supported under OpenGL/ES if the - \c{GL_OES_mapbuffer} extension is present. - - \sa unmap(), create(), bind() -*/ -void *QGLBuffer::map(QGLBuffer::Access access) -{ - Q_D(QGLBuffer); -#ifndef QT_NO_DEBUG - if (!isCreated()) - qWarning("QGLBuffer::map(): buffer not created"); -#endif - if (!d->guard || !d->guard->id()) - return 0; - return d->funcs->glMapBuffer(d->type, access); -} - -/*! - Unmaps the buffer after it was mapped into the application's - memory space with a previous call to map(). Returns \c true if - the unmap succeeded; false otherwise. - - It is assumed that this buffer has been bound to the current context, - and that it was previously mapped with map(). - - This function is only supported under OpenGL/ES if the - \c{GL_OES_mapbuffer} extension is present. - - \sa map() -*/ -bool QGLBuffer::unmap() -{ - Q_D(QGLBuffer); -#ifndef QT_NO_DEBUG - if (!isCreated()) - qWarning("QGLBuffer::unmap(): buffer not created"); -#endif - if (!d->guard || !d->guard->id()) - return false; - return d->funcs->glUnmapBuffer(d->type) == GL_TRUE; -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglbuffer.h b/src/opengl/qglbuffer.h deleted file mode 100644 index daf5227c66..0000000000 --- a/src/opengl/qglbuffer.h +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLBUFFER_H -#define QGLBUFFER_H - -#include <QtCore/qscopedpointer.h> -#include <QtOpenGL/qgl.h> - -QT_BEGIN_NAMESPACE - - -class QGLBufferPrivate; - -class Q_OPENGL_EXPORT QGLBuffer -{ -public: - enum Type - { - VertexBuffer = 0x8892, // GL_ARRAY_BUFFER - IndexBuffer = 0x8893, // GL_ELEMENT_ARRAY_BUFFER - PixelPackBuffer = 0x88EB, // GL_PIXEL_PACK_BUFFER - PixelUnpackBuffer = 0x88EC // GL_PIXEL_UNPACK_BUFFER - }; - - QGLBuffer(); - explicit QGLBuffer(QGLBuffer::Type type); - QGLBuffer(const QGLBuffer &other); - ~QGLBuffer(); - - QGLBuffer &operator=(const QGLBuffer &other); - - enum UsagePattern - { - StreamDraw = 0x88E0, // GL_STREAM_DRAW - StreamRead = 0x88E1, // GL_STREAM_READ - StreamCopy = 0x88E2, // GL_STREAM_COPY - StaticDraw = 0x88E4, // GL_STATIC_DRAW - StaticRead = 0x88E5, // GL_STATIC_READ - StaticCopy = 0x88E6, // GL_STATIC_COPY - DynamicDraw = 0x88E8, // GL_DYNAMIC_DRAW - DynamicRead = 0x88E9, // GL_DYNAMIC_READ - DynamicCopy = 0x88EA // GL_DYNAMIC_COPY - }; - - enum Access - { - ReadOnly = 0x88B8, // GL_READ_ONLY - WriteOnly = 0x88B9, // GL_WRITE_ONLY - ReadWrite = 0x88BA // GL_READ_WRITE - }; - - QGLBuffer::Type type() const; - - QGLBuffer::UsagePattern usagePattern() const; - void setUsagePattern(QGLBuffer::UsagePattern value); - - bool create(); - bool isCreated() const; - - void destroy(); - - bool bind(); - void release(); - - static void release(QGLBuffer::Type type); - - GLuint bufferId() const; - - int size() const; - - bool read(int offset, void *data, int count); - void write(int offset, const void *data, int count); - - void allocate(const void *data, int count); - inline void allocate(int count) { allocate(nullptr, count); } - - void *map(QGLBuffer::Access access); - bool unmap(); - -private: - QGLBufferPrivate *d_ptr; - - Q_DECLARE_PRIVATE(QGLBuffer) -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/opengl/qglcolormap.cpp b/src/opengl/qglcolormap.cpp deleted file mode 100644 index f314a9715d..0000000000 --- a/src/opengl/qglcolormap.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \class QGLColormap - \brief The QGLColormap class is used for installing custom colormaps into - a QGLWidget. - - \obsolete - \inmodule QtOpenGL - \ingroup painting-3D - \ingroup shared - - QGLColormap provides a platform independent way of specifying and - installing indexed colormaps for a QGLWidget. QGLColormap is - especially useful when using the OpenGL color-index mode. - - Under X11 you must use an X server that supports either a \c - PseudoColor or \c DirectColor visual class. If your X server - currently only provides a \c GrayScale, \c TrueColor, \c - StaticColor or \c StaticGray visual, you will not be able to - allocate colorcells for writing. If this is the case, try setting - your X server to 8 bit mode. It should then provide you with at - least a \c PseudoColor visual. Note that you may experience - colormap flashing if your X server is running in 8 bit mode. - - The size() of the colormap is always set to 256 - colors. Note that under Windows you can also install colormaps - in child widgets. - - This class uses \l{implicit sharing} as a memory and speed - optimization. - - Example of use: - \snippet code/src_opengl_qglcolormap.cpp 0 - - \sa QGLWidget::setColormap(), QGLWidget::colormap() -*/ - -/*! - \fn Qt::HANDLE QGLColormap::handle() - - \internal - - Returns the handle for this color map. -*/ - -/*! - \fn void QGLColormap::setHandle(Qt::HANDLE handle) - - \internal - - Sets the handle for this color map to \a handle. -*/ - -#include "qglcolormap.h" - -QT_BEGIN_NAMESPACE - -QGLColormap::QGLColormapData QGLColormap::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 }; - -/*! - Construct a QGLColormap. -*/ -QGLColormap::QGLColormap() - : d(&shared_null) -{ - d->ref.ref(); -} - - -/*! - Construct a shallow copy of \a map. -*/ -QGLColormap::QGLColormap(const QGLColormap &map) - : d(map.d) -{ - d->ref.ref(); -} - -/*! - Dereferences the QGLColormap and deletes it if this was the last - reference to it. -*/ -QGLColormap::~QGLColormap() -{ - if (!d->ref.deref()) - cleanup(d); -} - -void QGLColormap::cleanup(QGLColormap::QGLColormapData *x) -{ - delete x->cells; - x->cells = 0; - delete x; -} - -/*! - Assign a shallow copy of \a map to this QGLColormap. -*/ -QGLColormap & QGLColormap::operator=(const QGLColormap &map) -{ - map.d->ref.ref(); - if (!d->ref.deref()) - cleanup(d); - d = map.d; - return *this; -} - -/*! - \fn void QGLColormap::detach() - \internal - - Detaches this QGLColormap from the shared block. -*/ - -void QGLColormap::detach_helper() -{ - QGLColormapData *x = new QGLColormapData; - x->ref.storeRelaxed(1); - x->cmapHandle = 0; - x->cells = 0; - if (d->cells) { - x->cells = new QVector<QRgb>(256); - *x->cells = *d->cells; - } - if (!d->ref.deref()) - cleanup(d); - d = x; -} - -/*! - Set cell at index \a idx in the colormap to color \a color. -*/ -void QGLColormap::setEntry(int idx, QRgb color) -{ - detach(); - if (!d->cells) - d->cells = new QVector<QRgb>(256); - d->cells->replace(idx, color); -} - -/*! - Set an array of cells in this colormap. \a count is the number of - colors that should be set, \a colors is the array of colors, and - \a base is the starting index. The first element in \a colors - is set at \a base in the colormap. -*/ -void QGLColormap::setEntries(int count, const QRgb *colors, int base) -{ - detach(); - if (!d->cells) - d->cells = new QVector<QRgb>(256); - - Q_ASSERT_X(colors && base >= 0 && (base + count) <= d->cells->size(), "QGLColormap::setEntries", - "preconditions not met"); - for (int i = 0; i < count; ++i) - setEntry(base + i, colors[i]); -} - -/*! - Returns the QRgb value in the colorcell with index \a idx. -*/ -QRgb QGLColormap::entryRgb(int idx) const -{ - if (d == &shared_null || !d->cells) - return 0; - else - return d->cells->at(idx); -} - -/*! - \overload - - Set the cell with index \a idx in the colormap to color \a color. -*/ -void QGLColormap::setEntry(int idx, const QColor &color) -{ - setEntry(idx, color.rgb()); -} - -/*! - Returns the QRgb value in the colorcell with index \a idx. -*/ -QColor QGLColormap::entryColor(int idx) const -{ - if (d == &shared_null || !d->cells) - return QColor(); - else - return QColor(d->cells->at(idx)); -} - -/*! - Returns \c true if the colormap is empty or it is not in use - by a QGLWidget; otherwise returns \c false. - - A colormap with no color values set is considered to be empty. - For historical reasons, a colormap that has color values set - but which is not in use by a QGLWidget is also considered empty. - - Compare size() with zero to determine if the colormap is empty - regardless of whether it is in use by a QGLWidget or not. - - \sa size() -*/ -bool QGLColormap::isEmpty() const -{ - return d == &shared_null || d->cells == 0 || d->cells->size() == 0 || d->cmapHandle == 0; -} - - -/*! - Returns the number of colorcells in the colormap. -*/ -int QGLColormap::size() const -{ - return d->cells ? d->cells->size() : 0; -} - -/*! - Returns the index of the color \a color. If \a color is not in the - map, -1 is returned. -*/ -int QGLColormap::find(QRgb color) const -{ - if (d->cells) - return d->cells->indexOf(color); - return -1; -} - -/*! - Returns the index of the color that is the closest match to color - \a color. -*/ -int QGLColormap::findNearest(QRgb color) const -{ - int idx = find(color); - if (idx >= 0) - return idx; - int mapSize = size(); - int mindist = 200000; - int r = qRed(color); - int g = qGreen(color); - int b = qBlue(color); - int rx, gx, bx, dist; - for (int i = 0; i < mapSize; ++i) { - QRgb ci = d->cells->at(i); - rx = r - qRed(ci); - gx = g - qGreen(ci); - bx = b - qBlue(ci); - dist = rx * rx + gx * gx + bx * bx; // calculate distance - if (dist < mindist) { // minimal? - mindist = dist; - idx = i; - } - } - return idx; -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp deleted file mode 100644 index 362b3ef189..0000000000 --- a/src/opengl/qglframebufferobject.cpp +++ /dev/null @@ -1,1466 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qglframebufferobject.h" -#include "qglframebufferobject_p.h" - -#include <qdebug.h> -#include <private/qgl_p.h> -#include <private/qfont_p.h> -#include "gl2paintengineex/qpaintengineex_opengl2_p.h" - -#include <qimage.h> -#include <qwindow.h> - -QT_BEGIN_NAMESPACE - -extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool); - -#define QGL_FUNC_CONTEXT const QGLContext *ctx = QGLContext::currentContext(); -#define QGL_FUNCP_CONTEXT const QGLContext *ctx = QGLContext::currentContext(); - -#ifndef QT_NO_DEBUG -#define QT_RESET_GLERROR() \ -{ \ - while (QOpenGLContext::currentContext()->functions()->glGetError() != GL_NO_ERROR) {} \ -} -#define QT_CHECK_GLERROR() \ -{ \ - GLenum err = QOpenGLContext::currentContext()->functions()->glGetError(); \ - if (err != GL_NO_ERROR) { \ - qDebug("[%s line %d] GL Error: %d", \ - __FILE__, __LINE__, (int)err); \ - } \ -} -#else -#define QT_RESET_GLERROR() {} -#define QT_CHECK_GLERROR() {} -#endif - -// ####TODO Properly #ifdef this class to use #define symbols actually defined -// by OpenGL/ES includes -#ifndef GL_MAX_SAMPLES -#define GL_MAX_SAMPLES 0x8D57 -#endif - -#ifndef GL_RENDERBUFFER_SAMPLES -#define GL_RENDERBUFFER_SAMPLES 0x8CAB -#endif - -#ifndef GL_DEPTH24_STENCIL8 -#define GL_DEPTH24_STENCIL8 0x88F0 -#endif - -#ifndef GL_DEPTH_COMPONENT24 -#define GL_DEPTH_COMPONENT24 0x81A6 -#endif - -#ifndef GL_DEPTH_COMPONENT24_OES -#define GL_DEPTH_COMPONENT24_OES 0x81A6 -#endif - -#ifndef GL_READ_FRAMEBUFFER -#define GL_READ_FRAMEBUFFER 0x8CA8 -#endif - -#ifndef GL_DRAW_FRAMEBUFFER -#define GL_DRAW_FRAMEBUFFER 0x8CA9 -#endif - -#ifndef GL_DEPTH_STENCIL_ATTACHMENT -#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A -#endif - -#ifndef GL_DEPTH_STENCIL -#define GL_DEPTH_STENCIL 0x84F9 -#endif - -/*! - \class QGLFramebufferObjectFormat - \inmodule QtOpenGL - \brief The QGLFramebufferObjectFormat class specifies the format of an OpenGL - framebuffer object. - - \since 4.6 - \obsolete - - \ingroup painting-3D - - A framebuffer object has several characteristics: - \list - \li \l{setSamples()}{Number of samples per pixels.} - \li \l{setAttachment()}{Depth and/or stencil attachments.} - \li \l{setTextureTarget()}{Texture target.} - \li \l{setInternalTextureFormat()}{Internal texture format.} - \endlist - - Note that the desired attachments or number of samples per pixels might not - be supported by the hardware driver. Call QGLFramebufferObject::format() - after creating a QGLFramebufferObject to find the exact format that was - used to create the frame buffer object. - - \note This class has been deprecated in favor of QOpenGLFramebufferObjectFormat. - - \sa QGLFramebufferObject -*/ - -/*! - \internal -*/ -void QGLFramebufferObjectFormat::detach() -{ - if (d->ref.loadRelaxed() != 1) { - QGLFramebufferObjectFormatPrivate *newd - = new QGLFramebufferObjectFormatPrivate(d); - if (!d->ref.deref()) - delete d; - d = newd; - } -} - -/*! - Creates a QGLFramebufferObjectFormat object for specifying - the format of an OpenGL framebuffer object. - - By default the format specifies a non-multisample framebuffer object with no - attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8. - On OpenGL/ES systems, the default internal format is \c GL_RGBA. - - \sa samples(), attachment(), internalTextureFormat() -*/ - -QGLFramebufferObjectFormat::QGLFramebufferObjectFormat() -{ - d = new QGLFramebufferObjectFormatPrivate; -} - -/*! - Constructs a copy of \a other. -*/ - -QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other) -{ - d = other.d; - d->ref.ref(); -} - -/*! - Assigns \a other to this object. -*/ - -QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other) -{ - if (d != other.d) { - other.d->ref.ref(); - if (!d->ref.deref()) - delete d; - d = other.d; - } - return *this; -} - -/*! - Destroys the QGLFramebufferObjectFormat. -*/ -QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat() -{ - if (!d->ref.deref()) - delete d; -} - -/*! - Sets the number of samples per pixel for a multisample framebuffer object - to \a samples. The default sample count of 0 represents a regular - non-multisample framebuffer object. - - If the desired amount of samples per pixel is not supported by the hardware - then the maximum number of samples per pixel will be used. Note that - multisample framebuffer objects cannot be bound as textures. Also, the - \c{GL_EXT_framebuffer_multisample} extension is required to create a - framebuffer with more than one sample per pixel. - - \sa samples() -*/ -void QGLFramebufferObjectFormat::setSamples(int samples) -{ - detach(); - d->samples = samples; -} - -/*! - Returns the number of samples per pixel if a framebuffer object - is a multisample framebuffer object. Otherwise, returns 0. - The default value is 0. - - \sa setSamples() -*/ -int QGLFramebufferObjectFormat::samples() const -{ - return d->samples; -} - -/*! - \since 4.8 - - Enables mipmapping if \a enabled is true; otherwise disables it. - - Mipmapping is disabled by default. - - If mipmapping is enabled, additional memory will be allocated for - the mipmap levels. The mipmap levels can be updated by binding the - texture and calling glGenerateMipmap(). Mipmapping cannot be enabled - for multisampled framebuffer objects. - - \sa mipmap(), QGLFramebufferObject::texture() -*/ -void QGLFramebufferObjectFormat::setMipmap(bool enabled) -{ - detach(); - d->mipmap = enabled; -} - -/*! - \since 4.8 - - Returns \c true if mipmapping is enabled. - - \sa setMipmap() -*/ -bool QGLFramebufferObjectFormat::mipmap() const -{ - return d->mipmap; -} - -/*! - Sets the attachment configuration of a framebuffer object to \a attachment. - - \sa attachment() -*/ -void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment) -{ - detach(); - d->attachment = attachment; -} - -/*! - Returns the configuration of the depth and stencil buffers attached to - a framebuffer object. The default is QGLFramebufferObject::NoAttachment. - - \sa setAttachment() -*/ -QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::attachment() const -{ - return d->attachment; -} - -/*! - Sets the texture target of the texture attached to a framebuffer object to - \a target. Ignored for multisample framebuffer objects. - - \sa textureTarget(), samples() -*/ -void QGLFramebufferObjectFormat::setTextureTarget(GLenum target) -{ - detach(); - d->target = target; -} - -/*! - Returns the texture target of the texture attached to a framebuffer object. - Ignored for multisample framebuffer objects. The default is - \c GL_TEXTURE_2D. - - \sa setTextureTarget(), samples() -*/ -GLenum QGLFramebufferObjectFormat::textureTarget() const -{ - return d->target; -} - -/*! - Sets the internal format of a framebuffer object's texture or - multisample framebuffer object's color buffer to - \a internalTextureFormat. - - \sa internalTextureFormat() -*/ -void QGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat) -{ - detach(); - d->internal_format = internalTextureFormat; -} - -/*! - Returns the internal format of a framebuffer object's texture or - multisample framebuffer object's color buffer. The default is - \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on - OpenGL/ES systems. - - \sa setInternalTextureFormat() -*/ -GLenum QGLFramebufferObjectFormat::internalTextureFormat() const -{ - return d->internal_format; -} - -/*! - Returns \c true if all the options of this framebuffer object format - are the same as \a other; otherwise returns \c false. -*/ -bool QGLFramebufferObjectFormat::operator==(const QGLFramebufferObjectFormat& other) const -{ - if (d == other.d) - return true; - else - return d->equals(other.d); -} - -/*! - Returns \c false if all the options of this framebuffer object format - are the same as \a other; otherwise returns \c true. -*/ -bool QGLFramebufferObjectFormat::operator!=(const QGLFramebufferObjectFormat& other) const -{ - return !(*this == other); -} - -void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f, - QGLFramebufferObject::Attachment attachment) -{ - fbo = f; - m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed - - // The context that the fbo was created in may not have depth - // and stencil buffers, but the fbo itself might. - fboFormat = QGLContext::currentContext()->format(); - if (attachment == QGLFramebufferObject::CombinedDepthStencil) { - fboFormat.setDepth(true); - fboFormat.setStencil(true); - } else if (attachment == QGLFramebufferObject::Depth) { - fboFormat.setDepth(true); - fboFormat.setStencil(false); - } else { - fboFormat.setDepth(false); - fboFormat.setStencil(false); - } - - GLenum format = f->format().internalTextureFormat(); - reqAlpha = (format != GL_RGB -#ifdef GL_RGB5 - && format != GL_RGB5 -#endif -#ifdef GL_RGB8 - && format != GL_RGB8 -#endif - ); -} - -QGLContext *QGLFBOGLPaintDevice::context() const -{ - return const_cast<QGLContext *>(QGLContext::currentContext()); -} - -bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const -{ - QGL_FUNCP_CONTEXT; - if (!ctx) - return false; // Context no longer exists. - GLenum status = ctx->contextHandle()->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER); - switch(status) { - case GL_NO_ERROR: - case GL_FRAMEBUFFER_COMPLETE: - return true; - case GL_FRAMEBUFFER_UNSUPPORTED: - qDebug("QGLFramebufferObject: Unsupported framebuffer format."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - qDebug("QGLFramebufferObject: Framebuffer incomplete attachment."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - qDebug("QGLFramebufferObject: Framebuffer incomplete, missing attachment."); - break; -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT - case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT: - qDebug("QGLFramebufferObject: Framebuffer incomplete, duplicate attachment."); - break; -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions."); - break; -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same format."); - break; -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - qDebug("QGLFramebufferObject: Framebuffer incomplete, missing draw buffer."); - break; -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - qDebug("QGLFramebufferObject: Framebuffer incomplete, missing read buffer."); - break; -#endif -#ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - qDebug("QGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel."); - break; -#endif - default: - qDebug() <<"QGLFramebufferObject: An undefined error has occurred: "<< status; - break; - } - return false; -} - -namespace -{ - void freeFramebufferFunc(QGLContext *ctx, GLuint id) - { - Q_ASSERT(ctx); - ctx->contextHandle()->functions()->glDeleteFramebuffers(1, &id); - } - - void freeRenderbufferFunc(QGLContext *ctx, GLuint id) - { - Q_ASSERT(ctx); - ctx->contextHandle()->functions()->glDeleteRenderbuffers(1, &id); - } - - void freeTextureFunc(QGLContext *ctx, GLuint id) - { - Q_UNUSED(ctx); - ctx->contextHandle()->functions()->glDeleteTextures(1, &id); - } -} - -void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, - QGLFramebufferObject::Attachment attachment, - GLenum texture_target, GLenum internal_format, - GLint samples, bool mipmap) -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - - funcs.initializeOpenGLFunctions(); - - if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) - return; - - ctx->d_ptr->refreshCurrentFbo(); - - size = sz; - target = texture_target; - // texture dimensions - - QT_RESET_GLERROR(); // reset error state - GLuint fbo = 0; - funcs.glGenFramebuffers(1, &fbo); - funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo); - - GLuint texture = 0; - GLuint color_buffer = 0; - GLuint depth_buffer = 0; - GLuint stencil_buffer = 0; - - QT_CHECK_GLERROR(); - // init texture - if (samples == 0) { - funcs.glGenTextures(1, &texture); - funcs.glBindTexture(target, texture); - funcs.glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - if (mipmap) { - int width = size.width(); - int height = size.height(); - int level = 0; - while (width > 1 || height > 1) { - width = qMax(1, width >> 1); - height = qMax(1, height >> 1); - ++level; - funcs.glTexImage2D(target, level, internal_format, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - } - } - funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - target, texture, 0); - - QT_CHECK_GLERROR(); - valid = checkFramebufferStatus(); - funcs.glBindTexture(target, 0); - - color_buffer = 0; - } else { - mipmap = false; - GLint maxSamples; - funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); - - samples = qBound(0, int(samples), int(maxSamples)); - - funcs.glGenRenderbuffers(1, &color_buffer); - funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer); - if (funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) && samples > 0) { - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - internal_format, size.width(), size.height()); - } else { - samples = 0; - funcs.glRenderbufferStorage(GL_RENDERBUFFER, internal_format, - size.width(), size.height()); - } - - funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, color_buffer); - - QT_CHECK_GLERROR(); - valid = checkFramebufferStatus(); - - if (valid) - funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); - } - - // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a - // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer - // might not be supported while separate buffers are, according to QTBUG-12861. - - if (attachment == QGLFramebufferObject::CombinedDepthStencil - && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil)) { - // depth and stencil buffer needs another extension - funcs.glGenRenderbuffers(1, &depth_buffer); - funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); - Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); -#ifndef Q_OS_WASM - if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH24_STENCIL8, size.width(), size.height()); - else - funcs.glRenderbufferStorage(GL_RENDERBUFFER, - GL_DEPTH24_STENCIL8, size.width(), size.height()); - - stencil_buffer = depth_buffer; - funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, depth_buffer); - funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, stencil_buffer); -#else - // webgl does not allow separate depth and stencil attachments - if (samples != 0) { - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_STENCIL, size.width(), size.height()); - } else { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, - size.width(), size.height()); - } - stencil_buffer = depth_buffer; - funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, depth_buffer); -#endif - - valid = checkFramebufferStatus(); - if (!valid) { - funcs.glDeleteRenderbuffers(1, &depth_buffer); - stencil_buffer = depth_buffer = 0; - } - } - - if (depth_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil - || (attachment == QGLFramebufferObject::Depth))) - { - funcs.glGenRenderbuffers(1, &depth_buffer); - funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); - Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); - if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { -#ifdef QT_OPENGL_ES - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT24_OES, size.width(), size.height()); - } else { - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT16, size.width(), size.height()); - } -#else - if (ctx->contextHandle()->isOpenGLES()) { - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT24, size.width(), size.height()); - else - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT16, size.width(), size.height()); - } else { - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, - GL_DEPTH_COMPONENT, size.width(), size.height()); - } -#endif - } else { -#ifdef QT_OPENGL_ES - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, - size.width(), size.height()); - } else { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, - size.width(), size.height()); - } -#else - if (ctx->contextHandle()->isOpenGLES()) { - if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, - size.width(), size.height()); - } else { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, - size.width(), size.height()); - } - } else { - funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height()); - } -#endif - } - funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, depth_buffer); - valid = checkFramebufferStatus(); - if (!valid) { - funcs.glDeleteRenderbuffers(1, &depth_buffer); - depth_buffer = 0; - } - } - - if (stencil_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil)) { - funcs.glGenRenderbuffers(1, &stencil_buffer); - funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer); - Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer)); - -#ifdef QT_OPENGL_ES - GLenum storage = GL_STENCIL_INDEX8; -#else - GLenum storage = ctx->contextHandle()->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX; -#endif - - if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) - funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, size.width(), size.height()); - else - funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, size.width(), size.height()); - - funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, stencil_buffer); - valid = checkFramebufferStatus(); - if (!valid) { - funcs.glDeleteRenderbuffers(1, &stencil_buffer); - stencil_buffer = 0; - } - } - - // The FBO might have become valid after removing the depth or stencil buffer. - valid = checkFramebufferStatus(); - - if (depth_buffer && stencil_buffer) { - fbo_attachment = QGLFramebufferObject::CombinedDepthStencil; - } else if (depth_buffer) { - fbo_attachment = QGLFramebufferObject::Depth; - } else { - fbo_attachment = QGLFramebufferObject::NoAttachment; - } - - funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_ptr->current_fbo); - if (valid) { - fbo_guard = createSharedResourceGuard(ctx, fbo, freeFramebufferFunc); - if (color_buffer) - color_buffer_guard = createSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); - else - texture_guard = createSharedResourceGuard(ctx, texture, freeTextureFunc); - if (depth_buffer) - depth_buffer_guard = createSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc); - if (stencil_buffer) { - if (stencil_buffer == depth_buffer) - stencil_buffer_guard = depth_buffer_guard; - else - stencil_buffer_guard = createSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc); - } - } else { - if (color_buffer) - funcs.glDeleteRenderbuffers(1, &color_buffer); - else - funcs.glDeleteTextures(1, &texture); - if (depth_buffer) - funcs.glDeleteRenderbuffers(1, &depth_buffer); - if (stencil_buffer && depth_buffer != stencil_buffer) - funcs.glDeleteRenderbuffers(1, &stencil_buffer); - funcs.glDeleteFramebuffers(1, &fbo); - } - QT_CHECK_GLERROR(); - - format.setTextureTarget(target); - format.setSamples(int(samples)); - format.setAttachment(fbo_attachment); - format.setInternalTextureFormat(internal_format); - format.setMipmap(mipmap); - - glDevice.setFBO(q, attachment); -} - -/*! - \class QGLFramebufferObject - \inmodule QtOpenGL - \brief The QGLFramebufferObject class encapsulates an OpenGL framebuffer object. - \since 4.2 - - \obsolete - - \ingroup painting-3D - - The QGLFramebufferObject class encapsulates an OpenGL framebuffer - object, defined by the \c{GL_EXT_framebuffer_object} extension. In - addition it provides a rendering surface that can be painted on - with a QPainter, rendered to using native GL calls, or both. This - surface can be bound and used as a regular texture in your own GL - drawing code. By default, the QGLFramebufferObject class - generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target), - which is used as the internal rendering target. - - \b{It is important to have a current GL context when creating a - QGLFramebufferObject, otherwise initialization will fail.} - - OpenGL framebuffer objects and pbuffers (see - \l{QGLPixelBuffer}{QGLPixelBuffer}) can both be used to render to - offscreen surfaces, but there are a number of advantages with - using framebuffer objects instead of pbuffers: - - \list 1 - \li A framebuffer object does not require a separate rendering - context, so no context switching will occur when switching - rendering targets. There is an overhead involved in switching - targets, but in general it is cheaper than a context switch to a - pbuffer. - - \li Rendering to dynamic textures (i.e. render-to-texture - functionality) works on all platforms. No need to do explicit copy - calls from a render buffer into a texture, as was necessary on - systems that did not support the \c{render_texture} extension. - - \li It is possible to attach several rendering buffers (or texture - objects) to the same framebuffer object, and render to all of them - without doing a context switch. - - \li The OpenGL framebuffer extension is a pure GL extension with no - system dependant WGL, CGL, or GLX parts. This makes using - framebuffer objects more portable. - \endlist - - When using a QPainter to paint to a QGLFramebufferObject you should take - care that the QGLFramebufferObject is created with the CombinedDepthStencil - attachment for QPainter to be able to render correctly. - Note that you need to create a QGLFramebufferObject with more than one - sample per pixel for primitives to be antialiased when drawing using a - QPainter. To create a multisample framebuffer object you should use one of - the constructors that take a QGLFramebufferObjectFormat parameter, and set - the QGLFramebufferObjectFormat::samples() property to a non-zero value. - - When painting to a QGLFramebufferObject using QPainter, the state of - the current GL context will be altered by the paint engine to reflect - its needs. Applications should not rely upon the GL state being reset - to its original conditions, particularly the current shader program, - GL viewport, texture units, and drawing modes. - - For multisample framebuffer objects a color render buffer is created, - otherwise a texture with the specified texture target is created. - The color render buffer or texture will have the specified internal - format, and will be bound to the \c GL_COLOR_ATTACHMENT0 - attachment in the framebuffer object. - - If you want to use a framebuffer object with multisampling enabled - as a texture, you first need to copy from it to a regular framebuffer - object using QGLContext::blitFramebuffer(). - - \section1 Threading - - As of Qt 4.8, it's possible to draw into a QGLFramebufferObject - using a QPainter in a separate thread. Note that OpenGL 2.0 or - OpenGL ES 2.0 is required for this to work. - - \note This class has been deprecated in favor of QOpenGLFramebufferObject. -*/ - - -/*! - \enum QGLFramebufferObject::Attachment - \since 4.3 - - This enum type is used to configure the depth and stencil buffers - attached to the framebuffer object when it is created. - - \value NoAttachment No attachment is added to the framebuffer object. Note that the - OpenGL depth and stencil tests won't work when rendering to a - framebuffer object without any depth or stencil buffers. - This is the default value. - - \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present, - a combined depth and stencil buffer is attached. - If the extension is not present, only a depth buffer is attached. - - \value Depth A depth buffer is attached to the framebuffer object. - - \sa attachment() -*/ - - -/*! \fn QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target) - - Constructs an OpenGL framebuffer object and binds a 2D GL texture - to the buffer of the size \a size. The texture is bound to the - \c GL_COLOR_ATTACHMENT0 target in the framebuffer object. - - The \a target parameter is used to specify the GL texture - target. The default target is \c GL_TEXTURE_2D. Keep in mind that - \c GL_TEXTURE_2D textures must have a power of 2 width and height - (e.g. 256x512), unless you are using OpenGL 2.0 or higher. - - By default, no depth and stencil buffers are attached. This behavior - can be toggled using one of the overloaded constructors. - - The default internal texture format is \c GL_RGBA8 for desktop - OpenGL, and \c GL_RGBA for OpenGL/ES. - - It is important that you have a current GL context set when - creating the QGLFramebufferObject, otherwise the initialization - will fail. - - \sa size(), texture(), attachment() -*/ - -QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target) - : d_ptr(new QGLFramebufferObjectPrivate) -{ - Q_D(QGLFramebufferObject); - d->init(this, size, NoAttachment, target, -#ifndef QT_OPENGL_ES_2 - QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8 -#else - GL_RGBA -#endif - ); -} - -/*! \overload - - Constructs an OpenGL framebuffer object and binds a 2D GL texture - to the buffer of the given \a width and \a height. - - \sa size(), texture() -*/ -QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target) - : d_ptr(new QGLFramebufferObjectPrivate) -{ - Q_D(QGLFramebufferObject); - d->init(this, QSize(width, height), NoAttachment, target, -#ifndef QT_OPENGL_ES_2 - QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8 -#else - GL_RGBA -#endif - ); -} - -/*! \overload - - Constructs an OpenGL framebuffer object of the given \a size based on the - supplied \a format. -*/ - -QGLFramebufferObject::QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format) - : d_ptr(new QGLFramebufferObjectPrivate) -{ - Q_D(QGLFramebufferObject); - d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(), - format.samples(), format.mipmap()); -} - -/*! \overload - - Constructs an OpenGL framebuffer object of the given \a width and \a height - based on the supplied \a format. -*/ - -QGLFramebufferObject::QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format) - : d_ptr(new QGLFramebufferObjectPrivate) -{ - Q_D(QGLFramebufferObject); - d->init(this, QSize(width, height), format.attachment(), format.textureTarget(), - format.internalTextureFormat(), format.samples(), format.mipmap()); -} - -/*! \overload - - Constructs an OpenGL framebuffer object and binds a texture to the - buffer of the given \a width and \a height. - - The \a attachment parameter describes the depth/stencil buffer - configuration, \a target the texture target and \a internal_format - the internal texture format. The default texture target is \c - GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 - for desktop OpenGL and \c GL_RGBA for OpenGL/ES. - - \sa size(), texture(), attachment() -*/ -QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment, - GLenum target, GLenum internal_format) - : d_ptr(new QGLFramebufferObjectPrivate) -{ - Q_D(QGLFramebufferObject); - if (!internal_format) -#ifdef QT_OPENGL_ES_2 - internal_format = GL_RGBA; -#else - internal_format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; -#endif - d->init(this, QSize(width, height), attachment, target, internal_format); -} - -/*! \overload - - Constructs an OpenGL framebuffer object and binds a texture to the - buffer of the given \a size. - - The \a attachment parameter describes the depth/stencil buffer - configuration, \a target the texture target and \a internal_format - the internal texture format. The default texture target is \c - GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 - for desktop OpenGL and \c GL_RGBA for OpenGL/ES. - - \sa size(), texture(), attachment() -*/ -QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment, - GLenum target, GLenum internal_format) - : d_ptr(new QGLFramebufferObjectPrivate) -{ - Q_D(QGLFramebufferObject); - if (!internal_format) -#ifdef QT_OPENGL_ES_2 - internal_format = GL_RGBA; -#else - internal_format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; -#endif - d->init(this, size, attachment, target, internal_format); -} - -/*! - \fn QGLFramebufferObject::~QGLFramebufferObject() - - Destroys the framebuffer object and frees any allocated resources. -*/ -QGLFramebufferObject::~QGLFramebufferObject() -{ - Q_D(QGLFramebufferObject); - - delete d->engine; - - if (d->texture_guard) - d->texture_guard->free(); - if (d->color_buffer_guard) - d->color_buffer_guard->free(); - if (d->depth_buffer_guard) - d->depth_buffer_guard->free(); - if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard) - d->stencil_buffer_guard->free(); - if (d->fbo_guard) - d->fbo_guard->free(); -} - -/*! - \fn bool QGLFramebufferObject::isValid() const - - Returns \c true if the framebuffer object is valid. - - The framebuffer can become invalid if the initialization process - fails, the user attaches an invalid buffer to the framebuffer - object, or a non-power of two width/height is specified as the - texture size if the texture target is \c{GL_TEXTURE_2D}. - The non-power of two limitation does not apply if the OpenGL version - is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension - is present. - - The framebuffer can also become invalid if the QGLContext that - the framebuffer was created within is destroyed and there are - no other shared contexts that can take over ownership of the - framebuffer. -*/ -bool QGLFramebufferObject::isValid() const -{ - Q_D(const QGLFramebufferObject); - return d->valid && d->fbo_guard && d->fbo_guard->id(); -} - -/*! - \fn bool QGLFramebufferObject::bind() - - Switches rendering from the default, windowing system provided - framebuffer to this framebuffer object. - Returns \c true upon success, false otherwise. - - \sa release() -*/ -bool QGLFramebufferObject::bind() -{ - if (!isValid()) - return false; - Q_D(QGLFramebufferObject); - QGL_FUNC_CONTEXT; - if (!ctx) - return false; // Context no longer exists. - const QGLContext *current = QGLContext::currentContext(); -#ifdef QT_DEBUG - if (!current || - QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx)) - { - qWarning("QGLFramebufferObject::bind() called from incompatible context"); - } -#endif - d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); - d->valid = d->checkFramebufferStatus(); - if (d->valid && current) - current->d_ptr->setCurrentFbo(d->fbo()); - return d->valid; -} - -/*! - \fn bool QGLFramebufferObject::release() - - Switches rendering back to the default, windowing system provided - framebuffer. - Returns \c true upon success, false otherwise. - - \sa bind() -*/ -bool QGLFramebufferObject::release() -{ - if (!isValid()) - return false; - Q_D(QGLFramebufferObject); - QGL_FUNC_CONTEXT; - if (!ctx) - return false; // Context no longer exists. - - const QGLContext *current = QGLContext::currentContext(); - -#ifdef QT_DEBUG - if (!current || - QGLContextPrivate::contextGroup(current) != QGLContextPrivate::contextGroup(ctx)) - { - qWarning("QGLFramebufferObject::release() called from incompatible context"); - } -#endif - - if (current) { - current->d_ptr->setCurrentFbo(current->d_ptr->default_fbo); - d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->d_ptr->default_fbo); - } - - return true; -} - -/*! - \fn GLuint QGLFramebufferObject::texture() const - - Returns the texture id for the texture attached as the default - rendering target in this framebuffer object. This texture id can - be bound as a normal texture in your own GL code. - - If a multisample framebuffer object is used then the value returned - from this function will be invalid. -*/ -GLuint QGLFramebufferObject::texture() const -{ - Q_D(const QGLFramebufferObject); - return d->texture_guard ? d->texture_guard->id() : 0; -} - -/*! - \fn QSize QGLFramebufferObject::size() const - - Returns the size of the texture attached to this framebuffer - object. -*/ -QSize QGLFramebufferObject::size() const -{ - Q_D(const QGLFramebufferObject); - return d->size; -} - -/*! - Returns the format of this framebuffer object. -*/ -QGLFramebufferObjectFormat QGLFramebufferObject::format() const -{ - Q_D(const QGLFramebufferObject); - return d->format; -} - -/*! - \fn QImage QGLFramebufferObject::toImage() const - - Returns the contents of this framebuffer object as a QImage. - - The returned image has a format of premultiplied ARGB32 or RGB32. The latter is used - only when internalTextureFormat() is set to \c GL_RGB. - - If the rendering in the framebuffer was not done with premultiplied alpha in mind, - create a wrapper QImage with a non-premultiplied format. This is necessary before - performing operations like QImage::save() because otherwise the image data would get - unpremultiplied, even though it was not premultiplied in the first place. To create - such a wrapper without performing a copy of the pixel data, do the following: - - \code - QImage fboImage(fbo.toImage()); - QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32); - \endcode - - On QNX the back buffer is not preserved when a buffer swap occures. So this function - might return old content. -*/ -QImage QGLFramebufferObject::toImage() const -{ - Q_D(const QGLFramebufferObject); - if (!d->valid) - return QImage(); - - // qt_gl_read_frame_buffer doesn't work on a multisample FBO - if (format().samples() != 0) { - QGLFramebufferObject temp(size(), QGLFramebufferObjectFormat()); - - QRect rect(QPoint(0, 0), size()); - blitFramebuffer(&temp, rect, const_cast<QGLFramebufferObject *>(this), rect); - - return temp.toImage(); - } - - bool wasBound = isBound(); - if (!wasBound) - const_cast<QGLFramebufferObject *>(this)->bind(); - QImage image = qt_gl_read_frame_buffer(d->size, format().internalTextureFormat() != GL_RGB, true); - if (!wasBound) - const_cast<QGLFramebufferObject *>(this)->release(); - - return image; -} - -Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine) - -/*! \reimp */ -QPaintEngine *QGLFramebufferObject::paintEngine() const -{ - Q_D(const QGLFramebufferObject); - if (d->engine) - return d->engine; - - QPaintEngine *engine = qt_buffer_2_engine()->engine(); - if (engine->isActive() && engine->paintDevice() != this) { - d->engine = new QGL2PaintEngineEx; - return d->engine; - } - return engine; -} - -/*! - \fn bool QGLFramebufferObject::bindDefault() - - Switches rendering back to the default, windowing system provided - framebuffer. - Returns \c true upon success, false otherwise. - - \sa bind(), release() -*/ -bool QGLFramebufferObject::bindDefault() -{ - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - - if (ctx) { - QOpenGLFunctions functions(ctx->contextHandle()); - if (!functions.hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) - return false; - - ctx->d_ptr->setCurrentFbo(ctx->d_ptr->default_fbo); - functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_ptr->default_fbo); -#ifdef QT_DEBUG - } else { - qWarning("QGLFramebufferObject::bindDefault() called without current context."); -#endif - } - - return ctx != 0; -} - -/*! - \fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects() - - Returns \c true if the OpenGL \c{GL_EXT_framebuffer_object} extension - is present on this system; otherwise returns \c false. -*/ -bool QGLFramebufferObject::hasOpenGLFramebufferObjects() -{ - return qgl_hasFeature(QOpenGLFunctions::Framebuffers); -} - -/*! - \since 4.4 - - Draws the given texture, \a textureId, to the given target rectangle, - \a target, in OpenGL model space. The \a textureTarget should be a 2D - texture target. - - The framebuffer object should be bound when calling this function. - - Equivalent to the corresponding QGLContext::drawTexture(). -*/ -void QGLFramebufferObject::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) -{ - const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget); -} - -/*! - \since 4.4 - - Draws the given texture, \a textureId, at the given \a point in OpenGL - model space. The \a textureTarget should be a 2D texture target. - - The framebuffer object should be bound when calling this function. - - Equivalent to the corresponding QGLContext::drawTexture(). -*/ -void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) -{ - const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget); -} - -/*! \reimp */ -int QGLFramebufferObject::metric(PaintDeviceMetric metric) const -{ - Q_D(const QGLFramebufferObject); - - float dpmx = qt_defaultDpiX()*100./2.54; - float dpmy = qt_defaultDpiY()*100./2.54; - int w = d->size.width(); - int h = d->size.height(); - switch (metric) { - case PdmWidth: - return w; - - case PdmHeight: - return h; - - case PdmWidthMM: - return qRound(w * 1000 / dpmx); - - case PdmHeightMM: - return qRound(h * 1000 / dpmy); - - case PdmNumColors: - return 0; - - case PdmDepth: - return 32;//d->depth; - - case PdmDpiX: - return qRound(dpmx * 0.0254); - - case PdmDpiY: - return qRound(dpmy * 0.0254); - - case PdmPhysicalDpiX: - return qRound(dpmx * 0.0254); - - case PdmPhysicalDpiY: - return qRound(dpmy * 0.0254); - - case QPaintDevice::PdmDevicePixelRatio: - return 1; - - case QPaintDevice::PdmDevicePixelRatioScaled: - return 1 * QPaintDevice::devicePixelRatioFScale(); - - default: - qWarning("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric); - break; - } - return 0; -} - -/*! - \fn GLuint QGLFramebufferObject::handle() const - - Returns the GL framebuffer object handle for this framebuffer - object (returned by the \c{glGenFrameBuffersEXT()} function). This - handle can be used to attach new images or buffers to the - framebuffer. The user is responsible for cleaning up and - destroying these objects. -*/ -GLuint QGLFramebufferObject::handle() const -{ - Q_D(const QGLFramebufferObject); - return d->fbo(); -} - -/*! \fn int QGLFramebufferObject::devType() const - \internal -*/ - - -/*! - Returns the status of the depth and stencil buffers attached to - this framebuffer object. -*/ - -QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const -{ - Q_D(const QGLFramebufferObject); - if (d->valid) - return d->fbo_attachment; - return NoAttachment; -} - -/*! - \since 4.5 - - Returns \c true if the framebuffer object is currently bound to a context, - otherwise false is returned. -*/ - -bool QGLFramebufferObject::isBound() const -{ - Q_D(const QGLFramebufferObject); - const QGLContext *current = QGLContext::currentContext(); - if (current) { - current->d_ptr->refreshCurrentFbo(); - return current->d_ptr->current_fbo == d->fbo(); - } - - return false; -} - -/*! - \fn bool QGLFramebufferObject::hasOpenGLFramebufferBlit() - - \since 4.6 - - Returns \c true if the OpenGL \c{GL_EXT_framebuffer_blit} extension - is present on this system; otherwise returns \c false. - - \sa blitFramebuffer() -*/ -bool QGLFramebufferObject::hasOpenGLFramebufferBlit() -{ - return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); -} - -/*! - \since 4.6 - - Blits from the \a sourceRect rectangle in the \a source framebuffer - object to the \a targetRect rectangle in the \a target framebuffer object. - - If \a source or \a target is \nullptr, the default framebuffer will be used - instead of a framebuffer object as source or target respectively. - - The \a buffers parameter should be a mask consisting of any combination of - \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and - \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both - in the source and target buffers is ignored. - - The \a sourceRect and \a targetRect rectangles may have different sizes; - in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or - \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to - \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest - interpolation should be used when scaling is performed. - - If \a source equals \a target a copy is performed within the same buffer. - Results are undefined if the source and target rectangles overlap and - have different sizes. The sizes must also be the same if any of the - framebuffer objects are multisample framebuffers. - - Note that the scissor test will restrict the blit area if enabled. - - This function will have no effect unless hasOpenGLFramebufferBlit() returns - true. - - \sa hasOpenGLFramebufferBlit() -*/ -void QGLFramebufferObject::blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect, - QGLFramebufferObject *source, const QRect &sourceRect, - GLbitfield buffers, - GLenum filter) -{ - const QGLContext *ctx = QGLContext::currentContext(); - if (!ctx || !ctx->contextHandle()) - return; - - QOpenGLExtensions functions(ctx->contextHandle()); - if (!functions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) - return; - - QSurface *surface = ctx->contextHandle()->surface(); - - const int height = static_cast<QWindow *>(surface)->height(); - - const int sh = source ? source->height() : height; - const int th = target ? target->height() : height; - - const int sx0 = sourceRect.left(); - const int sx1 = sourceRect.left() + sourceRect.width(); - const int sy0 = sh - (sourceRect.top() + sourceRect.height()); - const int sy1 = sh - sourceRect.top(); - - const int tx0 = targetRect.left(); - const int tx1 = targetRect.left() + targetRect.width(); - const int ty0 = th - (targetRect.top() + targetRect.height()); - const int ty1 = th - targetRect.top(); - - ctx->d_ptr->refreshCurrentFbo(); - - functions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : 0); - functions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : 0); - - functions.glBlitFramebuffer(sx0, sy0, sx1, sy1, - tx0, ty0, tx1, ty1, - buffers, filter); - - functions.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_ptr->current_fbo); -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglframebufferobject.h b/src/opengl/qglframebufferobject.h deleted file mode 100644 index c88063cbb5..0000000000 --- a/src/opengl/qglframebufferobject.h +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLFRAMEBUFFEROBJECT_H -#define QGLFRAMEBUFFEROBJECT_H - -#include <QtOpenGL/qgl.h> -#include <QtGui/qpaintdevice.h> - -QT_BEGIN_NAMESPACE - - -class QGLFramebufferObjectPrivate; -class QGLFramebufferObjectFormat; - -class Q_OPENGL_EXPORT QGLFramebufferObject : public QPaintDevice -{ - Q_DECLARE_PRIVATE(QGLFramebufferObject) -public: - enum Attachment { - NoAttachment, - CombinedDepthStencil, - Depth - }; - - QGLFramebufferObject(const QSize &size, GLenum target = GL_TEXTURE_2D); - QGLFramebufferObject(int width, int height, GLenum target = GL_TEXTURE_2D); - - QGLFramebufferObject(const QSize &size, Attachment attachment, - GLenum target = GL_TEXTURE_2D, GLenum internal_format = 0); - QGLFramebufferObject(int width, int height, Attachment attachment, - GLenum target = GL_TEXTURE_2D, GLenum internal_format = 0); - - QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format); - QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format); - - virtual ~QGLFramebufferObject(); - - QGLFramebufferObjectFormat format() const; - - bool isValid() const; - bool isBound() const; - bool bind(); - bool release(); - - GLuint texture() const; - QSize size() const; - QImage toImage() const; - Attachment attachment() const; - - QPaintEngine *paintEngine() const override; - GLuint handle() const; - - static bool bindDefault(); - - static bool hasOpenGLFramebufferObjects(); - - void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - - static bool hasOpenGLFramebufferBlit(); - static void blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect, - QGLFramebufferObject *source, const QRect &sourceRect, - GLbitfield buffers = GL_COLOR_BUFFER_BIT, - GLenum filter = GL_NEAREST); - -protected: - int metric(PaintDeviceMetric metric) const override; - int devType() const override { return QInternal::FramebufferObject; } - -private: - Q_DISABLE_COPY(QGLFramebufferObject) - QScopedPointer<QGLFramebufferObjectPrivate> d_ptr; - friend class QGLPaintDevice; - friend class QGLFBOGLPaintDevice; -}; - -class QGLFramebufferObjectFormatPrivate; -class Q_OPENGL_EXPORT QGLFramebufferObjectFormat -{ -public: - QGLFramebufferObjectFormat(); - QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other); - QGLFramebufferObjectFormat &operator=(const QGLFramebufferObjectFormat &other); - ~QGLFramebufferObjectFormat(); - - void setSamples(int samples); - int samples() const; - - void setMipmap(bool enabled); - bool mipmap() const; - - void setAttachment(QGLFramebufferObject::Attachment attachment); - QGLFramebufferObject::Attachment attachment() const; - - void setTextureTarget(GLenum target); - GLenum textureTarget() const; - - void setInternalTextureFormat(GLenum internalTextureFormat); - GLenum internalTextureFormat() const; - - bool operator==(const QGLFramebufferObjectFormat& other) const; - bool operator!=(const QGLFramebufferObjectFormat& other) const; - -private: - QGLFramebufferObjectFormatPrivate *d; - - void detach(); -}; - -QT_END_NAMESPACE - -#endif // QGLFRAMEBUFFEROBJECT_H diff --git a/src/opengl/qglframebufferobject_p.h b/src/opengl/qglframebufferobject_p.h deleted file mode 100644 index 9d536527c3..0000000000 --- a/src/opengl/qglframebufferobject_p.h +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLFRAMEBUFFEROBJECT_P_H -#define QGLFRAMEBUFFEROBJECT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt 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. -// - -#include <qglframebufferobject.h> -#include <private/qglpaintdevice_p.h> -#include <private/qgl_p.h> -#include <private/qopenglextensions_p.h> - -QT_BEGIN_NAMESPACE - -class QGLFramebufferObjectFormatPrivate -{ -public: - QGLFramebufferObjectFormatPrivate() - : ref(1), - samples(0), - attachment(QGLFramebufferObject::NoAttachment), - target(GL_TEXTURE_2D), - mipmap(false) - { -#ifndef QT_OPENGL_ES_2 - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - const bool isES = ctx ? ctx->isOpenGLES() : QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL; - internal_format = isES ? GL_RGBA : GL_RGBA8; -#else - internal_format = GL_RGBA; -#endif - } - QGLFramebufferObjectFormatPrivate - (const QGLFramebufferObjectFormatPrivate *other) - : ref(1), - samples(other->samples), - attachment(other->attachment), - target(other->target), - internal_format(other->internal_format), - mipmap(other->mipmap) - { - } - bool equals(const QGLFramebufferObjectFormatPrivate *other) - { - return samples == other->samples && - attachment == other->attachment && - target == other->target && - internal_format == other->internal_format && - mipmap == other->mipmap; - } - - QAtomicInt ref; - int samples; - QGLFramebufferObject::Attachment attachment; - GLenum target; - GLenum internal_format; - uint mipmap : 1; -}; - -class QGLFBOGLPaintDevice : public QGLPaintDevice -{ -public: - virtual QPaintEngine* paintEngine() const override {return fbo->paintEngine();} - virtual QSize size() const override {return fbo->size();} - virtual QGLContext* context() const override; - virtual QGLFormat format() const override {return fboFormat;} - virtual bool alphaRequested() const override { return reqAlpha; } - - void setFBO(QGLFramebufferObject* f, - QGLFramebufferObject::Attachment attachment); - -private: - QGLFramebufferObject* fbo; - QGLFormat fboFormat; - bool reqAlpha; -}; - -class QGLFramebufferObjectPrivate -{ -public: - QGLFramebufferObjectPrivate() : fbo_guard(nullptr), texture_guard(nullptr), depth_buffer_guard(nullptr) - , stencil_buffer_guard(nullptr), color_buffer_guard(nullptr) - , valid(false), engine(nullptr) {} - ~QGLFramebufferObjectPrivate() {} - - void init(QGLFramebufferObject *q, const QSize& sz, - QGLFramebufferObject::Attachment attachment, - GLenum internal_format, GLenum texture_target, - GLint samples = 0, bool mipmap = false); - bool checkFramebufferStatus() const; - QGLSharedResourceGuardBase *fbo_guard; - QGLSharedResourceGuardBase *texture_guard; - QGLSharedResourceGuardBase *depth_buffer_guard; - QGLSharedResourceGuardBase *stencil_buffer_guard; - QGLSharedResourceGuardBase *color_buffer_guard; - GLenum target; - QSize size; - QGLFramebufferObjectFormat format; - uint valid : 1; - QGLFramebufferObject::Attachment fbo_attachment; - mutable QPaintEngine *engine; - QGLFBOGLPaintDevice glDevice; - QOpenGLExtensions funcs; - - inline GLuint fbo() const { return fbo_guard ? fbo_guard->id() : 0; } -}; - - -QT_END_NAMESPACE - -#endif // QGLFRAMEBUFFEROBJECT_P_H diff --git a/src/opengl/qglfunctions.cpp b/src/opengl/qglfunctions.cpp deleted file mode 100644 index b20311bec4..0000000000 --- a/src/opengl/qglfunctions.cpp +++ /dev/null @@ -1,1330 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qglfunctions.h" -#include "qgl_p.h" -#include "QtGui/private/qopenglcontext_p.h" -#include <private/qopengl_p.h> - -QT_BEGIN_NAMESPACE - -/*! - \class QGLFunctions - \inmodule QtOpenGL - \brief The QGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API. - \since 4.8 - \obsolete - \ingroup painting-3D - - OpenGL ES 2.0 defines a subset of the OpenGL specification that is - common across many desktop and embedded OpenGL implementations. - However, it can be difficult to use the functions from that subset - because they need to be resolved manually on desktop systems. - - QGLFunctions provides a guaranteed API that is available on all - OpenGL systems and takes care of function resolution on systems - that need it. The recommended way to use QGLFunctions is by - direct inheritance: - - \snippet code/src_opengl_qglfunctions.cpp 0 - - The \c{paintGL()} function can then use any of the OpenGL ES 2.0 - functions without explicit resolution, such as glActiveTexture() - in the following example: - - \snippet code/src_opengl_qglfunctions.cpp 1 - - QGLFunctions can also be used directly for ad-hoc invocation - of OpenGL ES 2.0 functions on all platforms: - - \snippet code/src_opengl_qglfunctions.cpp 2 - - QGLFunctions provides wrappers for all OpenGL ES 2.0 functions, - except those like \c{glDrawArrays()}, \c{glViewport()}, and - \c{glBindTexture()} that don't have portability issues. - - Including the header for QGLFunctions will also define all of - the OpenGL ES 2.0 macro constants that are not already defined by - the system's OpenGL headers, such as \c{GL_TEXTURE1} above. - - The hasOpenGLFeature() and openGLFeatures() functions can be used - to determine if the OpenGL implementation has a major OpenGL ES 2.0 - feature. For example, the following checks if non power of two - textures are available: - - \snippet code/src_opengl_qglfunctions.cpp 3 - - \note This class has been deprecated in favor of QOpenGLFunctions. -*/ - -/*! - \enum QGLFunctions::OpenGLFeature - This enum defines OpenGL ES 2.0 features that may be optional - on other platforms. - - \value Multitexture glActiveTexture() function is available. - \value Shaders Shader functions are available. - \value Buffers Vertex and index buffer functions are available. - \value Framebuffers Framebuffer object functions are available. - \value BlendColor glBlendColor() is available. - \value BlendEquation glBlendEquation() is available. - \value BlendEquationSeparate glBlendEquationSeparate() is available. - \value BlendFuncSeparate glBlendFuncSeparate() is available. - \value BlendSubtract Blend subtract mode is available. - \value CompressedTextures Compressed texture functions are available. - \value Multisample glSampleCoverage() function is available. - \value StencilSeparate Separate stencil functions are available. - \value NPOTTextures Non power of two textures are available. -*/ - -// Hidden private fields for additional extension data. -struct QGLFunctionsPrivateEx : public QGLFunctionsPrivate, public QOpenGLSharedResource -{ - QGLFunctionsPrivateEx(QOpenGLContext *context) - : QGLFunctionsPrivate(QGLContext::fromOpenGLContext(context)) - , QOpenGLSharedResource(context->shareGroup()) - , m_features(-1) - { - funcs = new QOpenGLFunctions(context); - funcs->initializeOpenGLFunctions(); - } - - ~QGLFunctionsPrivateEx() - { - delete funcs; - } - - void invalidateResource() override - { - m_features = -1; - } - - void freeResource(QOpenGLContext *) override - { - // no gl resources to free - } - - int m_features; -}; - -Q_GLOBAL_STATIC(QOpenGLMultiGroupSharedResource, qt_gl_functions_resource) - -static QGLFunctionsPrivateEx *qt_gl_functions(const QGLContext *context = 0) -{ - if (!context) - context = QGLContext::currentContext(); - Q_ASSERT(context); - QGLFunctionsPrivateEx *funcs = - reinterpret_cast<QGLFunctionsPrivateEx *> - (qt_gl_functions_resource()->value<QGLFunctionsPrivateEx>(context->contextHandle())); - return funcs; -} - -/*! - Constructs a default function resolver. The resolver cannot - be used until initializeGLFunctions() is called to specify - the context. - - \sa initializeGLFunctions() -*/ -QGLFunctions::QGLFunctions() - : d_ptr(0) -{ -} - -/*! - Constructs a function resolver for \a context. If \a context - is \nullptr, then the resolver will be created for the current - QGLContext. - - An object constructed in this way can only be used with \a context - and other contexts that share with it. Use initializeGLFunctions() - to change the object's context association. - - \sa initializeGLFunctions() -*/ -QGLFunctions::QGLFunctions(const QGLContext *context) - : d_ptr(qt_gl_functions(context)) -{ -} - -/*! - \fn QGLFunctions::~QGLFunctions() - - Destroys this function resolver. -*/ - -static int qt_gl_resolve_features() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (ctx->isOpenGLES()) { - // OpenGL ES 2 - int features = QGLFunctions::Multitexture | - QGLFunctions::Shaders | - QGLFunctions::Buffers | - QGLFunctions::Framebuffers | - QGLFunctions::BlendColor | - QGLFunctions::BlendEquation | - QGLFunctions::BlendEquationSeparate | - QGLFunctions::BlendFuncSeparate | - QGLFunctions::BlendSubtract | - QGLFunctions::CompressedTextures | - QGLFunctions::Multisample | - QGLFunctions::StencilSeparate; - QOpenGLExtensionMatcher extensions; - if (extensions.match("GL_OES_texture_npot")) - features |= QGLFunctions::NPOTTextures; - if (extensions.match("GL_IMG_texture_npot")) - features |= QGLFunctions::NPOTTextures; - return features; - } else { - // OpenGL - int features = 0; - QGLFormat::OpenGLVersionFlags versions = QGLFormat::openGLVersionFlags(); - QOpenGLExtensionMatcher extensions; - - // Recognize features by extension name. - if (extensions.match("GL_ARB_multitexture")) - features |= QGLFunctions::Multitexture; - if (extensions.match("GL_ARB_shader_objects")) - features |= QGLFunctions::Shaders; - if (extensions.match("GL_EXT_framebuffer_object") || - extensions.match("GL_ARB_framebuffer_object")) - features |= QGLFunctions::Framebuffers; - if (extensions.match("GL_EXT_blend_color")) - features |= QGLFunctions::BlendColor; - if (extensions.match("GL_EXT_blend_equation_separate")) - features |= QGLFunctions::BlendEquationSeparate; - if (extensions.match("GL_EXT_blend_func_separate")) - features |= QGLFunctions::BlendFuncSeparate; - if (extensions.match("GL_EXT_blend_subtract")) - features |= QGLFunctions::BlendSubtract; - if (extensions.match("GL_ARB_texture_compression")) - features |= QGLFunctions::CompressedTextures; - if (extensions.match("GL_ARB_multisample")) - features |= QGLFunctions::Multisample; - if (extensions.match("GL_ARB_texture_non_power_of_two")) - features |= QGLFunctions::NPOTTextures; - - // Recognize features by minimum OpenGL version. - if (versions & QGLFormat::OpenGL_Version_1_2) { - features |= QGLFunctions::BlendColor | - QGLFunctions::BlendEquation; - } - if (versions & QGLFormat::OpenGL_Version_1_3) { - features |= QGLFunctions::Multitexture | - QGLFunctions::CompressedTextures | - QGLFunctions::Multisample; - } - if (versions & QGLFormat::OpenGL_Version_1_4) - features |= QGLFunctions::BlendFuncSeparate; - if (versions & QGLFormat::OpenGL_Version_1_5) - features |= QGLFunctions::Buffers; - if (versions & QGLFormat::OpenGL_Version_2_0) { - features |= QGLFunctions::Shaders | - QGLFunctions::StencilSeparate | - QGLFunctions::BlendEquationSeparate | - QGLFunctions::NPOTTextures; - } - return features; - } -} - -/*! - Returns the set of features that are present on this system's - OpenGL implementation. - - It is assumed that the QGLContext associated with this function - resolver is current. - - \sa hasOpenGLFeature() -*/ -QGLFunctions::OpenGLFeatures QGLFunctions::openGLFeatures() const -{ - QGLFunctionsPrivateEx *d = static_cast<QGLFunctionsPrivateEx *>(d_ptr); - if (!d) - return { }; - if (d->m_features == -1) - d->m_features = qt_gl_resolve_features(); - return QGLFunctions::OpenGLFeatures(d->m_features); -} - -/*! - Returns \c true if \a feature is present on this system's OpenGL - implementation; false otherwise. - - It is assumed that the QGLContext associated with this function - resolver is current. - - \sa openGLFeatures() -*/ -bool QGLFunctions::hasOpenGLFeature(QGLFunctions::OpenGLFeature feature) const -{ - QGLFunctionsPrivateEx *d = static_cast<QGLFunctionsPrivateEx *>(d_ptr); - if (!d) - return false; - if (d->m_features == -1) - d->m_features = qt_gl_resolve_features(); - return (d->m_features & int(feature)) != 0; -} - -/*! - Initializes GL function resolution for \a context. If \a context - is \nullptr, then the current QGLContext will be used. - - After calling this function, the QGLFunctions object can only be - used with \a context and other contexts that share with it. - Call initializeGLFunctions() again to change the object's context - association. -*/ -void QGLFunctions::initializeGLFunctions(const QGLContext *context) -{ - d_ptr = qt_gl_functions(context); -} - -/*! - \fn void QGLFunctions::glActiveTexture(GLenum texture) - - Convenience function that calls glActiveTexture(\a texture). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glActiveTexture.xml}{glActiveTexture()}. -*/ - -/*! - \fn void QGLFunctions::glAttachShader(GLuint program, GLuint shader) - - Convenience function that calls glAttachShader(\a program, \a shader). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glAttachShader.xml}{glAttachShader()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) - - Convenience function that calls glBindAttribLocation(\a program, \a index, \a name). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBindAttribLocation.xml}{glBindAttribLocation()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glBindBuffer(GLenum target, GLuint buffer) - - Convenience function that calls glBindBuffer(\a target, \a buffer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBindBuffer.xml}{glBindBuffer()}. -*/ - -/*! - \fn void QGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) - - Convenience function that calls glBindFramebuffer(\a target, \a framebuffer). - - Note that Qt will translate a \a framebuffer argument of 0 to the currently - bound QOpenGLContext's defaultFramebufferObject(). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBindFramebuffer.xml}{glBindFramebuffer()}. -*/ - -/*! - \fn void QGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) - - Convenience function that calls glBindRenderbuffer(\a target, \a renderbuffer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBindRenderbuffer.xml}{glBindRenderbuffer()}. -*/ - -/*! - \fn void QGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) - - Convenience function that calls glBlendColor(\a red, \a green, \a blue, \a alpha). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendColor.xml}{glBlendColor()}. -*/ - -/*! - \fn void QGLFunctions::glBlendEquation(GLenum mode) - - Convenience function that calls glBlendEquation(\a mode). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendEquation.xml}{glBlendEquation()}. -*/ - -/*! - \fn void QGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) - - Convenience function that calls glBlendEquationSeparate(\a modeRGB, \a modeAlpha). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendEquationSeparate.xml}{glBlendEquationSeparate()}. -*/ - -/*! - \fn void QGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) - - Convenience function that calls glBlendFuncSeparate(\a srcRGB, \a dstRGB, \a srcAlpha, \a dstAlpha). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendFuncSeparate.xml}{glBlendFuncSeparate()}. -*/ - -/*! - \fn void QGLFunctions::glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage) - - Convenience function that calls glBufferData(\a target, \a size, \a data, \a usage). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBufferData.xml}{glBufferData()}. -*/ - -/*! - \fn void QGLFunctions::glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data) - - Convenience function that calls glBufferSubData(\a target, \a offset, \a size, \a data). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glBufferSubData.xml}{glBufferSubData()}. -*/ - -/*! - \fn GLenum QGLFunctions::glCheckFramebufferStatus(GLenum target) - - Convenience function that calls glCheckFramebufferStatus(\a target). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glCheckFramebufferStatus.xml}{glCheckFramebufferStatus()}. -*/ - -/*! - \fn void QGLFunctions::glClearDepthf(GLclampf depth) - - Convenience function that calls glClearDepth(\a depth) on - desktop OpenGL systems and glClearDepthf(\a depth) on - embedded OpenGL ES systems. - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glClearDepthf.xml}{glClearDepthf()}. -*/ - -/*! - \fn void QGLFunctions::glCompileShader(GLuint shader) - - Convenience function that calls glCompileShader(\a shader). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glCompileShader.xml}{glCompileShader()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) - - Convenience function that calls glCompressedTexImage2D(\a target, \a level, \a internalformat, \a width, \a height, \a border, \a imageSize, \a data). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glCompressedTexImage2D.xml}{glCompressedTexImage2D()}. -*/ - -/*! - \fn void QGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) - - Convenience function that calls glCompressedTexSubImage2D(\a target, \a level, \a xoffset, \a yoffset, \a width, \a height, \a format, \a imageSize, \a data). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glCompressedTexSubImage2D.xml}{glCompressedTexSubImage2D()}. -*/ - -/*! - \fn GLuint QGLFunctions::glCreateProgram() - - Convenience function that calls glCreateProgram(). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glCreateProgram.xml}{glCreateProgram()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn GLuint QGLFunctions::glCreateShader(GLenum type) - - Convenience function that calls glCreateShader(\a type). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glCreateShader.xml}{glCreateShader()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) - - Convenience function that calls glDeleteBuffers(\a n, \a buffers). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteBuffers.xml}{glDeleteBuffers()}. -*/ - -/*! - \fn void QGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) - - Convenience function that calls glDeleteFramebuffers(\a n, \a framebuffers). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteFramebuffers.xml}{glDeleteFramebuffers()}. -*/ - -/*! - \fn void QGLFunctions::glDeleteProgram(GLuint program) - - Convenience function that calls glDeleteProgram(\a program). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteProgram.xml}{glDeleteProgram()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) - - Convenience function that calls glDeleteRenderbuffers(\a n, \a renderbuffers). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteRenderbuffers.xml}{glDeleteRenderbuffers()}. -*/ - -/*! - \fn void QGLFunctions::glDeleteShader(GLuint shader) - - Convenience function that calls glDeleteShader(\a shader). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteShader.xml}{glDeleteShader()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) - - Convenience function that calls glDepthRange(\a zNear, \a zFar) on - desktop OpenGL systems and glDepthRangef(\a zNear, \a zFar) on - embedded OpenGL ES systems. - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDepthRangef.xml}{glDepthRangef()}. -*/ - -/*! - \fn void QGLFunctions::glDetachShader(GLuint program, GLuint shader) - - Convenience function that calls glDetachShader(\a program, \a shader). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDetachShader.xml}{glDetachShader()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glDisableVertexAttribArray(GLuint index) - - Convenience function that calls glDisableVertexAttribArray(\a index). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glDisableVertexAttribArray.xml}{glDisableVertexAttribArray()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glEnableVertexAttribArray(GLuint index) - - Convenience function that calls glEnableVertexAttribArray(\a index). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glEnableVertexAttribArray.xml}{glEnableVertexAttribArray()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) - - Convenience function that calls glFramebufferRenderbuffer(\a target, \a attachment, \a renderbuffertarget, \a renderbuffer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glFramebufferRenderbuffer.xml}{glFramebufferRenderbuffer()}. -*/ - -/*! - \fn void QGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) - - Convenience function that calls glFramebufferTexture2D(\a target, \a attachment, \a textarget, \a texture, \a level). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glFramebufferTexture2D.xml}{glFramebufferTexture2D()}. -*/ - -/*! - \fn void QGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) - - Convenience function that calls glGenBuffers(\a n, \a buffers). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGenBuffers.xml}{glGenBuffers()}. -*/ - -/*! - \fn void QGLFunctions::glGenerateMipmap(GLenum target) - - Convenience function that calls glGenerateMipmap(\a target). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGenerateMipmap.xml}{glGenerateMipmap()}. -*/ - -/*! - \fn void QGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) - - Convenience function that calls glGenFramebuffers(\a n, \a framebuffers). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGenFramebuffers.xml}{glGenFramebuffers()}. -*/ - -/*! - \fn void QGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) - - Convenience function that calls glGenRenderbuffers(\a n, \a renderbuffers). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGenRenderbuffers.xml}{glGenRenderbuffers()}. -*/ - -/*! - \fn void QGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) - - Convenience function that calls glGetActiveAttrib(\a program, \a index, \a bufsize, \a length, \a size, \a type, \a name). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetActiveAttrib.xml}{glGetActiveAttrib()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) - - Convenience function that calls glGetActiveUniform(\a program, \a index, \a bufsize, \a length, \a size, \a type, \a name). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetActiveUniform.xml}{glGetActiveUniform()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) - - Convenience function that calls glGetAttachedShaders(\a program, \a maxcount, \a count, \a shaders). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetAttachedShaders.xml}{glGetAttachedShaders()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn int QGLFunctions::glGetAttribLocation(GLuint program, const char* name) - - Convenience function that calls glGetAttribLocation(\a program, \a name). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetAttribLocation.xml}{glGetAttribLocation()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) - - Convenience function that calls glGetBufferParameteriv(\a target, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetBufferParameteriv.xml}{glGetBufferParameteriv()}. -*/ - -/*! - \fn void QGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) - - Convenience function that calls glGetFramebufferAttachmentParameteriv(\a target, \a attachment, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetFramebufferAttachmentParameteriv.xml}{glGetFramebufferAttachmentParameteriv()}. -*/ - -/*! - \fn void QGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) - - Convenience function that calls glGetProgramiv(\a program, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetProgramiv.xml}{glGetProgramiv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) - - Convenience function that calls glGetProgramInfoLog(\a program, \a bufsize, \a length, \a infolog). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetProgramInfoLog.xml}{glGetProgramInfoLog()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) - - Convenience function that calls glGetRenderbufferParameteriv(\a target, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetRenderbufferParameteriv.xml}{glGetRenderbufferParameteriv()}. -*/ - -/*! - \fn void QGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) - - Convenience function that calls glGetShaderiv(\a shader, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderiv.xml}{glGetShaderiv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) - - Convenience function that calls glGetShaderInfoLog(\a shader, \a bufsize, \a length, \a infolog). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderInfoLog.xml}{glGetShaderInfoLog()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) - - Convenience function that calls glGetShaderPrecisionFormat(\a shadertype, \a precisiontype, \a range, \a precision). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderPrecisionFormat.xml}{glGetShaderPrecisionFormat()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) - - Convenience function that calls glGetShaderSource(\a shader, \a bufsize, \a length, \a source). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderSource.xml}{glGetShaderSource()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) - - Convenience function that calls glGetUniformfv(\a program, \a location, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformfv.xml}{glGetUniformfv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) - - Convenience function that calls glGetUniformiv(\a program, \a location, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformiv.xml}{glGetUniformiv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn int QGLFunctions::glGetUniformLocation(GLuint program, const char* name) - - Convenience function that calls glGetUniformLocation(\a program, \a name). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformLocation.xml}{glGetUniformLocation()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) - - Convenience function that calls glGetVertexAttribfv(\a index, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribfv.xml}{glGetVertexAttribfv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) - - Convenience function that calls glGetVertexAttribiv(\a index, \a pname, \a params). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribiv.xml}{glGetVertexAttribiv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) - - Convenience function that calls glGetVertexAttribPointerv(\a index, \a pname, \a pointer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribPointerv.xml}{glGetVertexAttribPointerv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn GLboolean QGLFunctions::glIsBuffer(GLuint buffer) - - Convenience function that calls glIsBuffer(\a buffer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glIsBuffer.xml}{glIsBuffer()}. -*/ - -/*! - \fn GLboolean QGLFunctions::glIsFramebuffer(GLuint framebuffer) - - Convenience function that calls glIsFramebuffer(\a framebuffer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glIsFramebuffer.xml}{glIsFramebuffer()}. -*/ - -/*! - \fn GLboolean QGLFunctions::glIsProgram(GLuint program) - - Convenience function that calls glIsProgram(\a program). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glIsProgram.xml}{glIsProgram()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn GLboolean QGLFunctions::glIsRenderbuffer(GLuint renderbuffer) - - Convenience function that calls glIsRenderbuffer(\a renderbuffer). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glIsRenderbuffer.xml}{glIsRenderbuffer()}. -*/ - -/*! - \fn GLboolean QGLFunctions::glIsShader(GLuint shader) - - Convenience function that calls glIsShader(\a shader). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glIsShader.xml}{glIsShader()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glLinkProgram(GLuint program) - - Convenience function that calls glLinkProgram(\a program). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glLinkProgram.xml}{glLinkProgram()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glReleaseShaderCompiler() - - Convenience function that calls glReleaseShaderCompiler(). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glReleaseShaderCompiler.xml}{glReleaseShaderCompiler()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) - - Convenience function that calls glRenderbufferStorage(\a target, \a internalformat, \a width, \a height). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glRenderbufferStorage.xml}{glRenderbufferStorage()}. -*/ - -/*! - \fn void QGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) - - Convenience function that calls glSampleCoverage(\a value, \a invert). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glSampleCoverage.xml}{glSampleCoverage()}. -*/ - -/*! - \fn void QGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) - - Convenience function that calls glShaderBinary(\a n, \a shaders, \a binaryformat, \a binary, \a length). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glShaderBinary.xml}{glShaderBinary()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) - - Convenience function that calls glShaderSource(\a shader, \a count, \a string, \a length). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glShaderSource.xml}{glShaderSource()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) - - Convenience function that calls glStencilFuncSeparate(\a face, \a func, \a ref, \a mask). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilFuncSeparate.xml}{glStencilFuncSeparate()}. -*/ - -/*! - \fn void QGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) - - Convenience function that calls glStencilMaskSeparate(\a face, \a mask). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilMaskSeparate.xml}{glStencilMaskSeparate()}. -*/ - -/*! - \fn void QGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) - - Convenience function that calls glStencilOpSeparate(\a face, \a fail, \a zfail, \a zpass). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilOpSeparate.xml}{glStencilOpSeparate()}. -*/ - -/*! - \fn void QGLFunctions::glUniform1f(GLint location, GLfloat x) - - Convenience function that calls glUniform1f(\a location, \a x). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1f.xml}{glUniform1f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) - - Convenience function that calls glUniform1fv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1fv.xml}{glUniform1fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform1i(GLint location, GLint x) - - Convenience function that calls glUniform1i(\a location, \a x). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1i.xml}{glUniform1i()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) - - Convenience function that calls glUniform1iv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1iv.xml}{glUniform1iv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) - - Convenience function that calls glUniform2f(\a location, \a x, \a y). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2f.xml}{glUniform2f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) - - Convenience function that calls glUniform2fv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2fv.xml}{glUniform2fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform2i(GLint location, GLint x, GLint y) - - Convenience function that calls glUniform2i(\a location, \a x, \a y). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2i.xml}{glUniform2i()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) - - Convenience function that calls glUniform2iv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2iv.xml}{glUniform2iv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) - - Convenience function that calls glUniform3f(\a location, \a x, \a y, \a z). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3f.xml}{glUniform3f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) - - Convenience function that calls glUniform3fv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3fv.xml}{glUniform3fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) - - Convenience function that calls glUniform3i(\a location, \a x, \a y, \a z). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3i.xml}{glUniform3i()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) - - Convenience function that calls glUniform3iv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3iv.xml}{glUniform3iv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) - - Convenience function that calls glUniform4f(\a location, \a x, \a y, \a z, \a w). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4f.xml}{glUniform4f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) - - Convenience function that calls glUniform4fv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4fv.xml}{glUniform4fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) - - Convenience function that calls glUniform4i(\a location, \a x, \a y, \a z, \a w). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4i.xml}{glUniform4i()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) - - Convenience function that calls glUniform4iv(\a location, \a count, \a v). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4iv.xml}{glUniform4iv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) - - Convenience function that calls glUniformMatrix2fv(\a location, \a count, \a transpose, \a value). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix2fv.xml}{glUniformMatrix2fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) - - Convenience function that calls glUniformMatrix3fv(\a location, \a count, \a transpose, \a value). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix3fv.xml}{glUniformMatrix3fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) - - Convenience function that calls glUniformMatrix4fv(\a location, \a count, \a transpose, \a value). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix4fv.xml}{glUniformMatrix4fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glUseProgram(GLuint program) - - Convenience function that calls glUseProgram(\a program). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glUseProgram.xml}{glUseProgram()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glValidateProgram(GLuint program) - - Convenience function that calls glValidateProgram(\a program). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glValidateProgram.xml}{glValidateProgram()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) - - Convenience function that calls glVertexAttrib1f(\a indx, \a x). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib1f.xml}{glVertexAttrib1f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) - - Convenience function that calls glVertexAttrib1fv(\a indx, \a values). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib1fv.xml}{glVertexAttrib1fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) - - Convenience function that calls glVertexAttrib2f(\a indx, \a x, \a y). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib2f.xml}{glVertexAttrib2f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) - - Convenience function that calls glVertexAttrib2fv(\a indx, \a values). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib2fv.xml}{glVertexAttrib2fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) - - Convenience function that calls glVertexAttrib3f(\a indx, \a x, \a y, \a z). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib3f.xml}{glVertexAttrib3f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) - - Convenience function that calls glVertexAttrib3fv(\a indx, \a values). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib3fv.xml}{glVertexAttrib3fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) - - Convenience function that calls glVertexAttrib4f(\a indx, \a x, \a y, \a z, \a w). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib4f.xml}{glVertexAttrib4f()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) - - Convenience function that calls glVertexAttrib4fv(\a indx, \a values). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib4fv.xml}{glVertexAttrib4fv()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -/*! - \fn void QGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) - - Convenience function that calls glVertexAttribPointer(\a indx, \a size, \a type, \a normalized, \a stride, \a ptr). - - For more information, see the OpenGL ES 2.0 documentation for - \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttribPointer.xml}{glVertexAttribPointer()}. - - This convenience function will do nothing on OpenGL ES 1.x systems. -*/ - -QGLFunctionsPrivate::QGLFunctionsPrivate(const QGLContext *) - : funcs(0) -{ -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglfunctions.h b/src/opengl/qglfunctions.h deleted file mode 100644 index d8c5249a1a..0000000000 --- a/src/opengl/qglfunctions.h +++ /dev/null @@ -1,1688 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLFUNCTIONS_H -#define QGLFUNCTIONS_H - -#include <QtOpenGL/qgl.h> -#include <QtGui/qopenglcontext.h> -#include <QtGui/qopenglfunctions.h> - -QT_BEGIN_NAMESPACE - -struct QGLFunctionsPrivate; - -class Q_OPENGL_EXPORT QGLFunctions -{ -public: - QGLFunctions(); - explicit QGLFunctions(const QGLContext *context); - ~QGLFunctions() {} - - enum OpenGLFeature - { - Multitexture = 0x0001, - Shaders = 0x0002, - Buffers = 0x0004, - Framebuffers = 0x0008, - BlendColor = 0x0010, - BlendEquation = 0x0020, - BlendEquationSeparate = 0x0040, - BlendFuncSeparate = 0x0080, - BlendSubtract = 0x0100, - CompressedTextures = 0x0200, - Multisample = 0x0400, - StencilSeparate = 0x0800, - NPOTTextures = 0x1000 - }; - Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature) - - QGLFunctions::OpenGLFeatures openGLFeatures() const; - bool hasOpenGLFeature(QGLFunctions::OpenGLFeature feature) const; - - void initializeGLFunctions(const QGLContext *context = nullptr); - - void glActiveTexture(GLenum texture); - void glAttachShader(GLuint program, GLuint shader); - void glBindAttribLocation(GLuint program, GLuint index, const char* name); - void glBindBuffer(GLenum target, GLuint buffer); - void glBindFramebuffer(GLenum target, GLuint framebuffer); - void glBindRenderbuffer(GLenum target, GLuint renderbuffer); - void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); - void glBlendEquation(GLenum mode); - void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha); - void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); - void glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage); - void glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data); - GLenum glCheckFramebufferStatus(GLenum target); - void glClearDepthf(GLclampf depth); - void glCompileShader(GLuint shader); - void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); - void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); - GLuint glCreateProgram(); - GLuint glCreateShader(GLenum type); - void glDeleteBuffers(GLsizei n, const GLuint* buffers); - void glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers); - void glDeleteProgram(GLuint program); - void glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); - void glDeleteShader(GLuint shader); - void glDepthRangef(GLclampf zNear, GLclampf zFar); - void glDetachShader(GLuint program, GLuint shader); - void glDisableVertexAttribArray(GLuint index); - void glEnableVertexAttribArray(GLuint index); - void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); - void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); - void glGenBuffers(GLsizei n, GLuint* buffers); - void glGenerateMipmap(GLenum target); - void glGenFramebuffers(GLsizei n, GLuint* framebuffers); - void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers); - void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); - void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); - void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); - int glGetAttribLocation(GLuint program, const char* name); - void glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params); - void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params); - void glGetProgramiv(GLuint program, GLenum pname, GLint* params); - void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); - void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params); - void glGetShaderiv(GLuint shader, GLenum pname, GLint* params); - void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); - void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); - void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); - void glGetUniformfv(GLuint program, GLint location, GLfloat* params); - void glGetUniformiv(GLuint program, GLint location, GLint* params); - int glGetUniformLocation(GLuint program, const char* name); - void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); - void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params); - void glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer); - GLboolean glIsBuffer(GLuint buffer); - GLboolean glIsFramebuffer(GLuint framebuffer); - GLboolean glIsProgram(GLuint program); - GLboolean glIsRenderbuffer(GLuint renderbuffer); - GLboolean glIsShader(GLuint shader); - void glLinkProgram(GLuint program); - void glReleaseShaderCompiler(); - void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); - void glSampleCoverage(GLclampf value, GLboolean invert); - void glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); - void glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length); - void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); - void glStencilMaskSeparate(GLenum face, GLuint mask); - void glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); - void glUniform1f(GLint location, GLfloat x); - void glUniform1fv(GLint location, GLsizei count, const GLfloat* v); - void glUniform1i(GLint location, GLint x); - void glUniform1iv(GLint location, GLsizei count, const GLint* v); - void glUniform2f(GLint location, GLfloat x, GLfloat y); - void glUniform2fv(GLint location, GLsizei count, const GLfloat* v); - void glUniform2i(GLint location, GLint x, GLint y); - void glUniform2iv(GLint location, GLsizei count, const GLint* v); - void glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z); - void glUniform3fv(GLint location, GLsizei count, const GLfloat* v); - void glUniform3i(GLint location, GLint x, GLint y, GLint z); - void glUniform3iv(GLint location, GLsizei count, const GLint* v); - void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); - void glUniform4fv(GLint location, GLsizei count, const GLfloat* v); - void glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w); - void glUniform4iv(GLint location, GLsizei count, const GLint* v); - void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); - void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); - void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); - void glUseProgram(GLuint program); - void glValidateProgram(GLuint program); - void glVertexAttrib1f(GLuint indx, GLfloat x); - void glVertexAttrib1fv(GLuint indx, const GLfloat* values); - void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y); - void glVertexAttrib2fv(GLuint indx, const GLfloat* values); - void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z); - void glVertexAttrib3fv(GLuint indx, const GLfloat* values); - void glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); - void glVertexAttrib4fv(GLuint indx, const GLfloat* values); - void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); - -private: - QGLFunctionsPrivate *d_ptr; - static bool isInitialized(const QGLFunctionsPrivate *d) { return d != nullptr; } -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QGLFunctions::OpenGLFeatures) - -struct QGLFunctionsPrivate -{ - QGLFunctionsPrivate(const QGLContext *context = nullptr); - QOpenGLFunctions *funcs; -}; - -inline void QGLFunctions::glActiveTexture(GLenum texture) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glActiveTexture(texture); -} - -inline void QGLFunctions::glAttachShader(GLuint program, GLuint shader) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glAttachShader(program, shader); -} - -inline void QGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBindAttribLocation(program, index, name); -} - -inline void QGLFunctions::glBindBuffer(GLenum target, GLuint buffer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBindBuffer(target, buffer); -} - -inline void QGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) -{ - if (framebuffer == 0) - framebuffer = QOpenGLContext::currentContext()->defaultFramebufferObject(); - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBindFramebuffer(target, framebuffer); -} - -inline void QGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBindRenderbuffer(target, renderbuffer); -} - -inline void QGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBlendColor(red, green, blue, alpha); -} - -inline void QGLFunctions::glBlendEquation(GLenum mode) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBlendEquation(mode); -} - -inline void QGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBlendEquationSeparate(modeRGB, modeAlpha); -} - -inline void QGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); -} - -inline void QGLFunctions::glBufferData(GLenum target, qopengl_GLsizeiptr size, const void* data, GLenum usage) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBufferData(target, size, data, usage); -} - -inline void QGLFunctions::glBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, const void* data) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glBufferSubData(target, offset, size, data); -} - -inline GLenum QGLFunctions::glCheckFramebufferStatus(GLenum target) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glCheckFramebufferStatus(target); -} - -inline void QGLFunctions::glClearDepthf(GLclampf depth) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glClearDepthf(depth); -} - -inline void QGLFunctions::glCompileShader(GLuint shader) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glCompileShader(shader); -} - -inline void QGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); -} - -inline void QGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); -} - -inline GLuint QGLFunctions::glCreateProgram() -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glCreateProgram(); -} - -inline GLuint QGLFunctions::glCreateShader(GLenum type) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glCreateShader(type); -} - -inline void QGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDeleteBuffers(n, buffers); -} - -inline void QGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDeleteFramebuffers(n, framebuffers); -} - -inline void QGLFunctions::glDeleteProgram(GLuint program) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDeleteProgram(program); -} - -inline void QGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDeleteRenderbuffers(n, renderbuffers); -} - -inline void QGLFunctions::glDeleteShader(GLuint shader) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDeleteShader(shader); -} - -inline void QGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glDepthRangef(zNear, zFar); -} - -inline void QGLFunctions::glDetachShader(GLuint program, GLuint shader) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDetachShader(program, shader); -} - -inline void QGLFunctions::glDisableVertexAttribArray(GLuint index) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glDisableVertexAttribArray(index); -} - -inline void QGLFunctions::glEnableVertexAttribArray(GLuint index) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glEnableVertexAttribArray(index); -} - -inline void QGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); -} - -inline void QGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glFramebufferTexture2D(target, attachment, textarget, texture, level); -} - -inline void QGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGenBuffers(n, buffers); -} - -inline void QGLFunctions::glGenerateMipmap(GLenum target) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGenerateMipmap(target); -} - -inline void QGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGenFramebuffers(n, framebuffers); -} - -inline void QGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGenRenderbuffers(n, renderbuffers); -} - -inline void QGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetActiveAttrib(program, index, bufsize, length, size, type, name); -} - -inline void QGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetActiveUniform(program, index, bufsize, length, size, type, name); -} - -inline void QGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetAttachedShaders(program, maxcount, count, shaders); -} - -inline int QGLFunctions::glGetAttribLocation(GLuint program, const char* name) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glGetAttribLocation(program, name); -} - -inline void QGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetBufferParameteriv(target, pname, params); -} - -inline void QGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); -} - -inline void QGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetProgramiv(program, pname, params); -} - -inline void QGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetProgramInfoLog(program, bufsize, length, infolog); -} - -inline void QGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetRenderbufferParameteriv(target, pname, params); -} - -inline void QGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetShaderiv(shader, pname, params); -} - -inline void QGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetShaderInfoLog(shader, bufsize, length, infolog); -} - -inline void QGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); -} - -inline void QGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetShaderSource(shader, bufsize, length, source); -} - -inline void QGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetUniformfv(program, location, params); -} - -inline void QGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetUniformiv(program, location, params); -} - -inline int QGLFunctions::glGetUniformLocation(GLuint program, const char* name) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glGetUniformLocation(program, name); -} - -inline void QGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetVertexAttribfv(index, pname, params); -} - -inline void QGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetVertexAttribiv(index, pname, params); -} - -inline void QGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glGetVertexAttribPointerv(index, pname, pointer); -} - -inline GLboolean QGLFunctions::glIsBuffer(GLuint buffer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glIsBuffer(buffer); -} - -inline GLboolean QGLFunctions::glIsFramebuffer(GLuint framebuffer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glIsFramebuffer(framebuffer); -} - -inline GLboolean QGLFunctions::glIsProgram(GLuint program) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glIsProgram(program); -} - -inline GLboolean QGLFunctions::glIsRenderbuffer(GLuint renderbuffer) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glIsRenderbuffer(renderbuffer); -} - -inline GLboolean QGLFunctions::glIsShader(GLuint shader) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - return d_ptr->funcs->glIsShader(shader); -} - -inline void QGLFunctions::glLinkProgram(GLuint program) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glLinkProgram(program); -} - -inline void QGLFunctions::glReleaseShaderCompiler() -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glReleaseShaderCompiler(); -} - -inline void QGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glRenderbufferStorage(target, internalformat, width, height); -} - -inline void QGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glSampleCoverage(value, invert); -} - -inline void QGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glShaderBinary(n, shaders, binaryformat, binary, length); -} - -inline void QGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glShaderSource(shader, count, string, length); -} - -inline void QGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glStencilFuncSeparate(face, func, ref, mask); -} - -inline void QGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glStencilMaskSeparate(face, mask); -} - -inline void QGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glStencilOpSeparate(face, fail, zfail, zpass); -} - -inline void QGLFunctions::glUniform1f(GLint location, GLfloat x) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform1f(location, x); -} - -inline void QGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform1fv(location, count, v); -} - -inline void QGLFunctions::glUniform1i(GLint location, GLint x) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform1i(location, x); -} - -inline void QGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform1iv(location, count, v); -} - -inline void QGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform2f(location, x, y); -} - -inline void QGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform2fv(location, count, v); -} - -inline void QGLFunctions::glUniform2i(GLint location, GLint x, GLint y) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform2i(location, x, y); -} - -inline void QGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform2iv(location, count, v); -} - -inline void QGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform3f(location, x, y, z); -} - -inline void QGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform3fv(location, count, v); -} - -inline void QGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform3i(location, x, y, z); -} - -inline void QGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform3iv(location, count, v); -} - -inline void QGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform4f(location, x, y, z, w); -} - -inline void QGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform4fv(location, count, v); -} - -inline void QGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform4i(location, x, y, z, w); -} - -inline void QGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniform4iv(location, count, v); -} - -inline void QGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniformMatrix2fv(location, count, transpose, value); -} - -inline void QGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniformMatrix3fv(location, count, transpose, value); -} - -inline void QGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUniformMatrix4fv(location, count, transpose, value); -} - -inline void QGLFunctions::glUseProgram(GLuint program) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glUseProgram(program); -} - -inline void QGLFunctions::glValidateProgram(GLuint program) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glValidateProgram(program); -} - -inline void QGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib1f(indx, x); -} - -inline void QGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib1fv(indx, values); -} - -inline void QGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib2f(indx, x, y); -} - -inline void QGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib2fv(indx, values); -} - -inline void QGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib3f(indx, x, y, z); -} - -inline void QGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib3fv(indx, values); -} - -inline void QGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib4f(indx, x, y, z, w); -} - -inline void QGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttrib4fv(indx, values); -} - -inline void QGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) -{ - Q_ASSERT(QGLFunctions::isInitialized(d_ptr)); - d_ptr->funcs->glVertexAttribPointer(indx, size, type, normalized, stride, ptr); -} - -#ifndef GL_ACTIVE_ATTRIBUTE_MAX_LENGTH -#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A -#endif -#ifndef GL_ACTIVE_ATTRIBUTES -#define GL_ACTIVE_ATTRIBUTES 0x8B89 -#endif -#ifndef GL_ACTIVE_TEXTURE -#define GL_ACTIVE_TEXTURE 0x84E0 -#endif -#ifndef GL_ACTIVE_UNIFORM_MAX_LENGTH -#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 -#endif -#ifndef GL_ACTIVE_UNIFORMS -#define GL_ACTIVE_UNIFORMS 0x8B86 -#endif -#ifndef GL_ALIASED_LINE_WIDTH_RANGE -#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E -#endif -#ifndef GL_ALIASED_POINT_SIZE_RANGE -#define GL_ALIASED_POINT_SIZE_RANGE 0x846D -#endif -#ifndef GL_ALPHA -#define GL_ALPHA 0x1906 -#endif -#ifndef GL_ALPHA_BITS -#define GL_ALPHA_BITS 0x0D55 -#endif -#ifndef GL_ALWAYS -#define GL_ALWAYS 0x0207 -#endif -#ifndef GL_ARRAY_BUFFER -#define GL_ARRAY_BUFFER 0x8892 -#endif -#ifndef GL_ARRAY_BUFFER_BINDING -#define GL_ARRAY_BUFFER_BINDING 0x8894 -#endif -#ifndef GL_ATTACHED_SHADERS -#define GL_ATTACHED_SHADERS 0x8B85 -#endif -#ifndef GL_BACK -#define GL_BACK 0x0405 -#endif -#ifndef GL_BLEND -#define GL_BLEND 0x0BE2 -#endif -#ifndef GL_BLEND_COLOR -#define GL_BLEND_COLOR 0x8005 -#endif -#ifndef GL_BLEND_DST_ALPHA -#define GL_BLEND_DST_ALPHA 0x80CA -#endif -#ifndef GL_BLEND_DST_RGB -#define GL_BLEND_DST_RGB 0x80C8 -#endif -#ifndef GL_BLEND_EQUATION -#define GL_BLEND_EQUATION 0x8009 -#endif -#ifndef GL_BLEND_EQUATION_ALPHA -#define GL_BLEND_EQUATION_ALPHA 0x883D -#endif -#ifndef GL_BLEND_EQUATION_RGB -#define GL_BLEND_EQUATION_RGB 0x8009 -#endif -#ifndef GL_BLEND_SRC_ALPHA -#define GL_BLEND_SRC_ALPHA 0x80CB -#endif -#ifndef GL_BLEND_SRC_RGB -#define GL_BLEND_SRC_RGB 0x80C9 -#endif -#ifndef GL_BLUE_BITS -#define GL_BLUE_BITS 0x0D54 -#endif -#ifndef GL_BOOL -#define GL_BOOL 0x8B56 -#endif -#ifndef GL_BOOL_VEC2 -#define GL_BOOL_VEC2 0x8B57 -#endif -#ifndef GL_BOOL_VEC3 -#define GL_BOOL_VEC3 0x8B58 -#endif -#ifndef GL_BOOL_VEC4 -#define GL_BOOL_VEC4 0x8B59 -#endif -#ifndef GL_BUFFER_SIZE -#define GL_BUFFER_SIZE 0x8764 -#endif -#ifndef GL_BUFFER_USAGE -#define GL_BUFFER_USAGE 0x8765 -#endif -#ifndef GL_BYTE -#define GL_BYTE 0x1400 -#endif -#ifndef GL_CCW -#define GL_CCW 0x0901 -#endif -#ifndef GL_CLAMP_TO_EDGE -#define GL_CLAMP_TO_EDGE 0x812F -#endif -#ifndef GL_COLOR_ATTACHMENT0 -#define GL_COLOR_ATTACHMENT0 0x8CE0 -#endif -#ifndef GL_COLOR_BUFFER_BIT -#define GL_COLOR_BUFFER_BIT 0x00004000 -#endif -#ifndef GL_COLOR_CLEAR_VALUE -#define GL_COLOR_CLEAR_VALUE 0x0C22 -#endif -#ifndef GL_COLOR_WRITEMASK -#define GL_COLOR_WRITEMASK 0x0C23 -#endif -#ifndef GL_COMPILE_STATUS -#define GL_COMPILE_STATUS 0x8B81 -#endif -#ifndef GL_COMPRESSED_TEXTURE_FORMATS -#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 -#endif -#ifndef GL_CONSTANT_ALPHA -#define GL_CONSTANT_ALPHA 0x8003 -#endif -#ifndef GL_CONSTANT_COLOR -#define GL_CONSTANT_COLOR 0x8001 -#endif -#ifndef GL_CULL_FACE -#define GL_CULL_FACE 0x0B44 -#endif -#ifndef GL_CULL_FACE_MODE -#define GL_CULL_FACE_MODE 0x0B45 -#endif -#ifndef GL_CURRENT_PROGRAM -#define GL_CURRENT_PROGRAM 0x8B8D -#endif -#ifndef GL_CURRENT_VERTEX_ATTRIB -#define GL_CURRENT_VERTEX_ATTRIB 0x8626 -#endif -#ifndef GL_CW -#define GL_CW 0x0900 -#endif -#ifndef GL_DECR -#define GL_DECR 0x1E03 -#endif -#ifndef GL_DECR_WRAP -#define GL_DECR_WRAP 0x8508 -#endif -#ifndef GL_DELETE_STATUS -#define GL_DELETE_STATUS 0x8B80 -#endif -#ifndef GL_DEPTH_ATTACHMENT -#define GL_DEPTH_ATTACHMENT 0x8D00 -#endif -#ifndef GL_DEPTH_BITS -#define GL_DEPTH_BITS 0x0D56 -#endif -#ifndef GL_DEPTH_BUFFER_BIT -#define GL_DEPTH_BUFFER_BIT 0x00000100 -#endif -#ifndef GL_DEPTH_CLEAR_VALUE -#define GL_DEPTH_CLEAR_VALUE 0x0B73 -#endif -#ifndef GL_DEPTH_COMPONENT -#define GL_DEPTH_COMPONENT 0x1902 -#endif -#ifndef GL_DEPTH_COMPONENT16 -#define GL_DEPTH_COMPONENT16 0x81A5 -#endif -#ifndef GL_DEPTH_FUNC -#define GL_DEPTH_FUNC 0x0B74 -#endif -#ifndef GL_DEPTH_RANGE -#define GL_DEPTH_RANGE 0x0B70 -#endif -#ifndef GL_DEPTH_TEST -#define GL_DEPTH_TEST 0x0B71 -#endif -#ifndef GL_DEPTH_WRITEMASK -#define GL_DEPTH_WRITEMASK 0x0B72 -#endif -#ifndef GL_DITHER -#define GL_DITHER 0x0BD0 -#endif -#ifndef GL_DONT_CARE -#define GL_DONT_CARE 0x1100 -#endif -#ifndef GL_DST_ALPHA -#define GL_DST_ALPHA 0x0304 -#endif -#ifndef GL_DST_COLOR -#define GL_DST_COLOR 0x0306 -#endif -#ifndef GL_DYNAMIC_DRAW -#define GL_DYNAMIC_DRAW 0x88E8 -#endif -#ifndef GL_ELEMENT_ARRAY_BUFFER -#define GL_ELEMENT_ARRAY_BUFFER 0x8893 -#endif -#ifndef GL_ELEMENT_ARRAY_BUFFER_BINDING -#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 -#endif -#ifndef GL_EQUAL -#define GL_EQUAL 0x0202 -#endif -#ifndef GL_EXTENSIONS -#define GL_EXTENSIONS 0x1F03 -#endif -#ifndef GL_FALSE -#define GL_FALSE 0 -#endif -#ifndef GL_FASTEST -#define GL_FASTEST 0x1101 -#endif -#ifndef GL_FIXED -#define GL_FIXED 0x140C -#endif -#ifndef GL_FLOAT -#define GL_FLOAT 0x1406 -#endif -#ifndef GL_FLOAT_MAT2 -#define GL_FLOAT_MAT2 0x8B5A -#endif -#ifndef GL_FLOAT_MAT3 -#define GL_FLOAT_MAT3 0x8B5B -#endif -#ifndef GL_FLOAT_MAT4 -#define GL_FLOAT_MAT4 0x8B5C -#endif -#ifndef GL_FLOAT_VEC2 -#define GL_FLOAT_VEC2 0x8B50 -#endif -#ifndef GL_FLOAT_VEC3 -#define GL_FLOAT_VEC3 0x8B51 -#endif -#ifndef GL_FLOAT_VEC4 -#define GL_FLOAT_VEC4 0x8B52 -#endif -#ifndef GL_FRAGMENT_SHADER -#define GL_FRAGMENT_SHADER 0x8B30 -#endif -#ifndef GL_FRAMEBUFFER -#define GL_FRAMEBUFFER 0x8D40 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 -#endif -#ifndef GL_FRAMEBUFFER_BINDING -#define GL_FRAMEBUFFER_BINDING 0x8CA6 -#endif -#ifndef GL_FRAMEBUFFER_COMPLETE -#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 -#endif -#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 -#endif -#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 -#endif -#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 -#endif -#ifndef GL_FRAMEBUFFER_UNSUPPORTED -#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD -#endif -#ifndef GL_FRONT -#define GL_FRONT 0x0404 -#endif -#ifndef GL_FRONT_AND_BACK -#define GL_FRONT_AND_BACK 0x0408 -#endif -#ifndef GL_FRONT_FACE -#define GL_FRONT_FACE 0x0B46 -#endif -#ifndef GL_FUNC_ADD -#define GL_FUNC_ADD 0x8006 -#endif -#ifndef GL_FUNC_REVERSE_SUBTRACT -#define GL_FUNC_REVERSE_SUBTRACT 0x800B -#endif -#ifndef GL_FUNC_SUBTRACT -#define GL_FUNC_SUBTRACT 0x800A -#endif -#ifndef GL_GENERATE_MIPMAP_HINT -#define GL_GENERATE_MIPMAP_HINT 0x8192 -#endif -#ifndef GL_GEQUAL -#define GL_GEQUAL 0x0206 -#endif -#ifndef GL_GREATER -#define GL_GREATER 0x0204 -#endif -#ifndef GL_GREEN_BITS -#define GL_GREEN_BITS 0x0D53 -#endif -#ifndef GL_HIGH_FLOAT -#define GL_HIGH_FLOAT 0x8DF2 -#endif -#ifndef GL_HIGH_INT -#define GL_HIGH_INT 0x8DF5 -#endif -#ifndef GL_IMPLEMENTATION_COLOR_READ_FORMAT -#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B -#endif -#ifndef GL_IMPLEMENTATION_COLOR_READ_TYPE -#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A -#endif -#ifndef GL_INCR -#define GL_INCR 0x1E02 -#endif -#ifndef GL_INCR_WRAP -#define GL_INCR_WRAP 0x8507 -#endif -#ifndef GL_INFO_LOG_LENGTH -#define GL_INFO_LOG_LENGTH 0x8B84 -#endif -#ifndef GL_INT -#define GL_INT 0x1404 -#endif -#ifndef GL_INT_VEC2 -#define GL_INT_VEC2 0x8B53 -#endif -#ifndef GL_INT_VEC3 -#define GL_INT_VEC3 0x8B54 -#endif -#ifndef GL_INT_VEC4 -#define GL_INT_VEC4 0x8B55 -#endif -#ifndef GL_INVALID_ENUM -#define GL_INVALID_ENUM 0x0500 -#endif -#ifndef GL_INVALID_FRAMEBUFFER_OPERATION -#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 -#endif -#ifndef GL_INVALID_OPERATION -#define GL_INVALID_OPERATION 0x0502 -#endif -#ifndef GL_INVALID_VALUE -#define GL_INVALID_VALUE 0x0501 -#endif -#ifndef GL_INVERT -#define GL_INVERT 0x150A -#endif -#ifndef GL_KEEP -#define GL_KEEP 0x1E00 -#endif -#ifndef GL_LEQUAL -#define GL_LEQUAL 0x0203 -#endif -#ifndef GL_LESS -#define GL_LESS 0x0201 -#endif -#ifndef GL_LINEAR -#define GL_LINEAR 0x2601 -#endif -#ifndef GL_LINEAR_MIPMAP_LINEAR -#define GL_LINEAR_MIPMAP_LINEAR 0x2703 -#endif -#ifndef GL_LINEAR_MIPMAP_NEAREST -#define GL_LINEAR_MIPMAP_NEAREST 0x2701 -#endif -#ifndef GL_LINE_LOOP -#define GL_LINE_LOOP 0x0002 -#endif -#ifndef GL_LINES -#define GL_LINES 0x0001 -#endif -#ifndef GL_LINE_STRIP -#define GL_LINE_STRIP 0x0003 -#endif -#ifndef GL_LINE_WIDTH -#define GL_LINE_WIDTH 0x0B21 -#endif -#ifndef GL_LINK_STATUS -#define GL_LINK_STATUS 0x8B82 -#endif -#ifndef GL_LOW_FLOAT -#define GL_LOW_FLOAT 0x8DF0 -#endif -#ifndef GL_LOW_INT -#define GL_LOW_INT 0x8DF3 -#endif -#ifndef GL_LUMINANCE -#define GL_LUMINANCE 0x1909 -#endif -#ifndef GL_LUMINANCE_ALPHA -#define GL_LUMINANCE_ALPHA 0x190A -#endif -#ifndef GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS -#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D -#endif -#ifndef GL_MAX_CUBE_MAP_TEXTURE_SIZE -#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C -#endif -#ifndef GL_MAX_FRAGMENT_UNIFORM_VECTORS -#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD -#endif -#ifndef GL_MAX_RENDERBUFFER_SIZE -#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 -#endif -#ifndef GL_MAX_TEXTURE_IMAGE_UNITS -#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 -#endif -#ifndef GL_MAX_TEXTURE_SIZE -#define GL_MAX_TEXTURE_SIZE 0x0D33 -#endif -#ifndef GL_MAX_VARYING_VECTORS -#define GL_MAX_VARYING_VECTORS 0x8DFC -#endif -#ifndef GL_MAX_VERTEX_ATTRIBS -#define GL_MAX_VERTEX_ATTRIBS 0x8869 -#endif -#ifndef GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS -#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C -#endif -#ifndef GL_MAX_VERTEX_UNIFORM_VECTORS -#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB -#endif -#ifndef GL_MAX_VIEWPORT_DIMS -#define GL_MAX_VIEWPORT_DIMS 0x0D3A -#endif -#ifndef GL_MEDIUM_FLOAT -#define GL_MEDIUM_FLOAT 0x8DF1 -#endif -#ifndef GL_MEDIUM_INT -#define GL_MEDIUM_INT 0x8DF4 -#endif -#ifndef GL_MIRRORED_REPEAT -#define GL_MIRRORED_REPEAT 0x8370 -#endif -#ifndef GL_NEAREST -#define GL_NEAREST 0x2600 -#endif -#ifndef GL_NEAREST_MIPMAP_LINEAR -#define GL_NEAREST_MIPMAP_LINEAR 0x2702 -#endif -#ifndef GL_NEAREST_MIPMAP_NEAREST -#define GL_NEAREST_MIPMAP_NEAREST 0x2700 -#endif -#ifndef GL_NEVER -#define GL_NEVER 0x0200 -#endif -#ifndef GL_NICEST -#define GL_NICEST 0x1102 -#endif -#ifndef GL_NO_ERROR -#define GL_NO_ERROR 0 -#endif -#ifndef GL_NONE -#define GL_NONE 0 -#endif -#ifndef GL_NOTEQUAL -#define GL_NOTEQUAL 0x0205 -#endif -#ifndef GL_NUM_COMPRESSED_TEXTURE_FORMATS -#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 -#endif -#ifndef GL_NUM_SHADER_BINARY_FORMATS -#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 -#endif -#ifndef GL_ONE -#define GL_ONE 1 -#endif -#ifndef GL_ONE_MINUS_CONSTANT_ALPHA -#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 -#endif -#ifndef GL_ONE_MINUS_CONSTANT_COLOR -#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 -#endif -#ifndef GL_ONE_MINUS_DST_ALPHA -#define GL_ONE_MINUS_DST_ALPHA 0x0305 -#endif -#ifndef GL_ONE_MINUS_DST_COLOR -#define GL_ONE_MINUS_DST_COLOR 0x0307 -#endif -#ifndef GL_ONE_MINUS_SRC_ALPHA -#define GL_ONE_MINUS_SRC_ALPHA 0x0303 -#endif -#ifndef GL_ONE_MINUS_SRC_COLOR -#define GL_ONE_MINUS_SRC_COLOR 0x0301 -#endif -#ifndef GL_OUT_OF_MEMORY -#define GL_OUT_OF_MEMORY 0x0505 -#endif -#ifndef GL_PACK_ALIGNMENT -#define GL_PACK_ALIGNMENT 0x0D05 -#endif -#ifndef GL_POINTS -#define GL_POINTS 0x0000 -#endif -#ifndef GL_POLYGON_OFFSET_FACTOR -#define GL_POLYGON_OFFSET_FACTOR 0x8038 -#endif -#ifndef GL_POLYGON_OFFSET_FILL -#define GL_POLYGON_OFFSET_FILL 0x8037 -#endif -#ifndef GL_POLYGON_OFFSET_UNITS -#define GL_POLYGON_OFFSET_UNITS 0x2A00 -#endif -#ifndef GL_RED_BITS -#define GL_RED_BITS 0x0D52 -#endif -#ifndef GL_RENDERBUFFER -#define GL_RENDERBUFFER 0x8D41 -#endif -#ifndef GL_RENDERBUFFER_ALPHA_SIZE -#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 -#endif -#ifndef GL_RENDERBUFFER_BINDING -#define GL_RENDERBUFFER_BINDING 0x8CA7 -#endif -#ifndef GL_RENDERBUFFER_BLUE_SIZE -#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 -#endif -#ifndef GL_RENDERBUFFER_DEPTH_SIZE -#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 -#endif -#ifndef GL_RENDERBUFFER_GREEN_SIZE -#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 -#endif -#ifndef GL_RENDERBUFFER_HEIGHT -#define GL_RENDERBUFFER_HEIGHT 0x8D43 -#endif -#ifndef GL_RENDERBUFFER_INTERNAL_FORMAT -#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 -#endif -#ifndef GL_RENDERBUFFER_RED_SIZE -#define GL_RENDERBUFFER_RED_SIZE 0x8D50 -#endif -#ifndef GL_RENDERBUFFER_STENCIL_SIZE -#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 -#endif -#ifndef GL_RENDERBUFFER_WIDTH -#define GL_RENDERBUFFER_WIDTH 0x8D42 -#endif -#ifndef GL_RENDERER -#define GL_RENDERER 0x1F01 -#endif -#ifndef GL_REPEAT -#define GL_REPEAT 0x2901 -#endif -#ifndef GL_REPLACE -#define GL_REPLACE 0x1E01 -#endif -#ifndef GL_RGB -#define GL_RGB 0x1907 -#endif -#ifndef GL_RGB565 -#define GL_RGB565 0x8D62 -#endif -#ifndef GL_RGB5_A1 -#define GL_RGB5_A1 0x8057 -#endif -#ifndef GL_RGBA -#define GL_RGBA 0x1908 -#endif -#ifndef GL_RGBA4 -#define GL_RGBA4 0x8056 -#endif -#ifndef GL_BGRA -#define GL_BGRA 0x80E1 -#endif -#ifndef GL_SAMPLE_ALPHA_TO_COVERAGE -#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E -#endif -#ifndef GL_SAMPLE_BUFFERS -#define GL_SAMPLE_BUFFERS 0x80A8 -#endif -#ifndef GL_SAMPLE_COVERAGE -#define GL_SAMPLE_COVERAGE 0x80A0 -#endif -#ifndef GL_SAMPLE_COVERAGE_INVERT -#define GL_SAMPLE_COVERAGE_INVERT 0x80AB -#endif -#ifndef GL_SAMPLE_COVERAGE_VALUE -#define GL_SAMPLE_COVERAGE_VALUE 0x80AA -#endif -#ifndef GL_SAMPLER_2D -#define GL_SAMPLER_2D 0x8B5E -#endif -#ifndef GL_SAMPLER_CUBE -#define GL_SAMPLER_CUBE 0x8B60 -#endif -#ifndef GL_SAMPLES -#define GL_SAMPLES 0x80A9 -#endif -#ifndef GL_SCISSOR_BOX -#define GL_SCISSOR_BOX 0x0C10 -#endif -#ifndef GL_SCISSOR_TEST -#define GL_SCISSOR_TEST 0x0C11 -#endif -#ifndef GL_SHADER_BINARY_FORMATS -#define GL_SHADER_BINARY_FORMATS 0x8DF8 -#endif -#ifndef GL_SHADER_COMPILER -#define GL_SHADER_COMPILER 0x8DFA -#endif -#ifndef GL_SHADER_SOURCE_LENGTH -#define GL_SHADER_SOURCE_LENGTH 0x8B88 -#endif -#ifndef GL_SHADER_TYPE -#define GL_SHADER_TYPE 0x8B4F -#endif -#ifndef GL_SHADING_LANGUAGE_VERSION -#define GL_SHADING_LANGUAGE_VERSION 0x8B8C -#endif -#ifndef GL_SHORT -#define GL_SHORT 0x1402 -#endif -#ifndef GL_SRC_ALPHA -#define GL_SRC_ALPHA 0x0302 -#endif -#ifndef GL_SRC_ALPHA_SATURATE -#define GL_SRC_ALPHA_SATURATE 0x0308 -#endif -#ifndef GL_SRC_COLOR -#define GL_SRC_COLOR 0x0300 -#endif -#ifndef GL_STATIC_DRAW -#define GL_STATIC_DRAW 0x88E4 -#endif -#ifndef GL_STENCIL_ATTACHMENT -#define GL_STENCIL_ATTACHMENT 0x8D20 -#endif -#ifndef GL_STENCIL_BACK_FAIL -#define GL_STENCIL_BACK_FAIL 0x8801 -#endif -#ifndef GL_STENCIL_BACK_FUNC -#define GL_STENCIL_BACK_FUNC 0x8800 -#endif -#ifndef GL_STENCIL_BACK_PASS_DEPTH_FAIL -#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 -#endif -#ifndef GL_STENCIL_BACK_PASS_DEPTH_PASS -#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 -#endif -#ifndef GL_STENCIL_BACK_REF -#define GL_STENCIL_BACK_REF 0x8CA3 -#endif -#ifndef GL_STENCIL_BACK_VALUE_MASK -#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 -#endif -#ifndef GL_STENCIL_BACK_WRITEMASK -#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 -#endif -#ifndef GL_STENCIL_BITS -#define GL_STENCIL_BITS 0x0D57 -#endif -#ifndef GL_STENCIL_BUFFER_BIT -#define GL_STENCIL_BUFFER_BIT 0x00000400 -#endif -#ifndef GL_STENCIL_CLEAR_VALUE -#define GL_STENCIL_CLEAR_VALUE 0x0B91 -#endif -#ifndef GL_STENCIL_FAIL -#define GL_STENCIL_FAIL 0x0B94 -#endif -#ifndef GL_STENCIL_FUNC -#define GL_STENCIL_FUNC 0x0B92 -#endif -#ifndef GL_STENCIL_INDEX -#define GL_STENCIL_INDEX 0x1901 -#endif -#ifndef GL_STENCIL_INDEX8 -#define GL_STENCIL_INDEX8 0x8D48 -#endif -#ifndef GL_STENCIL_PASS_DEPTH_FAIL -#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 -#endif -#ifndef GL_STENCIL_PASS_DEPTH_PASS -#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 -#endif -#ifndef GL_STENCIL_REF -#define GL_STENCIL_REF 0x0B97 -#endif -#ifndef GL_STENCIL_TEST -#define GL_STENCIL_TEST 0x0B90 -#endif -#ifndef GL_STENCIL_VALUE_MASK -#define GL_STENCIL_VALUE_MASK 0x0B93 -#endif -#ifndef GL_STENCIL_WRITEMASK -#define GL_STENCIL_WRITEMASK 0x0B98 -#endif -#ifndef GL_STREAM_DRAW -#define GL_STREAM_DRAW 0x88E0 -#endif -#ifndef GL_SUBPIXEL_BITS -#define GL_SUBPIXEL_BITS 0x0D50 -#endif -#ifndef GL_TEXTURE0 -#define GL_TEXTURE0 0x84C0 -#endif -#ifndef GL_TEXTURE -#define GL_TEXTURE 0x1702 -#endif -#ifndef GL_TEXTURE10 -#define GL_TEXTURE10 0x84CA -#endif -#ifndef GL_TEXTURE1 -#define GL_TEXTURE1 0x84C1 -#endif -#ifndef GL_TEXTURE11 -#define GL_TEXTURE11 0x84CB -#endif -#ifndef GL_TEXTURE12 -#define GL_TEXTURE12 0x84CC -#endif -#ifndef GL_TEXTURE13 -#define GL_TEXTURE13 0x84CD -#endif -#ifndef GL_TEXTURE14 -#define GL_TEXTURE14 0x84CE -#endif -#ifndef GL_TEXTURE15 -#define GL_TEXTURE15 0x84CF -#endif -#ifndef GL_TEXTURE16 -#define GL_TEXTURE16 0x84D0 -#endif -#ifndef GL_TEXTURE17 -#define GL_TEXTURE17 0x84D1 -#endif -#ifndef GL_TEXTURE18 -#define GL_TEXTURE18 0x84D2 -#endif -#ifndef GL_TEXTURE19 -#define GL_TEXTURE19 0x84D3 -#endif -#ifndef GL_TEXTURE20 -#define GL_TEXTURE20 0x84D4 -#endif -#ifndef GL_TEXTURE2 -#define GL_TEXTURE2 0x84C2 -#endif -#ifndef GL_TEXTURE21 -#define GL_TEXTURE21 0x84D5 -#endif -#ifndef GL_TEXTURE22 -#define GL_TEXTURE22 0x84D6 -#endif -#ifndef GL_TEXTURE23 -#define GL_TEXTURE23 0x84D7 -#endif -#ifndef GL_TEXTURE24 -#define GL_TEXTURE24 0x84D8 -#endif -#ifndef GL_TEXTURE25 -#define GL_TEXTURE25 0x84D9 -#endif -#ifndef GL_TEXTURE26 -#define GL_TEXTURE26 0x84DA -#endif -#ifndef GL_TEXTURE27 -#define GL_TEXTURE27 0x84DB -#endif -#ifndef GL_TEXTURE28 -#define GL_TEXTURE28 0x84DC -#endif -#ifndef GL_TEXTURE29 -#define GL_TEXTURE29 0x84DD -#endif -#ifndef GL_TEXTURE_2D -#define GL_TEXTURE_2D 0x0DE1 -#endif -#ifndef GL_TEXTURE30 -#define GL_TEXTURE30 0x84DE -#endif -#ifndef GL_TEXTURE3 -#define GL_TEXTURE3 0x84C3 -#endif -#ifndef GL_TEXTURE31 -#define GL_TEXTURE31 0x84DF -#endif -#ifndef GL_TEXTURE4 -#define GL_TEXTURE4 0x84C4 -#endif -#ifndef GL_TEXTURE5 -#define GL_TEXTURE5 0x84C5 -#endif -#ifndef GL_TEXTURE6 -#define GL_TEXTURE6 0x84C6 -#endif -#ifndef GL_TEXTURE7 -#define GL_TEXTURE7 0x84C7 -#endif -#ifndef GL_TEXTURE8 -#define GL_TEXTURE8 0x84C8 -#endif -#ifndef GL_TEXTURE9 -#define GL_TEXTURE9 0x84C9 -#endif -#ifndef GL_TEXTURE_BINDING_2D -#define GL_TEXTURE_BINDING_2D 0x8069 -#endif -#ifndef GL_TEXTURE_BINDING_CUBE_MAP -#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 -#endif -#ifndef GL_TEXTURE_CUBE_MAP -#define GL_TEXTURE_CUBE_MAP 0x8513 -#endif -#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_X -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 -#endif -#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Y -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 -#endif -#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Z -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A -#endif -#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_X -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 -#endif -#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_Y -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 -#endif -#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_Z -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 -#endif -#ifndef GL_TEXTURE_MAG_FILTER -#define GL_TEXTURE_MAG_FILTER 0x2800 -#endif -#ifndef GL_TEXTURE_MIN_FILTER -#define GL_TEXTURE_MIN_FILTER 0x2801 -#endif -#ifndef GL_TEXTURE_WRAP_S -#define GL_TEXTURE_WRAP_S 0x2802 -#endif -#ifndef GL_TEXTURE_WRAP_T -#define GL_TEXTURE_WRAP_T 0x2803 -#endif -#ifndef GL_TRIANGLE_FAN -#define GL_TRIANGLE_FAN 0x0006 -#endif -#ifndef GL_TRIANGLES -#define GL_TRIANGLES 0x0004 -#endif -#ifndef GL_TRIANGLE_STRIP -#define GL_TRIANGLE_STRIP 0x0005 -#endif -#ifndef GL_TRUE -#define GL_TRUE 1 -#endif -#ifndef GL_UNPACK_ALIGNMENT -#define GL_UNPACK_ALIGNMENT 0x0CF5 -#endif -#ifndef GL_UNSIGNED_BYTE -#define GL_UNSIGNED_BYTE 0x1401 -#endif -#ifndef GL_UNSIGNED_INT -#define GL_UNSIGNED_INT 0x1405 -#endif -#ifndef GL_UNSIGNED_SHORT -#define GL_UNSIGNED_SHORT 0x1403 -#endif -#ifndef GL_UNSIGNED_SHORT_4_4_4_4 -#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 -#endif -#ifndef GL_UNSIGNED_SHORT_5_5_5_1 -#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 -#endif -#ifndef GL_UNSIGNED_SHORT_5_6_5 -#define GL_UNSIGNED_SHORT_5_6_5 0x8363 -#endif -#ifndef GL_VALIDATE_STATUS -#define GL_VALIDATE_STATUS 0x8B83 -#endif -#ifndef GL_VENDOR -#define GL_VENDOR 0x1F00 -#endif -#ifndef GL_VERSION -#define GL_VERSION 0x1F02 -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING -#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_ENABLED -#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_NORMALIZED -#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_POINTER -#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_SIZE -#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_STRIDE -#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 -#endif -#ifndef GL_VERTEX_ATTRIB_ARRAY_TYPE -#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 -#endif -#ifndef GL_VERTEX_SHADER -#define GL_VERTEX_SHADER 0x8B31 -#endif -#ifndef GL_VIEWPORT -#define GL_VIEWPORT 0x0BA2 -#endif -#ifndef GL_ZERO -#define GL_ZERO 0 -#endif - -QT_END_NAMESPACE - -#endif diff --git a/src/opengl/qglpaintdevice.cpp b/src/opengl/qglpaintdevice.cpp deleted file mode 100644 index c5151f66bb..0000000000 --- a/src/opengl/qglpaintdevice.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qglpaintdevice_p.h> -#include <private/qgl_p.h> -#include <private/qglpixelbuffer_p.h> -#include <private/qglframebufferobject_p.h> -#include <qopenglfunctions.h> -#include <qwindow.h> - -QT_BEGIN_NAMESPACE - -QGLPaintDevice::QGLPaintDevice() - : m_thisFBO(0) -{ -} - -QGLPaintDevice::~QGLPaintDevice() -{ -} - -int QGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const -{ - switch(metric) { - case PdmWidth: - return size().width(); - case PdmHeight: - return size().height(); - case PdmDepth: { - const QGLFormat f = format(); - return f.redBufferSize() + f.greenBufferSize() + f.blueBufferSize() + f.alphaBufferSize(); - } - case PdmDevicePixelRatio: - return 1; - case PdmDevicePixelRatioScaled: - return 1 * QPaintDevice::devicePixelRatioFScale(); - default: - qWarning("QGLPaintDevice::metric() - metric %d not known", metric); - return 0; - } -} - -void QGLPaintDevice::beginPaint() -{ - // Make sure our context is the current one: - QGLContext *ctx = context(); - ctx->makeCurrent(); - - ctx->d_func()->refreshCurrentFbo(); - - // Record the currently bound FBO so we can restore it again - // in endPaint() and bind this device's FBO - // - // Note: m_thisFBO could be zero if the paint device is not - // backed by an FBO (e.g. window back buffer). But there could - // be a previous FBO bound to the context which we need to - // explicitly unbind. Otherwise the painting will go into - // the previous FBO instead of to the window. - m_previousFBO = ctx->d_func()->current_fbo; - - if (m_previousFBO != m_thisFBO) { - ctx->d_func()->setCurrentFbo(m_thisFBO); - ctx->contextHandle()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO); - } - - // Set the default fbo for the context to m_thisFBO so that - // if some raw GL code between beginNativePainting() and - // endNativePainting() calls QGLFramebufferObject::release(), - // painting will revert to the window surface's fbo. - ctx->d_ptr->default_fbo = m_thisFBO; -} - -void QGLPaintDevice::ensureActiveTarget() -{ - QGLContext* ctx = context(); - if (ctx != QGLContext::currentContext()) - ctx->makeCurrent(); - - ctx->d_func()->refreshCurrentFbo(); - - if (ctx->d_ptr->current_fbo != m_thisFBO) { - ctx->d_func()->setCurrentFbo(m_thisFBO); - ctx->contextHandle()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO); - } - - ctx->d_ptr->default_fbo = m_thisFBO; -} - -void QGLPaintDevice::endPaint() -{ - // Make sure the FBO bound at beginPaint is re-bound again here: - QGLContext *ctx = context(); - ctx->makeCurrent(); - - ctx->d_func()->refreshCurrentFbo(); - - if (m_previousFBO != ctx->d_func()->current_fbo) { - ctx->d_func()->setCurrentFbo(m_previousFBO); - ctx->contextHandle()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_previousFBO); - } - - ctx->d_ptr->default_fbo = 0; -} - -QGLFormat QGLPaintDevice::format() const -{ - return context()->format(); -} - -bool QGLPaintDevice::alphaRequested() const -{ - return context()->d_func()->reqFormat.alpha(); -} - -bool QGLPaintDevice::isFlipped() const -{ - return false; -} - -////////////////// QGLWidgetGLPaintDevice ////////////////// - -QGLWidgetGLPaintDevice::QGLWidgetGLPaintDevice() -{ -} - -QPaintEngine* QGLWidgetGLPaintDevice::paintEngine() const -{ - return glWidget->paintEngine(); -} - -void QGLWidgetGLPaintDevice::setWidget(QGLWidget* w) -{ - glWidget = w; -} - -void QGLWidgetGLPaintDevice::beginPaint() -{ - QGLPaintDevice::beginPaint(); - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - if (!glWidget->d_func()->disable_clear_on_painter_begin && glWidget->autoFillBackground()) { - if (glWidget->testAttribute(Qt::WA_TranslucentBackground)) - funcs->glClearColor(0.0, 0.0, 0.0, 0.0); - else { - const QColor &c = glWidget->palette().brush(glWidget->backgroundRole()).color(); - float alpha = c.alphaF(); - funcs->glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha); - } - if (context()->d_func()->workaround_needsFullClearOnEveryFrame) - funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - else - funcs->glClear(GL_COLOR_BUFFER_BIT); - } -} - -void QGLWidgetGLPaintDevice::endPaint() -{ - if (glWidget->autoBufferSwap()) - glWidget->swapBuffers(); - QGLPaintDevice::endPaint(); -} - - -QSize QGLWidgetGLPaintDevice::size() const -{ - return glWidget->size() * (glWidget->windowHandle() ? - glWidget->windowHandle()->devicePixelRatio() : qApp->devicePixelRatio()); -} - -QGLContext* QGLWidgetGLPaintDevice::context() const -{ - return const_cast<QGLContext*>(glWidget->context()); -} - -// returns the QGLPaintDevice for the given QPaintDevice -QGLPaintDevice* QGLPaintDevice::getDevice(QPaintDevice* pd) -{ - QGLPaintDevice* glpd = 0; - - switch(pd->devType()) { - case QInternal::Widget: - // Should not be called on a non-gl widget: - Q_ASSERT(qobject_cast<QGLWidget*>(static_cast<QWidget*>(pd))); - glpd = &(static_cast<QGLWidget*>(pd)->d_func()->glDevice); - break; - case QInternal::Pbuffer: - glpd = &(static_cast<QGLPixelBuffer*>(pd)->d_func()->glDevice); - break; - case QInternal::FramebufferObject: - glpd = &(static_cast<QGLFramebufferObject*>(pd)->d_func()->glDevice); - break; - case QInternal::Pixmap: { - qWarning("Pixmap type not supported for GL rendering"); - break; - } - default: - qWarning("QGLPaintDevice::getDevice() - Unknown device type %d", pd->devType()); - break; - } - - return glpd; -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp deleted file mode 100644 index e2f434ac3c..0000000000 --- a/src/opengl/qglpixelbuffer.cpp +++ /dev/null @@ -1,654 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \class QGLPixelBuffer - \inmodule QtOpenGL - \brief The QGLPixelBuffer class encapsulates an OpenGL pbuffer. - \since 4.1 - \obsolete - - \ingroup painting-3D - - Rendering into a pbuffer is normally done using full hardware - acceleration. This can be significantly faster than rendering - into a QPixmap. - - There are three approaches to using this class: - - \list 1 - \li \b{We can draw into the pbuffer and convert it to a QImage - using toImage().} This is normally much faster than calling - QGLWidget::renderPixmap(). - - \li \b{We can draw into the pbuffer and copy the contents into - an OpenGL texture using updateDynamicTexture().} This allows - us to create dynamic textures and works on all systems - with pbuffer support. - - \li \b{On systems that support it, we can bind the pbuffer to - an OpenGL texture.} The texture is then updated automatically - when the pbuffer contents change, eliminating the need for - additional copy operations. This is supported only on Windows - and \macos systems that provide the \c render_texture - extension. Note that under Windows, a multi-sampled pbuffer - can't be used in conjunction with the \c render_texture - extension. If a multi-sampled pbuffer is requested under - Windows, the \c render_texture extension is turned off for that - pbuffer. - - - \endlist - - \note This class has been deprecated, use QOpenGLFramebufferObject - for offscreen rendering. - - \section1 Threading - - As of Qt 4.8, it's possible to render into a QGLPixelBuffer using - a QPainter in a separate thread. Note that OpenGL 2.0 or OpenGL ES - 2.0 is required for this to work. - - Pbuffers are provided by the OpenGL \c pbuffer extension; call - hasOpenGLPbuffer() to find out if the system provides pbuffers. -*/ - -#include <private/qopenglextensions_p.h> - -#include <QtCore/qglobal.h> -#include <QtGui/qopenglframebufferobject.h> - -#include "gl2paintengineex/qpaintengineex_opengl2_p.h" - -#include <qglframebufferobject.h> -#include <qglpixelbuffer.h> -#include <private/qglpixelbuffer_p.h> -#include <private/qfont_p.h> -#include <qimage.h> - -QT_BEGIN_NAMESPACE - -extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool); - - -QGLContext* QGLPBufferGLPaintDevice::context() const -{ - return pbuf->d_func()->qctx; -} - -void QGLPBufferGLPaintDevice::beginPaint() -{ - pbuf->makeCurrent(); - QGLPaintDevice::beginPaint(); -} - -void QGLPBufferGLPaintDevice::endPaint() -{ - QOpenGLContext::currentContext()->functions()->glFlush(); - QGLPaintDevice::endPaint(); -} - -void QGLPBufferGLPaintDevice::setFbo(GLuint fbo) -{ - m_thisFBO = fbo; -} - -void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb) -{ - pbuf = pb; -} - -void QGLPixelBufferPrivate::common_init(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget) -{ - Q_Q(QGLPixelBuffer); - if(init(size, format, shareWidget)) { - req_size = size; - req_format = format; - req_shareWidget = shareWidget; - invalid = false; - glDevice.setPBuffer(q); - } -} - -/*! - Constructs an OpenGL pbuffer of the given \a size. If no \a - format is specified, the \l{QGLFormat::defaultFormat()}{default - format} is used. If the \a shareWidget parameter points to a - valid QGLWidget, the pbuffer will share its context with \a - shareWidget. - - If you intend to bind this pbuffer as a dynamic texture, the width - and height components of \c size must be powers of two (e.g., 512 - x 128). - - \sa size(), format() -*/ -QGLPixelBuffer::QGLPixelBuffer(const QSize &size, const QGLFormat &format, QGLWidget *shareWidget) - : d_ptr(new QGLPixelBufferPrivate(this)) -{ - Q_D(QGLPixelBuffer); - d->common_init(size, format, shareWidget); -} - - -/*! \overload - - Constructs an OpenGL pbuffer with the \a width and \a height. If - no \a format is specified, the - \l{QGLFormat::defaultFormat()}{default format} is used. If the \a - shareWidget parameter points to a valid QGLWidget, the pbuffer - will share its context with \a shareWidget. - - If you intend to bind this pbuffer as a dynamic texture, the width - and height components of \c size must be powers of two (e.g., 512 - x 128). - - \sa size(), format() -*/ -QGLPixelBuffer::QGLPixelBuffer(int width, int height, const QGLFormat &format, QGLWidget *shareWidget) - : d_ptr(new QGLPixelBufferPrivate(this)) -{ - Q_D(QGLPixelBuffer); - d->common_init(QSize(width, height), format, shareWidget); -} - - -/*! \fn QGLPixelBuffer::~QGLPixelBuffer() - - Destroys the pbuffer and frees any allocated resources. -*/ -QGLPixelBuffer::~QGLPixelBuffer() -{ - Q_D(QGLPixelBuffer); - - // defined in qpaintengine_opengl.cpp - QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext()); - if (current != d->qctx) - makeCurrent(); - d->cleanup(); - if (current && current != d->qctx) - current->makeCurrent(); -} - -/*! \fn bool QGLPixelBuffer::makeCurrent() - - Makes this pbuffer the current OpenGL rendering context. Returns - true on success; otherwise returns \c false. - - \sa QGLContext::makeCurrent(), doneCurrent() -*/ - -bool QGLPixelBuffer::makeCurrent() -{ - Q_D(QGLPixelBuffer); - if (d->invalid) - return false; - d->qctx->makeCurrent(); - if (!d->fbo) { - QOpenGLFramebufferObjectFormat format; - if (d->req_format.stencil()) - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - else if (d->req_format.depth()) - format.setAttachment(QOpenGLFramebufferObject::Depth); - if (d->req_format.sampleBuffers()) - format.setSamples(d->req_format.samples()); - d->fbo = new QOpenGLFramebufferObject(d->req_size, format); - d->fbo->bind(); - d->glDevice.setFbo(d->fbo->handle()); - QOpenGLContext::currentContext()->functions()->glViewport(0, 0, d->req_size.width(), d->req_size.height()); - } - return true; -} - -/*! \fn bool QGLPixelBuffer::doneCurrent() - - Makes no context the current OpenGL context. Returns \c true on - success; otherwise returns \c false. -*/ - -bool QGLPixelBuffer::doneCurrent() -{ - Q_D(QGLPixelBuffer); - if (d->invalid) - return false; - d->qctx->doneCurrent(); - return true; -} - -/*! - Returns the context of this pixelbuffer. -*/ -QGLContext *QGLPixelBuffer::context() const -{ - Q_D(const QGLPixelBuffer); - return d->qctx; -} - -/*! - \fn GLuint QGLPixelBuffer::generateDynamicTexture() const - - Generates and binds a 2D GL texture that is the same size as the - pbuffer, and returns the texture's ID. This can be used in - conjunction with bindToDynamicTexture() and - updateDynamicTexture(). - - \sa size() -*/ - -/*! \fn bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id) - - Binds the texture specified by \a texture_id to this pbuffer. - Returns \c true on success; otherwise returns \c false. - - The texture must be of the same size and format as the pbuffer. - - To unbind the texture, call releaseFromDynamicTexture(). While - the texture is bound, it is updated automatically when the - pbuffer contents change, eliminating the need for additional copy - operations. - - Example: - - \snippet code/src_opengl_qglpixelbuffer.cpp 0 - - \warning This function uses the \c {render_texture} extension, - which is currently not supported under X11. An alternative that - works on all systems (including X11) is to manually copy the - pbuffer contents to a texture using updateDynamicTexture(). - - \warning For the bindToDynamicTexture() call to succeed on the - \macos, the pbuffer needs a shared context, i.e. the - QGLPixelBuffer must be created with a share widget. - - \sa generateDynamicTexture(), releaseFromDynamicTexture() -*/ - -/*! \fn void QGLPixelBuffer::releaseFromDynamicTexture() - - Releases the pbuffer from any previously bound texture. - - \sa bindToDynamicTexture() -*/ - -/*! \fn bool QGLPixelBuffer::hasOpenGLPbuffers() - - Returns \c true if the OpenGL \c pbuffer extension is present on - this system; otherwise returns \c false. -*/ - -/*! - Copies the pbuffer contents into the texture specified with \a - texture_id. - - The texture must be of the same size and format as the pbuffer. - - Example: - - \snippet code/src_opengl_qglpixelbuffer.cpp 1 - - An alternative on Windows and \macos systems that support the - \c render_texture extension is to use bindToDynamicTexture() to - get dynamic updates of the texture. - - \sa generateDynamicTexture(), bindToDynamicTexture() -*/ -void QGLPixelBuffer::updateDynamicTexture(GLuint texture_id) const -{ - Q_D(const QGLPixelBuffer); - if (d->invalid || !d->fbo) - return; - - const QGLContext *ctx = QGLContext::currentContext(); - if (!ctx) - return; - -#undef glBindFramebuffer - -#ifndef GL_READ_FRAMEBUFFER -#define GL_READ_FRAMEBUFFER 0x8CA8 -#endif - -#ifndef GL_DRAW_FRAMEBUFFER -#define GL_DRAW_FRAMEBUFFER 0x8CA9 -#endif - - QOpenGLExtensions extensions(ctx->contextHandle()); - - ctx->d_ptr->refreshCurrentFbo(); - - if (d->blit_fbo) { - QOpenGLFramebufferObject::blitFramebuffer(d->blit_fbo, d->fbo); - extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, d->blit_fbo->handle()); - } - - extensions.glBindTexture(GL_TEXTURE_2D, texture_id); -#ifndef QT_OPENGL_ES - GLenum format = ctx->contextHandle()->isOpenGLES() ? GL_RGBA : GL_RGBA8; - extensions.glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, d->req_size.width(), d->req_size.height(), 0); -#else - extensions.glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, d->req_size.width(), d->req_size.height(), 0); -#endif - - if (d->blit_fbo) - extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->d_func()->current_fbo); -} - -/*! - Returns the size of the pbuffer. -*/ -QSize QGLPixelBuffer::size() const -{ - Q_D(const QGLPixelBuffer); - return d->req_size; -} - -/*! - Returns the contents of the pbuffer as a QImage. -*/ -QImage QGLPixelBuffer::toImage() const -{ - Q_D(const QGLPixelBuffer); - if (d->invalid) - return QImage(); - - const_cast<QGLPixelBuffer *>(this)->makeCurrent(); - if (d->fbo) - d->fbo->bind(); - return qt_gl_read_frame_buffer(d->req_size, d->format.alpha(), true); -} - -/*! - Returns the native pbuffer handle. -*/ -Qt::HANDLE QGLPixelBuffer::handle() const -{ - Q_D(const QGLPixelBuffer); - if (d->invalid) - return 0; - return (Qt::HANDLE) d->pbuf; -} - -/*! - Returns \c true if this pbuffer is valid; otherwise returns \c false. -*/ -bool QGLPixelBuffer::isValid() const -{ - Q_D(const QGLPixelBuffer); - return !d->invalid; -} - -Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine) - -/*! \reimp */ -QPaintEngine *QGLPixelBuffer::paintEngine() const -{ - return qt_buffer_2_engine()->engine(); -} - -/*! \reimp */ -int QGLPixelBuffer::metric(PaintDeviceMetric metric) const -{ - Q_D(const QGLPixelBuffer); - - float dpmx = qt_defaultDpiX()*100./2.54; - float dpmy = qt_defaultDpiY()*100./2.54; - int w = d->req_size.width(); - int h = d->req_size.height(); - switch (metric) { - case PdmWidth: - return w; - - case PdmHeight: - return h; - - case PdmWidthMM: - return qRound(w * 1000 / dpmx); - - case PdmHeightMM: - return qRound(h * 1000 / dpmy); - - case PdmNumColors: - return 0; - - case PdmDepth: - return 32;//d->depth; - - case PdmDpiX: - return qRound(dpmx * 0.0254); - - case PdmDpiY: - return qRound(dpmy * 0.0254); - - case PdmPhysicalDpiX: - return qRound(dpmx * 0.0254); - - case PdmPhysicalDpiY: - return qRound(dpmy * 0.0254); - - case QPaintDevice::PdmDevicePixelRatio: - return 1; - - case QPaintDevice::PdmDevicePixelRatioScaled: - return QPaintDevice::devicePixelRatioFScale(); - - default: - qWarning("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric); - break; - } - return 0; -} - -/*! - Generates and binds a 2D GL texture to the current context, based - on \a image. The generated texture id is returned and can be used - in later glBindTexture() calls. - - The \a target parameter specifies the texture target. - - Equivalent to calling QGLContext::bindTexture(). - - \sa deleteTexture() -*/ -GLuint QGLPixelBuffer::bindTexture(const QImage &image, GLenum target) -{ - Q_D(QGLPixelBuffer); -#ifndef QT_OPENGL_ES - GLenum format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; - return d->qctx->bindTexture(image, target, GLint(format)); -#else - return d->qctx->bindTexture(image, target, GL_RGBA); -#endif -} - -/*! \overload - - Generates and binds a 2D GL texture based on \a pixmap. - - Equivalent to calling QGLContext::bindTexture(). - - \sa deleteTexture() -*/ -GLuint QGLPixelBuffer::bindTexture(const QPixmap &pixmap, GLenum target) -{ - Q_D(QGLPixelBuffer); -#ifndef QT_OPENGL_ES - GLenum format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; - return d->qctx->bindTexture(pixmap, target, GLint(format)); -#else - return d->qctx->bindTexture(pixmap, target, GL_RGBA); -#endif -} - -/*! \overload - - Reads the DirectDrawSurface (DDS) compressed file \a fileName and - generates a 2D GL texture from it. - - Equivalent to calling QGLContext::bindTexture(). - - \sa deleteTexture() -*/ -GLuint QGLPixelBuffer::bindTexture(const QString &fileName) -{ - Q_D(QGLPixelBuffer); - return d->qctx->bindTexture(fileName); -} - -/*! - Removes the texture identified by \a texture_id from the texture cache. - - Equivalent to calling QGLContext::deleteTexture(). - */ -void QGLPixelBuffer::deleteTexture(GLuint texture_id) -{ - Q_D(QGLPixelBuffer); - d->qctx->deleteTexture(texture_id); -} - -/*! - \since 4.4 - - Draws the given texture, \a textureId, to the given target rectangle, - \a target, in OpenGL model space. The \a textureTarget should be a 2D - texture target. - - Equivalent to the corresponding QGLContext::drawTexture(). -*/ -void QGLPixelBuffer::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) -{ - Q_D(QGLPixelBuffer); - d->qctx->drawTexture(target, textureId, textureTarget); -} - -/*! - \since 4.4 - - Draws the given texture, \a textureId, at the given \a point in OpenGL model - space. The textureTarget parameter should be a 2D texture target. - - Equivalent to the corresponding QGLContext::drawTexture(). -*/ -void QGLPixelBuffer::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) -{ - Q_D(QGLPixelBuffer); - d->qctx->drawTexture(point, textureId, textureTarget); -} - -/*! - Returns the format of the pbuffer. The format may be different - from the one that was requested. -*/ -QGLFormat QGLPixelBuffer::format() const -{ - Q_D(const QGLPixelBuffer); - return d->format; -} - -/*! \fn int QGLPixelBuffer::devType() const - \internal -*/ - -bool QGLPixelBufferPrivate::init(const QSize &, const QGLFormat &f, QGLWidget *shareWidget) -{ - widget = new QGLWidget(f, 0, shareWidget); - widget->resize(1, 1); - qctx = const_cast<QGLContext *>(widget->context()); - return widget->isValid(); -} - -bool QGLPixelBufferPrivate::cleanup() -{ - delete fbo; - fbo = 0; - delete blit_fbo; - blit_fbo = 0; - delete widget; - widget = 0; - return true; -} - -bool QGLPixelBuffer::bindToDynamicTexture(GLuint texture_id) -{ - Q_UNUSED(texture_id); - return false; -} - -void QGLPixelBuffer::releaseFromDynamicTexture() -{ -} - -GLuint QGLPixelBuffer::generateDynamicTexture() const -{ - Q_D(const QGLPixelBuffer); - if (!d->fbo) - return 0; - - if (d->fbo->format().samples() > 0 - && QOpenGLExtensions(QOpenGLContext::currentContext()) - .hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) - { - if (!d->blit_fbo) - const_cast<QOpenGLFramebufferObject *&>(d->blit_fbo) = new QOpenGLFramebufferObject(d->req_size); - } else { - return d->fbo->texture(); - } - - GLuint texture; - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - - funcs->glGenTextures(1, &texture); - funcs->glBindTexture(GL_TEXTURE_2D, texture); - - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->req_size.width(), d->req_size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, 0); - - return texture; -} - -bool QGLPixelBuffer::hasOpenGLPbuffers() -{ - return QGLFramebufferObject::hasOpenGLFramebufferObjects(); -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglpixelbuffer.h b/src/opengl/qglpixelbuffer.h deleted file mode 100644 index f5d7929c35..0000000000 --- a/src/opengl/qglpixelbuffer.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLPIXELBUFFER_H -#define QGLPIXELBUFFER_H - -#include <QtOpenGL/qgl.h> -#include <QtGui/qpaintdevice.h> - -QT_BEGIN_NAMESPACE - - -class QGLPixelBufferPrivate; - -class Q_OPENGL_EXPORT QGLPixelBuffer : public QPaintDevice -{ - Q_DECLARE_PRIVATE(QGLPixelBuffer) -public: - QGLPixelBuffer(const QSize &size, const QGLFormat &format = QGLFormat::defaultFormat(), - QGLWidget *shareWidget = nullptr); - QGLPixelBuffer(int width, int height, const QGLFormat &format = QGLFormat::defaultFormat(), - QGLWidget *shareWidget = nullptr); - virtual ~QGLPixelBuffer(); - - bool isValid() const; - bool makeCurrent(); - bool doneCurrent(); - - QGLContext *context() const; - - GLuint generateDynamicTexture() const; - bool bindToDynamicTexture(GLuint texture); - void releaseFromDynamicTexture(); - void updateDynamicTexture(GLuint texture_id) const; - - GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D); - GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D); - GLuint bindTexture(const QString &fileName); - void deleteTexture(GLuint texture_id); - - void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - - QSize size() const; - Qt::HANDLE handle() const; - QImage toImage() const; - - QPaintEngine *paintEngine() const override; - QGLFormat format() const; - - static bool hasOpenGLPbuffers(); - -protected: - int metric(PaintDeviceMetric metric) const override; - int devType() const override { return QInternal::Pbuffer; } - -private: - Q_DISABLE_COPY(QGLPixelBuffer) - QScopedPointer<QGLPixelBufferPrivate> d_ptr; - friend class QGLDrawable; - friend class QGLPaintDevice; - friend class QGLPBufferGLPaintDevice; - friend class QGLContextPrivate; -}; - -QT_END_NAMESPACE - -#endif // QGLPIXELBUFFER_H diff --git a/src/opengl/qglpixelbuffer_p.h b/src/opengl/qglpixelbuffer_p.h deleted file mode 100644 index 2729ade2b6..0000000000 --- a/src/opengl/qglpixelbuffer_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLPIXELBUFFER_P_H -#define QGLPIXELBUFFER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt 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. -// - -#include "QtOpenGL/qglpixelbuffer.h" -#include <private/qgl_p.h> -#include <private/qglpaintdevice_p.h> - -QT_BEGIN_NAMESPACE - -class QEglContext; -class QOpenGLFramebufferObject; - -class QGLPBufferGLPaintDevice : public QGLPaintDevice -{ -public: - QPaintEngine* paintEngine() const override {return pbuf->paintEngine();} - QSize size() const override {return pbuf->size();} - QGLContext* context() const override; - void beginPaint() override; - void endPaint() override; - void setPBuffer(QGLPixelBuffer* pb); - void setFbo(GLuint fbo); -private: - QGLPixelBuffer* pbuf; -}; - -class QGLPixelBufferPrivate { - Q_DECLARE_PUBLIC(QGLPixelBuffer) -public: - QGLPixelBufferPrivate(QGLPixelBuffer *q) : q_ptr(q), invalid(true), qctx(nullptr), widget(nullptr), fbo(nullptr), blit_fbo(nullptr), pbuf(nullptr), ctx(nullptr) - { - } - bool init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget); - void common_init(const QSize &size, const QGLFormat &f, QGLWidget *shareWidget); - bool cleanup(); - - QGLPixelBuffer *q_ptr; - bool invalid; - QGLContext *qctx; - QGLPBufferGLPaintDevice glDevice; - QGLWidget *widget; - QOpenGLFramebufferObject *fbo; - QOpenGLFramebufferObject *blit_fbo; - QGLFormat format; - - QGLFormat req_format; - QPointer<QGLWidget> req_shareWidget; - QSize req_size; - - //stubs - void *pbuf; - void *ctx; -}; - -QT_END_NAMESPACE - -#endif // QGLPIXELBUFFER_P_H diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp deleted file mode 100644 index 35f60318be..0000000000 --- a/src/opengl/qglshaderprogram.cpp +++ /dev/null @@ -1,3237 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qglshaderprogram.h" -#include <private/qopenglextensions_p.h> -#include "qgl_p.h" -#include <QtCore/private/qobject_p.h> -#include <QtCore/qdebug.h> -#include <QtCore/qfile.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qvector.h> -#include <QDebug> - -QT_BEGIN_NAMESPACE - -/*! - \class QGLShaderProgram - \inmodule QtOpenGL - \brief The QGLShaderProgram class allows OpenGL shader programs to be linked and used. - \since 4.6 - \obsolete - \ingroup painting-3D - - \section1 Introduction - - This class supports shader programs written in the OpenGL Shading - Language (GLSL) and in the OpenGL/ES Shading Language (GLSL/ES). - - QGLShader and QGLShaderProgram shelter the programmer from the details of - compiling and linking vertex and fragment shaders. - - The following example creates a vertex shader program using the - supplied source \c{code}. Once compiled and linked, the shader - program is activated in the current QGLContext by calling - QGLShaderProgram::bind(): - - \snippet code/src_opengl_qglshaderprogram.cpp 0 - - \section1 Writing Portable Shaders - - Shader programs can be difficult to reuse across OpenGL implementations - because of varying levels of support for standard vertex attributes and - uniform variables. In particular, GLSL/ES lacks all of the - standard variables that are present on desktop OpenGL systems: - \c{gl_Vertex}, \c{gl_Normal}, \c{gl_Color}, and so on. Desktop OpenGL - lacks the variable qualifiers \c{highp}, \c{mediump}, and \c{lowp}. - - The QGLShaderProgram class makes the process of writing portable shaders - easier by prefixing all shader programs with the following lines on - desktop OpenGL: - - \code - #define highp - #define mediump - #define lowp - \endcode - - This makes it possible to run most GLSL/ES shader programs - on desktop systems. The programmer should restrict themselves - to just features that are present in GLSL/ES, and avoid - standard variable names that only work on the desktop. - - \section1 Simple Shader Example - - \snippet code/src_opengl_qglshaderprogram.cpp 1 - - With the above shader program active, we can draw a green triangle - as follows: - - \snippet code/src_opengl_qglshaderprogram.cpp 2 - - \section1 Binary Shaders and Programs - - Binary shaders may be specified using \c{glShaderBinary()} on - the return value from QGLShader::shaderId(). The QGLShader instance - containing the binary can then be added to the shader program with - addShader() and linked in the usual fashion with link(). - - Binary programs may be specified using \c{glProgramBinaryOES()} - on the return value from programId(). Then the application should - call link(), which will notice that the program has already been - specified and linked, allowing other operations to be performed - on the shader program. - - \note This class has been deprecated in favor of QOpenGLShaderProgram. - - \sa QGLShader -*/ - -/*! - \class QGLShader - \inmodule QtOpenGL - \brief The QGLShader class allows OpenGL shaders to be compiled. - \since 4.6 - \obsolete - \ingroup painting-3D - - This class supports shaders written in the OpenGL Shading Language (GLSL) - and in the OpenGL/ES Shading Language (GLSL/ES). - - QGLShader and QGLShaderProgram shelter the programmer from the details of - compiling and linking vertex and fragment shaders. - - \note This class has been deprecated in favor of QOpenGLShader. - - \sa QGLShaderProgram -*/ - -/*! - \enum QGLShader::ShaderTypeBit - This enum specifies the type of QGLShader that is being created. - - \value Vertex Vertex shader written in the OpenGL Shading Language (GLSL). - \value Fragment Fragment shader written in the OpenGL Shading Language (GLSL). - \value Geometry Geometry shaders written in the OpenGL Shading - Language (GLSL), based on the GL_EXT_geometry_shader4 extension. -*/ - -#ifndef GL_FRAGMENT_SHADER -#define GL_FRAGMENT_SHADER 0x8B30 -#endif -#ifndef GL_VERTEX_SHADER -#define GL_VERTEX_SHADER 0x8B31 -#endif -#ifndef GL_COMPILE_STATUS -#define GL_COMPILE_STATUS 0x8B81 -#endif -#ifndef GL_LINK_STATUS -#define GL_LINK_STATUS 0x8B82 -#endif -#ifndef GL_INFO_LOG_LENGTH -#define GL_INFO_LOG_LENGTH 0x8B84 -#endif -#ifndef GL_ACTIVE_UNIFORMS -#define GL_ACTIVE_UNIFORMS 0x8B86 -#endif -#ifndef GL_ACTIVE_UNIFORM_MAX_LENGTH -#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 -#endif -#ifndef GL_ACTIVE_ATTRIBUTES -#define GL_ACTIVE_ATTRIBUTES 0x8B89 -#endif -#ifndef GL_ACTIVE_ATTRIBUTE_MAX_LENGTH -#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A -#endif -#ifndef GL_CURRENT_VERTEX_ATTRIB -#define GL_CURRENT_VERTEX_ATTRIB 0x8626 -#endif -#ifndef GL_SHADER_SOURCE_LENGTH -#define GL_SHADER_SOURCE_LENGTH 0x8B88 -#endif -#ifndef GL_SHADER_BINARY_FORMATS -#define GL_SHADER_BINARY_FORMATS 0x8DF8 -#endif -#ifndef GL_NUM_SHADER_BINARY_FORMATS -#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 -#endif - -class QGLShaderPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QGLShader) -public: - QGLShaderPrivate(const QGLContext *ctx, QGLShader::ShaderType type) - : shaderGuard(0) - , shaderType(type) - , compiled(false) - , glfuncs(new QOpenGLFunctions(ctx->contextHandle())) - { - } - ~QGLShaderPrivate(); - - QGLSharedResourceGuardBase *shaderGuard; - QGLShader::ShaderType shaderType; - bool compiled; - QString log; - - QOpenGLFunctions *glfuncs; - - bool create(); - bool compile(QGLShader *q); - void deleteShader(); -}; - -namespace { - void freeShaderFunc(QGLContext *ctx, GLuint id) - { - Q_ASSERT(ctx); - ctx->contextHandle()->functions()->glDeleteShader(id); - } -} - -#define ctx QGLContext::currentContext() - -QGLShaderPrivate::~QGLShaderPrivate() -{ - delete glfuncs; - if (shaderGuard) - shaderGuard->free(); -} - -bool QGLShaderPrivate::create() -{ - QGLContext *context = const_cast<QGLContext *>(QGLContext::currentContext()); - if (!context) - return false; - - if (glfuncs->hasOpenGLFeature(QOpenGLFunctions::Shaders)) { - GLuint shader; - if (shaderType == QGLShader::Vertex) - shader = glfuncs->glCreateShader(GL_VERTEX_SHADER); -#if !defined(QT_OPENGL_ES_2) - else if (shaderType == QGLShader::Geometry - && !context->contextHandle()->isOpenGLES()) - shader = glfuncs->glCreateShader(GL_GEOMETRY_SHADER_EXT); -#endif - else - shader = glfuncs->glCreateShader(GL_FRAGMENT_SHADER); - if (!shader) { - qWarning("Could not create shader of type %d.", int(shaderType)); - return false; - } - shaderGuard = createSharedResourceGuard(context, shader, freeShaderFunc); - return true; - } else { - return false; - } -} - -bool QGLShaderPrivate::compile(QGLShader *q) -{ - GLuint shader = shaderGuard ? shaderGuard->id() : 0; - if (!shader) - return false; - glfuncs->glCompileShader(shader); - GLint value = 0; - glfuncs->glGetShaderiv(shader, GL_COMPILE_STATUS, &value); - compiled = (value != 0); - value = 0; - glfuncs->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &value); - if (!compiled && value > 1) { - char *logbuf = new char [value]; - GLint len; - glfuncs->glGetShaderInfoLog(shader, value, &len, logbuf); - log = QString::fromLatin1(logbuf); - QString name = q->objectName(); - - const char *types[] = { - "Fragment", - "Vertex", - "Geometry", - "" - }; - - const char *type = types[3]; - if (shaderType == QGLShader::Fragment) - type = types[0]; - else if (shaderType == QGLShader::Vertex) - type = types[1]; - else if (shaderType == QGLShader::Geometry) - type = types[2]; - - if (name.isEmpty()) - qWarning("QGLShader::compile(%s): %s", type, qPrintable(log)); - else - qWarning("QGLShader::compile(%s)[%s]: %s", type, qPrintable(name), qPrintable(log)); - - delete [] logbuf; - } - return compiled; -} - -void QGLShaderPrivate::deleteShader() -{ - if (shaderGuard) { - shaderGuard->free(); - shaderGuard = 0; - } -} - -/*! - Constructs a new QGLShader object of the specified \a type - and attaches it to \a parent. If shader programs are not supported, - QGLShaderProgram::hasOpenGLShaderPrograms() will return false. - - This constructor is normally followed by a call to compileSourceCode() - or compileSourceFile(). - - The shader will be associated with the current QGLContext. - - \sa compileSourceCode(), compileSourceFile() -*/ -QGLShader::QGLShader(QGLShader::ShaderType type, QObject *parent) - : QObject(*new QGLShaderPrivate(QGLContext::currentContext(), type), parent) -{ - Q_D(QGLShader); - d->create(); -} - -/*! - Constructs a new QGLShader object of the specified \a type - and attaches it to \a parent. If shader programs are not supported, - then QGLShaderProgram::hasOpenGLShaderPrograms() will return false. - - This constructor is normally followed by a call to compileSourceCode() - or compileSourceFile(). - - The shader will be associated with \a context. - - \sa compileSourceCode(), compileSourceFile() -*/ -QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObject *parent) - : QObject(*new QGLShaderPrivate(context ? context : QGLContext::currentContext(), type), parent) -{ - Q_D(QGLShader); -#ifndef QT_NO_DEBUG - if (context && !QGLContext::areSharing(context, QGLContext::currentContext())) { - qWarning("QGLShader::QGLShader: \'context\' must be the current context or sharing with it."); - return; - } -#endif - d->create(); -} - -/*! - Deletes this shader. If the shader has been attached to a - QGLShaderProgram object, then the actual shader will stay around - until the QGLShaderProgram is destroyed. -*/ -QGLShader::~QGLShader() -{ -} - -/*! - Returns the type of this shader. -*/ -QGLShader::ShaderType QGLShader::shaderType() const -{ - Q_D(const QGLShader); - return d->shaderType; -} - -// The precision qualifiers are useful on OpenGL/ES systems, -// but usually not present on desktop systems. Define the -// keywords to empty strings on desktop systems. -#if !defined(QT_OPENGL_ES) || defined(QT_OPENGL_FORCE_SHADER_DEFINES) -#define QGL_DEFINE_QUALIFIERS 1 -static const char qualifierDefines[] = - "#define lowp\n" - "#define mediump\n" - "#define highp\n"; - -#else - -// The "highp" qualifier doesn't exist in fragment shaders -// on all ES platforms. When it doesn't exist, use "mediump". -#define QGL_REDEFINE_HIGHP 1 -static const char redefineHighp[] = - "#ifndef GL_FRAGMENT_PRECISION_HIGH\n" - "#define highp mediump\n" - "#endif\n"; -#endif - -/*! - Sets the \a source code for this shader and compiles it. - Returns \c true if the source was successfully compiled, false otherwise. - - \sa compileSourceFile() -*/ -bool QGLShader::compileSourceCode(const char *source) -{ - Q_D(QGLShader); - if (d->shaderGuard && d->shaderGuard->id()) { - QVarLengthArray<const char *, 4> src; - QVarLengthArray<GLint, 4> srclen; - int headerLen = 0; - while (source && source[headerLen] == '#') { - // Skip #version and #extension directives at the start of - // the shader code. We need to insert the qualifierDefines - // and redefineHighp just after them. - if (qstrncmp(source + headerLen, "#version", 8) != 0 && - qstrncmp(source + headerLen, "#extension", 10) != 0) { - break; - } - while (source[headerLen] != '\0' && source[headerLen] != '\n') - ++headerLen; - if (source[headerLen] == '\n') - ++headerLen; - } - if (headerLen > 0) { - src.append(source); - srclen.append(GLint(headerLen)); - } -#ifdef QGL_DEFINE_QUALIFIERS - if (!QOpenGLContext::currentContext()->isOpenGLES()) { - src.append(qualifierDefines); - srclen.append(GLint(sizeof(qualifierDefines) - 1)); - } -#endif -#ifdef QGL_REDEFINE_HIGHP - if (d->shaderType == Fragment - && QOpenGLContext::currentContext()->isOpenGLES()) { - src.append(redefineHighp); - srclen.append(GLint(sizeof(redefineHighp) - 1)); - } -#endif - src.append(source + headerLen); - srclen.append(GLint(qstrlen(source + headerLen))); - d->glfuncs->glShaderSource(d->shaderGuard->id(), src.size(), src.data(), srclen.data()); - return d->compile(this); - } else { - return false; - } -} - -/*! - \overload - - Sets the \a source code for this shader and compiles it. - Returns \c true if the source was successfully compiled, false otherwise. - - \sa compileSourceFile() -*/ -bool QGLShader::compileSourceCode(const QByteArray& source) -{ - return compileSourceCode(source.constData()); -} - -/*! - \overload - - Sets the \a source code for this shader and compiles it. - Returns \c true if the source was successfully compiled, false otherwise. - - \sa compileSourceFile() -*/ -bool QGLShader::compileSourceCode(const QString& source) -{ - return compileSourceCode(source.toLatin1().constData()); -} - -/*! - Sets the source code for this shader to the contents of \a fileName - and compiles it. Returns \c true if the file could be opened and the - source compiled, false otherwise. - - \sa compileSourceCode() -*/ -bool QGLShader::compileSourceFile(const QString& fileName) -{ - QFile file(fileName); - if (!file.open(QFile::ReadOnly)) { - qWarning() << "QGLShader: Unable to open file" << fileName; - return false; - } - - QByteArray contents = file.readAll(); - return compileSourceCode(contents.constData()); -} - -/*! - Returns the source code for this shader. - - \sa compileSourceCode() -*/ -QByteArray QGLShader::sourceCode() const -{ - Q_D(const QGLShader); - GLuint shader = d->shaderGuard ? d->shaderGuard->id() : 0; - if (!shader) - return QByteArray(); - GLint size = 0; - d->glfuncs->glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &size); - if (size <= 0) - return QByteArray(); - GLint len = 0; - char *source = new char [size]; - d->glfuncs->glGetShaderSource(shader, size, &len, source); - QByteArray src(source); - delete [] source; - return src; -} - -/*! - Returns \c true if this shader has been compiled; false otherwise. - - \sa compileSourceCode(), compileSourceFile() -*/ -bool QGLShader::isCompiled() const -{ - Q_D(const QGLShader); - return d->compiled; -} - -/*! - Returns the errors and warnings that occurred during the last compile. - - \sa compileSourceCode(), compileSourceFile() -*/ -QString QGLShader::log() const -{ - Q_D(const QGLShader); - return d->log; -} - -/*! - Returns the OpenGL identifier associated with this shader. - - \sa QGLShaderProgram::programId() -*/ -GLuint QGLShader::shaderId() const -{ - Q_D(const QGLShader); - return d->shaderGuard ? d->shaderGuard->id() : 0; -} - -#undef ctx - -class ShaderProgramOpenGLFunctions : public QOpenGLFunctions -{ -public: - ShaderProgramOpenGLFunctions() - : QOpenGLFunctions() - , glProgramParameteri(0) - { - } - - typedef void (QOPENGLF_APIENTRYP type_glProgramParameteri)(GLuint program, GLenum pname, GLint value); - - void initializeGeometryShaderFunctions() - { - QOpenGLContext *context = QOpenGLContext::currentContext(); - if (!context->isOpenGLES()) { - glProgramParameteri = (type_glProgramParameteri) - context->getProcAddress("glProgramParameteri"); - - if (!glProgramParameteri) { - glProgramParameteri = (type_glProgramParameteri) - context->getProcAddress("glProgramParameteriEXT"); - } - } - } - - type_glProgramParameteri glProgramParameteri; -}; - -class QGLShaderProgramPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QGLShaderProgram) -public: - QGLShaderProgramPrivate(const QGLContext *) - : programGuard(0) - , linked(false) - , inited(false) - , removingShaders(false) - , geometryVertexCount(64) - , geometryInputType(0) - , geometryOutputType(0) - , glfuncs(new ShaderProgramOpenGLFunctions) - { - } - ~QGLShaderProgramPrivate(); - - QGLSharedResourceGuardBase *programGuard; - bool linked; - bool inited; - bool removingShaders; - - int geometryVertexCount; - GLenum geometryInputType; - GLenum geometryOutputType; - - QString log; - QList<QGLShader *> shaders; - QList<QGLShader *> anonShaders; - - ShaderProgramOpenGLFunctions *glfuncs; - - bool hasShader(QGLShader::ShaderType type) const; -}; - -namespace { - void freeProgramFunc(QGLContext *ctx, GLuint id) - { - Q_ASSERT(ctx); - ctx->contextHandle()->functions()->glDeleteProgram(id); - } -} - - -QGLShaderProgramPrivate::~QGLShaderProgramPrivate() -{ - delete glfuncs; - if (programGuard) - programGuard->free(); -} - -bool QGLShaderProgramPrivate::hasShader(QGLShader::ShaderType type) const -{ - for (QGLShader *shader : shaders) { - if (shader->shaderType() == type) - return true; - } - return false; -} - -#define ctx QGLContext::currentContext() - -/*! - Constructs a new shader program and attaches it to \a parent. - The program will be invalid until addShader() is called. - - The shader program will be associated with the current QGLContext. - - \sa addShader() -*/ -QGLShaderProgram::QGLShaderProgram(QObject *parent) - : QObject(*new QGLShaderProgramPrivate(QGLContext::currentContext()), parent) -{ -} - -/*! - Constructs a new shader program and attaches it to \a parent. - The program will be invalid until addShader() is called. - - The shader program will be associated with \a context. - - \sa addShader() -*/ -QGLShaderProgram::QGLShaderProgram(const QGLContext *context, QObject *parent) - : QObject(*new QGLShaderProgramPrivate(context), parent) -{ -} - -/*! - Deletes this shader program. -*/ -QGLShaderProgram::~QGLShaderProgram() -{ -} - -bool QGLShaderProgram::init() -{ - Q_D(QGLShaderProgram); - if ((d->programGuard && d->programGuard->id()) || d->inited) - return true; - d->inited = true; - QGLContext *context = const_cast<QGLContext *>(QGLContext::currentContext()); - if (!context) - return false; - d->glfuncs->initializeOpenGLFunctions(); - d->glfuncs->initializeGeometryShaderFunctions(); - if (d->glfuncs->hasOpenGLFeature(QOpenGLFunctions::Shaders)) { - GLuint program = d->glfuncs->glCreateProgram(); - if (!program) { - qWarning("QGLShaderProgram: could not create shader program"); - return false; - } - if (d->programGuard) - delete d->programGuard; - d->programGuard = createSharedResourceGuard(context, program, freeProgramFunc); - return true; - } else { - qWarning("QGLShaderProgram: shader programs are not supported"); - return false; - } -} - -/*! - Adds a compiled \a shader to this shader program. Returns \c true - if the shader could be added, or false otherwise. - - Ownership of the \a shader object remains with the caller. - It will not be deleted when this QGLShaderProgram instance - is deleted. This allows the caller to add the same shader - to multiple shader programs. - - \sa addShaderFromSourceCode(), addShaderFromSourceFile() - \sa removeShader(), link(), removeAllShaders() -*/ -bool QGLShaderProgram::addShader(QGLShader *shader) -{ - Q_D(QGLShaderProgram); - if (!init()) - return false; - if (d->shaders.contains(shader)) - return true; // Already added to this shader program. - if (d->programGuard && d->programGuard->id() && shader) { - if (!shader->d_func()->shaderGuard || !shader->d_func()->shaderGuard->id()) - return false; - if (d->programGuard->group() != shader->d_func()->shaderGuard->group()) { - qWarning("QGLShaderProgram::addShader: Program and shader are not associated with same context."); - return false; - } - d->glfuncs->glAttachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); - d->linked = false; // Program needs to be relinked. - d->shaders.append(shader); - connect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); - return true; - } else { - return false; - } -} - -/*! - Compiles \a source as a shader of the specified \a type and - adds it to this shader program. Returns \c true if compilation - was successful, false otherwise. The compilation errors - and warnings will be made available via log(). - - This function is intended to be a short-cut for quickly - adding vertex and fragment shaders to a shader program without - creating an instance of QGLShader first. - - \sa addShader(), addShaderFromSourceFile() - \sa removeShader(), link(), log(), removeAllShaders() -*/ -bool QGLShaderProgram::addShaderFromSourceCode(QGLShader::ShaderType type, const char *source) -{ - Q_D(QGLShaderProgram); - if (!init()) - return false; - QGLShader *shader = new QGLShader(type, this); - if (!shader->compileSourceCode(source)) { - d->log = shader->log(); - delete shader; - return false; - } - d->anonShaders.append(shader); - return addShader(shader); -} - -/*! - \overload - - Compiles \a source as a shader of the specified \a type and - adds it to this shader program. Returns \c true if compilation - was successful, false otherwise. The compilation errors - and warnings will be made available via log(). - - This function is intended to be a short-cut for quickly - adding vertex and fragment shaders to a shader program without - creating an instance of QGLShader first. - - \sa addShader(), addShaderFromSourceFile() - \sa removeShader(), link(), log(), removeAllShaders() -*/ -bool QGLShaderProgram::addShaderFromSourceCode(QGLShader::ShaderType type, const QByteArray& source) -{ - return addShaderFromSourceCode(type, source.constData()); -} - -/*! - \overload - - Compiles \a source as a shader of the specified \a type and - adds it to this shader program. Returns \c true if compilation - was successful, false otherwise. The compilation errors - and warnings will be made available via log(). - - This function is intended to be a short-cut for quickly - adding vertex and fragment shaders to a shader program without - creating an instance of QGLShader first. - - \sa addShader(), addShaderFromSourceFile() - \sa removeShader(), link(), log(), removeAllShaders() -*/ -bool QGLShaderProgram::addShaderFromSourceCode(QGLShader::ShaderType type, const QString& source) -{ - return addShaderFromSourceCode(type, source.toLatin1().constData()); -} - -/*! - Compiles the contents of \a fileName as a shader of the specified - \a type and adds it to this shader program. Returns \c true if - compilation was successful, false otherwise. The compilation errors - and warnings will be made available via log(). - - This function is intended to be a short-cut for quickly - adding vertex and fragment shaders to a shader program without - creating an instance of QGLShader first. - - \sa addShader(), addShaderFromSourceCode() -*/ -bool QGLShaderProgram::addShaderFromSourceFile - (QGLShader::ShaderType type, const QString& fileName) -{ - Q_D(QGLShaderProgram); - if (!init()) - return false; - QGLShader *shader = new QGLShader(type, this); - if (!shader->compileSourceFile(fileName)) { - d->log = shader->log(); - delete shader; - return false; - } - d->anonShaders.append(shader); - return addShader(shader); -} - -/*! - Removes \a shader from this shader program. The object is not deleted. - - The shader program must be valid in the current QGLContext. - - \sa addShader(), link(), removeAllShaders() -*/ -void QGLShaderProgram::removeShader(QGLShader *shader) -{ - Q_D(QGLShaderProgram); - if (d->programGuard && d->programGuard->id() - && shader && shader->d_func()->shaderGuard) - { - d->glfuncs->glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); - } - d->linked = false; // Program needs to be relinked. - if (shader) { - d->shaders.removeAll(shader); - d->anonShaders.removeAll(shader); - disconnect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); - } -} - -/*! - Returns a list of all shaders that have been added to this shader - program using addShader(). - - \sa addShader(), removeShader() -*/ -QList<QGLShader *> QGLShaderProgram::shaders() const -{ - Q_D(const QGLShaderProgram); - return d->shaders; -} - -/*! - Removes all of the shaders that were added to this program previously. - The QGLShader objects for the shaders will not be deleted if they - were constructed externally. QGLShader objects that are constructed - internally by QGLShaderProgram will be deleted. - - \sa addShader(), removeShader() -*/ -void QGLShaderProgram::removeAllShaders() -{ - Q_D(QGLShaderProgram); - d->removingShaders = true; - if (d->programGuard) { - if (const auto programGuardId = d->programGuard->id()) { - for (QGLShader *shader : qAsConst(d->shaders)) { - if (shader && shader->d_func()->shaderGuard) - d->glfuncs->glDetachShader(programGuardId, shader->d_func()->shaderGuard->id()); - } - } - } - // Delete shader objects that were created anonymously. - qDeleteAll(d->anonShaders); - d->shaders.clear(); - d->anonShaders.clear(); - d->linked = false; // Program needs to be relinked. - d->removingShaders = false; -} - -/*! - Links together the shaders that were added to this program with - addShader(). Returns \c true if the link was successful or - false otherwise. If the link failed, the error messages can - be retrieved with log(). - - Subclasses can override this function to initialize attributes - and uniform variables for use in specific shader programs. - - If the shader program was already linked, calling this - function again will force it to be re-linked. - - \sa addShader(), log() -*/ -bool QGLShaderProgram::link() -{ - Q_D(QGLShaderProgram); - GLuint program = d->programGuard ? d->programGuard->id() : 0; - if (!program) - return false; - - GLint value; - if (d->shaders.isEmpty()) { - // If there are no explicit shaders, then it is possible that the - // application added a program binary with glProgramBinaryOES(), - // or otherwise populated the shaders itself. Check to see if the - // program is already linked and bail out if so. - value = 0; - d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); - d->linked = (value != 0); - if (d->linked) - return true; - } - -#if !defined(QT_OPENGL_ES_2) - // Set up the geometry shader parameters - if (!QOpenGLContext::currentContext()->isOpenGLES() - && d->glfuncs->glProgramParameteri) { - for (QGLShader *shader : qAsConst(d->shaders)) { - if (shader->shaderType() & QGLShader::Geometry) { - d->glfuncs->glProgramParameteri(program, GL_GEOMETRY_INPUT_TYPE_EXT, - d->geometryInputType); - d->glfuncs->glProgramParameteri(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, - d->geometryOutputType); - d->glfuncs->glProgramParameteri(program, GL_GEOMETRY_VERTICES_OUT_EXT, - d->geometryVertexCount); - break; - } - } - } -#endif - - d->glfuncs->glLinkProgram(program); - value = 0; - d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); - d->linked = (value != 0); - value = 0; - d->glfuncs->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value); - d->log = QString(); - if (value > 1) { - char *logbuf = new char [value]; - GLint len; - d->glfuncs->glGetProgramInfoLog(program, value, &len, logbuf); - d->log = QString::fromLatin1(logbuf); - QString name = objectName(); - if (!d->linked) { - if (name.isEmpty()) - qWarning() << "QGLShader::link:" << d->log; - else - qWarning() << "QGLShader::link[" << name << "]:" << d->log; - } - delete [] logbuf; - } - return d->linked; -} - -/*! - Returns \c true if this shader program has been linked; false otherwise. - - \sa link() -*/ -bool QGLShaderProgram::isLinked() const -{ - Q_D(const QGLShaderProgram); - return d->linked; -} - -/*! - Returns the errors and warnings that occurred during the last link() - or addShader() with explicitly specified source code. - - \sa link() -*/ -QString QGLShaderProgram::log() const -{ - Q_D(const QGLShaderProgram); - return d->log; -} - -/*! - Binds this shader program to the active QGLContext and makes - it the current shader program. Any previously bound shader program - is released. This is equivalent to calling \c{glUseProgram()} on - programId(). Returns \c true if the program was successfully bound; - false otherwise. If the shader program has not yet been linked, - or it needs to be re-linked, this function will call link(). - - \sa link(), release() -*/ -bool QGLShaderProgram::bind() -{ - Q_D(QGLShaderProgram); - GLuint program = d->programGuard ? d->programGuard->id() : 0; - if (!program) - return false; - if (!d->linked && !link()) - return false; -#ifndef QT_NO_DEBUG - if (d->programGuard->group() != QOpenGLContextGroup::currentContextGroup()) { - qWarning("QGLShaderProgram::bind: program is not valid in the current context."); - return false; - } -#endif - d->glfuncs->glUseProgram(program); - return true; -} - -#undef ctx -#define ctx QGLContext::currentContext() - -/*! - Releases the active shader program from the current QGLContext. - This is equivalent to calling \c{glUseProgram(0)}. - - \sa bind() -*/ -void QGLShaderProgram::release() -{ - Q_D(QGLShaderProgram); -#ifndef QT_NO_DEBUG - if (d->programGuard && d->programGuard->group() != QOpenGLContextGroup::currentContextGroup()) - qWarning("QGLShaderProgram::release: program is not valid in the current context."); -#endif - d->glfuncs->glUseProgram(0); -} - -/*! - Returns the OpenGL identifier associated with this shader program. - - \sa QGLShader::shaderId() -*/ -GLuint QGLShaderProgram::programId() const -{ - Q_D(const QGLShaderProgram); - GLuint id = d->programGuard ? d->programGuard->id() : 0; - if (id) - return id; - - // Create the identifier if we don't have one yet. This is for - // applications that want to create the attached shader configuration - // themselves, particularly those using program binaries. - if (!const_cast<QGLShaderProgram *>(this)->init()) - return 0; - return d->programGuard ? d->programGuard->id() : 0; -} - -/*! - Binds the attribute \a name to the specified \a location. This - function can be called before or after the program has been linked. - Any attributes that have not been explicitly bound when the program - is linked will be assigned locations automatically. - - When this function is called after the program has been linked, - the program will need to be relinked for the change to take effect. - - \sa attributeLocation() -*/ -void QGLShaderProgram::bindAttributeLocation(const char *name, int location) -{ - Q_D(QGLShaderProgram); - if (!init() || !d->programGuard || !d->programGuard->id()) - return; - d->glfuncs->glBindAttribLocation(d->programGuard->id(), location, name); - d->linked = false; // Program needs to be relinked. -} - -/*! - \overload - - Binds the attribute \a name to the specified \a location. This - function can be called before or after the program has been linked. - Any attributes that have not been explicitly bound when the program - is linked will be assigned locations automatically. - - When this function is called after the program has been linked, - the program will need to be relinked for the change to take effect. - - \sa attributeLocation() -*/ -void QGLShaderProgram::bindAttributeLocation(const QByteArray& name, int location) -{ - bindAttributeLocation(name.constData(), location); -} - -/*! - \overload - - Binds the attribute \a name to the specified \a location. This - function can be called before or after the program has been linked. - Any attributes that have not been explicitly bound when the program - is linked will be assigned locations automatically. - - When this function is called after the program has been linked, - the program will need to be relinked for the change to take effect. - - \sa attributeLocation() -*/ -void QGLShaderProgram::bindAttributeLocation(const QString& name, int location) -{ - bindAttributeLocation(name.toLatin1().constData(), location); -} - -/*! - Returns the location of the attribute \a name within this shader - program's parameter list. Returns -1 if \a name is not a valid - attribute for this shader program. - - \sa uniformLocation(), bindAttributeLocation() -*/ -int QGLShaderProgram::attributeLocation(const char *name) const -{ - Q_D(const QGLShaderProgram); - if (d->linked && d->programGuard && d->programGuard->id()) { - return d->glfuncs->glGetAttribLocation(d->programGuard->id(), name); - } else { - qWarning() << "QGLShaderProgram::attributeLocation(" << name - << "): shader program is not linked"; - return -1; - } -} - -/*! - \overload - - Returns the location of the attribute \a name within this shader - program's parameter list. Returns -1 if \a name is not a valid - attribute for this shader program. - - \sa uniformLocation(), bindAttributeLocation() -*/ -int QGLShaderProgram::attributeLocation(const QByteArray& name) const -{ - return attributeLocation(name.constData()); -} - -/*! - \overload - - Returns the location of the attribute \a name within this shader - program's parameter list. Returns -1 if \a name is not a valid - attribute for this shader program. - - \sa uniformLocation(), bindAttributeLocation() -*/ -int QGLShaderProgram::attributeLocation(const QString& name) const -{ - return attributeLocation(name.toLatin1().constData()); -} - -/*! - Sets the attribute at \a location in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(int location, GLfloat value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glVertexAttrib1fv(location, &value); -} - -/*! - \overload - - Sets the attribute called \a name in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(const char *name, GLfloat value) -{ - setAttributeValue(attributeLocation(name), value); -} - -/*! - Sets the attribute at \a location in the current context to - the 2D vector (\a x, \a y). - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(int location, GLfloat x, GLfloat y) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[2] = {x, y}; - d->glfuncs->glVertexAttrib2fv(location, values); - } -} - -/*! - \overload - - Sets the attribute called \a name in the current context to - the 2D vector (\a x, \a y). - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(const char *name, GLfloat x, GLfloat y) -{ - setAttributeValue(attributeLocation(name), x, y); -} - -/*! - Sets the attribute at \a location in the current context to - the 3D vector (\a x, \a y, \a z). - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue - (int location, GLfloat x, GLfloat y, GLfloat z) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[3] = {x, y, z}; - d->glfuncs->glVertexAttrib3fv(location, values); - } -} - -/*! - \overload - - Sets the attribute called \a name in the current context to - the 3D vector (\a x, \a y, \a z). - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue - (const char *name, GLfloat x, GLfloat y, GLfloat z) -{ - setAttributeValue(attributeLocation(name), x, y, z); -} - -/*! - Sets the attribute at \a location in the current context to - the 4D vector (\a x, \a y, \a z, \a w). - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue - (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {x, y, z, w}; - d->glfuncs->glVertexAttrib4fv(location, values); - } -} - -/*! - \overload - - Sets the attribute called \a name in the current context to - the 4D vector (\a x, \a y, \a z, \a w). - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue - (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - setAttributeValue(attributeLocation(name), x, y, z, w); -} - -/*! - Sets the attribute at \a location in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(int location, const QVector2D& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glVertexAttrib2fv(location, reinterpret_cast<const GLfloat *>(&value)); -} - -/*! - \overload - - Sets the attribute called \a name in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(const char *name, const QVector2D& value) -{ - setAttributeValue(attributeLocation(name), value); -} - -/*! - Sets the attribute at \a location in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(int location, const QVector3D& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glVertexAttrib3fv(location, reinterpret_cast<const GLfloat *>(&value)); -} - -/*! - \overload - - Sets the attribute called \a name in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(const char *name, const QVector3D& value) -{ - setAttributeValue(attributeLocation(name), value); -} - -/*! - Sets the attribute at \a location in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(int location, const QVector4D& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glVertexAttrib4fv(location, reinterpret_cast<const GLfloat *>(&value)); -} - -/*! - \overload - - Sets the attribute called \a name in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(const char *name, const QVector4D& value) -{ - setAttributeValue(attributeLocation(name), value); -} - -/*! - Sets the attribute at \a location in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(int location, const QColor& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {GLfloat(value.redF()), GLfloat(value.greenF()), - GLfloat(value.blueF()), GLfloat(value.alphaF())}; - d->glfuncs->glVertexAttrib4fv(location, values); - } -} - -/*! - \overload - - Sets the attribute called \a name in the current context to \a value. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue(const char *name, const QColor& value) -{ - setAttributeValue(attributeLocation(name), value); -} - -/*! - Sets the attribute at \a location in the current context to the - contents of \a values, which contains \a columns elements, each - consisting of \a rows elements. The \a rows value should be - 1, 2, 3, or 4. This function is typically used to set matrix - values and column vectors. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue - (int location, const GLfloat *values, int columns, int rows) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (rows < 1 || rows > 4) { - qWarning() << "QGLShaderProgram::setAttributeValue: rows" << rows << "not supported"; - return; - } - if (location != -1) { - while (columns-- > 0) { - if (rows == 1) - d->glfuncs->glVertexAttrib1fv(location, values); - else if (rows == 2) - d->glfuncs->glVertexAttrib2fv(location, values); - else if (rows == 3) - d->glfuncs->glVertexAttrib3fv(location, values); - else - d->glfuncs->glVertexAttrib4fv(location, values); - values += rows; - ++location; - } - } -} - -/*! - \overload - - Sets the attribute called \a name in the current context to the - contents of \a values, which contains \a columns elements, each - consisting of \a rows elements. The \a rows value should be - 1, 2, 3, or 4. This function is typically used to set matrix - values and column vectors. - - \sa setUniformValue() -*/ -void QGLShaderProgram::setAttributeValue - (const char *name, const GLfloat *values, int columns, int rows) -{ - setAttributeValue(attributeLocation(name), values, columns, rows); -} - -/*! - Sets an array of vertex \a values on the attribute at \a location - in this shader program. The \a tupleSize indicates the number of - components per vertex (1, 2, 3, or 4), and the \a stride indicates - the number of bytes between vertices. A default \a stride value - of zero indicates that the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on the \a location. Otherwise the value specified with - setAttributeValue() for \a location will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (int location, const GLfloat *values, int tupleSize, int stride) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - d->glfuncs->glVertexAttribPointer(location, tupleSize, GL_FLOAT, GL_FALSE, - stride, values); - } -} - -/*! - Sets an array of 2D vertex \a values on the attribute at \a location - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on the \a location. Otherwise the value specified with - setAttributeValue() for \a location will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (int location, const QVector2D *values, int stride) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - d->glfuncs->glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, - stride, values); - } -} - -/*! - Sets an array of 3D vertex \a values on the attribute at \a location - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on the \a location. Otherwise the value specified with - setAttributeValue() for \a location will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (int location, const QVector3D *values, int stride) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - d->glfuncs->glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, - stride, values); - } -} - -/*! - Sets an array of 4D vertex \a values on the attribute at \a location - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on the \a location. Otherwise the value specified with - setAttributeValue() for \a location will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (int location, const QVector4D *values, int stride) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - d->glfuncs->glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, - stride, values); - } -} - -/*! - Sets an array of vertex \a values on the attribute at \a location - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The \a type indicates the type of elements in the \a values array, - usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize - indicates the number of components per vertex: 1, 2, 3, or 4. - - The array will become active when enableAttributeArray() is called - on the \a location. Otherwise the value specified with - setAttributeValue() for \a location will be used. - - The setAttributeBuffer() function can be used to set the attribute - array to an offset within a vertex buffer. - - \note Normalization will be enabled. If this is not desired, call - glVertexAttribPointer directly through QGLFunctions. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray(), setAttributeBuffer() - \since 4.7 -*/ -void QGLShaderProgram::setAttributeArray - (int location, GLenum type, const void *values, int tupleSize, int stride) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - d->glfuncs->glVertexAttribPointer(location, tupleSize, type, GL_TRUE, - stride, values); - } -} - -/*! - \overload - - Sets an array of vertex \a values on the attribute called \a name - in this shader program. The \a tupleSize indicates the number of - components per vertex (1, 2, 3, or 4), and the \a stride indicates - the number of bytes between vertices. A default \a stride value - of zero indicates that the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on \a name. Otherwise the value specified with setAttributeValue() - for \a name will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (const char *name, const GLfloat *values, int tupleSize, int stride) -{ - setAttributeArray(attributeLocation(name), values, tupleSize, stride); -} - -/*! - \overload - - Sets an array of 2D vertex \a values on the attribute called \a name - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on \a name. Otherwise the value specified with setAttributeValue() - for \a name will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (const char *name, const QVector2D *values, int stride) -{ - setAttributeArray(attributeLocation(name), values, stride); -} - -/*! - \overload - - Sets an array of 3D vertex \a values on the attribute called \a name - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on \a name. Otherwise the value specified with setAttributeValue() - for \a name will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (const char *name, const QVector3D *values, int stride) -{ - setAttributeArray(attributeLocation(name), values, stride); -} - -/*! - \overload - - Sets an array of 4D vertex \a values on the attribute called \a name - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The array will become active when enableAttributeArray() is called - on \a name. Otherwise the value specified with setAttributeValue() - for \a name will be used. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray() -*/ -void QGLShaderProgram::setAttributeArray - (const char *name, const QVector4D *values, int stride) -{ - setAttributeArray(attributeLocation(name), values, stride); -} - -/*! - \overload - - Sets an array of vertex \a values on the attribute called \a name - in this shader program. The \a stride indicates the number of bytes - between vertices. A default \a stride value of zero indicates that - the vertices are densely packed in \a values. - - The \a type indicates the type of elements in the \a values array, - usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize - indicates the number of components per vertex: 1, 2, 3, or 4. - - The array will become active when enableAttributeArray() is called - on the \a name. Otherwise the value specified with - setAttributeValue() for \a name will be used. - - The setAttributeBuffer() function can be used to set the attribute - array to an offset within a vertex buffer. - - \sa setAttributeValue(), setUniformValue(), enableAttributeArray() - \sa disableAttributeArray(), setAttributeBuffer() - \since 4.7 -*/ -void QGLShaderProgram::setAttributeArray - (const char *name, GLenum type, const void *values, int tupleSize, int stride) -{ - setAttributeArray(attributeLocation(name), type, values, tupleSize, stride); -} - -/*! - Sets an array of vertex values on the attribute at \a location in - this shader program, starting at a specific \a offset in the - currently bound vertex buffer. The \a stride indicates the number - of bytes between vertices. A default \a stride value of zero - indicates that the vertices are densely packed in the value array. - - The \a type indicates the type of elements in the vertex value - array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a - tupleSize indicates the number of components per vertex: 1, 2, 3, - or 4. - - The array will become active when enableAttributeArray() is called - on the \a location. Otherwise the value specified with - setAttributeValue() for \a location will be used. - - \note Normalization will be enabled. If this is not desired, call - glVertexAttribPointer directly though QGLFunctions. - - \sa setAttributeArray() - \since 4.7 -*/ -void QGLShaderProgram::setAttributeBuffer - (int location, GLenum type, int offset, int tupleSize, int stride) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - d->glfuncs->glVertexAttribPointer(location, tupleSize, type, GL_TRUE, stride, - reinterpret_cast<const void *>(qintptr(offset))); - } -} - -/*! - \overload - - Sets an array of vertex values on the attribute called \a name - in this shader program, starting at a specific \a offset in the - currently bound vertex buffer. The \a stride indicates the number - of bytes between vertices. A default \a stride value of zero - indicates that the vertices are densely packed in the value array. - - The \a type indicates the type of elements in the vertex value - array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a - tupleSize indicates the number of components per vertex: 1, 2, 3, - or 4. - - The array will become active when enableAttributeArray() is called - on the \a name. Otherwise the value specified with - setAttributeValue() for \a name will be used. - - \sa setAttributeArray() - \since 4.7 -*/ -void QGLShaderProgram::setAttributeBuffer - (const char *name, GLenum type, int offset, int tupleSize, int stride) -{ - setAttributeBuffer(attributeLocation(name), type, offset, tupleSize, stride); -} - -/*! - Enables the vertex array at \a location in this shader program - so that the value set by setAttributeArray() on \a location - will be used by the shader program. - - \sa disableAttributeArray(), setAttributeArray(), setAttributeValue() - \sa setUniformValue() -*/ -void QGLShaderProgram::enableAttributeArray(int location) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glEnableVertexAttribArray(location); -} - -/*! - \overload - - Enables the vertex array called \a name in this shader program - so that the value set by setAttributeArray() on \a name - will be used by the shader program. - - \sa disableAttributeArray(), setAttributeArray(), setAttributeValue() - \sa setUniformValue() -*/ -void QGLShaderProgram::enableAttributeArray(const char *name) -{ - enableAttributeArray(attributeLocation(name)); -} - -/*! - Disables the vertex array at \a location in this shader program - that was enabled by a previous call to enableAttributeArray(). - - \sa enableAttributeArray(), setAttributeArray(), setAttributeValue() - \sa setUniformValue() -*/ -void QGLShaderProgram::disableAttributeArray(int location) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glDisableVertexAttribArray(location); -} - -/*! - \overload - - Disables the vertex array called \a name in this shader program - that was enabled by a previous call to enableAttributeArray(). - - \sa enableAttributeArray(), setAttributeArray(), setAttributeValue() - \sa setUniformValue() -*/ -void QGLShaderProgram::disableAttributeArray(const char *name) -{ - disableAttributeArray(attributeLocation(name)); -} - -/*! - Returns the location of the uniform variable \a name within this shader - program's parameter list. Returns -1 if \a name is not a valid - uniform variable for this shader program. - - \sa attributeLocation() -*/ -int QGLShaderProgram::uniformLocation(const char *name) const -{ - Q_D(const QGLShaderProgram); - Q_UNUSED(d); - if (d->linked && d->programGuard && d->programGuard->id()) { - return d->glfuncs->glGetUniformLocation(d->programGuard->id(), name); - } else { - qWarning() << "QGLShaderProgram::uniformLocation(" << name - << "): shader program is not linked"; - return -1; - } -} - -/*! - \overload - - Returns the location of the uniform variable \a name within this shader - program's parameter list. Returns -1 if \a name is not a valid - uniform variable for this shader program. - - \sa attributeLocation() -*/ -int QGLShaderProgram::uniformLocation(const QByteArray& name) const -{ - return uniformLocation(name.constData()); -} - -/*! - \overload - - Returns the location of the uniform variable \a name within this shader - program's parameter list. Returns -1 if \a name is not a valid - uniform variable for this shader program. - - \sa attributeLocation() -*/ -int QGLShaderProgram::uniformLocation(const QString& name) const -{ - return uniformLocation(name.toLatin1().constData()); -} - -/*! - Sets the uniform variable at \a location in the current context to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, GLfloat value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform1fv(location, 1, &value); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, GLfloat value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, GLint value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform1i(location, value); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, GLint value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to \a value. - This function should be used when setting sampler values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, GLuint value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform1i(location, value); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to \a value. This function should be used when setting sampler values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, GLuint value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to - the 2D vector (\a x, \a y). - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, GLfloat x, GLfloat y) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[2] = {x, y}; - d->glfuncs->glUniform2fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context to - the 2D vector (\a x, \a y). - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, GLfloat x, GLfloat y) -{ - setUniformValue(uniformLocation(name), x, y); -} - -/*! - Sets the uniform variable at \a location in the current context to - the 3D vector (\a x, \a y, \a z). - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue - (int location, GLfloat x, GLfloat y, GLfloat z) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[3] = {x, y, z}; - d->glfuncs->glUniform3fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context to - the 3D vector (\a x, \a y, \a z). - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue - (const char *name, GLfloat x, GLfloat y, GLfloat z) -{ - setUniformValue(uniformLocation(name), x, y, z); -} - -/*! - Sets the uniform variable at \a location in the current context to - the 4D vector (\a x, \a y, \a z, \a w). - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue - (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {x, y, z, w}; - d->glfuncs->glUniform4fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context to - the 4D vector (\a x, \a y, \a z, \a w). - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue - (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - setUniformValue(uniformLocation(name), x, y, z, w); -} - -/*! - Sets the uniform variable at \a location in the current context to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QVector2D& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform2fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QVector2D& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QVector3D& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform3fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QVector3D& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QVector4D& value) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform4fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QVector4D& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to - the red, green, blue, and alpha components of \a color. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QColor& color) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {GLfloat(color.redF()), GLfloat(color.greenF()), - GLfloat(color.blueF()), GLfloat(color.alphaF())}; - d->glfuncs->glUniform4fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context to - the red, green, blue, and alpha components of \a color. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QColor& color) -{ - setUniformValue(uniformLocation(name), color); -} - -/*! - Sets the uniform variable at \a location in the current context to - the x and y coordinates of \a point. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QPoint& point) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())}; - d->glfuncs->glUniform2fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable associated with \a name in the current - context to the x and y coordinates of \a point. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QPoint& point) -{ - setUniformValue(uniformLocation(name), point); -} - -/*! - Sets the uniform variable at \a location in the current context to - the x and y coordinates of \a point. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QPointF& point) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())}; - d->glfuncs->glUniform2fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable associated with \a name in the current - context to the x and y coordinates of \a point. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QPointF& point) -{ - setUniformValue(uniformLocation(name), point); -} - -/*! - Sets the uniform variable at \a location in the current context to - the width and height of the given \a size. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QSize& size) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())}; - d->glfuncs->glUniform2fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable associated with \a name in the current - context to the width and height of the given \a size. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QSize& size) -{ - setUniformValue(uniformLocation(name), size); -} - -/*! - Sets the uniform variable at \a location in the current context to - the width and height of the given \a size. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QSizeF& size) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) { - GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())}; - d->glfuncs->glUniform2fv(location, 1, values); - } -} - -/*! - \overload - - Sets the uniform variable associated with \a name in the current - context to the width and height of the given \a size. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QSizeF& size) -{ - setUniformValue(uniformLocation(name), size); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 2x2 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix2x2& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniformMatrix2fv(location, 1, GL_FALSE, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 2x2 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix2x2& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 2x3 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix2x3& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniform3fv(location, 2, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 2x3 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix2x3& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 2x4 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix2x4& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniform4fv(location, 2, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 2x4 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix2x4& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 3x2 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix3x2& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniform2fv(location, 3, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 3x2 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix3x2& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 3x3 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix3x3& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 3x3 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix3x3& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 3x4 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix3x4& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniform4fv(location, 3, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 3x4 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix3x4& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 4x2 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix4x2& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniform2fv(location, 4, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 4x2 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x2& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 4x3 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix4x3& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniform3fv(location, 4, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 4x3 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x3& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context - to a 4x4 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const QMatrix4x4& value) -{ - Q_D(QGLShaderProgram); - d->glfuncs->glUniformMatrix4fv(location, 1, GL_FALSE, value.constData()); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 4x4 matrix \a value. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x4& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - \overload - - Sets the uniform variable at \a location in the current context - to a 2x2 matrix \a value. The matrix elements must be specified - in column-major order. - - \sa setAttributeValue() - \since 4.7 -*/ -void QGLShaderProgram::setUniformValue(int location, const GLfloat value[2][2]) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniformMatrix2fv(location, 1, GL_FALSE, value[0]); -} - -/*! - \overload - - Sets the uniform variable at \a location in the current context - to a 3x3 matrix \a value. The matrix elements must be specified - in column-major order. - - \sa setAttributeValue() - \since 4.7 -*/ -void QGLShaderProgram::setUniformValue(int location, const GLfloat value[3][3]) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, value[0]); -} - -/*! - \overload - - Sets the uniform variable at \a location in the current context - to a 4x4 matrix \a value. The matrix elements must be specified - in column-major order. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4]) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniformMatrix4fv(location, 1, GL_FALSE, value[0]); -} - - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 2x2 matrix \a value. The matrix elements must be specified - in column-major order. - - \sa setAttributeValue() - \since 4.7 -*/ -void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[2][2]) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 3x3 matrix \a value. The matrix elements must be specified - in column-major order. - - \sa setAttributeValue() - \since 4.7 -*/ -void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[3][3]) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context - to a 4x4 matrix \a value. The matrix elements must be specified - in column-major order. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[4][4]) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable at \a location in the current context to a - 3x3 transformation matrix \a value that is specified as a QTransform value. - - To set a QTransform value as a 4x4 matrix in a shader, use - \c{setUniformValue(location, QMatrix4x4(value))}. -*/ -void QGLShaderProgram::setUniformValue(int location, const QTransform& value) -{ - Q_D(QGLShaderProgram); - if (location != -1) { - GLfloat mat[3][3] = { - {GLfloat(value.m11()), GLfloat(value.m12()), GLfloat(value.m13())}, - {GLfloat(value.m21()), GLfloat(value.m22()), GLfloat(value.m23())}, - {GLfloat(value.m31()), GLfloat(value.m32()), GLfloat(value.m33())} - }; - d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, mat[0]); - } -} - -/*! - \overload - - Sets the uniform variable called \a name in the current context to a - 3x3 transformation matrix \a value that is specified as a QTransform value. - - To set a QTransform value as a 4x4 matrix in a shader, use - \c{setUniformValue(name, QMatrix4x4(value))}. -*/ -void QGLShaderProgram::setUniformValue - (const char *name, const QTransform& value) -{ - setUniformValue(uniformLocation(name), value); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const GLint *values, int count) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniform1iv(location, count, values); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray - (const char *name, const GLint *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count elements of \a values. This overload - should be used when setting an array of sampler values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const GLuint *values, int count) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniform1iv(location, count, reinterpret_cast<const GLint *>(values)); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count elements of \a values. This overload - should be used when setting an array of sampler values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray - (const char *name, const GLuint *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count elements of \a values. Each element - has \a tupleSize components. The \a tupleSize must be 1, 2, 3, or 4. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const GLfloat *values, int count, int tupleSize) -{ - Q_D(QGLShaderProgram); - if (location != -1) { - if (tupleSize == 1) - d->glfuncs->glUniform1fv(location, count, values); - else if (tupleSize == 2) - d->glfuncs->glUniform2fv(location, count, values); - else if (tupleSize == 3) - d->glfuncs->glUniform3fv(location, count, values); - else if (tupleSize == 4) - d->glfuncs->glUniform4fv(location, count, values); - else - qWarning() << "QGLShaderProgram::setUniformValue: size" << tupleSize << "not supported"; - } -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count elements of \a values. Each element - has \a tupleSize components. The \a tupleSize must be 1, 2, 3, or 4. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray - (const char *name, const GLfloat *values, int count, int tupleSize) -{ - setUniformValueArray(uniformLocation(name), values, count, tupleSize); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 2D vector elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QVector2D *values, int count) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniform2fv(location, count, reinterpret_cast<const GLfloat *>(values)); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 2D vector elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QVector2D *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 3D vector elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QVector3D *values, int count) -{ - Q_D(QGLShaderProgram); - if (location != -1) - d->glfuncs->glUniform3fv(location, count, reinterpret_cast<const GLfloat *>(values)); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 3D vector elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QVector3D *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 4D vector elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QVector4D *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - if (location != -1) - d->glfuncs->glUniform4fv(location, count, reinterpret_cast<const GLfloat *>(values)); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 4D vector elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QVector4D *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -// We have to repack matrix arrays from qreal to GLfloat. -#define setUniformMatrixArray(func,location,values,count,type,cols,rows) \ - if (location == -1 || count <= 0) \ - return; \ - if (sizeof(type) == sizeof(GLfloat) * cols * rows) { \ - func(location, count, GL_FALSE, \ - reinterpret_cast<const GLfloat *>(values[0].constData())); \ - } else { \ - QVarLengthArray<GLfloat> temp(cols * rows * count); \ - for (int index = 0; index < count; ++index) { \ - for (int index2 = 0; index2 < (cols * rows); ++index2) { \ - temp.data()[cols * rows * index + index2] = \ - values[index].constData()[index2]; \ - } \ - } \ - func(location, count, GL_FALSE, temp.constData()); \ - } -#define setUniformGenericMatrixArray(colfunc,location,values,count,type,cols,rows) \ - if (location == -1 || count <= 0) \ - return; \ - if (sizeof(type) == sizeof(GLfloat) * cols * rows) { \ - const GLfloat *data = reinterpret_cast<const GLfloat *> \ - (values[0].constData()); \ - colfunc(location, count * cols, data); \ - } else { \ - QVarLengthArray<GLfloat> temp(cols * rows * count); \ - for (int index = 0; index < count; ++index) { \ - for (int index2 = 0; index2 < (cols * rows); ++index2) { \ - temp.data()[cols * rows * index + index2] = \ - values[index].constData()[index2]; \ - } \ - } \ - colfunc(location, count * cols, temp.constData()); \ - } - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 2x2 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x2 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformMatrixArray - (d->glfuncs->glUniformMatrix2fv, location, values, count, QMatrix2x2, 2, 2); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 2x2 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x2 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 2x3 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x3 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformGenericMatrixArray - (d->glfuncs->glUniform3fv, location, values, count, - QMatrix2x3, 2, 3); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 2x3 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x3 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 2x4 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x4 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformGenericMatrixArray - (d->glfuncs->glUniform4fv, location, values, count, - QMatrix2x4, 2, 4); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 2x4 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x4 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 3x2 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x2 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformGenericMatrixArray - (d->glfuncs->glUniform2fv, location, values, count, - QMatrix3x2, 3, 2); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 3x2 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x2 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 3x3 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x3 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformMatrixArray - (d->glfuncs->glUniformMatrix3fv, location, values, count, QMatrix3x3, 3, 3); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 3x3 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x3 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 3x4 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x4 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformGenericMatrixArray - (d->glfuncs->glUniform4fv, location, values, count, - QMatrix3x4, 3, 4); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 3x4 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x4 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 4x2 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x2 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformGenericMatrixArray - (d->glfuncs->glUniform2fv, location, values, count, - QMatrix4x2, 4, 2); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 4x2 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x2 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 4x3 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x3 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformGenericMatrixArray - (d->glfuncs->glUniform3fv, location, values, count, - QMatrix4x3, 4, 3); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 4x3 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x3 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -/*! - Sets the uniform variable array at \a location in the current - context to the \a count 4x4 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x4 *values, int count) -{ - Q_D(QGLShaderProgram); - Q_UNUSED(d); - setUniformMatrixArray - (d->glfuncs->glUniformMatrix4fv, location, values, count, QMatrix4x4, 4, 4); -} - -/*! - \overload - - Sets the uniform variable array called \a name in the current - context to the \a count 4x4 matrix elements of \a values. - - \sa setAttributeValue() -*/ -void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x4 *values, int count) -{ - setUniformValueArray(uniformLocation(name), values, count); -} - -#undef ctx - -/*! - Returns the hardware limit for how many vertices a geometry shader - can output. - - \since 4.7 - - \sa setGeometryOutputVertexCount() -*/ -int QGLShaderProgram::maxGeometryOutputVertices() const -{ - GLint n = 0; -#if !defined(QT_OPENGL_ES_2) - Q_D(const QGLShaderProgram); - if (!QOpenGLContext::currentContext()->isOpenGLES()) - d->glfuncs->glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, &n); -#endif - return n; -} - -/*! - Sets the maximum number of vertices the current geometry shader - program will produce, if active, to \a count. - - \since 4.7 - - This parameter takes effect the next time the program is linked. -*/ -void QGLShaderProgram::setGeometryOutputVertexCount(int count) -{ -#ifndef QT_NO_DEBUG - int max = maxGeometryOutputVertices(); - if (count > max) { - qWarning("QGLShaderProgram::setGeometryOutputVertexCount: count: %d higher than maximum: %d", - count, max); - } -#endif - d_func()->geometryVertexCount = count; -} - - -/*! - Returns the maximum number of vertices the current geometry shader - program will produce, if active. - - \since 4.7 - - This parameter takes effect the ntext time the program is linked. -*/ -int QGLShaderProgram::geometryOutputVertexCount() const -{ - return d_func()->geometryVertexCount; -} - - -/*! - Sets the input type from \a inputType. - - This parameter takes effect the next time the program is linked. -*/ -void QGLShaderProgram::setGeometryInputType(GLenum inputType) -{ - d_func()->geometryInputType = inputType; -} - - -/*! - Returns the geometry shader input type, if active. - - This parameter takes effect the next time the program is linked. - - \since 4.7 - */ - -GLenum QGLShaderProgram::geometryInputType() const -{ - return d_func()->geometryInputType; -} - - -/*! - Sets the output type from the geometry shader, if active, to - \a outputType. - - This parameter takes effect the next time the program is linked. - - \since 4.7 -*/ -void QGLShaderProgram::setGeometryOutputType(GLenum outputType) -{ - d_func()->geometryOutputType = outputType; -} - - -/*! - Returns the geometry shader output type, if active. - - This parameter takes effect the next time the program is linked. - - \since 4.7 - */ -GLenum QGLShaderProgram::geometryOutputType() const -{ - return d_func()->geometryOutputType; -} - - -/*! - Returns \c true if shader programs written in the OpenGL Shading - Language (GLSL) are supported on this system; false otherwise. - - The \a context is used to resolve the GLSL extensions. - If \a context is \nullptr, then QGLContext::currentContext() is - used. -*/ -bool QGLShaderProgram::hasOpenGLShaderPrograms(const QGLContext *context) -{ -#if !defined(QT_OPENGL_ES_2) - if (!context) - context = QGLContext::currentContext(); - if (!context) - return false; - - QOpenGLFunctions functions(context->contextHandle()); - return functions.hasOpenGLFeature(QOpenGLFunctions::Shaders); -#else - Q_UNUSED(context); - return true; -#endif -} - -/*! - \internal -*/ -void QGLShaderProgram::shaderDestroyed() -{ - Q_D(QGLShaderProgram); - QGLShader *shader = qobject_cast<QGLShader *>(sender()); - if (shader && !d->removingShaders) - removeShader(shader); -} - - -#undef ctx -#undef context - -/*! - Returns \c true if shader programs of type \a type are supported on - this system; false otherwise. - - The \a context is used to resolve the GLSL extensions. - If \a context is \nullptr, then QGLContext::currentContext() is - used. - - \since 4.7 -*/ -bool QGLShader::hasOpenGLShaders(ShaderType type, const QGLContext *context) -{ - if (!context) - context = QGLContext::currentContext(); - if (!context) - return false; - - if ((type & ~(Geometry | Vertex | Fragment)) || type == 0) - return false; - - QOpenGLFunctions functions(context->contextHandle()); - bool resolved = functions.hasOpenGLFeature(QOpenGLFunctions::Shaders); - if (!resolved) - return false; - - if ((type & Geometry) && !QByteArray((const char *) functions.glGetString(GL_EXTENSIONS)).contains("GL_EXT_geometry_shader4")) - return false; - - return true; -} - -QT_END_NAMESPACE diff --git a/src/opengl/qglshaderprogram.h b/src/opengl/qglshaderprogram.h deleted file mode 100644 index 3ce88197d2..0000000000 --- a/src/opengl/qglshaderprogram.h +++ /dev/null @@ -1,303 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGLSHADERPROGRAM_H -#define QGLSHADERPROGRAM_H - -#include <QtOpenGL/qgl.h> -#include <QtGui/qvector2d.h> -#include <QtGui/qvector3d.h> -#include <QtGui/qvector4d.h> -#include <QtGui/qmatrix4x4.h> - -QT_BEGIN_NAMESPACE - - -class QGLShaderProgram; -class QGLShaderPrivate; - -class Q_OPENGL_EXPORT QGLShader : public QObject -{ - Q_OBJECT -public: - enum ShaderTypeBit - { - Vertex = 0x0001, - Fragment = 0x0002, - Geometry = 0x0004 - }; - Q_DECLARE_FLAGS(ShaderType, ShaderTypeBit) - - explicit QGLShader(QGLShader::ShaderType type, QObject *parent = nullptr); - QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObject *parent = nullptr); - virtual ~QGLShader(); - - QGLShader::ShaderType shaderType() const; - - bool compileSourceCode(const char *source); - bool compileSourceCode(const QByteArray& source); - bool compileSourceCode(const QString& source); - bool compileSourceFile(const QString& fileName); - - QByteArray sourceCode() const; - - bool isCompiled() const; - QString log() const; - - GLuint shaderId() const; - - static bool hasOpenGLShaders(ShaderType type, const QGLContext *context = nullptr); - -private: - friend class QGLShaderProgram; - - Q_DISABLE_COPY(QGLShader) - Q_DECLARE_PRIVATE(QGLShader) -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QGLShader::ShaderType) - - -class QGLShaderProgramPrivate; - -class Q_OPENGL_EXPORT QGLShaderProgram : public QObject -{ - Q_OBJECT -public: - explicit QGLShaderProgram(QObject *parent = nullptr); - explicit QGLShaderProgram(const QGLContext *context, QObject *parent = nullptr); - virtual ~QGLShaderProgram(); - - bool addShader(QGLShader *shader); - void removeShader(QGLShader *shader); - QList<QGLShader *> shaders() const; - - bool addShaderFromSourceCode(QGLShader::ShaderType type, const char *source); - bool addShaderFromSourceCode(QGLShader::ShaderType type, const QByteArray& source); - bool addShaderFromSourceCode(QGLShader::ShaderType type, const QString& source); - bool addShaderFromSourceFile(QGLShader::ShaderType type, const QString& fileName); - - void removeAllShaders(); - - virtual bool link(); - bool isLinked() const; - QString log() const; - - bool bind(); - void release(); - - GLuint programId() const; - - int maxGeometryOutputVertices() const; - - void setGeometryOutputVertexCount(int count); - int geometryOutputVertexCount() const; - - void setGeometryInputType(GLenum inputType); - GLenum geometryInputType() const; - - void setGeometryOutputType(GLenum outputType); - GLenum geometryOutputType() const; - - void bindAttributeLocation(const char *name, int location); - void bindAttributeLocation(const QByteArray& name, int location); - void bindAttributeLocation(const QString& name, int location); - - int attributeLocation(const char *name) const; - int attributeLocation(const QByteArray& name) const; - int attributeLocation(const QString& name) const; - - void setAttributeValue(int location, GLfloat value); - void setAttributeValue(int location, GLfloat x, GLfloat y); - void setAttributeValue(int location, GLfloat x, GLfloat y, GLfloat z); - void setAttributeValue(int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); - void setAttributeValue(int location, const QVector2D& value); - void setAttributeValue(int location, const QVector3D& value); - void setAttributeValue(int location, const QVector4D& value); - void setAttributeValue(int location, const QColor& value); - void setAttributeValue(int location, const GLfloat *values, int columns, int rows); - - void setAttributeValue(const char *name, GLfloat value); - void setAttributeValue(const char *name, GLfloat x, GLfloat y); - void setAttributeValue(const char *name, GLfloat x, GLfloat y, GLfloat z); - void setAttributeValue(const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); - void setAttributeValue(const char *name, const QVector2D& value); - void setAttributeValue(const char *name, const QVector3D& value); - void setAttributeValue(const char *name, const QVector4D& value); - void setAttributeValue(const char *name, const QColor& value); - void setAttributeValue(const char *name, const GLfloat *values, int columns, int rows); - - void setAttributeArray - (int location, const GLfloat *values, int tupleSize, int stride = 0); - void setAttributeArray - (int location, const QVector2D *values, int stride = 0); - void setAttributeArray - (int location, const QVector3D *values, int stride = 0); - void setAttributeArray - (int location, const QVector4D *values, int stride = 0); - void setAttributeArray - (int location, GLenum type, const void *values, int tupleSize, int stride = 0); - void setAttributeArray - (const char *name, const GLfloat *values, int tupleSize, int stride = 0); - void setAttributeArray - (const char *name, const QVector2D *values, int stride = 0); - void setAttributeArray - (const char *name, const QVector3D *values, int stride = 0); - void setAttributeArray - (const char *name, const QVector4D *values, int stride = 0); - void setAttributeArray - (const char *name, GLenum type, const void *values, int tupleSize, int stride = 0); - - void setAttributeBuffer - (int location, GLenum type, int offset, int tupleSize, int stride = 0); - void setAttributeBuffer - (const char *name, GLenum type, int offset, int tupleSize, int stride = 0); - - void enableAttributeArray(int location); - void enableAttributeArray(const char *name); - void disableAttributeArray(int location); - void disableAttributeArray(const char *name); - - int uniformLocation(const char *name) const; - int uniformLocation(const QByteArray& name) const; - int uniformLocation(const QString& name) const; - - void setUniformValue(int location, GLfloat value); - void setUniformValue(int location, GLint value); - void setUniformValue(int location, GLuint value); - void setUniformValue(int location, GLfloat x, GLfloat y); - void setUniformValue(int location, GLfloat x, GLfloat y, GLfloat z); - void setUniformValue(int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); - void setUniformValue(int location, const QVector2D& value); - void setUniformValue(int location, const QVector3D& value); - void setUniformValue(int location, const QVector4D& value); - void setUniformValue(int location, const QColor& color); - void setUniformValue(int location, const QPoint& point); - void setUniformValue(int location, const QPointF& point); - void setUniformValue(int location, const QSize& size); - void setUniformValue(int location, const QSizeF& size); - void setUniformValue(int location, const QMatrix2x2& value); - void setUniformValue(int location, const QMatrix2x3& value); - void setUniformValue(int location, const QMatrix2x4& value); - void setUniformValue(int location, const QMatrix3x2& value); - void setUniformValue(int location, const QMatrix3x3& value); - void setUniformValue(int location, const QMatrix3x4& value); - void setUniformValue(int location, const QMatrix4x2& value); - void setUniformValue(int location, const QMatrix4x3& value); - void setUniformValue(int location, const QMatrix4x4& value); - void setUniformValue(int location, const GLfloat value[2][2]); - void setUniformValue(int location, const GLfloat value[3][3]); - void setUniformValue(int location, const GLfloat value[4][4]); - void setUniformValue(int location, const QTransform& value); - - void setUniformValue(const char *name, GLfloat value); - void setUniformValue(const char *name, GLint value); - void setUniformValue(const char *name, GLuint value); - void setUniformValue(const char *name, GLfloat x, GLfloat y); - void setUniformValue(const char *name, GLfloat x, GLfloat y, GLfloat z); - void setUniformValue(const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); - void setUniformValue(const char *name, const QVector2D& value); - void setUniformValue(const char *name, const QVector3D& value); - void setUniformValue(const char *name, const QVector4D& value); - void setUniformValue(const char *name, const QColor& color); - void setUniformValue(const char *name, const QPoint& point); - void setUniformValue(const char *name, const QPointF& point); - void setUniformValue(const char *name, const QSize& size); - void setUniformValue(const char *name, const QSizeF& size); - void setUniformValue(const char *name, const QMatrix2x2& value); - void setUniformValue(const char *name, const QMatrix2x3& value); - void setUniformValue(const char *name, const QMatrix2x4& value); - void setUniformValue(const char *name, const QMatrix3x2& value); - void setUniformValue(const char *name, const QMatrix3x3& value); - void setUniformValue(const char *name, const QMatrix3x4& value); - void setUniformValue(const char *name, const QMatrix4x2& value); - void setUniformValue(const char *name, const QMatrix4x3& value); - void setUniformValue(const char *name, const QMatrix4x4& value); - void setUniformValue(const char *name, const GLfloat value[2][2]); - void setUniformValue(const char *name, const GLfloat value[3][3]); - void setUniformValue(const char *name, const GLfloat value[4][4]); - void setUniformValue(const char *name, const QTransform& value); - - void setUniformValueArray(int location, const GLfloat *values, int count, int tupleSize); - void setUniformValueArray(int location, const GLint *values, int count); - void setUniformValueArray(int location, const GLuint *values, int count); - void setUniformValueArray(int location, const QVector2D *values, int count); - void setUniformValueArray(int location, const QVector3D *values, int count); - void setUniformValueArray(int location, const QVector4D *values, int count); - void setUniformValueArray(int location, const QMatrix2x2 *values, int count); - void setUniformValueArray(int location, const QMatrix2x3 *values, int count); - void setUniformValueArray(int location, const QMatrix2x4 *values, int count); - void setUniformValueArray(int location, const QMatrix3x2 *values, int count); - void setUniformValueArray(int location, const QMatrix3x3 *values, int count); - void setUniformValueArray(int location, const QMatrix3x4 *values, int count); - void setUniformValueArray(int location, const QMatrix4x2 *values, int count); - void setUniformValueArray(int location, const QMatrix4x3 *values, int count); - void setUniformValueArray(int location, const QMatrix4x4 *values, int count); - - void setUniformValueArray(const char *name, const GLfloat *values, int count, int tupleSize); - void setUniformValueArray(const char *name, const GLint *values, int count); - void setUniformValueArray(const char *name, const GLuint *values, int count); - void setUniformValueArray(const char *name, const QVector2D *values, int count); - void setUniformValueArray(const char *name, const QVector3D *values, int count); - void setUniformValueArray(const char *name, const QVector4D *values, int count); - void setUniformValueArray(const char *name, const QMatrix2x2 *values, int count); - void setUniformValueArray(const char *name, const QMatrix2x3 *values, int count); - void setUniformValueArray(const char *name, const QMatrix2x4 *values, int count); - void setUniformValueArray(const char *name, const QMatrix3x2 *values, int count); - void setUniformValueArray(const char *name, const QMatrix3x3 *values, int count); - void setUniformValueArray(const char *name, const QMatrix3x4 *values, int count); - void setUniformValueArray(const char *name, const QMatrix4x2 *values, int count); - void setUniformValueArray(const char *name, const QMatrix4x3 *values, int count); - void setUniformValueArray(const char *name, const QMatrix4x4 *values, int count); - - static bool hasOpenGLShaderPrograms(const QGLContext *context = nullptr); - -private Q_SLOTS: - void shaderDestroyed(); - -private: - Q_DISABLE_COPY(QGLShaderProgram) - Q_DECLARE_PRIVATE(QGLShaderProgram) - - bool init(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/opengl/qgraphicsshadereffect.cpp b/src/opengl/qgraphicsshadereffect.cpp deleted file mode 100644 index 97b83a6b3d..0000000000 --- a/src/opengl/qgraphicsshadereffect.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qgraphicsshadereffect_p.h" - -#include "qglshaderprogram.h" -#include "gl2paintengineex/qglcustomshaderstage_p.h" -#define QGL_HAVE_CUSTOM_SHADERS 1 -#include <QtGui/qpainter.h> -#include <QtWidgets/qgraphicsitem.h> -#include <private/qgraphicseffect_p.h> - -QT_BEGIN_NAMESPACE - -/*# - \class QGraphicsShaderEffect - \inmodule QtOpenGL - \brief The QGraphicsShaderEffect class is the base class for creating - custom GLSL shader effects in a QGraphicsScene. - \since 4.6 - \ingroup multimedia - \ingroup graphicsview-api - - The specific effect is defined by a fragment of GLSL source code - supplied to setPixelShaderFragment(). This source code must define a - function with the signature - \c{lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords)} - that returns the source pixel value - to use in the paint engine's shader program. The shader fragment - is linked with the regular shader code used by the GL2 paint engine - to construct a complete QGLShaderProgram. - - The following example shader converts the incoming pixmap to - grayscale and then applies a colorize operation using the - \c effectColor value: - - \snippet code/src_opengl_qgraphicsshadereffect.cpp 0 - - To use this shader code, it is necessary to define a subclass - of QGraphicsShaderEffect as follows: - - \snippet code/src_opengl_qgraphicsshadereffect.cpp 1 - - The setUniforms() function is called when the effect is about - to be used for drawing to give the subclass the opportunity to - set effect-specific uniform variables. - - QGraphicsShaderEffect is only supported when the GL2 paint engine - is in use. When any other paint engine is in use (GL1, raster, etc), - the drawItem() method will draw its item argument directly with - no effect applied. - - \sa QGraphicsEffect -*/ - -static const char qglslDefaultImageFragmentShader[] = "\ - lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) { \ - return texture2D(imageTexture, textureCoords); \ - }\n"; - -#ifdef QGL_HAVE_CUSTOM_SHADERS - -class QGLCustomShaderEffectStage : public QGLCustomShaderStage -{ -public: - QGLCustomShaderEffectStage - (QGraphicsShaderEffect *e, const QByteArray& source) - : QGLCustomShaderStage(), - effect(e) - { - setSource(source); - } - - void setUniforms(QGLShaderProgram *program) override; - - QGraphicsShaderEffect *effect; -}; - -void QGLCustomShaderEffectStage::setUniforms(QGLShaderProgram *program) -{ - effect->setUniforms(program); -} - -#endif - -class QGraphicsShaderEffectPrivate : public QGraphicsEffectPrivate -{ - Q_DECLARE_PUBLIC(QGraphicsShaderEffect) -public: - QGraphicsShaderEffectPrivate() - : pixelShaderFragment(qglslDefaultImageFragmentShader) -#ifdef QGL_HAVE_CUSTOM_SHADERS - , customShaderStage(0) -#endif - { - } - - QByteArray pixelShaderFragment; -#ifdef QGL_HAVE_CUSTOM_SHADERS - QGLCustomShaderEffectStage *customShaderStage; -#endif -}; - -/*# - Constructs a shader effect and attaches it to \a parent. -*/ -QGraphicsShaderEffect::QGraphicsShaderEffect(QObject *parent) - : QGraphicsEffect(*new QGraphicsShaderEffectPrivate(), parent) -{ -} - -/*# - Destroys this shader effect. -*/ -QGraphicsShaderEffect::~QGraphicsShaderEffect() -{ -#ifdef QGL_HAVE_CUSTOM_SHADERS - Q_D(QGraphicsShaderEffect); - delete d->customShaderStage; -#endif -} - -/*# - Returns the source code for the pixel shader fragment for - this shader effect. The default is a shader that copies - its incoming pixmap directly to the output with no effect - applied. - - \sa setPixelShaderFragment() -*/ -QByteArray QGraphicsShaderEffect::pixelShaderFragment() const -{ - Q_D(const QGraphicsShaderEffect); - return d->pixelShaderFragment; -} - -/*# - Sets the source code for the pixel shader fragment for - this shader effect to \a code. - - The \a code must define a GLSL function with the signature - \c{lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords)} - that returns the source pixel value to use in the paint engine's - shader program. The following is the default pixel shader fragment, - which draws a pixmap with no effect applied: - - \snippet code/src_opengl_qgraphicsshadereffect.cpp 2 - - \sa pixelShaderFragment(), setUniforms() -*/ -void QGraphicsShaderEffect::setPixelShaderFragment(const QByteArray& code) -{ - Q_D(QGraphicsShaderEffect); - if (d->pixelShaderFragment != code) { - d->pixelShaderFragment = code; -#ifdef QGL_HAVE_CUSTOM_SHADERS - delete d->customShaderStage; - d->customShaderStage = 0; -#endif - } -} - -/*# - \reimp -*/ -void QGraphicsShaderEffect::draw(QPainter *painter) -{ - Q_D(QGraphicsShaderEffect); - -#ifdef QGL_HAVE_CUSTOM_SHADERS - // Set the custom shader on the paint engine. The setOnPainter() - // call may fail if the paint engine is not GL2. In that case, - // we fall through to drawing the pixmap normally. - if (!d->customShaderStage) { - d->customShaderStage = new QGLCustomShaderEffectStage - (this, d->pixelShaderFragment); - } - bool usingShader = d->customShaderStage->setOnPainter(painter); - - QPoint offset; - if (sourceIsPixmap()) { - // No point in drawing in device coordinates (pixmap will be scaled anyways). - const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset); - painter->drawPixmap(offset, pixmap); - } else { - // Draw pixmap in device coordinates to avoid pixmap scaling. - const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset); - QTransform restoreTransform = painter->worldTransform(); - painter->setWorldTransform(QTransform()); - painter->drawPixmap(offset, pixmap); - painter->setWorldTransform(restoreTransform); - } - - // Remove the custom shader to return to normal painting operations. - if (usingShader) - d->customShaderStage->removeFromPainter(painter); -#else - drawSource(painter); -#endif -} - -/*# - Sets the custom uniform variables on this shader effect to - be dirty. The setUniforms() function will be called the next - time the shader program corresponding to this effect is used. - - This function is typically called by subclasses when an - effect-specific parameter is changed by the application. - - \sa setUniforms() -*/ -void QGraphicsShaderEffect::setUniformsDirty() -{ -#ifdef QGL_HAVE_CUSTOM_SHADERS - Q_D(QGraphicsShaderEffect); - if (d->customShaderStage) - d->customShaderStage->setUniformsDirty(); -#endif -} - -/*# - Sets custom uniform variables on the current GL context when - \a program is about to be used by the paint engine. - - This function should be overridden if the shader set with - setPixelShaderFragment() has additional parameters beyond - those that the paint engine normally sets itself. - - \sa setUniformsDirty() -*/ -void QGraphicsShaderEffect::setUniforms(QGLShaderProgram *program) -{ - Q_UNUSED(program); -} - -QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/qopengl2pexvertexarray.cpp index 979ea19fee..df0fb764c9 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp +++ b/src/opengl/qopengl2pexvertexarray.cpp @@ -37,13 +37,13 @@ ** ****************************************************************************/ -#include "qgl2pexvertexarray_p.h" +#include "qopengl2pexvertexarray_p.h" #include <private/qbezier_p.h> QT_BEGIN_NAMESPACE -void QGL2PEXVertexArray::clear() +void QOpenGL2PEXVertexArray::clear() { vertexArray.reset(); vertexArrayStops.reset(); @@ -51,22 +51,22 @@ void QGL2PEXVertexArray::clear() } -QGLRect QGL2PEXVertexArray::boundingRect() const +QOpenGLRect QOpenGL2PEXVertexArray::boundingRect() const { if (boundingRectDirty) - return QGLRect(0.0, 0.0, 0.0, 0.0); + return QOpenGLRect(0.0, 0.0, 0.0, 0.0); else - return QGLRect(minX, minY, maxX, maxY); + return QOpenGLRect(minX, minY, maxX, maxY); } -void QGL2PEXVertexArray::addClosingLine(int index) +void QOpenGL2PEXVertexArray::addClosingLine(int index) { QPointF point(vertexArray.at(index)); if (point != QPointF(vertexArray.last())) vertexArray.add(point); } -void QGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex) +void QOpenGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex) { const QPointF *const points = reinterpret_cast<const QPointF *>(path.points()); const QPainterPath::ElementType *const elements = path.elements(); @@ -83,7 +83,7 @@ void QGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex) vertexArray.add(centroid); } -void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline) +void QOpenGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline) { const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); const QPainterPath::ElementType* const elements = path.elements(); @@ -156,9 +156,9 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc vertexArrayStops.add(vertexArray.size()); } -void QGL2PEXVertexArray::lineToArray(const GLfloat x, const GLfloat y) +void QOpenGL2PEXVertexArray::lineToArray(const GLfloat x, const GLfloat y) { - vertexArray.add(QGLPoint(x, y)); + vertexArray.add(QOpenGLPoint(x, y)); if (x > maxX) maxX = x; diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/qopengl2pexvertexarray_p.h index 5c95268790..3ef26e908d 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h +++ b/src/opengl/qopengl2pexvertexarray_p.h @@ -48,27 +48,27 @@ // We mean it. // -#ifndef QGL2PEXVERTEXARRAY_P_H -#define QGL2PEXVERTEXARRAY_P_H +#ifndef QOPENGL2PEXVERTEXARRAY_P_H +#define QOPENGL2PEXVERTEXARRAY_P_H #include <QRectF> #include <private/qdatabuffer_p.h> #include <private/qvectorpath_p.h> -#include <private/qgl_p.h> +#include <private/qopenglcontext_p.h> QT_BEGIN_NAMESPACE -class QGLPoint +class QOpenGLPoint { public: - QGLPoint(GLfloat new_x, GLfloat new_y) : + QOpenGLPoint(GLfloat new_x, GLfloat new_y) : x(new_x), y(new_y) {}; - QGLPoint(const QPointF &p) : + QOpenGLPoint(const QPointF &p) : x(p.x()), y(p.y()) {}; - QGLPoint(const QPointF* p) : + QOpenGLPoint(const QPointF* p) : x(p->x()), y(p->y()) {}; GLfloat x; @@ -78,12 +78,12 @@ public: operator QPointF() const {return QPointF(x,y);} }; -struct QGLRect +struct QOpenGLRect { - QGLRect(const QRectF &r) + QOpenGLRect(const QRectF &r) : left(r.left()), top(r.top()), right(r.right()), bottom(r.bottom()) {} - QGLRect(GLfloat l, GLfloat t, GLfloat r, GLfloat b) + QOpenGLRect(GLfloat l, GLfloat t, GLfloat r, GLfloat b) : left(l), top(t), right(r), bottom(b) {} GLfloat left; @@ -94,10 +94,10 @@ struct QGLRect operator QRectF() const {return QRectF(left, top, right-left, bottom-top);} }; -class QGL2PEXVertexArray +class QOpenGL2PEXVertexArray { public: - QGL2PEXVertexArray() : + QOpenGL2PEXVertexArray() : vertexArray(0), vertexArrayStops(0), maxX(-2e10), maxY(-2e10), minX(2e10), minY(2e10), boundingRectDirty(true) @@ -110,12 +110,12 @@ public: qreal bottom = rect.bottom(); qreal right = rect.right(); - vertexArray << QGLPoint(left, top) - << QGLPoint(right, top) - << QGLPoint(right, bottom) - << QGLPoint(right, bottom) - << QGLPoint(left, bottom) - << QGLPoint(left, top); + vertexArray << QOpenGLPoint(left, top) + << QOpenGLPoint(right, top) + << QOpenGLPoint(right, bottom) + << QOpenGLPoint(right, bottom) + << QOpenGLPoint(left, bottom) + << QOpenGLPoint(left, top); } inline void addQuad(const QRectF &rect) @@ -125,32 +125,32 @@ public: qreal bottom = rect.bottom(); qreal right = rect.right(); - vertexArray << QGLPoint(left, top) - << QGLPoint(right, top) - << QGLPoint(left, bottom) - << QGLPoint(right, bottom); + vertexArray << QOpenGLPoint(left, top) + << QOpenGLPoint(right, top) + << QOpenGLPoint(left, bottom) + << QOpenGLPoint(right, bottom); } inline void addVertex(const GLfloat x, const GLfloat y) { - vertexArray.add(QGLPoint(x, y)); + vertexArray.add(QOpenGLPoint(x, y)); } void addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline = true); void clear(); - QGLPoint* data() {return vertexArray.data();} + QOpenGLPoint* data() {return vertexArray.data();} int *stops() const { return vertexArrayStops.data(); } int stopCount() const { return vertexArrayStops.size(); } - QGLRect boundingRect() const; + QOpenGLRect boundingRect() const; int vertexCount() const { return vertexArray.size(); } void lineToArray(const GLfloat x, const GLfloat y); private: - QDataBuffer<QGLPoint> vertexArray; + QDataBuffer<QOpenGLPoint> vertexArray; QDataBuffer<int> vertexArrayStops; GLfloat maxX; diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/qopenglcustomshaderstage.cpp index 7d6d4595ba..7a32b2fbc0 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp +++ b/src/opengl/qopenglcustomshaderstage.cpp @@ -37,34 +37,34 @@ ** ****************************************************************************/ -#include "qglcustomshaderstage_p.h" -#include "qglengineshadermanager_p.h" -#include "qpaintengineex_opengl2_p.h" +#include "qopenglcustomshaderstage_p.h" +#include "qopenglengineshadermanager_p.h" +#include "qopenglpaintengine_p.h" #include <private/qpainter_p.h> QT_BEGIN_NAMESPACE -class QGLCustomShaderStagePrivate +class QOpenGLCustomShaderStagePrivate { public: - QGLCustomShaderStagePrivate() : - m_manager(0) {} + QOpenGLCustomShaderStagePrivate() : + m_manager(nullptr) {} - QPointer<QGLEngineShaderManager> m_manager; + QPointer<QOpenGLEngineShaderManager> m_manager; QByteArray m_source; }; -QGLCustomShaderStage::QGLCustomShaderStage() - : d_ptr(new QGLCustomShaderStagePrivate) +QOpenGLCustomShaderStage::QOpenGLCustomShaderStage() + : d_ptr(new QOpenGLCustomShaderStagePrivate) { } -QGLCustomShaderStage::~QGLCustomShaderStage() +QOpenGLCustomShaderStage::~QOpenGLCustomShaderStage() { - Q_D(QGLCustomShaderStage); + Q_D(QOpenGLCustomShaderStage); if (d->m_manager) { d->m_manager->removeCustomStage(); d->m_manager->sharedShaders->cleanupCustomStage(this); @@ -72,65 +72,65 @@ QGLCustomShaderStage::~QGLCustomShaderStage() delete d_ptr; } -void QGLCustomShaderStage::setUniformsDirty() +void QOpenGLCustomShaderStage::setUniformsDirty() { - Q_D(QGLCustomShaderStage); + Q_D(QOpenGLCustomShaderStage); if (d->m_manager) d->m_manager->setDirty(); // ### Probably a bit overkill! } -bool QGLCustomShaderStage::setOnPainter(QPainter* p) +bool QOpenGLCustomShaderStage::setOnPainter(QPainter* p) { - Q_D(QGLCustomShaderStage); + Q_D(QOpenGLCustomShaderStage); if (p->paintEngine()->type() != QPaintEngine::OpenGL2) { - qWarning("QGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2"); + qWarning("QOpenGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2"); return false; } if (d->m_manager) qWarning("Custom shader is already set on a painter"); - QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx*>(p->paintEngine()); - d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine); + QOpenGL2PaintEngineEx *engine = static_cast<QOpenGL2PaintEngineEx*>(p->paintEngine()); + d->m_manager = QOpenGL2PaintEngineExPrivate::shaderManagerForEngine(engine); Q_ASSERT(d->m_manager); d->m_manager->setCustomStage(this); return true; } -void QGLCustomShaderStage::removeFromPainter(QPainter* p) +void QOpenGLCustomShaderStage::removeFromPainter(QPainter* p) { - Q_D(QGLCustomShaderStage); + Q_D(QOpenGLCustomShaderStage); if (p->paintEngine()->type() != QPaintEngine::OpenGL2) return; - QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx*>(p->paintEngine()); - d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine); + QOpenGL2PaintEngineEx *engine = static_cast<QOpenGL2PaintEngineEx*>(p->paintEngine()); + d->m_manager = QOpenGL2PaintEngineExPrivate::shaderManagerForEngine(engine); Q_ASSERT(d->m_manager); // Just set the stage to null, don't call removeCustomStage(). // This should leave the program in a compiled/linked state // if the next custom shader stage is this one again. - d->m_manager->setCustomStage(0); - d->m_manager = 0; + d->m_manager->setCustomStage(nullptr); + d->m_manager = nullptr; } -QByteArray QGLCustomShaderStage::source() const +QByteArray QOpenGLCustomShaderStage::source() const { - Q_D(const QGLCustomShaderStage); + Q_D(const QOpenGLCustomShaderStage); return d->m_source; } // Called by the shader manager if another custom shader is attached or // the manager is deleted -void QGLCustomShaderStage::setInactive() +void QOpenGLCustomShaderStage::setInactive() { - Q_D(QGLCustomShaderStage); - d->m_manager = 0; + Q_D(QOpenGLCustomShaderStage); + d->m_manager = nullptr; } -void QGLCustomShaderStage::setSource(const QByteArray& s) +void QOpenGLCustomShaderStage::setSource(const QByteArray& s) { - Q_D(QGLCustomShaderStage); + Q_D(QOpenGLCustomShaderStage); d->m_source = s; } diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/qopenglcustomshaderstage_p.h index 77421ebdd3..255c115e3c 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h +++ b/src/opengl/qopenglcustomshaderstage_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QGL_CUSTOM_SHADER_STAGE_H -#define QGL_CUSTOM_SHADER_STAGE_H +#ifndef QOPENGL_CUSTOM_SHADER_STAGE_H +#define QOPENGL_CUSTOM_SHADER_STAGE_H // // W A R N I N G @@ -51,19 +51,21 @@ // We mean it. // -#include <QGLShaderProgram> +#include <QtOpenGL/qtopenglglobal.h> +#include <QOpenGLShaderProgram> QT_BEGIN_NAMESPACE -class QGLCustomShaderStagePrivate; -class Q_OPENGL_EXPORT QGLCustomShaderStage +class QPainter; +class QOpenGLCustomShaderStagePrivate; +class Q_OPENGL_EXPORT QOpenGLCustomShaderStage { - Q_DECLARE_PRIVATE(QGLCustomShaderStage) + Q_DECLARE_PRIVATE(QOpenGLCustomShaderStage) public: - QGLCustomShaderStage(); - virtual ~QGLCustomShaderStage(); - virtual void setUniforms(QGLShaderProgram*) {} + QOpenGLCustomShaderStage(); + virtual ~QOpenGLCustomShaderStage(); + virtual void setUniforms(QOpenGLShaderProgram*) {} void setUniformsDirty(); @@ -76,7 +78,9 @@ protected: void setSource(const QByteArray&); private: - QGLCustomShaderStagePrivate* d_ptr; + QOpenGLCustomShaderStagePrivate* d_ptr; + + Q_DISABLE_COPY_MOVE(QOpenGLCustomShaderStage) }; diff --git a/src/opengl/qopengldebug.cpp b/src/opengl/qopengldebug.cpp new file mode 100644 index 0000000000..a69f6069dc --- /dev/null +++ b/src/opengl/qopengldebug.cpp @@ -0,0 +1,1826 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/private/qobject_p.h> +#include <QtCore/qglobal.h> +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qopengl.h> +#include <QtGui/qopenglfunctions.h> +#include <QtGui/qoffscreensurface.h> + +#include "qopengldebug.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLDebugMessage + \brief The QOpenGLDebugMessage class wraps an OpenGL debug message. + \inmodule QtOpenGL + \reentrant + \since 5.1 + \ingroup shared + \ingroup painting-3D + + Debug messages are usually created by the OpenGL server and then read by + OpenGL clients (either from the OpenGL internal debug log, or logged in real-time). + A debug message has a textual representation, a vendor-specific numeric id, + a source, a type and a severity. + + It's also possible for applications or third-party libraries and toolkits + to create and insert messages in the debug log. In order to do so, you can use + the createApplicationMessage() or the createThirdPartyMessage() static functions. + + \sa QOpenGLDebugLogger +*/ + +/*! + \class QOpenGLDebugLogger + \brief The QOpenGLDebugLogger enables logging of OpenGL debugging messages. + \inmodule QtOpenGL + \since 5.1 + \ingroup painting-3D + + \tableofcontents + + \section1 Introduction + + OpenGL programming can be very error prone. Most of the time, a single + failing call to OpenGL can cause an entire portion of an application to + stop working, with nothing being drawn on the screen. + + The only way to be sure that no errors are being returned from the OpenGL + implementation is checking with \c{glGetError} after each and every API + call. Moreover, OpenGL errors stack up, therefore glGetError should always + be used in a loop like this: + + \snippet code/src_gui_opengl_qopengldebug.cpp 0 + + If you try to clear the error stack, make sure not just keep going until + GL_NO_ERROR is returned but also break on GL_CONTEXT_LOST as that error + value will keep repeating. + + There are also many other information we are interested in (as application + developers), for instance performance issues, or warnings about using + deprecated APIs. Those kind of messages are not reported through the + ordinary OpenGL error reporting mechanisms. + + QOpenGLDebugLogger aims at addressing these issues by providing access to + the \e{OpenGL debug log}. If your OpenGL implementation supports it (by + exposing the \c{GL_KHR_debug} extension), messages from the OpenGL server + will be either logged in an internal OpenGL log, or passed in "real-time" + to listeners as they're generated from OpenGL. + + QOpenGLDebugLogger supports both these modes of operation. Refer to the + following sections to find out the differences between them. + + \section1 Creating an OpenGL Debug Context + + For efficiency reasons, OpenGL implementations are allowed not to create + any debug output at all, unless the OpenGL context is a debug context. In order + to create a debug context from Qt, you must set the QSurfaceFormat::DebugContext + format option on the QSurfaceFormat used to create the QOpenGLContext object: + + \snippet code/src_gui_opengl_qopengldebug.cpp 1 + + Note that requesting a 3.2 OpenGL Core Profile is just for the example's + purposes; this class is not tied to any specific OpenGL or OpenGL ES + version, as it relies on the availability of the \c{GL_KHR_debug} extension + (see below). + + \section1 Creating and Initializing a QOpenGLDebugLogger + + QOpenGLDebugLogger is a simple QObject-derived class. Just like all QObject + subclasses, you create an instance (and optionally specify a parent + object), and like the other OpenGL functions in Qt you \e{must} initialize + it before usage by calling initialize() whilst there is a current OpenGL context: + + \snippet code/src_gui_opengl_qopengldebug.cpp 2 + + Note that the \c{GL_KHR_debug} extension \e{must} be available in the context + in order to access the messages logged by OpenGL. You can check the + presence of this extension by calling: + + \snippet code/src_gui_opengl_qopengldebug.cpp 3 + + where \c{ctx} is a valid QOpenGLContext. If the extension is not available, + initialize() will return false. + + \section1 Reading the Internal OpenGL Debug Log + + OpenGL implementations keep an internal log of debug messages. Messages + stored in this log can be retrieved by using the loggedMessages() function: + + \snippet code/src_gui_opengl_qopengldebug.cpp 4 + + The internal log has a limited size; when it fills up, older messages will + get discarded to make room for the new incoming messages. When you call + loggedMessages(), the internal log will be emptied as well. + + If you want to be sure not to lose any debug message, you must use real-time + logging instead of calling this function. However, debug messages might + still be generated in the timespan between context creation and activation + of real-time logging (or, in general, when the real-time logging is disabled). + + \section1 Real-time logging of messages + + It is also possible to receive a stream of debug messages from the OpenGL + server \e{as they are generated} by the implementation. In order to do so, + you need to connect a suitable slot to the messageLogged() signal, and + start logging by calling startLogging(): + + \snippet code/src_gui_opengl_qopengldebug.cpp 5 + + Similarly, logging can be disabled at any time by calling the stopLogging() + function. + + Real-time logging can be either asynchronous or synchronous, depending on + the parameter passed to startLogging(). When logging in asynchronous mode + (the default, as it has a very small overhead), the OpenGL implementation + can generate messages at any time, and/or in an order which is different from the + order of the OpenGL commands which caused those messages to be logged. + The messages could also be generated from a thread that it's different from + the thread the context is currently bound to. This is because OpenGL + implementations are usually highly threaded and asynchronous, and therefore + no warranties are made about the relative order and the timings of the + debug messages. + + On the other hand, logging in synchronous mode has a high overhead, but + the OpenGL implementation guarantees that all the messages caused by a + certain command are received in order, before the command returns, + and from the same thread the OpenGL context is bound to. + + This means that when logging in synchronous mode you will be able to run + your OpenGL application in a debugger, put a breakpoint on a slot connected + to the messageLogged() signal, and see in the backtrace the exact call + that caused the logged message. This can be extremely useful to debug + an OpenGL problem. Note that if OpenGL rendering is happening in another + thread, you must force the signal/slot connection type to Qt::DirectConnection + in order to be able to see the actual backtrace. + + Refer to the LoggingMode enum documentation for more information about + logging modes. + + \note When real-time logging is enabled, debug messages will \e{not} be + inserted in the internal OpenGL debug log any more; messages already + present in the internal log will not be deleted, nor they will be emitted + through the messageLogged() signal. Since some messages might be generated + before real-time logging is started (and therefore be kept in the internal + OpenGL log), it is important to always check if it contains any message + after calling startLogging(). + + \section1 Inserting Messages in the Debug Log + + It is possible for applications and libraries to insert custom messages in + the debug log, for instance for marking a group of related OpenGL commands + and therefore being then able to identify eventual messages coming from them. + + In order to do so, you can create a QOpenGLDebugMessage object by calling + \l{QOpenGLDebugMessage::}{createApplicationMessage()} or + \l{QOpenGLDebugMessage::}{createThirdPartyMessage()}, and then inserting it + into the log by calling logMessage(): + + \snippet code/src_gui_opengl_qopengldebug.cpp 6 + + Note that OpenGL implementations have a vendor-specific limit to the length + of the messages that can be inserted in the debug log. You can retrieve + this length by calling the maximumMessageLength() method; messages longer + than the limit will automatically get truncated. + + \section1 Controlling the Debug Output + + QOpenGLDebugMessage is also able to apply filters to the debug messages, and + therefore limit the amount of messages logged. You can enable or disable + logging of messages by calling enableMessages() and disableMessages() + respectively. By default, all messages are logged. + + It is possible to enable or disable messages by selecting them by: + + \list + \li source, type and severity (and including all ids in the selection); + \li id, source and type (and including all severities in the selection). + \endlist + + Note that the "enabled" status for a given message is a property of the + (id, source, type, severity) tuple; the message attributes \e{do not} form + a hierarchy of any kind. You should be careful about the order of the calls + to enableMessages() and disableMessages(), as it will change which + messages will are enabled / disabled. + + It's not possible to filter by the message text itself; applications + have to do that on their own (in slots connected to the messageLogged() + signal, or after fetching the messages in the internal debug log + through loggedMessages()). + + In order to simplify the management of the enabled / disabled statuses, + QOpenGLDebugMessage also supports the concept of \c{debug groups}. A debug + group contains the group of enabled / disabled configurations of debug + messages. Moreover, debug groups are organized in a stack: it is possible + to push and pop groups by calling pushGroup() and popGroup() respectively. + (When an OpenGL context is created, there is already a group in the stack). + + The enableMessages() and disableMessages() functions will modify the + configuration in the current debug group, that is, the one at the top of + the debug groups stack. + + When a new group is pushed onto the debug groups stack, it will inherit + the configuration of the group that was previously on the top of the stack. + Vice versa, popping a debug group will restore the configuration of + the debug group that becomes the new top. + + Pushing (respectively popping) debug groups will also automatically generate + a debug message of type QOpenGLDebugMessage::GroupPushType (respectively + \l{QOpenGLDebugMessage::}{GroupPopType}). + + \sa QOpenGLDebugMessage +*/ + +/*! + \enum QOpenGLDebugMessage::Source + + The Source enum defines the source of the debug message. + + \value InvalidSource + The source of the message is invalid; this is the source of a + default-constructed QOpenGLDebugMessage object. + + \value APISource + The message was generated in response to OpenGL API calls. + + \value WindowSystemSource + The message was generated by the window system. + + \value ShaderCompilerSource + The message was generated by the shader compiler. + + \value ThirdPartySource + The message was generated by a third party, for instance an OpenGL + framework a or debugging toolkit. + + \value ApplicationSource + The message was generated by the application itself. + + \value OtherSource + The message was generated by a source not included in this + enumeration. + + \omitvalue LastSource + + \value AnySource + This value corresponds to a mask of all possible message sources. +*/ + +/*! + \enum QOpenGLDebugMessage::Type + + The Type enum defines the type of the debug message. + + \value InvalidType + The type of the message is invalid; this is the type of a + default-constructed QOpenGLDebugMessage object. + + \value ErrorType + The message represents an error. + + \value DeprecatedBehaviorType + The message represents an usage of deprecated behavior. + + \value UndefinedBehaviorType + The message represents an usage of undefined behavior. + + \value PortabilityType + The message represents an usage of vendor-specific behavior, + that might pose portability concerns. + + \value PerformanceType + The message represents a performance issue. + + \value OtherType + The message represents a type not included in this + enumeration. + + \value MarkerType + The message represents a marker in the debug log. + + \value GroupPushType + The message represents a debug group push operation. + + \value GroupPopType + The message represents a debug group pop operation. + + \omitvalue LastType + + \value AnyType + This value corresponds to a mask of all possible message types. +*/ + +/*! + \enum QOpenGLDebugMessage::Severity + + The Severity enum defines the severity of the debug message. + + \value InvalidSeverity + The severity of the message is invalid; this is the severity of a + default-constructed QOpenGLDebugMessage object. + + \value HighSeverity + The message has a high severity. + + \value MediumSeverity + The message has a medium severity. + + \value LowSeverity + The message has a low severity. + + \value NotificationSeverity + The message is a notification. + + \omitvalue LastSeverity + + \value AnySeverity + This value corresponds to a mask of all possible message severities. +*/ + +/*! + \property QOpenGLDebugLogger::loggingMode + + \brief the logging mode passed to startLogging(). + + Note that logging must have been started or the value of this property + will be meaningless. + + \sa startLogging(), isLogging() +*/ +/*! + \enum QOpenGLDebugLogger::LoggingMode + + The LoggingMode enum defines the logging mode of the logger object. + + \value AsynchronousLogging + Messages from the OpenGL server are logged asynchronously. This means + that messages can be logged some time after the corresponding OpenGL + actions that caused them, and even be received in an out-of-order + fashion, depending on the OpenGL implementation. This mode has a very low + performance penalty, as OpenGL implementations are heavily threaded + and asynchronous by nature. + + \value SynchronousLogging + Messages from the OpenGL server are logged synchronously and + sequentially. This has a severe performance hit, as OpenGL + implementations are very asynchronous by nature; but it's very useful + to debug OpenGL problems, as OpenGL guarantees that the messages + generated by a OpenGL command will be logged before the corresponding + command execution has returned. Therefore, you can install a breakpoint + on the messageLogged() signal and see in the backtrace which OpenGL + command caused it; the only caveat is that if you are using OpenGL from + multiple threads you may need to force direct connection when + connecting to the messageLogged() signal. +*/ + +// When using OpenGL ES 2.0, all the necessary GL_KHR_debug constants are +// provided in qopengles2ext.h. Unfortunately, newer versions of that file +// suffix everything with _KHR which causes extra headache when the goal is +// to have a single piece of code that builds in all our target +// environments. Therefore, try to detect this and use our custom defines +// instead, which we anyway need for OS X. + +#if defined(GL_KHR_debug) && defined(GL_DEBUG_SOURCE_API_KHR) +#define USE_MANUAL_DEFS +#endif + +// Under OSX (at least up to 10.8) we cannot include our copy of glext.h, +// but we use the system-wide one, which unfortunately lacks all the needed +// defines/typedefs. In order to make the code compile, we just add here +// the GL_KHR_debug defines. + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#define USE_MANUAL_DEFS +#endif + +#ifdef USE_MANUAL_DEFS + +#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#endif +#ifndef GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#endif +#ifndef GL_DEBUG_CALLBACK_FUNCTION +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#endif +#ifndef GL_DEBUG_CALLBACK_USER_PARAM +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#endif +#ifndef GL_DEBUG_SOURCE_API +#define GL_DEBUG_SOURCE_API 0x8246 +#endif +#ifndef GL_DEBUG_SOURCE_WINDOW_SYSTEM +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#endif +#ifndef GL_DEBUG_SOURCE_SHADER_COMPILER +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#endif +#ifndef GL_DEBUG_SOURCE_THIRD_PARTY +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#endif +#ifndef GL_DEBUG_SOURCE_APPLICATION +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#endif +#ifndef GL_DEBUG_SOURCE_OTHER +#define GL_DEBUG_SOURCE_OTHER 0x824B +#endif +#ifndef GL_DEBUG_TYPE_ERROR +#define GL_DEBUG_TYPE_ERROR 0x824C +#endif +#ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#endif +#ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#endif +#ifndef GL_DEBUG_TYPE_PORTABILITY +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#endif +#ifndef GL_DEBUG_TYPE_PERFORMANCE +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#endif +#ifndef GL_DEBUG_TYPE_OTHER +#define GL_DEBUG_TYPE_OTHER 0x8251 +#endif +#ifndef GL_DEBUG_TYPE_MARKER +#define GL_DEBUG_TYPE_MARKER 0x8268 +#endif +#ifndef GL_DEBUG_TYPE_PUSH_GROUP +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#endif +#ifndef GL_DEBUG_TYPE_POP_GROUP +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#endif +#ifndef GL_DEBUG_SEVERITY_NOTIFICATION +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#endif +#ifndef GL_MAX_DEBUG_GROUP_STACK_DEPTH +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#endif +#ifndef GL_DEBUG_GROUP_STACK_DEPTH +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#endif +#ifndef GL_BUFFER +#define GL_BUFFER 0x82E0 +#endif +#ifndef GL_SHADER +#define GL_SHADER 0x82E1 +#endif +#ifndef GL_PROGRAM +#define GL_PROGRAM 0x82E2 +#endif +#ifndef GL_QUERY +#define GL_QUERY 0x82E3 +#endif +#ifndef GL_PROGRAM_PIPELINE +#define GL_PROGRAM_PIPELINE 0x82E4 +#endif +#ifndef GL_SAMPLER +#define GL_SAMPLER 0x82E6 +#endif +#ifndef GL_DISPLAY_LIST +#define GL_DISPLAY_LIST 0x82E7 +#endif +#ifndef GL_MAX_LABEL_LENGTH +#define GL_MAX_LABEL_LENGTH 0x82E8 +#endif +#ifndef GL_MAX_DEBUG_MESSAGE_LENGTH +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#endif +#ifndef GL_MAX_DEBUG_LOGGED_MESSAGES +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#endif +#ifndef GL_DEBUG_LOGGED_MESSAGES +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#endif +#ifndef GL_DEBUG_SEVERITY_HIGH +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#endif +#ifndef GL_DEBUG_SEVERITY_MEDIUM +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#endif +#ifndef GL_DEBUG_SEVERITY_LOW +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#endif +#ifndef GL_DEBUG_OUTPUT +#define GL_DEBUG_OUTPUT 0x92E0 +#endif +#ifndef GL_CONTEXT_FLAG_DEBUG_BIT +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#endif +#ifndef GL_STACK_OVERFLOW +#define GL_STACK_OVERFLOW 0x0503 +#endif +#ifndef GL_STACK_UNDERFLOW +#define GL_STACK_UNDERFLOW 0x0504 +#endif + +typedef void (QOPENGLF_APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam); + +#endif /* USE_MANUAL_DEFS */ + + +/*! + \internal +*/ +static QOpenGLDebugMessage::Source qt_messageSourceFromGL(GLenum source) +{ + switch (source) { + case GL_DEBUG_SOURCE_API: + return QOpenGLDebugMessage::APISource; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + return QOpenGLDebugMessage::WindowSystemSource; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + return QOpenGLDebugMessage::ShaderCompilerSource; + case GL_DEBUG_SOURCE_THIRD_PARTY: + return QOpenGLDebugMessage::ThirdPartySource; + case GL_DEBUG_SOURCE_APPLICATION: + return QOpenGLDebugMessage::ApplicationSource; + case GL_DEBUG_SOURCE_OTHER: + return QOpenGLDebugMessage::OtherSource; + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source from GL"); + return QOpenGLDebugMessage::OtherSource; +} + +/*! + \internal +*/ +static GLenum qt_messageSourceToGL(QOpenGLDebugMessage::Source source) +{ + switch (source) { + case QOpenGLDebugMessage::InvalidSource: + break; + case QOpenGLDebugMessage::APISource: + return GL_DEBUG_SOURCE_API; + case QOpenGLDebugMessage::WindowSystemSource: + return GL_DEBUG_SOURCE_WINDOW_SYSTEM; + case QOpenGLDebugMessage::ShaderCompilerSource: + return GL_DEBUG_SOURCE_SHADER_COMPILER; + case QOpenGLDebugMessage::ThirdPartySource: + return GL_DEBUG_SOURCE_THIRD_PARTY; + case QOpenGLDebugMessage::ApplicationSource: + return GL_DEBUG_SOURCE_APPLICATION; + case QOpenGLDebugMessage::OtherSource: + return GL_DEBUG_SOURCE_OTHER; + case QOpenGLDebugMessage::AnySource: + break; + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message source"); + return GL_DEBUG_SOURCE_OTHER; +} + +/*! + \internal +*/ +static QString qt_messageSourceToString(QOpenGLDebugMessage::Source source) +{ + switch (source) { + case QOpenGLDebugMessage::InvalidSource: + return QStringLiteral("InvalidSource"); + case QOpenGLDebugMessage::APISource: + return QStringLiteral("APISource"); + case QOpenGLDebugMessage::WindowSystemSource: + return QStringLiteral("WindowSystemSource"); + case QOpenGLDebugMessage::ShaderCompilerSource: + return QStringLiteral("ShaderCompilerSource"); + case QOpenGLDebugMessage::ThirdPartySource: + return QStringLiteral("ThirdPartySource"); + case QOpenGLDebugMessage::ApplicationSource: + return QStringLiteral("ApplicationSource"); + case QOpenGLDebugMessage::OtherSource: + return QStringLiteral("OtherSource"); + case QOpenGLDebugMessage::AnySource: + return QStringLiteral("AnySource"); + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source"); + return QString(); +} + +/*! + \internal +*/ +static QOpenGLDebugMessage::Type qt_messageTypeFromGL(GLenum type) +{ + switch (type) { + case GL_DEBUG_TYPE_ERROR: + return QOpenGLDebugMessage::ErrorType; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + return QOpenGLDebugMessage::DeprecatedBehaviorType; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + return QOpenGLDebugMessage::UndefinedBehaviorType; + case GL_DEBUG_TYPE_PORTABILITY: + return QOpenGLDebugMessage::PortabilityType; + case GL_DEBUG_TYPE_PERFORMANCE: + return QOpenGLDebugMessage::PerformanceType; + case GL_DEBUG_TYPE_OTHER: + return QOpenGLDebugMessage::OtherType; + case GL_DEBUG_TYPE_MARKER: + return QOpenGLDebugMessage::MarkerType; + case GL_DEBUG_TYPE_PUSH_GROUP: + return QOpenGLDebugMessage::GroupPushType; + case GL_DEBUG_TYPE_POP_GROUP: + return QOpenGLDebugMessage::GroupPopType; + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type from GL"); + return QOpenGLDebugMessage::OtherType; +} + +/*! + \internal +*/ +static GLenum qt_messageTypeToGL(QOpenGLDebugMessage::Type type) +{ + switch (type) { + case QOpenGLDebugMessage::InvalidType: + break; + case QOpenGLDebugMessage::ErrorType: + return GL_DEBUG_TYPE_ERROR; + case QOpenGLDebugMessage::DeprecatedBehaviorType: + return GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR; + case QOpenGLDebugMessage::UndefinedBehaviorType: + return GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR; + case QOpenGLDebugMessage::PortabilityType: + return GL_DEBUG_TYPE_PORTABILITY; + case QOpenGLDebugMessage::PerformanceType: + return GL_DEBUG_TYPE_PERFORMANCE; + case QOpenGLDebugMessage::OtherType: + return GL_DEBUG_TYPE_OTHER; + case QOpenGLDebugMessage::MarkerType: + return GL_DEBUG_TYPE_MARKER; + case QOpenGLDebugMessage::GroupPushType: + return GL_DEBUG_TYPE_PUSH_GROUP; + case QOpenGLDebugMessage::GroupPopType: + return GL_DEBUG_TYPE_POP_GROUP; + case QOpenGLDebugMessage::AnyType: + break; + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type"); + return GL_DEBUG_TYPE_OTHER; +} + +/*! + \internal +*/ +static QString qt_messageTypeToString(QOpenGLDebugMessage::Type type) +{ + switch (type) { + case QOpenGLDebugMessage::InvalidType: + return QStringLiteral("InvalidType"); + case QOpenGLDebugMessage::ErrorType: + return QStringLiteral("ErrorType"); + case QOpenGLDebugMessage::DeprecatedBehaviorType: + return QStringLiteral("DeprecatedBehaviorType"); + case QOpenGLDebugMessage::UndefinedBehaviorType: + return QStringLiteral("UndefinedBehaviorType"); + case QOpenGLDebugMessage::PortabilityType: + return QStringLiteral("PortabilityType"); + case QOpenGLDebugMessage::PerformanceType: + return QStringLiteral("PerformanceType"); + case QOpenGLDebugMessage::OtherType: + return QStringLiteral("OtherType"); + case QOpenGLDebugMessage::MarkerType: + return QStringLiteral("MarkerType"); + case QOpenGLDebugMessage::GroupPushType: + return QStringLiteral("GroupPushType"); + case QOpenGLDebugMessage::GroupPopType: + return QStringLiteral("GroupPopType"); + case QOpenGLDebugMessage::AnyType: + return QStringLiteral("AnyType"); + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type"); + return QString(); +} + +/*! + \internal +*/ +static QOpenGLDebugMessage::Severity qt_messageSeverityFromGL(GLenum severity) +{ + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + return QOpenGLDebugMessage::HighSeverity; + case GL_DEBUG_SEVERITY_MEDIUM: + return QOpenGLDebugMessage::MediumSeverity; + case GL_DEBUG_SEVERITY_LOW: + return QOpenGLDebugMessage::LowSeverity; + case GL_DEBUG_SEVERITY_NOTIFICATION: + return QOpenGLDebugMessage::NotificationSeverity; + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity from GL"); + return QOpenGLDebugMessage::NotificationSeverity; +} + +/*! + \internal +*/ +static GLenum qt_messageSeverityToGL(QOpenGLDebugMessage::Severity severity) +{ + switch (severity) { + case QOpenGLDebugMessage::InvalidSeverity: + break; + case QOpenGLDebugMessage::HighSeverity: + return GL_DEBUG_SEVERITY_HIGH; + case QOpenGLDebugMessage::MediumSeverity: + return GL_DEBUG_SEVERITY_MEDIUM; + case QOpenGLDebugMessage::LowSeverity: + return GL_DEBUG_SEVERITY_LOW; + case QOpenGLDebugMessage::NotificationSeverity: + return GL_DEBUG_SEVERITY_NOTIFICATION; + case QOpenGLDebugMessage::AnySeverity: + break; + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message severity"); + return GL_DEBUG_SEVERITY_NOTIFICATION; +} + +/*! + \internal +*/ +static QString qt_messageSeverityToString(QOpenGLDebugMessage::Severity severity) +{ + switch (severity) { + case QOpenGLDebugMessage::InvalidSeverity: + return QStringLiteral("InvalidSeverity"); + case QOpenGLDebugMessage::HighSeverity: + return QStringLiteral("HighSeverity"); + case QOpenGLDebugMessage::MediumSeverity: + return QStringLiteral("MediumSeverity"); + case QOpenGLDebugMessage::LowSeverity: + return QStringLiteral("LowSeverity"); + case QOpenGLDebugMessage::NotificationSeverity: + return QStringLiteral("NotificationSeverity"); + case QOpenGLDebugMessage::AnySeverity: + return QStringLiteral("AnySeverity"); + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity"); + return QString(); +} + +class QOpenGLDebugMessagePrivate : public QSharedData +{ +public: + QOpenGLDebugMessagePrivate(); + + QString message; + GLuint id; + QOpenGLDebugMessage::Source source; + QOpenGLDebugMessage::Type type; + QOpenGLDebugMessage::Severity severity; +}; + +/*! + \internal +*/ +QOpenGLDebugMessagePrivate::QOpenGLDebugMessagePrivate() + : message(), + id(0), + source(QOpenGLDebugMessage::InvalidSource), + type(QOpenGLDebugMessage::InvalidType), + severity(QOpenGLDebugMessage::InvalidSeverity) +{ +} + + +/*! + Constructs a debug message with an empty message string, id set to 0, + source set to InvalidSource, type set to InvalidType, and severity set to + InvalidSeverity. + + \note This constructor should not be used to create a debug message; + instead, use the createApplicationMessage() or the createThirdPartyMessage() + static functions. + + \sa createApplicationMessage(), createThirdPartyMessage() +*/ +QOpenGLDebugMessage::QOpenGLDebugMessage() + : d(new QOpenGLDebugMessagePrivate) +{ +} + +/*! + Constructs a debug message as a copy of \a debugMessage. + + \sa operator=() +*/ +QOpenGLDebugMessage::QOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage) + : d(debugMessage.d) +{ +} + +/*! + Destroys this debug message. +*/ +QOpenGLDebugMessage::~QOpenGLDebugMessage() +{ +} + +/*! + Assigns the message \a debugMessage to this object, and returns a reference + to the copy. +*/ +QOpenGLDebugMessage &QOpenGLDebugMessage::operator=(const QOpenGLDebugMessage &debugMessage) +{ + d = debugMessage.d; + return *this; +} + +/*! + \fn QOpenGLDebugMessage &QOpenGLDebugMessage::operator=(QOpenGLDebugMessage &&debugMessage) + + Move-assigns \a debugMessage to this object. +*/ + +/*! + \fn void QOpenGLDebugMessage::swap(QOpenGLDebugMessage &debugMessage) + + Swaps the message \a debugMessage with this message. This operation is very + fast and never fails. +*/ + +/*! + Returns the source of the debug message. +*/ +QOpenGLDebugMessage::Source QOpenGLDebugMessage::source() const +{ + return d->source; +} + +/*! + Returns the type of the debug message. +*/ +QOpenGLDebugMessage::Type QOpenGLDebugMessage::type() const +{ + return d->type; +} + +/*! + Returns the severity of the debug message. +*/ +QOpenGLDebugMessage::Severity QOpenGLDebugMessage::severity() const +{ + return d->severity; +} + +/*! + Returns the id of the debug message. Ids are generally vendor-specific. +*/ +GLuint QOpenGLDebugMessage::id() const +{ + return d->id; +} + +/*! + Returns the textual message contained by this debug message. +*/ +QString QOpenGLDebugMessage::message() const +{ + return d->message; +} + +/*! + Constructs and returns a debug message with \a text as its text, \a id + as id, \a severity as severity, and \a type as type. The message source + will be set to ApplicationSource. + + \sa QOpenGLDebugLogger::logMessage(), createThirdPartyMessage() +*/ +QOpenGLDebugMessage QOpenGLDebugMessage::createApplicationMessage(const QString &text, + GLuint id, + QOpenGLDebugMessage::Severity severity, + QOpenGLDebugMessage::Type type) +{ + QOpenGLDebugMessage m; + m.d->message = text; + m.d->id = id; + m.d->severity = severity; + m.d->type = type; + m.d->source = ApplicationSource; + return m; +} + +/*! + Constructs and returns a debug message with \a text as its text, \a id + as id, \a severity as severity, and \a type as type. The message source + will be set to ThirdPartySource. + + \sa QOpenGLDebugLogger::logMessage(), createApplicationMessage() +*/ +QOpenGLDebugMessage QOpenGLDebugMessage::createThirdPartyMessage(const QString &text, + GLuint id, + QOpenGLDebugMessage::Severity severity, + QOpenGLDebugMessage::Type type) +{ + QOpenGLDebugMessage m; + m.d->message = text; + m.d->id = id; + m.d->severity = severity; + m.d->type = type; + m.d->source = ThirdPartySource; + return m; +} + +/*! + Returns \c true if this debug message is equal to \a debugMessage, or false + otherwise. Two debugging messages are equal if they have the same textual + message, the same id, the same source, the same type and the same severity. + + \sa operator!=() +*/ +bool QOpenGLDebugMessage::operator==(const QOpenGLDebugMessage &debugMessage) const +{ + return (d == debugMessage.d) + || (d->id == debugMessage.d->id + && d->source == debugMessage.d->source + && d->type == debugMessage.d->type + && d->severity == debugMessage.d->severity + && d->message == debugMessage.d->message); +} + +/*! + \fn bool QOpenGLDebugMessage::operator!=(const QOpenGLDebugMessage &debugMessage) const + + Returns \c true if this message is different from \a debugMessage, or false + otherwise. + + \sa operator==() +*/ + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QOpenGLDebugMessage + + Writes the source \a source into the debug object \a debug for debugging + purposes. +*/ +QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Source source) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QOpenGLDebugMessage::Source(" + << qt_messageSourceToString(source) + << ')'; + return debug; +} + +/*! + \relates QOpenGLDebugMessage + + Writes the type \a type into the debug object \a debug for debugging + purposes. +*/ +QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Type type) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QOpenGLDebugMessage::Type(" + << qt_messageTypeToString(type) + << ')'; + return debug; +} + +/*! + \relates QOpenGLDebugMessage + + Writes the severity \a severity into the debug object \a debug for debugging + purposes. +*/ +QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Severity severity) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QOpenGLDebugMessage::Severity(" + << qt_messageSeverityToString(severity) + << ')'; + return debug; +} + +/*! + \relates QOpenGLDebugMessage + + Writes the message \a message into the debug object \a debug for debugging + purposes. +*/ +QDebug operator<<(QDebug debug, const QOpenGLDebugMessage &message) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QOpenGLDebugMessage(" + << qt_messageSourceToString(message.source()) << ", " + << message.id() << ", " + << message.message() << ", " + << qt_messageSeverityToString(message.severity()) << ", " + << qt_messageTypeToString(message.type()) << ')'; + return debug; + +} +#endif // QT_NO_DEBUG_STREAM + +typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageControl_t)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageInsert_t)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageCallback_t)(GLDEBUGPROC callback, const void *userParam); +typedef GLuint (QOPENGLF_APIENTRYP qt_glGetDebugMessageLog_t)(GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (QOPENGLF_APIENTRYP qt_glPushDebugGroup_t)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (QOPENGLF_APIENTRYP qt_glPopDebugGroup_t)(); +typedef void (QOPENGLF_APIENTRYP qt_glGetPointerv_t)(GLenum pname, GLvoid **params); + +class QOpenGLDebugLoggerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLDebugLogger) +public: + QOpenGLDebugLoggerPrivate(); + + void handleMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *rawMessage); + void controlDebugMessages(QOpenGLDebugMessage::Sources sources, + QOpenGLDebugMessage::Types types, + QOpenGLDebugMessage::Severities severities, + const QVector<GLuint> &ids, + const QByteArray &callerName, + bool enable); + void _q_contextAboutToBeDestroyed(); + + qt_glDebugMessageControl_t glDebugMessageControl; + qt_glDebugMessageInsert_t glDebugMessageInsert; + qt_glDebugMessageCallback_t glDebugMessageCallback; + qt_glGetDebugMessageLog_t glGetDebugMessageLog; + qt_glPushDebugGroup_t glPushDebugGroup; + qt_glPopDebugGroup_t glPopDebugGroup; + qt_glGetPointerv_t glGetPointerv; + + GLDEBUGPROC oldDebugCallbackFunction; + void *oldDebugCallbackParameter; + QOpenGLContext *context; + GLint maxMessageLength; + QOpenGLDebugLogger::LoggingMode loggingMode; + bool initialized : 1; + bool isLogging : 1; + bool debugWasEnabled : 1; + bool syncDebugWasEnabled : 1; +}; + +/*! + \internal +*/ +QOpenGLDebugLoggerPrivate::QOpenGLDebugLoggerPrivate() + : glDebugMessageControl(nullptr), + glDebugMessageInsert(nullptr), + glDebugMessageCallback(nullptr), + glGetDebugMessageLog(nullptr), + glPushDebugGroup(nullptr), + glPopDebugGroup(nullptr), + oldDebugCallbackFunction(nullptr), + context(nullptr), + maxMessageLength(0), + loggingMode(QOpenGLDebugLogger::AsynchronousLogging), + initialized(false), + isLogging(false), + debugWasEnabled(false), + syncDebugWasEnabled(false) +{ +} + +/*! + \internal +*/ +void QOpenGLDebugLoggerPrivate::handleMessage(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *rawMessage) +{ + if (oldDebugCallbackFunction) + oldDebugCallbackFunction(source, type, id, severity, length, rawMessage, oldDebugCallbackParameter); + + QOpenGLDebugMessage message; + + QOpenGLDebugMessagePrivate *messagePrivate = message.d.data(); + messagePrivate->source = qt_messageSourceFromGL(source); + messagePrivate->type = qt_messageTypeFromGL(type); + messagePrivate->id = id; + messagePrivate->severity = qt_messageSeverityFromGL(severity); + // not passing the length to fromUtf8, as some bugged OpenGL drivers + // do not handle the length correctly. Just rely on the message to be NUL terminated. + messagePrivate->message = QString::fromUtf8(rawMessage); + + Q_Q(QOpenGLDebugLogger); + emit q->messageLogged(message); +} + +/*! + \internal +*/ +void QOpenGLDebugLoggerPrivate::controlDebugMessages(QOpenGLDebugMessage::Sources sources, + QOpenGLDebugMessage::Types types, + QOpenGLDebugMessage::Severities severities, + const QVector<GLuint> &ids, + const QByteArray &callerName, + bool enable) +{ + if (!initialized) { + qWarning("QOpenGLDebugLogger::%s(): object must be initialized before enabling/disabling messages", callerName.constData()); + return; + } + if (sources == QOpenGLDebugMessage::InvalidSource) { + qWarning("QOpenGLDebugLogger::%s(): invalid source specified", callerName.constData()); + return; + } + if (types == QOpenGLDebugMessage::InvalidType) { + qWarning("QOpenGLDebugLogger::%s(): invalid type specified", callerName.constData()); + return; + } + if (severities == QOpenGLDebugMessage::InvalidSeverity) { + qWarning("QOpenGLDebugLogger::%s(): invalid severity specified", callerName.constData()); + return; + } + + QVarLengthArray<GLenum, 8> glSources; + QVarLengthArray<GLenum, 8> glTypes; + QVarLengthArray<GLenum, 8> glSeverities; + + if (ids.count() > 0) { + Q_ASSERT(severities == QOpenGLDebugMessage::AnySeverity); + + // The GL_KHR_debug extension says: + // + // - If <count> is greater than zero, then <ids> is an array of <count> + // message IDs for the specified combination of <source> and <type>. In + // this case, if <source> or <type> is DONT_CARE, or <severity> is not + // DONT_CARE, the error INVALID_OPERATION is generated. If <count> is + // zero, the value if <ids> is ignored. + // + // This means we can't convert AnySource or AnyType into DONT_CARE, but we have to roll + // them into individual sources/types. + + if (sources == QOpenGLDebugMessage::AnySource) { + sources = QOpenGLDebugMessage::InvalidSource; + for (uint i = 1; i <= QOpenGLDebugMessage::LastSource; i = i << 1) + sources |= QOpenGLDebugMessage::Source(i); + } + + if (types == QOpenGLDebugMessage::AnyType) { + types = QOpenGLDebugMessage::InvalidType; + for (uint i = 1; i <= QOpenGLDebugMessage::LastType; i = i << 1) + types |= QOpenGLDebugMessage::Type(i); + } + } + +#define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target) \ + if (source == QOpenGLDebugMessage::Any ## type) { \ + target << GL_DONT_CARE; \ + } else { \ + for (uint i = 1; i <= QOpenGLDebugMessage::Last ## type; i = i << 1) \ + if (source.testFlag(QOpenGLDebugMessage:: type (i))) \ + target << qt_message ## type ## ToGL (QOpenGLDebugMessage:: type (i)); \ + } + + CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Source, sources, glSources) + CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Type, types, glTypes) + CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Severity, severities, glSeverities) +#undef CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS + + const GLsizei idCount = ids.count(); + // The GL_KHR_debug extension says that if idCount is 0, idPtr must be ignored. + // Unfortunately, some bugged drivers do NOT ignore it, so pass NULL in case. + const GLuint * const idPtr = idCount ? ids.constData() : nullptr; + + for (GLenum source : glSources) + for (GLenum type : glTypes) + for (GLenum severity : glSeverities) + glDebugMessageControl(source, type, severity, idCount, idPtr, GLboolean(enable)); +} + +/*! + \internal +*/ +void QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed() +{ + Q_ASSERT(context); + + // Re-make our context current somehow, otherwise stopLogging will fail. + + // Save the current context and its surface in case we need to set them back + QOpenGLContext *currentContext = QOpenGLContext::currentContext(); + QSurface *currentSurface = nullptr; + + QScopedPointer<QOffscreenSurface> offscreenSurface; + + if (context != currentContext) { + // Make our old context current on a temporary surface + if (currentContext) + currentSurface = currentContext->surface(); + + offscreenSurface.reset(new QOffscreenSurface); + offscreenSurface->setFormat(context->format()); + offscreenSurface->create(); + if (!context->makeCurrent(offscreenSurface.data())) + qWarning("QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed(): could not make the owning GL context current for cleanup"); + } + + Q_Q(QOpenGLDebugLogger); + q->stopLogging(); + + if (offscreenSurface) { + // We did change the current context: set it back + if (currentContext) + currentContext->makeCurrent(currentSurface); + else + context->doneCurrent(); + } + + QObject::disconnect(context, SIGNAL(aboutToBeDestroyed()), q, SLOT(_q_contextAboutToBeDestroyed())); + context = nullptr; + initialized = false; +} + +extern "C" { +static void QOPENGLF_APIENTRY qt_opengl_debug_callback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *rawMessage, + const GLvoid *userParam) +{ + QOpenGLDebugLoggerPrivate *loggerPrivate = static_cast<QOpenGLDebugLoggerPrivate *>(const_cast<GLvoid *>(userParam)); + loggerPrivate->handleMessage(source, type, id, severity, length, rawMessage); +} +} + +/*! + Constructs a new logger object with the given \a parent. + + \note The object must be initialized before logging can happen. + + \sa initialize() +*/ +QOpenGLDebugLogger::QOpenGLDebugLogger(QObject *parent) + : QObject(*new QOpenGLDebugLoggerPrivate, parent) +{ + // QOpenGLDebugMessage is going to be mostly used as an argument + // of a cross thread connection, therefore let's ease the life for the users + // and register the type for them. + qRegisterMetaType<QOpenGLDebugMessage>(); +} + +/*! + Destroys the logger object. +*/ +QOpenGLDebugLogger::~QOpenGLDebugLogger() +{ + stopLogging(); +} + +/*! + Initializes the object in the current OpenGL context. The context must + support the \c{GL_KHR_debug} extension for the initialization to succeed. + The object must be initialized before any logging can happen. + + It is safe to call this function multiple times from the same context. + + This function can also be used to change the context of a previously + initialized object; note that in this case the object must not be logging + when you call this function. + + Returns \c true if the logger is successfully initialized; false otherwise. + + \sa QOpenGLContext +*/ +bool QOpenGLDebugLogger::initialize() +{ + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) { + qWarning("QOpenGLDebugLogger::initialize(): no current OpenGL context found."); + return false; + } + + Q_D(QOpenGLDebugLogger); + if (d->context == context) { + // context is non-NULL, d->context is non NULL only on successful initialization. + Q_ASSERT(d->initialized); + return true; + } + + if (d->isLogging) { + qWarning("QOpenGLDebugLogger::initialize(): cannot initialize the object while logging. Please stop the logging first."); + return false; + } + + if (d->context) + disconnect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed())); + + d->initialized = false; + d->context = nullptr; + + if (!context->hasExtension(QByteArrayLiteral("GL_KHR_debug"))) + return false; + + d->context = context; + connect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed())); + +#define GET_DEBUG_PROC_ADDRESS(procName) \ + d->procName = reinterpret_cast< qt_ ## procName ## _t >( \ + d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName)) \ + ); + + GET_DEBUG_PROC_ADDRESS(glDebugMessageControl); + GET_DEBUG_PROC_ADDRESS(glDebugMessageInsert); + GET_DEBUG_PROC_ADDRESS(glDebugMessageCallback); + GET_DEBUG_PROC_ADDRESS(glGetDebugMessageLog); + GET_DEBUG_PROC_ADDRESS(glPushDebugGroup); + GET_DEBUG_PROC_ADDRESS(glPopDebugGroup); + GET_DEBUG_PROC_ADDRESS(glGetPointerv) + +#undef GET_DEBUG_PROC_ADDRESS + + QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &d->maxMessageLength); + +#ifndef QT_NO_DEBUG + if (!d->context->format().testOption(QSurfaceFormat::DebugContext)) { + qWarning("QOpenGLDebugLogger::initialize(): the current context is not a debug context:\n" + " this means that the GL may not generate any debug output at all.\n" + " To avoid this warning, try creating the context with the\n" + " QSurfaceFormat::DebugContext surface format option."); + } +#endif // QT_NO_DEBUG + + d->initialized = true; + return true; +} + +/*! + Returns \c true if this object is currently logging, false otherwise. + + \sa startLogging() +*/ +bool QOpenGLDebugLogger::isLogging() const +{ + Q_D(const QOpenGLDebugLogger); + return d->isLogging; +} + +/*! + Starts logging messages coming from the OpenGL server. When a new message + is received, the signal messageLogged() is emitted, carrying the logged + message as argument. + + \a loggingMode specifies whether the logging must be asynchronous (the default) + or synchronous. + + QOpenGLDebugLogger will record the values of \c{GL_DEBUG_OUTPUT} and + \c{GL_DEBUG_OUTPUT_SYNCHRONOUS} when logging is started, and set them back + when logging is stopped. Moreover, any user-defined OpenGL debug callback + installed when this function is invoked will be restored when logging is + stopped; QOpenGLDebugLogger will ensure that the pre-existing callback will + still be invoked when logging. + + \note It's not possible to change the logging mode without stopping and + starting logging again. This might change in a future version of Qt. + + \note The object must be initialized before logging can happen. + + \sa stopLogging(), initialize() +*/ +void QOpenGLDebugLogger::startLogging(QOpenGLDebugLogger::LoggingMode loggingMode) +{ + Q_D(QOpenGLDebugLogger); + if (!d->initialized) { + qWarning("QOpenGLDebugLogger::startLogging(): object must be initialized before logging can start"); + return; + } + if (d->isLogging) { + qWarning("QOpenGLDebugLogger::startLogging(): this object is already logging"); + return; + } + + d->isLogging = true; + d->loggingMode = loggingMode; + + d->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, reinterpret_cast<void **>(&d->oldDebugCallbackFunction)); + d->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &d->oldDebugCallbackParameter); + + d->glDebugMessageCallback(&qt_opengl_debug_callback, d); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + d->debugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT); + d->syncDebugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + if (d->loggingMode == SynchronousLogging) + funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + else + funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + funcs->glEnable(GL_DEBUG_OUTPUT); +} + +/*! + Returns the logging mode of the object. + + \sa startLogging() +*/ +QOpenGLDebugLogger::LoggingMode QOpenGLDebugLogger::loggingMode() const +{ + Q_D(const QOpenGLDebugLogger); + return d->loggingMode; +} + +/*! + Stops logging messages from the OpenGL server. + + \sa startLogging() +*/ +void QOpenGLDebugLogger::stopLogging() +{ + Q_D(QOpenGLDebugLogger); + if (!d->isLogging) + return; + + QOpenGLContext *currentContext = QOpenGLContext::currentContext(); + if (!currentContext || currentContext != d->context) { + qWarning("QOpenGLDebugLogger::stopLogging(): attempting to stop logging with the wrong OpenGL context current"); + return; + } + + d->isLogging = false; + + d->glDebugMessageCallback(d->oldDebugCallbackFunction, d->oldDebugCallbackParameter); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + if (!d->debugWasEnabled) + funcs->glDisable(GL_DEBUG_OUTPUT); + + if (d->syncDebugWasEnabled) + funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + else + funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS); +} + +/*! + Inserts the message \a debugMessage into the OpenGL debug log. This provides + a way for applications or libraries to insert custom messages that can + ease the debugging of OpenGL applications. + + \note \a debugMessage must have QOpenGLDebugMessage::ApplicationSource or + QOpenGLDebugMessage::ThirdPartySource as its source, and a valid + type and severity, otherwise it will not be inserted into the log. + + \note The object must be initialized before logging can happen. + + \sa initialize() +*/ +void QOpenGLDebugLogger::logMessage(const QOpenGLDebugMessage &debugMessage) +{ + Q_D(QOpenGLDebugLogger); + if (!d->initialized) { + qWarning("QOpenGLDebugLogger::logMessage(): object must be initialized before logging messages"); + return; + } + if (debugMessage.source() != QOpenGLDebugMessage::ApplicationSource + && debugMessage.source() != QOpenGLDebugMessage::ThirdPartySource) { + qWarning("QOpenGLDebugLogger::logMessage(): using a message source different from ApplicationSource\n" + " or ThirdPartySource is not supported by GL_KHR_debug. The message will not be logged."); + return; + } + if (debugMessage.type() == QOpenGLDebugMessage::InvalidType + || debugMessage.type() == QOpenGLDebugMessage::AnyType + || debugMessage.severity() == QOpenGLDebugMessage::InvalidSeverity + || debugMessage.severity() == QOpenGLDebugMessage::AnySeverity) { + qWarning("QOpenGLDebugLogger::logMessage(): the message has a non-valid type and/or severity. The message will not be logged."); + return; + } + + const GLenum source = qt_messageSourceToGL(debugMessage.source()); + const GLenum type = qt_messageTypeToGL(debugMessage.type()); + const GLenum severity = qt_messageSeverityToGL(debugMessage.severity()); + QByteArray rawMessage = debugMessage.message().toUtf8(); + rawMessage.append('\0'); + + if (rawMessage.length() > d->maxMessageLength) { + qWarning("QOpenGLDebugLogger::logMessage(): message too long, truncating it\n" + " (%d bytes long, but the GL accepts up to %d bytes)", rawMessage.length(), d->maxMessageLength); + rawMessage.resize(d->maxMessageLength - 1); + rawMessage.append('\0'); + } + + // Don't pass rawMessage.length(), as unfortunately bugged + // OpenGL drivers will eat the trailing NUL in the message. Just rely + // on the message being NUL terminated. + d->glDebugMessageInsert(source, + type, + debugMessage.id(), + severity, + -1, + rawMessage.constData()); +} + +/*! + Pushes a debug group with name \a name, id \a id, and source \a source onto + the debug groups stack. If the group is successfully pushed, OpenGL will + automatically log a message with message \a name, id \a id, source \a + source, type QOpenGLDebugMessage::GroupPushType and severity + QOpenGLDebugMessage::NotificationSeverity. + + The newly pushed group will inherit the same filtering settings of the + group that was on the top of the stack; that is, the filtering will not be + changed by pushing a new group. + + \note The \a source must either be QOpenGLDebugMessage::ApplicationSource or + QOpenGLDebugMessage::ThirdPartySource, otherwise the group will not be pushed. + + \note The object must be initialized before managing debug groups. + + \sa popGroup(), enableMessages(), disableMessages() +*/ +void QOpenGLDebugLogger::pushGroup(const QString &name, GLuint id, QOpenGLDebugMessage::Source source) +{ + Q_D(QOpenGLDebugLogger); + if (!d->initialized) { + qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before pushing a debug group"); + return; + } + if (source != QOpenGLDebugMessage::ApplicationSource + && source != QOpenGLDebugMessage::ThirdPartySource) { + qWarning("QOpenGLDebugLogger::pushGroup(): using a source different from ApplicationSource\n" + " or ThirdPartySource is not supported by GL_KHR_debug. The group will not be pushed."); + return; + } + + QByteArray rawName = name.toUtf8(); + rawName.append('\0'); + if (rawName.length() > d->maxMessageLength) { + qWarning("QOpenGLDebugLogger::pushGroup(): group name too long, truncating it\n" + " (%d bytes long, but the GL accepts up to %d bytes)", rawName.length(), d->maxMessageLength); + rawName.resize(d->maxMessageLength - 1); + rawName.append('\0'); + } + + // Don't pass rawMessage.length(), as unfortunately bugged + // OpenGL drivers will eat the trailing NUL in the name. Just rely + // on the name being NUL terminated. + d->glPushDebugGroup(qt_messageSourceToGL(source), id, -1, rawName.constData()); +} + +/*! + Pops the topmost debug group from the debug groups stack. If the group is + successfully popped, OpenGL will automatically log a message with message, + id and source matching those of the popped group, type + QOpenGLDebugMessage::GroupPopType and severity + QOpenGLDebugMessage::NotificationSeverity. + + Popping a debug group will restore the message filtering settings of the + group that becomes the top of the debug groups stack. + + \note The object must be initialized before managing debug groups. + + \sa pushGroup() +*/ +void QOpenGLDebugLogger::popGroup() +{ + Q_D(QOpenGLDebugLogger); + if (!d->initialized) { + qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before popping a debug group"); + return; + } + + d->glPopDebugGroup(); +} + +/*! + Enables the logging of messages from the given \a sources, of the given \a + types and with the given \a severities and any message id. + + The logging will be enabled in the current control group. + + \sa disableMessages(), pushGroup(), popGroup() +*/ +void QOpenGLDebugLogger::enableMessages(QOpenGLDebugMessage::Sources sources, + QOpenGLDebugMessage::Types types, + QOpenGLDebugMessage::Severities severities) +{ + Q_D(QOpenGLDebugLogger); + d->controlDebugMessages(sources, + types, + severities, + QVector<GLuint>(), + QByteArrayLiteral("enableMessages"), + true); +} + +/*! + Enables the logging of messages with the given \a ids, from the given \a + sources and of the given \a types and any severity. + + The logging will be enabled in the current control group. + + \sa disableMessages(), pushGroup(), popGroup() +*/ +void QOpenGLDebugLogger::enableMessages(const QVector<GLuint> &ids, + QOpenGLDebugMessage::Sources sources, + QOpenGLDebugMessage::Types types) +{ + Q_D(QOpenGLDebugLogger); + d->controlDebugMessages(sources, + types, + QOpenGLDebugMessage::AnySeverity, + ids, + QByteArrayLiteral("enableMessages"), + true); +} + +/*! + Disables the logging of messages with the given \a sources, of the given \a + types and with the given \a severities and any message id. + + The logging will be disabled in the current control group. + + \sa enableMessages(), pushGroup(), popGroup() +*/ +void QOpenGLDebugLogger::disableMessages(QOpenGLDebugMessage::Sources sources, + QOpenGLDebugMessage::Types types, + QOpenGLDebugMessage::Severities severities) +{ + Q_D(QOpenGLDebugLogger); + d->controlDebugMessages(sources, + types, + severities, + QVector<GLuint>(), + QByteArrayLiteral("disableMessages"), + false); +} + +/*! + Disables the logging of messages with the given \a ids, from the given \a + sources and of the given \a types and any severity. + + The logging will be disabled in the current control group. + + \sa enableMessages(), pushGroup(), popGroup() +*/ +void QOpenGLDebugLogger::disableMessages(const QVector<GLuint> &ids, + QOpenGLDebugMessage::Sources sources, + QOpenGLDebugMessage::Types types) +{ + Q_D(QOpenGLDebugLogger); + d->controlDebugMessages(sources, + types, + QOpenGLDebugMessage::AnySeverity, + ids, + QByteArrayLiteral("disableMessages"), + false); +} + +/*! + Reads all the available messages in the OpenGL internal debug log and + returns them. Moreover, this function will clear the internal debug log, + so that subsequent invocations will not return messages that were + already returned. + + \sa startLogging() +*/ +QList<QOpenGLDebugMessage> QOpenGLDebugLogger::loggedMessages() const +{ + Q_D(const QOpenGLDebugLogger); + if (!d->initialized) { + qWarning("QOpenGLDebugLogger::loggedMessages(): object must be initialized before reading logged messages"); + return QList<QOpenGLDebugMessage>(); + } + + static const GLuint maxMessageCount = 128; + GLuint messagesRead; + GLenum messageSources[maxMessageCount]; + GLenum messageTypes[maxMessageCount]; + GLuint messageIds[maxMessageCount]; + GLenum messageSeverities[maxMessageCount]; + GLsizei messageLengths[maxMessageCount]; + + QByteArray messagesBuffer; + messagesBuffer.resize(maxMessageCount * d->maxMessageLength); + + QList<QOpenGLDebugMessage> messages; + do { + messagesRead = d->glGetDebugMessageLog(maxMessageCount, + GLsizei(messagesBuffer.size()), + messageSources, + messageTypes, + messageIds, + messageSeverities, + messageLengths, + messagesBuffer.data()); + + const char *messagesBufferPtr = messagesBuffer.constData(); + for (GLuint i = 0; i < messagesRead; ++i) { + QOpenGLDebugMessage message; + + QOpenGLDebugMessagePrivate *messagePrivate = message.d.data(); + messagePrivate->source = qt_messageSourceFromGL(messageSources[i]); + messagePrivate->type = qt_messageTypeFromGL(messageTypes[i]); + messagePrivate->id = messageIds[i]; + messagePrivate->severity = qt_messageSeverityFromGL(messageSeverities[i]); + messagePrivate->message = QString::fromUtf8(messagesBufferPtr, messageLengths[i] - 1); + + messagesBufferPtr += messageLengths[i]; + messages << message; + } + } while (messagesRead == maxMessageCount); + + return messages; +} + +/*! + \fn void QOpenGLDebugLogger::messageLogged(const QOpenGLDebugMessage &debugMessage) + + This signal is emitted when a debug message (wrapped by the \a debugMessage + argument) is logged from the OpenGL server. + + Depending on the OpenGL implementation, this signal can be emitted + from other threads than the one(s) the receiver(s) lives in, and even + different from the thread the QOpenGLContext in which this object has + been initialized lives in. Moreover, the signal could be emitted from + multiple threads at the same time. This is normally not a problem, + as Qt will utilize a queued connection for cross-thread signal emissions, + but if you force the connection type to Direct then you must be aware of + the potential races in the slots connected to this signal. + + If logging have been started in SynchronousLogging mode, OpenGL guarantees + that this signal will be emitted from the same thread the QOpenGLContext + has been bound to, and no concurrent invocations will ever happen. + + \note Logging must have been started, or this signal will not be emitted. + + \sa startLogging() +*/ + +/*! + Returns the maximum supported length, in bytes, for the text of the messages + passed to logMessage(). This is also the maximum length of a debug group + name, as pushing or popping groups will automatically log a message with + the debug group name as the message text. + + If a message text is too long, it will be automatically truncated by + QOpenGLDebugLogger. + + \note Message texts are encoded in UTF-8 when they get passed to OpenGL, so + their size in bytes does not usually match the amount of UTF-16 code units, + as returned, for instance, by QString::length(). (It does if the message contains + 7-bit ASCII only data, which is typical for debug messages.) +*/ +qint64 QOpenGLDebugLogger::maximumMessageLength() const +{ + Q_D(const QOpenGLDebugLogger); + if (!d->initialized) { + qWarning("QOpenGLDebugLogger::maximumMessageLength(): object must be initialized before reading the maximum message length"); + return -1; + } + return d->maxMessageLength; +} + + +QT_END_NAMESPACE + +#include "moc_qopengldebug.cpp" diff --git a/src/opengl/qopengldebug.h b/src/opengl/qopengldebug.h new file mode 100644 index 0000000000..fef2782302 --- /dev/null +++ b/src/opengl/qopengldebug.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLDEBUG_H +#define QOPENGLDEBUG_H + +#include <QtOpenGL/qtopenglglobal.h> + +#ifndef QT_NO_OPENGL + +#include <QtCore/qshareddata.h> +#include <QtCore/qflags.h> +#include <QtCore/qlist.h> +#include <QtCore/qvector.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qdebug.h> +#include <QtGui/qopenglcontext.h> + +#if defined(Q_CLANG_QDOC) +#undef GLuint +typedef unsigned int GLuint; +#endif + +QT_BEGIN_NAMESPACE + +class QOpenGLDebugLogger; +class QOpenGLDebugLoggerPrivate; +class QOpenGLDebugMessagePrivate; + +class Q_OPENGL_EXPORT QOpenGLDebugMessage +{ +public: + enum Source { + InvalidSource = 0x00000000, + APISource = 0x00000001, + WindowSystemSource = 0x00000002, + ShaderCompilerSource = 0x00000004, + ThirdPartySource = 0x00000008, + ApplicationSource = 0x00000010, + OtherSource = 0x00000020, + LastSource = OtherSource, // private API + AnySource = 0xffffffff + }; + Q_DECLARE_FLAGS(Sources, Source) + + enum Type { + InvalidType = 0x00000000, + ErrorType = 0x00000001, + DeprecatedBehaviorType = 0x00000002, + UndefinedBehaviorType = 0x00000004, + PortabilityType = 0x00000008, + PerformanceType = 0x00000010, + OtherType = 0x00000020, + MarkerType = 0x00000040, + GroupPushType = 0x00000080, + GroupPopType = 0x00000100, + LastType = GroupPopType, // private API + AnyType = 0xffffffff + }; + Q_DECLARE_FLAGS(Types, Type) + + enum Severity { + InvalidSeverity = 0x00000000, + HighSeverity = 0x00000001, + MediumSeverity = 0x00000002, + LowSeverity = 0x00000004, + NotificationSeverity = 0x00000008, + LastSeverity = NotificationSeverity, // private API + AnySeverity = 0xffffffff + }; + Q_DECLARE_FLAGS(Severities, Severity) + + QOpenGLDebugMessage(); + QOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage); + + QOpenGLDebugMessage &operator=(const QOpenGLDebugMessage &debugMessage); + QOpenGLDebugMessage &operator=(QOpenGLDebugMessage &&other) noexcept { swap(other); return *this; } + ~QOpenGLDebugMessage(); + + void swap(QOpenGLDebugMessage &other) noexcept { qSwap(d, other.d); } + + Source source() const; + Type type() const; + Severity severity() const; + GLuint id() const; + QString message() const; + + static QOpenGLDebugMessage createApplicationMessage(const QString &text, + GLuint id = 0, + Severity severity = NotificationSeverity, + Type type = OtherType); + static QOpenGLDebugMessage createThirdPartyMessage(const QString &text, + GLuint id = 0, + Severity severity = NotificationSeverity, + Type type = OtherType); + + bool operator==(const QOpenGLDebugMessage &debugMessage) const; + inline bool operator!=(const QOpenGLDebugMessage &debugMessage) const { return !operator==(debugMessage); } + +private: + friend class QOpenGLDebugLogger; + friend class QOpenGLDebugLoggerPrivate; + QSharedDataPointer<QOpenGLDebugMessagePrivate> d; +}; + +Q_DECLARE_SHARED(QOpenGLDebugMessage) +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLDebugMessage::Sources) +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLDebugMessage::Types) +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLDebugMessage::Severities) + +#ifndef QT_NO_DEBUG_STREAM +Q_OPENGL_EXPORT QDebug operator<<(QDebug debug, const QOpenGLDebugMessage &message); +Q_OPENGL_EXPORT QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Source source); +Q_OPENGL_EXPORT QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Type type); +Q_OPENGL_EXPORT QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Severity severity); +#endif + +class QOpenGLDebugLoggerPrivate; + +class Q_OPENGL_EXPORT QOpenGLDebugLogger : public QObject +{ + Q_OBJECT + Q_PROPERTY(LoggingMode loggingMode READ loggingMode) + +public: + enum LoggingMode { + AsynchronousLogging, + SynchronousLogging + }; + Q_ENUM(LoggingMode) + + explicit QOpenGLDebugLogger(QObject *parent = nullptr); + ~QOpenGLDebugLogger(); + + bool initialize(); + + bool isLogging() const; + LoggingMode loggingMode() const; + + qint64 maximumMessageLength() const; + + void pushGroup(const QString &name, + GLuint id = 0, + QOpenGLDebugMessage::Source source = QOpenGLDebugMessage::ApplicationSource); + void popGroup(); + + void enableMessages(QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, + QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType, + QOpenGLDebugMessage::Severities severities = QOpenGLDebugMessage::AnySeverity); + + void enableMessages(const QVector<GLuint> &ids, + QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, + QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType); + + void disableMessages(QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, + QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType, + QOpenGLDebugMessage::Severities severities = QOpenGLDebugMessage::AnySeverity); + + void disableMessages(const QVector<GLuint> &ids, + QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, + QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType); + + QList<QOpenGLDebugMessage> loggedMessages() const; + +public Q_SLOTS: + void logMessage(const QOpenGLDebugMessage &debugMessage); + void startLogging(LoggingMode loggingMode = AsynchronousLogging); + void stopLogging(); + +Q_SIGNALS: + void messageLogged(const QOpenGLDebugMessage &debugMessage); + +private: + Q_DISABLE_COPY(QOpenGLDebugLogger) + Q_DECLARE_PRIVATE(QOpenGLDebugLogger) + Q_PRIVATE_SLOT(d_func(), void _q_contextAboutToBeDestroyed()) +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QOpenGLDebugMessage) + +#endif // QT_NO_OPENGL + +#endif // QOPENGLDEBUG_H diff --git a/src/opengl/qopenglengineshadermanager.cpp b/src/opengl/qopenglengineshadermanager.cpp new file mode 100644 index 0000000000..09bd9ff096 --- /dev/null +++ b/src/opengl/qopenglengineshadermanager.cpp @@ -0,0 +1,898 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglengineshadermanager_p.h" +#include "qopenglengineshadersource_p.h" +#include "qopenglpaintengine_p.h" +#include <private/qopenglshadercache_p.h> + +#include <QtGui/private/qopenglcontext_p.h> +#include <QtCore/qthreadstorage.h> + +#include <algorithm> + +#if defined(QT_DEBUG) +#include <QMetaEnum> +#endif + +// #define QT_GL_SHARED_SHADER_DEBUG + +QT_BEGIN_NAMESPACE + +class QOpenGLEngineSharedShadersResource : public QOpenGLSharedResource +{ +public: + QOpenGLEngineSharedShadersResource(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) + , m_shaders(new QOpenGLEngineSharedShaders(ctx)) + { + } + + ~QOpenGLEngineSharedShadersResource() + { + delete m_shaders; + } + + void invalidateResource() override + { + delete m_shaders; + m_shaders = nullptr; + } + + void freeResource(QOpenGLContext *) override + { + } + + QOpenGLEngineSharedShaders *shaders() const { return m_shaders; } + +private: + QOpenGLEngineSharedShaders *m_shaders; +}; + +class QOpenGLShaderStorage +{ +public: + QOpenGLEngineSharedShaders *shadersForThread(QOpenGLContext *context) { + QOpenGLMultiGroupSharedResource *&shaders = m_storage.localData(); + if (!shaders) + shaders = new QOpenGLMultiGroupSharedResource; + QOpenGLEngineSharedShadersResource *resource = + shaders->value<QOpenGLEngineSharedShadersResource>(context); + return resource ? resource->shaders() : nullptr; + } + +private: + QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage; +}; + +Q_GLOBAL_STATIC(QOpenGLShaderStorage, qt_shader_storage); + +QOpenGLEngineSharedShaders *QOpenGLEngineSharedShaders::shadersForContext(QOpenGLContext *context) +{ + return qt_shader_storage()->shadersForThread(context); +} + +const char* QOpenGLEngineSharedShaders::qShaderSnippets[] = { + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 +}; + +QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context) + : blitShaderProg(nullptr) + , simpleShaderProg(nullptr) +{ + +/* + Rather than having the shader source array statically initialised, it is initialised + here instead. This is to allow new shader names to be inserted or existing names moved + around without having to change the order of the glsl strings. It is hoped this will + make future hard-to-find runtime bugs more obvious and generally give more solid code. +*/ + + // Check if the user has requested an OpenGL 3.2 Core Profile or higher + // and if so use GLSL 1.50 core shaders instead of legacy ones. + const QSurfaceFormat &fmt = context->format(); + const bool isCoreProfile = fmt.profile() == QSurfaceFormat::CoreProfile && fmt.version() >= qMakePair(3,2); + + const char** code = qShaderSnippets; // shortcut + + if (isCoreProfile) { + code[MainVertexShader] = qopenglslMainVertexShader_core; + code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader_core; + code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader_core; + + code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader_core; + code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader_core; + code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader_core; + code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader_core; + code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader_core; + code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader_core; + code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader_core; + code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader_core; + code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader_core; + code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader_core; + code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader_core; + code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core; + code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core; + + code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core; + code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core; + code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core; + code[MainFragmentShader] = qopenglslMainFragmentShader_core; + code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core; + + code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader_core; + code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader_core; + code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader_core; + code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader_core; + code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader_core; + code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader_core; // Calls "customShader", which must be appended + code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader_core; + + code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_core; + code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader_core; + code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader_core; + code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader_core; + code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader_core; + code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader_core; + code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader_core; + + code[NoMaskFragmentShader] = ""; + code[MaskFragmentShader] = qopenglslMaskFragmentShader_core; + code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1_core; + code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2_core; + code[RgbMaskWithGammaFragmentShader] = ""; //### + } else { + code[MainVertexShader] = qopenglslMainVertexShader; + code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader; + code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader; + + code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader; + code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader; + code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader; + code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader; + code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader; + code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader; + code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader; + code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader; + code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader; + code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader; + code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader; + code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader; + code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader; + + code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO; + code[MainFragmentShader_M] = qopenglslMainFragmentShader_M; + code[MainFragmentShader_O] = qopenglslMainFragmentShader_O; + code[MainFragmentShader] = qopenglslMainFragmentShader; + code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays; + + code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader; + code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader; + code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader; + code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader; + code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader; + code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended + code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader; + code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader; + code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader; + code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader; + code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader; + code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader; + code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader; + code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader; + + code[NoMaskFragmentShader] = ""; + code[MaskFragmentShader] = qopenglslMaskFragmentShader; + code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1; + code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2; + code[RgbMaskWithGammaFragmentShader] = ""; //### + } + + // The composition shaders are just layout qualifiers and the same + // for all profiles that support them. + code[NoCompositionModeFragmentShader] = ""; + code[MultiplyCompositionModeFragmentShader] = qopenglslMultiplyCompositionModeFragmentShader; + code[ScreenCompositionModeFragmentShader] = qopenglslScreenCompositionModeFragmentShader; + code[OverlayCompositionModeFragmentShader] = qopenglslOverlayCompositionModeFragmentShader; + code[DarkenCompositionModeFragmentShader] = qopenglslDarkenCompositionModeFragmentShader; + code[LightenCompositionModeFragmentShader] = qopenglslLightenCompositionModeFragmentShader; + code[ColorDodgeCompositionModeFragmentShader] = qopenglslColorDodgeCompositionModeFragmentShader; + code[ColorBurnCompositionModeFragmentShader] = qopenglslColorBurnCompositionModeFragmentShader; + code[HardLightCompositionModeFragmentShader] = qopenglslHardLightCompositionModeFragmentShader; + code[SoftLightCompositionModeFragmentShader] = qopenglslSoftLightCompositionModeFragmentShader; + code[DifferenceCompositionModeFragmentShader] = qopenglslDifferenceCompositionModeFragmentShader; + code[ExclusionCompositionModeFragmentShader] = qopenglslExclusionCompositionModeFragmentShader; + +#if defined(QT_DEBUG) + // Check that all the elements have been filled: + for (int i = 0; i < TotalSnippetCount; ++i) { + if (Q_UNLIKELY(!qShaderSnippets[i])) { + qFatal("Shader snippet for %s (#%d) is missing!", + snippetNameStr(SnippetName(i)).constData(), i); + } + } +#endif + + QByteArray vertexSource; + QByteArray fragSource; + + // Compile up the simple shader: +#ifdef Q_OS_WASM + vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]); + vertexSource.append(qShaderSnippets[MainVertexShader]); +#else + vertexSource.append(qShaderSnippets[MainVertexShader]); + vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]); +#endif + fragSource.append(qShaderSnippets[MainFragmentShader]); + fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); + + simpleShaderProg = new QOpenGLShaderProgram; + + CachedShader simpleShaderCache(fragSource, vertexSource); + + bool inCache = simpleShaderCache.load(simpleShaderProg, context); + + if (!inCache) { + if (!simpleShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) + qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); + if (!simpleShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) + qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); + + simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + } + + simpleShaderProg->link(); + + if (Q_UNLIKELY(!simpleShaderProg->isLinked())) { + qCritical("Errors linking simple shader: %s", qPrintable(simpleShaderProg->log())); + } else { + if (!inCache) + simpleShaderCache.store(simpleShaderProg, context); + } + + // Compile the blit shader: + vertexSource.clear(); + vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]); + vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]); + + fragSource.clear(); + fragSource.append(qShaderSnippets[MainFragmentShader]); + fragSource.append(qShaderSnippets[ImageSrcFragmentShader]); + + blitShaderProg = new QOpenGLShaderProgram; + + CachedShader blitShaderCache(fragSource, vertexSource); + + inCache = blitShaderCache.load(blitShaderProg, context); + + if (!inCache) { + if (!blitShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) + qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); + if (!blitShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) + qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); + + blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + } + + blitShaderProg->link(); + if (Q_UNLIKELY(!blitShaderProg->isLinked())) { + qCritical("Errors linking blit shader: %s", qPrintable(blitShaderProg->log())); + } else { + if (!inCache) + blitShaderCache.store(blitShaderProg, context); + } + +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif +} + +QOpenGLEngineSharedShaders::~QOpenGLEngineSharedShaders() +{ +#ifdef QT_GL_SHARED_SHADER_DEBUG + qDebug(" -> ~QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread()); +#endif + qDeleteAll(cachedPrograms); + cachedPrograms.clear(); + + if (blitShaderProg) { + delete blitShaderProg; + blitShaderProg = nullptr; + } + + if (simpleShaderProg) { + delete simpleShaderProg; + simpleShaderProg = nullptr; + } +} + +#if defined (QT_DEBUG) +QByteArray QOpenGLEngineSharedShaders::snippetNameStr(SnippetName name) +{ + QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName")); + return QByteArray(m.valueToKey(name)); +} +#endif + +// The address returned here will only be valid until next time this function is called. +// The program is return bound. +QOpenGLEngineShaderProg *QOpenGLEngineSharedShaders::findProgramInCache(const QOpenGLEngineShaderProg &prog) +{ + for (int i = 0; i < cachedPrograms.size(); ++i) { + QOpenGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (*cachedProg == prog) { + // Move the program to the top of the list as a poor-man's cache algo + cachedPrograms.move(i, 0); + cachedProg->program->bind(); + return cachedProg; + } + } + + QScopedPointer<QOpenGLEngineShaderProg> newProg; + + do { + QByteArray fragSource; + // Insert the custom stage before the srcPixel shader to work around an ATI driver bug + // where you cannot forward declare a function that takes a sampler as argument. + if (prog.srcPixelFragShader == CustomImageSrcFragmentShader) + fragSource.append(prog.customStageSource); + fragSource.append(qShaderSnippets[prog.mainFragShader]); + fragSource.append(qShaderSnippets[prog.srcPixelFragShader]); + if (prog.compositionFragShader) + fragSource.append(qShaderSnippets[prog.compositionFragShader]); + if (prog.maskFragShader) + fragSource.append(qShaderSnippets[prog.maskFragShader]); + + QByteArray vertexSource; +#ifdef Q_OS_WASM + vertexSource.append(qShaderSnippets[prog.positionVertexShader]); + vertexSource.append(qShaderSnippets[prog.mainVertexShader]); +#else + vertexSource.append(qShaderSnippets[prog.mainVertexShader]); + vertexSource.append(qShaderSnippets[prog.positionVertexShader]); +#endif + QScopedPointer<QOpenGLShaderProgram> shaderProgram(new QOpenGLShaderProgram); + + CachedShader shaderCache(fragSource, vertexSource); + bool inCache = shaderCache.load(shaderProgram.data(), QOpenGLContext::currentContext()); + + if (!inCache) { + if (!shaderProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) { + QByteArray description; +#if defined(QT_DEBUG) + description.append("Vertex shader: main="); + description.append(snippetNameStr(prog.mainVertexShader)); + description.append(", position="); + description.append(snippetNameStr(prog.positionVertexShader)); +#endif + qWarning("Warning: \"%s\" failed to compile!", description.constData()); + break; + } + if (!shaderProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) { + QByteArray description; +#if defined(QT_DEBUG) + description.append("Fragment shader: main="); + description.append(snippetNameStr(prog.mainFragShader)); + description.append(", srcPixel="); + description.append(snippetNameStr(prog.srcPixelFragShader)); + if (prog.compositionFragShader) { + description.append(", composition="); + description.append(snippetNameStr(prog.compositionFragShader)); + } + if (prog.maskFragShader) { + description.append(", mask="); + description.append(snippetNameStr(prog.maskFragShader)); + } +#endif + qWarning("Warning: \"%s\" failed to compile!", description.constData()); + break; + } + + // We have to bind the vertex attribute names before the program is linked: + shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + if (prog.useTextureCoords) + shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + if (prog.useOpacityAttribute) + shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); + if (prog.usePmvMatrixAttribute) { + shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); + shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); + shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + } + } + + newProg.reset(new QOpenGLEngineShaderProg(prog)); + newProg->program = shaderProgram.take(); + + newProg->program->link(); + if (newProg->program->isLinked()) { + if (!inCache) + shaderCache.store(newProg->program, QOpenGLContext::currentContext()); + } else { + QString error; + error = QLatin1String("Shader program failed to link") + + QLatin1String(" Error Log:\n") + + QLatin1String(" ") + newProg->program->log(); + qWarning() << error; + break; + } + + newProg->program->bind(); + + if (newProg->maskFragShader != QOpenGLEngineSharedShaders::NoMaskFragmentShader) { + GLuint location = newProg->program->uniformLocation("maskTexture"); + newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT); + } + + if (cachedPrograms.count() > 30) { + // The cache is full, so delete the last 5 programs in the list. + // These programs will be least used, as a program us bumped to + // the top of the list when it's used. + for (int i = 0; i < 5; ++i) { + delete cachedPrograms.last(); + cachedPrograms.removeLast(); + } + } + + cachedPrograms.insert(0, newProg.data()); + } while (false); + + return newProg.take(); +} + +void QOpenGLEngineSharedShaders::cleanupCustomStage(QOpenGLCustomShaderStage* stage) +{ + auto hasStageAsCustomShaderSouce = [stage](QOpenGLEngineShaderProg *cachedProg) -> bool { + if (cachedProg->customStageSource == stage->source()) { + delete cachedProg; + return true; + } + return false; + }; + cachedPrograms.erase(std::remove_if(cachedPrograms.begin(), cachedPrograms.end(), + hasStageAsCustomShaderSouce), + cachedPrograms.end()); +} + + +QOpenGLEngineShaderManager::QOpenGLEngineShaderManager(QOpenGLContext* context) + : ctx(context), + shaderProgNeedsChanging(true), + complexGeometry(false), + srcPixelType(Qt::NoBrush), + opacityMode(NoOpacity), + maskType(NoMask), + compositionMode(QPainter::CompositionMode_SourceOver), + customSrcStage(nullptr), + currentShaderProg(nullptr) +{ + sharedShaders = QOpenGLEngineSharedShaders::shadersForContext(context); +} + +QOpenGLEngineShaderManager::~QOpenGLEngineShaderManager() +{ + //### + removeCustomStage(); +} + +GLuint QOpenGLEngineShaderManager::getUniformLocation(Uniform id) +{ + if (!currentShaderProg) + return 0; + + QVector<uint> &uniformLocations = currentShaderProg->uniformLocations; + if (uniformLocations.isEmpty()) + uniformLocations.fill(GLuint(-1), NumUniforms); + + const char uniformNames[][26] = { + "imageTexture", + "patternColor", + "globalOpacity", + "depth", + "maskTexture", + "fragmentColor", + "linearData", + "angle", + "halfViewportSize", + "fmp", + "fmp2_m_radius2", + "inverse_2_fmp2_m_radius2", + "sqrfr", + "bradius", + "invertedTextureSize", + "brushTransform", + "brushTexture", + "matrix" + }; + + if (uniformLocations.at(id) == GLuint(-1)) + uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]); + + return uniformLocations.at(id); +} + + +void QOpenGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType) +{ + Q_UNUSED(transformType); // Currently ignored +} + +void QOpenGLEngineShaderManager::setDirty() +{ + shaderProgNeedsChanging = true; +} + +void QOpenGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style) +{ + Q_ASSERT(style != Qt::NoBrush); + if (srcPixelType == PixelSrcType(style)) + return; + + srcPixelType = style; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setSrcPixelType(PixelSrcType type) +{ + if (srcPixelType == type) + return; + + srcPixelType = type; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setOpacityMode(OpacityMode mode) +{ + if (opacityMode == mode) + return; + + opacityMode = mode; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setMaskType(MaskType type) +{ + if (maskType == type) + return; + + maskType = type; + shaderProgNeedsChanging = true; //### +} + +void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) +{ + if (compositionMode == mode) + return; + + bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus; + bool isAdvanced = mode > QPainter::CompositionMode_Plus; + + compositionMode = mode; + shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced; +} + +void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage) +{ + if (customSrcStage) + removeCustomStage(); + customSrcStage = stage; + shaderProgNeedsChanging = true; +} + +void QOpenGLEngineShaderManager::removeCustomStage() +{ + if (customSrcStage) + customSrcStage->setInactive(); + customSrcStage = nullptr; + shaderProgNeedsChanging = true; +} + +QOpenGLShaderProgram* QOpenGLEngineShaderManager::currentProgram() +{ + if (currentShaderProg) + return currentShaderProg->program; + else + return sharedShaders->simpleProgram(); +} + +void QOpenGLEngineShaderManager::useSimpleProgram() +{ + sharedShaders->simpleProgram()->bind(); + QOpenGLContextPrivate* ctx_d = ctx->d_func(); + Q_UNUSED(ctx_d); + + QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine); + + active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); + + shaderProgNeedsChanging = true; +} + +void QOpenGLEngineShaderManager::useBlitProgram() +{ + sharedShaders->blitProgram()->bind(); + QOpenGLContextPrivate* ctx_d = ctx->d_func(); + QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); + shaderProgNeedsChanging = true; +} + +QOpenGLShaderProgram* QOpenGLEngineShaderManager::simpleProgram() +{ + return sharedShaders->simpleProgram(); +} + +QOpenGLShaderProgram* QOpenGLEngineShaderManager::blitProgram() +{ + return sharedShaders->blitProgram(); +} + + + +// Select & use the correct shader program using the current state. +// Returns \c true if program needed changing. +bool QOpenGLEngineShaderManager::useCorrectShaderProg() +{ + if (!shaderProgNeedsChanging) + return false; + + bool useCustomSrc = customSrcStage != nullptr; + if (useCustomSrc && srcPixelType != QOpenGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) { + useCustomSrc = false; + qWarning("QOpenGLEngineShaderManager - Ignoring custom shader stage for non image src"); + } + + QOpenGLEngineShaderProg requiredProgram; + + bool texCoords = false; + + // Choose vertex shader shader position function (which typically also sets + // varyings) and the source pixel (srcPixel) fragment shader function: + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::InvalidSnippetName; + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::InvalidSnippetName; + bool isAffine = brushTransform.isAffine(); + if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) { + if (isAffine) + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader; + else + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionWithPatternBrushVertexShader; + + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::PatternBrushSrcFragmentShader; + } + else switch (srcPixelType) { + default: + case Qt::NoBrush: + qFatal("QOpenGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set"); + break; + case QOpenGLEngineShaderManager::ImageSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::NonPremultipliedImageSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::GrayscaleImageSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::GrayscaleImageSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::AlphaImageSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::AlphaImageSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::PatternSrc: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcWithPatternFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + texCoords = true; + break; + case QOpenGLEngineShaderManager::TextureSrcWithPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader; + break; + case Qt::SolidPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::SolidBrushSrcFragmentShader; + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader; + break; + case Qt::LinearGradientPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader; + break; + case Qt::ConicalGradientPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader; + break; + case Qt::RadialGradientPattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader; + break; + case Qt::TexturePattern: + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader; + break; + }; + + if (useCustomSrc) { + requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::CustomImageSrcFragmentShader; + requiredProgram.customStageSource = customSrcStage->source(); + } + + const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; + const bool hasMask = maskType != QOpenGLEngineShaderManager::NoMask; + + // Choose fragment shader main function: + if (opacityMode == AttributeOpacity) { + Q_ASSERT(!hasCompose && !hasMask); + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays; + } else { + bool useGlobalOpacity = (opacityMode == UniformOpacity); + if (hasMask && useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO; + if (hasMask && !useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M; + if (!hasMask && useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O; + if (!hasMask && !useGlobalOpacity) + requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader; + } + + if (hasMask) { + if (maskType == PixelMask) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::MaskFragmentShader; + texCoords = true; + } else if (maskType == SubPixelMaskPass1) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass1; + texCoords = true; + } else if (maskType == SubPixelMaskPass2) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass2; + texCoords = true; + } else if (maskType == SubPixelWithGammaMask) { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskWithGammaFragmentShader; + texCoords = true; + } else { + qCritical("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type"); + } + } else { + requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::NoMaskFragmentShader; + } + + if (hasCompose) { + switch (compositionMode) { + case QPainter::CompositionMode_Multiply: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::MultiplyCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Screen: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ScreenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Overlay: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::OverlayCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Darken: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DarkenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Lighten: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::LightenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_ColorDodge: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_ColorBurn: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_HardLight: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::HardLightCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_SoftLight: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::SoftLightCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Difference: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DifferenceCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Exclusion: + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ExclusionCompositionModeFragmentShader; + break; + default: + qWarning("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode"); + } + } else { + requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::NoCompositionModeFragmentShader; + } + + // Choose vertex shader main function + if (opacityMode == AttributeOpacity) { + Q_ASSERT(texCoords); + requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader; + } else if (texCoords) { + requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsVertexShader; + } else { + requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainVertexShader; + } + requiredProgram.useTextureCoords = texCoords; + requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity); + if (complexGeometry && srcPixelType == Qt::SolidPattern) { + requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader; + requiredProgram.usePmvMatrixAttribute = false; + } else { + requiredProgram.usePmvMatrixAttribute = true; + + // Force complexGeometry off, since we currently don't support that mode for + // non-solid brushes + complexGeometry = false; + } + + // At this point, requiredProgram is fully populated so try to find the program in the cache + currentShaderProg = sharedShaders->findProgramInCache(requiredProgram); + + if (currentShaderProg && useCustomSrc) { + customSrcStage->setUniforms(currentShaderProg->program); + } + + // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it + // doesn't use are disabled) + QOpenGLContextPrivate* ctx_d = ctx->d_func(); + QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords); + active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute); + + shaderProgNeedsChanging = false; + return true; +} + +QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/qopenglengineshadermanager_p.h index d23b3ad550..71e6214278 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/qopenglengineshadermanager_p.h @@ -53,9 +53,9 @@ ============== Vertex shaders are specified as multiple (partial) shaders. On desktop, - this works fine. On ES, QGLShader & QGLShaderProgram will make partial - shaders work by concatenating the source in each QGLShader and compiling - it as a single shader. This is abstracted nicely by QGLShaderProgram and + this works fine. On ES, QOpenGLShader & QOpenGLShaderProgram will make partial + shaders work by concatenating the source in each QOpenGLShader and compiling + it as a single shader. This is abstracted nicely by QOpenGLShaderProgram and the GL2 engine doesn't need to worry about it. Generally, there's two vertex shader objects. The position shaders are @@ -85,21 +85,21 @@ correction is needed and a simpler vertex shader can be used instead. So there are the following "main" vertex shaders: - qglslMainVertexShader - qglslMainWithTexCoordsVertexShader + qopenglslMainVertexShader + qopenglslMainWithTexCoordsVertexShader And the following position vertex shaders: - qglslPositionOnlyVertexShader - qglslPositionWithTextureBrushVertexShader - qglslPositionWithPatternBrushVertexShader - qglslPositionWithLinearGradientBrushVertexShader - qglslPositionWithRadialGradientBrushVertexShader - qglslPositionWithConicalGradientBrushVertexShader - qglslAffinePositionWithTextureBrushVertexShader - qglslAffinePositionWithPatternBrushVertexShader - qglslAffinePositionWithLinearGradientBrushVertexShader - qglslAffinePositionWithRadialGradientBrushVertexShader - qglslAffinePositionWithConicalGradientBrushVertexShader + qopenglslPositionOnlyVertexShader + qopenglslPositionWithTextureBrushVertexShader + qopenglslPositionWithPatternBrushVertexShader + qopenglslPositionWithLinearGradientBrushVertexShader + qopenglslPositionWithRadialGradientBrushVertexShader + qopenglslPositionWithConicalGradientBrushVertexShader + qopenglslAffinePositionWithTextureBrushVertexShader + qopenglslAffinePositionWithPatternBrushVertexShader + qopenglslAffinePositionWithLinearGradientBrushVertexShader + qopenglslAffinePositionWithRadialGradientBrushVertexShader + qopenglslAffinePositionWithConicalGradientBrushVertexShader Leading to 23 possible vertex shaders @@ -126,36 +126,36 @@ GLSL function calls with the following signatures: Brushes & image drawing are implementations of "qcolorp vec4 srcPixel()": - qglslImageSrcFragShader - qglslImageSrcWithPatternFragShader - qglslNonPremultipliedImageSrcFragShader - qglslSolidBrushSrcFragShader - qglslTextureBrushSrcFragShader - qglslTextureBrushWithPatternFragShader - qglslPatternBrushSrcFragShader - qglslLinearGradientBrushSrcFragShader - qglslRadialGradientBrushSrcFragShader - qglslConicalGradientBrushSrcFragShader + qopenglslImageSrcFragShader + qopenglslImageSrcWithPatternFragShader + qopenglslNonPremultipliedImageSrcFragShader + qopenglslSolidBrushSrcFragShader + qopenglslTextureBrushSrcFragShader + qopenglslTextureBrushWithPatternFragShader + qopenglslPatternBrushSrcFragShader + qopenglslLinearGradientBrushSrcFragShader + qopenglslRadialGradientBrushSrcFragShader + qopenglslConicalGradientBrushSrcFragShader NOTE: It is assumed the colour returned by srcPixel() is pre-multiplied Masks are implementations of "qcolorp vec4 applyMask(qcolorp vec4 src)": - qglslMaskFragmentShader - qglslRgbMaskFragmentShaderPass1 - qglslRgbMaskFragmentShaderPass2 - qglslRgbMaskWithGammaFragmentShader + qopenglslMaskFragmentShader + qopenglslRgbMaskFragmentShaderPass1 + qopenglslRgbMaskFragmentShaderPass2 + qopenglslRgbMaskWithGammaFragmentShader Composition modes are "qcolorp vec4 compose(qcolorp vec4 src)": - qglslColorBurnCompositionModeFragmentShader - qglslColorDodgeCompositionModeFragmentShader - qglslDarkenCompositionModeFragmentShader - qglslDifferenceCompositionModeFragmentShader - qglslExclusionCompositionModeFragmentShader - qglslHardLightCompositionModeFragmentShader - qglslLightenCompositionModeFragmentShader - qglslMultiplyCompositionModeFragmentShader - qglslOverlayCompositionModeFragmentShader - qglslScreenCompositionModeFragmentShader - qglslSoftLightCompositionModeFragmentShader + qopenglslColorBurnCompositionModeFragmentShader + qopenglslColorDodgeCompositionModeFragmentShader + qopenglslDarkenCompositionModeFragmentShader + qopenglslDifferenceCompositionModeFragmentShader + qopenglslExclusionCompositionModeFragmentShader + qopenglslHardLightCompositionModeFragmentShader + qopenglslLightenCompositionModeFragmentShader + qopenglslMultiplyCompositionModeFragmentShader + qopenglslOverlayCompositionModeFragmentShader + qopenglslScreenCompositionModeFragmentShader + qopenglslSoftLightCompositionModeFragmentShader Note: In the future, some GLSL compilers will support an extension allowing @@ -183,14 +183,14 @@ gl_FragColor = srcPixel(); Called: - qglslMainFragmentShader_CMO - qglslMainFragmentShader_CM - qglslMainFragmentShader_MO - qglslMainFragmentShader_M - qglslMainFragmentShader_CO - qglslMainFragmentShader_C - qglslMainFragmentShader_O - qglslMainFragmentShader + qopenglslMainFragmentShader_CMO + qopenglslMainFragmentShader_CM + qopenglslMainFragmentShader_MO + qopenglslMainFragmentShader_M + qopenglslMainFragmentShader_CO + qopenglslMainFragmentShader_C + qopenglslMainFragmentShader_O + qopenglslMainFragmentShader Where: M = Mask @@ -205,7 +205,7 @@ drawPixmap calls. This is implemented via hooks in the fragment pipeline. The custom shader is passed to the engine as a partial fragment shader - (QGLCustomShaderStage). The shader will implement a pre-defined method name + (QOpenGLCustomShaderStage). The shader will implement a pre-defined method name which Qt's fragment pipeline will call: lowp vec4 customShader(lowp sampler2d imageTexture, highp vec2 textureCoords) @@ -217,31 +217,31 @@ will be respected when using the custom shader hook. */ -#ifndef QGLENGINE_SHADER_MANAGER_H -#define QGLENGINE_SHADER_MANAGER_H +#ifndef QOPENGLENGINE_SHADER_MANAGER_H +#define QOPENGLENGINE_SHADER_MANAGER_H -#include <QGLShader> -#include <QGLShaderProgram> +#include <QOpenGLShader> +#include <QOpenGLShaderProgram> #include <QPainter> -#include <private/qgl_p.h> -#include <private/qglcustomshaderstage_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglcustomshaderstage_p.h> QT_BEGIN_NAMESPACE /* -struct QGLEngineCachedShaderProg +struct QOpenGLEngineCachedShaderProg { - QGLEngineCachedShaderProg(QGLEngineShaderManager::ShaderName vertexMain, - QGLEngineShaderManager::ShaderName vertexPosition, - QGLEngineShaderManager::ShaderName fragMain, - QGLEngineShaderManager::ShaderName pixelSrc, - QGLEngineShaderManager::ShaderName mask, - QGLEngineShaderManager::ShaderName composition); + QOpenGLEngineCachedShaderProg(QOpenGLEngineShaderManager::ShaderName vertexMain, + QOpenGLEngineShaderManager::ShaderName vertexPosition, + QOpenGLEngineShaderManager::ShaderName fragMain, + QOpenGLEngineShaderManager::ShaderName pixelSrc, + QOpenGLEngineShaderManager::ShaderName mask, + QOpenGLEngineShaderManager::ShaderName composition); int cacheKey; - QGLShaderProgram* program; + QOpenGLShaderProgram* program; } */ @@ -252,9 +252,9 @@ static const GLuint QT_PMV_MATRIX_1_ATTR = 3; static const GLuint QT_PMV_MATRIX_2_ATTR = 4; static const GLuint QT_PMV_MATRIX_3_ATTR = 5; -class QGLEngineShaderProg; +class QOpenGLEngineShaderProg; -class Q_OPENGL_EXPORT QGLEngineSharedShaders +class Q_OPENGL_EXPORT QOpenGLEngineSharedShaders { Q_GADGET public: @@ -280,12 +280,8 @@ public: AffinePositionWithTextureBrushVertexShader, // MainFragmentShader_CMO must be first in the list: - MainFragmentShader_CMO, - MainFragmentShader_CM, MainFragmentShader_MO, MainFragmentShader_M, - MainFragmentShader_CO, - MainFragmentShader_C, MainFragmentShader_O, MainFragmentShader, MainFragmentShader_ImageArrays, @@ -294,6 +290,8 @@ public: ImageSrcFragmentShader, ImageSrcWithPatternFragmentShader, NonPremultipliedImageSrcFragmentShader, + GrayscaleImageSrcFragmentShader, + AlphaImageSrcFragmentShader, CustomImageSrcFragmentShader, SolidBrushSrcFragmentShader, TextureBrushSrcFragmentShader, @@ -328,7 +326,7 @@ public: TotalSnippetCount, InvalidSnippetName }; #if defined (QT_DEBUG) - Q_ENUMS(SnippetName) + Q_ENUM(SnippetName) static QByteArray snippetNameStr(SnippetName snippetName); #endif @@ -342,52 +340,51 @@ public: const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader; */ - QGLEngineSharedShaders(const QGLContext *context); - ~QGLEngineSharedShaders(); + QOpenGLEngineSharedShaders(QOpenGLContext *context); + ~QOpenGLEngineSharedShaders(); - QGLShaderProgram *simpleProgram() { return simpleShaderProg; } - QGLShaderProgram *blitProgram() { return blitShaderProg; } + QOpenGLShaderProgram *simpleProgram() { return simpleShaderProg; } + QOpenGLShaderProgram *blitProgram() { return blitShaderProg; } // Compile the program if it's not already in the cache, return the item in the cache. - QGLEngineShaderProg *findProgramInCache(const QGLEngineShaderProg &prog); + QOpenGLEngineShaderProg *findProgramInCache(const QOpenGLEngineShaderProg &prog); // Compile the custom shader if it's not already in the cache, return the item in the cache. - static QGLEngineSharedShaders *shadersForContext(const QGLContext *context); + static QOpenGLEngineSharedShaders *shadersForContext(QOpenGLContext *context); // Ideally, this would be static and cleanup all programs in all contexts which // contain the custom code. Currently it is just a hint and we rely on deleted // custom shaders being cleaned up by being kicked out of the cache when it's // full. - void cleanupCustomStage(QGLCustomShaderStage* stage); + void cleanupCustomStage(QOpenGLCustomShaderStage* stage); private: - QGLShaderProgram *blitShaderProg; - QGLShaderProgram *simpleShaderProg; - QList<QGLEngineShaderProg*> cachedPrograms; - QList<QGLShader *> shaders; + QOpenGLShaderProgram *blitShaderProg; + QOpenGLShaderProgram *simpleShaderProg; + QList<QOpenGLEngineShaderProg*> cachedPrograms; static const char* qShaderSnippets[TotalSnippetCount]; }; -class QGLEngineShaderProg +class QOpenGLEngineShaderProg { public: - QGLEngineShaderProg() : program(nullptr) {} + QOpenGLEngineShaderProg() : program(nullptr) {} - ~QGLEngineShaderProg() { + ~QOpenGLEngineShaderProg() { if (program) delete program; } - QGLEngineSharedShaders::SnippetName mainVertexShader; - QGLEngineSharedShaders::SnippetName positionVertexShader; - QGLEngineSharedShaders::SnippetName mainFragShader; - QGLEngineSharedShaders::SnippetName srcPixelFragShader; - QGLEngineSharedShaders::SnippetName maskFragShader; - QGLEngineSharedShaders::SnippetName compositionFragShader; + QOpenGLEngineSharedShaders::SnippetName mainVertexShader; + QOpenGLEngineSharedShaders::SnippetName positionVertexShader; + QOpenGLEngineSharedShaders::SnippetName mainFragShader; + QOpenGLEngineSharedShaders::SnippetName srcPixelFragShader; + QOpenGLEngineSharedShaders::SnippetName maskFragShader; + QOpenGLEngineSharedShaders::SnippetName compositionFragShader; QByteArray customStageSource; //TODO: Decent cache key for custom stages - QGLShaderProgram* program; + QOpenGLShaderProgram* program; QVector<uint> uniformLocations; @@ -395,7 +392,7 @@ public: bool useOpacityAttribute; bool usePmvMatrixAttribute; - bool operator==(const QGLEngineShaderProg& other) const { + bool operator==(const QOpenGLEngineShaderProg& other) const { // We don't care about the program return ( mainVertexShader == other.mainVertexShader && positionVertexShader == other.positionVertexShader && @@ -408,19 +405,21 @@ public: } }; -class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject +class Q_OPENGL_EXPORT QOpenGLEngineShaderManager : public QObject { Q_OBJECT public: - QGLEngineShaderManager(QGLContext* context); - ~QGLEngineShaderManager(); + QOpenGLEngineShaderManager(QOpenGLContext* context); + ~QOpenGLEngineShaderManager(); enum MaskType {NoMask, PixelMask, SubPixelMaskPass1, SubPixelMaskPass2, SubPixelWithGammaMask}; enum PixelSrcType { ImageSrc = Qt::TexturePattern+1, NonPremultipliedImageSrc = Qt::TexturePattern+2, PatternSrc = Qt::TexturePattern+3, - TextureSrcWithPattern = Qt::TexturePattern+4 + TextureSrcWithPattern = Qt::TexturePattern+4, + GrayscaleImageSrc = Qt::TexturePattern+5, + AlphaImageSrc = Qt::TexturePattern+6, }; enum Uniform { @@ -442,7 +441,6 @@ public: BrushTransform, BrushTexture, Matrix, - TranslateZ, NumUniforms }; @@ -461,7 +459,7 @@ public: void setOpacityMode(OpacityMode); void setMaskType(MaskType); void setCompositionMode(QPainter::CompositionMode); - void setCustomStage(QGLCustomShaderStage* stage); + void setCustomStage(QOpenGLCustomShaderStage* stage); void removeCustomStage(); GLuint getUniformLocation(Uniform id); @@ -481,14 +479,14 @@ public: return complexGeometry; } - QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen - QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers - QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer + QOpenGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen + QOpenGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers + QOpenGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer - QGLEngineSharedShaders* sharedShaders; + QOpenGLEngineSharedShaders* sharedShaders; private: - QGLContext* ctx; + QOpenGLContext* ctx; bool shaderProgNeedsChanging; bool complexGeometry; @@ -498,11 +496,11 @@ private: OpacityMode opacityMode; MaskType maskType; QPainter::CompositionMode compositionMode; - QGLCustomShaderStage* customSrcStage; + QOpenGLCustomShaderStage* customSrcStage; - QGLEngineShaderProg* currentShaderProg; + QOpenGLEngineShaderProg* currentShaderProg; }; QT_END_NAMESPACE -#endif //QGLENGINE_SHADER_MANAGER_H +#endif //QOPENGLENGINE_SHADER_MANAGER_H diff --git a/src/opengl/qopenglengineshadersource_p.h b/src/opengl/qopenglengineshadersource_p.h new file mode 100644 index 0000000000..49d17c8d79 --- /dev/null +++ b/src/opengl/qopenglengineshadersource_p.h @@ -0,0 +1,969 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt 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 QOPENGL_ENGINE_SHADER_SOURCE_H +#define QOPENGL_ENGINE_SHADER_SOURCE_H + +#include "qopenglengineshadermanager_p.h" + +QT_BEGIN_NAMESPACE + + +static const char* const qopenglslMainVertexShader = "\n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + }\n"; + +static const char* const qopenglslMainWithTexCoordsVertexShader = "\n\ + attribute highp vec2 textureCoordArray; \n\ + varying highp vec2 textureCoords; \n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + textureCoords = textureCoordArray; \n\ + }\n"; + +static const char* const qopenglslMainWithTexCoordsAndOpacityVertexShader = "\n\ + attribute highp vec2 textureCoordArray; \n\ + attribute lowp float opacityArray; \n\ + varying highp vec2 textureCoords; \n\ + varying lowp float opacity; \n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + textureCoords = textureCoordArray; \n\ + opacity = opacityArray; \n\ + }\n"; + +// NOTE: We let GL do the perspective correction so texture lookups in the fragment +// shader are also perspective corrected. +static const char* const qopenglslPositionOnlyVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\ + }\n"; + +static const char* const qopenglslComplexGeometryPositionOnlyVertexShader = "\n\ + uniform highp mat3 matrix; \n\ + attribute highp vec2 vertexCoordsArray; \n\ + void setPosition(void) \n\ + { \n\ + gl_Position = vec4(matrix * vec3(vertexCoordsArray, 1), 1);\n\ + } \n"; + +static const char* const qopenglslUntransformedPositionVertexShader = "\n\ + attribute highp vec4 vertexCoordsArray; \n\ + void setPosition(void) \n\ + { \n\ + gl_Position = vertexCoordsArray; \n\ + }\n"; + +// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125 +static const char* const qopenglslPositionWithPatternBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp vec2 invertedTextureSize; \n\ + uniform highp mat3 brushTransform; \n\ + varying highp vec2 patternTexCoords; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithPatternBrushVertexShader + = qopenglslPositionWithPatternBrushVertexShader; + +static const char* const qopenglslPatternBrushSrcFragmentShader = "\n\ + uniform sampler2D brushTexture; \n\ + uniform lowp vec4 patternColor; \n\ + varying highp vec2 patternTexCoords;\n\ + lowp vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture2D(brushTexture, patternTexCoords).r); \n\ + }\n"; + + +// Linear Gradient Brush +static const char* const qopenglslPositionWithLinearGradientBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp vec3 linearData; \n\ + uniform highp mat3 brushTransform; \n\ + varying mediump float index; \n\ + void setPosition() \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithLinearGradientBrushVertexShader + = qopenglslPositionWithLinearGradientBrushVertexShader; + +static const char* const qopenglslLinearGradientBrushSrcFragmentShader = "\n\ + uniform sampler2D brushTexture; \n\ + varying mediump float index; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + mediump vec2 val = vec2(index, 0.5); \n\ + return texture2D(brushTexture, val); \n\ + }\n"; + + +// Conical Gradient Brush +static const char* const qopenglslPositionWithConicalGradientBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp mat3 brushTransform; \n\ + varying highp vec2 A; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + A = hTexCoords.xy * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithConicalGradientBrushVertexShader + = qopenglslPositionWithConicalGradientBrushVertexShader; + +static const char* const qopenglslConicalGradientBrushSrcFragmentShader = "\n\ + #define INVERSE_2PI 0.1591549430918953358 \n\ + uniform sampler2D brushTexture; \n\ + uniform mediump float angle; \n\ + varying highp vec2 A; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + highp float t; \n\ + if (abs(A.y) == abs(A.x)) \n\ + t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \n\ + else \n\ + t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \n\ + return texture2D(brushTexture, vec2(t - floor(t), 0.5)); \n\ + }\n"; + + +// Radial Gradient Brush +static const char* const qopenglslPositionWithRadialGradientBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray;\n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp mat3 brushTransform; \n\ + uniform highp vec2 fmp; \n\ + uniform mediump vec3 bradius; \n\ + varying highp float b; \n\ + varying highp vec2 A; \n\ + void setPosition(void) \n\ + {\n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + A = hTexCoords.xy * invertedHTexCoordsZ; \n\ + b = bradius.x + 2.0 * dot(A, fmp); \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithRadialGradientBrushVertexShader + = qopenglslPositionWithRadialGradientBrushVertexShader; + +static const char* const qopenglslRadialGradientBrushSrcFragmentShader = "\n\ + uniform sampler2D brushTexture; \n\ + uniform highp float fmp2_m_radius2; \n\ + uniform highp float inverse_2_fmp2_m_radius2; \n\ + uniform highp float sqrfr; \n\ + varying highp float b; \n\ + varying highp vec2 A; \n\ + uniform mediump vec3 bradius; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + highp float c = sqrfr-dot(A, A); \n\ + highp float det = b*b - 4.0*fmp2_m_radius2*c; \n\ + lowp vec4 result = vec4(0.0); \n\ + if (det >= 0.0) { \n\ + highp float detSqrt = sqrt(det); \n\ + highp float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\ + if (bradius.y + w * bradius.z >= 0.0) \n\ + result = texture2D(brushTexture, vec2(w, 0.5)); \n\ + } \n\ + return result; \n\ + }\n"; + + +// Texture Brush +static const char* const qopenglslPositionWithTextureBrushVertexShader = "\n\ + attribute highp vec2 vertexCoordsArray; \n\ + attribute highp vec3 pmvMatrix1; \n\ + attribute highp vec3 pmvMatrix2; \n\ + attribute highp vec3 pmvMatrix3; \n\ + uniform mediump vec2 halfViewportSize; \n\ + uniform highp vec2 invertedTextureSize; \n\ + uniform highp mat3 brushTransform; \n\ + varying highp vec2 brushTextureCoords; \n\ + void setPosition(void) \n\ + { \n\ + highp mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithTextureBrushVertexShader + = qopenglslPositionWithTextureBrushVertexShader; + +static const char* const qopenglslTextureBrushSrcFragmentShader = "\n\ + varying highp vec2 brushTextureCoords; \n\ + uniform sampler2D brushTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return texture2D(brushTexture, brushTextureCoords); \n\ + }\n"; + +static const char* const qopenglslTextureBrushSrcWithPatternFragmentShader = "\n\ + varying highp vec2 brushTextureCoords; \n\ + uniform lowp vec4 patternColor; \n\ + uniform sampler2D brushTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \n\ + }\n"; + +// Solid Fill Brush +static const char* const qopenglslSolidBrushSrcFragmentShader = "\n\ + uniform lowp vec4 fragmentColor; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return fragmentColor; \n\ + }\n"; + +static const char* const qopenglslImageSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n" + "return texture2D(imageTexture, textureCoords); \n" + "}\n"; + +static const char* const qopenglslCustomSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return customShader(imageTexture, textureCoords); \n\ + }\n"; + +static const char* const qopenglslImageSrcWithPatternFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform lowp vec4 patternColor; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture2D(imageTexture, textureCoords).r); \n\ + }\n"; + +static const char* const qopenglslNonPremultipliedImageSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + lowp vec4 sample = texture2D(imageTexture, textureCoords); \n\ + sample.rgb = sample.rgb * sample.a; \n\ + return sample; \n\ + }\n"; + +static const char* const qopenglslGrayscaleImageSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return texture2D(imageTexture, textureCoords).rrra; \n\ + }\n"; + +static const char* const qopenglslAlphaImageSrcFragmentShader = "\n\ + varying highp vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + lowp vec4 srcPixel() \n\ + { \n\ + return vec4(0, 0, 0, texture2D(imageTexture, textureCoords).r); \n\ + }\n"; + +static const char* const qopenglslShockingPinkSrcFragmentShader = "\n\ + lowp vec4 srcPixel() \n\ + { \n\ + return vec4(0.98, 0.06, 0.75, 1.0); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_ImageArrays = "\n\ + varying lowp float opacity; \n\ + lowp vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + gl_FragColor = srcPixel() * opacity; \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_MO = "\n\ + uniform lowp float globalOpacity; \n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 applyMask(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = applyMask(srcPixel()*globalOpacity); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_M = "\n\ + lowp vec4 srcPixel(); \n\ + lowp vec4 applyMask(lowp vec4); \n\ + void main() \n\ + { \n\ + gl_FragColor = applyMask(srcPixel()); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_O = "\n\ + uniform lowp float globalOpacity; \n\ + lowp vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + gl_FragColor = srcPixel()*globalOpacity; \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader = "\n\ + lowp vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + gl_FragColor = srcPixel(); \n\ + }\n"; + +static const char* const qopenglslMaskFragmentShader = "\n\ + varying highp vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + lowp vec4 applyMask(lowp vec4 src) \n\ + {\n\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ + return src * mask.a; \n\ + }\n"; + +// For source over with subpixel antialiasing, the final color is calculated per component as follows +// (.a is alpha component, .c is red, green or blue component): +// alpha = src.a * mask.c * opacity +// dest.c = dest.c * (1 - alpha) + src.c * alpha +// +// In the first pass, calculate: dest.c = dest.c * (1 - alpha) with blend funcs: zero, 1 - source color +// In the second pass, calculate: dest.c = dest.c + src.c * alpha with blend funcs: one, one +// +// If source is a solid color (src is constant), only the first pass is needed, with blend funcs: constant, 1 - source color + +// For source composition with subpixel antialiasing, the final color is calculated per component as follows: +// alpha = src.a * mask.c * opacity +// dest.c = dest.c * (1 - mask.c) + src.c * alpha +// + +static const char* const qopenglslRgbMaskFragmentShaderPass1 = "\n\ + varying highp vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + lowp vec4 applyMask(lowp vec4 src) \n\ + { \n\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ + return src.a * mask; \n\ + }\n"; + +static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\ + varying highp vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + lowp vec4 applyMask(lowp vec4 src) \n\ + { \n\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \n\ + return src * mask; \n\ + }\n"; + +static const char* const qopenglslMultiplyCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_multiply) out;\n\ + #endif\n"; + +static const char* const qopenglslScreenCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_screen) out;\n\ + #endif\n"; + +static const char* const qopenglslOverlayCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_overlay) out;\n\ + #endif\n"; + +static const char* const qopenglslDarkenCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_darken) out;\n\ + #endif\n"; + +static const char* const qopenglslLightenCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_lighten) out;\n\ + #endif\n"; + +static const char* const qopenglslColorDodgeCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_colordodge) out;\n\ + #endif\n"; + +static const char* const qopenglslColorBurnCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_colorburn) out;\n\ + #endif\n"; + +static const char* const qopenglslHardLightCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_hardlight) out;\n\ + #endif\n"; + +static const char* const qopenglslSoftLightCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_softlight) out;\n\ + #endif\n"; + +static const char* const qopenglslDifferenceCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_difference) out;\n\ + #endif\n"; + +static const char* const qopenglslExclusionCompositionModeFragmentShader = "\n\ + #ifdef GL_KHR_blend_equation_advanced\n\ + layout(blend_support_exclusion) out;\n\ + #endif\n"; + +/* + Left to implement: + RgbMaskFragmentShader, + RgbMaskWithGammaFragmentShader, +*/ + +/* + OpenGL 3.2+ Core Profile shaders + The following shader snippets are copies of the snippets above + but use the modern GLSL 1.5 keywords. New shaders should make + a snippet for both profiles and add them appropriately in the + shader manager. +*/ +static const char* const qopenglslMainVertexShader_core = + "#version 150 core\n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + }\n"; + +static const char* const qopenglslMainWithTexCoordsVertexShader_core = + "#version 150 core\n\ + in vec2 textureCoordArray; \n\ + out vec2 textureCoords; \n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + textureCoords = textureCoordArray; \n\ + }\n"; + +static const char* const qopenglslMainWithTexCoordsAndOpacityVertexShader_core = + "#version 150 core\n\ + in vec2 textureCoordArray; \n\ + in float opacityArray; \n\ + out vec2 textureCoords; \n\ + out float opacity; \n\ + void setPosition(); \n\ + void main(void) \n\ + { \n\ + setPosition(); \n\ + textureCoords = textureCoordArray; \n\ + opacity = opacityArray; \n\ + }\n"; + +// NOTE: We let GL do the perspective correction so texture lookups in the fragment +// shader are also perspective corrected. +static const char* const qopenglslPositionOnlyVertexShader_core = "\n\ + in vec2 vertexCoordsArray; \n\ + in vec3 pmvMatrix1; \n\ + in vec3 pmvMatrix2; \n\ + in vec3 pmvMatrix3; \n\ + void setPosition(void) \n\ + { \n\ + mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\ + }\n"; + +static const char* const qopenglslComplexGeometryPositionOnlyVertexShader_core = "\n\ + in vec2 vertexCoordsArray; \n\ + uniform mat3 matrix; \n\ + void setPosition(void) \n\ + { \n\ + gl_Position = vec4(matrix * vec3(vertexCoordsArray, 1), 1);\n\ + } \n"; + +static const char* const qopenglslUntransformedPositionVertexShader_core = "\n\ + in vec4 vertexCoordsArray; \n\ + void setPosition(void) \n\ + { \n\ + gl_Position = vertexCoordsArray; \n\ + }\n"; + +// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125 +static const char* const qopenglslPositionWithPatternBrushVertexShader_core = "\n\ + in vec2 vertexCoordsArray; \n\ + in vec3 pmvMatrix1; \n\ + in vec3 pmvMatrix2; \n\ + in vec3 pmvMatrix3; \n\ + out vec2 patternTexCoords; \n\ + uniform vec2 halfViewportSize; \n\ + uniform vec2 invertedTextureSize; \n\ + uniform mat3 brushTransform; \n\ + void setPosition(void) \n\ + { \n\ + mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \n\ + float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithPatternBrushVertexShader_core + = qopenglslPositionWithPatternBrushVertexShader_core; + +static const char* const qopenglslPatternBrushSrcFragmentShader_core = "\n\ + in vec2 patternTexCoords;\n\ + uniform sampler2D brushTexture; \n\ + uniform vec4 patternColor; \n\ + vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture(brushTexture, patternTexCoords).r); \n\ + }\n"; + + +// Linear Gradient Brush +static const char* const qopenglslPositionWithLinearGradientBrushVertexShader_core = "\n\ + in vec2 vertexCoordsArray; \n\ + in vec3 pmvMatrix1; \n\ + in vec3 pmvMatrix2; \n\ + in vec3 pmvMatrix3; \n\ + out float index; \n\ + uniform vec2 halfViewportSize; \n\ + uniform vec3 linearData; \n\ + uniform mat3 brushTransform; \n\ + void setPosition() \n\ + { \n\ + mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithLinearGradientBrushVertexShader_core + = qopenglslPositionWithLinearGradientBrushVertexShader_core; + +static const char* const qopenglslLinearGradientBrushSrcFragmentShader_core = "\n\ + uniform sampler2D brushTexture; \n\ + in float index; \n\ + vec4 srcPixel() \n\ + { \n\ + vec2 val = vec2(index, 0.5); \n\ + return texture(brushTexture, val); \n\ + }\n"; + + +// Conical Gradient Brush +static const char* const qopenglslPositionWithConicalGradientBrushVertexShader_core = "\n\ + in vec2 vertexCoordsArray; \n\ + in vec3 pmvMatrix1; \n\ + in vec3 pmvMatrix2; \n\ + in vec3 pmvMatrix3; \n\ + out vec2 A; \n\ + uniform vec2 halfViewportSize; \n\ + uniform mat3 brushTransform; \n\ + void setPosition(void) \n\ + { \n\ + mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + A = hTexCoords.xy * invertedHTexCoordsZ; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithConicalGradientBrushVertexShader_core + = qopenglslPositionWithConicalGradientBrushVertexShader_core; + +static const char* const qopenglslConicalGradientBrushSrcFragmentShader_core = "\n\ + #define INVERSE_2PI 0.1591549430918953358 \n\ + in vec2 A; \n\ + uniform sampler2D brushTexture; \n\ + uniform float angle; \n\ + vec4 srcPixel() \n\ + { \n\ + float t; \n\ + if (abs(A.y) == abs(A.x)) \n\ + t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \n\ + else \n\ + t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \n\ + return texture(brushTexture, vec2(t - floor(t), 0.5)); \n\ + }\n"; + + +// Radial Gradient Brush +static const char* const qopenglslPositionWithRadialGradientBrushVertexShader_core = "\n\ + in vec2 vertexCoordsArray;\n\ + in vec3 pmvMatrix1; \n\ + in vec3 pmvMatrix2; \n\ + in vec3 pmvMatrix3; \n\ + out float b; \n\ + out vec2 A; \n\ + uniform vec2 halfViewportSize; \n\ + uniform mat3 brushTransform; \n\ + uniform vec2 fmp; \n\ + uniform vec3 bradius; \n\ + void setPosition(void) \n\ + {\n\ + mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + A = hTexCoords.xy * invertedHTexCoordsZ; \n\ + b = bradius.x + 2.0 * dot(A, fmp); \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithRadialGradientBrushVertexShader_core + = qopenglslPositionWithRadialGradientBrushVertexShader_core; + +static const char* const qopenglslRadialGradientBrushSrcFragmentShader_core = "\n\ + in float b; \n\ + in vec2 A; \n\ + uniform sampler2D brushTexture; \n\ + uniform float fmp2_m_radius2; \n\ + uniform float inverse_2_fmp2_m_radius2; \n\ + uniform float sqrfr; \n\ + uniform vec3 bradius; \n\ + \n\ + vec4 srcPixel() \n\ + { \n\ + float c = sqrfr-dot(A, A); \n\ + float det = b*b - 4.0*fmp2_m_radius2*c; \n\ + vec4 result = vec4(0.0); \n\ + if (det >= 0.0) { \n\ + float detSqrt = sqrt(det); \n\ + float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\ + if (bradius.y + w * bradius.z >= 0.0) \n\ + result = texture(brushTexture, vec2(w, 0.5)); \n\ + } \n\ + return result; \n\ + }\n"; + + +// Texture Brush +static const char* const qopenglslPositionWithTextureBrushVertexShader_core = "\n\ + in vec2 vertexCoordsArray; \n\ + in vec3 pmvMatrix1; \n\ + in vec3 pmvMatrix2; \n\ + in vec3 pmvMatrix3; \n\ + out vec2 brushTextureCoords; \n\ + uniform vec2 halfViewportSize; \n\ + uniform vec2 invertedTextureSize; \n\ + uniform mat3 brushTransform; \n\ + \n\ + void setPosition(void) \n\ + { \n\ + mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\ + vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\ + gl_Position.xy = transformedPos.xy / transformedPos.z; \n\ + vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\ + vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\ + float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\ + gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\ + brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\ + }\n"; + +static const char* const qopenglslAffinePositionWithTextureBrushVertexShader_core + = qopenglslPositionWithTextureBrushVertexShader_core; + +static const char* const qopenglslTextureBrushSrcFragmentShader_core = "\n\ + in vec2 brushTextureCoords; \n\ + uniform sampler2D brushTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return texture(brushTexture, brushTextureCoords); \n\ + }\n"; + +static const char* const qopenglslTextureBrushSrcWithPatternFragmentShader_core = "\n\ + in vec2 brushTextureCoords; \n\ + uniform vec4 patternColor; \n\ + uniform sampler2D brushTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture(brushTexture, brushTextureCoords).r); \n\ + }\n"; + +// Solid Fill Brush +static const char* const qopenglslSolidBrushSrcFragmentShader_core = "\n\ + uniform vec4 fragmentColor; \n\ + vec4 srcPixel() \n\ + { \n\ + return fragmentColor; \n\ + }\n"; + +static const char* const qopenglslImageSrcFragmentShader_core = "\n\ + in vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return texture(imageTexture, textureCoords); \n\ + }\n"; + +static const char* const qopenglslCustomSrcFragmentShader_core = "\n\ + in vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return customShader(imageTexture, textureCoords); \n\ + }\n"; + +static const char* const qopenglslImageSrcWithPatternFragmentShader_core = "\n\ + in vec2 textureCoords; \n\ + uniform vec4 patternColor; \n\ + uniform sampler2D imageTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return patternColor * (1.0 - texture(imageTexture, textureCoords).r); \n\ + }\n"; + +static const char* const qopenglslNonPremultipliedImageSrcFragmentShader_core = "\n\ + in vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + vec4 sample = texture(imageTexture, textureCoords); \n\ + sample.rgb = sample.rgb * sample.a; \n\ + return sample; \n\ + }\n"; + +static const char* const qopenglslGrayscaleImageSrcFragmentShader_core = "\n\ + in vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return texture(imageTexture, textureCoords).rrra; \n\ + }\n"; + +static const char* const qopenglslAlphaImageSrcFragmentShader_core = "\n\ + in vec2 textureCoords; \n\ + uniform sampler2D imageTexture; \n\ + vec4 srcPixel() \n\ + { \n\ + return vec4(0, 0, 0, texture(imageTexture, textureCoords).r); \n\ + }\n"; + +static const char* const qopenglslShockingPinkSrcFragmentShader_core = "\n\ + vec4 srcPixel() \n\ + { \n\ + return vec4(0.98, 0.06, 0.75, 1.0); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_ImageArrays_core = + "#version 150 core\n\ + in float opacity; \n\ + out vec4 fragColor; \n\ + vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + fragColor = srcPixel() * opacity; \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_MO_core = + "#version 150 core\n\ + out vec4 fragColor; \n\ + uniform float globalOpacity; \n\ + vec4 srcPixel(); \n\ + vec4 applyMask(vec4); \n\ + void main() \n\ + { \n\ + fragColor = applyMask(srcPixel()*globalOpacity); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_M_core = + "#version 150 core\n\ + out vec4 fragColor; \n\ + vec4 srcPixel(); \n\ + vec4 applyMask(vec4); \n\ + void main() \n\ + { \n\ + fragColor = applyMask(srcPixel()); \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_O_core = + "#version 150 core\n\ + out vec4 fragColor; \n\ + uniform float globalOpacity; \n\ + vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + fragColor = srcPixel()*globalOpacity; \n\ + }\n"; + +static const char* const qopenglslMainFragmentShader_core = + "#version 150 core\n\ + out vec4 fragColor; \n\ + vec4 srcPixel(); \n\ + void main() \n\ + { \n\ + fragColor = srcPixel(); \n\ + }\n"; + +static const char* const qopenglslMaskFragmentShader_core = "\n\ + in vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + vec4 applyMask(vec4 src) \n\ + {\n\ + vec4 mask = texture(maskTexture, textureCoords); \n\ + return src * mask.r; \n\ + }\n"; + +// For source over with subpixel antialiasing, the final color is calculated per component as follows +// (.a is alpha component, .c is red, green or blue component): +// alpha = src.a * mask.c * opacity +// dest.c = dest.c * (1 - alpha) + src.c * alpha +// +// In the first pass, calculate: dest.c = dest.c * (1 - alpha) with blend funcs: zero, 1 - source color +// In the second pass, calculate: dest.c = dest.c + src.c * alpha with blend funcs: one, one +// +// If source is a solid color (src is constant), only the first pass is needed, with blend funcs: constant, 1 - source color + +// For source composition with subpixel antialiasing, the final color is calculated per component as follows: +// alpha = src.a * mask.c * opacity +// dest.c = dest.c * (1 - mask.c) + src.c * alpha +// + +static const char* const qopenglslRgbMaskFragmentShaderPass1_core = "\n\ + in vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + vec4 applyMask(vec4 src) \n\ + { \n\ + vec4 mask = texture(maskTexture, textureCoords); \n\ + return src.a * mask; \n\ + }\n"; + +static const char* const qopenglslRgbMaskFragmentShaderPass2_core = "\n\ + in vec2 textureCoords;\n\ + uniform sampler2D maskTexture;\n\ + vec4 applyMask(vec4 src) \n\ + { \n\ + vec4 mask = texture(maskTexture, textureCoords); \n\ + return src * mask; \n\ + }\n"; + +/* + Left to implement: + RgbMaskFragmentShader_core, + RgbMaskWithGammaFragmentShader_core, +*/ + +QT_END_NAMESPACE + +#endif // GLGC_SHADER_SOURCE_H diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/qopenglgradientcache.cpp index fc5e236ca6..7a932a19bb 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/qopenglgradientcache.cpp @@ -37,20 +37,27 @@ ** ****************************************************************************/ -#include "qglgradientcache_p.h" +#include "qopenglgradientcache_p.h" #include <private/qdrawhelper_p.h> -#include <private/qgl_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qrgba64_p.h> #include <QtCore/qmutex.h> #include <QtCore/qrandom.h> +#include "qopenglfunctions.h" +#include <private/qopenglextensions_p.h> + +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif QT_BEGIN_NAMESPACE -class QGL2GradientCacheWrapper +class QOpenGL2GradientCacheWrapper { public: - QGL2GradientCache *cacheForContext(const QGLContext *context) { + QOpenGL2GradientCache *cacheForContext(QOpenGLContext *context) { QMutexLocker lock(&m_mutex); - return m_resource.value<QGL2GradientCache>(context->contextHandle()); + return m_resource.value<QOpenGL2GradientCache>(context); } private: @@ -58,39 +65,39 @@ private: QMutex m_mutex; }; -Q_GLOBAL_STATIC(QGL2GradientCacheWrapper, qt_gradient_caches) +Q_GLOBAL_STATIC(QOpenGL2GradientCacheWrapper, qt_gradient_caches) -QGL2GradientCache::QGL2GradientCache(QOpenGLContext *ctx) +QOpenGL2GradientCache::QOpenGL2GradientCache(QOpenGLContext *ctx) : QOpenGLSharedResource(ctx->shareGroup()) { } -QGL2GradientCache::~QGL2GradientCache() +QOpenGL2GradientCache::~QOpenGL2GradientCache() { cache.clear(); } -QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) +QOpenGL2GradientCache *QOpenGL2GradientCache::cacheForContext(QOpenGLContext *context) { return qt_gradient_caches()->cacheForContext(context); } -void QGL2GradientCache::invalidateResource() +void QOpenGL2GradientCache::invalidateResource() { QMutexLocker lock(&m_mutex); cache.clear(); } -void QGL2GradientCache::freeResource(QOpenGLContext *) +void QOpenGL2GradientCache::freeResource(QOpenGLContext *) { cleanCache(); } -void QGL2GradientCache::cleanCache() +void QOpenGL2GradientCache::cleanCache() { - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); QMutexLocker lock(&m_mutex); - QGLGradientColorTableHash::const_iterator it = cache.constBegin(); + QOpenGLGradientColorTableHash::const_iterator it = cache.constBegin(); + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); for (; it != cache.constEnd(); ++it) { const CacheInfo &cache_info = it.value(); funcs->glDeleteTextures(1, &cache_info.texId); @@ -98,16 +105,16 @@ void QGL2GradientCache::cleanCache() cache.clear(); } -GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity) +GLuint QOpenGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity) { - QMutexLocker lock(&m_mutex); quint64 hash_val = 0; const QGradientStops stops = gradient.stops(); for (int i = 0; i < stops.size() && i <= 2; i++) hash_val += stops[i].second.rgba(); - QGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val); + const QMutexLocker lock(&m_mutex); + QOpenGLGradientColorTableHash::const_iterator it = cache.constFind(hash_val); if (it == cache.constEnd()) return addCacheElement(hash_val, gradient, opacity); @@ -127,7 +134,7 @@ GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity) } -GLuint QGL2GradientCache::addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) +GLuint QOpenGL2GradientCache::addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) { QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); if (cache.size() == maxCacheSize()) { @@ -135,7 +142,7 @@ GLuint QGL2GradientCache::addCacheElement(quint64 hash_val, const QGradient &gra quint64 key = cache.keys()[elem_to_remove]; // need to call glDeleteTextures on each removed cache entry: - QGLGradientColorTableHash::const_iterator it = cache.constFind(key); + QOpenGLGradientColorTableHash::const_iterator it = cache.constFind(key); do { funcs->glDeleteTextures(1, &it.value().texId); } while (++it != cache.constEnd() && it.key() == key); @@ -143,37 +150,81 @@ GLuint QGL2GradientCache::addCacheElement(quint64 hash_val, const QGradient &gra } CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode()); - uint buffer[1024]; - generateGradientColorTable(gradient, buffer, paletteSize(), opacity); funcs->glGenTextures(1, &cache_entry.texId); funcs->glBindTexture(GL_TEXTURE_2D, cache_entry.texId); - funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, paletteSize(), 1, - 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + if (static_cast<QOpenGLExtensions *>(funcs)->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats)) { + QRgba64 buffer[1024]; + generateGradientColorTable(gradient, buffer, paletteSize(), opacity); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, paletteSize(), 1, + 0, GL_RGBA, GL_UNSIGNED_SHORT, buffer); + } else { + uint buffer[1024]; + generateGradientColorTable(gradient, buffer, paletteSize(), opacity); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, paletteSize(), 1, + 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + } return cache.insert(hash_val, cache_entry).value().texId; } -// GL's expects pixels in RGBA (when using GL_RGBA), bin-endian (ABGR on x86). -// Qt always stores in ARGB reguardless of the byte-order the mancine uses. -static inline uint qtToGlColor(uint c) +//TODO: Let GL generate the texture using an FBO +void QOpenGL2GradientCache::generateGradientColorTable(const QGradient& gradient, QRgba64 *colorTable, int size, qreal opacity) const { - uint o; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - o = (c & 0xff00ff00) // alpha & green already in the right place - | ((c >> 16) & 0x000000ff) // red - | ((c << 16) & 0x00ff0000); // blue -#else //Q_BIG_ENDIAN - o = (c << 8) - | ((c >> 24) & 0x000000ff); -#endif // Q_BYTE_ORDER - return o; + int pos = 0; + const QGradientStops s = gradient.stops(); + + bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation); + + uint alpha = qRound(opacity * 256); + QRgba64 current_color = combineAlpha256(s[0].second.rgba64(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = qPremultiply(current_color); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + QRgba64 next_color = combineAlpha256(s[i + 1].second.rgba64(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = interpolate256(current_color, idist, next_color, dist); + else + colorTable[pos] = qPremultiply(interpolate256(current_color, idist, next_color, dist)); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + QRgba64 last_color = qPremultiply(combineAlpha256(s[sLast].second.rgba64(), alpha)); + for (;pos < size; ++pos) + colorTable[pos] = last_color; + + // Make sure the last color stop is represented at the end of the table + colorTable[size-1] = last_color; } -//TODO: Let GL generate the texture using an FBO -void QGL2GradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const +void QOpenGL2GradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const { int pos = 0; const QGradientStops s = gradient.stops(); + bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation); uint alpha = qRound(opacity * 256); @@ -181,7 +232,7 @@ void QGL2GradientCache::generateGradientColorTable(const QGradient& gradient, ui uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); qreal incr = 1.0 / qreal(size); qreal fpos = 1.5 * incr; - colorTable[pos++] = qtToGlColor(qPremultiply(current_color)); + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); while (fpos <= s.first().first) { colorTable[pos] = colorTable[pos - 1]; @@ -203,9 +254,9 @@ void QGL2GradientCache::generateGradientColorTable(const QGradient& gradient, ui int dist = int(256 * ((fpos - s[i].first) * delta)); int idist = 256 - dist; if (colorInterpolation) - colorTable[pos] = qtToGlColor(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); else - colorTable[pos] = qtToGlColor(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); ++pos; fpos += incr; } @@ -214,7 +265,7 @@ void QGL2GradientCache::generateGradientColorTable(const QGradient& gradient, ui Q_ASSERT(s.size() > 0); - uint last_color = qtToGlColor(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); for (;pos < size; ++pos) colorTable[pos] = last_color; diff --git a/src/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/qopenglgradientcache_p.h index 3c9da982e9..da070ae233 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache_p.h +++ b/src/opengl/qopenglgradientcache_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QGLGRADIENTCACHE_P_H -#define QGLGRADIENTCACHE_P_H +#ifndef QOPENGLGRADIENTCACHE_P_H +#define QOPENGLGRADIENTCACHE_P_H // // W A R N I N G @@ -53,18 +53,19 @@ #include <QMultiHash> #include <QObject> -#include <QtOpenGL/QtOpenGL> -#include <private/qgl_p.h> +#include <private/qopenglcontext_p.h> #include <QtCore/qmutex.h> +#include <QGradient> +#include <qrgba64.h> QT_BEGIN_NAMESPACE -class QGL2GradientCache : public QOpenGLSharedResource +class QOpenGL2GradientCache : public QOpenGLSharedResource { struct CacheInfo { inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) : - stops(s), opacity(op), interpolationMode(mode) {} + stops(std::move(s)), opacity(op), interpolationMode(mode) {} GLuint texId; QGradientStops stops; @@ -72,13 +73,13 @@ class QGL2GradientCache : public QOpenGLSharedResource QGradient::InterpolationMode interpolationMode; }; - typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash; + typedef QMultiHash<quint64, CacheInfo> QOpenGLGradientColorTableHash; public: - static QGL2GradientCache *cacheForContext(const QGLContext *context); + static QOpenGL2GradientCache *cacheForContext(QOpenGLContext *context); - QGL2GradientCache(QOpenGLContext *); - ~QGL2GradientCache(); + QOpenGL2GradientCache(QOpenGLContext *); + ~QOpenGL2GradientCache(); GLuint getBuffer(const QGradient &gradient, qreal opacity); inline int paletteSize() const { return 1024; } @@ -89,15 +90,18 @@ public: private: inline int maxCacheSize() const { return 60; } inline void generateGradientColorTable(const QGradient& gradient, + QRgba64 *colorTable, + int size, qreal opacity) const; + inline void generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const; GLuint addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity); void cleanCache(); - QGLGradientColorTableHash cache; + QOpenGLGradientColorTableHash cache; QMutex m_mutex; }; QT_END_NAMESPACE -#endif // QGLGRADIENTCACHE_P_H +#endif // QOPENGLGRADIENTCACHE_P_H diff --git a/src/opengl/qopenglpaintdevice.cpp b/src/opengl/qopenglpaintdevice.cpp new file mode 100644 index 0000000000..811425cf69 --- /dev/null +++ b/src/opengl/qopenglpaintdevice.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qopenglpaintdevice.h> +#include <qpaintengine.h> +#include <qthreadstorage.h> + +#include <private/qopenglpaintdevice_p.h> +#include <private/qobject_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglframebufferobject_p.h> +#include <private/qopenglpaintengine_p.h> + +// for qt_defaultDpiX/Y +#include <private/qfont_p.h> + +#include <qopenglfunctions.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLPaintDevice + \brief The QOpenGLPaintDevice class enables painting to an OpenGL context using QPainter. + \since 5.0 + \inmodule QtOpenGL + + \ingroup painting-3D + + The QOpenGLPaintDevice uses the \b current QOpenGL context to render + QPainter draw commands. The context is captured upon construction. It + requires support for OpenGL (ES) 2.0 or higher. + + \section1 Performance + + The QOpenGLPaintDevice is almost always hardware accelerated and + has the potential of being much faster than software + rasterization. However, it is more sensitive to state changes, and + therefore requires the drawing commands to be carefully ordered to + achieve optimal performance. + + \section1 Antialiasing and Quality + + Antialiasing in the OpenGL paint engine is done using + multisampling. Most hardware require significantly more memory to + do multisampling and the resulting quality is not on par with the + quality of the software paint engine. The OpenGL paint engine's + strength lies in its performance, not its visual rendering + quality. + + \section1 State Changes + + When painting to a QOpenGLPaintDevice using QPainter, the state of + the current OpenGL context will be altered by the paint engine to + reflect its needs. Applications should not rely upon the OpenGL + state being reset to its original conditions, particularly the + current shader program, OpenGL viewport, texture units, and + drawing modes. + + \section1 Mixing QPainter and OpenGL + + When intermixing QPainter and OpenGL, it is important to notify + QPainter that the OpenGL state may have been cluttered so it can + restore its internal state. This is achieved by calling \l + QPainter::beginNativePainting() before starting the OpenGL + rendering and calling \l QPainter::endNativePainting() after + finishing. + + \sa {OpenGL Window Example} + +*/ + +/*! + Constructs a QOpenGLPaintDevice. + + The QOpenGLPaintDevice is only valid for the current context. + + \sa QOpenGLContext::currentContext() +*/ +QOpenGLPaintDevice::QOpenGLPaintDevice() + : d_ptr(new QOpenGLPaintDevicePrivate(QSize())) +{ +} + +/*! + Constructs a QOpenGLPaintDevice with the given \a size. + + The QOpenGLPaintDevice is only valid for the current context. + + \sa QOpenGLContext::currentContext() +*/ +QOpenGLPaintDevice::QOpenGLPaintDevice(const QSize &size) + : d_ptr(new QOpenGLPaintDevicePrivate(size)) +{ +} + +/*! + Constructs a QOpenGLPaintDevice with the given \a width and \a height. + + The QOpenGLPaintDevice is only valid for the current context. + + \sa QOpenGLContext::currentContext() +*/ +QOpenGLPaintDevice::QOpenGLPaintDevice(int width, int height) + : QOpenGLPaintDevice(QSize(width, height)) +{ +} + +/*! + \internal + */ +QOpenGLPaintDevice::QOpenGLPaintDevice(QOpenGLPaintDevicePrivate &dd) + : d_ptr(&dd) +{ +} + +/*! + Destroys the QOpenGLPaintDevice. +*/ + +QOpenGLPaintDevice::~QOpenGLPaintDevice() +{ + delete d_ptr->engine; +} + +/*! + \fn int QOpenGLPaintDevice::devType() const + \internal + \reimp +*/ + +QOpenGLPaintDevicePrivate::QOpenGLPaintDevicePrivate(const QSize &sz) + : size(sz) + , ctx(QOpenGLContext::currentContext()) + , dpmx(qt_defaultDpiX() * 100. / 2.54) + , dpmy(qt_defaultDpiY() * 100. / 2.54) + , devicePixelRatio(1.0) + , flipped(false) + , engine(nullptr) +{ +} + +QOpenGLPaintDevicePrivate::~QOpenGLPaintDevicePrivate() +{ +} + +class QOpenGLEngineThreadStorage +{ +public: + QPaintEngine *engine() { + QPaintEngine *&localEngine = storage.localData(); + if (!localEngine) + localEngine = new QOpenGL2PaintEngineEx; + return localEngine; + } + +private: + QThreadStorage<QPaintEngine *> storage; +}; + +Q_GLOBAL_STATIC(QOpenGLEngineThreadStorage, qt_opengl_engine) + +/*! + \reimp +*/ + +QPaintEngine *QOpenGLPaintDevice::paintEngine() const +{ + if (d_ptr->engine) + return d_ptr->engine; + + QPaintEngine *engine = qt_opengl_engine()->engine(); + if (engine->isActive() && engine->paintDevice() != this) { + d_ptr->engine = new QOpenGL2PaintEngineEx; + return d_ptr->engine; + } + + return engine; +} + +/*! + Returns the OpenGL context associated with the paint device. +*/ + +QOpenGLContext *QOpenGLPaintDevice::context() const +{ + return d_ptr->ctx; +} + +/*! + Returns the pixel size of the paint device. + + \sa setSize() +*/ + +QSize QOpenGLPaintDevice::size() const +{ + return d_ptr->size; +} + +/*! + Sets the pixel size of the paint device to \a size. + + \sa size() +*/ + +void QOpenGLPaintDevice::setSize(const QSize &size) +{ + d_ptr->size = size; +} + +/*! + Sets the device pixel ratio for the paint device to \a devicePixelRatio. +*/ +void QOpenGLPaintDevice::setDevicePixelRatio(qreal devicePixelRatio) +{ + d_ptr->devicePixelRatio = devicePixelRatio; +} + +/*! + \reimp +*/ + +int QOpenGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + switch (metric) { + case PdmWidth: + return d_ptr->size.width(); + case PdmHeight: + return d_ptr->size.height(); + case PdmDepth: + return 32; + case PdmWidthMM: + return qRound(d_ptr->size.width() * 1000 / d_ptr->dpmx); + case PdmHeightMM: + return qRound(d_ptr->size.height() * 1000 / d_ptr->dpmy); + case PdmNumColors: + return 0; + case PdmDpiX: + return qRound(d_ptr->dpmx * 0.0254); + case PdmDpiY: + return qRound(d_ptr->dpmy * 0.0254); + case PdmPhysicalDpiX: + return qRound(d_ptr->dpmx * 0.0254); + case PdmPhysicalDpiY: + return qRound(d_ptr->dpmy * 0.0254); + case PdmDevicePixelRatio: + return d_ptr->devicePixelRatio; + case PdmDevicePixelRatioScaled: + return d_ptr->devicePixelRatio * QPaintDevice::devicePixelRatioFScale(); + + default: + qWarning("QOpenGLPaintDevice::metric() - metric %d not known", metric); + return 0; + } +} + +/*! + Returns the number of pixels per meter horizontally. + + \sa setDotsPerMeterX() +*/ + +qreal QOpenGLPaintDevice::dotsPerMeterX() const +{ + return d_ptr->dpmx; +} + +/*! + Returns the number of pixels per meter vertically. + + \sa setDotsPerMeterY() +*/ + +qreal QOpenGLPaintDevice::dotsPerMeterY() const +{ + return d_ptr->dpmy; +} + +/*! + Sets the number of pixels per meter horizontally to \a dpmx. + + \sa dotsPerMeterX() +*/ + +void QOpenGLPaintDevice::setDotsPerMeterX(qreal dpmx) +{ + d_ptr->dpmx = dpmx; +} + +/*! + Sets the number of pixels per meter vertically to \a dpmy. + + \sa dotsPerMeterY() +*/ + +void QOpenGLPaintDevice::setDotsPerMeterY(qreal dpmy) +{ + d_ptr->dpmx = dpmy; +} + +/*! + Sets whether painting should be flipped around the Y-axis or not to \a flipped. + + \sa paintFlipped() +*/ +void QOpenGLPaintDevice::setPaintFlipped(bool flipped) +{ + d_ptr->flipped = flipped; +} + +/*! + Returns \c true if painting is flipped around the Y-axis. + + \sa setPaintFlipped() +*/ + +bool QOpenGLPaintDevice::paintFlipped() const +{ + return d_ptr->flipped; +} + +/*! + This virtual method is provided as a callback to allow re-binding a target + frame buffer object or context when different QOpenGLPaintDevice instances + are issuing draw calls alternately. + + \l{QPainter::beginNativePainting()}{beginNativePainting()} will also trigger + this method. + + The default implementation does nothing. +*/ +void QOpenGLPaintDevice::ensureActiveTarget() +{ +} + +QT_END_NAMESPACE diff --git a/src/opengl/qopenglpaintdevice.h b/src/opengl/qopenglpaintdevice.h new file mode 100644 index 0000000000..f4e1ce4a2e --- /dev/null +++ b/src/opengl/qopenglpaintdevice.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLPAINTDEVICE_H +#define QOPENGLPAINTDEVICE_H + +#include <QtOpenGL/qtopenglglobal.h> + +#ifndef QT_NO_OPENGL + +#include <QtGui/qpaintdevice.h> +#include <QtGui/qopengl.h> +#include <QtGui/qopenglcontext.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLPaintDevicePrivate; + +class Q_OPENGL_EXPORT QOpenGLPaintDevice : public QPaintDevice +{ + Q_DECLARE_PRIVATE(QOpenGLPaintDevice) +public: + QOpenGLPaintDevice(); + explicit QOpenGLPaintDevice(const QSize &size); + QOpenGLPaintDevice(int width, int height); + ~QOpenGLPaintDevice(); + + int devType() const override { return QInternal::OpenGL; } + QPaintEngine *paintEngine() const override; + + QOpenGLContext *context() const; + QSize size() const; + void setSize(const QSize &size); + void setDevicePixelRatio(qreal devicePixelRatio); + + qreal dotsPerMeterX() const; + qreal dotsPerMeterY() const; + + void setDotsPerMeterX(qreal); + void setDotsPerMeterY(qreal); + + void setPaintFlipped(bool flipped); + bool paintFlipped() const; + + virtual void ensureActiveTarget(); + +protected: + QOpenGLPaintDevice(QOpenGLPaintDevicePrivate &dd); + int metric(QPaintDevice::PaintDeviceMetric metric) const override; + + Q_DISABLE_COPY(QOpenGLPaintDevice) + QScopedPointer<QOpenGLPaintDevicePrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QOPENGLPAINTDEVICE_H diff --git a/src/opengl/qopenglpaintdevice_p.h b/src/opengl/qopenglpaintdevice_p.h new file mode 100644 index 0000000000..f4f02e7b57 --- /dev/null +++ b/src/opengl/qopenglpaintdevice_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGL_PAINTDEVICE_P_H +#define QOPENGL_PAINTDEVICE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Qt OpenGL classes. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <qopenglpaintdevice.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; +class QPaintEngine; + +class Q_OPENGL_EXPORT QOpenGLPaintDevicePrivate +{ +public: + QOpenGLPaintDevicePrivate(const QSize &size); + virtual ~QOpenGLPaintDevicePrivate(); + + static QOpenGLPaintDevicePrivate *get(QOpenGLPaintDevice *dev) { return dev->d_func(); } + + virtual void beginPaint() { } + virtual void endPaint() { } + +public: + QSize size; + QOpenGLContext *ctx; + + qreal dpmx; + qreal dpmy; + qreal devicePixelRatio; + + bool flipped; + + QPaintEngine *engine; +}; + +QT_END_NAMESPACE + +#endif // QOPENGL_PAINTDEVICE_P_H diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/qopenglpaintengine.cpp index 2546f6dc13..4168067e8f 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/qopenglpaintengine.cpp @@ -62,76 +62,74 @@ // #define QT_OPENGL_CACHE_AS_VBOS -#include "qglgradientcache_p.h" -#include "qpaintengineex_opengl2_p.h" +#include <private/qopenglgradientcache_p.h> +#include <private/qopengltexturecache_p.h> +#include "qopenglpaintengine_p.h" +#include "qopenglpaintdevice_p.h" #include <string.h> //for memcpy #include <qmath.h> -#include <private/qgl_p.h> +#include <private/qopengl_p.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> #include <private/qpaintengineex_p.h> #include <QPaintEngine> #include <private/qpainter_p.h> #include <private/qfontengine_p.h> #include <private/qdatabuffer_p.h> #include <private/qstatictext_p.h> -#include <QtGui/private/qtriangulator_p.h> +#include <private/qtriangulator_p.h> -#include "qglengineshadermanager_p.h" -#include "qgl2pexvertexarray_p.h" -#include "qtextureglyphcache_gl_p.h" +#include <private/qopenglengineshadermanager_p.h> +#include <private/qopengl2pexvertexarray_p.h> +#include <private/qopengltextureglyphcache_p.h> #include <QDebug> -#ifndef QT_OPENGL_ES_2 -# include <qopenglfunctions_1_1.h> -#endif +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ QT_BEGIN_NAMESPACE - -Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); +Q_OPENGL_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); ////////////////////////////////// Private Methods ////////////////////////////////////////// -QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate() +QOpenGL2PaintEngineExPrivate::~QOpenGL2PaintEngineExPrivate() { delete shaderManager; - while (pathCaches.size()) { - QVectorPath::CacheEntry *e = *(pathCaches.constBegin()); - e->cleanup(e->engine, e->data); - e->data = 0; - e->engine = 0; - } + vertexBuffer.destroy(); + texCoordBuffer.destroy(); + opacityBuffer.destroy(); + indexBuffer.destroy(); + vao.destroy(); if (elementIndicesVBOId != 0) { - glDeleteBuffers(1, &elementIndicesVBOId); + funcs.glDeleteBuffers(1, &elementIndicesVBOId); elementIndicesVBOId = 0; } } -void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id) -{ -// glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit? - if (id != GLuint(-1) && id == lastTextureUsed) - return; - - lastTextureUsed = id; - - if (smoothPixmapTransform) { - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode); - glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode); -} - - inline QColor qt_premultiplyColor(QColor c, GLfloat opacity) { qreal alpha = c.alphaF() * opacity; @@ -143,7 +141,7 @@ inline QColor qt_premultiplyColor(QColor c, GLfloat opacity) } -void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush) +void QOpenGL2PaintEngineExPrivate::setBrush(const QBrush& brush) { if (qbrush_fast_equals(currentBrush, brush)) return; @@ -152,8 +150,8 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush) Q_ASSERT(newStyle != Qt::NoBrush); currentBrush = brush; - if (!currentBrushPixmap.isNull()) - currentBrushPixmap = QPixmap(); + if (!currentBrushImage.isNull()) + currentBrushImage = QImage(); brushUniformsDirty = true; // All brushes have at least one uniform if (newStyle > Qt::SolidPattern) @@ -162,7 +160,7 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush) if (currentBrush.style() == Qt::TexturePattern && qHasPixmapTexture(brush) && brush.texture().isQBitmap()) { - shaderManager->setSrcPixelType(QGLEngineShaderManager::TextureSrcWithPattern); + shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::TextureSrcWithPattern); } else { shaderManager->setSrcPixelType(newStyle); } @@ -170,7 +168,7 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush& brush) } -void QGL2PaintEngineExPrivate::useSimpleShader() +void QOpenGL2PaintEngineExPrivate::useSimpleShader() { shaderManager->useSimpleProgram(); @@ -178,74 +176,162 @@ void QGL2PaintEngineExPrivate::useSimpleShader() updateMatrix(); } -// ####TODO Properly #ifdef this class to use #define symbols actually defined -// by OpenGL/ES includes -#ifndef GL_MIRRORED_REPEAT_IBM -#define GL_MIRRORED_REPEAT_IBM 0x8370 -#endif +/* + Single entry-point for activating, binding, and setting properties. + + Allows keeping track of (caching) the latest texture unit and bound + texture in a central place, so that we can skip re-binding unless + needed. + + \note Any code or Qt API that internally activates or binds will + not affect the cache used by this function, which means they will + lead to inconsisent state. QPainter::beginNativePainting() takes + care of resetting the cache, so for user–code this is fine, but + internally in the paint engine care must be taken to not call + functions that may activate or bind under our feet. +*/ +template<typename T> +void QOpenGL2PaintEngineExPrivate::updateTexture(GLenum textureUnit, const T &texture, GLenum wrapMode, GLenum filterMode, TextureUpdateMode updateMode) +{ + static const GLenum target = GL_TEXTURE_2D; -void QGL2PaintEngineExPrivate::updateBrushTexture() + activateTextureUnit(textureUnit); + + GLuint textureId = bindTexture(texture); + + if (updateMode == UpdateIfNeeded && textureId == lastTextureUsed) + return; + + lastTextureUsed = textureId; + + funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode); + funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode); + + funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filterMode); + funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filterMode); +} + +void QOpenGL2PaintEngineExPrivate::activateTextureUnit(GLenum textureUnit) { - Q_Q(QGL2PaintEngineEx); -// qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()"); + if (textureUnit != lastTextureUnitUsed) { + funcs.glActiveTexture(GL_TEXTURE0 + textureUnit); + lastTextureUnitUsed = textureUnit; + + // We simplify things by keeping a single cached value of the last + // texture that was bound, instead of one per texture unit. This + // means that switching texture units could potentially mean we + // need a re-bind and corresponding parameter updates. + lastTextureUsed = GLuint(-1); + } +} + +template<> +GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const GLuint &textureId) +{ + if (textureId != lastTextureUsed) + funcs.glBindTexture(GL_TEXTURE_2D, textureId); + + return textureId; +} + +template<> +GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QImage &image) +{ + return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, image); +} + +template<> +GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QPixmap &pixmap) +{ + return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, pixmap); +} + +template<> +GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const QGradient &gradient) +{ + // We apply global opacity in the fragment shaders, so we always pass 1.0 + // for opacity to the cache. + GLuint textureId = QOpenGL2GradientCache::cacheForContext(ctx)->getBuffer(gradient, 1.0); + + // QOpenGL2GradientCache::getBuffer() may bind and generate a new texture if it + // hasn't been cached yet, but will otherwise return an unbound texture id. To + // be sure that the texture is bound, we unfortunately have to bind again, + // which results in the initial generation of the texture doing two binds. + return bindTexture(textureId); +} + +struct ImageWithBindOptions +{ + const QImage ℑ + QOpenGLTextureUploader::BindOptions options; +}; + +template<> +GLuint QOpenGL2PaintEngineExPrivate::bindTexture(const ImageWithBindOptions &imageWithOptions) +{ + return QOpenGLTextureCache::cacheForContext(ctx)->bindTexture(ctx, imageWithOptions.image, imageWithOptions.options); +} + +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +void QOpenGL2PaintEngineExPrivate::updateBrushTexture() +{ + Q_Q(QOpenGL2PaintEngineEx); +// qDebug("QOpenGL2PaintEngineExPrivate::updateBrushTexture()"); Qt::BrushStyle style = currentBrush.style(); + bool smoothPixmapTransform = q->state()->renderHints & QPainter::SmoothPixmapTransform; + GLenum filterMode = smoothPixmapTransform ? GL_LINEAR : GL_NEAREST; + if ( (style >= Qt::Dense1Pattern) && (style <= Qt::DiagCrossPattern) ) { // Get the image data for the pattern - QImage texImage = qt_imageForBrush(style, false); + QImage textureImage = qt_imageForBrush(style, false); - glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); - ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption); - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); + updateTexture(QT_BRUSH_TEXTURE_UNIT, textureImage, GL_REPEAT, filterMode, ForceUpdate); } else if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { // Gradiant brush: All the gradiants use the same texture - const QGradient* g = currentBrush.gradient(); + const QGradient *gradient = currentBrush.gradient(); - // We apply global opacity in the fragment shaders, so we always pass 1.0 - // for opacity to the cache. - GLuint texId = QGL2GradientCache::cacheForContext(ctx)->getBuffer(*g, 1.0); + GLenum wrapMode = GL_CLAMP_TO_EDGE; + if (gradient->spread() == QGradient::RepeatSpread || gradient->type() == QGradient::ConicalGradient) + wrapMode = GL_REPEAT; + else if (gradient->spread() == QGradient::ReflectSpread) + wrapMode = GL_MIRRORED_REPEAT; - glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); - glBindTexture(GL_TEXTURE_2D, texId); - - if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient) - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); - else if (g->spread() == QGradient::ReflectSpread) - updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, q->state()->renderHints & QPainter::SmoothPixmapTransform); - else - updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, q->state()->renderHints & QPainter::SmoothPixmapTransform); + updateTexture(QT_BRUSH_TEXTURE_UNIT, *gradient, wrapMode, filterMode, ForceUpdate); } else if (style == Qt::TexturePattern) { - currentBrushPixmap = currentBrush.texture(); + currentBrushImage = currentBrush.textureImage(); int max_texture_size = ctx->d_func()->maxTextureSize(); - if (currentBrushPixmap.width() > max_texture_size || currentBrushPixmap.height() > max_texture_size) - currentBrushPixmap = currentBrushPixmap.scaled(max_texture_size, max_texture_size, Qt::KeepAspectRatio); + QSize newSize = currentBrushImage.size(); + newSize = newSize.boundedTo(QSize(max_texture_size, max_texture_size)); + if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) { + if (!isPowerOfTwo(newSize.width()) || !isPowerOfTwo(newSize.height())) { + newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1)); + newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1)); + } + } + if (currentBrushImage.size() != newSize) + currentBrushImage = currentBrushImage.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); GLuint wrapMode = GL_REPEAT; - if (ctx->contextHandle()->isOpenGLES()) { - // OpenGL ES does not support GL_REPEAT wrap modes for NPOT textures. So instead, - // we emulate GL_REPEAT by only taking the fractional part of the texture coords - // in the qopenglslTextureBrushSrcFragmentShader program. - wrapMode = GL_CLAMP_TO_EDGE; - } - glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); - QGLTexture *tex = ctx->d_func()->bindTexture(currentBrushPixmap, GL_TEXTURE_2D, GL_RGBA, - QGLContext::InternalBindOption | - QGLContext::CanFlipNativePixmapBindOption); - updateTextureFilter(GL_TEXTURE_2D, wrapMode, q->state()->renderHints & QPainter::SmoothPixmapTransform); - textureInvertedY = tex->options & QGLContext::InvertedYBindOption ? -1 : 1; + updateTexture(QT_BRUSH_TEXTURE_UNIT, currentBrushImage, wrapMode, filterMode, ForceUpdate); } brushTextureDirty = false; } -void QGL2PaintEngineExPrivate::updateBrushUniforms() +void QOpenGL2PaintEngineExPrivate::updateBrushUniforms() { -// qDebug("QGL2PaintEngineExPrivate::updateBrushUniforms()"); +// qDebug("QOpenGL2PaintEngineExPrivate::updateBrushUniforms()"); Qt::BrushStyle style = currentBrush.style(); if (style == Qt::NoBrush) @@ -255,7 +341,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() if (style == Qt::SolidPattern) { QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::FragmentColor), col); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::FragmentColor), col); } else { // All other brushes have a transform and thus need the translation point: @@ -264,10 +350,10 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() if (style <= Qt::DiagCrossPattern) { QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); QVector2D halfViewportSize(width*0.5, height*0.5); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); } else if (style == Qt::LinearGradientPattern) { const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush.gradient()); @@ -284,10 +370,10 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() 1.0f / (l.x() * l.x() + l.y() * l.y()) ); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::LinearData), linearData); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::LinearData), linearData); QVector2D halfViewportSize(width*0.5, height*0.5); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); } else if (style == Qt::ConicalGradientPattern) { const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush.gradient()); @@ -295,10 +381,10 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() GLfloat angle = -qDegreesToRadians(g->angle()); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Angle), angle); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Angle), angle); QVector2D halfViewportSize(width*0.5, height*0.5); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); } else if (style == Qt::RadialGradientPattern) { const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush.gradient()); @@ -308,38 +394,38 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() translationPoint = realFocal; QPointF fmp = realCenter - realFocal; - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp), fmp); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp), fmp); GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius; - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Inverse2Fmp2MRadius2), + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Fmp2MRadius2), fmp2_m_radius2); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Inverse2Fmp2MRadius2), GLfloat(1.0 / (2.0*fmp2_m_radius2))); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::SqrFr), + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::SqrFr), GLfloat(g->focalRadius() * g->focalRadius())); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BRadius), + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BRadius), GLfloat(2 * (g->centerRadius() - g->focalRadius()) * g->focalRadius()), g->focalRadius(), g->centerRadius() - g->focalRadius()); QVector2D halfViewportSize(width*0.5, height*0.5); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); } else if (style == Qt::TexturePattern) { const QPixmap& texPixmap = currentBrush.texture(); if (qHasPixmapTexture(currentBrush) && currentBrush.texture().isQBitmap()) { QColor col = qt_premultiplyColor(currentBrush.color(), (GLfloat)q->state()->opacity); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); } QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height()); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::InvertedTextureSize), invertedTextureSize); QVector2D halfViewportSize(width*0.5, height*0.5); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::HalfViewportSize), halfViewportSize); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::HalfViewportSize), halfViewportSize); } else - qWarning("QGL2PaintEngineEx: Unimplemented fill style"); + qWarning("QOpenGL2PaintEngineEx: Unimplemented fill style"); const QPointF &brushOrigin = q->state()->brushOrigin; QTransform matrix = q->state()->matrix; @@ -348,28 +434,24 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y()); qreal m22 = -1; qreal dy = height; - if (device->isFlipped()) { + if (device->paintFlipped()) { m22 = 1; dy = 0; } QTransform gl_to_qt(1, 0, 0, m22, 0, dy); - QTransform inv_matrix; - if (style == Qt::TexturePattern && textureInvertedY == -1) - inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate; - else - inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate; + QTransform inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate; - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTransform), inv_matrix); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTransform), inv_matrix); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT); } brushUniformsDirty = false; } // This assumes the shader manager has already setup the correct shader program -void QGL2PaintEngineExPrivate::updateMatrix() +void QOpenGL2PaintEngineExPrivate::updateMatrix() { -// qDebug("QGL2PaintEngineExPrivate::updateMatrix()"); +// qDebug("QOpenGL2PaintEngineExPrivate::updateMatrix()"); const QTransform& transform = q->state()->matrix; @@ -396,7 +478,7 @@ void QGL2PaintEngineExPrivate::updateMatrix() GLfloat dx = transform.dx(); GLfloat dy = transform.dy(); - if (device->isFlipped()) { + if (device->paintFlipped()) { hfactor *= -1; dy -= height; } @@ -429,59 +511,107 @@ void QGL2PaintEngineExPrivate::updateMatrix() // Set the PMV matrix attribute. As we use an attributes rather than uniforms, we only // need to do this once for every matrix change and persists across all shader programs. - glVertexAttrib3fv(QT_PMV_MATRIX_1_ATTR, pmvMatrix[0]); - glVertexAttrib3fv(QT_PMV_MATRIX_2_ATTR, pmvMatrix[1]); - glVertexAttrib3fv(QT_PMV_MATRIX_3_ATTR, pmvMatrix[2]); + funcs.glVertexAttrib3fv(QT_PMV_MATRIX_1_ATTR, pmvMatrix[0]); + funcs.glVertexAttrib3fv(QT_PMV_MATRIX_2_ATTR, pmvMatrix[1]); + funcs.glVertexAttrib3fv(QT_PMV_MATRIX_3_ATTR, pmvMatrix[2]); dasher.setInvScale(inverseScale); stroker.setInvScale(inverseScale); } -void QGL2PaintEngineExPrivate::updateCompositionMode() +void QOpenGL2PaintEngineExPrivate::updateCompositionMode() { // NOTE: The entire paint engine works on pre-multiplied data - which is why some of these // composition modes look odd. -// qDebug() << "QGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode; +// qDebug() << "QOpenGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode; + if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::BlendEquationAdvanced)) { + if (q->state()->composition_mode <= QPainter::CompositionMode_Plus) { + funcs.glDisable(GL_BLEND_ADVANCED_COHERENT_KHR); + funcs.glBlendEquation(GL_FUNC_ADD); + } else { + funcs.glEnable(GL_BLEND_ADVANCED_COHERENT_KHR); + } + shaderManager->setCompositionMode(q->state()->composition_mode); + } else { + if (q->state()->composition_mode > QPainter::CompositionMode_Plus) { + qWarning("Unsupported composition mode"); + compositionModeDirty = false; + return; + } + } switch(q->state()->composition_mode) { case QPainter::CompositionMode_SourceOver: - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + funcs.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break; case QPainter::CompositionMode_DestinationOver: - glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE); + funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE); break; case QPainter::CompositionMode_Clear: - glBlendFunc(GL_ZERO, GL_ZERO); + funcs.glBlendFunc(GL_ZERO, GL_ZERO); break; case QPainter::CompositionMode_Source: - glBlendFunc(GL_ONE, GL_ZERO); + funcs.glBlendFunc(GL_ONE, GL_ZERO); break; case QPainter::CompositionMode_Destination: - glBlendFunc(GL_ZERO, GL_ONE); + funcs.glBlendFunc(GL_ZERO, GL_ONE); break; case QPainter::CompositionMode_SourceIn: - glBlendFunc(GL_DST_ALPHA, GL_ZERO); + funcs.glBlendFunc(GL_DST_ALPHA, GL_ZERO); break; case QPainter::CompositionMode_DestinationIn: - glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + funcs.glBlendFunc(GL_ZERO, GL_SRC_ALPHA); break; case QPainter::CompositionMode_SourceOut: - glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO); + funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO); break; case QPainter::CompositionMode_DestinationOut: - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); + funcs.glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); break; case QPainter::CompositionMode_SourceAtop: - glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + funcs.glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; case QPainter::CompositionMode_DestinationAtop: - glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA); + funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA); break; case QPainter::CompositionMode_Xor: - glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + funcs.glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; case QPainter::CompositionMode_Plus: - glBlendFunc(GL_ONE, GL_ONE); + funcs.glBlendFunc(GL_ONE, GL_ONE); + break; + case QPainter::CompositionMode_Multiply: + funcs.glBlendEquation(GL_MULTIPLY_KHR); + break; + case QPainter::CompositionMode_Screen: + funcs.glBlendEquation(GL_SCREEN_KHR); + break; + case QPainter::CompositionMode_Overlay: + funcs.glBlendEquation(GL_OVERLAY_KHR); + break; + case QPainter::CompositionMode_Darken: + funcs.glBlendEquation(GL_DARKEN_KHR); + break; + case QPainter::CompositionMode_Lighten: + funcs.glBlendEquation(GL_LIGHTEN_KHR); + break; + case QPainter::CompositionMode_ColorDodge: + funcs.glBlendEquation(GL_COLORDODGE_KHR); + break; + case QPainter::CompositionMode_ColorBurn: + funcs.glBlendEquation(GL_COLORBURN_KHR); + break; + case QPainter::CompositionMode_HardLight: + funcs.glBlendEquation(GL_HARDLIGHT_KHR); + break; + case QPainter::CompositionMode_SoftLight: + funcs.glBlendEquation(GL_SOFTLIGHT_KHR); + break; + case QPainter::CompositionMode_Difference: + funcs.glBlendEquation(GL_DIFFERENCE_KHR); + break; + case QPainter::CompositionMode_Exclusion: + funcs.glBlendEquation(GL_EXCLUSION_KHR); break; default: qWarning("Unsupported composition mode"); @@ -491,7 +621,7 @@ void QGL2PaintEngineExPrivate::updateCompositionMode() compositionModeDirty = false; } -static inline void setCoords(GLfloat *coords, const QGLRect &rect) +static inline void setCoords(GLfloat *coords, const QOpenGLRect &rect) { coords[0] = rect.left; coords[1] = rect.top; @@ -503,11 +633,10 @@ static inline void setCoords(GLfloat *coords, const QGLRect &rect) coords[7] = rect.bottom; } -void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern) +void QOpenGL2PaintEngineExPrivate::drawTexture(const QOpenGLRect& dest, const QOpenGLRect& src, const QSize &textureSize, bool opaque, bool pattern) { // Setup for texture drawing currentBrush = noBrush; - shaderManager->setSrcPixelType(pattern ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc); if (snapToPixelGrid) { snapToPixelGrid = false; @@ -515,176 +644,143 @@ void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& s } if (prepareForDraw(opaque)) - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); if (pattern) { QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); } GLfloat dx = 1.0 / textureSize.width(); GLfloat dy = 1.0 / textureSize.height(); - QGLRect srcTextureRect(src.left*dx, src.top*dy, src.right*dx, src.bottom*dy); + QOpenGLRect srcTextureRect(src.left*dx, src.top*dy, src.right*dx, src.bottom*dy); setCoords(staticVertexCoordinateArray, dest); setCoords(staticTextureCoordinateArray, srcTextureRect); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true); + + uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8); + uploadData(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray, 8); + + funcs.glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } -void QGL2PaintEngineEx::beginNativePainting() +void QOpenGL2PaintEngineEx::beginNativePainting() { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); ensureActive(); d->transferMode(BrushDrawingMode); d->nativePaintingActive = true; - d->glUseProgram(0); + d->funcs.glUseProgram(0); // Disable all the vertex attribute arrays: for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) - d->glDisableVertexAttribArray(i); + d->funcs.glDisableVertexAttribArray(i); + +#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_DYNAMIC) + Q_ASSERT(QOpenGLContext::currentContext()); + const QOpenGLContext *ctx = d->ctx; + const QSurfaceFormat &fmt = d->device->context()->format(); + if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 1) + || (fmt.majorVersion() == 3 && fmt.minorVersion() == 1 && ctx->hasExtension(QByteArrayLiteral("GL_ARB_compatibility"))) + || fmt.profile() == QSurfaceFormat::CompatibilityProfile) + { + // be nice to people who mix OpenGL 1.x code with QPainter commands + // by setting modelview and projection matrices to mirror the GL 1 + // paint engine + const QTransform& mtx = state()->matrix; -#ifndef QT_OPENGL_ES_2 - if (!d->ctx->contextHandle()->isOpenGLES()) { - const QGLContext *ctx = d->ctx; - const QGLFormat &fmt = d->device->format(); - if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 1) - || (fmt.majorVersion() == 3 && fmt.minorVersion() == 1 && ctx->contextHandle()->hasExtension(QByteArrayLiteral("GL_ARB_compatibility"))) - || fmt.profile() == QGLFormat::CompatibilityProfile) - { - // be nice to people who mix OpenGL 1.x code with QPainter commands - // by setting modelview and projection matrices to mirror the GL 1 - // paint engine - const QTransform& mtx = state()->matrix; - - float mv_matrix[4][4] = - { - { float(mtx.m11()), float(mtx.m12()), 0, float(mtx.m13()) }, - { float(mtx.m21()), float(mtx.m22()), 0, float(mtx.m23()) }, - { 0, 0, 1, 0 }, - { float(mtx.dx()), float(mtx.dy()), 0, float(mtx.m33()) } - }; - - const QSize sz = d->device->size(); - - QOpenGLFunctions_1_1 *gl1funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_1_1>(); - gl1funcs->initializeOpenGLFunctions(); - - gl1funcs->glMatrixMode(GL_PROJECTION); - gl1funcs->glLoadIdentity(); - gl1funcs->glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999); - - gl1funcs->glMatrixMode(GL_MODELVIEW); - gl1funcs->glLoadMatrixf(&mv_matrix[0][0]); - } + float mv_matrix[4][4] = + { + { float(mtx.m11()), float(mtx.m12()), 0, float(mtx.m13()) }, + { float(mtx.m21()), float(mtx.m22()), 0, float(mtx.m23()) }, + { 0, 0, 1, 0 }, + { float(mtx.dx()), float(mtx.dy()), 0, float(mtx.m33()) } + }; + + const QSize sz = d->device->size(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, sz.width(), sz.height(), 0, -999999, 999999); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(&mv_matrix[0][0]); } -#endif +#endif // QT_OPENGL_ES_2 + d->resetGLState(); + + // We don't know what texture units and textures the native painting + // will activate and bind, so we can't assume anything when we return + // from the native painting. + d->lastTextureUnitUsed = QT_UNKNOWN_TEXTURE_UNIT; d->lastTextureUsed = GLuint(-1); + d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); - d->resetGLState(); d->shaderManager->setDirty(); d->needsSync = true; } -void QGL2PaintEngineExPrivate::resetGLState() -{ - glDisable(GL_BLEND); - glActiveTexture(GL_TEXTURE0); - glDisable(GL_STENCIL_TEST); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glDepthMask(true); - glDepthFunc(GL_LESS); - glClearDepthf(1); - glStencilMask(0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_ALWAYS, 0, 0xff); - ctx->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false); - ctx->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false); - ctx->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); -#ifndef QT_OPENGL_ES_2 - if (!ctx->contextHandle()->isOpenGLES()) { +void QOpenGL2PaintEngineExPrivate::resetGLState() +{ + activateTextureUnit(QT_DEFAULT_TEXTURE_UNIT); + + funcs.glDisable(GL_BLEND); + funcs.glDisable(GL_STENCIL_TEST); + funcs.glDisable(GL_DEPTH_TEST); + funcs.glDisable(GL_SCISSOR_TEST); + funcs.glDepthMask(true); + funcs.glDepthFunc(GL_LESS); + funcs.glClearDepthf(1); + funcs.glStencilMask(0xff); + funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + funcs.glStencilFunc(GL_ALWAYS, 0, 0xff); + setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false); + setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, false); + setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false); + if (!QOpenGLContext::currentContext()->isOpenGLES()) { // gl_Color, corresponding to vertex attribute 3, may have been changed float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - glVertexAttrib4fv(3, color); + funcs.glVertexAttrib4fv(3, color); } -#endif -} - -bool QGL2PaintEngineExPrivate::resetOpenGLContextActiveEngine() -{ - QOpenGLContext *guiGlContext = ctx->contextHandle(); - QOpenGLContextPrivate *guiGlContextPrivate = - guiGlContext ? QOpenGLContextPrivate::get(guiGlContext) : 0; - - if (guiGlContextPrivate && guiGlContextPrivate->active_engine) { - ctx->d_func()->refreshCurrentFbo(); - guiGlContextPrivate->active_engine = 0; - return true; + if (vao.isCreated()) { + vao.release(); + funcs.glBindBuffer(GL_ARRAY_BUFFER, 0); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - - return false; } -void QGL2PaintEngineEx::endNativePainting() +void QOpenGL2PaintEngineEx::endNativePainting() { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); d->needsSync = true; d->nativePaintingActive = false; } -void QGL2PaintEngineEx::invalidateState() +void QOpenGL2PaintEngineEx::invalidateState() { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); d->needsSync = true; } -bool QGL2PaintEngineEx::isNativePaintingActive() const { - Q_D(const QGL2PaintEngineEx); +bool QOpenGL2PaintEngineEx::isNativePaintingActive() const { + Q_D(const QOpenGL2PaintEngineEx); return d->nativePaintingActive; } -bool QGL2PaintEngineEx::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &t) const -{ - // The paint engine does not support projected cached glyph drawing - if (t.type() == QTransform::TxProject) - return false; - - // The font engine might not support filling the glyph cache - // with the given transform applied, in which case we need to - // fall back to the QPainterPath code-path. - if (!fontEngine->supportsTransformation(t)) { - // Except that drawing paths is slow, so for scales between - // 0.5 and 2.0 we leave the glyph cache untransformed and deal - // with the transform ourselves when painting, resulting in - // drawing 1x cached glyphs with a smooth-scale. - float det = t.determinant(); - if (det >= 0.25f && det <= 4.f) { - // Assuming the baseclass still agrees - return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, t); - } - - return false; // Fall back to path-drawing - } - - return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, t); -} - -void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) +void QOpenGL2PaintEngineExPrivate::transferMode(EngineMode newMode) { if (newMode == mode) return; - if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) { - lastTextureUsed = GLuint(-1); - } - if (newMode == TextDrawingMode) { shaderManager->setHasComplexGeometry(true); } else { @@ -692,24 +788,26 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) } if (newMode == ImageDrawingMode) { - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray); + uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8); + uploadData(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray, 8); } - if (newMode == ImageArrayDrawingMode) { - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data()); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data()); - setVertexAttributePointer(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data()); + if (newMode == ImageArrayDrawingMode || newMode == ImageOpacityArrayDrawingMode) { + uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data(), vertexCoordinateArray.vertexCount() * 2); + uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data(), textureCoordinateArray.vertexCount() * 2); + + if (newMode == ImageOpacityArrayDrawingMode) + uploadData(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data(), opacityArray.size()); } // This needs to change when we implement high-quality anti-aliasing... if (newMode != TextDrawingMode) - shaderManager->setMaskType(QGLEngineShaderManager::NoMask); + shaderManager->setMaskType(QOpenGLEngineShaderManager::NoMask); mode = newMode; } -struct QGL2PEVectorPathCache +struct QOpenGL2PEVectorPathCache { #ifdef QT_OPENGL_CACHE_AS_VBOS GLuint vbo; @@ -725,12 +823,12 @@ struct QGL2PEVectorPathCache QVertexIndexVector::Type indexType; }; -void QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data) +void QOpenGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *data) { - QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data; + QOpenGL2PEVectorPathCache *c = (QOpenGL2PEVectorPathCache *) data; #ifdef QT_OPENGL_CACHE_AS_VBOS Q_ASSERT(engine->type() == QPaintEngine::OpenGL2); - static_cast<QGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo; + static_cast<QOpenGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo; if (c->ibo) d->unusedIBOSToClean << c->ibo; #else @@ -742,7 +840,7 @@ void QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *d } // Assumes everything is configured for the brush you want to use -void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) +void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path) { transferMode(BrushDrawingMode); @@ -755,40 +853,40 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) if (matrixDirty) updateMatrix(); + const bool supportsElementIndexUint = funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint); + const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); // Check to see if there's any hints if (path.shape() == QVectorPath::RectangleHint) { - QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y()); + QOpenGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y()); prepareForDraw(currentBrush.isOpaque()); composite(rect); } else if (path.isConvex()) { if (path.isCacheable()) { QVectorPath::CacheEntry *data = path.lookupCacheData(q); - QGL2PEVectorPathCache *cache; + QOpenGL2PEVectorPathCache *cache; bool updateCache = false; if (data) { - cache = (QGL2PEVectorPathCache *) data->data; - // Check if scale factor is exceeded for curved paths and generate curves if so... - if (path.isCurved()) { - qreal scaleFactor = cache->iscale / inverseScale; - if (scaleFactor < 0.5 || scaleFactor > 2.0) { + cache = (QOpenGL2PEVectorPathCache *) data->data; + // Check if scale factor is exceeded and regenerate if so... + qreal scaleFactor = cache->iscale / inverseScale; + if (scaleFactor < 0.5 || scaleFactor > 2.0) { #ifdef QT_OPENGL_CACHE_AS_VBOS - glDeleteBuffers(1, &cache->vbo); - cache->vbo = 0; - Q_ASSERT(cache->ibo == 0); + glDeleteBuffers(1, &cache->vbo); + cache->vbo = 0; + Q_ASSERT(cache->ibo == 0); #else - free(cache->vertices); - Q_ASSERT(cache->indices == 0); + free(cache->vertices); + Q_ASSERT(cache->indices == nullptr); #endif - updateCache = true; - } + updateCache = true; } } else { - cache = new QGL2PEVectorPathCache; + cache = new QOpenGL2PEVectorPathCache; data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath); updateCache = true; } @@ -804,25 +902,26 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) cache->primitiveType = GL_TRIANGLE_FAN; cache->iscale = inverseScale; #ifdef QT_OPENGL_CACHE_AS_VBOS - glGenBuffers(1, &cache->vbo); - glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); - glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW); + funcs.glGenBuffers(1, &cache->vbo); + funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + funcs.glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW); cache->ibo = 0; #else cache->vertices = (float *) malloc(floatSizeInBytes); memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes); - cache->indices = 0; + cache->indices = nullptr; #endif } prepareForDraw(currentBrush.isOpaque()); #ifdef QT_OPENGL_CACHE_AS_VBOS - glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + uploadData(QT_VERTEX_COORD_ATTR, 0, cache->vertexCount); setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); #else - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); + uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2); #endif - glDrawArrays(cache->primitiveType, 0, cache->vertexCount); + funcs.glDrawArrays(cache->primitiveType, 0, cache->vertexCount); } else { // printf(" - Marking path as cachable...\n"); @@ -847,55 +946,53 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) if (useCache) { QVectorPath::CacheEntry *data = path.lookupCacheData(q); - QGL2PEVectorPathCache *cache; + QOpenGL2PEVectorPathCache *cache; bool updateCache = false; if (data) { - cache = (QGL2PEVectorPathCache *) data->data; - // Check if scale factor is exceeded for curved paths and generate curves if so... - if (path.isCurved()) { - qreal scaleFactor = cache->iscale / inverseScale; - if (scaleFactor < 0.5 || scaleFactor > 2.0) { + cache = (QOpenGL2PEVectorPathCache *) data->data; + // Check if scale factor is exceeded and regenerate if so... + qreal scaleFactor = cache->iscale / inverseScale; + if (scaleFactor < 0.5 || scaleFactor > 2.0) { #ifdef QT_OPENGL_CACHE_AS_VBOS - glDeleteBuffers(1, &cache->vbo); - glDeleteBuffers(1, &cache->ibo); + glDeleteBuffers(1, &cache->vbo); + glDeleteBuffers(1, &cache->ibo); #else - free(cache->vertices); - free(cache->indices); + free(cache->vertices); + free(cache->indices); #endif - updateCache = true; - } + updateCache = true; } } else { - cache = new QGL2PEVectorPathCache; + cache = new QOpenGL2PEVectorPathCache; data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath); updateCache = true; } // Flatten the path at the current scale factor and fill it into the cache struct. if (updateCache) { - QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale)); + QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale), 1, supportsElementIndexUint); cache->vertexCount = polys.vertices.size() / 2; cache->indexCount = polys.indices.size(); cache->primitiveType = GL_TRIANGLES; cache->iscale = inverseScale; cache->indexType = polys.indices.type(); #ifdef QT_OPENGL_CACHE_AS_VBOS - glGenBuffers(1, &cache->vbo); - glGenBuffers(1, &cache->ibo); - glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + funcs.glGenBuffers(1, &cache->vbo); + funcs.glGenBuffers(1, &cache->ibo); + funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); if (polys.indices.type() == QVertexIndexVector::UnsignedInt) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); + funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); else - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint16) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); + funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint16) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); QVarLengthArray<float> vertices(polys.vertices.size()); for (int i = 0; i < polys.vertices.size(); ++i) vertices[i] = float(inverseScale * polys.vertices.at(i)); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW); + funcs.glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW); #else cache->vertices = (float *) malloc(sizeof(float) * polys.vertices.size()); if (polys.indices.type() == QVertexIndexVector::UnsignedInt) { @@ -912,21 +1009,21 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) prepareForDraw(currentBrush.isOpaque()); #ifdef QT_OPENGL_CACHE_AS_VBOS - glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + uploadData(QT_VERTEX_COORDS_ATTR, 0, cache->vertexCount); setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); if (cache->indexType == QVertexIndexVector::UnsignedInt) - glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0); + funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0); else - glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, 0); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + funcs.glBindBuffer(GL_ARRAY_BUFFER, 0); #else - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); - if (cache->indexType == QVertexIndexVector::UnsignedInt) - glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, (qint32 *)cache->indices); - else - glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, (qint16 *)cache->indices); + uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2); + const GLenum indexValueType = cache->indexType == QVertexIndexVector::UnsignedInt ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + const bool useIndexVbo = uploadIndexData(cache->indices, indexValueType, cache->indexCount); + funcs.glDrawElements(cache->primitiveType, cache->indexCount, indexValueType, useIndexVbo ? nullptr : cache->indices); #endif } else { @@ -934,7 +1031,7 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable path.makeCacheable(); - if (!device->format().stencil()) { + if (device->context()->format().stencilBufferSize() <= 0) { // If there is no stencil buffer, triangulate the path instead. QRectF bbox = path.controlPointRect(); @@ -944,18 +1041,17 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) && (bbox.top() > -0x8000 * inverseScale) && (bbox.bottom() < 0x8000 * inverseScale); if (withinLimits) { - QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale)); + QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale), 1, supportsElementIndexUint); QVarLengthArray<float> vertices(polys.vertices.size()); for (int i = 0; i < polys.vertices.size(); ++i) vertices[i] = float(inverseScale * polys.vertices.at(i)); prepareForDraw(currentBrush.isOpaque()); - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, vertices.constData()); - if (polys.indices.type() == QVertexIndexVector::UnsignedInt) - glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_INT, polys.indices.data()); - else - glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_SHORT, polys.indices.data()); + uploadData(QT_VERTEX_COORDS_ATTR, vertices.constData(), vertices.size()); + const GLenum indexValueType = funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + const bool useIndexVbo = uploadIndexData(polys.indices.data(), indexValueType, polys.indices.size()); + funcs.glDrawElements(GL_TRIANGLES, polys.indices.size(), indexValueType, useIndexVbo ? nullptr : polys.indices.data()); } else { // We can't handle big, concave painter paths with OpenGL without stencil buffer. qWarning("Painter path exceeds +/-32767 pixels."); @@ -969,50 +1065,50 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); - glStencilMask(0xff); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + funcs.glStencilMask(0xff); + funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); if (q->state()->clipTestEnabled) { // Pass when high bit is set, replace stencil value with current clip - glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT); } else if (path.hasWindingFill()) { // Pass when any bit is set, replace stencil value with 0 - glStencilFunc(GL_NOTEQUAL, 0, 0xff); + funcs.glStencilFunc(GL_NOTEQUAL, 0, 0xff); } else { // Pass when high bit is set, replace stencil value with 0 - glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); } prepareForDraw(currentBrush.isOpaque()); // Stencil the brush onto the dest buffer composite(vertexCoordinateArray.boundingRect()); - glStencilMask(0); + funcs.glStencilMask(0); updateClipScissorTest(); } } } -void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, +void QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, int count, int *stops, int stopCount, - const QGLRect &bounds, + const QOpenGLRect &bounds, StencilFillMode mode) { Q_ASSERT(count || stops); -// qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()"); - glStencilMask(0xff); // Enable stencil writes +// qDebug("QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray()"); + funcs.glStencilMask(0xff); // Enable stencil writes if (dirtyStencilRegion.intersects(currentScissorBounds)) { const QRegion clearRegion = dirtyStencilRegion.intersected(currentScissorBounds); - glClearStencil(0); // Clear to zero + funcs.glClearStencil(0); // Clear to zero for (const QRect &rect : clearRegion) { #ifndef QT_GL_NO_SCISSOR_TEST setScissor(rect); #endif - glClear(GL_STENCIL_BUFFER_BIT); + funcs.glClear(GL_STENCIL_BUFFER_BIT); } dirtyStencilRegion -= currentScissorBounds; @@ -1022,68 +1118,69 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, #endif } - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes + funcs.glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes useSimpleShader(); - glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d + funcs.glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d if (mode == WindingFillMode) { Q_ASSERT(stops && !count); if (q->state()->clipTestEnabled) { // Flatten clip values higher than current clip, and set high bit to match current clip - glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + funcs.glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); composite(bounds); - glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT); } else if (!stencilClean) { // Clear stencil buffer within bounding rect - glStencilFunc(GL_ALWAYS, 0, 0xff); - glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); + funcs.glStencilFunc(GL_ALWAYS, 0, 0xff); + funcs.glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); composite(bounds); } // Inc. for front-facing triangle - glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP); + funcs.glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP); // Dec. for back-facing "holes" - glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP); - glStencilMask(~GL_STENCIL_HIGH_BIT); + funcs.glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP); + funcs.glStencilMask(~GL_STENCIL_HIGH_BIT); drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN); if (q->state()->clipTestEnabled) { // Clear high bit of stencil outside of path - glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - glStencilMask(GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + funcs.glStencilMask(GL_STENCIL_HIGH_BIT); composite(bounds); } } else if (mode == OddEvenFillMode) { - glStencilMask(GL_STENCIL_HIGH_BIT); - glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit + funcs.glStencilMask(GL_STENCIL_HIGH_BIT); + funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit drawVertexArrays(data, stops, stopCount, GL_TRIANGLE_FAN); } else { // TriStripStrokeFillMode Q_ASSERT(count && !stops); // tristrips generated directly, so no vertexArray or stops - glStencilMask(GL_STENCIL_HIGH_BIT); + funcs.glStencilMask(GL_STENCIL_HIGH_BIT); #if 0 - glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit + funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); - glDrawArrays(GL_TRIANGLE_STRIP, 0, count); + funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count); #else - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + funcs.glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); if (q->state()->clipTestEnabled) { - glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT, - ~GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_LEQUAL, q->state()->currentClip | GL_STENCIL_HIGH_BIT, + ~GL_STENCIL_HIGH_BIT); } else { - glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff); + funcs.glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff); } - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); - glDrawArrays(GL_TRIANGLE_STRIP, 0, count); + + uploadData(QT_VERTEX_COORDS_ATTR, data, count * 2); + funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count); #endif } // Enable color writes & disable stencil writes - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + funcs.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } /* @@ -1091,30 +1188,30 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, restore the stencil buffer to a pristine state. The current clip region is set to 1, and the rest to 0. */ -void QGL2PaintEngineExPrivate::resetClipIfNeeded() +void QOpenGL2PaintEngineExPrivate::resetClipIfNeeded() { if (maxClip != (GL_STENCIL_HIGH_BIT - 1)) return; - Q_Q(QGL2PaintEngineEx); + Q_Q(QOpenGL2PaintEngineEx); useSimpleShader(); - glEnable(GL_STENCIL_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + funcs.glEnable(GL_STENCIL_TEST); + funcs.glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height)); - QGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); + QOpenGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); // Set high bit on clip region - glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff); - glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); - glStencilMask(GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff); + funcs.glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + funcs.glStencilMask(GL_STENCIL_HIGH_BIT); composite(rect); // Reset clipping to 1 and everything else to zero - glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT); - glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE); - glStencilMask(0xff); + funcs.glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT); + funcs.glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE); + funcs.glStencilMask(0xff); composite(rect); q->state()->currentClip = 1; @@ -1122,13 +1219,13 @@ void QGL2PaintEngineExPrivate::resetClipIfNeeded() maxClip = 1; - glStencilMask(0x0); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + funcs.glStencilMask(0x0); + funcs.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } -bool QGL2PaintEngineExPrivate::prepareForCachedGlyphDraw(const QFontEngineGlyphCache &cache) +bool QOpenGL2PaintEngineExPrivate::prepareForCachedGlyphDraw(const QFontEngineGlyphCache &cache) { - Q_Q(QGL2PaintEngineEx); + Q_Q(QOpenGL2PaintEngineEx); Q_ASSERT(cache.transform().type() <= QTransform::TxScale); @@ -1140,9 +1237,9 @@ bool QGL2PaintEngineExPrivate::prepareForCachedGlyphDraw(const QFontEngineGlyphC return ret; } -bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) +bool QOpenGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) { - if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) + if (brushTextureDirty && (mode == TextDrawingMode || mode == BrushDrawingMode)) updateBrushTexture(); if (compositionModeDirty) @@ -1156,24 +1253,24 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) || (q->state()->composition_mode == QPainter::CompositionMode_SourceOver && srcPixelsAreOpaque && !stateHasOpacity)) { - glDisable(GL_BLEND); + funcs.glDisable(GL_BLEND); } else { - glEnable(GL_BLEND); + funcs.glEnable(GL_BLEND); } - QGLEngineShaderManager::OpacityMode opacityMode; - if (mode == ImageArrayDrawingMode) { - opacityMode = QGLEngineShaderManager::AttributeOpacity; + QOpenGLEngineShaderManager::OpacityMode opacityMode; + if (mode == ImageOpacityArrayDrawingMode) { + opacityMode = QOpenGLEngineShaderManager::AttributeOpacity; } else { - opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity - : QGLEngineShaderManager::NoOpacity; - if (stateHasOpacity && (mode != ImageDrawingMode)) { + opacityMode = stateHasOpacity ? QOpenGLEngineShaderManager::UniformOpacity + : QOpenGLEngineShaderManager::NoOpacity; + if (stateHasOpacity && (mode != ImageDrawingMode && mode != ImageArrayDrawingMode)) { // Using a brush bool brushIsPattern = (currentBrush.style() >= Qt::Dense1Pattern) && (currentBrush.style() <= Qt::DiagCrossPattern); if ((currentBrush.style() == Qt::SolidPattern) || brushIsPattern) - opacityMode = QGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader + opacityMode = QOpenGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader } } shaderManager->setOpacityMode(opacityMode); @@ -1185,73 +1282,64 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) brushUniformsDirty = true; opacityUniformDirty = true; matrixUniformDirty = true; - translateZUniformDirty = true; } - if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) + if (brushUniformsDirty && (mode == TextDrawingMode || mode == BrushDrawingMode)) updateBrushUniforms(); - if (opacityMode == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) { - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity); + if (opacityMode == QOpenGLEngineShaderManager::UniformOpacity && opacityUniformDirty) { + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity); opacityUniformDirty = false; } if (matrixUniformDirty && shaderManager->hasComplexGeometry()) { - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Matrix), + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::Matrix), pmvMatrix); matrixUniformDirty = false; } - if (translateZUniformDirty && shaderManager->hasComplexGeometry()) { - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::TranslateZ), - translateZ); - translateZUniformDirty = false; - } - return changed; } -void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect) +void QOpenGL2PaintEngineExPrivate::composite(const QOpenGLRect& boundingRect) { setCoords(staticVertexCoordinateArray, boundingRect); - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8); + funcs.glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } // Draws the vertex array as a set of <vertexArrayStops.size()> triangle fans. -void QGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount, +void QOpenGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stops, int stopCount, GLenum primitive) { // Now setup the pointer to the vertex array: - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); + uploadData(QT_VERTEX_COORDS_ATTR, data, stops[stopCount-1] * 2); int previousStop = 0; for (int i=0; i<stopCount; ++i) { int stop = stops[i]; -/* - qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1); - for (int i=previousStop; i<stop; ++i) - qDebug(" %02d: [%.2f, %.2f]", i, vertexArray.data()[i].x, vertexArray.data()[i].y); -*/ - glDrawArrays(primitive, previousStop, stop - previousStop); + + funcs.glDrawArrays(primitive, previousStop, stop - previousStop); previousStop = stop; } } /////////////////////////////////// Public Methods ////////////////////////////////////////// -QGL2PaintEngineEx::QGL2PaintEngineEx() - : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this))) +QOpenGL2PaintEngineEx::QOpenGL2PaintEngineEx() + : QPaintEngineEx(*(new QOpenGL2PaintEngineExPrivate(this))) { + gccaps &= ~QPaintEngine::RasterOpModes; } -QGL2PaintEngineEx::~QGL2PaintEngineEx() +QOpenGL2PaintEngineEx::~QOpenGL2PaintEngineEx() { } -void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) +void QOpenGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); if (qbrush_style(brush) == Qt::NoBrush) return; @@ -1260,19 +1348,19 @@ void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) d->fill(path); } -Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp +Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp -void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) +void QOpenGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); const QBrush &penBrush = qpen_brush(pen); if (qpen_style(pen) == Qt::NoPen || qbrush_style(penBrush) == Qt::NoBrush) return; - QGL2PaintEngineState *s = state(); - if (qt_pen_is_cosmetic(pen, s->renderHints) && !qt_scaleForTransform(s->transform(), 0)) { + QOpenGL2PaintEngineState *s = state(); + if (qt_pen_is_cosmetic(pen, state()->renderHints) && !qt_scaleForTransform(s->transform(), nullptr)) { // QTriangulatingStroker class is not meant to support cosmetically sheared strokes. QPaintEngineEx::stroke(path, pen); return; @@ -1283,9 +1371,9 @@ void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) d->stroke(path, pen); } -void QGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen) +void QOpenGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen) { - const QGL2PaintEngineState *s = q->state(); + const QOpenGL2PaintEngineState *s = q->state(); if (snapToPixelGrid) { snapToPixelGrid = false; matrixDirty = true; @@ -1323,14 +1411,9 @@ void QGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen) if (opaque) { prepareForDraw(opaque); - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, stroker.vertices()); - glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2); - -// QBrush b(Qt::green); -// d->setBrush(&b); -// d->prepareForDraw(true); -// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2); + uploadData(QT_VERTEX_COORDS_ATTR, stroker.vertices(), stroker.vertexCount()); + funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2); } else { qreal width = qpen_widthf(pen) / 2; if (width == 0) @@ -1339,37 +1422,37 @@ void QGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &pen) ? qMax(pen.miterLimit() * width, width) : width; - if (qt_pen_is_cosmetic(pen, s->renderHints)) + if (qt_pen_is_cosmetic(pen, q->state()->renderHints)) extra = extra * inverseScale; QRectF bounds = path.controlPointRect().adjusted(-extra, -extra, extra, extra); fillStencilWithVertexArray(stroker.vertices(), stroker.vertexCount() / 2, - 0, 0, bounds, QGL2PaintEngineExPrivate::TriStripStrokeFillMode); + nullptr, 0, bounds, QOpenGL2PaintEngineExPrivate::TriStripStrokeFillMode); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); // Pass when any bit is set, replace stencil value with 0 - glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); prepareForDraw(false); // Stencil the brush onto the dest buffer composite(bounds); - glStencilMask(0); + funcs.glStencilMask(0); updateClipScissorTest(); } } -void QGL2PaintEngineEx::penChanged() { } -void QGL2PaintEngineEx::brushChanged() { } -void QGL2PaintEngineEx::brushOriginChanged() { } +void QOpenGL2PaintEngineEx::penChanged() { } +void QOpenGL2PaintEngineEx::brushChanged() { } +void QOpenGL2PaintEngineEx::brushOriginChanged() { } -void QGL2PaintEngineEx::opacityChanged() +void QOpenGL2PaintEngineEx::opacityChanged() { -// qDebug("QGL2PaintEngineEx::opacityChanged()"); - Q_D(QGL2PaintEngineEx); +// qDebug("QOpenGL2PaintEngineEx::opacityChanged()"); + Q_D(QOpenGL2PaintEngineEx); state()->opacityChanged = true; Q_ASSERT(d->shaderManager); @@ -1377,43 +1460,49 @@ void QGL2PaintEngineEx::opacityChanged() d->opacityUniformDirty = true; } -void QGL2PaintEngineEx::compositionModeChanged() +void QOpenGL2PaintEngineEx::compositionModeChanged() { -// qDebug("QGL2PaintEngineEx::compositionModeChanged()"); - Q_D(QGL2PaintEngineEx); +// qDebug("QOpenGL2PaintEngineEx::compositionModeChanged()"); + Q_D(QOpenGL2PaintEngineEx); state()->compositionModeChanged = true; d->compositionModeDirty = true; } -void QGL2PaintEngineEx::renderHintsChanged() +void QOpenGL2PaintEngineEx::renderHintsChanged() { - Q_D(QGL2PaintEngineEx); state()->renderHintsChanged = true; -#if !defined(QT_OPENGL_ES_2) +#ifndef QT_OPENGL_ES_2 + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGL2PaintEngineEx); QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED - if (!d->ctx->contextHandle()->isOpenGLES()) { if ((state()->renderHints & QPainter::Antialiasing) #if QT_DEPRECATED_SINCE(5, 14) || (state()->renderHints & QPainter::HighQualityAntialiasing) #endif ) - d->glEnable(GL_MULTISAMPLE); + d->funcs.glEnable(GL_MULTISAMPLE); else - d->glDisable(GL_MULTISAMPLE); - } + d->funcs.glDisable(GL_MULTISAMPLE); QT_WARNING_POP -#endif + } +#endif // QT_OPENGL_ES_2 + + Q_D(QOpenGL2PaintEngineEx); + // This is a somewhat sneaky way of conceptually making the next call to + // updateTexture() use FoceUpdate for the TextureUpdateMode. We need this + // as new render hints may require updating the filter mode. d->lastTextureUsed = GLuint(-1); + d->brushTextureDirty = true; -// qDebug("QGL2PaintEngineEx::renderHintsChanged() not implemented!"); +// qDebug("QOpenGL2PaintEngineEx::renderHintsChanged() not implemented!"); } -void QGL2PaintEngineEx::transformChanged() +void QOpenGL2PaintEngineEx::transformChanged() { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); d->matrixDirty = true; state()->matrixChanged = true; } @@ -1424,10 +1513,15 @@ static const QRectF scaleRect(const QRectF &r, qreal sx, qreal sy) return QRectF(r.x() * sx, r.y() * sy, r.width() * sx, r.height() * sy); } -void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src) +void QOpenGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src) { - Q_D(QGL2PaintEngineEx); - QGLContext *ctx = d->ctx; + Q_D(QOpenGL2PaintEngineEx); + QOpenGLContext *ctx = d->ctx; + + // Draw pixmaps that are really images as images since drawImage has + // better handling of non-default image formats. + if (pixmap.paintEngine()->type() == QPaintEngine::Raster && !pixmap.isQBitmap()) + return drawImage(dest, pixmap.toImage(), src); int max_texture_size = ctx->d_func()->maxTextureSize(); if (pixmap.width() > max_texture_size || pixmap.height() > max_texture_size) { @@ -1443,38 +1537,23 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c ensureActive(); d->transferMode(ImageDrawingMode); - QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption|QGLContext::CanFlipNativePixmapBindOption; -#ifdef QGL_USE_TEXTURE_POOL - bindOptions |= QGLContext::TemporarilyCachedBindOption; -#endif - - d->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - QGLTexture *texture = - ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, bindOptions); - - GLfloat top = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.top()) : src.top(); - GLfloat bottom = texture->options & QGLContext::InvertedYBindOption ? (pixmap.height() - src.bottom()) : src.bottom(); - QGLRect srcRect(src.left(), top, src.right(), bottom); + GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST; + d->updateTexture(QT_IMAGE_TEXTURE_UNIT, pixmap, GL_CLAMP_TO_EDGE, filterMode); bool isBitmap = pixmap.isQBitmap(); bool isOpaque = !isBitmap && !pixmap.hasAlpha(); - d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, - state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); - d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap); + d->shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc : QOpenGLEngineShaderManager::ImageSrc); - if (texture->options&QGLContext::TemporarilyCachedBindOption) { - // pixmap was temporarily cached as a QImage texture by pooling system - // and should be destroyed immediately - QGLTextureCache::instance()->remove(ctx, texture->id); - } + QOpenGLRect srcRect(src.left(), src.top(), src.right(), src.bottom()); + d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap); } -void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src, +void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src, Qt::ImageConversionFlags) { - Q_D(QGL2PaintEngineEx); - QGLContext *ctx = d->ctx; + Q_D(QOpenGL2PaintEngineEx); + QOpenGLContext *ctx = d->ctx; int max_texture_size = ctx->d_func()->maxTextureSize(); if (image.width() > max_texture_size || image.height() > max_texture_size) { @@ -1490,45 +1569,56 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const ensureActive(); d->transferMode(ImageDrawingMode); - d->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - - QGLContext::BindOptions bindOptions = QGLContext::InternalBindOption; -#ifdef QGL_USE_TEXTURE_POOL - bindOptions |= QGLContext::TemporarilyCachedBindOption; -#endif + QOpenGLTextureUploader::BindOptions bindOption = QOpenGLTextureUploader::PremultipliedAlphaBindOption; + // Use specialized bind for formats we have specialized shaders for. + switch (image.format()) { + case QImage::Format_RGBA8888: + case QImage::Format_ARGB32: + case QImage::Format_RGBA64: + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc); + bindOption = { }; + break; + case QImage::Format_Alpha8: + if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) { + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::AlphaImageSrc); + bindOption = QOpenGLTextureUploader::UseRedForAlphaAndLuminanceBindOption; + } else + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); + break; + case QImage::Format_Grayscale8: + case QImage::Format_Grayscale16: + if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats)) { + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::GrayscaleImageSrc); + bindOption = QOpenGLTextureUploader::UseRedForAlphaAndLuminanceBindOption; + } else + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); + break; + default: + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); + break; + } - QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, bindOptions); - GLuint id = texture->id; + ImageWithBindOptions imageWithOptions = { image, bindOption }; + GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST; + d->updateTexture(QT_IMAGE_TEXTURE_UNIT, imageWithOptions, GL_CLAMP_TO_EDGE, filterMode); - d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, - state()->renderHints & QPainter::SmoothPixmapTransform, id); d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel()); - - if (texture->options&QGLContext::TemporarilyCachedBindOption) { - // image was temporarily cached by texture pooling system - // and should be destroyed immediately - QGLTextureCache::instance()->remove(ctx, texture->id); - } } -void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem) +void QOpenGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); ensureActive(); QPainterState *s = state(); - // don't try to cache huge fonts or vastly transformed fonts QFontEngine *fontEngine = textItem->fontEngine(); if (shouldDrawCachedGlyphs(fontEngine, s->matrix)) { - QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat; - if (glyphFormat == QFontEngine::Format_A32) { - if (!QGLFramebufferObject::hasOpenGLFramebufferObjects() - || d->device->alphaRequested() || s->matrix.type() > QTransform::TxTranslate + if (d->device->context()->format().alphaBufferSize() > 0 || s->matrix.type() > QTransform::TxTranslate || (s->composition_mode != QPainter::CompositionMode_Source && s->composition_mode != QPainter::CompositionMode_SourceOver)) { @@ -1542,32 +1632,32 @@ void QGL2PaintEngineEx::drawStaticTextItem(QStaticTextItem *textItem) } } -bool QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src) +bool QOpenGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); if (!d->shaderManager) return false; ensureActive(); d->transferMode(ImageDrawingMode); - d->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - d->glBindTexture(GL_TEXTURE_2D, textureId); + GLenum filterMode = state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST; + d->updateTexture(QT_IMAGE_TEXTURE_UNIT, textureId, GL_CLAMP_TO_EDGE, filterMode); - QGLRect srcRect(src.left(), src.bottom(), src.right(), src.top()); + d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); - d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, - state()->renderHints & QPainter::SmoothPixmapTransform, textureId); + QOpenGLRect srcRect(src.left(), src.bottom(), src.right(), src.top()); d->drawTexture(dest, srcRect, size, false); + return true; } -void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem) +void QOpenGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); ensureActive(); - QGL2PaintEngineState *s = state(); + QOpenGL2PaintEngineState *s = state(); const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); @@ -1577,8 +1667,7 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem ? ti.fontEngine->glyphFormat : d->glyphCacheFormat; if (glyphFormat == QFontEngine::Format_A32) { - if (!QGLFramebufferObject::hasOpenGLFramebufferObjects() - || d->device->alphaRequested() || txtype > QTransform::TxTranslate + if (d->device->context()->format().alphaBufferSize() > 0 || txtype > QTransform::TxTranslate || (state()->composition_mode != QPainter::CompositionMode_Source && state()->composition_mode != QPainter::CompositionMode_SourceOver)) { @@ -1622,8 +1711,8 @@ namespace { } QSize cacheSize; - QGL2PEXVertexArray vertexCoordinateArray; - QGL2PEXVertexArray textureCoordinateArray; + QOpenGL2PEXVertexArray vertexCoordinateArray; + QOpenGL2PEXVertexArray textureCoordinateArray; QFontEngine::GlyphFormat glyphFormat; int cacheSerialNumber; }; @@ -1633,14 +1722,40 @@ namespace { // #define QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO -void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFormat, +bool QOpenGL2PaintEngineEx::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &t) const +{ + // The paint engine does not support projected cached glyph drawing + if (t.type() == QTransform::TxProject) + return false; + + // The font engine might not support filling the glyph cache + // with the given transform applied, in which case we need to + // fall back to the QPainterPath code-path. + if (!fontEngine->supportsTransformation(t)) { + // Except that drawing paths is slow, so for scales between + // 0.5 and 2.0 we leave the glyph cache untransformed and deal + // with the transform ourselves when painting, resulting in + // drawing 1x cached glyphs with a smooth-scale. + float det = t.determinant(); + if (det >= 0.25f && det <= 4.f) { + // Assuming the baseclass still agrees + return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, t); + } + + return false; // Fall back to path-drawing + } + + return QPaintEngineEx::shouldDrawCachedGlyphs(fontEngine, t); +} + +void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFormat, QStaticTextItem *staticTextItem) { - Q_Q(QGL2PaintEngineEx); + Q_Q(QOpenGL2PaintEngineEx); - QGL2PaintEngineState *s = q->state(); + QOpenGL2PaintEngineState *s = q->state(); - void *cacheKey = const_cast<QGLContext *>(QGLContextPrivate::contextGroup(ctx)->context()); + void *cacheKey = ctx; // use context, not the shareGroup() -> the GL glyph cache uses FBOs which may not be shareable bool recreateVertexArrays = false; QTransform glyphCacheTransform; @@ -1656,17 +1771,17 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo QVector2D(s->matrix.m21(), s->matrix.m22()).length()); } - QGLTextureGlyphCache *cache = - (QGLTextureGlyphCache *) fe->glyphCache(cacheKey, glyphFormat, glyphCacheTransform); - if (!cache || cache->glyphFormat() != glyphFormat || cache->contextGroup() == 0) { - cache = new QGLTextureGlyphCache(glyphFormat, glyphCacheTransform); + QOpenGLTextureGlyphCache *cache = + (QOpenGLTextureGlyphCache *) fe->glyphCache(cacheKey, glyphFormat, glyphCacheTransform); + if (!cache || cache->glyphFormat() != glyphFormat || cache->contextGroup() == nullptr) { + cache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform); fe->setGlyphCache(cacheKey, cache); recreateVertexArrays = true; } if (staticTextItem->userDataNeedsUpdate) { recreateVertexArrays = true; - } else if (staticTextItem->userData() == 0) { + } else if (staticTextItem->userData() == nullptr) { recreateVertexArrays = true; } else if (staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) { recreateVertexArrays = true; @@ -1691,13 +1806,35 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo cache->populate(fe, staticTextItem->numGlyphs, staticTextItem->glyphs, staticTextItem->glyphPositions); } - cache->fillInPendingGlyphs(); + + if (cache->hasPendingGlyphs()) { + // Filling in the glyphs binds and sets parameters, so we need to + // ensure that the glyph cache doesn't mess with whatever unit + // is currently active. Note that the glyph cache internally + // uses the image texture unit for blitting to the cache, while + // we switch between image and mask units when drawing. + static const GLenum glypchCacheTextureUnit = QT_IMAGE_TEXTURE_UNIT; + activateTextureUnit(glypchCacheTextureUnit); + + cache->fillInPendingGlyphs(); + + // We assume the cache can be trusted on which texture was bound + lastTextureUsed = cache->texture(); + + // But since the brush and image texture units are possibly shared + // we may have to re-bind brush textures after filling in the cache. + brushTextureDirty = (QT_BRUSH_TEXTURE_UNIT == glypchCacheTextureUnit); + } + cache->setPaintEnginePrivate(nullptr); } if (cache->width() == 0 || cache->height() == 0) return; - transferMode(TextDrawingMode); + if (glyphFormat == QFontEngine::Format_ARGB) + transferMode(ImageArrayDrawingMode); + else + transferMode(TextDrawingMode); int margin = fe->glyphMargin(glyphFormat); @@ -1705,13 +1842,13 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo GLfloat dy = 1.0 / cache->height(); // Use global arrays by default - QGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray; - QGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray; + QOpenGL2PEXVertexArray *vertexCoordinates = &vertexCoordinateArray; + QOpenGL2PEXVertexArray *textureCoordinates = &textureCoordinateArray; if (staticTextItem->useBackendOptimizations) { - QOpenGLStaticTextUserData *userData = 0; + QOpenGLStaticTextUserData *userData = nullptr; - if (staticTextItem->userData() == 0 + if (staticTextItem->userData() == nullptr || staticTextItem->userData()->type != QStaticTextUserData::OpenGLUserData) { userData = new QOpenGLStaticTextUserData(); @@ -1781,20 +1918,22 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) if (elementIndicesVBOId == 0) - glGenBuffers(1, &elementIndicesVBOId); + funcs.glGenBuffers(1, &elementIndicesVBOId); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort), - elementIndices.constData(), GL_STATIC_DRAW); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId); + funcs.glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementIndices.size() * sizeof(GLushort), + elementIndices.constData(), GL_STATIC_DRAW); #endif } else { #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementIndicesVBOId); #endif } - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data()); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data()); + if (glyphFormat != QFontEngine::Format_ARGB || recreateVertexArrays) { + uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data(), vertexCoordinates->vertexCount() * 2); + uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data(), textureCoordinates->vertexCount() * 2); + } if (!snapToPixelGrid) { snapToPixelGrid = true; @@ -1812,7 +1951,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo Q_ASSERT(compMode == QPainter::CompositionMode_Source || compMode == QPainter::CompositionMode_SourceOver); - shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass1); + shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass1); if (pensBrush.style() == Qt::SolidPattern) { // Solid patterns can get away with only one pass. @@ -1833,9 +1972,9 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo opacityUniformDirty = true; } - glEnable(GL_BLEND); - glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); - glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + funcs.glEnable(GL_BLEND); + funcs.glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR); + funcs.glBlendColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()); } else { // Other brush styles need two passes. @@ -1849,20 +1988,19 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo compositionModeDirty = false; // I can handle this myself, thank you very much prepareForCachedGlyphDraw(*cache); - glEnable(GL_BLEND); - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + funcs.glEnable(GL_BLEND); + funcs.glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT); - glBindTexture(GL_TEXTURE_2D, cache->texture()); - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); + updateTexture(QT_MASK_TEXTURE_UNIT, cache->texture(), GL_REPEAT, GL_NEAREST, ForceUpdate); #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) - glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); + funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); #else - glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data()); + const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs); + funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ? nullptr : elementIndices.data()); #endif - shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMaskPass2); + shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass2); if (compMode == QPainter::CompositionMode_Source) { q->state()->opacity = oldOpacity; @@ -1873,50 +2011,52 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat glyphFo compositionModeDirty = false; prepareForCachedGlyphDraw(*cache); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); + funcs.glEnable(GL_BLEND); + funcs.glBlendFunc(GL_ONE, GL_ONE); } compositionModeDirty = true; + } else if (glyphFormat == QFontEngine::Format_ARGB) { + currentBrush = noBrush; + shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::ImageSrc); + if (prepareForCachedGlyphDraw(*cache)) + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); } else { // Grayscale/mono glyphs - shaderManager->setMaskType(QGLEngineShaderManager::PixelMask); + shaderManager->setMaskType(QOpenGLEngineShaderManager::PixelMask); prepareForCachedGlyphDraw(*cache); } - QGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate)?QGLTextureGlyphCache::Linear:QGLTextureGlyphCache::Nearest; - if (lastMaskTextureUsed != cache->texture() || cache->filterMode() != filterMode) { + GLenum textureUnit = QT_MASK_TEXTURE_UNIT; + if (glyphFormat == QFontEngine::Format_ARGB) + textureUnit = QT_IMAGE_TEXTURE_UNIT; - glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT); - if (lastMaskTextureUsed != cache->texture()) { - glBindTexture(GL_TEXTURE_2D, cache->texture()); - lastMaskTextureUsed = cache->texture(); - } + QOpenGLTextureGlyphCache::FilterMode filterMode = (s->matrix.type() > QTransform::TxTranslate) ? + QOpenGLTextureGlyphCache::Linear : QOpenGLTextureGlyphCache::Nearest; - if (cache->filterMode() != filterMode) { - if (filterMode == QGLTextureGlyphCache::Linear) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - cache->setFilterMode(filterMode); - } + GLenum glFilterMode = filterMode == QOpenGLTextureGlyphCache::Linear ? GL_LINEAR : GL_NEAREST; + + TextureUpdateMode updateMode = UpdateIfNeeded; + if (cache->filterMode() != filterMode) { + updateMode = ForceUpdate; + cache->setFilterMode(filterMode); } + updateTexture(textureUnit, cache->texture(), GL_REPEAT, glFilterMode, updateMode); + #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) - glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #else - glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data()); + const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs); + funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ? nullptr : elementIndices.data()); #endif } -void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, +void QOpenGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, QPainter::PixmapFragmentHints hints) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); // Use fallback for extended composition modes. if (state()->composition_mode > QPainter::CompositionMode_Plus) { QPaintEngineEx::drawPixmapFragments(fragments, fragmentCount, pixmap, hints); @@ -1934,7 +2074,7 @@ void QGL2PaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *frag } -void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments, +void QOpenGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, QPainter::PixmapFragmentHints hints) { @@ -1962,8 +2102,8 @@ void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragmen qreal right = 0.5 * fragments[i].scaleX * fragments[i].width; qreal bottom = 0.5 * fragments[i].scaleY * fragments[i].height; - QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c); - QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c); + QOpenGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c); + QOpenGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c); vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y); vertexCoordinateArray.addVertex(-bottomLeft.x + fragments[i].x, -bottomLeft.y + fragments[i].y); @@ -1972,7 +2112,7 @@ void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragmen vertexCoordinateArray.addVertex(bottomLeft.x + fragments[i].x, bottomLeft.y + fragments[i].y); vertexCoordinateArray.addVertex(bottomRight.x + fragments[i].x, bottomRight.y + fragments[i].y); - QGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy, + QOpenGLRect src(fragments[i].sourceLeft * dx, fragments[i].sourceTop * dy, (fragments[i].sourceLeft + fragments[i].width) * dx, (fragments[i].sourceTop + fragments[i].height) * dy); @@ -1988,58 +2128,100 @@ void QGL2PaintEngineExPrivate::drawPixmapFragments(const QPainter::PixmapFragmen allOpaque &= (opacity >= 0.99f); } - glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); - QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, - QGLContext::InternalBindOption - | QGLContext::CanFlipNativePixmapBindOption); - - if (texture->options & QGLContext::InvertedYBindOption) { - // Flip texture y-coordinate. - QGLPoint *data = textureCoordinateArray.data(); - for (int i = 0; i < 6 * fragmentCount; ++i) - data[i].y = 1 - data[i].y; - } + transferMode(ImageOpacityArrayDrawingMode); - transferMode(ImageArrayDrawingMode); + GLenum filterMode = q->state()->renderHints & QPainter::SmoothPixmapTransform ? GL_LINEAR : GL_NEAREST; + updateTexture(QT_IMAGE_TEXTURE_UNIT, pixmap, GL_CLAMP_TO_EDGE, filterMode); bool isBitmap = pixmap.isQBitmap(); bool isOpaque = !isBitmap && (!pixmap.hasAlpha() || (hints & QPainter::OpaqueHint)) && allOpaque; - updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, - q->state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); - // Setup for texture drawing currentBrush = noBrush; - shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc - : QGLEngineShaderManager::ImageSrc); + shaderManager->setSrcPixelType(isBitmap ? QOpenGLEngineShaderManager::PatternSrc + : QOpenGLEngineShaderManager::ImageSrc); if (prepareForDraw(isOpaque)) - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); if (isBitmap) { QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); + shaderManager->currentProgram()->setUniformValue(location(QOpenGLEngineShaderManager::PatternColor), col); } - glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount); + funcs.glDrawArrays(GL_TRIANGLES, 0, 6 * fragmentCount); } -bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) +bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev) { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); -// qDebug("QGL2PaintEngineEx::begin()"); - if (pdev->devType() == QInternal::OpenGL) - d->device = static_cast<QGLPaintDevice*>(pdev); - else - d->device = QGLPaintDevice::getDevice(pdev); + Q_ASSERT(pdev->devType() == QInternal::OpenGL); + d->device = static_cast<QOpenGLPaintDevice*>(pdev); if (!d->device) return false; - d->ctx = d->device->context(); - d->ctx->d_ptr->active_engine = this; + d->device->ensureActiveTarget(); + + if (d->device->context() != QOpenGLContext::currentContext() || !d->device->context()) { + qWarning("QPainter::begin(): QOpenGLPaintDevice's context needs to be current"); + return false; + } + + if (d->ctx != QOpenGLContext::currentContext() + || (d->ctx && QOpenGLContext::currentContext() && d->ctx->format() != QOpenGLContext::currentContext()->format())) { + d->vertexBuffer.destroy(); + d->texCoordBuffer.destroy(); + d->opacityBuffer.destroy(); + d->indexBuffer.destroy(); + d->vao.destroy(); + } + + d->ctx = QOpenGLContext::currentContext(); + d->ctx->d_func()->active_engine = this; + + QOpenGLPaintDevicePrivate::get(d->device)->beginPaint(); + + d->funcs.initializeOpenGLFunctions(); + + // Generate a new Vertex Array Object if we don't have one already. We can + // only hit the VAO-based path when using a core profile context. This is + // because while non-core contexts can support VAOs via extensions, legacy + // components like the QtOpenGL module do not know about VAOs. There are + // still tests for QGL-QOpenGL paint engine interoperability, so keep the + // status quo for now, and avoid introducing a VAO in non-core contexts. + const bool needsVAO = d->ctx->format().profile() == QSurfaceFormat::CoreProfile + && d->ctx->format().version() >= qMakePair(3, 2); + if (needsVAO && !d->vao.isCreated()) { + bool created = d->vao.create(); + + // If we managed to create it then we have a profile that supports VAOs + if (created) { + d->vao.bind(); - d->resetOpenGLContextActiveEngine(); + // Generate a new Vertex Buffer Object if we don't have one already + if (!d->vertexBuffer.isCreated()) { + d->vertexBuffer.create(); + // Set its usage to StreamDraw, we will use this buffer only a few times before refilling it + d->vertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + if (!d->texCoordBuffer.isCreated()) { + d->texCoordBuffer.create(); + d->texCoordBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + if (!d->opacityBuffer.isCreated()) { + d->opacityBuffer.create(); + d->opacityBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + if (!d->indexBuffer.isCreated()) { + d->indexBuffer.create(); + d->indexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + } + } + + for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) + d->vertexAttributeArraysEnabledState[i] = false; const QSize sz = d->device->size(); d->width = sz.width(); @@ -2051,7 +2233,6 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) d->matrixDirty = true; d->compositionModeDirty = true; d->opacityUniformDirty = true; - d->translateZUniformDirty = true; d->needsSync = true; d->useSystemClip = !systemClip().isEmpty(); d->currentBrush = QBrush(); @@ -2059,67 +2240,55 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); d->stencilClean = true; - // Calling begin paint should make the correct context current. So, any - // code which calls into GL or otherwise needs a current context *must* - // go after beginPaint: - d->device->beginPaint(); + d->shaderManager = new QOpenGLEngineShaderManager(d->ctx); - d->initializeOpenGLFunctions(); - - d->shaderManager = new QGLEngineShaderManager(d->ctx); - - d->glDisable(GL_STENCIL_TEST); - d->glDisable(GL_DEPTH_TEST); - d->glDisable(GL_SCISSOR_TEST); - -#if !defined(QT_OPENGL_ES_2) - if (!d->ctx->contextHandle()->isOpenGLES()) - d->glDisable(GL_MULTISAMPLE); -#endif + d->funcs.glDisable(GL_STENCIL_TEST); + d->funcs.glDisable(GL_DEPTH_TEST); + d->funcs.glDisable(GL_SCISSOR_TEST); d->glyphCacheFormat = QFontEngine::Format_A8; -#if !defined(QT_OPENGL_ES_2) - if (!d->ctx->contextHandle()->isOpenGLES()) { +#ifndef QT_OPENGL_ES_2 + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + d->funcs.glDisable(GL_MULTISAMPLE); d->glyphCacheFormat = QFontEngine::Format_A32; d->multisamplingAlwaysEnabled = false; - } else { - d->multisamplingAlwaysEnabled = d->device->format().sampleBuffers(); + } else +#endif // QT_OPENGL_ES_2 + { + // OpenGL ES can't switch MSAA off, so if the gl paint device is + // multisampled, it's always multisampled. + d->multisamplingAlwaysEnabled = d->device->context()->format().samples() > 1; } -#else - // OpenGL ES can't switch MSAA off, so if the gl paint device is - // multisampled, it's always multisampled. - d->multisamplingAlwaysEnabled = d->device->format().sampleBuffers(); -#endif return true; } -bool QGL2PaintEngineEx::end() +bool QOpenGL2PaintEngineEx::end() { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); + + QOpenGLPaintDevicePrivate::get(d->device)->endPaint(); - QGLContext *ctx = d->ctx; - d->glUseProgram(0); + QOpenGLContext *ctx = d->ctx; + d->funcs.glUseProgram(0); d->transferMode(BrushDrawingMode); - d->device->endPaint(); - ctx->d_ptr->active_engine = 0; - ctx->makeCurrent(); - d->resetOpenGLContextActiveEngine(); + ctx->d_func()->active_engine = nullptr; + d->resetGLState(); delete d->shaderManager; - d->shaderManager = 0; + d->shaderManager = nullptr; d->currentBrush = QBrush(); #ifdef QT_OPENGL_CACHE_AS_VBOS if (!d->unusedVBOSToClean.isEmpty()) { - d->glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData()); + glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData()); d->unusedVBOSToClean.clear(); } if (!d->unusedIBOSToClean.isEmpty()) { - d->glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData()); + glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData()); d->unusedIBOSToClean.clear(); } #endif @@ -2127,40 +2296,42 @@ bool QGL2PaintEngineEx::end() return false; } -void QGL2PaintEngineEx::ensureActive() +void QOpenGL2PaintEngineEx::ensureActive() { - Q_D(QGL2PaintEngineEx); - QGLContext *ctx = d->ctx; + Q_D(QOpenGL2PaintEngineEx); + QOpenGLContext *ctx = d->ctx; + + if (d->vao.isCreated()) + d->vao.bind(); - if (isActive() && (ctx->d_ptr->active_engine != this || d->resetOpenGLContextActiveEngine())) { - ctx->d_ptr->active_engine = this; + if (isActive() && ctx->d_func()->active_engine != this) { + ctx->d_func()->active_engine = this; d->needsSync = true; } - d->device->ensureActiveTarget(); - if (d->needsSync) { + d->device->ensureActiveTarget(); + d->transferMode(BrushDrawingMode); - d->glViewport(0, 0, d->width, d->height); + d->funcs.glViewport(0, 0, d->width, d->height); d->needsSync = false; - d->lastMaskTextureUsed = 0; d->shaderManager->setDirty(); - d->ctx->d_func()->syncGlState(); + d->syncGlState(); for (int i = 0; i < 3; ++i) d->vertexAttribPointers[i] = (GLfloat*)-1; // Assume the pointers are clobbered setState(state()); } } -void QGL2PaintEngineExPrivate::updateClipScissorTest() +void QOpenGL2PaintEngineExPrivate::updateClipScissorTest() { - Q_Q(QGL2PaintEngineEx); + Q_Q(QOpenGL2PaintEngineEx); if (q->state()->clipTestEnabled) { - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + funcs.glEnable(GL_STENCIL_TEST); + funcs.glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); } else { - glDisable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 0, 0xff); + funcs.glDisable(GL_STENCIL_TEST); + funcs.glStencilFunc(GL_ALWAYS, 0, 0xff); } #ifdef QT_GL_NO_SCISSOR_TEST @@ -2182,30 +2353,30 @@ void QGL2PaintEngineExPrivate::updateClipScissorTest() currentScissorBounds = bounds; if (bounds == QRect(0, 0, width, height)) { - glDisable(GL_SCISSOR_TEST); + funcs.glDisable(GL_SCISSOR_TEST); } else { - glEnable(GL_SCISSOR_TEST); + funcs.glEnable(GL_SCISSOR_TEST); setScissor(bounds); } #endif } -void QGL2PaintEngineExPrivate::setScissor(const QRect &rect) +void QOpenGL2PaintEngineExPrivate::setScissor(const QRect &rect) { const int left = rect.left(); const int width = rect.width(); int bottom = height - (rect.top() + rect.height()); - if (device->isFlipped()) { + if (device->paintFlipped()) { bottom = rect.top(); } const int height = rect.height(); - glScissor(left, bottom, width, height); + funcs.glScissor(left, bottom, width, height); } -void QGL2PaintEngineEx::clipEnabledChanged() +void QOpenGL2PaintEngineEx::clipEnabledChanged() { - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); state()->clipChanged = true; @@ -2215,19 +2386,19 @@ void QGL2PaintEngineEx::clipEnabledChanged() d->systemStateChanged(); } -void QGL2PaintEngineExPrivate::clearClip(uint value) +void QOpenGL2PaintEngineExPrivate::clearClip(uint value) { dirtyStencilRegion -= currentScissorBounds; - glStencilMask(0xff); - glClearStencil(value); - glClear(GL_STENCIL_BUFFER_BIT); - glStencilMask(0x0); + funcs.glStencilMask(0xff); + funcs.glClearStencil(value); + funcs.glClear(GL_STENCIL_BUFFER_BIT); + funcs.glStencilMask(0x0); q->state()->needsClipBufferClear = false; } -void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value) +void QOpenGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value) { transferMode(BrushDrawingMode); @@ -2250,15 +2421,15 @@ void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value) clearClip(1); if (path.isEmpty()) { - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); + funcs.glEnable(GL_STENCIL_TEST); + funcs.glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); return; } if (q->state()->clipTestEnabled) - glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); else - glStencilFunc(GL_ALWAYS, 0, 0xff); + funcs.glStencilFunc(GL_ALWAYS, 0, 0xff); vertexCoordinateArray.clear(); vertexCoordinateArray.addPath(path, inverseScale, false); @@ -2266,45 +2437,45 @@ void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value) if (!singlePass) fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); - glColorMask(false, false, false, false); - glEnable(GL_STENCIL_TEST); + funcs.glColorMask(false, false, false, false); + funcs.glEnable(GL_STENCIL_TEST); useSimpleShader(); if (singlePass) { // Under these conditions we can set the new stencil value in a single // pass, by using the current value and the "new value" as the toggles - glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT); - glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); - glStencilMask(value ^ referenceClipValue); + funcs.glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT); + funcs.glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + funcs.glStencilMask(value ^ referenceClipValue); drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN); } else { - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - glStencilMask(0xff); + funcs.glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + funcs.glStencilMask(0xff); if (!q->state()->clipTestEnabled && path.hasWindingFill()) { // Pass when any clip bit is set, set high bit - glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT); composite(vertexCoordinateArray.boundingRect()); } // Pass when high bit is set, replace stencil value with new clip value - glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT); + funcs.glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT); composite(vertexCoordinateArray.boundingRect()); } - glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); - glStencilMask(0); + funcs.glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); + funcs.glStencilMask(0); - glColorMask(true, true, true, true); + funcs.glColorMask(true, true, true, true); } -void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) +void QOpenGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) { -// qDebug("QGL2PaintEngineEx::clip()"); - Q_D(QGL2PaintEngineEx); +// qDebug("QOpenGL2PaintEngineEx::clip()"); + Q_D(QOpenGL2PaintEngineEx); state()->clipChanged = true; @@ -2363,15 +2534,15 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) } } -void QGL2PaintEngineExPrivate::regenerateClip() +void QOpenGL2PaintEngineExPrivate::regenerateClip() { systemStateChanged(); replayClipOperations(); } -void QGL2PaintEngineExPrivate::systemStateChanged() +void QOpenGL2PaintEngineExPrivate::systemStateChanged() { - Q_Q(QGL2PaintEngineEx); + Q_Q(QOpenGL2PaintEngineEx); q->state()->clipChanged = true; @@ -2379,8 +2550,9 @@ void QGL2PaintEngineExPrivate::systemStateChanged() useSystemClip = false; } else { if (q->paintDevice()->devType() == QInternal::Widget && currentClipDevice) { - QWidgetPrivate *widgetPrivate = qt_widget_private(static_cast<QWidget *>(currentClipDevice)->window()); - useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter; + //QWidgetPrivate *widgetPrivate = qt_widget_private(static_cast<QWidget *>(currentClipDevice)->window()); + //useSystemClip = widgetPrivate->extra && widgetPrivate->extra->inRenderWithPainter; + useSystemClip = true; } else { useSystemClip = true; } @@ -2417,23 +2589,14 @@ void QGL2PaintEngineExPrivate::systemStateChanged() } } -void QGL2PaintEngineEx::setTranslateZ(GLfloat z) +void QOpenGL2PaintEngineEx::setState(QPainterState *new_state) { - Q_D(QGL2PaintEngineEx); - if (d->translateZ != z) { - d->translateZ = z; - d->translateZUniformDirty = true; - } -} - -void QGL2PaintEngineEx::setState(QPainterState *new_state) -{ - // qDebug("QGL2PaintEngineEx::setState()"); + // qDebug("QOpenGL2PaintEngineEx::setState()"); - Q_D(QGL2PaintEngineEx); + Q_D(QOpenGL2PaintEngineEx); - QGL2PaintEngineState *s = static_cast<QGL2PaintEngineState *>(new_state); - QGL2PaintEngineState *old_state = state(); + QOpenGL2PaintEngineState *s = static_cast<QOpenGL2PaintEngineState *>(new_state); + QOpenGL2PaintEngineState *old_state = state(); QPaintEngineEx::setState(s); @@ -2462,23 +2625,23 @@ void QGL2PaintEngineEx::setState(QPainterState *new_state) if (old_state == s || old_state->clipChanged) { if (old_state && old_state != s && old_state->canRestoreClip) { d->updateClipScissorTest(); - d->glDepthFunc(GL_LEQUAL); + d->funcs.glDepthFunc(GL_LEQUAL); } else { d->regenerateClip(); } } } -QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const +QPainterState *QOpenGL2PaintEngineEx::createState(QPainterState *orig) const { if (orig) - const_cast<QGL2PaintEngineEx *>(this)->ensureActive(); + const_cast<QOpenGL2PaintEngineEx *>(this)->ensureActive(); - QGL2PaintEngineState *s; + QOpenGL2PaintEngineState *s; if (!orig) - s = new QGL2PaintEngineState(); + s = new QOpenGL2PaintEngineState(); else - s = new QGL2PaintEngineState(*static_cast<QGL2PaintEngineState *>(orig)); + s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig)); s->matrixChanged = false; s->compositionModeChanged = false; @@ -2489,7 +2652,7 @@ QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const return s; } -QGL2PaintEngineState::QGL2PaintEngineState(QGL2PaintEngineState &other) +QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other) : QPainterState(other) { isNew = true; @@ -2500,7 +2663,7 @@ QGL2PaintEngineState::QGL2PaintEngineState(QGL2PaintEngineState &other) rectangleClip = other.rectangleClip; } -QGL2PaintEngineState::QGL2PaintEngineState() +QOpenGL2PaintEngineState::QOpenGL2PaintEngineState() { isNew = true; needsClipBufferClear = true; @@ -2508,8 +2671,32 @@ QGL2PaintEngineState::QGL2PaintEngineState() canRestoreClip = true; } -QGL2PaintEngineState::~QGL2PaintEngineState() +QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState() +{ +} + +void QOpenGL2PaintEngineExPrivate::setVertexAttribArrayEnabled(int arrayIndex, bool enabled) +{ + Q_ASSERT(arrayIndex < QT_GL_VERTEX_ARRAY_TRACKED_COUNT); + + if (vertexAttributeArraysEnabledState[arrayIndex] && !enabled) + funcs.glDisableVertexAttribArray(arrayIndex); + + if (!vertexAttributeArraysEnabledState[arrayIndex] && enabled) + funcs.glEnableVertexAttribArray(arrayIndex); + + vertexAttributeArraysEnabledState[arrayIndex] = enabled; +} + +void QOpenGL2PaintEngineExPrivate::syncGlState() { + for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) { + if (vertexAttributeArraysEnabledState[i]) + funcs.glEnableVertexAttribArray(i); + else + funcs.glDisableVertexAttribArray(i); + } } + QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/qopenglpaintengine_p.h index 762aac2f65..9dc92e3810 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/qopenglpaintengine_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QPAINTENGINEEX_OPENGL2_P_H -#define QPAINTENGINEEX_OPENGL2_P_H +#ifndef QOPENGLPAINTENGINE_P_H +#define QOPENGLPAINTENGINE_P_H // // W A R N I N G @@ -53,39 +53,46 @@ #include <QDebug> +#include <qopenglpaintdevice.h> + #include <private/qpaintengineex_p.h> -#include <private/qglengineshadermanager_p.h> -#include <private/qgl2pexvertexarray_p.h> -#include <private/qglpaintdevice_p.h> +#include <private/qopenglengineshadermanager_p.h> +#include <private/qopengl2pexvertexarray_p.h> #include <private/qfontengine_p.h> #include <private/qdatabuffer_p.h> #include <private/qtriangulatingstroker_p.h> + #include <private/qopenglextensions_p.h> +#include <QOpenGLVertexArrayObject> +#include <QOpenGLBuffer> + enum EngineMode { ImageDrawingMode, TextDrawingMode, BrushDrawingMode, - ImageArrayDrawingMode + ImageArrayDrawingMode, + ImageOpacityArrayDrawingMode }; QT_BEGIN_NAMESPACE #define GL_STENCIL_HIGH_BIT GLuint(0x80) +#define QT_UNKNOWN_TEXTURE_UNIT GLuint(-1) +#define QT_DEFAULT_TEXTURE_UNIT GLuint(0) #define QT_BRUSH_TEXTURE_UNIT GLuint(0) #define QT_IMAGE_TEXTURE_UNIT GLuint(0) //Can be the same as brush texture unit #define QT_MASK_TEXTURE_UNIT GLuint(1) #define QT_BACKGROUND_TEXTURE_UNIT GLuint(2) -class QGL2PaintEngineExPrivate; +class QOpenGL2PaintEngineExPrivate; - -class QGL2PaintEngineState : public QPainterState +class QOpenGL2PaintEngineState : public QPainterState { public: - QGL2PaintEngineState(QGL2PaintEngineState &other); - QGL2PaintEngineState(); - ~QGL2PaintEngineState(); + QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other); + QOpenGL2PaintEngineState(); + ~QOpenGL2PaintEngineState(); uint isNew : 1; uint needsClipBufferClear : 1; @@ -101,12 +108,12 @@ public: QRect rectangleClip; }; -class Q_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx +class Q_OPENGL_EXPORT QOpenGL2PaintEngineEx : public QPaintEngineEx { - Q_DECLARE_PRIVATE(QGL2PaintEngineEx) + Q_DECLARE_PRIVATE(QOpenGL2PaintEngineEx) public: - QGL2PaintEngineEx(); - ~QGL2PaintEngineEx(); + QOpenGL2PaintEngineEx(); + ~QOpenGL2PaintEngineEx(); bool begin(QPaintDevice *device) override; void ensureActive(); @@ -139,11 +146,11 @@ public: virtual void setState(QPainterState *s) override; virtual QPainterState *createState(QPainterState *orig) const override; - inline QGL2PaintEngineState *state() { - return static_cast<QGL2PaintEngineState *>(QPaintEngineEx::state()); + inline QOpenGL2PaintEngineState *state() { + return static_cast<QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); } - inline const QGL2PaintEngineState *state() const { - return static_cast<const QGL2PaintEngineState *>(QPaintEngineEx::state()); + inline const QOpenGL2PaintEngineState *state() const { + return static_cast<const QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); } void beginNativePainting() override; @@ -157,15 +164,19 @@ public: bool requiresPretransformedGlyphPositions(QFontEngine *, const QTransform &) const override { return false; } bool shouldDrawCachedGlyphs(QFontEngine *, const QTransform &) const override; - void setTranslateZ(GLfloat z); - private: - Q_DISABLE_COPY_MOVE(QGL2PaintEngineEx) + Q_DISABLE_COPY_MOVE(QOpenGL2PaintEngineEx) + + friend class QOpenGLEngineShaderManager; }; -class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate, protected QOpenGLExtensions +// This probably needs to grow to GL_MAX_VERTEX_ATTRIBS, but 3 is ok for now as that's +// all the GL2 engine uses: +#define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3 + +class QOpenGL2PaintEngineExPrivate : public QPaintEngineExPrivate { - Q_DECLARE_PUBLIC(QGL2PaintEngineEx) + Q_DECLARE_PUBLIC(QOpenGL2PaintEngineEx) public: enum StencilFillMode { OddEvenFillMode, @@ -173,7 +184,7 @@ public: TriStripStrokeFillMode }; - QGL2PaintEngineExPrivate(QGL2PaintEngineEx *q_ptr) : + QOpenGL2PaintEngineExPrivate(QOpenGL2PaintEngineEx *q_ptr) : q(q_ptr), shaderManager(nullptr), width(0), height(0), @@ -184,45 +195,54 @@ public: snapToPixelGrid(false), nativePaintingActive(false), inverseScale(1), - lastMaskTextureUsed(0), - translateZ(0) + lastTextureUnitUsed(QT_UNKNOWN_TEXTURE_UNIT), + vertexBuffer(QOpenGLBuffer::VertexBuffer), + texCoordBuffer(QOpenGLBuffer::VertexBuffer), + opacityBuffer(QOpenGLBuffer::VertexBuffer), + indexBuffer(QOpenGLBuffer::IndexBuffer) { } - ~QGL2PaintEngineExPrivate(); + ~QOpenGL2PaintEngineExPrivate(); void updateBrushTexture(); void updateBrushUniforms(); void updateMatrix(); void updateCompositionMode(); - void updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id = GLuint(-1)); + + enum TextureUpdateMode { UpdateIfNeeded, ForceUpdate }; + template<typename T> + void updateTexture(GLenum textureUnit, const T &texture, GLenum wrapMode, GLenum filterMode, TextureUpdateMode updateMode = UpdateIfNeeded); + template<typename T> + GLuint bindTexture(const T &texture); + void activateTextureUnit(GLenum textureUnit); void resetGLState(); - bool resetOpenGLContextActiveEngine(); // fill, stroke, drawTexture, drawPixmaps & drawCachedGlyphs are the main rendering entry-points, // however writeClip can also be thought of as en entry point as it does similar things. void fill(const QVectorPath &path); void stroke(const QVectorPath &path, const QPen &pen); - void drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false); + void drawTexture(const QOpenGLRect& dest, const QOpenGLRect& src, const QSize &textureSize, bool opaque, bool pattern = false); void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, QPainter::PixmapFragmentHints hints); void drawCachedGlyphs(QFontEngine::GlyphFormat glyphFormat, QStaticTextItem *staticTextItem); // Calls glVertexAttributePointer if the pointer has changed - inline void setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer); + inline void uploadData(unsigned int arrayIndex, const GLfloat *data, GLuint count); + inline bool uploadIndexData(const void *data, GLenum indexValueType, GLuint count); // draws whatever is in the vertex array: void drawVertexArrays(const float *data, int *stops, int stopCount, GLenum primitive); - void drawVertexArrays(QGL2PEXVertexArray &vertexArray, GLenum primitive) { + void drawVertexArrays(QOpenGL2PEXVertexArray &vertexArray, GLenum primitive) { drawVertexArrays((const float *) vertexArray.data(), vertexArray.stops(), vertexArray.stopCount(), primitive); } // Composites the bounding rect onto dest buffer: - void composite(const QGLRect& boundingRect); + void composite(const QOpenGLRect& boundingRect); // Calls drawVertexArrays to render into stencil buffer: - void fillStencilWithVertexArray(const float *data, int count, int *stops, int stopCount, const QGLRect &bounds, StencilFillMode mode); - void fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill) { + void fillStencilWithVertexArray(const float *data, int count, int *stops, int stopCount, const QOpenGLRect &bounds, StencilFillMode mode); + void fillStencilWithVertexArray(QOpenGL2PEXVertexArray& vertexArray, bool useWindingFill) { fillStencilWithVertexArray((const float *) vertexArray.data(), 0, vertexArray.stops(), vertexArray.stopCount(), vertexArray.boundingRect(), useWindingFill ? WindingFillMode : OddEvenFillMode); @@ -233,7 +253,7 @@ public: bool prepareForDraw(bool srcPixelsAreOpaque); // returns true if the program has changed bool prepareForCachedGlyphDraw(const QFontEngineGlyphCache &cache); inline void useSimpleShader(); - inline GLuint location(const QGLEngineShaderManager::Uniform uniform) { + inline GLuint location(const QOpenGLEngineShaderManager::Uniform uniform) { return shaderManager->getUniformLocation(uniform); } @@ -246,20 +266,25 @@ public: void regenerateClip(); void systemStateChanged() override; + void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true); + void syncGlState(); - static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; } - static QGL2PaintEngineExPrivate *getData(QGL2PaintEngineEx *engine) { return engine->d_func(); } + static QOpenGLEngineShaderManager* shaderManagerForEngine(QOpenGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; } + static QOpenGL2PaintEngineExPrivate *getData(QOpenGL2PaintEngineEx *engine) { return engine->d_func(); } static void cleanupVectorPath(QPaintEngineEx *engine, void *data); + QOpenGLExtensions funcs; - QGL2PaintEngineEx* q; - QGLEngineShaderManager* shaderManager; - QGLPaintDevice* device; + QOpenGL2PaintEngineEx* q; + QOpenGLEngineShaderManager* shaderManager; + QOpenGLPaintDevice* device; int width, height; - QGLContext *ctx; + QOpenGLContext *ctx; EngineMode mode; QFontEngine::GlyphFormat glyphCacheFormat; + bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT]; + // Dirty flags bool matrixDirty; // Implies matrix uniforms are also dirty bool compositionModeDirty; @@ -267,7 +292,6 @@ public: bool brushUniformsDirty; bool opacityUniformDirty; bool matrixUniformDirty; - bool translateZUniformDirty; bool stencilClean; // Has the stencil not been used for clipping so far? bool useSystemClip; @@ -278,10 +302,10 @@ public: QBrush currentBrush; // May not be the state's brush! const QBrush noBrush; - QPixmap currentBrushPixmap; + QImage currentBrushImage; - QGL2PEXVertexArray vertexCoordinateArray; - QGL2PEXVertexArray textureCoordinateArray; + QOpenGL2PEXVertexArray vertexCoordinateArray; + QOpenGL2PEXVertexArray textureCoordinateArray; QVector<GLushort> elementIndices; GLuint elementIndicesVBOId; QDataBuffer<GLfloat> opacityArray; @@ -293,42 +317,79 @@ public: GLfloat pmvMatrix[3][3]; GLfloat inverseScale; + GLenum lastTextureUnitUsed; GLuint lastTextureUsed; - GLuint lastMaskTextureUsed; + + QOpenGLVertexArrayObject vao; + QOpenGLBuffer vertexBuffer; + QOpenGLBuffer texCoordBuffer; + QOpenGLBuffer opacityBuffer; + QOpenGLBuffer indexBuffer; bool needsSync; bool multisamplingAlwaysEnabled; - GLfloat depthRange[2]; - - float textureInvertedY; - QTriangulatingStroker stroker; QDashedStrokeProcessor dasher; - QSet<QVectorPath::CacheEntry *> pathCaches; QVector<GLuint> unusedVBOSToClean; QVector<GLuint> unusedIBOSToClean; const GLfloat *vertexAttribPointers[3]; - - GLfloat translateZ; }; -void QGL2PaintEngineExPrivate::setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer) +void QOpenGL2PaintEngineExPrivate::uploadData(unsigned int arrayIndex, const GLfloat *data, GLuint count) { Q_ASSERT(arrayIndex < 3); - if (pointer == vertexAttribPointers[arrayIndex]) - return; - - vertexAttribPointers[arrayIndex] = pointer; - if (arrayIndex == QT_OPACITY_ATTR) - glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, pointer); - else - glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, pointer); + + // If a vertex array object is created we have a profile that supports them + // and we will upload the data via a QOpenGLBuffer. Otherwise we will use + // the legacy way of uploading the data via glVertexAttribPointer. + if (vao.isCreated()) { + if (arrayIndex == QT_VERTEX_COORDS_ATTR) { + vertexBuffer.bind(); + vertexBuffer.allocate(data, count * sizeof(float)); + } + if (arrayIndex == QT_TEXTURE_COORDS_ATTR) { + texCoordBuffer.bind(); + texCoordBuffer.allocate(data, count * sizeof(float)); + } + if (arrayIndex == QT_OPACITY_ATTR) { + opacityBuffer.bind(); + opacityBuffer.allocate(data, count * sizeof(float)); + } + if (arrayIndex == QT_OPACITY_ATTR) + funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, nullptr); + else + funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + } else { + // If we already uploaded the data we don't have to do it again + if (data == vertexAttribPointers[arrayIndex]) + return; + + // Store the data in cache and upload it to the graphics card. + vertexAttribPointers[arrayIndex] = data; + if (arrayIndex == QT_OPACITY_ATTR) + funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, data); + else + funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, data); + } +} + +bool QOpenGL2PaintEngineExPrivate::uploadIndexData(const void *data, GLenum indexValueType, GLuint count) +{ + // Follow the uploadData() logic: VBOs are used only when VAO support is available. + // Otherwise the legacy client-side pointer path is used. + if (vao.isCreated()) { + Q_ASSERT(indexValueType == GL_UNSIGNED_SHORT || indexValueType == GL_UNSIGNED_INT); + indexBuffer.bind(); + indexBuffer.allocate(data, count * (indexValueType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32))); + return true; + } + return false; } QT_END_NAMESPACE -#endif // QPAINTENGINEEX_OPENGL2_P_H +#endif diff --git a/src/opengl/qopenglpixeltransferoptions.cpp b/src/opengl/qopenglpixeltransferoptions.cpp new file mode 100644 index 0000000000..aa1af3b092 --- /dev/null +++ b/src/opengl/qopenglpixeltransferoptions.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglpixeltransferoptions.h" +#include <QSharedData> + +QT_BEGIN_NAMESPACE + +/*! + * \class QOpenGLPixelTransferOptions + * + * \brief The QOpenGLPixelTransferOptions class describes the pixel storage + * modes that affect the unpacking of pixels during texture upload. + */ + +/*! + * \fn QOpenGLPixelTransferOptions & QOpenGLPixelTransferOptions::operator=(QOpenGLPixelTransferOptions &&other) + * \internal + */ + +/*! + * \fn void QOpenGLPixelTransferOptions::swap(QOpenGLPixelTransferOptions &other) + * \internal + */ + +class QOpenGLPixelTransferOptionsData : public QSharedData +{ +public: + QOpenGLPixelTransferOptionsData() + : alignment(4) + , skipImages(0) + , skipRows(0) + , skipPixels(0) + , imageHeight(0) + , rowLength(0) + , lsbFirst(false) + , swapBytes(false) + {} + + int alignment; + int skipImages; + int skipRows; + int skipPixels; + int imageHeight; + int rowLength; + bool lsbFirst; + bool swapBytes; +}; + +/*! + * Constructs a new QOpenGLPixelTransferOptions instance with the default settings. + */ +QOpenGLPixelTransferOptions::QOpenGLPixelTransferOptions() + : data(new QOpenGLPixelTransferOptionsData) +{ +} + +/*! + * \internal + */ +QOpenGLPixelTransferOptions::QOpenGLPixelTransferOptions(const QOpenGLPixelTransferOptions &rhs) + : data(rhs.data) +{ +} + +/*! + * \internal + */ +QOpenGLPixelTransferOptions &QOpenGLPixelTransferOptions::operator=(const QOpenGLPixelTransferOptions &rhs) +{ + if (this != &rhs) + data.operator=(rhs.data); + return *this; +} + +/*! + * Destructor. + */ +QOpenGLPixelTransferOptions::~QOpenGLPixelTransferOptions() +{ +} + +/*! + * Sets the \a alignment requirements for each pixel row. Corresponds to \c GL_UNPACK_ALIGNMENT. + * The default value is 4, as specified by OpenGL. + */ +void QOpenGLPixelTransferOptions::setAlignment(int alignment) +{ + data->alignment = alignment; +} + +/*! + * \return the current alignment requirement for each pixel row. + */ +int QOpenGLPixelTransferOptions::alignment() const +{ + return data->alignment; +} + +/*! + * Sets the number of images that are skipped to \a skipImages. + * Corresponds to \c GL_UNPACK_SKIP_IMAGES. Equivalent to incrementing the pointer + * passed to QOpenGLTexture::setData(). The default value is 0. + */ +void QOpenGLPixelTransferOptions::setSkipImages(int skipImages) +{ + data->skipImages = skipImages; +} + +/*! + * \return the number of images that are skipped. + */ +int QOpenGLPixelTransferOptions::skipImages() const +{ + return data->skipImages; +} + +/*! + * Sets the number of rows that are skipped to \a skipRows. + * Corresponds to \c GL_UNPACK_SKIP_ROWS. Equivalent to incrementing the pointer + * passed to QOpenGLTexture::setData(). The default value is 0. + */ +void QOpenGLPixelTransferOptions::setSkipRows(int skipRows) +{ + data->skipRows = skipRows; +} + +/*! + * \return the number of rows that are skipped. + */ +int QOpenGLPixelTransferOptions::skipRows() const +{ + return data->skipRows; +} + +/*! + * Sets the number of pixels that are skipped to \a skipPixels. + * Corresponds to \c GL_UNPACK_SKIP_PIXELS. Equivalent to incrementing the pointer + * passed to QOpenGLTexture::setData(). The default value is 0. + */ +void QOpenGLPixelTransferOptions::setSkipPixels(int skipPixels) +{ + data->skipPixels = skipPixels; +} + +/*! + * \return the number of pixels that are skipped. + */ +int QOpenGLPixelTransferOptions::skipPixels() const +{ + return data->skipPixels; +} + +/*! + * Sets the image height for 3D textures to \a imageHeight. + * Corresponds to \c GL_UNPACK_IMAGE_HEIGHT. + * The default value is 0. + */ +void QOpenGLPixelTransferOptions::setImageHeight(int imageHeight) +{ + data->imageHeight = imageHeight; +} + +/*! + * \return the currently set image height. + */ +int QOpenGLPixelTransferOptions::imageHeight() const +{ + return data->imageHeight; +} + +/*! + * Sets the number of pixels in a row to \a rowLength. + * Corresponds to \c GL_UNPACK_ROW_LENGTH. + * The default value is 0. + */ +void QOpenGLPixelTransferOptions::setRowLength(int rowLength) +{ + data->rowLength = rowLength; +} + +/*! + * \return the currently set row length. + */ +int QOpenGLPixelTransferOptions::rowLength() const +{ + return data->rowLength; +} + +/*! + * \a lsbFirst specifies if bits within a byte are ordered from least to most significat. + * The default value is \c false, meaning that the first bit in each byte is the + * most significant one. This is significant for bitmap data only. + * Corresponds to \c GL_UNPACK_LSB_FIRST. + */ +void QOpenGLPixelTransferOptions::setLeastSignificantByteFirst(bool lsbFirst) +{ + data->lsbFirst = lsbFirst; +} + +/*! + * \return \c true if bits within a byte are ordered from least to most significant. + */ +bool QOpenGLPixelTransferOptions::isLeastSignificantBitFirst() const +{ + return data->lsbFirst; +} + +/*! + * \a swapBytes specifies if the byte ordering for multibyte components is reversed. + * The default value is \c false. + * Corresponds to \c GL_UNPACK_SWAP_BYTES. + */ +void QOpenGLPixelTransferOptions::setSwapBytesEnabled(bool swapBytes) +{ + data->swapBytes = swapBytes; +} + +/*! + * \return \c true if the byte ordering for multibyte components is reversed. + */ +bool QOpenGLPixelTransferOptions::isSwapBytesEnabled() const +{ + return data->swapBytes; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qopenglpixeltransferoptions.h b/src/opengl/qopenglpixeltransferoptions.h new file mode 100644 index 0000000000..252c2a2f1e --- /dev/null +++ b/src/opengl/qopenglpixeltransferoptions.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLPIXELUPLOADOPTIONS_H +#define QOPENGLPIXELUPLOADOPTIONS_H + +#include <QtOpenGL/qtopenglglobal.h> + +#if !defined(QT_NO_OPENGL) + +#include <QtCore/QSharedDataPointer> + +QT_BEGIN_NAMESPACE + +class QOpenGLPixelTransferOptionsData; + +class Q_OPENGL_EXPORT QOpenGLPixelTransferOptions +{ +public: + QOpenGLPixelTransferOptions(); + QOpenGLPixelTransferOptions(const QOpenGLPixelTransferOptions &); + QOpenGLPixelTransferOptions &operator=(QOpenGLPixelTransferOptions &&other) noexcept + { swap(other); return *this; } + QOpenGLPixelTransferOptions &operator=(const QOpenGLPixelTransferOptions &); + ~QOpenGLPixelTransferOptions(); + + void swap(QOpenGLPixelTransferOptions &other) noexcept + { data.swap(other.data); } + + void setAlignment(int alignment); + int alignment() const; + + void setSkipImages(int skipImages); + int skipImages() const; + + void setSkipRows(int skipRows); + int skipRows() const; + + void setSkipPixels(int skipPixels); + int skipPixels() const; + + void setImageHeight(int imageHeight); + int imageHeight() const; + + void setRowLength(int rowLength); + int rowLength() const; + + void setLeastSignificantByteFirst(bool lsbFirst); + bool isLeastSignificantBitFirst() const; + + void setSwapBytesEnabled(bool swapBytes); + bool isSwapBytesEnabled() const; + +private: + QSharedDataPointer<QOpenGLPixelTransferOptionsData> data; +}; + +Q_DECLARE_SHARED(QOpenGLPixelTransferOptions) + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QOPENGLPIXELUPLOADOPTIONS_H diff --git a/src/opengl/qopenglqueryhelper_p.h b/src/opengl/qopenglqueryhelper_p.h new file mode 100644 index 0000000000..f3ed997f98 --- /dev/null +++ b/src/opengl/qopenglqueryhelper_p.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLQUERYHELPER_P_H +#define QOPENGLQUERYHELPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt 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. +// + +#include <QtGui/private/qtguiglobal_p.h> + +#if !defined(QT_OPENGL_ES_2) + +#include <QtGui/QOpenGLContext> + +QT_BEGIN_NAMESPACE + +// Helper class used by QOpenGLTimerQuery and later will be used by +// QOpenGLOcclusionQuery +class QOpenGLQueryHelper +{ +public: + QOpenGLQueryHelper(QOpenGLContext *context) + : GetQueryObjectuiv(nullptr), + GetQueryObjectiv(nullptr), + GetQueryiv(nullptr), + EndQuery(nullptr), + BeginQuery(nullptr), + IsQuery(nullptr), + DeleteQueries(nullptr), + GenQueries(nullptr), + GetInteger64v(nullptr), + GetQueryObjectui64v(nullptr), + GetQueryObjecti64v(nullptr), + QueryCounter(nullptr) + { + Q_ASSERT(context); + + // Core in OpenGL >=1.5 + GetQueryObjectuiv = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLuint *)>(context->getProcAddress("glGetQueryObjectuiv")); + GetQueryObjectiv = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint *)>(context->getProcAddress("glGetQueryObjectiv")); + GetQueryiv = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLenum , GLint *)>(context->getProcAddress("glGetQueryiv")); + EndQuery = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum )>(context->getProcAddress("glEndQuery")); + BeginQuery = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLuint )>(context->getProcAddress("glBeginQuery")); + IsQuery = reinterpret_cast<GLboolean (QOPENGLF_APIENTRYP)(GLuint )>(context->getProcAddress("glIsQuery")); + DeleteQueries = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , const GLuint *)>(context->getProcAddress("glDeleteQueries")); + GenQueries = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLsizei , GLuint *)>(context->getProcAddress("glGenQueries")); + + // Core in OpenGL >=3.2 + GetInteger64v = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint64 *)>(context->getProcAddress("glGetInteger64v")); + + // Core in OpenGL >=3.3 / ARB_timer_query + GetQueryObjectui64v = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLuint64 *)>(context->getProcAddress("glGetQueryObjectui64v")); + GetQueryObjecti64v = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint64 *)>(context->getProcAddress("glGetQueryObjecti64v")); + QueryCounter = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum )>(context->getProcAddress("glQueryCounter")); + } + + inline void glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) + { + GetQueryObjectuiv(id, pname, params); + } + + inline void glGetQueryObjectiv(GLuint id, GLenum pname, GLint *params) + { + GetQueryObjectiv(id, pname, params); + } + + inline void glGetQueryiv(GLenum target, GLenum pname, GLint *params) + { + GetQueryiv(target, pname, params); + } + + inline void glEndQuery(GLenum target) + { + EndQuery(target); + } + + inline void glBeginQuery(GLenum target, GLuint id) + { + BeginQuery(target, id); + } + + inline GLboolean glIsQuery(GLuint id) + { + return IsQuery(id); + } + + inline void glDeleteQueries(GLsizei n, const GLuint *ids) + { + DeleteQueries(n, ids); + } + + inline void glGenQueries(GLsizei n, GLuint *ids) + { + GenQueries(n, ids); + } + + inline void glGetInteger64v(GLenum pname, GLint64 *params) + { + GetInteger64v(pname, params); + } + + inline void glGetQueryObjectui64v(GLuint id, GLenum pname, GLuint64 *params) + { + GetQueryObjectui64v(id, pname, params); + } + + inline void glGetQueryObjecti64v(GLuint id, GLenum pname, GLint64 *params) + { + GetQueryObjecti64v(id, pname, params); + } + + inline void glQueryCounter(GLuint id, GLenum target) + { + QueryCounter(id, target); + } + +private: + // Core in OpenGL >=1.5 + void (QOPENGLF_APIENTRYP GetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params); + void (QOPENGLF_APIENTRYP GetQueryObjectiv)(GLuint id, GLenum pname, GLint *params); + void (QOPENGLF_APIENTRYP GetQueryiv)(GLenum target, GLenum pname, GLint *params); + void (QOPENGLF_APIENTRYP EndQuery)(GLenum target); + void (QOPENGLF_APIENTRYP BeginQuery)(GLenum target, GLuint id); + GLboolean (QOPENGLF_APIENTRYP IsQuery)(GLuint id); + void (QOPENGLF_APIENTRYP DeleteQueries)(GLsizei n, const GLuint *ids); + void (QOPENGLF_APIENTRYP GenQueries)(GLsizei n, GLuint *ids); + + // Core in OpenGL >=3.2 + void (QOPENGLF_APIENTRYP GetInteger64v)(GLenum pname, GLint64 *params); + + // Core in OpenGL >=3.3 and provided by ARB_timer_query + void (QOPENGLF_APIENTRYP GetQueryObjectui64v)(GLuint id, GLenum pname, GLuint64 *params); + void (QOPENGLF_APIENTRYP GetQueryObjecti64v)(GLuint id, GLenum pname, GLint64 *params); + void (QOPENGLF_APIENTRYP QueryCounter)(GLuint id, GLenum target); +}; + +QT_END_NAMESPACE + +#endif + +#endif // QOPENGLQUERYHELPER_P_H diff --git a/src/opengl/gl2paintengineex/qglshadercache_p.h b/src/opengl/qopenglshadercache_p.h index 4204e3e256..88efa34216 100644 --- a/src/opengl/gl2paintengineex/qglshadercache_p.h +++ b/src/opengl/qopenglshadercache_p.h @@ -48,16 +48,16 @@ // We mean it. // -#ifndef QGLSHADERCACHE_P_H -#define QGLSHADERCACHE_P_H +#ifndef QOPENGLSHADERCACHE_P_H +#define QOPENGLSHADERCACHE_P_H -#include <QtCore/qglobal.h> +#include <QtOpenGL/qtopenglglobal.h> QT_BEGIN_NAMESPACE -class QGLShaderProgram; -class QGLContext; +class QOpenGLShaderProgram; +class QOpenGLContext; class CachedShader { @@ -70,12 +70,12 @@ public: return false; } - inline bool load(QGLShaderProgram *, const QGLContext *) + inline bool load(QOpenGLShaderProgram *, QOpenGLContext *) { return false; } - inline bool store(QGLShaderProgram *, const QGLContext *) + inline bool store(QOpenGLShaderProgram *, QOpenGLContext *) { return false; } diff --git a/src/opengl/qopengltexture.cpp b/src/opengl/qopengltexture.cpp new file mode 100644 index 0000000000..49ebdfbec2 --- /dev/null +++ b/src/opengl/qopengltexture.cpp @@ -0,0 +1,4989 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltexture.h" +#include "qopengltexture_p.h" +#include "qopengltexturehelper_p.h" +#include "qopenglfunctions.h" +#include <QtGui/qcolor.h> +#include <QtGui/qopenglcontext.h> +#include <QtCore/qdebug.h> +#include <private/qobject_p.h> +#include <private/qopenglcontext_p.h> + +QT_BEGIN_NAMESPACE + +//this is to work around GL_TEXTURE_WRAP_R_OES which also has 0x8072 as value +#if !defined(GL_TEXTURE_WRAP_R) + #define GL_TEXTURE_WRAP_R 0x8072 +#endif + +QOpenGLTexturePrivate::QOpenGLTexturePrivate(QOpenGLTexture::Target textureTarget, + QOpenGLTexture *qq) + : q_ptr(qq), + context(nullptr), + target(textureTarget), + textureId(0), + format(QOpenGLTexture::NoFormat), + formatClass(QOpenGLTexture::NoFormatClass), + requestedMipLevels(1), + mipLevels(-1), + layers(1), + faces(1), + samples(0), + fixedSamplePositions(true), + baseLevel(0), + maxLevel(1000), + depthStencilMode(QOpenGLTexture::DepthMode), + comparisonFunction(QOpenGLTexture::CompareLessEqual), + comparisonMode(QOpenGLTexture::CompareNone), + minFilter(QOpenGLTexture::Nearest), + magFilter(QOpenGLTexture::Nearest), + maxAnisotropy(1.0f), + minLevelOfDetail(-1000.0f), + maxLevelOfDetail(1000.0f), + levelOfDetailBias(0.0f), + textureView(false), + autoGenerateMipMaps(true), + storageAllocated(false), + texFuncs(nullptr), + functions(nullptr) +{ + dimensions[0] = dimensions[1] = dimensions[2] = 1; + + switch (target) { + case QOpenGLTexture::Target1D: + bindingTarget = QOpenGLTexture::BindingTarget1D; + break; + case QOpenGLTexture::Target1DArray: + bindingTarget = QOpenGLTexture::BindingTarget1DArray; + break; + case QOpenGLTexture::Target2D: + bindingTarget = QOpenGLTexture::BindingTarget2D; + break; + case QOpenGLTexture::Target2DArray: + bindingTarget = QOpenGLTexture::BindingTarget2DArray; + break; + case QOpenGLTexture::Target3D: + bindingTarget = QOpenGLTexture::BindingTarget3D; + break; + case QOpenGLTexture::TargetCubeMap: + bindingTarget = QOpenGLTexture::BindingTargetCubeMap; + faces = 6; + break; + case QOpenGLTexture::TargetCubeMapArray: + bindingTarget = QOpenGLTexture::BindingTargetCubeMapArray; + faces = 6; + break; + case QOpenGLTexture::Target2DMultisample: + bindingTarget = QOpenGLTexture::BindingTarget2DMultisample; + break; + case QOpenGLTexture::Target2DMultisampleArray: + bindingTarget = QOpenGLTexture::BindingTarget2DMultisampleArray; + break; + case QOpenGLTexture::TargetRectangle: + bindingTarget = QOpenGLTexture::BindingTargetRectangle; + break; + case QOpenGLTexture::TargetBuffer: + bindingTarget = QOpenGLTexture::BindingTargetBuffer; + break; + } + + swizzleMask[0] = QOpenGLTexture::RedValue; + swizzleMask[1] = QOpenGLTexture::GreenValue; + swizzleMask[2] = QOpenGLTexture::BlueValue; + swizzleMask[3] = QOpenGLTexture::AlphaValue; + + wrapModes[0] = wrapModes[1] = wrapModes[2] = target == QOpenGLTexture::TargetRectangle + ? QOpenGLTexture::ClampToEdge : QOpenGLTexture::Repeat; +} + +QOpenGLTexturePrivate::~QOpenGLTexturePrivate() +{ + destroy(); +} + +void QOpenGLTexturePrivate::initializeOpenGLFunctions() +{ + // If we already have a functions object, there is nothing to do + if (texFuncs) + return; + + // See if the context already has a suitable resource we can use. + // If not create a functions object and add it to the context in case + // others wish to use it too + texFuncs = context->textureFunctions(); + if (!texFuncs) { + texFuncs = new QOpenGLTextureHelper(context); + auto *funcs = texFuncs; // lets us capture by pointer value below + context->setTextureFunctions(funcs, [funcs] { delete funcs; }); + } +} + +bool QOpenGLTexturePrivate::create() +{ + if (textureId != 0) + return true; + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning("Requires a valid current OpenGL context.\n" + "Texture has not been created"); + return false; + } + context = ctx; + functions = ctx->functions(); + + // Resolve any functions we will need based upon context version and create the texture + initializeOpenGLFunctions(); + + // What features do we have? + QOpenGLTexture::Feature feature = QOpenGLTexture::ImmutableStorage; + while (feature != QOpenGLTexture::MaxFeatureFlag) { + if (QOpenGLTexture::hasFeature(feature)) + features |= feature; + feature = static_cast<QOpenGLTexture::Feature>(feature << 1); + } + + functions->glGenTextures(1, &textureId); + return textureId != 0; +} + +void QOpenGLTexturePrivate::destroy() +{ + if (!textureId) { + // not created or already destroyed + return; + } + QOpenGLContext *currentContext = QOpenGLContext::currentContext(); + if (!currentContext) { + qWarning("QOpenGLTexturePrivate::destroy() called without a current context.\n" + "Texture has not been destroyed"); + return; + } + if (!QOpenGLContext::areSharing(currentContext, context)) { + + qWarning("QOpenGLTexturePrivate::destroy() called but texture context %p" + " is not shared with current context %p.\n" + "Texture has not been destroyed", + static_cast<const void *>(context), + static_cast<const void *>(currentContext)); + return; + } + + functions->glDeleteTextures(1, &textureId); + + context = nullptr; + functions = nullptr; + textureId = 0; + format = QOpenGLTexture::NoFormat; + formatClass = QOpenGLTexture::NoFormatClass; + requestedMipLevels = 1; + mipLevels = -1; + layers = 1; + faces = 1; + samples = 0; + fixedSamplePositions = true, + baseLevel = 0; + maxLevel = 1000; + depthStencilMode = QOpenGLTexture::DepthMode; + minFilter = QOpenGLTexture::Nearest; + magFilter = QOpenGLTexture::Nearest; + maxAnisotropy = 1.0f; + minLevelOfDetail = -1000.0f; + maxLevelOfDetail = 1000.0f; + levelOfDetailBias = 0.0f; + textureView = false; + autoGenerateMipMaps = true; + storageAllocated = false; + texFuncs = nullptr; + + swizzleMask[0] = QOpenGLTexture::RedValue; + swizzleMask[1] = QOpenGLTexture::GreenValue; + swizzleMask[2] = QOpenGLTexture::BlueValue; + swizzleMask[3] = QOpenGLTexture::AlphaValue; + + wrapModes[0] = wrapModes[1] = wrapModes[2] = target == QOpenGLTexture::TargetRectangle + ? QOpenGLTexture::ClampToEdge : QOpenGLTexture::Repeat; +} + +void QOpenGLTexturePrivate::bind() +{ + functions->glBindTexture(target, textureId); +} + +void QOpenGLTexturePrivate::bind(uint unit, QOpenGLTexture::TextureUnitReset reset) +{ + GLint oldTextureUnit = 0; + if (reset == QOpenGLTexture::ResetTextureUnit) + functions->glGetIntegerv(GL_ACTIVE_TEXTURE, &oldTextureUnit); + + texFuncs->glActiveTexture(GL_TEXTURE0 + unit); + functions->glBindTexture(target, textureId); + + if (reset == QOpenGLTexture::ResetTextureUnit) + texFuncs->glActiveTexture(GL_TEXTURE0 + oldTextureUnit); +} + +void QOpenGLTexturePrivate::release() +{ + functions->glBindTexture(target, 0); +} + +void QOpenGLTexturePrivate::release(uint unit, QOpenGLTexture::TextureUnitReset reset) +{ + GLint oldTextureUnit = 0; + if (reset == QOpenGLTexture::ResetTextureUnit) + functions->glGetIntegerv(GL_ACTIVE_TEXTURE, &oldTextureUnit); + + texFuncs->glActiveTexture(GL_TEXTURE0 + unit); + functions->glBindTexture(target, 0); + + if (reset == QOpenGLTexture::ResetTextureUnit) + texFuncs->glActiveTexture(GL_TEXTURE0 + oldTextureUnit); +} + +bool QOpenGLTexturePrivate::isBound() const +{ + GLint boundTextureId = 0; + functions->glGetIntegerv(bindingTarget, &boundTextureId); + return (static_cast<GLuint>(boundTextureId) == textureId); +} + +bool QOpenGLTexturePrivate::isBound(uint unit) const +{ + GLint oldTextureUnit = 0; + functions->glGetIntegerv(GL_ACTIVE_TEXTURE, &oldTextureUnit); + + GLint boundTextureId = 0; + texFuncs->glActiveTexture(GL_TEXTURE0 + unit); + functions->glGetIntegerv(bindingTarget, &boundTextureId); + bool result = (static_cast<GLuint>(boundTextureId) == textureId); + + texFuncs->glActiveTexture(GL_TEXTURE0 + oldTextureUnit); + return result; +} + +int QOpenGLTexturePrivate::evaluateMipLevels() const +{ + switch (target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + return qMin(maximumMipLevelCount(), qMax(1, requestedMipLevels)); + + case QOpenGLTexture::TargetRectangle: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetBuffer: + default: + return 1; + } +} + +static bool isSizedTextureFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + return false; + + case QOpenGLTexture::R8_UNorm: + case QOpenGLTexture::RG8_UNorm: + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::R16_UNorm: + case QOpenGLTexture::RG16_UNorm: + case QOpenGLTexture::RGB16_UNorm: + case QOpenGLTexture::RGBA16_UNorm: + case QOpenGLTexture::R8_SNorm: + case QOpenGLTexture::RG8_SNorm: + case QOpenGLTexture::RGB8_SNorm: + case QOpenGLTexture::RGBA8_SNorm: + case QOpenGLTexture::R16_SNorm: + case QOpenGLTexture::RG16_SNorm: + case QOpenGLTexture::RGB16_SNorm: + case QOpenGLTexture::RGBA16_SNorm: + case QOpenGLTexture::R8U: + case QOpenGLTexture::RG8U: + case QOpenGLTexture::RGB8U: + case QOpenGLTexture::RGBA8U: + case QOpenGLTexture::R16U: + case QOpenGLTexture::RG16U: + case QOpenGLTexture::RGB16U: + case QOpenGLTexture::RGBA16U: + case QOpenGLTexture::R32U: + case QOpenGLTexture::RG32U: + case QOpenGLTexture::RGB32U: + case QOpenGLTexture::RGBA32U: + case QOpenGLTexture::R8I: + case QOpenGLTexture::RG8I: + case QOpenGLTexture::RGB8I: + case QOpenGLTexture::RGBA8I: + case QOpenGLTexture::R16I: + case QOpenGLTexture::RG16I: + case QOpenGLTexture::RGB16I: + case QOpenGLTexture::RGBA16I: + case QOpenGLTexture::R32I: + case QOpenGLTexture::RG32I: + case QOpenGLTexture::RGB32I: + case QOpenGLTexture::RGBA32I: + case QOpenGLTexture::R16F: + case QOpenGLTexture::RG16F: + case QOpenGLTexture::RGB16F: + case QOpenGLTexture::RGBA16F: + case QOpenGLTexture::R32F: + case QOpenGLTexture::RG32F: + case QOpenGLTexture::RGB32F: + case QOpenGLTexture::RGBA32F: + case QOpenGLTexture::RGB9E5: + case QOpenGLTexture::RG11B10F: + case QOpenGLTexture::RG3B2: + case QOpenGLTexture::R5G6B5: + case QOpenGLTexture::RGB5A1: + case QOpenGLTexture::RGBA4: + case QOpenGLTexture::RGB10A2: + + case QOpenGLTexture::D16: + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + case QOpenGLTexture::D32F: + + case QOpenGLTexture::D24S8: + case QOpenGLTexture::D32FS8X24: + + case QOpenGLTexture::S8: + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + case QOpenGLTexture::RG11_EAC_UNorm: + case QOpenGLTexture::RG11_EAC_SNorm: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC: + case QOpenGLTexture::RGBA_ASTC_4x4: + case QOpenGLTexture::RGBA_ASTC_5x4: + case QOpenGLTexture::RGBA_ASTC_5x5: + case QOpenGLTexture::RGBA_ASTC_6x5: + case QOpenGLTexture::RGBA_ASTC_6x6: + case QOpenGLTexture::RGBA_ASTC_8x5: + case QOpenGLTexture::RGBA_ASTC_8x6: + case QOpenGLTexture::RGBA_ASTC_8x8: + case QOpenGLTexture::RGBA_ASTC_10x5: + case QOpenGLTexture::RGBA_ASTC_10x6: + case QOpenGLTexture::RGBA_ASTC_10x8: + case QOpenGLTexture::RGBA_ASTC_10x10: + case QOpenGLTexture::RGBA_ASTC_12x10: + case QOpenGLTexture::RGBA_ASTC_12x12: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12: + return true; + + case QOpenGLTexture::RGB8_ETC1: + return false; + + case QOpenGLTexture::DepthFormat: + case QOpenGLTexture::AlphaFormat: + + case QOpenGLTexture::RGBFormat: + case QOpenGLTexture::RGBAFormat: + + case QOpenGLTexture::LuminanceFormat: + + case QOpenGLTexture::LuminanceAlphaFormat: + return false; + } + + Q_UNREACHABLE(); + return false; +} + +static bool isTextureTargetMultisample(QOpenGLTexture::Target target) +{ + switch (target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + return false; + + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + return true; + + case QOpenGLTexture::TargetRectangle: + case QOpenGLTexture::TargetBuffer: + return false; + } + + Q_UNREACHABLE(); + return false; +} + +bool QOpenGLTexturePrivate::isUsingImmutableStorage() const +{ + // Use immutable storage whenever possible, falling back to mutable + // Note that if multisample textures are not supported at all, we'll still fail into + // the mutable storage allocation + return isSizedTextureFormat(format) + && (isTextureTargetMultisample(target) + ? features.testFlag(QOpenGLTexture::ImmutableMultisampleStorage) + : features.testFlag(QOpenGLTexture::ImmutableStorage)); +} + +void QOpenGLTexturePrivate::allocateStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType) +{ + // Resolve the actual number of mipmap levels we can use + mipLevels = evaluateMipLevels(); + + if (isUsingImmutableStorage()) + allocateImmutableStorage(); + else + allocateMutableStorage(pixelFormat, pixelType); +} + +static QOpenGLTexture::PixelFormat pixelFormatCompatibleWithInternalFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + return QOpenGLTexture::NoSourceFormat; + + case QOpenGLTexture::R8_UNorm: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG8_UNorm: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB8_UNorm: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBA8_UNorm: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::R16_UNorm: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG16_UNorm: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB16_UNorm: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBA16_UNorm: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::R8_SNorm: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG8_SNorm: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB8_SNorm: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBA8_SNorm: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::R16_SNorm: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG16_SNorm: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB16_SNorm: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBA16_SNorm: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::R8U: + return QOpenGLTexture::Red_Integer; + + case QOpenGLTexture::RG8U: + return QOpenGLTexture::RG_Integer; + + case QOpenGLTexture::RGB8U: + return QOpenGLTexture::RGB_Integer; + + case QOpenGLTexture::RGBA8U: + return QOpenGLTexture::RGBA_Integer; + + case QOpenGLTexture::R16U: + return QOpenGLTexture::Red_Integer; + + case QOpenGLTexture::RG16U: + return QOpenGLTexture::RG_Integer; + + case QOpenGLTexture::RGB16U: + return QOpenGLTexture::RGB_Integer; + + case QOpenGLTexture::RGBA16U: + return QOpenGLTexture::RGBA_Integer; + + case QOpenGLTexture::R32U: + return QOpenGLTexture::Red_Integer; + + case QOpenGLTexture::RG32U: + return QOpenGLTexture::RG_Integer; + + case QOpenGLTexture::RGB32U: + return QOpenGLTexture::RGB_Integer; + + case QOpenGLTexture::RGBA32U: + return QOpenGLTexture::RGBA_Integer; + + case QOpenGLTexture::R8I: + return QOpenGLTexture::Red_Integer; + + case QOpenGLTexture::RG8I: + return QOpenGLTexture::RG_Integer; + + case QOpenGLTexture::RGB8I: + return QOpenGLTexture::RGB_Integer; + + case QOpenGLTexture::RGBA8I: + return QOpenGLTexture::RGBA_Integer; + + case QOpenGLTexture::R16I: + return QOpenGLTexture::Red_Integer; + + case QOpenGLTexture::RG16I: + return QOpenGLTexture::RG_Integer; + + case QOpenGLTexture::RGB16I: + return QOpenGLTexture::RGB_Integer; + + case QOpenGLTexture::RGBA16I: + return QOpenGLTexture::RGBA_Integer; + + case QOpenGLTexture::R32I: + return QOpenGLTexture::Red_Integer; + + case QOpenGLTexture::RG32I: + return QOpenGLTexture::RG_Integer; + + case QOpenGLTexture::RGB32I: + return QOpenGLTexture::RGB_Integer; + + case QOpenGLTexture::RGBA32I: + return QOpenGLTexture::RGBA_Integer; + + case QOpenGLTexture::R16F: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG16F: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB16F: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBA16F: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::R32F: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG32F: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB32F: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBA32F: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::RGB9E5: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RG11B10F: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RG3B2: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::R5G6B5: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGB5A1: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::RGBA4: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::RGB10A2: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::D16: + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + case QOpenGLTexture::D32F: + return QOpenGLTexture::Depth; + + case QOpenGLTexture::D24S8: + case QOpenGLTexture::D32FS8X24: + return QOpenGLTexture::DepthStencil; + + case QOpenGLTexture::S8: + return QOpenGLTexture::Stencil; + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + case QOpenGLTexture::RGB8_ETC1: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + return QOpenGLTexture::Red; + + case QOpenGLTexture::RG11_EAC_UNorm: + case QOpenGLTexture::RG11_EAC_SNorm: + return QOpenGLTexture::RG; + + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::RGBA_ASTC_4x4: + case QOpenGLTexture::RGBA_ASTC_5x4: + case QOpenGLTexture::RGBA_ASTC_5x5: + case QOpenGLTexture::RGBA_ASTC_6x5: + case QOpenGLTexture::RGBA_ASTC_6x6: + case QOpenGLTexture::RGBA_ASTC_8x5: + case QOpenGLTexture::RGBA_ASTC_8x6: + case QOpenGLTexture::RGBA_ASTC_8x8: + case QOpenGLTexture::RGBA_ASTC_10x5: + case QOpenGLTexture::RGBA_ASTC_10x6: + case QOpenGLTexture::RGBA_ASTC_10x8: + case QOpenGLTexture::RGBA_ASTC_10x10: + case QOpenGLTexture::RGBA_ASTC_12x10: + case QOpenGLTexture::RGBA_ASTC_12x12: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::DepthFormat: + return QOpenGLTexture::Depth; + + case QOpenGLTexture::AlphaFormat: + return QOpenGLTexture::Alpha; + + case QOpenGLTexture::RGBFormat: + return QOpenGLTexture::RGB; + + case QOpenGLTexture::RGBAFormat: + return QOpenGLTexture::RGBA; + + case QOpenGLTexture::LuminanceFormat: + return QOpenGLTexture::Luminance; + + case QOpenGLTexture::LuminanceAlphaFormat: + return QOpenGLTexture::LuminanceAlpha; + } + + Q_UNREACHABLE(); + return QOpenGLTexture::NoSourceFormat; +} + +static QOpenGLTexture::PixelType pixelTypeCompatibleWithInternalFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + return QOpenGLTexture::NoPixelType; + + case QOpenGLTexture::R8_UNorm: + case QOpenGLTexture::RG8_UNorm: + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::R16_UNorm: + case QOpenGLTexture::RG16_UNorm: + case QOpenGLTexture::RGB16_UNorm: + case QOpenGLTexture::RGBA16_UNorm: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::R8_SNorm: + case QOpenGLTexture::RG8_SNorm: + case QOpenGLTexture::RGB8_SNorm: + case QOpenGLTexture::RGBA8_SNorm: + case QOpenGLTexture::R16_SNorm: + case QOpenGLTexture::RG16_SNorm: + case QOpenGLTexture::RGB16_SNorm: + case QOpenGLTexture::RGBA16_SNorm: + return QOpenGLTexture::Int8; + + case QOpenGLTexture::R8U: + case QOpenGLTexture::RG8U: + case QOpenGLTexture::RGB8U: + case QOpenGLTexture::RGBA8U: + case QOpenGLTexture::R16U: + case QOpenGLTexture::RG16U: + case QOpenGLTexture::RGB16U: + case QOpenGLTexture::RGBA16U: + case QOpenGLTexture::R32U: + case QOpenGLTexture::RG32U: + case QOpenGLTexture::RGB32U: + case QOpenGLTexture::RGBA32U: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::R8I: + case QOpenGLTexture::RG8I: + case QOpenGLTexture::RGB8I: + case QOpenGLTexture::RGBA8I: + case QOpenGLTexture::R16I: + case QOpenGLTexture::RG16I: + case QOpenGLTexture::RGB16I: + case QOpenGLTexture::RGBA16I: + case QOpenGLTexture::R32I: + case QOpenGLTexture::RG32I: + case QOpenGLTexture::RGB32I: + case QOpenGLTexture::RGBA32I: + return QOpenGLTexture::Int8; + + case QOpenGLTexture::R16F: + case QOpenGLTexture::RG16F: + case QOpenGLTexture::RGB16F: + case QOpenGLTexture::RGBA16F: + return QOpenGLTexture::Float16; + + case QOpenGLTexture::R32F: + case QOpenGLTexture::RG32F: + case QOpenGLTexture::RGB32F: + case QOpenGLTexture::RGBA32F: + return QOpenGLTexture::Float32; + + case QOpenGLTexture::RGB9E5: + return QOpenGLTexture::UInt16_RGB5A1_Rev; + + case QOpenGLTexture::RG11B10F: + return QOpenGLTexture::UInt32_RG11B10F; + + case QOpenGLTexture::RG3B2: + return QOpenGLTexture::UInt8_RG3B2; + + case QOpenGLTexture::R5G6B5: + return QOpenGLTexture::UInt16_R5G6B5; + + case QOpenGLTexture::RGB5A1: + return QOpenGLTexture::UInt16_RGB5A1; + + case QOpenGLTexture::RGBA4: + return QOpenGLTexture::UInt16_RGBA4; + + case QOpenGLTexture::RGB10A2: + return QOpenGLTexture::UInt32_RGB10A2; + + case QOpenGLTexture::D16: + return QOpenGLTexture::UInt16; + + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + return QOpenGLTexture::UInt32; + + case QOpenGLTexture::D32F: + return QOpenGLTexture::Float32; + + case QOpenGLTexture::D24S8: + return QOpenGLTexture::UInt32_D24S8; + + case QOpenGLTexture::D32FS8X24: + return QOpenGLTexture::Float32_D32_UInt32_S8_X24; + + case QOpenGLTexture::S8: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + case QOpenGLTexture::RG11_EAC_UNorm: + case QOpenGLTexture::RG11_EAC_SNorm: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC: + case QOpenGLTexture::RGB8_ETC1: + case QOpenGLTexture::RGBA_ASTC_4x4: + case QOpenGLTexture::RGBA_ASTC_5x4: + case QOpenGLTexture::RGBA_ASTC_5x5: + case QOpenGLTexture::RGBA_ASTC_6x5: + case QOpenGLTexture::RGBA_ASTC_6x6: + case QOpenGLTexture::RGBA_ASTC_8x5: + case QOpenGLTexture::RGBA_ASTC_8x6: + case QOpenGLTexture::RGBA_ASTC_8x8: + case QOpenGLTexture::RGBA_ASTC_10x5: + case QOpenGLTexture::RGBA_ASTC_10x6: + case QOpenGLTexture::RGBA_ASTC_10x8: + case QOpenGLTexture::RGBA_ASTC_10x10: + case QOpenGLTexture::RGBA_ASTC_12x10: + case QOpenGLTexture::RGBA_ASTC_12x12: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12: + return QOpenGLTexture::UInt8; + + case QOpenGLTexture::DepthFormat: + return QOpenGLTexture::UInt32; + + case QOpenGLTexture::AlphaFormat: + case QOpenGLTexture::RGBFormat: + case QOpenGLTexture::RGBAFormat: + case QOpenGLTexture::LuminanceFormat: + case QOpenGLTexture::LuminanceAlphaFormat: + return QOpenGLTexture::UInt8; + } + + Q_UNREACHABLE(); + return QOpenGLTexture::NoPixelType; +} + +static bool isCompressedFormat(QOpenGLTexture::TextureFormat internalFormat) +{ + switch (internalFormat) { + case QOpenGLTexture::NoFormat: + + case QOpenGLTexture::R8_UNorm: + case QOpenGLTexture::RG8_UNorm: + case QOpenGLTexture::RGB8_UNorm: + case QOpenGLTexture::RGBA8_UNorm: + case QOpenGLTexture::R16_UNorm: + case QOpenGLTexture::RG16_UNorm: + case QOpenGLTexture::RGB16_UNorm: + case QOpenGLTexture::RGBA16_UNorm: + case QOpenGLTexture::R8_SNorm: + case QOpenGLTexture::RG8_SNorm: + case QOpenGLTexture::RGB8_SNorm: + case QOpenGLTexture::RGBA8_SNorm: + case QOpenGLTexture::R16_SNorm: + case QOpenGLTexture::RG16_SNorm: + case QOpenGLTexture::RGB16_SNorm: + case QOpenGLTexture::RGBA16_SNorm: + case QOpenGLTexture::R8U: + case QOpenGLTexture::RG8U: + case QOpenGLTexture::RGB8U: + case QOpenGLTexture::RGBA8U: + case QOpenGLTexture::R16U: + case QOpenGLTexture::RG16U: + case QOpenGLTexture::RGB16U: + case QOpenGLTexture::RGBA16U: + case QOpenGLTexture::R32U: + case QOpenGLTexture::RG32U: + case QOpenGLTexture::RGB32U: + case QOpenGLTexture::RGBA32U: + case QOpenGLTexture::R8I: + case QOpenGLTexture::RG8I: + case QOpenGLTexture::RGB8I: + case QOpenGLTexture::RGBA8I: + case QOpenGLTexture::R16I: + case QOpenGLTexture::RG16I: + case QOpenGLTexture::RGB16I: + case QOpenGLTexture::RGBA16I: + case QOpenGLTexture::R32I: + case QOpenGLTexture::RG32I: + case QOpenGLTexture::RGB32I: + case QOpenGLTexture::RGBA32I: + case QOpenGLTexture::R16F: + case QOpenGLTexture::RG16F: + case QOpenGLTexture::RGB16F: + case QOpenGLTexture::RGBA16F: + case QOpenGLTexture::R32F: + case QOpenGLTexture::RG32F: + case QOpenGLTexture::RGB32F: + case QOpenGLTexture::RGBA32F: + case QOpenGLTexture::RGB9E5: + case QOpenGLTexture::RG11B10F: + case QOpenGLTexture::RG3B2: + case QOpenGLTexture::R5G6B5: + case QOpenGLTexture::RGB5A1: + case QOpenGLTexture::RGBA4: + case QOpenGLTexture::RGB10A2: + + case QOpenGLTexture::D16: + case QOpenGLTexture::D24: + case QOpenGLTexture::D32: + case QOpenGLTexture::D32F: + + case QOpenGLTexture::D24S8: + case QOpenGLTexture::D32FS8X24: + + case QOpenGLTexture::S8: + return false; + + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::RGBA_DXT1: + case QOpenGLTexture::RGBA_DXT3: + case QOpenGLTexture::RGBA_DXT5: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_UNorm: + case QOpenGLTexture::SRGB8: + case QOpenGLTexture::SRGB8_Alpha8: + case QOpenGLTexture::SRGB_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT1: + case QOpenGLTexture::SRGB_Alpha_DXT3: + case QOpenGLTexture::SRGB_Alpha_DXT5: + case QOpenGLTexture::SRGB_BP_UNorm: + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + case QOpenGLTexture::RG11_EAC_UNorm: + case QOpenGLTexture::RG11_EAC_SNorm: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC: + case QOpenGLTexture::RGB8_ETC1: + case QOpenGLTexture::RGBA_ASTC_4x4: + case QOpenGLTexture::RGBA_ASTC_5x4: + case QOpenGLTexture::RGBA_ASTC_5x5: + case QOpenGLTexture::RGBA_ASTC_6x5: + case QOpenGLTexture::RGBA_ASTC_6x6: + case QOpenGLTexture::RGBA_ASTC_8x5: + case QOpenGLTexture::RGBA_ASTC_8x6: + case QOpenGLTexture::RGBA_ASTC_8x8: + case QOpenGLTexture::RGBA_ASTC_10x5: + case QOpenGLTexture::RGBA_ASTC_10x6: + case QOpenGLTexture::RGBA_ASTC_10x8: + case QOpenGLTexture::RGBA_ASTC_10x10: + case QOpenGLTexture::RGBA_ASTC_12x10: + case QOpenGLTexture::RGBA_ASTC_12x12: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12: + return true; + + case QOpenGLTexture::DepthFormat: + case QOpenGLTexture::AlphaFormat: + case QOpenGLTexture::RGBFormat: + case QOpenGLTexture::RGBAFormat: + case QOpenGLTexture::LuminanceFormat: + case QOpenGLTexture::LuminanceAlphaFormat: + return false; + } + + Q_UNREACHABLE(); + return false; +} + +void QOpenGLTexturePrivate::allocateMutableStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType) +{ + // There is no way to allocate mutable storage for compressed textures in in + // versions older than OpenGL 3.1 and OpenGL ES 3.0, because the older specs + // do not mandate accepting null data pointers for glCompressedTexImage*D, + // unlike glTexImage*D (which in turn does not accept compressed formats). + if (isCompressedFormat(format)) { + storageAllocated = true; + return; + } + + switch (target) { + case QOpenGLTexture::TargetBuffer: + // Buffer textures get their storage from an external OpenGL buffer + qWarning("Buffer textures do not allocate storage"); + return; + + case QOpenGLTexture::Target1D: + if (features.testFlag(QOpenGLTexture::Texture1D)) { + for (int level = 0; level < mipLevels; ++level) + texFuncs->glTextureImage1D(textureId, target, bindingTarget, level, format, + mipLevelSize(level, dimensions[0]), + 0, + pixelFormat, pixelType, nullptr); + } else { + qWarning("1D textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target1DArray: + if (features.testFlag(QOpenGLTexture::Texture1D) + && features.testFlag(QOpenGLTexture::TextureArrays)) { + for (int level = 0; level < mipLevels; ++level) + texFuncs->glTextureImage2D(textureId, target, bindingTarget, level, format, + mipLevelSize(level, dimensions[0]), + layers, + 0, + pixelFormat, pixelType, nullptr); + } else { + qWarning("1D array textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::TargetRectangle: + for (int level = 0; level < mipLevels; ++level) + texFuncs->glTextureImage2D(textureId, target, bindingTarget, level, format, + mipLevelSize(level, dimensions[0]), + mipLevelSize(level, dimensions[1]), + 0, + pixelFormat, pixelType, nullptr); + break; + + case QOpenGLTexture::TargetCubeMap: { + // Cubemaps are the odd one out. We have to allocate storage for each + // face and miplevel using the special cubemap face targets rather than + // GL_TARGET_CUBEMAP. + const QOpenGLTexture::CubeMapFace faceTargets[] = { + QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapNegativeX, + QOpenGLTexture::CubeMapPositiveY, QOpenGLTexture::CubeMapNegativeY, + QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeZ + }; + + for (int faceTarget = 0; faceTarget < 6; ++faceTarget) { + for (int level = 0; level < mipLevels; ++level) { + texFuncs->glTextureImage2D(textureId, faceTargets[faceTarget], bindingTarget, + level, format, + mipLevelSize(level, dimensions[0]), + mipLevelSize(level, dimensions[1]), + 0, + pixelFormat, pixelType, nullptr); + } + } + break; + } + + case QOpenGLTexture::Target2DArray: + if (features.testFlag(QOpenGLTexture::TextureArrays)) { + for (int level = 0; level < mipLevels; ++level) + texFuncs->glTextureImage3D(textureId, target, bindingTarget, level, format, + mipLevelSize(level, dimensions[0]), + mipLevelSize(level, dimensions[1]), + layers, + 0, + pixelFormat, pixelType, nullptr); + } else { + qWarning("Array textures are not supported"); + return; + } + break; + + case QOpenGLTexture::TargetCubeMapArray: + // Cubemap arrays must specify number of layer-faces (6 * layers) as depth parameter + if (features.testFlag(QOpenGLTexture::TextureCubeMapArrays)) { + for (int level = 0; level < mipLevels; ++level) + texFuncs->glTextureImage3D(textureId, target, bindingTarget, level, format, + mipLevelSize(level, dimensions[0]), + mipLevelSize(level, dimensions[1]), + 6 * layers, + 0, + pixelFormat, pixelType, nullptr); + } else { + qWarning("Cubemap Array textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target3D: + if (features.testFlag(QOpenGLTexture::Texture3D)) { + for (int level = 0; level < mipLevels; ++level) + texFuncs->glTextureImage3D(textureId, target, bindingTarget, level, format, + mipLevelSize(level, dimensions[0]), + mipLevelSize(level, dimensions[1]), + mipLevelSize(level, dimensions[2]), + 0, + pixelFormat, pixelType, nullptr); + } else { + qWarning("3D textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target2DMultisample: + if (features.testFlag(QOpenGLTexture::TextureMultisample)) { + texFuncs->glTextureImage2DMultisample(textureId, target, bindingTarget, samples, format, + dimensions[0], dimensions[1], + fixedSamplePositions); + } else { + qWarning("Multisample textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target2DMultisampleArray: + if (features.testFlag(QOpenGLTexture::TextureMultisample) + && features.testFlag(QOpenGLTexture::TextureArrays)) { + texFuncs->glTextureImage3DMultisample(textureId, target, bindingTarget, samples, format, + dimensions[0], dimensions[1], layers, + fixedSamplePositions); + } else { + qWarning("Multisample array textures are not supported"); + return; + } + break; + } + + storageAllocated = true; +} + +void QOpenGLTexturePrivate::allocateImmutableStorage() +{ + switch (target) { + case QOpenGLTexture::TargetBuffer: + // Buffer textures get their storage from an external OpenGL buffer + qWarning("Buffer textures do not allocate storage"); + return; + + case QOpenGLTexture::Target1D: + if (features.testFlag(QOpenGLTexture::Texture1D)) { + texFuncs->glTextureStorage1D(textureId, target, bindingTarget, mipLevels, format, + dimensions[0]); + } else { + qWarning("1D textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target1DArray: + if (features.testFlag(QOpenGLTexture::Texture1D) + && features.testFlag(QOpenGLTexture::TextureArrays)) { + texFuncs->glTextureStorage2D(textureId, target, bindingTarget, mipLevels, format, + dimensions[0], layers); + } else { + qWarning("1D array textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetRectangle: + texFuncs->glTextureStorage2D(textureId, target, bindingTarget, mipLevels, format, + dimensions[0], dimensions[1]); + break; + + case QOpenGLTexture::Target2DArray: + if (features.testFlag(QOpenGLTexture::TextureArrays)) { + texFuncs->glTextureStorage3D(textureId, target, bindingTarget, mipLevels, format, + dimensions[0], dimensions[1], layers); + } else { + qWarning("Array textures are not supported"); + return; + } + break; + + case QOpenGLTexture::TargetCubeMapArray: + // Cubemap arrays must specify number of layer-faces (6 * layers) as depth parameter + if (features.testFlag(QOpenGLTexture::TextureCubeMapArrays)) { + texFuncs->glTextureStorage3D(textureId, target, bindingTarget, mipLevels, format, + dimensions[0], dimensions[1], 6 * layers); + } else { + qWarning("Cubemap Array textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target3D: + if (features.testFlag(QOpenGLTexture::Texture3D)) { + texFuncs->glTextureStorage3D(textureId, target, bindingTarget, mipLevels, format, + dimensions[0], dimensions[1], dimensions[2]); + } else { + qWarning("3D textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target2DMultisample: + if (features.testFlag(QOpenGLTexture::ImmutableMultisampleStorage)) { + texFuncs->glTextureStorage2DMultisample(textureId, target, bindingTarget, samples, format, + dimensions[0], dimensions[1], + fixedSamplePositions); + } else { + qWarning("Multisample textures are not supported"); + return; + } + break; + + case QOpenGLTexture::Target2DMultisampleArray: + if (features.testFlag(QOpenGLTexture::ImmutableMultisampleStorage) + && features.testFlag(QOpenGLTexture::TextureArrays)) { + texFuncs->glTextureStorage3DMultisample(textureId, target, bindingTarget, samples, format, + dimensions[0], dimensions[1], layers, + fixedSamplePositions); + } else { + qWarning("Multisample array textures are not supported"); + return; + } + break; + } + + storageAllocated = true; +} + +void QOpenGLTexturePrivate::setData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, + QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + switch (target) { + case QOpenGLTexture::Target1D: + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + texFuncs->glTextureSubImage1D(textureId, target, bindingTarget, mipLevel, + 0, mipLevelSize( mipLevel, dimensions[0] ), + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target1DArray: + Q_UNUSED(cubeFace); + texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, mipLevel, + 0, layer, + mipLevelSize(mipLevel, dimensions[0]), + layerCount, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target2D: + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, mipLevel, + 0, 0, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target2DArray: + Q_UNUSED(cubeFace); + texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + 0, 0, layer, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + layerCount, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target3D: + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + 0, 0, layer, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + mipLevelSize(mipLevel, dimensions[2]), + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::TargetCubeMap: + Q_UNUSED(layer); + Q_UNUSED(layerCount); + texFuncs->glTextureSubImage2D(textureId, cubeFace, bindingTarget, mipLevel, + 0, 0, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::TargetCubeMapArray: { + int faceIndex = cubeFace - QOpenGLTexture::CubeMapPositiveX; + int layerFace = 6 * layer + faceIndex; + texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + 0, 0, layerFace, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + layerCount, + sourceFormat, sourceType, data, options); + break; + } + + case QOpenGLTexture::TargetRectangle: + Q_UNUSED(mipLevel); + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, 0, + 0, 0, + dimensions[0], + dimensions[1], + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetBuffer: + // We don't upload pixel data for these targets + qWarning("QOpenGLTexture::setData(): Texture target does not support pixel data upload"); + break; + } + + // If requested perform automatic mip map generation + if (mipLevel == 0 && autoGenerateMipMaps && mipLevels > 1) { + Q_Q(QOpenGLTexture); + q->generateMipMaps(); + } +} + +void QOpenGLTexturePrivate::setData(int xOffset, int yOffset, int zOffset, int width, int height, int depth, + int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, + QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + switch (target) { + case QOpenGLTexture::Target1D: + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + Q_UNUSED(yOffset); + Q_UNUSED(zOffset); + Q_UNUSED(height); + Q_UNUSED(depth); + texFuncs->glTextureSubImage1D(textureId, target, bindingTarget, mipLevel, + xOffset, width, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target1DArray: + Q_UNUSED(cubeFace); + Q_UNUSED(yOffset); + Q_UNUSED(zOffset); + Q_UNUSED(height); + Q_UNUSED(depth); + texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, mipLevel, + xOffset, layer, + width, + layerCount, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target2D: + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + Q_UNUSED(zOffset); + Q_UNUSED(depth); + texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, mipLevel, + xOffset, yOffset, + width, height, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target2DArray: + Q_UNUSED(cubeFace); + Q_UNUSED(zOffset); + Q_UNUSED(depth); + texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + xOffset, yOffset, layer, + width, height, layerCount, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target3D: + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + xOffset, yOffset, zOffset, + width, height, depth, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::TargetCubeMap: + Q_UNUSED(layer); + Q_UNUSED(layerCount); + Q_UNUSED(zOffset); + Q_UNUSED(depth); + texFuncs->glTextureSubImage2D(textureId, cubeFace, bindingTarget, mipLevel, + xOffset, yOffset, + width, height, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::TargetCubeMapArray: { + Q_UNUSED(zOffset); + Q_UNUSED(depth); + int faceIndex = cubeFace - QOpenGLTexture::CubeMapPositiveX; + int layerFace = 6 * layer + faceIndex; + texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + xOffset, yOffset, layerFace, + width, height, + layerCount, + sourceFormat, sourceType, data, options); + break; + } + + case QOpenGLTexture::TargetRectangle: + Q_UNUSED(mipLevel); + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + Q_UNUSED(zOffset); + Q_UNUSED(depth); + texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, 0, + xOffset, yOffset, + width, height, + sourceFormat, sourceType, data, options); + break; + + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetBuffer: + // We don't upload pixel data for these targets + qWarning("QOpenGLTexture::setData(): Texture target does not support pixel data upload"); + break; + } + + // If requested perform automatic mip map generation + if (mipLevel == 0 && autoGenerateMipMaps && mipLevels > 1) { + Q_Q(QOpenGLTexture); + q->generateMipMaps(); + } +} + + +void QOpenGLTexturePrivate::setCompressedData(int mipLevel, int layer, int layerCount, + QOpenGLTexture::CubeMapFace cubeFace, + int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options) +{ + if (!isCompressedFormat(format)) { + qWarning("Cannot set compressed data for non-compressed format 0x%x", format); + return; + } + + const bool needsFullSpec = !isUsingImmutableStorage(); // was allocateStorage() a no-op? + + switch (target) { + case QOpenGLTexture::Target1D: + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + if (needsFullSpec) { + texFuncs->glCompressedTextureImage1D(textureId, target, bindingTarget, mipLevel, + format, + mipLevelSize(mipLevel, dimensions[0]), + 0, dataSize, data, options); + } else { + texFuncs->glCompressedTextureSubImage1D(textureId, target, bindingTarget, mipLevel, + 0, mipLevelSize( mipLevel, dimensions[0] ), + format, dataSize, data, options); + } + break; + + case QOpenGLTexture::Target1DArray: + Q_UNUSED(cubeFace); + if (!needsFullSpec) { + texFuncs->glCompressedTextureSubImage2D(textureId, target, bindingTarget, mipLevel, + 0, layer, + mipLevelSize(mipLevel, dimensions[0]), + layerCount, + format, dataSize, data, options); + } + break; + + case QOpenGLTexture::Target2D: + Q_UNUSED(layer); + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + if (needsFullSpec) { + texFuncs->glCompressedTextureImage2D(textureId, target, bindingTarget, mipLevel, + format, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + 0, dataSize, data, options); + } else { + texFuncs->glCompressedTextureSubImage2D(textureId, target, bindingTarget, mipLevel, + 0, 0, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + format, dataSize, data, options); + } + break; + + case QOpenGLTexture::Target2DArray: + Q_UNUSED(cubeFace); + if (!needsFullSpec) { + texFuncs->glCompressedTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + 0, 0, layer, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + layerCount, + format, dataSize, data, options); + } + break; + + case QOpenGLTexture::Target3D: + Q_UNUSED(cubeFace); + Q_UNUSED(layerCount); + if (needsFullSpec) { + texFuncs->glCompressedTextureImage3D(textureId, target, bindingTarget, mipLevel, + format, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + mipLevelSize(mipLevel, dimensions[2]), + 0, dataSize, data, options); + } else { + texFuncs->glCompressedTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + 0, 0, layer, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + mipLevelSize(mipLevel, dimensions[2]), + format, dataSize, data, options); + } + break; + + case QOpenGLTexture::TargetCubeMap: + Q_UNUSED(layer); + Q_UNUSED(layerCount); + if (needsFullSpec) { + texFuncs->glCompressedTextureImage2D(textureId, cubeFace, bindingTarget, mipLevel, + format, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + 0, dataSize, data, options); + } else { + texFuncs->glCompressedTextureSubImage2D(textureId, cubeFace, bindingTarget, mipLevel, + 0, 0, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + format, dataSize, data, options); + } + break; + + case QOpenGLTexture::TargetCubeMapArray: { + int faceIndex = cubeFace - QOpenGLTexture::CubeMapPositiveX; + int layerFace = 6 * layer + faceIndex; + if (!needsFullSpec) { + texFuncs->glCompressedTextureSubImage3D(textureId, target, bindingTarget, mipLevel, + 0, 0, layerFace, + mipLevelSize(mipLevel, dimensions[0]), + mipLevelSize(mipLevel, dimensions[1]), + layerCount, + format, dataSize, data, options); + } + break; + } + + case QOpenGLTexture::TargetRectangle: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetBuffer: + // We don't upload pixel data for these targets + qWarning("QOpenGLTexture::setCompressedData(): Texture target does not support pixel data upload"); + break; + } + + // If requested perform automatic mip map generation + if (mipLevel == 0 && autoGenerateMipMaps && mipLevels > 1) { + Q_Q(QOpenGLTexture); + q->generateMipMaps(); + } +} + +void QOpenGLTexturePrivate::setWrapMode(QOpenGLTexture::WrapMode mode) +{ + switch (target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::TargetBuffer: + wrapModes[0] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_S, mode); + break; + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetRectangle: + wrapModes[0] = wrapModes[1] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_S, mode); + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_T, mode); + break; + + case QOpenGLTexture::Target3D: + wrapModes[0] = wrapModes[1] = wrapModes[2] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_S, mode); + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_T, mode); + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_R, mode); + break; + } +} + +void QOpenGLTexturePrivate::setWrapMode(QOpenGLTexture::CoordinateDirection direction, QOpenGLTexture::WrapMode mode) +{ + switch (target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::TargetBuffer: + switch (direction) { + case QOpenGLTexture::DirectionS: + wrapModes[0] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_S, mode); + break; + + case QOpenGLTexture::DirectionT: + case QOpenGLTexture::DirectionR: + qWarning("QOpenGLTexture::setWrapMode() direction not valid for this texture target"); + break; + } + break; + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetRectangle: + switch (direction) { + case QOpenGLTexture::DirectionS: + wrapModes[0] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_S, mode); + break; + + case QOpenGLTexture::DirectionT: + wrapModes[1] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, GL_TEXTURE_WRAP_T, mode); + break; + + case QOpenGLTexture::DirectionR: + qWarning("QOpenGLTexture::setWrapMode() direction not valid for this texture target"); + break; + } + break; + + case QOpenGLTexture::Target3D: + switch (direction) { + case QOpenGLTexture::DirectionS: + wrapModes[0] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, direction, mode); + break; + + case QOpenGLTexture::DirectionT: + wrapModes[1] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, direction, mode); + break; + + case QOpenGLTexture::DirectionR: + wrapModes[2] = mode; + texFuncs->glTextureParameteri(textureId, target, bindingTarget, direction, mode); + break; + } + break; + } +} + +QOpenGLTexture::WrapMode QOpenGLTexturePrivate::wrapMode(QOpenGLTexture::CoordinateDirection direction) const +{ + switch (target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::TargetBuffer: + switch (direction) { + case QOpenGLTexture::DirectionS: + return wrapModes[0]; + + case QOpenGLTexture::DirectionT: + case QOpenGLTexture::DirectionR: + qWarning("QOpenGLTexture::wrapMode() direction not valid for this texture target"); + return QOpenGLTexture::Repeat; + } + break; + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + case QOpenGLTexture::TargetRectangle: + switch (direction) { + case QOpenGLTexture::DirectionS: + return wrapModes[0]; + + case QOpenGLTexture::DirectionT: + return wrapModes[1]; + + case QOpenGLTexture::DirectionR: + qWarning("QOpenGLTexture::wrapMode() direction not valid for this texture target"); + return QOpenGLTexture::Repeat; + } + break; + + case QOpenGLTexture::Target3D: + switch (direction) { + case QOpenGLTexture::DirectionS: + return wrapModes[0]; + + case QOpenGLTexture::DirectionT: + return wrapModes[1]; + + case QOpenGLTexture::DirectionR: + return wrapModes[2]; + } + break; + } + // Should never get here + Q_ASSERT(false); + return QOpenGLTexture::Repeat; +} + +QOpenGLTexture *QOpenGLTexturePrivate::createTextureView(QOpenGLTexture::Target viewTarget, + QOpenGLTexture::TextureFormat viewFormat, + int minimumMipmapLevel, int maximumMipmapLevel, + int minimumLayer, int maximumLayer) const +{ + // Do sanity checks - see http://www.opengl.org/wiki/GLAPI/glTextureView + + // Check the targets are compatible + bool viewTargetCompatible = false; + switch (target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + viewTargetCompatible = (viewTarget == QOpenGLTexture::Target1D + || viewTarget == QOpenGLTexture::Target1DArray); + break; + + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + viewTargetCompatible = (viewTarget == QOpenGLTexture::Target2D + || viewTarget == QOpenGLTexture::Target2DArray); + break; + + case QOpenGLTexture::Target3D: + viewTargetCompatible = (viewTarget == QOpenGLTexture::Target3D); + break; + + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + viewTargetCompatible = (viewTarget == QOpenGLTexture::TargetCubeMap + || viewTarget == QOpenGLTexture::Target2D + || viewTarget == QOpenGLTexture::Target2DArray + || viewTarget == QOpenGLTexture::TargetCubeMapArray); + break; + + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + viewTargetCompatible = (viewTarget == QOpenGLTexture::Target2DMultisample + || viewTarget == QOpenGLTexture::Target2DMultisampleArray); + break; + + case QOpenGLTexture::TargetRectangle: + viewTargetCompatible = (viewTarget == QOpenGLTexture::TargetRectangle); + break; + + case QOpenGLTexture::TargetBuffer: + // Cannot be used with texture views + break; + } + + if (!viewTargetCompatible) { + qWarning("QOpenGLTexture::createTextureView(): Incompatible source and view targets"); + return nullptr; + } + + // Check the formats are compatible + bool viewFormatCompatible = false; + switch (formatClass) { + case QOpenGLTexture::NoFormatClass: + break; + + case QOpenGLTexture::FormatClass_128Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGBA32F + || viewFormat == QOpenGLTexture::RGBA32U + || viewFormat == QOpenGLTexture::RGBA32I); + break; + + case QOpenGLTexture::FormatClass_96Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGB32F + || viewFormat == QOpenGLTexture::RGB32U + || viewFormat == QOpenGLTexture::RGB32I); + break; + + case QOpenGLTexture::FormatClass_64Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGBA16F + || viewFormat == QOpenGLTexture::RG32F + || viewFormat == QOpenGLTexture::RGBA16U + || viewFormat == QOpenGLTexture::RG32U + || viewFormat == QOpenGLTexture::RGBA16I + || viewFormat == QOpenGLTexture::RG32I + || viewFormat == QOpenGLTexture::RGBA16_UNorm + || viewFormat == QOpenGLTexture::RGBA16_SNorm); + break; + + case QOpenGLTexture::FormatClass_48Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGB16_UNorm + || viewFormat == QOpenGLTexture::RGB16_SNorm + || viewFormat == QOpenGLTexture::RGB16F + || viewFormat == QOpenGLTexture::RGB16U + || viewFormat == QOpenGLTexture::RGB16I); + break; + + case QOpenGLTexture::FormatClass_32Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RG16F + || viewFormat == QOpenGLTexture::RG11B10F + || viewFormat == QOpenGLTexture::R32F + || viewFormat == QOpenGLTexture::RGB10A2 + || viewFormat == QOpenGLTexture::RGBA8U + || viewFormat == QOpenGLTexture::RG16U + || viewFormat == QOpenGLTexture::R32U + || viewFormat == QOpenGLTexture::RGBA8I + || viewFormat == QOpenGLTexture::RG16I + || viewFormat == QOpenGLTexture::R32I + || viewFormat == QOpenGLTexture::RGBA8_UNorm + || viewFormat == QOpenGLTexture::RG16_UNorm + || viewFormat == QOpenGLTexture::RGBA8_SNorm + || viewFormat == QOpenGLTexture::RG16_SNorm + || viewFormat == QOpenGLTexture::SRGB8_Alpha8 + || viewFormat == QOpenGLTexture::RGB9E5); + break; + + case QOpenGLTexture::FormatClass_24Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGB8_UNorm + || viewFormat == QOpenGLTexture::RGB8_SNorm + || viewFormat == QOpenGLTexture::SRGB8 + || viewFormat == QOpenGLTexture::RGB8U + || viewFormat == QOpenGLTexture::RGB8I); + break; + + case QOpenGLTexture::FormatClass_16Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::R16F + || viewFormat == QOpenGLTexture::RG8U + || viewFormat == QOpenGLTexture::R16U + || viewFormat == QOpenGLTexture::RG8I + || viewFormat == QOpenGLTexture::R16I + || viewFormat == QOpenGLTexture::RG8_UNorm + || viewFormat == QOpenGLTexture::R16_UNorm + || viewFormat == QOpenGLTexture::RG8_SNorm + || viewFormat == QOpenGLTexture::R16_SNorm); + break; + + case QOpenGLTexture::FormatClass_8Bit: + viewFormatCompatible = (viewFormat == QOpenGLTexture::R8U + || viewFormat == QOpenGLTexture::R8I + || viewFormat == QOpenGLTexture::R8_UNorm + || viewFormat == QOpenGLTexture::R8_SNorm); + break; + + case QOpenGLTexture::FormatClass_RGTC1_R: + viewFormatCompatible = (viewFormat == QOpenGLTexture::R_ATI1N_UNorm + || viewFormat == QOpenGLTexture::R_ATI1N_SNorm); + break; + + case QOpenGLTexture::FormatClass_RGTC2_RG: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RG_ATI2N_UNorm + || viewFormat == QOpenGLTexture::RG_ATI2N_SNorm); + break; + + case QOpenGLTexture::FormatClass_BPTC_Unorm: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGB_BP_UNorm + || viewFormat == QOpenGLTexture::SRGB_BP_UNorm); + break; + + case QOpenGLTexture::FormatClass_BPTC_Float: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT + || viewFormat == QOpenGLTexture::RGB_BP_SIGNED_FLOAT); + break; + + case QOpenGLTexture::FormatClass_S3TC_DXT1_RGB: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGB_DXT1 + || viewFormat == QOpenGLTexture::SRGB_DXT1); + break; + + case QOpenGLTexture::FormatClass_S3TC_DXT1_RGBA: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGBA_DXT1 + || viewFormat == QOpenGLTexture::SRGB_Alpha_DXT1); + break; + + case QOpenGLTexture::FormatClass_S3TC_DXT3_RGBA: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGBA_DXT3 + || viewFormat == QOpenGLTexture::SRGB_Alpha_DXT3); + break; + + case QOpenGLTexture::FormatClass_S3TC_DXT5_RGBA: + viewFormatCompatible = (viewFormat == QOpenGLTexture::RGBA_DXT5 + || viewFormat == QOpenGLTexture::SRGB_Alpha_DXT5); + break; + + case QOpenGLTexture::FormatClass_Unique: + viewFormatCompatible = (viewFormat == format); + break; + } + + if (!viewFormatCompatible) { + qWarning("QOpenGLTexture::createTextureView(): Incompatible source and view formats"); + return nullptr; + } + + + // Create a view + QOpenGLTexture *view = new QOpenGLTexture(viewTarget); + view->setFormat(viewFormat); + view->create(); + view->d_ptr->textureView = true; + texFuncs->glTextureView(view->textureId(), viewTarget, textureId, viewFormat, + minimumMipmapLevel, maximumMipmapLevel - minimumMipmapLevel + 1, + minimumLayer, maximumLayer - minimumLayer + 1); + return view; +} + + +/*! + \class QOpenGLTexture + \inmodule QtGui + \since 5.2 + \wrapper + \brief The QOpenGLTexture class encapsulates an OpenGL texture object. + + QOpenGLTexture makes it easy to work with OpenGL textures and the myriad features + and targets that they offer depending upon the capabilities of your OpenGL implementation. + + The typical usage pattern for QOpenGLTexture is + \list + \li Instantiate the object specifying the texture target type + \li Set properties that affect the storage requirements e.g. storage format, dimensions + \li Allocate the server-side storage + \li Optionally upload pixel data + \li Optionally set any additional properties e.g. filtering and border options + \li Render with texture or render to texture + \endlist + + In the common case of simply using a QImage as the source of texture pixel data + most of the above steps are performed automatically. + + \code + // Prepare texture + QOpenGLTexture *texture = new QOpenGLTexture(QImage(fileName).mirrored()); + texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); + texture->setMagnificationFilter(QOpenGLTexture::Linear); + ... + // Render with texture + texture->bind(); + glDrawArrays(...); + \endcode + + Note that the QImage is mirrored vertically to account for the fact that + OpenGL and QImage use opposite directions for the y axis. Another option + would be to transform your texture coordinates. +*/ + +/*! + \enum QOpenGLTexture::Filter + This enum defines the filtering parameters for a QOpenGLTexture object. + \value Nearest Equivalent to GL_NEAREST + \value Linear Equivalent to GL_LINEAR + \value NearestMipMapNearest Equivalent to GL_NEAREST_MIPMAP_NEAREST + \value NearestMipMapLinear Equivalent to GL_NEAREST_MIPMAP_LINEAR + \value LinearMipMapNearest Equivalent to GL_LINEAR_MIPMAP_NEAREST + \value LinearMipMapLinear Equivalent to GL_LINEAR_MIPMAP_LINEAR +*/ + +/*! + \enum QOpenGLTexture::Target + This enum defines the texture target of a QOpenGLTexture object. + For more information on creating array textures, see \l{Array Texture}. + + \value Target1D A 1-dimensional texture. + Equivalent to GL_TEXTURE_1D. + \value Target1DArray An array of 1-dimensional textures. + Equivalent to GL_TEXTURE_1D_ARRAY + \value Target2D A 2-dimensional texture. + Equivalent to GL_TEXTURE_2D + \value Target2DArray An array of 2-dimensional textures. + Equivalent to GL_TEXTURE_2D_ARRAY + \value Target3D A 3-dimensional texture. + Equivalent to GL_TEXTURE_3D + \value TargetCubeMap A cubemap texture. + Equivalent to GL_TEXTURE_CUBE_MAP + \value TargetCubeMapArray An array of cubemap textures. + Equivalent to GL_TEXTURE_CUBE_MAP_ARRAY + \value Target2DMultisample A 2-dimensional texture with multisample support. + Equivalent to GL_TEXTURE_2D_MULTISAMPLE + \value Target2DMultisampleArray An array of 2-dimensional textures with multisample support. + Equivalent to GL_TEXTURE_2D_MULTISAMPLE_ARRAY + \value TargetRectangle A rectangular 2-dimensional texture. + Equivalent to GL_TEXTURE_RECTANGLE + \value TargetBuffer A texture with data from an OpenGL buffer object. + Equivalent to GL_TEXTURE_BUFFER +*/ + +/*! + \enum QOpenGLTexture::BindingTarget + This enum defines the possible binding targets of texture units. + + \value BindingTarget1D Equivalent to GL_TEXTURE_BINDING_1D + \value BindingTarget1DArray Equivalent to GL_TEXTURE_BINDING_1D_ARRAY + \value BindingTarget2D Equivalent to GL_TEXTURE_BINDING_2D + \value BindingTarget2DArray Equivalent to GL_TEXTURE_BINDING_2D_ARRAY + \value BindingTarget3D Equivalent to GL_TEXTURE_BINDING_3D + \value BindingTargetCubeMap Equivalent to GL_TEXTURE_BINDING_CUBE_MAP + \value BindingTargetCubeMapArray Equivalent to GL_TEXTURE_BINDING_CUBE_MAP_ARRAY + \value BindingTarget2DMultisample Equivalent to GL_TEXTURE_BINDING_2D_MULTISAMPLE + \value BindingTarget2DMultisampleArray Equivalent to GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY + \value BindingTargetRectangle Equivalent to GL_TEXTURE_BINDING_RECTANGLE + \value BindingTargetBuffer Equivalent to GL_TEXTURE_BINDING_BUFFER +*/ + +/*! + \enum QOpenGLTexture::MipMapGeneration + This enum defines the options to control mipmap generation. + + \value GenerateMipMaps Mipmaps should be generated + \value DontGenerateMipMaps Mipmaps should not be generated +*/ + +/*! + \enum QOpenGLTexture::TextureUnitReset + This enum defines options ot control texture unit activation. + + \value ResetTextureUnit The previous active texture unit will be reset + \value DontResetTextureUnit The previous active texture unit will not be rest +*/ + +/*! + \enum QOpenGLTexture::TextureFormat + This enum defines the possible texture formats. Depending upon your OpenGL + implementation only a subset of these may be supported. + + \value NoFormat Equivalent to GL_NONE + + \value R8_UNorm Equivalent to GL_R8 + \value RG8_UNorm Equivalent to GL_RG8 + \value RGB8_UNorm Equivalent to GL_RGB8 + \value RGBA8_UNorm Equivalent to GL_RGBA8 + + \value R16_UNorm Equivalent to GL_R16 + \value RG16_UNorm Equivalent to GL_RG16 + \value RGB16_UNorm Equivalent to GL_RGB16 + \value RGBA16_UNorm Equivalent to GL_RGBA16 + + \value R8_SNorm Equivalent to GL_R8_SNORM + \value RG8_SNorm Equivalent to GL_RG8_SNORM + \value RGB8_SNorm Equivalent to GL_RGB8_SNORM + \value RGBA8_SNorm Equivalent to GL_RGBA8_SNORM + + \value R16_SNorm Equivalent to GL_R16_SNORM + \value RG16_SNorm Equivalent to GL_RG16_SNORM + \value RGB16_SNorm Equivalent to GL_RGB16_SNORM + \value RGBA16_SNorm Equivalent to GL_RGBA16_SNORM + + \value R8U Equivalent to GL_R8UI + \value RG8U Equivalent to GL_RG8UI + \value RGB8U Equivalent to GL_RGB8UI + \value RGBA8U Equivalent to GL_RGBA8UI + + \value R16U Equivalent to GL_R16UI + \value RG16U Equivalent to GL_RG16UI + \value RGB16U Equivalent to GL_RGB16UI + \value RGBA16U Equivalent to GL_RGBA16UI + + \value R32U Equivalent to GL_R32UI + \value RG32U Equivalent to GL_RG32UI + \value RGB32U Equivalent to GL_RGB32UI + \value RGBA32U Equivalent to GL_RGBA32UI + + \value R8I Equivalent to GL_R8I + \value RG8I Equivalent to GL_RG8I + \value RGB8I Equivalent to GL_RGB8I + \value RGBA8I Equivalent to GL_RGBA8I + + \value R16I Equivalent to GL_R16I + \value RG16I Equivalent to GL_RG16I + \value RGB16I Equivalent to GL_RGB16I + \value RGBA16I Equivalent to GL_RGBA16I + + \value R32I Equivalent to GL_R32I + \value RG32I Equivalent to GL_RG32I + \value RGB32I Equivalent to GL_RGB32I + \value RGBA32I Equivalent to GL_RGBA32I + + \value R16F Equivalent to GL_R16F + \value RG16F Equivalent to GL_RG16F + \value RGB16F Equivalent to GL_RGB16F + \value RGBA16F Equivalent to GL_RGBA16F + + \value R32F Equivalent to GL_R32F + \value RG32F Equivalent to GL_RG32F + \value RGB32F Equivalent to GL_RGB32F + \value RGBA32F Equivalent to GL_RGBA32F + + \value RGB9E5 Equivalent to GL_RGB9_E5 + \value RG11B10F Equivalent to GL_R11F_G11F_B10F + \value RG3B2 Equivalent to GL_R3_G3_B2 + \value R5G6B5 Equivalent to GL_RGB565 + \value RGB5A1 Equivalent to GL_RGB5_A1 + \value RGBA4 Equivalent to GL_RGBA4 + \value RGB10A2 Equivalent to GL_RGB10_A2UI + + \value D16 Equivalent to GL_DEPTH_COMPONENT16 + \value D24 Equivalent to GL_DEPTH_COMPONENT24 + \value D24S8 Equivalent to GL_DEPTH24_STENCIL8 + \value D32 Equivalent to GL_DEPTH_COMPONENT32 + \value D32F Equivalent to GL_DEPTH_COMPONENT32F + \value D32FS8X24 Equivalent to GL_DEPTH32F_STENCIL8 + \value S8 Equivalent to GL_STENCIL_INDEX8. Introduced in Qt 5.4 + + \value RGB_DXT1 Equivalent to GL_COMPRESSED_RGB_S3TC_DXT1_EXT + \value RGBA_DXT1 Equivalent to GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + \value RGBA_DXT3 Equivalent to GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + \value RGBA_DXT5 Equivalent to GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + \value R_ATI1N_UNorm Equivalent to GL_COMPRESSED_RED_RGTC1 + \value R_ATI1N_SNorm Equivalent to GL_COMPRESSED_SIGNED_RED_RGTC1 + \value RG_ATI2N_UNorm Equivalent to GL_COMPRESSED_RG_RGTC2 + \value RG_ATI2N_SNorm Equivalent to GL_COMPRESSED_SIGNED_RG_RGTC2 + \value RGB_BP_UNSIGNED_FLOAT Equivalent to GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + \value RGB_BP_SIGNED_FLOAT Equivalent to GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + \value RGB_BP_UNorm Equivalent to GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + \value R11_EAC_UNorm Equivalent to GL_COMPRESSED_R11_EAC + \value R11_EAC_SNorm Equivalent to GL_COMPRESSED_SIGNED_R11_EAC + \value RG11_EAC_UNorm Equivalent to GL_COMPRESSED_RG11_EAC + \value RG11_EAC_SNorm Equivalent to GL_COMPRESSED_SIGNED_RG11_EAC + \value RGB8_ETC2 Equivalent to GL_COMPRESSED_RGB8_ETC2 + \value SRGB8_ETC2 Equivalent to GL_COMPRESSED_SRGB8_ETC2 + \value RGB8_PunchThrough_Alpha1_ETC2 Equivalent to GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + \value SRGB8_PunchThrough_Alpha1_ETC2 Equivalent to GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 + \value RGBA8_ETC2_EAC Equivalent to GL_COMPRESSED_RGBA8_ETC2_EAC + \value SRGB8_Alpha8_ETC2_EAC Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + \value RGB8_ETC1 Equivalent to GL_ETC1_RGB8_OES + \value RGBA_ASTC_4x4 Equivalent to GL_COMPRESSED_RGBA_ASTC_4x4_KHR + \value RGBA_ASTC_5x4 Equivalent to GL_COMPRESSED_RGBA_ASTC_5x4_KHR + \value RGBA_ASTC_5x5 Equivalent to GL_COMPRESSED_RGBA_ASTC_5x5_KHR + \value RGBA_ASTC_6x5 Equivalent to GL_COMPRESSED_RGBA_ASTC_6x5_KHR + \value RGBA_ASTC_6x6 Equivalent to GL_COMPRESSED_RGBA_ASTC_6x6_KHR + \value RGBA_ASTC_8x5 Equivalent to GL_COMPRESSED_RGBA_ASTC_8x5_KHR + \value RGBA_ASTC_8x6 Equivalent to GL_COMPRESSED_RGBA_ASTC_8x6_KHR + \value RGBA_ASTC_8x8 Equivalent to GL_COMPRESSED_RGBA_ASTC_8x8_KHR + \value RGBA_ASTC_10x5 Equivalent to GL_COMPRESSED_RGBA_ASTC_10x5_KHR + \value RGBA_ASTC_10x6 Equivalent to GL_COMPRESSED_RGBA_ASTC_10x6_KHR + \value RGBA_ASTC_10x8 Equivalent to GL_COMPRESSED_RGBA_ASTC_10x8_KHR + \value RGBA_ASTC_10x10 Equivalent to GL_COMPRESSED_RGBA_ASTC_10x10_KHR + \value RGBA_ASTC_12x10 Equivalent to GL_COMPRESSED_RGBA_ASTC_12x10_KHR + \value RGBA_ASTC_12x12 Equivalent to GL_COMPRESSED_RGBA_ASTC_12x12_KHR + \value SRGB8_Alpha8_ASTC_4x4 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + \value SRGB8_Alpha8_ASTC_5x4 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR + \value SRGB8_Alpha8_ASTC_5x5 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR + \value SRGB8_Alpha8_ASTC_6x5 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR + \value SRGB8_Alpha8_ASTC_6x6 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR + \value SRGB8_Alpha8_ASTC_8x5 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR + \value SRGB8_Alpha8_ASTC_8x6 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR + \value SRGB8_Alpha8_ASTC_8x8 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR + \value SRGB8_Alpha8_ASTC_10x5 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR + \value SRGB8_Alpha8_ASTC_10x6 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR + \value SRGB8_Alpha8_ASTC_10x8 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR + \value SRGB8_Alpha8_ASTC_10x10 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR + \value SRGB8_Alpha8_ASTC_12x10 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR + \value SRGB8_Alpha8_ASTC_12x12 Equivalent to GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR + + \value SRGB8 Equivalent to GL_SRGB8 + \value SRGB8_Alpha8 Equivalent to GL_SRGB8_ALPHA8 + \value SRGB_DXT1 Equivalent to GL_COMPRESSED_SRGB_S3TC_DXT1_EXT + \value SRGB_Alpha_DXT1 Equivalent to GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT + \value SRGB_Alpha_DXT3 Equivalent to GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT + \value SRGB_Alpha_DXT5 Equivalent to GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + \value SRGB_BP_UNorm Equivalent to GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + + \value DepthFormat Equivalent to GL_DEPTH_COMPONENT (only OpenGL ES 3 or ES 2 with OES_depth_texture) + \value AlphaFormat Equivalent to GL_ALPHA (OpenGL ES 2 only) + \value RGBFormat Equivalent to GL_RGB (OpenGL ES 2 only) + \value RGBAFormat Equivalent to GL_RGBA (OpenGL ES 2 only) + \value LuminanceFormat Equivalent to GL_LUMINANCE (OpenGL ES 2 only) + \value LuminanceAlphaFormat Equivalent to GL_LUMINANCE_ALPHA (OpenGL ES 2 only) +*/ + +/*! + \enum QOpenGLTexture::CubeMapFace + This enum defines the possible CubeMap faces. + + \value CubeMapPositiveX Equivalent to GL_TEXTURE_CUBE_MAP_POSITIVE_X + \value CubeMapNegativeX Equivalent to GL_TEXTURE_CUBE_MAP_NEGATIVE_X + \value CubeMapPositiveY Equivalent to GL_TEXTURE_CUBE_MAP_POSITIVE_Y + \value CubeMapNegativeY Equivalent to GL_TEXTURE_CUBE_MAP_NEGATIVE_Y + \value CubeMapPositiveZ Equivalent to GL_TEXTURE_CUBE_MAP_POSITIVE_Z + \value CubeMapNegativeZ Equivalent to GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +*/ + +/*! + \enum QOpenGLTexture::PixelFormat + This enum defines the possible client-side pixel formats for a pixel + transfer operation. + + \value NoSourceFormat Equivalent to GL_NONE + \value Red Equivalent to GL_RED + \value RG Equivalent to GL_RG + \value RGB Equivalent to GL_RGB + \value BGR Equivalent to GL_BGR + \value RGBA Equivalent to GL_RGBA + \value BGRA Equivalent to GL_BGRA + \value Red_Integer Equivalent to GL_RED_INTEGER + \value RG_Integer Equivalent to GL_RG_INTEGER + \value RGB_Integer Equivalent to GL_RGB_INTEGER + \value BGR_Integer Equivalent to GL_BGR_INTEGER + \value RGBA_Integer Equivalent to GL_RGBA_INTEGER + \value BGRA_Integer Equivalent to GL_BGRA_INTEGER + \value Stencil Equivalent to GL_STENCIL_INDEX. Introduced in Qt 5.4 + \value Depth Equivalent to GL_DEPTH_COMPONENT + \value DepthStencil Equivalent to GL_DEPTH_STENCIL + \value Alpha Equivalent to GL_ALPHA (OpenGL ES 2 only) + \value Luminance Equivalent to GL_LUMINANCE (OpenGL ES 2 only) + \value LuminanceAlpha Equivalent to GL_LUMINANCE_ALPHA (OpenGL ES 2 only) + +*/ + +/*! + \enum QOpenGLTexture::PixelType + This enum defines the possible pixel data types for a pixel transfer operation + + \value NoPixelType Equivalent to GL_NONE + \value Int8 Equivalent to GL_BYTE + \value UInt8 Equivalent to GL_UNSIGNED_BYTE + \value Int16 Equivalent to GL_SHORT + \value UInt16 Equivalent to GL_UNSIGNED_SHORT + \value Int32 Equivalent to GL_INT + \value UInt32 Equivalent to GL_UNSIGNED_INT + \value Float16 Equivalent to GL_HALF_FLOAT + \value Float16OES Equivalent to GL_HALF_FLOAT_OES + \value Float32 Equivalent to GL_FLOAT + \value UInt32_RGB9_E5 Equivalent to GL_UNSIGNED_INT_5_9_9_9_REV + \value UInt32_RG11B10F Equivalent to GL_UNSIGNED_INT_10F_11F_11F_REV + \value UInt8_RG3B2 Equivalent to GL_UNSIGNED_BYTE_3_3_2 + \value UInt8_RG3B2_Rev Equivalent to GL_UNSIGNED_BYTE_2_3_3_REV + \value UInt16_RGB5A1 Equivalent to GL_UNSIGNED_SHORT_5_5_5_1 + \value UInt16_RGB5A1_Rev Equivalent to GL_UNSIGNED_SHORT_1_5_5_5_REV + \value UInt16_R5G6B5 Equivalent to GL_UNSIGNED_SHORT_5_6_5 + \value UInt16_R5G6B5_Rev Equivalent to GL_UNSIGNED_SHORT_5_6_5_REV + \value UInt16_RGBA4 Equivalent to GL_UNSIGNED_SHORT_4_4_4_4 + \value UInt16_RGBA4_Rev Equivalent to GL_UNSIGNED_SHORT_4_4_4_4_REV + \value UInt32_RGBA8 Equivalent to GL_UNSIGNED_INT_8_8_8_8 + \value UInt32_RGBA8_Rev Equivalent to GL_UNSIGNED_INT_8_8_8_8_REV + \value UInt32_RGB10A2 Equivalent to GL_UNSIGNED_INT_10_10_10_2 + \value UInt32_RGB10A2_Rev Equivalent to GL_UNSIGNED_INT_2_10_10_10_REV + \value UInt32_D24S8 Equivalent to GL_UNSIGNED_INT_24_8. Introduced in Qt 5.4 + \value Float32_D32_UInt32_S8_X24 Equivalent to GL_FLOAT_32_UNSIGNED_INT_24_8_REV. Introduced in Qt 5.4 +*/ + +/*! + \enum QOpenGLTexture::Feature + This enum defines the OpenGL texture-related features that can be tested for. + + \value ImmutableStorage Support for immutable texture storage + \value ImmutableMultisampleStorage Support for immutable texture storage with + multisample targets + \value TextureRectangle Support for the GL_TEXTURE_RECTANGLE target + \value TextureArrays Support for texture targets with array layers + \value Texture3D Support for the 3 dimensional texture target + \value TextureMultisample Support for texture targets that have multisample capabilities + \value TextureBuffer Support for textures that use OpenGL buffer objects + as their data source + \value TextureCubeMapArrays Support for cubemap array texture target + \value Swizzle Support for texture component swizzle masks + \value StencilTexturing Support for stencil texturing (i.e. looking up depth or stencil + components of a combined depth/stencil format texture in GLSL shaders). + \value AnisotropicFiltering Support for anisotropic texture filtering + \value NPOTTextures Basic support for non-power-of-two textures + \value NPOTTextureRepeat Full support for non-power-of-two textures including texture + repeat modes + \value Texture1D Support for the 1 dimensional texture target + \value TextureComparisonOperators Support for texture comparison operators + \value TextureMipMapLevel Support for setting the base and maximum mipmap levels +*/ + +/*! + \enum QOpenGLTexture::SwizzleComponent + This enum defines the texture color components that can be assigned a swizzle mask. + + \value SwizzleRed The red component. Equivalent to GL_TEXTURE_SWIZZLE_R + \value SwizzleGreen The green component. Equivalent to GL_TEXTURE_SWIZZLE_G + \value SwizzleBlue The blue component. Equivalent to GL_TEXTURE_SWIZZLE_B + \value SwizzleAlpha The alpha component. Equivalent to GL_TEXTURE_SWIZZLE_A +*/ + +/*! + \enum QOpenGLTexture::SwizzleValue + This enum defines the possible mask values for texture swizzling. + + \value RedValue Maps the component to the red channel. Equivalent to GL_RED + \value GreenValue Maps the component to the green channel. Equivalent to GL_GREEN + \value BlueValue Maps the component to the blue channel. Equivalent to GL_BLUE + \value AlphaValue Maps the component to the alpha channel. Equivalent to GL_ALPHA + \value ZeroValue Maps the component to a fixed value of 0. Equivalent to GL_ZERO + \value OneValue Maps the component to a fixed value of 1. Equivalent to GL_ONE +*/ + +/*! + \enum QOpenGLTexture::WrapMode + This enum defines the possible texture coordinate wrapping modes. + + \value Repeat Texture coordinate is repeated. Equivalent to GL_REPEAT + \value MirroredRepeat Texture coordinate is reflected about 0 and 1. Equivalent to GL_MIRRORED_REPEAT + \value ClampToEdge Clamps the texture coordinates to [0,1]. Equivalent to GL_CLAMP_TO_EDGE + \value ClampToBorder As for ClampToEdge but also blends samples at 0 and 1 with a + fixed border color. Equivalent to GL_CLAMP_TO_BORDER +*/ + +/*! + \enum QOpenGLTexture::CoordinateDirection + This enum defines the possible texture coordinate directions + + \value DirectionS The horizontal direction. Equivalent to GL_TEXTURE_WRAP_S + \value DirectionT The vertical direction. Equivalent to GL_TEXTURE_WRAP_T + \value DirectionR The depth direction. Equivalent to GL_TEXTURE_WRAP_R +*/ + +/*! + Creates a QOpenGLTexture object that can later be bound to \a target. + + This does not create the underlying OpenGL texture object. Therefore, + construction using this constructor does not require a valid current + OpenGL context. +*/ +QOpenGLTexture::QOpenGLTexture(Target target) + : d_ptr(new QOpenGLTexturePrivate(target, this)) +{ +} + +/*! + Creates a QOpenGLTexture object that can later be bound to the 2D texture + target and contains the pixel data contained in \a image. If you wish + to have a chain of mipmaps generated then set \a genMipMaps to \c true (this + is the default). + + This does create the underlying OpenGL texture object. Therefore, + construction using this constructor does require a valid current + OpenGL context. +*/ +QOpenGLTexture::QOpenGLTexture(const QImage& image, MipMapGeneration genMipMaps) + : QOpenGLTexture(QOpenGLTexture::Target2D) +{ + setData(image, genMipMaps); +} + +QOpenGLTexture::~QOpenGLTexture() +{ +} + +/*! + Returns the binding target of this texture. + + \since 5.4 +*/ +QOpenGLTexture::Target QOpenGLTexture::target() const +{ + Q_D(const QOpenGLTexture); + return d->target; +} + +/*! + Creates the underlying OpenGL texture object. This requires a current valid + OpenGL context. If the texture object already exists, this function does + nothing. + + Once the texture object is created you can obtain the object + name from the textureId() function. This may be useful if you wish to make + some raw OpenGL calls related to this texture. + + Normally it should not be necessary to call this function directly as all + functions that set properties of the texture object implicitly call create() + on your behalf. + + Returns \c true if the creation succeeded, otherwise returns \c false. + + \sa destroy(), isCreated(), textureId() +*/ +bool QOpenGLTexture::create() +{ + Q_D(QOpenGLTexture); + return d->create(); +} + +/*! + Destroys the underlying OpenGL texture object. This requires a current valid + OpenGL context. + + \sa create(), isCreated(), textureId() +*/ +void QOpenGLTexture::destroy() +{ + Q_D(QOpenGLTexture); + return d->destroy(); +} + +/*! + Returns \c true if the underlying OpenGL texture object has been created. + + \sa create(), destroy(), textureId() +*/ +bool QOpenGLTexture::isCreated() const +{ + Q_D(const QOpenGLTexture); + return d->textureId != 0; +} + +/*! + Returns the name of the underlying OpenGL texture object or 0 if it has + not yet been created. + + \sa create(), destroy(), isCreated() +*/ +GLuint QOpenGLTexture::textureId() const +{ + Q_D(const QOpenGLTexture); + return d->textureId; +} + +/*! + Binds this texture to the currently active texture unit ready for + rendering. Note that you do not need to bind QOpenGLTexture objects + in order to modify them as the implementation makes use of the + EXT_direct_state_access extension where available and simulates it + where it is not. + + \sa release() +*/ +void QOpenGLTexture::bind() +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->bind(); +} + +/*! + Binds this texture to texture unit \a unit ready for + rendering. Note that you do not need to bind QOpenGLTexture objects + in order to modify them as the implementation makes use of the + EXT_direct_state_access extension where available and simulates it + where it is not. + + If parameter \a reset is \c true then this function will restore + the active unit to the texture unit that was active upon entry. + + \sa release() +*/ +void QOpenGLTexture::bind(uint unit, TextureUnitReset reset) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->bind(unit, reset); +} + +/*! + Unbinds this texture from the currently active texture unit. + + \sa bind() +*/ +void QOpenGLTexture::release() +{ + Q_D(QOpenGLTexture); + d->release(); +} + +/*! + Unbinds this texture from texture unit \a unit. + + If parameter \a reset is \c true then this function + will restore the active unit to the texture unit that was active + upon entry. +*/ +void QOpenGLTexture::release(uint unit, TextureUnitReset reset) +{ + Q_D(QOpenGLTexture); + d->release(unit, reset); +} + +/*! + Returns \c true if this texture is bound to the corresponding target + of the currently active texture unit. + + \sa bind(), release() +*/ +bool QOpenGLTexture::isBound() const +{ + Q_D(const QOpenGLTexture); + Q_ASSERT(d->textureId); + return d->isBound(); +} + +/*! + Returns \c true if this texture is bound to the corresponding target + of texture unit \a unit. + + \sa bind(), release() +*/ +bool QOpenGLTexture::isBound(uint unit) +{ + Q_D(const QOpenGLTexture); + Q_ASSERT(d->textureId); + return d->isBound(unit); +} + +/*! + Returns the textureId of the texture that is bound to the \a target + of the currently active texture unit. +*/ +GLuint QOpenGLTexture::boundTextureId(BindingTarget target) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning("QOpenGLTexture::boundTextureId() requires a valid current context"); + return 0; + } + + GLint textureId = 0; + ctx->functions()->glGetIntegerv(target, &textureId); + return static_cast<GLuint>(textureId); +} + +/*! + Returns the textureId of the texture that is bound to the \a target + of the texture unit \a unit. +*/ +GLuint QOpenGLTexture::boundTextureId(uint unit, BindingTarget target) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning("QOpenGLTexture::boundTextureId() requires a valid current context"); + return 0; + } + + QOpenGLFunctions *funcs = ctx->functions(); + funcs->initializeOpenGLFunctions(); + + GLint oldTextureUnit = 0; + funcs->glGetIntegerv(GL_ACTIVE_TEXTURE, &oldTextureUnit); + + funcs->glActiveTexture(unit); + GLint textureId = 0; + funcs->glGetIntegerv(target, &textureId); + funcs->glActiveTexture(oldTextureUnit); + + return static_cast<GLuint>(textureId); +} + +/*! + Sets the format of this texture object to \a format. This function + must be called before texture storage is allocated. + + Note that all formats may not be supported. The exact set of supported + formats is dependent upon your OpenGL implementation and version. + + \sa format(), allocateStorage() +*/ +void QOpenGLTexture::setFormat(TextureFormat format) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("QOpenGLTexture::setFormat(): Cannot change format once storage has been allocated"); + return; + } + + d->format = format; + + switch (format) { + case NoFormat: + d->formatClass = NoFormatClass; + break; + + case RGBA32F: + case RGBA32U: + case RGBA32I: + d->formatClass = FormatClass_128Bit; + break; + + case RGB32F: + case RGB32U: + case RGB32I: + d->formatClass = FormatClass_96Bit; + break; + + case RGBA16F: + case RG32F: + case RGBA16U: + case RG32U: + case RGBA16I: + case RG32I: + case RGBA16_UNorm: + case RGBA16_SNorm: + d->formatClass = FormatClass_64Bit; + break; + + case RGB16_UNorm: + case RGB16_SNorm: + case RGB16F: + case RGB16U: + case RGB16I: + d->formatClass = FormatClass_48Bit; + break; + + case RG16F: + case RG11B10F: + case R32F: + case RGB10A2: + case RGBA8U: + case RG16U: + case R32U: + case RGBA8I: + case RG16I: + case R32I: + case RGBA8_UNorm: + case RG16_UNorm: + case RGBA8_SNorm: + case RG16_SNorm: + case SRGB8_Alpha8: + case RGB9E5: + d->formatClass = FormatClass_32Bit; + break; + + case RGB8_UNorm: + case RGB8_SNorm: + case SRGB8: + case RGB8U: + case RGB8I: + d->formatClass = FormatClass_24Bit; + break; + + case R16F: + case RG8U: + case R16U: + case RG8I: + case R16I: + case RG8_UNorm: + case R16_UNorm: + case RG8_SNorm: + case R16_SNorm: + d->formatClass = FormatClass_16Bit; + break; + + case R8U: + case R8I: + case R8_UNorm: + case R8_SNorm: + d->formatClass = FormatClass_8Bit; + break; + + case R_ATI1N_UNorm: + case R_ATI1N_SNorm: + d->formatClass = FormatClass_RGTC1_R; + break; + + case RG_ATI2N_UNorm: + case RG_ATI2N_SNorm: + d->formatClass = FormatClass_RGTC2_RG; + break; + + case RGB_BP_UNorm: + case SRGB_BP_UNorm: + d->formatClass = FormatClass_BPTC_Unorm; + break; + + case RGB_BP_UNSIGNED_FLOAT: + case RGB_BP_SIGNED_FLOAT: + d->formatClass = FormatClass_BPTC_Float; + break; + + case RGB_DXT1: + case SRGB_DXT1: + d->formatClass = FormatClass_S3TC_DXT1_RGB; + break; + + case RGBA_DXT1: + case SRGB_Alpha_DXT1: + d->formatClass = FormatClass_S3TC_DXT1_RGBA; + break; + + case RGBA_DXT3: + case SRGB_Alpha_DXT3: + d->formatClass = FormatClass_S3TC_DXT3_RGBA; + break; + + case RGBA_DXT5: + case SRGB_Alpha_DXT5: + d->formatClass = FormatClass_S3TC_DXT5_RGBA; + break; + + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + case QOpenGLTexture::RG11_EAC_UNorm: + case QOpenGLTexture::RG11_EAC_SNorm: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2: + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC: + case QOpenGLTexture::RGB8_ETC1: + case RG3B2: + case R5G6B5: + case RGB5A1: + case RGBA4: + case D16: + case D24: + case D24S8: + case D32: + case D32F: + case D32FS8X24: + case S8: + case DepthFormat: + case AlphaFormat: + case RGBFormat: + case RGBAFormat: + case LuminanceFormat: + case LuminanceAlphaFormat: + case QOpenGLTexture::RGBA_ASTC_4x4: + case QOpenGLTexture::RGBA_ASTC_5x4: + case QOpenGLTexture::RGBA_ASTC_5x5: + case QOpenGLTexture::RGBA_ASTC_6x5: + case QOpenGLTexture::RGBA_ASTC_6x6: + case QOpenGLTexture::RGBA_ASTC_8x5: + case QOpenGLTexture::RGBA_ASTC_8x6: + case QOpenGLTexture::RGBA_ASTC_8x8: + case QOpenGLTexture::RGBA_ASTC_10x5: + case QOpenGLTexture::RGBA_ASTC_10x6: + case QOpenGLTexture::RGBA_ASTC_10x8: + case QOpenGLTexture::RGBA_ASTC_10x10: + case QOpenGLTexture::RGBA_ASTC_12x10: + case QOpenGLTexture::RGBA_ASTC_12x12: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10: + case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12: + d->formatClass = FormatClass_Unique; + break; + } +} + +/*! + Returns the format of this texture object. + + \sa setFormat() +*/ +QOpenGLTexture::TextureFormat QOpenGLTexture::format() const +{ + Q_D(const QOpenGLTexture); + return d->format; +} + +static bool isNpot(int width, int height = 1, int depth = 1) +{ + return width & (width-1) || height & (height-1) || depth & (depth-1); +} + +/*! + Sets the dimensions of this texture object to \a width, + \a height, and \a depth. The default for each dimension is 1. + The maximum allowable texture size is dependent upon your OpenGL + implementation. Allocating storage for a texture less than the + maximum size can still fail if your system is low on resources. + + If a non-power-of-two \a width, \a height or \a depth is provided and your + OpenGL implementation doesn't have support for repeating non-power-of-two + textures, then the wrap mode is automatically set to ClampToEdge. + + \sa width(), height(), depth() +*/ +void QOpenGLTexture::setSize(int width, int height, int depth) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot resize a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setSize()"); + return; + } + + if (isNpot(width, height, depth) && !hasFeature(Feature::NPOTTextureRepeat) && d->target != Target::TargetRectangle) + d->setWrapMode(WrapMode::ClampToEdge); + + switch (d->target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::TargetBuffer: + d->dimensions[0] = width; + Q_UNUSED(height); + Q_UNUSED(depth); + break; + + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetRectangle: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + d->dimensions[0] = width; + d->dimensions[1] = height; + Q_UNUSED(depth); + break; + + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + if (width != height) + qWarning("QAbstractOpenGLTexture::setSize(): Cube map textures must be square"); + d->dimensions[0] = d->dimensions[1] = width; + Q_UNUSED(depth); + break; + + case QOpenGLTexture::Target3D: + d->dimensions[0] = width; + d->dimensions[1] = height; + d->dimensions[2] = depth; + break; + } +} + +/*! + Returns the width of a 1D, 2D or 3D texture. + + \sa height(), depth(), setSize() +*/ +int QOpenGLTexture::width() const +{ + Q_D(const QOpenGLTexture); + return d->dimensions[0]; +} + +/*! + Returns the height of a 2D or 3D texture. + + \sa width(), depth(), setSize() +*/ +int QOpenGLTexture::height() const +{ + Q_D(const QOpenGLTexture); + return d->dimensions[1]; +} + +/*! + Returns the depth of a 3D texture. + + \sa width(), height(), setSize() +*/ +int QOpenGLTexture::depth() const +{ + Q_D(const QOpenGLTexture); + return d->dimensions[2]; +} + +/*! + For texture targets that support mipmaps, this function + sets the requested number of mipmap \a levels to allocate storage + for. This function should be called before storage is allocated + for the texture. + + If the texture target does not support mipmaps this function + has no effect. + + \sa mipLevels(), maximumMipLevels(), isStorageAllocated() +*/ +void QOpenGLTexture::setMipLevels(int levels) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot set mip levels on a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setMipLevels()"); + return; + } + + switch (d->target) { + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::Target3D: + d->requestedMipLevels = levels; + break; + + case QOpenGLTexture::TargetBuffer: + case QOpenGLTexture::TargetRectangle: + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + qWarning("QAbstractOpenGLTexture::setMipLevels(): This texture target does not support mipmaps"); + break; + } +} + +/*! + Returns the number of mipmap levels for this texture. If storage + has not yet been allocated for this texture it returns the + requested number of mipmap levels. + + \sa setMipLevels(), maximumMipLevels(), isStorageAllocated() +*/ +int QOpenGLTexture::mipLevels() const +{ + Q_D(const QOpenGLTexture); + return isStorageAllocated() ? d->mipLevels : d->requestedMipLevels; +} + +/*! + Returns the maximum number of mipmap levels that this texture + can have given the current dimensions. + + \sa setMipLevels(), mipLevels(), setSize() +*/ +int QOpenGLTexture::maximumMipLevels() const +{ + Q_D(const QOpenGLTexture); + return d->maximumMipLevelCount(); +} + +/*! + Sets the number of array \a layers to allocate storage for. This + function should be called before storage is allocated for the texture. + + For targets that do not support array layers this function has + no effect. + + \sa layers(), isStorageAllocated() +*/ +void QOpenGLTexture::setLayers(int layers) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot set layers on a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setLayers()"); + return; + } + + switch (d->target) { + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::Target2DMultisampleArray: + d->layers = layers; + break; + + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetBuffer: + case QOpenGLTexture::TargetRectangle: + case QOpenGLTexture::Target2DMultisample: + qWarning("Texture target does not support array layers"); + break; + } +} + +/*! + Returns the number of array layers for this texture. If + storage has not yet been allocated for this texture then + this function returns the requested number of array layers. + + For texture targets that do not support array layers this + will return 1. + + \sa setLayers(), isStorageAllocated() +*/ +int QOpenGLTexture::layers() const +{ + Q_D(const QOpenGLTexture); + return d->layers; +} + +/*! + Returns the number of faces for this texture. For cubemap + and cubemap array type targets this will be 6. + + For non-cubemap type targets this will return 1. +*/ +int QOpenGLTexture::faces() const +{ + Q_D(const QOpenGLTexture); + return d->faces; +} + +/*! + Sets the number of \a samples to allocate storage for when rendering to + a multisample capable texture target. This function should + be called before storage is allocated for the texture. + + For targets that do not support multisampling this function has + no effect. + + \sa samples(), isStorageAllocated() +*/ +void QOpenGLTexture::setSamples(int samples) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot set sample count on a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setSamples()"); + return; + } + + switch (d->target) { + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + d->samples = samples; + break; + + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::TargetBuffer: + case QOpenGLTexture::TargetRectangle: + + qWarning("Texture target does not support multisampling"); + break; + } +} + +/*! + Returns the number of multisample sample points for this texture. + If storage has not yet been allocated for this texture then + this function returns the requested number of samples. + + For texture targets that do not support multisampling this + will return 0. + + \sa setSamples(), isStorageAllocated() +*/ +int QOpenGLTexture::samples() const +{ + Q_D(const QOpenGLTexture); + return d->samples; +} + +/*! + Sets whether the sample positions and number of samples used with + a multisample capable texture target to \a fixed. If set to \c true + the sample positions and number of samples used are the same for + all texels in the image and will not depend upon the image size or + internal format. This function should be called before storage is allocated + for the texture. + + For targets that do not support multisampling this function has + no effect. + + The default value is \c true. + + \sa isFixedSamplePositions(), isStorageAllocated() +*/ +void QOpenGLTexture::setFixedSamplePositions(bool fixed) +{ + Q_D(QOpenGLTexture); + d->create(); + if (isStorageAllocated()) { + qWarning("Cannot set sample positions on a texture that already has storage allocated.\n" + "To do so, destroy() the texture and then create() and setFixedSamplePositions()"); + return; + } + + switch (d->target) { + case QOpenGLTexture::Target2DMultisample: + case QOpenGLTexture::Target2DMultisampleArray: + d->fixedSamplePositions = fixed; + break; + + case QOpenGLTexture::Target1D: + case QOpenGLTexture::Target2D: + case QOpenGLTexture::Target3D: + case QOpenGLTexture::Target1DArray: + case QOpenGLTexture::Target2DArray: + case QOpenGLTexture::TargetCubeMap: + case QOpenGLTexture::TargetCubeMapArray: + case QOpenGLTexture::TargetBuffer: + case QOpenGLTexture::TargetRectangle: + + qWarning("Texture target does not support multisampling"); + break; + } +} + +/*! + Returns whether this texture uses a fixed pattern of multisample + samples. If storage has not yet been allocated for this texture then + this function returns the requested fixed sample position setting. + + For texture targets that do not support multisampling this + will return \c true. + + \sa setFixedSamplePositions(), isStorageAllocated() +*/ +bool QOpenGLTexture::isFixedSamplePositions() const +{ + Q_D(const QOpenGLTexture); + return d->fixedSamplePositions; +} + +/*! + Allocates server-side storage for this texture object taking + into account, the format, dimensions, mipmap levels, array + layers and cubemap faces. + + Once storage has been allocated it is no longer possible to change + these properties. + + If supported QOpenGLTexture makes use of immutable texture + storage. + + Once storage has been allocated for the texture then pixel data + can be uploaded via one of the setData() overloads. + + \note If immutable texture storage is not available, + then a default pixel format and pixel type will be used to + create the mutable storage. You can use the other + allocateStorage() overload to specify exactly the pixel format + and the pixel type to use when allocating mutable storage; + this is particulary useful under certain OpenGL ES implementations + (notably, OpenGL ES 2), where the pixel format and the pixel type + used at allocation time must perfectly match the format + and the type passed to any subsequent setData() call. + + \sa isStorageAllocated(), setData() +*/ +void QOpenGLTexture::allocateStorage() +{ + Q_D(QOpenGLTexture); + if (d->create()) { + const QOpenGLTexture::PixelFormat pixelFormat = pixelFormatCompatibleWithInternalFormat(d->format); + const QOpenGLTexture::PixelType pixelType = pixelTypeCompatibleWithInternalFormat(d->format); + d->allocateStorage(pixelFormat, pixelType); + } +} + +/*! + \since 5.5 + + Allocates server-side storage for this texture object taking + into account, the format, dimensions, mipmap levels, array + layers and cubemap faces. + + Once storage has been allocated it is no longer possible to change + these properties. + + If supported QOpenGLTexture makes use of immutable texture + storage. However, if immutable texture storage is not available, + then the specified \a pixelFormat and \a pixelType will be used + to allocate mutable storage; note that in certain OpenGL implementations + (notably, OpenGL ES 2) they must perfectly match the format + and the type passed to any subsequent setData() call. + + Once storage has been allocated for the texture then pixel data + can be uploaded via one of the setData() overloads. + + \sa isStorageAllocated(), setData() +*/ +void QOpenGLTexture::allocateStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType) +{ + Q_D(QOpenGLTexture); + if (d->create()) + d->allocateStorage(pixelFormat, pixelType); +} + +/*! + Returns \c true if server-side storage for this texture as been + allocated. + + The texture format, dimensions, mipmap levels and array layers + cannot be altered once storage ihas been allocated. + + \sa allocateStorage(), setSize(), setMipLevels(), setLayers(), setFormat() +*/ +bool QOpenGLTexture::isStorageAllocated() const +{ + Q_D(const QOpenGLTexture); + return d->storageAllocated; +} + +/*! + Attempts to create a texture view onto this texture. A texture + view is somewhat analogous to a view in SQL in that it presents + a restricted or reinterpreted view of the original data. Texture + views do not allocate any more server-side storage, insted relying + on the storage buffer of the source texture. + + Texture views are only available when using immutable storage. For + more information on texture views see + http://www.opengl.org/wiki/Texture_Storage#Texture_views. + + The \a target argument specifies the target to use for the view. + Only some targets can be used depending upon the target of the original + target. For e.g. a view onto a Target1DArray texture can specify + either Target1DArray or Target1D but for the latter the number of + array layers specified with \a minimumLayer and \a maximumLayer must + be exactly 1. + + Simpliar constraints apply for the \a viewFormat. See the above link + and the specification for more details. + + The \a minimumMipmapLevel, \a maximumMipmapLevel, \a minimumLayer, + and \a maximumLayer arguments serve to restrict the parts of the + texture accessible by the texture view. + + If creation of the texture view fails this function will return + 0. If the function succeeds it will return a pointer to a new + QOpenGLTexture object that will return \c true from its isTextureView() + function. + + \sa isTextureView() +*/ +QOpenGLTexture *QOpenGLTexture::createTextureView(Target target, + TextureFormat viewFormat, + int minimumMipmapLevel, int maximumMipmapLevel, + int minimumLayer, int maximumLayer) const +{ + Q_D(const QOpenGLTexture); + if (!isStorageAllocated()) { + qWarning("Cannot set create a texture view of a texture that does not have storage allocated."); + return nullptr; + } + Q_ASSERT(maximumMipmapLevel >= minimumMipmapLevel); + Q_ASSERT(maximumLayer >= minimumLayer); + return d->createTextureView(target, viewFormat, + minimumMipmapLevel, maximumMipmapLevel, + minimumLayer, maximumLayer); +} + +/*! + Returns \c true if this texture object is actually a view onto another + texture object. + + \sa createTextureView() +*/ +bool QOpenGLTexture::isTextureView() const +{ + Q_D(const QOpenGLTexture); + Q_ASSERT(d->textureId); + return d->textureView; +} + +/*! + Uploads pixel \a data for this texture object \a mipLevel, array \a layer, and \a cubeFace. + Storage must have been allocated before uploading pixel data. Some overloads of setData() + will set appropriate dimensions, mipmap levels, and array layers and then allocate storage + for you if they have enough information to do so. This will be noted in the function + documentation. + + The structure of the pixel data pointed to by \a data is specified by \a sourceFormat + and \a sourceType. The pixel data upload can optionally be controlled by \a options. + + If using a compressed format() then you should use setCompressedData() instead of this + function. + + \since 5.3 + \sa setCompressedData() +*/ +void QOpenGLTexture::setData(int mipLevel, int layer, CubeMapFace cubeFace, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + if (!isStorageAllocated()) { + qWarning("Cannot set data on a texture that does not have storage allocated.\n" + "To do so call allocateStorage() before this function"); + return; + } + d->setData(mipLevel, layer, 1, cubeFace, sourceFormat, sourceType, data, options); +} + +/*! + \since 5.9 + \overload + + Parameter \a layerCount is the number of layers in a texture array + that are being uploaded/populated by this call. +*/ +void QOpenGLTexture::setData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType, const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + if (!isStorageAllocated()) { + qWarning("Cannot set data on a texture that does not have storage allocated.\n" + "To do so call allocateStorage() before this function"); + return; + } + d->setData(mipLevel, layer, layerCount, cubeFace, sourceFormat, sourceType, data, options); +} + +/*! + \since 5.3 + \overload +*/ +void QOpenGLTexture::setData(int mipLevel, int layer, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(mipLevel, layer, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options); +} + +/*! + \since 5.3 + \overload +*/ +void QOpenGLTexture::setData(int mipLevel, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(mipLevel, 0, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options); +} + +/*! + \since 5.3 + \overload +*/ +void QOpenGLTexture::setData(PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(0, 0, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options); +} + +/*! + \since 5.14 + \overload + + This overload is to be used to update a part of the texture. Parameters \a + xOffset, \a yOffset, \a zOffset specify the texel offsets within the + texture. Parameters \a width, \a height and \a depth specify the dimensions + of the sub image. + + The structure of the pixel data pointed to by \a data is specified by \a + sourceFormat and \a sourceType. The pixel data upload can optionally be + controlled by \a options. +*/ +void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(xOffset, yOffset, zOffset, + width, height, depth, + 0, 0, 1, + QOpenGLTexture::CubeMapPositiveX, sourceFormat, + sourceType, data, options); +} + +/*! + \since 5.14 + \overload + + This overload is to be used to update a part of the texture. Parameters \a + xOffset, \a yOffset, \a zOffset specify the texel offsets within the + texture. Parameters \a width, \a height and \a depth specify the dimensions + of the sub image. The mip map level the sub image we want to + update is specified with \a mipLevel. + + The structure of the pixel data pointed to by \a data is specified by \a + sourceFormat and \a sourceType. The pixel data upload can optionally be + controlled by \a options. +*/ +void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(xOffset, yOffset, zOffset, + width, height, depth, + mipLevel, 0, 1, + QOpenGLTexture::CubeMapPositiveX, sourceFormat, + sourceType, data, options); +} + +/*! + \since 5.14 + \overload + + This overload is to be used to update a part of the texture. Parameters \a + xOffset, \a yOffset, \a zOffset specify the texel offsets within the + texture. Parameters \a width, \a height and \a depth specify the dimensions + of the sub image. The mip map level and layerof the sub image we want to + update are specified with \a mipLevel and \a layer. + + The structure of the pixel data pointed to by \a data is specified by \a + sourceFormat and \a sourceType. The pixel data upload can optionally be + controlled by \a options. +*/ +void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, int layer, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(xOffset, yOffset, zOffset, + width, height, depth, + mipLevel, layer, 1, + QOpenGLTexture::CubeMapPositiveX, sourceFormat, + sourceType, data, options); +} + +/*! + \since 5.14 + \overload + + This overload is to be used to update a part of the texture. Parameters \a + xOffset, \a yOffset, \a zOffset specify the texel offsets within the + texture. Parameters \a width, \a height and \a depth specify the dimensions + of the sub image.The mip map level, layer and cube map face of the sub + image we want to update are specified with \a mipLevel, \a layer and \a + face. + + The structure of the pixel data pointed to by \a data is specified by \a + sourceFormat and \a sourceType. The pixel data upload can optionally be + controlled by \a options. +*/ +void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, int layer, + CubeMapFace face, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(xOffset, yOffset, zOffset, + width, height, depth, + mipLevel, layer, 1, + face, sourceFormat, + sourceType, data, options); +} + +/*! + \since 5.14 + \overload + + This overload is to be used to update a part of the texture. Parameters \a + xOffset, \a yOffset, \a zOffset specify the texel offsets within the + texture. Parameters \a width, \a height and \a depth specify the dimensions + of the sub image.The mip map level, starting layer, cube map face and + number of layers of the sub image we want to update are specified with \a + mipLevel, \a layer, \a face and \a layerCount. + + The structure of the pixel data pointed to by \a data is specified by \a + sourceFormat and \a sourceType. The pixel data upload can optionally be + controlled by \a options. +*/ +void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, int layer, + CubeMapFace face, int layerCount, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(xOffset, yOffset, zOffset, + width, height, depth, + mipLevel, layer, layerCount, + face, sourceFormat, + sourceType, data, options); +} + +#if QT_DEPRECATED_SINCE(5, 3) +/*! + \obsolete + \overload + + \sa setCompressedData() +*/ +void QOpenGLTexture::setData(int mipLevel, int layer, CubeMapFace cubeFace, + PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + if (!isStorageAllocated()) { + qWarning("Cannot set data on a texture that does not have storage allocated.\n" + "To do so call allocateStorage() before this function"); + return; + } + d->setData(mipLevel, layer, 1, cubeFace, sourceFormat, sourceType, data, options); +} + +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setData(int mipLevel, int layer, + PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(mipLevel, layer, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options); +} + +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setData(int mipLevel, + PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(mipLevel, 0, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options); +} + +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setData(PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setData(0, 0, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options); +} +#endif + +/*! + This overload of setData() will allocate storage for you. + The pixel data is contained in \a image. Mipmaps are generated by default. + Set \a genMipMaps to \l DontGenerateMipMaps to turn off mipmap generation. + + \overload +*/ +void QOpenGLTexture::setData(const QImage& image, MipMapGeneration genMipMaps) +{ + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) { + qWarning("QOpenGLTexture::setData() requires a valid current context"); + return; + } + + if (image.isNull()) { + qWarning("QOpenGLTexture::setData() tried to set a null image"); + return; + } + + if (context->isOpenGLES() && context->format().majorVersion() < 3) + setFormat(QOpenGLTexture::RGBAFormat); + else + setFormat(QOpenGLTexture::RGBA8_UNorm); + + setSize(image.width(), image.height()); + setMipLevels(genMipMaps == GenerateMipMaps ? maximumMipLevels() : 1); + allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + + // Upload pixel data and generate mipmaps + QImage glImage = image.convertToFormat(QImage::Format_RGBA8888); + QOpenGLPixelTransferOptions uploadOptions; + uploadOptions.setAlignment(1); + setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, glImage.constBits(), &uploadOptions); +} + +/*! + Uploads compressed pixel \a data to \a mipLevel, array \a layer, and \a cubeFace. + The pixel transfer can optionally be controlled with \a options. The \a dataSize + argument should specify the size of the data pointed to by \a data. + + If not using a compressed format() then you should use setData() instead of this + function. + + \since 5.3 +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int layer, CubeMapFace cubeFace, + int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + if (!isStorageAllocated()) { + qWarning("Cannot set data on a texture that does not have storage allocated.\n" + "To do so call allocateStorage() before this function"); + return; + } + d->setCompressedData(mipLevel, layer, 1, cubeFace, dataSize, data, options); +} + +/*! + \since 5.9 + \overload + + Parameter \a layerCount is the number of layers in a texture array + that are being uploaded/populated by this call. +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, int dataSize, const void *data, const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + if (!isStorageAllocated()) { + qWarning("Cannot set data on a texture that does not have storage allocated.\n" + "To do so call allocateStorage() before this function"); + return; + } + d->setCompressedData(mipLevel, layer, layerCount, cubeFace, dataSize, data, options); +} + +/*! + \overload +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int layer, int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setCompressedData(mipLevel, layer, 1, QOpenGLTexture::CubeMapPositiveX, dataSize, data, options); +} + +/*! + \overload +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setCompressedData(mipLevel, 0, 1, QOpenGLTexture::CubeMapPositiveX, dataSize, data, options); +} + +/*! + \overload +*/ +void QOpenGLTexture::setCompressedData(int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setCompressedData(0, 0, 1, QOpenGLTexture::CubeMapPositiveX, dataSize, data, options); +} + +#if QT_DEPRECATED_SINCE(5, 3) +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int layer, CubeMapFace cubeFace, + int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + if (!isStorageAllocated()) { + qWarning("Cannot set data on a texture that does not have storage allocated.\n" + "To do so call allocateStorage() before this function"); + return; + } + d->setCompressedData(mipLevel, layer, 1, cubeFace, dataSize, data, options); +} + +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int layer, int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setCompressedData(mipLevel, layer, 1, QOpenGLTexture::CubeMapPositiveX, dataSize, data, options); +} + +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setCompressedData(int mipLevel, int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setCompressedData(mipLevel, 0, 1, QOpenGLTexture::CubeMapPositiveX, dataSize, data, options); +} + +/*! + \obsolete + \overload +*/ +void QOpenGLTexture::setCompressedData(int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->textureId); + d->setCompressedData(0, 0, 1, QOpenGLTexture::CubeMapPositiveX, dataSize, data, options); +} +#endif + +/*! + Returns \c true if your OpenGL implementation and version supports the texture + feature \a feature. +*/ +bool QOpenGLTexture::hasFeature(Feature feature) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) { + qWarning("QOpenGLTexture::hasFeature() requires a valid current context"); + return false; + } + + QSurfaceFormat f = ctx->format(); + + bool supported = false; + +#if !defined(QT_OPENGL_ES_2) + if (!ctx->isOpenGLES()) { + switch (feature) { + case ImmutableMultisampleStorage: + supported = f.version() >= qMakePair(4, 3) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_storage_multisample")); + break; + + case TextureBuffer: + supported = f.version() >= qMakePair(3, 0) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_buffer_object")); + break; + + case StencilTexturing: + supported = f.version() >= qMakePair(4, 3) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_stencil_texturing")); + break; + + case ImmutableStorage: + supported = f.version() >= qMakePair(4, 2) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_storage")) + || ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_storage")); + break; + + case TextureCubeMapArrays: + supported = f.version() >= qMakePair(4, 0) + || ctx->hasExtension(QByteArrayLiteral("ARB_texture_cube_map_array")); + break; + + case Swizzle: + supported = f.version() >= qMakePair(3, 3) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_swizzle")); + break; + + case TextureMultisample: + supported = f.version() >= qMakePair(3, 2) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_multisample")); + break; + + case TextureArrays: + supported = f.version() >= qMakePair(3, 0) + || ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_array")); + break; + + case TextureRectangle: + supported = f.version() >= qMakePair(2, 1) + || ctx->hasExtension(QByteArrayLiteral("ARB_texture_rectangle")); + break; + + case Texture3D: + supported = f.version() >= qMakePair(1, 3); + break; + + case AnisotropicFiltering: + supported = ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_filter_anisotropic")); + break; + + case NPOTTextures: + case NPOTTextureRepeat: + supported = ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_non_power_of_two")); + break; + + case Texture1D: + supported = f.version() >= qMakePair(1, 1); + break; + + case TextureComparisonOperators: + // GL 1.4 and GL_ARB_shadow alone support only LEQUAL and GEQUAL; + // since we're talking about history anyhow avoid to be extra pedantic + // in the feature set, and simply claim supported if we have the full set of operators + // (which has been added into 1.5 / GL_EXT_shadow_funcs). + supported = f.version() >= qMakePair(1, 5) + || (ctx->hasExtension(QByteArrayLiteral("GL_ARB_shadow")) + && ctx->hasExtension(QByteArrayLiteral("GL_EXT_shadow_funcs"))); + break; + + case TextureMipMapLevel: + supported = f.version() >= qMakePair(1, 2); + break; + + case MaxFeatureFlag: + break; + } + } + + if (ctx->isOpenGLES()) +#endif + { + const char *renderer = reinterpret_cast<const char *>(ctx->functions()->glGetString(GL_RENDERER)); + switch (feature) { + case ImmutableStorage: + supported = (f.version() >= qMakePair(3, 0) || ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_storage"))) + && !(renderer && strstr(renderer, "Mali")); // do not use on Mali: QTBUG-45106 + break; + + case ImmutableMultisampleStorage: + supported = f.version() >= qMakePair(3, 1); + break; + + case TextureRectangle: + break; + + case TextureArrays: + supported = f.version() >= qMakePair(3, 0); + break; + + case Texture3D: + supported = f.version() >= qMakePair(3, 0) + || ctx->hasExtension(QByteArrayLiteral("GL_OES_texture_3D")); + break; + + case TextureMultisample: + supported = f.version() >= qMakePair(3, 1); + break; + + case TextureBuffer: + break; + + case TextureCubeMapArrays: + break; + + case Swizzle: + supported = f.version() >= qMakePair(3, 0); + break; + + case StencilTexturing: + break; + + case AnisotropicFiltering: + supported = ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_filter_anisotropic")); + break; + + case NPOTTextures: + case NPOTTextureRepeat: + supported = f.version() >= qMakePair(3,0) + || ctx->hasExtension(QByteArrayLiteral("GL_OES_texture_npot")) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_non_power_of_two")); + break; + + case Texture1D: + break; + + case TextureComparisonOperators: + supported = f.version() >= qMakePair(3, 0) + || ctx->hasExtension(QByteArrayLiteral("GL_EXT_shadow_samplers")); + break; + + case TextureMipMapLevel: + supported = f.version() >= qMakePair(3, 0); + break; + + case MaxFeatureFlag: + break; + } + } + + return supported; +} + +/*! + Sets the base mipmap level used for all texture lookups with this texture to \a baseLevel. + + \note This function has no effect on Qt built for OpenGL ES 2. + \sa mipBaseLevel(), setMipMaxLevel(), setMipLevelRange() +*/ +void QOpenGLTexture::setMipBaseLevel(int baseLevel) +{ + Q_D(QOpenGLTexture); + d->create(); + if (!d->features.testFlag(TextureMipMapLevel)) { + qWarning("QOpenGLTexture::setMipBaseLevel: requires OpenGL >= 1.2 or OpenGL ES >= 3.0"); + return; + } + Q_ASSERT(d->textureId); + Q_ASSERT(d->texFuncs); + Q_ASSERT(baseLevel <= d->maxLevel); + d->baseLevel = baseLevel; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_BASE_LEVEL, baseLevel); +} + +/*! + Returns the mipmap base level used for all texture lookups with this texture. + The default is 0. + + \sa setMipBaseLevel(), mipMaxLevel(), mipLevelRange() +*/ +int QOpenGLTexture::mipBaseLevel() const +{ + Q_D(const QOpenGLTexture); + return d->baseLevel; +} + +/*! + Sets the maximum mipmap level used for all texture lookups with this texture to \a maxLevel. + + \note This function has no effect on Qt built for OpenGL ES 2. + \sa mipMaxLevel(), setMipBaseLevel(), setMipLevelRange() +*/ +void QOpenGLTexture::setMipMaxLevel(int maxLevel) +{ + Q_D(QOpenGLTexture); + d->create(); + if (!d->features.testFlag(TextureMipMapLevel)) { + qWarning("QOpenGLTexture::setMipMaxLevel: requires OpenGL >= 1.2 or OpenGL ES >= 3.0"); + return; + } + Q_ASSERT(d->textureId); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->baseLevel <= maxLevel); + d->maxLevel = maxLevel; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAX_LEVEL, maxLevel); +} + +/*! + Returns the mipmap maximum level used for all texture lookups with this texture. + + \sa setMipMaxLevel(), mipBaseLevel(), mipLevelRange() +*/ +int QOpenGLTexture::mipMaxLevel() const +{ + Q_D(const QOpenGLTexture); + return d->maxLevel; +} + +/*! + Sets the range of mipmap levels that can be used for texture lookups with this texture + to range from \a baseLevel to \a maxLevel. + + \note This function has no effect on Qt built for OpenGL ES 2. + \sa setMipBaseLevel(), setMipMaxLevel(), mipLevelRange() +*/ +void QOpenGLTexture::setMipLevelRange(int baseLevel, int maxLevel) +{ + Q_D(QOpenGLTexture); + d->create(); + if (!d->features.testFlag(TextureMipMapLevel)) { + qWarning("QOpenGLTexture::setMipLevelRange: requires OpenGL >= 1.2 or OpenGL ES >= 3.0"); + return; + } + Q_ASSERT(d->textureId); + Q_ASSERT(d->texFuncs); + Q_ASSERT(baseLevel <= maxLevel); + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_BASE_LEVEL, baseLevel); + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAX_LEVEL, maxLevel); +} + +/*! + Returns the range of mipmap levels that can be used for texture lookups with this texture. + + \sa mipBaseLevel(), mipMaxLevel() +*/ +QPair<int, int> QOpenGLTexture::mipLevelRange() const +{ + Q_D(const QOpenGLTexture); + return qMakePair(d->baseLevel, d->maxLevel); +} + +/*! + If \a enabled is \c true, enables automatic mipmap generation for this texture object + to occur whenever the level 0 mipmap data is set via setData(). + + The automatic mipmap generation is enabled by default. + + \note Mipmap generation is not supported for compressed textures with OpenGL ES 2.0. + + \sa isAutoMipMapGenerationEnabled(), generateMipMaps() +*/ +void QOpenGLTexture::setAutoMipMapGenerationEnabled(bool enabled) +{ + Q_D(QOpenGLTexture); + d->autoGenerateMipMaps = enabled; +} + +/*! + Returns whether auto mipmap generation is enabled for this texture object. + + \sa setAutoMipMapGenerationEnabled(), generateMipMaps() +*/ +bool QOpenGLTexture::isAutoMipMapGenerationEnabled() const +{ + Q_D(const QOpenGLTexture); + return d->autoGenerateMipMaps; +} + +/*! + Generates mipmaps for this texture object from mipmap level 0. If you are + using a texture target and filtering option that requires mipmaps and you + have disabled automatic mipmap generation then you need to call this function + or the overload to create the mipmap chain. + + \note Mipmap generation is not supported for compressed textures with OpenGL ES. + + \sa setAutoMipMapGenerationEnabled(), setMipLevels(), mipLevels() +*/ +void QOpenGLTexture::generateMipMaps() +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + if (isCompressedFormat(d->format)) { + if (QOpenGLContext *ctx = QOpenGLContext::currentContext()) + if (ctx->isOpenGLES()) + return; + } + d->texFuncs->glGenerateTextureMipmap(d->textureId, d->target, d->bindingTarget); +} + +/*! + Generates mipmaps for this texture object from mipmap level \a baseLevel. If you are + using a texture target and filtering option that requires mipmaps and you + have disabled automatic mipmap generation then you need to call this function + or the overload to create the mipmap chain. + + The generation of mipmaps to above \a baseLevel is achieved by setting the mipmap + base level to \a baseLevel and then generating the mipmap chain. If \a resetBaseLevel + is \c true, then the baseLevel of the texture will be reset to its previous value. + + \sa setAutoMipMapGenerationEnabled(), setMipLevels(), mipLevels() +*/ +void QOpenGLTexture::generateMipMaps(int baseLevel, bool resetBaseLevel) +{ + Q_D(QOpenGLTexture); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + if (isCompressedFormat(d->format)) { + if (QOpenGLContext *ctx = QOpenGLContext::currentContext()) + if (ctx->isOpenGLES()) + return; + } + int oldBaseLevel; + if (resetBaseLevel) + oldBaseLevel = mipBaseLevel(); + setMipBaseLevel(baseLevel); + d->texFuncs->glGenerateTextureMipmap(d->textureId, d->target, d->bindingTarget); + if (resetBaseLevel) + setMipBaseLevel(oldBaseLevel); +} + +/*! + GLSL shaders are able to reorder the components of the vec4 returned by texture + functions. It is also desirable to be able to control this reordering from CPU + side code. This is made possible by swizzle masks since OpenGL 3.3. + + Each component of the texture can be mapped to one of the SwizzleValue options. + + This function maps \a component to the output \a value. + + \note This function has no effect on Mac and Qt built for OpenGL ES 2. + \sa swizzleMask() +*/ +void QOpenGLTexture::setSwizzleMask(SwizzleComponent component, SwizzleValue value) +{ +#if !defined(Q_OS_MAC) && !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + if (!d->features.testFlag(Swizzle)) { + qWarning("QOpenGLTexture::setSwizzleMask() requires OpenGL >= 3.3"); + return; + } + d->swizzleMask[component - SwizzleRed] = value; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, component, value); + return; + } +#else + Q_UNUSED(component); + Q_UNUSED(value); +#endif + qWarning("QOpenGLTexture: Texture swizzling is not supported"); +} + +/*! + Parameters \a {r}, \a {g}, \a {b}, and \a {a} are values used for setting + the colors red, green, blue, and the alpha value. + \overload +*/ +void QOpenGLTexture::setSwizzleMask(SwizzleValue r, SwizzleValue g, + SwizzleValue b, SwizzleValue a) +{ +#if !defined(Q_OS_MAC) && !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + if (!d->features.testFlag(Swizzle)) { + qWarning("QOpenGLTexture::setSwizzleMask() requires OpenGL >= 3.3"); + return; + } + GLint swizzleMask[] = {GLint(r), GLint(g), GLint(b), GLint(a)}; + d->swizzleMask[0] = r; + d->swizzleMask[1] = g; + d->swizzleMask[2] = b; + d->swizzleMask[3] = a; + d->texFuncs->glTextureParameteriv(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + return; + } +#else + Q_UNUSED(r); + Q_UNUSED(g); + Q_UNUSED(b); + Q_UNUSED(a); +#endif + qWarning("QOpenGLTexture: Texture swizzling is not supported"); +} + +/*! + Returns the swizzle mask for texture \a component. +*/ +QOpenGLTexture::SwizzleValue QOpenGLTexture::swizzleMask(SwizzleComponent component) const +{ + Q_D(const QOpenGLTexture); + return d->swizzleMask[component - SwizzleRed]; +} + +/*! + \enum QOpenGLTexture::DepthStencilMode + \since 5.4 + This enum specifies which component of a depth/stencil texture is + accessed when the texture is sampled. + + \value DepthMode Equivalent to GL_DEPTH_COMPONENT. + \value StencilMode Equivalent to GL_STENCIL_INDEX. +*/ + +/*! + If using a texture that has a combined depth/stencil format this function sets + which component of the texture is accessed to \a mode. + + When the parameter is set to DepthMode, then accessing it from the + shader will access the depth component as a single float, as normal. But when + the parameter is set to StencilMode, the shader will access the stencil component. + + \note This function has no effect on Mac and Qt built for OpenGL ES 2. + \since 5.4 + \sa depthStencilMode() +*/ +void QOpenGLTexture::setDepthStencilMode(QOpenGLTexture::DepthStencilMode mode) +{ +#if !defined(Q_OS_MAC) && !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + if (!d->features.testFlag(StencilTexturing)) { + qWarning("QOpenGLTexture::setDepthStencilMode() requires OpenGL >= 4.3 or GL_ARB_stencil_texturing"); + return; + } + d->depthStencilMode = mode; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_DEPTH_STENCIL_TEXTURE_MODE, mode); + return; + } +#else + Q_UNUSED(mode); +#endif + qWarning("QOpenGLTexture: DepthStencil Mode is not supported"); +} + +/*! + Returns the depth stencil mode for textures using a combined depth/stencil format. + + \since 5.4 + \sa setDepthStencilMode() +*/ +QOpenGLTexture::DepthStencilMode QOpenGLTexture::depthStencilMode() const +{ + Q_D(const QOpenGLTexture); + return d->depthStencilMode; +} + +/*! + \enum QOpenGLTexture::ComparisonFunction + \since 5.5 + This enum specifies which comparison operator is used when texture comparison + is enabled on this texture. + + \value CompareLessEqual Equivalent to GL_LEQUAL. + \value CompareGreaterEqual Equivalent to GL_GEQUAL. + \value CompareLess Equivalent to GL_LESS. + \value CompareGreater Equivalent to GL_GREATER. + \value CompareEqual Equivalent to GL_EQUAL. + \value CommpareNotEqual Equivalent to GL_NOTEQUAL. + \value CompareAlways Equivalent to GL_ALWAYS. + \value CompareNever Equivalent to GL_NEVER. + +*/ + +/*! + \since 5.5 + + Sets the texture comparison function on this texture to \a function. The texture + comparison function is used by shadow samplers when sampling a depth texture. + + \sa comparisonFunction() +*/ +void QOpenGLTexture::setComparisonFunction(QOpenGLTexture::ComparisonFunction function) +{ + Q_D(QOpenGLTexture); + d->create(); + if (!d->features.testFlag(TextureComparisonOperators)) { + qWarning("QOpenGLTexture::setComparisonFunction: requires OpenGL >= 1.5 or OpenGL ES >= 3.0"); + return; + } + d->comparisonFunction = function; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_COMPARE_FUNC, function); +} + +/*! + \since 5.5 + + Returns the texture comparison operator set on this texture. By default, a + texture has a CompareLessEqual comparison function. + + \sa setComparisonFunction() +*/ +QOpenGLTexture::ComparisonFunction QOpenGLTexture::comparisonFunction() const +{ + Q_D(const QOpenGLTexture); + return d->comparisonFunction; +} + +/*! + \enum QOpenGLTexture::ComparisonMode + \since 5.5 + This enum specifies which comparison mode is used when sampling this texture. + + \value CompareRefToTexture Equivalent to GL_COMPARE_REF_TO_TEXTURE. + \value CompareNone Equivalent to GL_NONE. +*/ + +/*! + \since 5.5 + + Sets the texture comparison mode on this texture to \a mode. The texture + comparison mode is used by shadow samplers when sampling a depth texture. + + \sa comparisonMode() +*/ +void QOpenGLTexture::setComparisonMode(QOpenGLTexture::ComparisonMode mode) +{ + Q_D(QOpenGLTexture); + d->create(); + if (!d->features.testFlag(TextureComparisonOperators)) { + qWarning("QOpenGLTexture::setComparisonMode: requires OpenGL >= 1.5 or OpenGL ES >= 3.0"); + return; + } + d->comparisonMode = mode; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_COMPARE_MODE, mode); +} + +/*! + \since 5.5 + + Returns the texture comparison mode set on this texture. By default, a + texture has a CompareNone comparison mode (i.e. comparisons are disabled). + + \sa setComparisonMode() +*/ +QOpenGLTexture::ComparisonMode QOpenGLTexture::comparisonMode() const +{ + Q_D(const QOpenGLTexture); + return d->comparisonMode; +} + +/*! + Sets the filter used for minification to \a filter. + + \sa minificationFilter(), setMagnificationFilter(), setMinMagFilters() +*/ +void QOpenGLTexture::setMinificationFilter(QOpenGLTexture::Filter filter) +{ + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + d->minFilter = filter; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MIN_FILTER, filter); +} + +/*! + Returns the minification filter. + + \sa setMinificationFilter() +*/ +QOpenGLTexture::Filter QOpenGLTexture::minificationFilter() const +{ + Q_D(const QOpenGLTexture); + return d->minFilter; +} + +/*! + Sets the magnification filter to \a filter. + + \sa magnificationFilter(), setMinificationFilter(), setMinMagFilters() +*/ +void QOpenGLTexture::setMagnificationFilter(QOpenGLTexture::Filter filter) +{ + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + d->magFilter = filter; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAG_FILTER, filter); +} + +/*! + Returns the magnification filter. + + \sa setMagnificationFilter() +*/ +QOpenGLTexture::Filter QOpenGLTexture::magnificationFilter() const +{ + Q_D(const QOpenGLTexture); + return d->magFilter; +} + +/*! + Sets the minification filter to \a minificationFilter and the magnification filter + to \a magnificationFilter. + + \sa minMagFilters(), setMinificationFilter(), setMagnificationFilter() +*/ +void QOpenGLTexture::setMinMagFilters(QOpenGLTexture::Filter minificationFilter, + QOpenGLTexture::Filter magnificationFilter) +{ + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + d->minFilter = minificationFilter; + d->magFilter = magnificationFilter; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MIN_FILTER, minificationFilter); + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAG_FILTER, magnificationFilter); +} + +/*! + Returns the current minification and magnification filters. + + \sa setMinMagFilters() +*/ +QPair<QOpenGLTexture::Filter, QOpenGLTexture::Filter> QOpenGLTexture::minMagFilters() const +{ + Q_D(const QOpenGLTexture); + return QPair<QOpenGLTexture::Filter, QOpenGLTexture::Filter>(d->minFilter, d->magFilter); +} + +/*! + If your OpenGL implementation supports the GL_EXT_texture_filter_anisotropic extension + this function sets the maximum anisotropy level to \a anisotropy. + + \sa maximumAnisotropy() +*/ +void QOpenGLTexture::setMaximumAnisotropy(float anisotropy) +{ + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + if (!d->features.testFlag(AnisotropicFiltering)) { + qWarning("QOpenGLTexture::setMaximumAnisotropy() requires GL_EXT_texture_filter_anisotropic"); + return; + } + d->maxAnisotropy = anisotropy; + d->texFuncs->glTextureParameteri(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); +} + +/*! + Returns the maximum level of anisotropy to be accounted for when performing texture lookups. + This requires the GL_EXT_texture_filter_anisotropic extension. + + \sa setMaximumAnisotropy() +*/ +float QOpenGLTexture::maximumAnisotropy() const +{ + Q_D(const QOpenGLTexture); + return d->maxAnisotropy; +} + +/*! + Sets the wrap (or repeat mode) for all texture dimentions to \a mode. + + \sa wrapMode() +*/ +void QOpenGLTexture::setWrapMode(QOpenGLTexture::WrapMode mode) +{ + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + d->setWrapMode(mode); +} + +/*! + Holds the texture dimension \a direction. + \overload +*/ +void QOpenGLTexture::setWrapMode(QOpenGLTexture::CoordinateDirection direction, QOpenGLTexture::WrapMode mode) +{ + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + d->setWrapMode(direction, mode); +} + +/*! + Returns the wrap mode for the texture dimension \a direction. + + \sa setWrapMode() +*/ +QOpenGLTexture::WrapMode QOpenGLTexture::wrapMode(QOpenGLTexture::CoordinateDirection direction) const +{ + Q_D(const QOpenGLTexture); + return d->wrapMode(direction); +} + +/*! + Sets the border color of the texture to \a color. + + \note This function has no effect on Mac and Qt built for OpenGL ES 2. + \sa borderColor() +*/ +void QOpenGLTexture::setBorderColor(const QColor &color) +{ + setBorderColor(static_cast<float>(color.redF()), static_cast<float>(color.greenF()), + static_cast<float>(color.blueF()), static_cast<float>(color.alphaF())); +} + +/*! + Sets the color red to \a {r}, green to \a {g}, blue to \a {b}, and \a {a} to the + alpha value. + \overload +*/ +void QOpenGLTexture::setBorderColor(float r, float g, float b, float a) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + float values[4]; + values[0] = r; + values[1] = g; + values[2] = b; + values[3] = a; + d->borderColor.clear(); + for (int i = 0; i < 4; ++i) + d->borderColor.append(QVariant(values[i])); + d->texFuncs->glTextureParameterfv(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_BORDER_COLOR, values); + return; + } +#else + Q_UNUSED(r); + Q_UNUSED(g); + Q_UNUSED(b); + Q_UNUSED(a); +#endif + qWarning("QOpenGLTexture: Border color is not supported"); +} + +/*! + Sets the color red to \a {r}, green to \a {g}, blue to \a {b}, and the alpha + value to \a {a}. + \overload +*/ +void QOpenGLTexture::setBorderColor(int r, int g, int b, int a) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + int values[4]; + values[0] = r; + values[1] = g; + values[2] = b; + values[3] = a; + d->borderColor.clear(); + for (int i = 0; i < 4; ++i) + d->borderColor.append(QVariant(values[i])); + d->texFuncs->glTextureParameteriv(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_BORDER_COLOR, values); + return; + } +#else + Q_UNUSED(r); + Q_UNUSED(g); + Q_UNUSED(b); + Q_UNUSED(a); +#endif + qWarning("QOpenGLTexture: Border color is not supported"); + + // TODO Handle case of using glTextureParameterIiv() based on format +} + +/*! + Sets the color red to \a {r}, green to \a {g}, blue to \a {b}, and the alpha + value to \a {a}. + \overload +*/ +void QOpenGLTexture::setBorderColor(uint r, uint g, uint b, uint a) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + int values[4]; + values[0] = int(r); + values[1] = int(g); + values[2] = int(b); + values[3] = int(a); + d->borderColor.clear(); + for (int i = 0; i < 4; ++i) + d->borderColor.append(QVariant(values[i])); + d->texFuncs->glTextureParameteriv(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_BORDER_COLOR, values); + return; + } +#else + Q_UNUSED(r); + Q_UNUSED(g); + Q_UNUSED(b); + Q_UNUSED(a); +#endif + qWarning("QOpenGLTexture: Border color is not supported"); + + // TODO Handle case of using glTextureParameterIuiv() based on format +} + +/*! + Returns the borderColor of this texture. + + \sa setBorderColor() +*/ +QColor QOpenGLTexture::borderColor() const +{ + Q_D(const QOpenGLTexture); + QColor c(0.0f, 0.0f, 0.0f, 0.0f); + if (!d->borderColor.isEmpty()) { + c.setRedF(d->borderColor.at(0).toFloat()); + c.setGreenF(d->borderColor.at(1).toFloat()); + c.setBlueF(d->borderColor.at(2).toFloat()); + c.setAlphaF(d->borderColor.at(3).toFloat()); + } + return c; +} + +/*! + Writes the texture border color into the first four elements + of the array pointed to by \a border. + + \sa setBorderColor() +*/ +void QOpenGLTexture::borderColor(float *border) const +{ + Q_D(const QOpenGLTexture); + Q_ASSERT(border); + if (d->borderColor.isEmpty()) { + for (int i = 0; i < 4; ++i) + border[i] = 0.0f; + } else { + for (int i = 0; i < 4; ++i) + border[i] = d->borderColor.at(i).toFloat(); + } +} + +/*! + Writes the texture border color into the first four elements + of the array pointed to by \a border. + + \overload +*/ +void QOpenGLTexture::borderColor(int *border) const +{ + Q_D(const QOpenGLTexture); + Q_ASSERT(border); + if (d->borderColor.isEmpty()) { + for (int i = 0; i < 4; ++i) + border[i] = 0; + } else { + for (int i = 0; i < 4; ++i) + border[i] = d->borderColor.at(i).toInt(); + } +} + +/*! + Writes the texture border color into the first four elements + of the array pointed to by \a border. + + \overload +*/ +void QOpenGLTexture::borderColor(unsigned int *border) const +{ + Q_D(const QOpenGLTexture); + Q_ASSERT(border); + if (d->borderColor.isEmpty()) { + for (int i = 0; i < 4; ++i) + border[i] = 0; + } else { + for (int i = 0; i < 4; ++i) + border[i] = d->borderColor.at(i).toUInt(); + } +} + +/*! + Sets the minimum level of detail to \a value. This limits the selection of highest + resolution mipmap (lowest mipmap level). The default value is -1000. + + \note This function has no effect on Qt built for OpenGL ES 2. + \sa minimumLevelOfDetail(), setMaximumLevelOfDetail(), setLevelOfDetailRange() +*/ +void QOpenGLTexture::setMinimumLevelOfDetail(float value) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + Q_ASSERT(value < d->maxLevelOfDetail); + d->minLevelOfDetail = value; + d->texFuncs->glTextureParameterf(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MIN_LOD, value); + return; + } +#else + Q_UNUSED(value); +#endif + qWarning("QOpenGLTexture: Detail level is not supported"); +} + +/*! + Returns the minimum level of detail parameter. + + \sa setMinimumLevelOfDetail(), maximumLevelOfDetail(), levelOfDetailRange() +*/ +float QOpenGLTexture::minimumLevelOfDetail() const +{ + Q_D(const QOpenGLTexture); + return d->minLevelOfDetail; +} + +/*! + Sets the maximum level of detail to \a value. This limits the selection of lowest + resolution mipmap (highest mipmap level). The default value is 1000. + + \note This function has no effect on Qt built for OpenGL ES 2. + \sa maximumLevelOfDetail(), setMinimumLevelOfDetail(), setLevelOfDetailRange() +*/ +void QOpenGLTexture::setMaximumLevelOfDetail(float value) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + Q_ASSERT(value > d->minLevelOfDetail); + d->maxLevelOfDetail = value; + d->texFuncs->glTextureParameterf(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAX_LOD, value); + return; + } +#else + Q_UNUSED(value); +#endif + qWarning("QOpenGLTexture: Detail level is not supported"); +} + +/*! + Returns the maximum level of detail parameter. + + \sa setMaximumLevelOfDetail(), minimumLevelOfDetail(), levelOfDetailRange() +*/ +float QOpenGLTexture::maximumLevelOfDetail() const +{ + Q_D(const QOpenGLTexture); + return d->maxLevelOfDetail; +} + +/*! + Sets the minimum level of detail parameters to \a min and the maximum level + to \a max. + \note This function has no effect on Qt built for OpenGL ES 2. + \sa levelOfDetailRange(), setMinimumLevelOfDetail(), setMaximumLevelOfDetail() +*/ +void QOpenGLTexture::setLevelOfDetailRange(float min, float max) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + Q_ASSERT(min < max); + d->minLevelOfDetail = min; + d->maxLevelOfDetail = max; + d->texFuncs->glTextureParameterf(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MIN_LOD, min); + d->texFuncs->glTextureParameterf(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_MAX_LOD, max); + return; + } +#else + Q_UNUSED(min); + Q_UNUSED(max); +#endif + qWarning("QOpenGLTexture: Detail level is not supported"); +} + +/*! + Returns the minimum and maximum level of detail parameters. + + \sa setLevelOfDetailRange(), minimumLevelOfDetail(), maximumLevelOfDetail() +*/ +QPair<float, float> QOpenGLTexture::levelOfDetailRange() const +{ + Q_D(const QOpenGLTexture); + return qMakePair(d->minLevelOfDetail, d->maxLevelOfDetail); +} + +/*! + Sets the level of detail bias to \a bias. + Level of detail bias affects the point at which mipmapping levels change. + Increasing values for level of detail bias makes the overall images blurrier + or smoother. Decreasing values make the overall images sharper. + + \note This function has no effect on Qt built for OpenGL ES 2. + \sa levelofDetailBias() +*/ +void QOpenGLTexture::setLevelofDetailBias(float bias) +{ +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + Q_D(QOpenGLTexture); + d->create(); + Q_ASSERT(d->texFuncs); + Q_ASSERT(d->textureId); + d->levelOfDetailBias = bias; + d->texFuncs->glTextureParameterf(d->textureId, d->target, d->bindingTarget, GL_TEXTURE_LOD_BIAS, bias); + return; + } +#else + Q_UNUSED(bias); +#endif + qWarning("QOpenGLTexture: Detail level is not supported"); +} + +/*! + Returns the level of detail bias parameter. + + \sa setLevelofDetailBias() +*/ +float QOpenGLTexture::levelofDetailBias() const +{ + Q_D(const QOpenGLTexture); + return d->levelOfDetailBias; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QOpenGLTexture *t) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + debug << "QOpenGLTexture("; + if (t) { + const QOpenGLTexturePrivate *d = t->d_ptr.data(); + debug << d->target << ", bindingTarget=" << d->bindingTarget + << ", size=[" << d->dimensions[0] + << ", " << d->dimensions[1]; + if (d->target == QOpenGLTexture::Target3D) + debug << ", " << d->dimensions[2]; + debug << "], format=" << d->format << ", formatClass=" << d->formatClass; + if (t->isCreated()) + debug << ", textureId=" << d->textureId; + if (t->isBound()) + debug << ", [bound]"; + if (t->isTextureView()) + debug << ", [view]"; + if (d->fixedSamplePositions) + debug << ", [fixedSamplePositions]"; + debug << ", mipLevels=" << d->requestedMipLevels << ", layers=" << d->layers + << ", faces=" << d->faces << ", samples=" << d->samples + << ", depthStencilMode=" << d->depthStencilMode << ", comparisonFunction=" + << d->comparisonFunction << ", comparisonMode=" << d->comparisonMode + << ", features=" << d->features << ", minificationFilter=" << d->minFilter + << ", magnificationFilter=" << d->magFilter << ", wrapMode=" << d->wrapModes[0]; + } else { + debug << '0'; + } + debug << ')'; + return debug; +} +#endif // QT_NO_DEBUG_STREAM + +QT_END_NAMESPACE diff --git a/src/opengl/qopengltexture.h b/src/opengl/qopengltexture.h new file mode 100644 index 0000000000..8eba2724df --- /dev/null +++ b/src/opengl/qopengltexture.h @@ -0,0 +1,663 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLABSTRACTTEXTURE_H +#define QOPENGLABSTRACTTEXTURE_H + +#include <QtOpenGL/qtopenglglobal.h> + +#ifndef QT_NO_OPENGL + +#include <QtGui/qopengl.h> +#include <QtGui/qimage.h> +#include <QtCore/QScopedPointer> + +QT_BEGIN_NAMESPACE + +class QDebug; +class QOpenGLTexturePrivate; +class QOpenGLPixelTransferOptions; + +class Q_OPENGL_EXPORT QOpenGLTexture +{ + Q_GADGET +public: + enum Target { + Target1D = 0x0DE0, // GL_TEXTURE_1D + Target1DArray = 0x8C18, // GL_TEXTURE_1D_ARRAY + Target2D = 0x0DE1, // GL_TEXTURE_2D + Target2DArray = 0x8C1A, // GL_TEXTURE_2D_ARRAY + Target3D = 0x806F, // GL_TEXTURE_3D + TargetCubeMap = 0x8513, // GL_TEXTURE_CUBE_MAP + TargetCubeMapArray = 0x9009, // GL_TEXTURE_CUBE_MAP_ARRAY + Target2DMultisample = 0x9100, // GL_TEXTURE_2D_MULTISAMPLE + Target2DMultisampleArray = 0x9102, // GL_TEXTURE_2D_MULTISAMPLE_ARRAY + TargetRectangle = 0x84F5, // GL_TEXTURE_RECTANGLE + TargetBuffer = 0x8C2A // GL_TEXTURE_BUFFER + }; + Q_ENUM(Target) + + enum BindingTarget { + BindingTarget1D = 0x8068, // GL_TEXTURE_BINDING_1D + BindingTarget1DArray = 0x8C1C, // GL_TEXTURE_BINDING_1D_ARRAY + BindingTarget2D = 0x8069, // GL_TEXTURE_BINDING_2D + BindingTarget2DArray = 0x8C1D, // GL_TEXTURE_BINDING_2D_ARRAY + BindingTarget3D = 0x806A, // GL_TEXTURE_BINDING_3D + BindingTargetCubeMap = 0x8514, // GL_TEXTURE_BINDING_CUBE_MAP + BindingTargetCubeMapArray = 0x900A, // GL_TEXTURE_BINDING_CUBE_MAP_ARRAY + BindingTarget2DMultisample = 0x9104, // GL_TEXTURE_BINDING_2D_MULTISAMPLE + BindingTarget2DMultisampleArray = 0x9105, // GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY + BindingTargetRectangle = 0x84F6, // GL_TEXTURE_BINDING_RECTANGLE + BindingTargetBuffer = 0x8C2C // GL_TEXTURE_BINDING_BUFFER + }; + Q_ENUM(BindingTarget) + + enum MipMapGeneration { + GenerateMipMaps, + DontGenerateMipMaps + }; + Q_ENUM(MipMapGeneration) + + enum TextureUnitReset { + ResetTextureUnit, + DontResetTextureUnit + }; + Q_ENUM(TextureUnitReset) + + enum TextureFormat { + NoFormat = 0, // GL_NONE + + // Unsigned normalized formats + R8_UNorm = 0x8229, // GL_R8 + RG8_UNorm = 0x822B, // GL_RG8 + RGB8_UNorm = 0x8051, // GL_RGB8 + RGBA8_UNorm = 0x8058, // GL_RGBA8 + + R16_UNorm = 0x822A, // GL_R16 + RG16_UNorm = 0x822C, // GL_RG16 + RGB16_UNorm = 0x8054, // GL_RGB16 + RGBA16_UNorm = 0x805B, // GL_RGBA16 + + // Signed normalized formats + R8_SNorm = 0x8F94, // GL_R8_SNORM + RG8_SNorm = 0x8F95, // GL_RG8_SNORM + RGB8_SNorm = 0x8F96, // GL_RGB8_SNORM + RGBA8_SNorm = 0x8F97, // GL_RGBA8_SNORM + + R16_SNorm = 0x8F98, // GL_R16_SNORM + RG16_SNorm = 0x8F99, // GL_RG16_SNORM + RGB16_SNorm = 0x8F9A, // GL_RGB16_SNORM + RGBA16_SNorm = 0x8F9B, // GL_RGBA16_SNORM + + // Unsigned integer formats + R8U = 0x8232, // GL_R8UI + RG8U = 0x8238, // GL_RG8UI + RGB8U = 0x8D7D, // GL_RGB8UI + RGBA8U = 0x8D7C, // GL_RGBA8UI + + R16U = 0x8234, // GL_R16UI + RG16U = 0x823A, // GL_RG16UI + RGB16U = 0x8D77, // GL_RGB16UI + RGBA16U = 0x8D76, // GL_RGBA16UI + + R32U = 0x8236, // GL_R32UI + RG32U = 0x823C, // GL_RG32UI + RGB32U = 0x8D71, // GL_RGB32UI + RGBA32U = 0x8D70, // GL_RGBA32UI + + // Signed integer formats + R8I = 0x8231, // GL_R8I + RG8I = 0x8237, // GL_RG8I + RGB8I = 0x8D8F, // GL_RGB8I + RGBA8I = 0x8D8E, // GL_RGBA8I + + R16I = 0x8233, // GL_R16I + RG16I = 0x8239, // GL_RG16I + RGB16I = 0x8D89, // GL_RGB16I + RGBA16I = 0x8D88, // GL_RGBA16I + + R32I = 0x8235, // GL_R32I + RG32I = 0x823B, // GL_RG32I + RGB32I = 0x8D83, // GL_RGB32I + RGBA32I = 0x8D82, // GL_RGBA32I + + // Floating point formats + R16F = 0x822D, // GL_R16F + RG16F = 0x822F, // GL_RG16F + RGB16F = 0x881B, // GL_RGB16F + RGBA16F = 0x881A, // GL_RGBA16F + + R32F = 0x822E, // GL_R32F + RG32F = 0x8230, // GL_RG32F + RGB32F = 0x8815, // GL_RGB32F + RGBA32F = 0x8814, // GL_RGBA32F + + // Packed formats + RGB9E5 = 0x8C3D, // GL_RGB9_E5 + RG11B10F = 0x8C3A, // GL_R11F_G11F_B10F + RG3B2 = 0x2A10, // GL_R3_G3_B2 + R5G6B5 = 0x8D62, // GL_RGB565 + RGB5A1 = 0x8057, // GL_RGB5_A1 + RGBA4 = 0x8056, // GL_RGBA4 + RGB10A2 = 0x906F, // GL_RGB10_A2UI + + // Depth formats + D16 = 0x81A5, // GL_DEPTH_COMPONENT16 + D24 = 0x81A6, // GL_DEPTH_COMPONENT24 + D24S8 = 0x88F0, // GL_DEPTH24_STENCIL8 + D32 = 0x81A7, // GL_DEPTH_COMPONENT32 + D32F = 0x8CAC, // GL_DEPTH_COMPONENT32F + D32FS8X24 = 0x8CAD, // GL_DEPTH32F_STENCIL8 + S8 = 0x8D48, // GL_STENCIL_INDEX8 + + // Compressed formats + RGB_DXT1 = 0x83F0, // GL_COMPRESSED_RGB_S3TC_DXT1_EXT + RGBA_DXT1 = 0x83F1, // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + RGBA_DXT3 = 0x83F2, // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + RGBA_DXT5 = 0x83F3, // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + R_ATI1N_UNorm = 0x8DBB, // GL_COMPRESSED_RED_RGTC1 + R_ATI1N_SNorm = 0x8DBC, // GL_COMPRESSED_SIGNED_RED_RGTC1 + RG_ATI2N_UNorm = 0x8DBD, // GL_COMPRESSED_RG_RGTC2 + RG_ATI2N_SNorm = 0x8DBE, // GL_COMPRESSED_SIGNED_RG_RGTC2 + RGB_BP_UNSIGNED_FLOAT = 0x8E8F, // GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + RGB_BP_SIGNED_FLOAT = 0x8E8E, // GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + RGB_BP_UNorm = 0x8E8C, // GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + R11_EAC_UNorm = 0x9270, // GL_COMPRESSED_R11_EAC + R11_EAC_SNorm = 0x9271, // GL_COMPRESSED_SIGNED_R11_EAC + RG11_EAC_UNorm = 0x9272, // GL_COMPRESSED_RG11_EAC + RG11_EAC_SNorm = 0x9273, // GL_COMPRESSED_SIGNED_RG11_EAC + RGB8_ETC2 = 0x9274, // GL_COMPRESSED_RGB8_ETC2 + SRGB8_ETC2 = 0x9275, // GL_COMPRESSED_SRGB8_ETC2 + RGB8_PunchThrough_Alpha1_ETC2 = 0x9276, // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + SRGB8_PunchThrough_Alpha1_ETC2 = 0x9277, // GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 + RGBA8_ETC2_EAC = 0x9278, // GL_COMPRESSED_RGBA8_ETC2_EAC + SRGB8_Alpha8_ETC2_EAC = 0x9279, // GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + RGB8_ETC1 = 0x8D64, // GL_ETC1_RGB8_OES + RGBA_ASTC_4x4 = 0x93B0, // GL_COMPRESSED_RGBA_ASTC_4x4_KHR + RGBA_ASTC_5x4 = 0x93B1, // GL_COMPRESSED_RGBA_ASTC_5x4_KHR + RGBA_ASTC_5x5 = 0x93B2, // GL_COMPRESSED_RGBA_ASTC_5x5_KHR + RGBA_ASTC_6x5 = 0x93B3, // GL_COMPRESSED_RGBA_ASTC_6x5_KHR + RGBA_ASTC_6x6 = 0x93B4, // GL_COMPRESSED_RGBA_ASTC_6x6_KHR + RGBA_ASTC_8x5 = 0x93B5, // GL_COMPRESSED_RGBA_ASTC_8x5_KHR + RGBA_ASTC_8x6 = 0x93B6, // GL_COMPRESSED_RGBA_ASTC_8x6_KHR + RGBA_ASTC_8x8 = 0x93B7, // GL_COMPRESSED_RGBA_ASTC_8x8_KHR + RGBA_ASTC_10x5 = 0x93B8, // GL_COMPRESSED_RGBA_ASTC_10x5_KHR + RGBA_ASTC_10x6 = 0x93B9, // GL_COMPRESSED_RGBA_ASTC_10x6_KHR + RGBA_ASTC_10x8 = 0x93BA, // GL_COMPRESSED_RGBA_ASTC_10x8_KHR + RGBA_ASTC_10x10 = 0x93BB, // GL_COMPRESSED_RGBA_ASTC_10x10_KHR + RGBA_ASTC_12x10 = 0x93BC, // GL_COMPRESSED_RGBA_ASTC_12x10_KHR + RGBA_ASTC_12x12 = 0x93BD, // GL_COMPRESSED_RGBA_ASTC_12x12_KHR + SRGB8_Alpha8_ASTC_4x4 = 0x93D0, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + SRGB8_Alpha8_ASTC_5x4 = 0x93D1, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR + SRGB8_Alpha8_ASTC_5x5 = 0x93D2, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR + SRGB8_Alpha8_ASTC_6x5 = 0x93D3, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR + SRGB8_Alpha8_ASTC_6x6 = 0x93D4, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR + SRGB8_Alpha8_ASTC_8x5 = 0x93D5, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR + SRGB8_Alpha8_ASTC_8x6 = 0x93D6, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR + SRGB8_Alpha8_ASTC_8x8 = 0x93D7, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR + SRGB8_Alpha8_ASTC_10x5 = 0x93D8, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR + SRGB8_Alpha8_ASTC_10x6 = 0x93D9, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR + SRGB8_Alpha8_ASTC_10x8 = 0x93DA, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR + SRGB8_Alpha8_ASTC_10x10 = 0x93DB, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR + SRGB8_Alpha8_ASTC_12x10 = 0x93DC, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR + SRGB8_Alpha8_ASTC_12x12 = 0x93DD, // GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR + + // sRGB formats + SRGB8 = 0x8C41, // GL_SRGB8 + SRGB8_Alpha8 = 0x8C43, // GL_SRGB8_ALPHA8 + SRGB_DXT1 = 0x8C4C, // GL_COMPRESSED_SRGB_S3TC_DXT1_EXT + SRGB_Alpha_DXT1 = 0x8C4D, // GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT + SRGB_Alpha_DXT3 = 0x8C4E, // GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT + SRGB_Alpha_DXT5 = 0x8C4F, // GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + SRGB_BP_UNorm = 0x8E8D, // GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + + // ES 2 formats + DepthFormat = 0x1902, // GL_DEPTH_COMPONENT + AlphaFormat = 0x1906, // GL_ALPHA + RGBFormat = 0x1907, // GL_RGB + RGBAFormat = 0x1908, // GL_RGBA + LuminanceFormat = 0x1909, // GL_LUMINANCE + LuminanceAlphaFormat = 0x190A + + }; + Q_ENUM(TextureFormat) + + // This is not used externally yet but is reserved to allow checking of + // compatibility between texture formats +#ifndef Q_QDOC + enum TextureFormatClass { + NoFormatClass, + FormatClass_128Bit, + FormatClass_96Bit, + FormatClass_64Bit, + FormatClass_48Bit, + FormatClass_32Bit, + FormatClass_24Bit, + FormatClass_16Bit, + FormatClass_8Bit, + FormatClass_RGTC1_R, + FormatClass_RGTC2_RG, + FormatClass_BPTC_Unorm, + FormatClass_BPTC_Float, + FormatClass_S3TC_DXT1_RGB, + FormatClass_S3TC_DXT1_RGBA, + FormatClass_S3TC_DXT3_RGBA, + FormatClass_S3TC_DXT5_RGBA, + FormatClass_Unique + }; +#endif + + enum CubeMapFace { + CubeMapPositiveX = 0x8515, // GL_TEXTURE_CUBE_MAP_POSITIVE_X + CubeMapNegativeX = 0x8516, // GL_TEXTURE_CUBE_MAP_NEGATIVE_X + CubeMapPositiveY = 0x8517, // GL_TEXTURE_CUBE_MAP_POSITIVE_Y + CubeMapNegativeY = 0x8518, // GL_TEXTURE_CUBE_MAP_NEGATIVE_Y + CubeMapPositiveZ = 0x8519, // GL_TEXTURE_CUBE_MAP_POSITIVE_Z + CubeMapNegativeZ = 0x851A // GL_TEXTURE_CUBE_MAP_NEGATIVE_Z + }; + Q_ENUM(CubeMapFace) + + enum PixelFormat { + NoSourceFormat = 0, // GL_NONE + Red = 0x1903, // GL_RED + RG = 0x8227, // GL_RG + RGB = 0x1907, // GL_RGB + BGR = 0x80E0, // GL_BGR + RGBA = 0x1908, // GL_RGBA + BGRA = 0x80E1, // GL_BGRA + Red_Integer = 0x8D94, // GL_RED_INTEGER + RG_Integer = 0x8228, // GL_RG_INTEGER + RGB_Integer = 0x8D98, // GL_RGB_INTEGER + BGR_Integer = 0x8D9A, // GL_BGR_INTEGER + RGBA_Integer = 0x8D99, // GL_RGBA_INTEGER + BGRA_Integer = 0x8D9B, // GL_BGRA_INTEGER + Stencil = 0x1901, // GL_STENCIL_INDEX + Depth = 0x1902, // GL_DEPTH_COMPONENT + DepthStencil = 0x84F9, // GL_DEPTH_STENCIL + Alpha = 0x1906, // GL_ALPHA + Luminance = 0x1909, // GL_LUMINANCE + LuminanceAlpha = 0x190A // GL_LUMINANCE_ALPHA + }; + Q_ENUM(PixelFormat) + + enum PixelType { + NoPixelType = 0, // GL_NONE + Int8 = 0x1400, // GL_BYTE + UInt8 = 0x1401, // GL_UNSIGNED_BYTE + Int16 = 0x1402, // GL_SHORT + UInt16 = 0x1403, // GL_UNSIGNED_SHORT + Int32 = 0x1404, // GL_INT + UInt32 = 0x1405, // GL_UNSIGNED_INT + Float16 = 0x140B, // GL_HALF_FLOAT + Float16OES = 0x8D61, // GL_HALF_FLOAT_OES + Float32 = 0x1406, // GL_FLOAT + UInt32_RGB9_E5 = 0x8C3E, // GL_UNSIGNED_INT_5_9_9_9_REV + UInt32_RG11B10F = 0x8C3B, // GL_UNSIGNED_INT_10F_11F_11F_REV + UInt8_RG3B2 = 0x8032, // GL_UNSIGNED_BYTE_3_3_2 + UInt8_RG3B2_Rev = 0x8362, // GL_UNSIGNED_BYTE_2_3_3_REV + UInt16_RGB5A1 = 0x8034, // GL_UNSIGNED_SHORT_5_5_5_1 + UInt16_RGB5A1_Rev = 0x8366, // GL_UNSIGNED_SHORT_1_5_5_5_REV + UInt16_R5G6B5 = 0x8363, // GL_UNSIGNED_SHORT_5_6_5 + UInt16_R5G6B5_Rev = 0x8364, // GL_UNSIGNED_SHORT_5_6_5_REV + UInt16_RGBA4 = 0x8033, // GL_UNSIGNED_SHORT_4_4_4_4 + UInt16_RGBA4_Rev = 0x8365, // GL_UNSIGNED_SHORT_4_4_4_4_REV + UInt32_RGBA8 = 0x8035, // GL_UNSIGNED_INT_8_8_8_8 + UInt32_RGBA8_Rev = 0x8367, // GL_UNSIGNED_INT_8_8_8_8_REV + UInt32_RGB10A2 = 0x8036, // GL_UNSIGNED_INT_10_10_10_2 + UInt32_RGB10A2_Rev = 0x8368, // GL_UNSIGNED_INT_2_10_10_10_REV + UInt32_D24S8 = 0x84FA, // GL_UNSIGNED_INT_24_8 + Float32_D32_UInt32_S8_X24 = 0x8DAD // GL_FLOAT_32_UNSIGNED_INT_24_8_REV + }; + Q_ENUM(PixelType) + + enum SwizzleComponent { + SwizzleRed = 0x8E42, // GL_TEXTURE_SWIZZLE_R + SwizzleGreen = 0x8E43, // GL_TEXTURE_SWIZZLE_G + SwizzleBlue = 0x8E44, // GL_TEXTURE_SWIZZLE_B + SwizzleAlpha = 0x8E45 // GL_TEXTURE_SWIZZLE_A + }; + Q_ENUM(SwizzleComponent) + + enum SwizzleValue { + RedValue = 0x1903, // GL_RED + GreenValue = 0x1904, // GL_GREEN + BlueValue = 0x1905, // GL_BLUE + AlphaValue = 0x1906, // GL_ALPHA + ZeroValue = 0, // GL_ZERO + OneValue = 1 // GL_ONE + }; + Q_ENUM(SwizzleValue) + + enum WrapMode { + Repeat = 0x2901, // GL_REPEAT + MirroredRepeat = 0x8370, // GL_MIRRORED_REPEAT + ClampToEdge = 0x812F, // GL_CLAMP_TO_EDGE + ClampToBorder = 0x812D // GL_CLAMP_TO_BORDER + }; + Q_ENUM(WrapMode) + + enum CoordinateDirection { + DirectionS = 0x2802, // GL_TEXTURE_WRAP_S + DirectionT = 0x2803, // GL_TEXTURE_WRAP_T + DirectionR = 0x8072 // GL_TEXTURE_WRAP_R + }; + Q_ENUM(CoordinateDirection) + + // Features + enum Feature { + ImmutableStorage = 0x00000001, + ImmutableMultisampleStorage = 0x00000002, + TextureRectangle = 0x00000004, + TextureArrays = 0x00000008, + Texture3D = 0x00000010, + TextureMultisample = 0x00000020, + TextureBuffer = 0x00000040, + TextureCubeMapArrays = 0x00000080, + Swizzle = 0x00000100, + StencilTexturing = 0x00000200, + AnisotropicFiltering = 0x00000400, + NPOTTextures = 0x00000800, + NPOTTextureRepeat = 0x00001000, + Texture1D = 0x00002000, + TextureComparisonOperators = 0x00004000, + TextureMipMapLevel = 0x00008000, +#ifndef Q_QDOC + MaxFeatureFlag = 0x00010000 +#endif + }; + Q_DECLARE_FLAGS(Features, Feature) + Q_ENUM(Feature) + + explicit QOpenGLTexture(Target target); + explicit QOpenGLTexture(const QImage& image, MipMapGeneration genMipMaps = GenerateMipMaps); + ~QOpenGLTexture(); + + Target target() const; + + // Creation and destruction + bool create(); + void destroy(); + bool isCreated() const; + GLuint textureId() const; + + // Binding and releasing + void bind(); + void bind(uint unit, TextureUnitReset reset = DontResetTextureUnit); + void release(); + void release(uint unit, TextureUnitReset reset = DontResetTextureUnit); + + bool isBound() const; + bool isBound(uint unit); + static GLuint boundTextureId(BindingTarget target); + static GLuint boundTextureId(uint unit, BindingTarget target); + + // Storage allocation + void setFormat(TextureFormat format); + TextureFormat format() const; + void setSize(int width, int height = 1, int depth = 1); + int width() const; + int height() const; + int depth() const; + void setMipLevels(int levels); + int mipLevels() const; + int maximumMipLevels() const; + void setLayers(int layers); + int layers() const; + int faces() const; + void setSamples(int samples); + int samples() const; + void setFixedSamplePositions(bool fixed); + bool isFixedSamplePositions() const; + void allocateStorage(); + void allocateStorage(PixelFormat pixelFormat, PixelType pixelType); + bool isStorageAllocated() const; + + QOpenGLTexture *createTextureView(Target target, + TextureFormat viewFormat, + int minimumMipmapLevel, int maximumMipmapLevel, + int minimumLayer, int maximumLayer) const; + bool isTextureView() const; + + // Pixel transfer + // ### Qt 6: remove the non-const void * overloads +#if QT_DEPRECATED_SINCE(5, 3) + QT_DEPRECATED void setData(int mipLevel, int layer, CubeMapFace cubeFace, + PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + QT_DEPRECATED void setData(int mipLevel, int layer, + PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + QT_DEPRECATED void setData(int mipLevel, + PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + QT_DEPRECATED void setData(PixelFormat sourceFormat, PixelType sourceType, + void *data, const QOpenGLPixelTransferOptions * const options = nullptr); +#endif // QT_DEPRECATED_SINCE(5, 3) + + void setData(int mipLevel, int layer, CubeMapFace cubeFace, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int mipLevel, int layer, int layerCount, CubeMapFace cubeFace, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int mipLevel, int layer, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int mipLevel, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + + void setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, int mipLevel, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, int layer, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, int layer, + CubeMapFace cubeFace, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + void setData(int xOffset, int yOffset, int zOffset, + int width, int height, int depth, + int mipLevel, int layer, + CubeMapFace cubeFace, int layerCount, + PixelFormat sourceFormat, PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options = nullptr); + + // Compressed data upload + // ### Qt 6: remove the non-const void * overloads +#if QT_DEPRECATED_SINCE(5, 3) + QT_DEPRECATED void setCompressedData(int mipLevel, int layer, CubeMapFace cubeFace, + int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + QT_DEPRECATED void setCompressedData(int mipLevel, int layer, + int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + QT_DEPRECATED void setCompressedData(int mipLevel, int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + QT_DEPRECATED void setCompressedData(int dataSize, void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); +#endif // QT_DEPRECATED_SINCE(5, 3) + + void setCompressedData(int mipLevel, int layer, CubeMapFace cubeFace, + int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + void setCompressedData(int mipLevel, int layer, int layerCount, CubeMapFace cubeFace, + int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + void setCompressedData(int mipLevel, int layer, + int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + void setCompressedData(int mipLevel, int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + void setCompressedData(int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options = nullptr); + + // Helpful overloads for setData + void setData(const QImage& image, MipMapGeneration genMipMaps = GenerateMipMaps); + + static bool hasFeature(Feature feature); + + // Texture Parameters + void setMipBaseLevel(int baseLevel); + int mipBaseLevel() const; + void setMipMaxLevel(int maxLevel); + int mipMaxLevel() const; + void setMipLevelRange(int baseLevel, int maxLevel); + QPair<int, int> mipLevelRange() const; + + void setAutoMipMapGenerationEnabled(bool enabled); + bool isAutoMipMapGenerationEnabled() const; + + void generateMipMaps(); + void generateMipMaps(int baseLevel, bool resetBaseLevel = true); + + void setSwizzleMask(SwizzleComponent component, SwizzleValue value); + void setSwizzleMask(SwizzleValue r, SwizzleValue g, + SwizzleValue b, SwizzleValue a); + SwizzleValue swizzleMask(SwizzleComponent component) const; + + enum DepthStencilMode { + DepthMode = 0x1902, // GL_DEPTH_COMPONENT + StencilMode = 0x1901 // GL_STENCIL_INDEX + }; + Q_ENUM(DepthStencilMode) + + void setDepthStencilMode(DepthStencilMode mode); + DepthStencilMode depthStencilMode() const; + + enum ComparisonFunction { + CompareLessEqual = 0x0203, // GL_LEQUAL + CompareGreaterEqual = 0x0206, // GL_GEQUAL + CompareLess = 0x0201, // GL_LESS + CompareGreater = 0x0204, // GL_GREATER + CompareEqual = 0x0202, // GL_EQUAL + CommpareNotEqual = 0x0205, // GL_NOTEQUAL + CompareAlways = 0x0207, // GL_ALWAYS + CompareNever = 0x0200 // GL_NEVER + }; + Q_ENUM(ComparisonFunction) + + void setComparisonFunction(ComparisonFunction function); + ComparisonFunction comparisonFunction() const; + + enum ComparisonMode { + CompareRefToTexture = 0x884E, // GL_COMPARE_REF_TO_TEXTURE + CompareNone = 0x0000 // GL_NONE + }; + + void setComparisonMode(ComparisonMode mode); + ComparisonMode comparisonMode() const; + + // Sampling Parameters + enum Filter { + Nearest = 0x2600, // GL_NEAREST + Linear = 0x2601, // GL_LINEAR + NearestMipMapNearest = 0x2700, // GL_NEAREST_MIPMAP_NEAREST + NearestMipMapLinear = 0x2702, // GL_NEAREST_MIPMAP_LINEAR + LinearMipMapNearest = 0x2701, // GL_LINEAR_MIPMAP_NEAREST + LinearMipMapLinear = 0x2703 // GL_LINEAR_MIPMAP_LINEAR + }; + Q_ENUM(Filter) + + void setMinificationFilter(Filter filter); + Filter minificationFilter() const; + void setMagnificationFilter(Filter filter); + Filter magnificationFilter() const; + void setMinMagFilters(Filter minificationFilter, + Filter magnificationFilter); + QPair<Filter, Filter> minMagFilters() const; + void setMaximumAnisotropy(float anisotropy); + float maximumAnisotropy() const; + + void setWrapMode(WrapMode mode); + void setWrapMode(CoordinateDirection direction, WrapMode mode); + WrapMode wrapMode(CoordinateDirection direction) const; + + void setBorderColor(const QColor &color); + void setBorderColor(float r, float g, float b, float a); + void setBorderColor(int r, int g, int b, int a); + void setBorderColor(uint r, uint g, uint b, uint a); + + QColor borderColor() const; + void borderColor(float *border) const; + void borderColor(int *border) const; + void borderColor(unsigned int *border) const; + + void setMinimumLevelOfDetail(float value); + float minimumLevelOfDetail() const; + void setMaximumLevelOfDetail(float value); + float maximumLevelOfDetail() const; + void setLevelOfDetailRange(float min, float max); + QPair<float, float> levelOfDetailRange() const; + void setLevelofDetailBias(float bias); + float levelofDetailBias() const; + +#ifndef QT_NO_DEBUG_STREAM + friend Q_OPENGL_EXPORT QDebug operator<<(QDebug dbg, const QOpenGLTexture *t); +#endif + +private: + Q_DISABLE_COPY(QOpenGLTexture) + Q_DECLARE_PRIVATE(QOpenGLTexture) + QScopedPointer<QOpenGLTexturePrivate> d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTexture::Features) + +#ifndef QT_NO_DEBUG_STREAM +Q_OPENGL_EXPORT QDebug operator<<(QDebug debug, const QOpenGLTexture *t); +#endif + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QOPENGLABSTRACTTEXTURE_H diff --git a/src/opengl/qopengltexture_p.h b/src/opengl/qopengltexture_p.h new file mode 100644 index 0000000000..1dc0801644 --- /dev/null +++ b/src/opengl/qopengltexture_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTOPENGLTEXTURE_P_H +#define QABSTRACTOPENGLTEXTURE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt 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 QT_NO_OPENGL + +#include <QtOpenGL/qtopenglglobal.h> +#include "private/qobject_p.h" +#include "qopengltexture.h" +#include "qopengl.h" + +#include <cmath> + +namespace { +inline double qLog2(const double x) +{ + return std::log(x) / std::log(2.0); +} +} + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; +class QOpenGLTextureHelper; +class QOpenGLFunctions; + +class QOpenGLTexturePrivate +{ +public: + QOpenGLTexturePrivate(QOpenGLTexture::Target textureTarget, + QOpenGLTexture *qq); + ~QOpenGLTexturePrivate(); + + Q_DECLARE_PUBLIC(QOpenGLTexture) + + void resetFuncs(QOpenGLTextureHelper *funcs); + void initializeOpenGLFunctions(); + + bool create(); + void destroy(); + + void bind(); + void bind(uint unit, QOpenGLTexture::TextureUnitReset reset = QOpenGLTexture::DontResetTextureUnit); + void release(); + void release(uint unit, QOpenGLTexture::TextureUnitReset reset = QOpenGLTexture::DontResetTextureUnit); + bool isBound() const; + bool isBound(uint unit) const; + + void allocateStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType); + void allocateMutableStorage(QOpenGLTexture::PixelFormat pixelFormat, QOpenGLTexture::PixelType pixelType); + void allocateImmutableStorage(); + void setData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, + QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options); + void setData(int xOffset, int yOffset, int zOffset, int width, int height, int depth, + int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, + QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType, + const void *data, const QOpenGLPixelTransferOptions * const options); + void setCompressedData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace, + int dataSize, const void *data, + const QOpenGLPixelTransferOptions * const options); + + + void setWrapMode(QOpenGLTexture::WrapMode mode); + void setWrapMode(QOpenGLTexture::CoordinateDirection direction, QOpenGLTexture::WrapMode mode); + QOpenGLTexture::WrapMode wrapMode(QOpenGLTexture::CoordinateDirection direction) const; + + QOpenGLTexture *createTextureView(QOpenGLTexture::Target target, QOpenGLTexture::TextureFormat viewFormat, + int minimumMipmapLevel, int maximumMipmapLevel, + int minimumLayer, int maximumLayer) const; + + int evaluateMipLevels() const; + + inline int maximumMipLevelCount() const + { + return 1 + std::floor(qLog2(qMax(dimensions[0], qMax(dimensions[1], dimensions[2])))); + } + + static inline int mipLevelSize(int mipLevel, int baseLevelSize) + { + return std::floor(double(qMax(1, baseLevelSize >> mipLevel))); + } + + bool isUsingImmutableStorage() const; + + QOpenGLTexture *q_ptr; + QOpenGLContext *context; + QOpenGLTexture::Target target; + QOpenGLTexture::BindingTarget bindingTarget; + GLuint textureId; + QOpenGLTexture::TextureFormat format; + QOpenGLTexture::TextureFormatClass formatClass; + int dimensions[3]; + int requestedMipLevels; + int mipLevels; + int layers; + int faces; + + int samples; + bool fixedSamplePositions; + + int baseLevel; + int maxLevel; + + QOpenGLTexture::SwizzleValue swizzleMask[4]; + QOpenGLTexture::DepthStencilMode depthStencilMode; + QOpenGLTexture::ComparisonFunction comparisonFunction; + QOpenGLTexture::ComparisonMode comparisonMode; + + QOpenGLTexture::Filter minFilter; + QOpenGLTexture::Filter magFilter; + float maxAnisotropy; + QOpenGLTexture::WrapMode wrapModes[3]; + QVariantList borderColor; + float minLevelOfDetail; + float maxLevelOfDetail; + float levelOfDetailBias; + + bool textureView; + bool autoGenerateMipMaps; + bool storageAllocated; + + QOpenGLTextureHelper *texFuncs; + QOpenGLFunctions *functions; + + QOpenGLTexture::Features features; +}; + +QT_END_NAMESPACE + +#undef Q_CALL_MEMBER_FUNCTION + +#endif // QT_NO_OPENGL + +#endif // QABSTRACTOPENGLTEXTURE_P_H diff --git a/src/opengl/qopengltextureblitter.cpp b/src/opengl/qopengltextureblitter.cpp new file mode 100644 index 0000000000..ba2eaf7754 --- /dev/null +++ b/src/opengl/qopengltextureblitter.cpp @@ -0,0 +1,682 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltextureblitter.h" + +#include <QtGui/QOpenGLBuffer> +#include <QtGui/QOpenGLShaderProgram> +#include <QtGui/QOpenGLVertexArrayObject> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> + +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLTextureBlitter + \brief The QOpenGLTextureBlitter class provides a convenient way to draw textured quads via OpenGL. + \since 5.8 + \ingroup painting-3D + \inmodule QtOpenGL + + Drawing textured quads, in order to get the contents of a texture + onto the screen, is a common operation when developing 2D user + interfaces. QOpenGLTextureBlitter provides a convenience class to + avoid repeating vertex data, shader sources, buffer and program + management and matrix calculations. + + For example, a QOpenGLWidget subclass can do the following to draw + the contents rendered into a framebuffer at the pixel position \c{(x, y)}: + + \code + void OpenGLWidget::initializeGL() + { + m_blitter.create(); + m_fbo = new QOpenGLFramebufferObject(size); + } + + void OpenGLWidget::paintGL() + { + m_fbo->bind(); + // update offscreen content + m_fbo->release(); + + m_blitter.bind(); + const QRect targetRect(QPoint(x, y), m_fbo->size()); + const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, QRect(QPoint(0, 0), m_fbo->size())); + m_blitter.blit(m_fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft); + m_blitter.release(); + } + \endcode + + The blitter implements GLSL shaders both for GLSL 1.00 (suitable + for OpenGL (ES) 2.x and compatibility profiles of newer OpenGL + versions) and version 150 (suitable for core profile contexts with + OpenGL 3.2 and newer). + */ + +static const char vertex_shader150[] = + "#version 150 core\n" + "in vec3 vertexCoord;" + "in vec2 textureCoord;" + "out vec2 uv;" + "uniform mat4 vertexTransform;" + "uniform mat3 textureTransform;" + "void main() {" + " uv = (textureTransform * vec3(textureCoord,1.0)).xy;" + " gl_Position = vertexTransform * vec4(vertexCoord,1.0);" + "}"; + +static const char fragment_shader150[] = + "#version 150 core\n" + "in vec2 uv;" + "out vec4 fragcolor;" + "uniform sampler2D textureSampler;" + "uniform bool swizzle;" + "uniform float opacity;" + "void main() {" + " vec4 tmpFragColor = texture(textureSampler, uv);" + " tmpFragColor.a *= opacity;" + " fragcolor = swizzle ? tmpFragColor.bgra : tmpFragColor;" + "}"; + +static const char vertex_shader[] = + "attribute highp vec3 vertexCoord;" + "attribute highp vec2 textureCoord;" + "varying highp vec2 uv;" + "uniform highp mat4 vertexTransform;" + "uniform highp mat3 textureTransform;" + "void main() {" + " uv = (textureTransform * vec3(textureCoord,1.0)).xy;" + " gl_Position = vertexTransform * vec4(vertexCoord,1.0);" + "}"; + +static const char fragment_shader[] = + "varying highp vec2 uv;" + "uniform sampler2D textureSampler;" + "uniform bool swizzle;" + "uniform highp float opacity;" + "void main() {" + " highp vec4 tmpFragColor = texture2D(textureSampler,uv);" + " tmpFragColor.a *= opacity;" + " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" + "}"; + +static const char fragment_shader_external_oes[] = + "#extension GL_OES_EGL_image_external : require\n" + "varying highp vec2 uv;" + "uniform samplerExternalOES textureSampler;\n" + "uniform bool swizzle;" + "uniform highp float opacity;" + "void main() {" + " highp vec4 tmpFragColor = texture2D(textureSampler, uv);" + " tmpFragColor.a *= opacity;" + " gl_FragColor = swizzle ? tmpFragColor.bgra : tmpFragColor;" + "}"; + +static const GLfloat vertex_buffer_data[] = { + -1,-1, 0, + -1, 1, 0, + 1,-1, 0, + -1, 1, 0, + 1,-1, 0, + 1, 1, 0 +}; + +static const GLfloat texture_buffer_data[] = { + 0, 0, + 0, 1, + 1, 0, + 0, 1, + 1, 0, + 1, 1 +}; + +class TextureBinder +{ +public: + TextureBinder(GLenum target, GLuint textureId) : m_target(target) + { + QOpenGLContext::currentContext()->functions()->glBindTexture(m_target, textureId); + } + ~TextureBinder() + { + QOpenGLContext::currentContext()->functions()->glBindTexture(m_target, 0); + } + +private: + GLenum m_target; +}; + +class QOpenGLTextureBlitterPrivate +{ +public: + enum TextureMatrixUniform { + User, + Identity, + IdentityFlipped + }; + + enum ProgramIndex { + TEXTURE_2D, + TEXTURE_EXTERNAL_OES + }; + + QOpenGLTextureBlitterPrivate() : + swizzle(false), + opacity(1.0f), + vao(new QOpenGLVertexArrayObject), + currentTarget(TEXTURE_2D) + { } + + bool buildProgram(ProgramIndex idx, const char *vs, const char *fs); + + void blit(GLuint texture, const QMatrix4x4 &vertexTransform, const QMatrix3x3 &textureTransform); + void blit(GLuint texture, const QMatrix4x4 &vertexTransform, QOpenGLTextureBlitter::Origin origin); + + void prepareProgram(const QMatrix4x4 &vertexTransform); + + QOpenGLBuffer vertexBuffer; + QOpenGLBuffer textureBuffer; + struct Program { + Program() : + vertexCoordAttribPos(0), + vertexTransformUniformPos(0), + textureCoordAttribPos(0), + textureTransformUniformPos(0), + swizzleUniformPos(0), + opacityUniformPos(0), + swizzle(false), + opacity(0.0f), + textureMatrixUniformState(User) + { } + QScopedPointer<QOpenGLShaderProgram> glProgram; + GLuint vertexCoordAttribPos; + GLuint vertexTransformUniformPos; + GLuint textureCoordAttribPos; + GLuint textureTransformUniformPos; + GLuint swizzleUniformPos; + GLuint opacityUniformPos; + bool swizzle; + float opacity; + TextureMatrixUniform textureMatrixUniformState; + } programs[2]; + bool swizzle; + float opacity; + QScopedPointer<QOpenGLVertexArrayObject> vao; + GLenum currentTarget; +}; + +static inline QOpenGLTextureBlitterPrivate::ProgramIndex targetToProgramIndex(GLenum target) +{ + switch (target) { + case GL_TEXTURE_2D: + return QOpenGLTextureBlitterPrivate::TEXTURE_2D; + case GL_TEXTURE_EXTERNAL_OES: + return QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES; + default: + qWarning("Unsupported texture target 0x%x", target); + return QOpenGLTextureBlitterPrivate::TEXTURE_2D; + } +} + +void QOpenGLTextureBlitterPrivate::prepareProgram(const QMatrix4x4 &vertexTransform) +{ + Program *program = &programs[targetToProgramIndex(currentTarget)]; + + vertexBuffer.bind(); + program->glProgram->setAttributeBuffer(program->vertexCoordAttribPos, GL_FLOAT, 0, 3, 0); + program->glProgram->enableAttributeArray(program->vertexCoordAttribPos); + vertexBuffer.release(); + + program->glProgram->setUniformValue(program->vertexTransformUniformPos, vertexTransform); + + textureBuffer.bind(); + program->glProgram->setAttributeBuffer(program->textureCoordAttribPos, GL_FLOAT, 0, 2, 0); + program->glProgram->enableAttributeArray(program->textureCoordAttribPos); + textureBuffer.release(); + + if (swizzle != program->swizzle) { + program->glProgram->setUniformValue(program->swizzleUniformPos, swizzle); + program->swizzle = swizzle; + } + + if (opacity != program->opacity) { + program->glProgram->setUniformValue(program->opacityUniformPos, opacity); + program->opacity = opacity; + } +} + +void QOpenGLTextureBlitterPrivate::blit(GLuint texture, + const QMatrix4x4 &vertexTransform, + const QMatrix3x3 &textureTransform) +{ + TextureBinder binder(currentTarget, texture); + prepareProgram(vertexTransform); + + Program *program = &programs[targetToProgramIndex(currentTarget)]; + program->glProgram->setUniformValue(program->textureTransformUniformPos, textureTransform); + program->textureMatrixUniformState = User; + + QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, 0, 6); +} + +void QOpenGLTextureBlitterPrivate::blit(GLuint texture, + const QMatrix4x4 &vertexTransform, + QOpenGLTextureBlitter::Origin origin) +{ + TextureBinder binder(currentTarget, texture); + prepareProgram(vertexTransform); + + Program *program = &programs[targetToProgramIndex(currentTarget)]; + if (origin == QOpenGLTextureBlitter::OriginTopLeft) { + if (program->textureMatrixUniformState != IdentityFlipped) { + QMatrix3x3 flipped; + flipped(1,1) = -1; + flipped(1,2) = 1; + program->glProgram->setUniformValue(program->textureTransformUniformPos, flipped); + program->textureMatrixUniformState = IdentityFlipped; + } + } else if (program->textureMatrixUniformState != Identity) { + program->glProgram->setUniformValue(program->textureTransformUniformPos, QMatrix3x3()); + program->textureMatrixUniformState = Identity; + } + + QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, 0, 6); +} + +bool QOpenGLTextureBlitterPrivate::buildProgram(ProgramIndex idx, const char *vs, const char *fs) +{ + Program *p = &programs[idx]; + + p->glProgram.reset(new QOpenGLShaderProgram); + + p->glProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vs); + p->glProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fs); + p->glProgram->link(); + if (!p->glProgram->isLinked()) { + qWarning() << "Could not link shader program:\n" << p->glProgram->log(); + return false; + } + + p->glProgram->bind(); + + p->vertexCoordAttribPos = p->glProgram->attributeLocation("vertexCoord"); + p->vertexTransformUniformPos = p->glProgram->uniformLocation("vertexTransform"); + p->textureCoordAttribPos = p->glProgram->attributeLocation("textureCoord"); + p->textureTransformUniformPos = p->glProgram->uniformLocation("textureTransform"); + p->swizzleUniformPos = p->glProgram->uniformLocation("swizzle"); + p->opacityUniformPos = p->glProgram->uniformLocation("opacity"); + + p->glProgram->setUniformValue(p->swizzleUniformPos, false); + + // minmize state left set after a create() + p->glProgram->release(); + + return true; +} + +/*! + Constructs a new QOpenGLTextureBlitter instance. + + \note no graphics resources are initialized in the + constructor. This makes it safe to place plain + QOpenGLTextureBlitter members into classes because the actual + initialization that depends on the OpenGL context happens only in + create(). + */ +QOpenGLTextureBlitter::QOpenGLTextureBlitter() + : d_ptr(new QOpenGLTextureBlitterPrivate) +{ +} + +/*! + Destructs the instance. + + \note When the OpenGL context - or a context sharing resources + with it - that was current when calling create() is not current, + graphics resources will not be released. Therefore, it is + recommended to call destroy() manually instead of relying on the + destructor to perform OpenGL resource cleanup. + */ +QOpenGLTextureBlitter::~QOpenGLTextureBlitter() +{ + destroy(); +} + +/*! + Initializes the graphics resources used by the blitter. + + \return \c true if successful, \c false if there was a + failure. Failures can occur when there is no OpenGL context + current on the current thread, or when shader compilation fails + for some reason. + + \sa isCreated(), destroy() + */ +bool QOpenGLTextureBlitter::create() +{ + QOpenGLContext *currentContext = QOpenGLContext::currentContext(); + if (!currentContext) + return false; + + Q_D(QOpenGLTextureBlitter); + + if (d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram) + return true; + + QSurfaceFormat format = currentContext->format(); + if (format.profile() == QSurfaceFormat::CoreProfile && format.version() >= qMakePair(3,2)) { + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D, vertex_shader150, fragment_shader150)) + return false; + } else { + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_2D, vertex_shader, fragment_shader)) + return false; + if (supportsExternalOESTarget()) + if (!d->buildProgram(QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES, vertex_shader, fragment_shader_external_oes)) + return false; + } + + // Create and bind the VAO, if supported. + QOpenGLVertexArrayObject::Binder vaoBinder(d->vao.data()); + + d->vertexBuffer.create(); + d->vertexBuffer.bind(); + d->vertexBuffer.allocate(vertex_buffer_data, sizeof(vertex_buffer_data)); + d->vertexBuffer.release(); + + d->textureBuffer.create(); + d->textureBuffer.bind(); + d->textureBuffer.allocate(texture_buffer_data, sizeof(texture_buffer_data)); + d->textureBuffer.release(); + + return true; +} + +/*! + \return \c true if create() was called and succeeded. \c false otherwise. + + \sa create(), destroy() + */ +bool QOpenGLTextureBlitter::isCreated() const +{ + Q_D(const QOpenGLTextureBlitter); + return d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram; +} + +/*! + Frees all graphics resources held by the blitter. Assumes that + the OpenGL context, or another context sharing resources with it, + that was current on the thread when invoking create() is current. + + The function has no effect when the blitter is not in created state. + + \sa create() + */ +void QOpenGLTextureBlitter::destroy() +{ + if (!isCreated()) + return; + Q_D(QOpenGLTextureBlitter); + d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_2D].glProgram.reset(); + d->programs[QOpenGLTextureBlitterPrivate::TEXTURE_EXTERNAL_OES].glProgram.reset(); + d->vertexBuffer.destroy(); + d->textureBuffer.destroy(); + d->vao.reset(); +} + +/*! + \return \c true when bind() accepts \c GL_TEXTURE_EXTERNAL_OES as + its target argument. + + \sa bind(), blit() + */ +bool QOpenGLTextureBlitter::supportsExternalOESTarget() const +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + return ctx && ctx->isOpenGLES() && ctx->hasExtension("GL_OES_EGL_image_external"); +} + +/*! + Binds the graphics resources used by the blitter. This must be + called before calling blit(). Code modifying the OpenGL state + should be avoided between the call to bind() and blit() because + otherwise conflicts may arise. + + \a target is the texture target for the source texture and must be + either \c GL_TEXTURE_2D or \c GL_OES_EGL_image_external. + + \sa release(), blit() + */ +void QOpenGLTextureBlitter::bind(GLenum target) +{ + Q_D(QOpenGLTextureBlitter); + + if (d->vao->isCreated()) + d->vao->bind(); + + d->currentTarget = target; + QOpenGLTextureBlitterPrivate::Program *p = &d->programs[targetToProgramIndex(target)]; + p->glProgram->bind(); + + d->vertexBuffer.bind(); + p->glProgram->setAttributeBuffer(p->vertexCoordAttribPos, GL_FLOAT, 0, 3, 0); + p->glProgram->enableAttributeArray(p->vertexCoordAttribPos); + d->vertexBuffer.release(); + + d->textureBuffer.bind(); + p->glProgram->setAttributeBuffer(p->textureCoordAttribPos, GL_FLOAT, 0, 2, 0); + p->glProgram->enableAttributeArray(p->textureCoordAttribPos); + d->textureBuffer.release(); +} + +/*! + Unbinds the graphics resources used by the blitter. + + \sa bind() + */ +void QOpenGLTextureBlitter::release() +{ + Q_D(QOpenGLTextureBlitter); + d->programs[targetToProgramIndex(d->currentTarget)].glProgram->release(); + if (d->vao->isCreated()) + d->vao->release(); +} + +/*! + Sets whether swizzling is enabled for the red and blue color channels to + \a swizzle. An BGRA to RGBA conversion (occurring in the shader on + the GPU, instead of a slow CPU-side transformation) can be useful + when the source texture contains data from a QImage with a format + like QImage::Format_ARGB32 which maps to BGRA on little endian + systems. + + By default the red-blue swizzle is disabled since this is what a + texture attached to an framebuffer object or a texture based on a + byte ordered QImage format (like QImage::Format_RGBA8888) needs. + */ +void QOpenGLTextureBlitter::setRedBlueSwizzle(bool swizzle) +{ + Q_D(QOpenGLTextureBlitter); + d->swizzle = swizzle; +} + +/*! + Changes the opacity to \a opacity. The default opacity is 1.0. + + \note the blitter does not alter the blend state. It is up to the + caller of blit() to ensure the correct blend settings are active. + + */ +void QOpenGLTextureBlitter::setOpacity(float opacity) +{ + Q_D(QOpenGLTextureBlitter); + d->opacity = opacity; +} + +/*! + \enum QOpenGLTextureBlitter::Origin + + \value OriginBottomLeft Indicates that the data in the texture + follows the OpenGL convention of coordinate systems, meaning Y is + running from bottom to top. + + \value OriginTopLeft Indicates that the data in the texture has Y + running from top to bottom, which is typical with regular, + unflipped image data. + + \sa blit() + */ + +/*! + Performs the blit with the source texture \a texture. + + \a targetTransform specifies the transformation applied. This is + usually generated by the targetTransform() helper function. + + \a sourceOrigin specifies if the image data needs flipping. When + \a texture corresponds to a texture attached to an FBO pass + OriginBottomLeft. On the other hand, when \a texture is based on + unflipped image data, pass OriginTopLeft. This is more efficient + than using QImage::mirrored(). + + \sa targetTransform(), Origin, bind() + */ +void QOpenGLTextureBlitter::blit(GLuint texture, + const QMatrix4x4 &targetTransform, + Origin sourceOrigin) +{ + Q_D(QOpenGLTextureBlitter); + d->blit(texture,targetTransform, sourceOrigin); +} + +/*! + Performs the blit with the source texture \a texture. + + \a targetTransform specifies the transformation applied. This is + usually generated by the targetTransform() helper function. + + \a sourceTransform specifies the transformation applied to the + source. This allows using only a sub-rect of the source + texture. This is usually generated by the sourceTransform() helper + function. + + \sa sourceTransform(), targetTransform(), Origin, bind() + */ +void QOpenGLTextureBlitter::blit(GLuint texture, + const QMatrix4x4 &targetTransform, + const QMatrix3x3 &sourceTransform) +{ + Q_D(QOpenGLTextureBlitter); + d->blit(texture, targetTransform, sourceTransform); +} + +/*! + Calculates a target transform suitable for blit(). + + \a target is the target rectangle in pixels. \a viewport describes + the source dimensions and will in most cases be set to (0, 0, + image width, image height). + + For unscaled output the size of \a target and \a viewport should + match. + + \sa blit() + */ +QMatrix4x4 QOpenGLTextureBlitter::targetTransform(const QRectF &target, + const QRect &viewport) +{ + qreal x_scale = target.width() / viewport.width(); + qreal y_scale = target.height() / viewport.height(); + + const QPointF relative_to_viewport = target.topLeft() - viewport.topLeft(); + qreal x_translate = x_scale - 1 + ((relative_to_viewport.x() / viewport.width()) * 2); + qreal y_translate = -y_scale + 1 - ((relative_to_viewport.y() / viewport.height()) * 2); + + QMatrix4x4 matrix; + matrix(0,3) = x_translate; + matrix(1,3) = y_translate; + + matrix(0,0) = x_scale; + matrix(1,1) = y_scale; + + return matrix; +} + +/*! + Calculates a 3x3 matrix suitable as the input to blit(). This is + used when only a part of the texture is to be used in the blit. + + \a subTexture is the desired source rectangle in pixels, \a + textureSize is the full width and height of the texture data. \a + origin specifies the orientation of the image data when it comes + to the Y axis. + + \sa blit(), Origin + */ +QMatrix3x3 QOpenGLTextureBlitter::sourceTransform(const QRectF &subTexture, + const QSize &textureSize, + Origin origin) +{ + qreal x_scale = subTexture.width() / textureSize.width(); + qreal y_scale = subTexture.height() / textureSize.height(); + + const QPointF topLeft = subTexture.topLeft(); + qreal x_translate = topLeft.x() / textureSize.width(); + qreal y_translate = topLeft.y() / textureSize.height(); + + if (origin == OriginTopLeft) { + y_scale = -y_scale; + y_translate = 1 - y_translate; + } + + QMatrix3x3 matrix; + matrix(0,2) = x_translate; + matrix(1,2) = y_translate; + + matrix(0,0) = x_scale; + matrix(1,1) = y_scale; + + return matrix; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qglcolormap.h b/src/opengl/qopengltextureblitter.h index b59b56e040..1818576085 100644 --- a/src/opengl/qglcolormap.h +++ b/src/opengl/qopengltextureblitter.h @@ -37,63 +37,54 @@ ** ****************************************************************************/ -#ifndef QGLCOLORMAP_H -#define QGLCOLORMAP_H +#ifndef QOPENGLTEXTUREBLITTER_H +#define QOPENGLTEXTUREBLITTER_H -#include <QtGui/qcolor.h> -#include <QtCore/qvector.h> #include <QtOpenGL/qtopenglglobal.h> +#include <QtGui/qopengl.h> +#include <QtGui/QMatrix3x3> +#include <QtGui/QMatrix4x4> + QT_BEGIN_NAMESPACE +class QOpenGLTextureBlitterPrivate; -class Q_OPENGL_EXPORT QGLColormap +class Q_OPENGL_EXPORT QOpenGLTextureBlitter { public: - QGLColormap(); - QGLColormap(const QGLColormap &); - ~QGLColormap(); + QOpenGLTextureBlitter(); + ~QOpenGLTextureBlitter(); - QGLColormap &operator=(const QGLColormap &); + enum Origin { + OriginBottomLeft, + OriginTopLeft + }; - bool isEmpty() const; - int size() const; - void detach(); + bool create(); + bool isCreated() const; + void destroy(); - void setEntries(int count, const QRgb * colors, int base = 0); - void setEntry(int idx, QRgb color); - void setEntry(int idx, const QColor & color); - QRgb entryRgb(int idx) const; - QColor entryColor(int idx) const; - int find(QRgb color) const; - int findNearest(QRgb color) const; + bool supportsExternalOESTarget() const; -protected: - Qt::HANDLE handle() { return d ? d->cmapHandle : nullptr; } - void setHandle(Qt::HANDLE ahandle) { d->cmapHandle = ahandle; } + void bind(GLenum target = GL_TEXTURE_2D); + void release(); -private: - struct QGLColormapData { - QBasicAtomicInt ref; - QVector<QRgb> *cells; - Qt::HANDLE cmapHandle; - }; + void setRedBlueSwizzle(bool swizzle); + void setOpacity(float opacity); - QGLColormapData *d; - static struct QGLColormapData shared_null; - static void cleanup(QGLColormapData *x); - void detach_helper(); + void blit(GLuint texture, const QMatrix4x4 &targetTransform, Origin sourceOrigin); + void blit(GLuint texture, const QMatrix4x4 &targetTransform, const QMatrix3x3 &sourceTransform); - friend class QGLWidget; - friend class QGLWidgetPrivate; -}; + static QMatrix4x4 targetTransform(const QRectF &target, const QRect &viewport); + static QMatrix3x3 sourceTransform(const QRectF &subTexture, const QSize &textureSize, Origin origin); -inline void QGLColormap::detach() -{ - if (d->ref.loadRelaxed() != 1) - detach_helper(); -} +private: + Q_DISABLE_COPY(QOpenGLTextureBlitter) + Q_DECLARE_PRIVATE(QOpenGLTextureBlitter) + QScopedPointer<QOpenGLTextureBlitterPrivate> d_ptr; +}; QT_END_NAMESPACE -#endif // QGLCOLORMAP_H +#endif //QOPENGLTEXTUREBLITTER_H diff --git a/src/opengl/qopengltexturecache.cpp b/src/opengl/qopengltexturecache.cpp new file mode 100644 index 0000000000..5256c429e0 --- /dev/null +++ b/src/opengl/qopengltexturecache.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltexturecache_p.h" +#include <private/qopengltextureuploader_p.h> +#include <qmath.h> +#include <qopenglfunctions.h> +#include <private/qimagepixmapcleanuphooks_p.h> +#include <qpa/qplatformpixmap.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLTextureCacheWrapper +{ +public: + QOpenGLTextureCacheWrapper() + { + QImagePixmapCleanupHooks::instance()->addPlatformPixmapModificationHook(cleanupTexturesForPixmapData); + QImagePixmapCleanupHooks::instance()->addPlatformPixmapDestructionHook(cleanupTexturesForPixmapData); + QImagePixmapCleanupHooks::instance()->addImageHook(cleanupTexturesForCacheKey); + } + + ~QOpenGLTextureCacheWrapper() + { + QImagePixmapCleanupHooks::instance()->removePlatformPixmapModificationHook(cleanupTexturesForPixmapData); + QImagePixmapCleanupHooks::instance()->removePlatformPixmapDestructionHook(cleanupTexturesForPixmapData); + QImagePixmapCleanupHooks::instance()->removeImageHook(cleanupTexturesForCacheKey); + } + + QOpenGLTextureCache *cacheForContext(QOpenGLContext *context) { + QMutexLocker lock(&m_mutex); + return m_resource.value<QOpenGLTextureCache>(context); + } + + static void cleanupTexturesForCacheKey(qint64 key); + static void cleanupTexturesForPixmapData(QPlatformPixmap *pmd); + +private: + QOpenGLMultiGroupSharedResource m_resource; + QMutex m_mutex; +}; + +Q_GLOBAL_STATIC(QOpenGLTextureCacheWrapper, qt_texture_caches) + +QOpenGLTextureCache *QOpenGLTextureCache::cacheForContext(QOpenGLContext *context) +{ + return qt_texture_caches()->cacheForContext(context); +} + +void QOpenGLTextureCacheWrapper::cleanupTexturesForCacheKey(qint64 key) +{ + QList<QOpenGLSharedResource *> resources = qt_texture_caches()->m_resource.resources(); + for (QList<QOpenGLSharedResource *>::iterator it = resources.begin(); it != resources.end(); ++it) + static_cast<QOpenGLTextureCache *>(*it)->invalidate(key); +} + +void QOpenGLTextureCacheWrapper::cleanupTexturesForPixmapData(QPlatformPixmap *pmd) +{ + cleanupTexturesForCacheKey(pmd->cacheKey()); +} + +QOpenGLTextureCache::QOpenGLTextureCache(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) + , m_cache(256 * 1024) // 256 MB cache +{ +} + +QOpenGLTextureCache::~QOpenGLTextureCache() +{ +} + +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QPixmap &pixmap, QOpenGLTextureUploader::BindOptions options) +{ + if (pixmap.isNull()) + return 0; + QMutexLocker locker(&m_mutex); + qint64 key = pixmap.cacheKey(); + + // A QPainter is active on the image - take the safe route and replace the texture. + if (!pixmap.paintingActive()) { + QOpenGLCachedTexture *entry = m_cache.object(key); + if (entry && entry->options() == options) { + context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); + return entry->id(); + } + } + + GLuint id = bindTexture(context, key, pixmap.toImage(), options); + if (id > 0) + QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); + + return id; +} + +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, QOpenGLTextureUploader::BindOptions options) +{ + if (image.isNull()) + return 0; + QMutexLocker locker(&m_mutex); + qint64 key = image.cacheKey(); + + // A QPainter is active on the image - take the safe route and replace the texture. + if (!image.paintingActive()) { + QOpenGLCachedTexture *entry = m_cache.object(key); + if (entry && entry->options() == options) { + context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); + return entry->id(); + } + } + + QImage img = image; + if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) + options |= QOpenGLTextureUploader::PowerOfTwoBindOption; + + GLuint id = bindTexture(context, key, img, options); + if (id > 0) + QImagePixmapCleanupHooks::enableCleanupHooks(image); + + return id; +} + +GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options) +{ + GLuint id; + QOpenGLFunctions *funcs = context->functions(); + funcs->glGenTextures(1, &id); + funcs->glBindTexture(GL_TEXTURE_2D, id); + + int cost = QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, image, options); + + m_cache.insert(key, new QOpenGLCachedTexture(id, options, context), cost / 1024); + + return id; +} + +void QOpenGLTextureCache::invalidate(qint64 key) +{ + QMutexLocker locker(&m_mutex); + m_cache.remove(key); +} + +void QOpenGLTextureCache::invalidateResource() +{ + m_cache.clear(); +} + +void QOpenGLTextureCache::freeResource(QOpenGLContext *) +{ + Q_ASSERT(false); // the texture cache lives until the context group disappears +} + +static void freeTexture(QOpenGLFunctions *funcs, GLuint id) +{ + funcs->glDeleteTextures(1, &id); +} + +QOpenGLCachedTexture::QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context) : m_options(options) +{ + m_resource = new QOpenGLSharedResourceGuard(context, id, freeTexture); +} + +QT_END_NAMESPACE diff --git a/src/opengl/qopengltexturecache_p.h b/src/opengl/qopengltexturecache_p.h new file mode 100644 index 0000000000..14e03ebea1 --- /dev/null +++ b/src/opengl/qopengltexturecache_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt 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 QOPENGLTEXTURECACHE_P_H +#define QOPENGLTEXTURECACHE_P_H + +#include <QtOpenGL/qtopenglglobal.h> +#include <QHash> +#include <QObject> +#include <QCache> +#include <private/qopenglcontext_p.h> +#include <private/qopengltextureuploader_p.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLCachedTexture; + +class Q_OPENGL_EXPORT QOpenGLTextureCache : public QOpenGLSharedResource +{ +public: + static QOpenGLTextureCache *cacheForContext(QOpenGLContext *context); + + QOpenGLTextureCache(QOpenGLContext *); + ~QOpenGLTextureCache(); + + GLuint bindTexture(QOpenGLContext *context, const QPixmap &pixmap, + QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption); + GLuint bindTexture(QOpenGLContext *context, const QImage &image, + QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption); + + void invalidate(qint64 key); + + void invalidateResource() override; + void freeResource(QOpenGLContext *ctx) override; + +private: + GLuint bindTexture(QOpenGLContext *context, qint64 key, const QImage &image, QOpenGLTextureUploader::BindOptions options); + + QMutex m_mutex; + QCache<quint64, QOpenGLCachedTexture> m_cache; +}; + +class QOpenGLCachedTexture +{ +public: + QOpenGLCachedTexture(GLuint id, QOpenGLTextureUploader::BindOptions options, QOpenGLContext *context); + ~QOpenGLCachedTexture() { m_resource->free(); } + + GLuint id() const { return m_resource->id(); } + QOpenGLTextureUploader::BindOptions options() const { return m_options; } + +private: + QOpenGLSharedResourceGuard *m_resource; + QOpenGLTextureUploader::BindOptions m_options; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/opengl/qopengltextureglyphcache.cpp b/src/opengl/qopengltextureglyphcache.cpp new file mode 100644 index 0000000000..66fd3a7507 --- /dev/null +++ b/src/opengl/qopengltextureglyphcache.cpp @@ -0,0 +1,485 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltextureglyphcache_p.h" +#include <private/qopenglpaintengine_p.h> +#include "private/qopenglengineshadersource_p.h" +#include <private/qopenglextensions_p.h> +#include <qrgb.h> +#include <private/qdrawhelper_p.h> + +QT_BEGIN_NAMESPACE + + +static int next_qopengltextureglyphcache_serial_number() +{ + static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0); + return 1 + serial.fetchAndAddRelaxed(1); +} + +QOpenGLTextureGlyphCache::QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color) + : QImageTextureGlyphCache(format, matrix, color) + , m_textureResource(nullptr) + , pex(nullptr) + , m_blitProgram(nullptr) + , m_filterMode(Nearest) + , m_serialNumber(next_qopengltextureglyphcache_serial_number()) + , m_buffer(QOpenGLBuffer::VertexBuffer) +{ +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> QOpenGLTextureGlyphCache() %p for context %p.", this, QOpenGLContext::currentContext()); +#endif + m_vertexCoordinateArray[0] = -1.0f; + m_vertexCoordinateArray[1] = -1.0f; + m_vertexCoordinateArray[2] = 1.0f; + m_vertexCoordinateArray[3] = -1.0f; + m_vertexCoordinateArray[4] = 1.0f; + m_vertexCoordinateArray[5] = 1.0f; + m_vertexCoordinateArray[6] = -1.0f; + m_vertexCoordinateArray[7] = 1.0f; + + m_textureCoordinateArray[0] = 0.0f; + m_textureCoordinateArray[1] = 0.0f; + m_textureCoordinateArray[2] = 1.0f; + m_textureCoordinateArray[3] = 0.0f; + m_textureCoordinateArray[4] = 1.0f; + m_textureCoordinateArray[5] = 1.0f; + m_textureCoordinateArray[6] = 0.0f; + m_textureCoordinateArray[7] = 1.0f; +} + +QOpenGLTextureGlyphCache::~QOpenGLTextureGlyphCache() +{ +#ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG + qDebug(" -> ~QOpenGLTextureGlyphCache() %p.", this); +#endif + clear(); +} + +#if !defined(QT_OPENGL_ES_2) +static inline bool isCoreProfile() +{ + return QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile; +} +#endif + +void QOpenGLTextureGlyphCache::createTextureData(int width, int height) +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (ctx == nullptr) { + qWarning("QOpenGLTextureGlyphCache::createTextureData: Called with no context"); + return; + } + + // create in QImageTextureGlyphCache baseclass is meant to be called + // only to create the initial image and does not preserve the content, + // so we don't call when this function is called from resize. + if (ctx->d_func()->workaround_brokenFBOReadBack && image().isNull()) + QImageTextureGlyphCache::createTextureData(width, height); + + // Make the lower glyph texture size 16 x 16. + if (width < 16) + width = 16; + if (height < 16) + height = 16; + + if (m_textureResource && !m_textureResource->m_texture) { + delete m_textureResource; + m_textureResource = nullptr; + } + + if (!m_textureResource) + m_textureResource = new QOpenGLGlyphTexture(ctx); + + QOpenGLFunctions *funcs = ctx->functions(); + funcs->glGenTextures(1, &m_textureResource->m_texture); + funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); + + m_textureResource->m_width = width; + m_textureResource->m_height = height; + + if (m_format == QFontEngine::Format_A32 || m_format == QFontEngine::Format_ARGB) { + QVarLengthArray<uchar> data(width * height * 4); + for (int i = 0; i < data.size(); ++i) + data[i] = 0; + funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); + } else { + QVarLengthArray<uchar> data(width * height); + for (int i = 0; i < data.size(); ++i) + data[i] = 0; +#if !defined(QT_OPENGL_ES_2) + const GLint internalFormat = isCoreProfile() ? GL_R8 : GL_ALPHA; + const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA; +#else + const GLint internalFormat = GL_ALPHA; + const GLenum format = GL_ALPHA; +#endif + funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, &data[0]); + } + + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_filterMode = Nearest; + + if (!m_buffer.isCreated()) { + m_buffer.create(); + m_buffer.bind(); + static GLfloat buf[sizeof(m_vertexCoordinateArray) + sizeof(m_textureCoordinateArray)]; + memcpy(buf, m_vertexCoordinateArray, sizeof(m_vertexCoordinateArray)); + memcpy(buf + (sizeof(m_vertexCoordinateArray) / sizeof(GLfloat)), + m_textureCoordinateArray, + sizeof(m_textureCoordinateArray)); + m_buffer.allocate(buf, sizeof(buf)); + m_buffer.release(); + } + + if (!m_vao.isCreated()) + m_vao.create(); +} + +void QOpenGLTextureGlyphCache::setupVertexAttribs() +{ + m_buffer.bind(); + m_blitProgram->setAttributeBuffer(int(QT_VERTEX_COORDS_ATTR), GL_FLOAT, 0, 2); + m_blitProgram->setAttributeBuffer(int(QT_TEXTURE_COORDS_ATTR), GL_FLOAT, sizeof(m_vertexCoordinateArray), 2); + m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); + m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); + m_buffer.release(); +} + +static void load_glyph_image_to_texture(QOpenGLContext *ctx, + QImage &img, + GLuint texture, + int tx, int ty) +{ + QOpenGLFunctions *funcs = ctx->functions(); + + const int imgWidth = img.width(); + const int imgHeight = img.height(); + + if (img.format() == QImage::Format_Mono) { + img = img.convertToFormat(QImage::Format_Grayscale8); + } else if (img.depth() == 32) { + if (img.format() == QImage::Format_RGB32 + // We need to make the alpha component equal to the average of the RGB values. + // This is needed when drawing sub-pixel antialiased text on translucent targets. +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + || img.format() == QImage::Format_ARGB32_Premultiplied +#else + || (img.format() == QImage::Format_ARGB32_Premultiplied + && ctx->isOpenGLES()) +#endif + ) { + for (int y = 0; y < imgHeight; ++y) { + QRgb *src = (QRgb *) img.scanLine(y); + for (int x = 0; x < imgWidth; ++x) { + int r = qRed(src[x]); + int g = qGreen(src[x]); + int b = qBlue(src[x]); + int avg; + if (img.format() == QImage::Format_RGB32) + avg = (r + g + b + 1) / 3; // "+1" for rounding. + else // Format_ARGB_Premultiplied + avg = qAlpha(src[x]); + + src[x] = qRgba(r, g, b, avg); + // swizzle the bits to accommodate for the GL_RGBA upload. +#if Q_BYTE_ORDER != Q_BIG_ENDIAN + if (ctx->isOpenGLES()) +#endif + src[x] = ARGB2RGBA(src[x]); + } + } + } + } + + funcs->glBindTexture(GL_TEXTURE_2D, texture); + if (img.depth() == 32) { +#ifdef QT_OPENGL_ES_2 + GLenum fmt = GL_RGBA; +#else + GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA; +#endif // QT_OPENGL_ES_2 + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + fmt = GL_RGBA; +#endif + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, fmt, GL_UNSIGNED_BYTE, img.constBits()); + } else { + // The scanlines in image are 32-bit aligned, even for mono or 8-bit formats. This + // is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT. +#if !defined(QT_OPENGL_ES_2) + const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA; +#else + const GLenum format = GL_ALPHA; +#endif + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, format, GL_UNSIGNED_BYTE, img.constBits()); + } +} + +static void load_glyph_image_region_to_texture(QOpenGLContext *ctx, + const QImage &srcImg, + int x, int y, + int w, int h, + GLuint texture, + int tx, int ty) +{ + Q_ASSERT(x + w <= srcImg.width() && y + h <= srcImg.height()); + + QImage img; + if (x != 0 || y != 0 || w != srcImg.width() || h != srcImg.height()) + img = srcImg.copy(x, y, w, h); + else + img = srcImg; + + load_glyph_image_to_texture(ctx, img, texture, tx, ty); +} + +void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx == nullptr) { + qWarning("QOpenGLTextureGlyphCache::resizeTextureData: Called with no context"); + return; + } + + QOpenGLFunctions *funcs = ctx->functions(); + GLint oldFbo; + funcs->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFbo); + + int oldWidth = m_textureResource->m_width; + int oldHeight = m_textureResource->m_height; + + // Make the lower glyph texture size 16 x 16. + if (width < 16) + width = 16; + if (height < 16) + height = 16; + + GLuint oldTexture = m_textureResource->m_texture; + createTextureData(width, height); + + if (ctx->d_func()->workaround_brokenFBOReadBack) { + QImageTextureGlyphCache::resizeTextureData(width, height); + load_glyph_image_region_to_texture(ctx, image(), 0, 0, qMin(oldWidth, width), qMin(oldHeight, height), + m_textureResource->m_texture, 0, 0); + return; + } + + // ### the QTextureGlyphCache API needs to be reworked to allow + // ### resizeTextureData to fail + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, m_textureResource->m_fbo); + + GLuint tmp_texture; + funcs->glGenTextures(1, &tmp_texture); + funcs->glBindTexture(GL_TEXTURE_2D, tmp_texture); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_filterMode = Nearest; + funcs->glBindTexture(GL_TEXTURE_2D, 0); + funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tmp_texture, 0); + + funcs->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + funcs->glBindTexture(GL_TEXTURE_2D, oldTexture); + + if (pex != nullptr) + pex->transferMode(BrushDrawingMode); + + funcs->glDisable(GL_STENCIL_TEST); + funcs->glDisable(GL_DEPTH_TEST); + funcs->glDisable(GL_SCISSOR_TEST); + funcs->glDisable(GL_BLEND); + + funcs->glViewport(0, 0, oldWidth, oldHeight); + + QOpenGLShaderProgram *blitProgram = nullptr; + if (pex == nullptr) { + if (m_blitProgram == nullptr) { + m_blitProgram = new QOpenGLShaderProgram; + const bool isCoreProfile = ctx->format().profile() == QSurfaceFormat::CoreProfile; + + { + QString source; +#ifdef Q_OS_WASM + source.append(QLatin1String(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader)); + source.append(QLatin1String(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader)); +#else + source.append(QLatin1String(isCoreProfile ? qopenglslMainWithTexCoordsVertexShader_core : qopenglslMainWithTexCoordsVertexShader)); + source.append(QLatin1String(isCoreProfile ? qopenglslUntransformedPositionVertexShader_core : qopenglslUntransformedPositionVertexShader)); +#endif + m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, source); + } + + { + QString source; +#ifdef Q_OS_WASM + source.append(QLatin1String(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader)); + source.append(QLatin1String(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader)); +#else + source.append(QLatin1String(isCoreProfile ? qopenglslMainFragmentShader_core : qopenglslMainFragmentShader)); + source.append(QLatin1String(isCoreProfile ? qopenglslImageSrcFragmentShader_core : qopenglslImageSrcFragmentShader)); +#endif + m_blitProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, source); + } + + m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + + m_blitProgram->link(); + + if (m_vao.isCreated()) { + m_vao.bind(); + setupVertexAttribs(); + } + } + + if (m_vao.isCreated()) + m_vao.bind(); + else + setupVertexAttribs(); + + m_blitProgram->bind(); + blitProgram = m_blitProgram; + + } else { + pex->uploadData(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray, 8); + pex->uploadData(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray, 8); + + pex->shaderManager->useBlitProgram(); + blitProgram = pex->shaderManager->blitProgram(); + } + + blitProgram->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT); + + funcs->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); + + funcs->glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, oldWidth, oldHeight); + + funcs->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, 0); + funcs->glDeleteTextures(1, &tmp_texture); + funcs->glDeleteTextures(1, &oldTexture); + + funcs->glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)oldFbo); + + if (pex != nullptr) { + funcs->glViewport(0, 0, pex->width, pex->height); + pex->updateClipScissorTest(); + } else { + if (m_vao.isCreated()) { + m_vao.release(); + } else { + m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); + m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); + } + } +} + +void QOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx == nullptr) { + qWarning("QOpenGLTextureGlyphCache::fillTexture: Called with no context"); + return; + } + + if (ctx->d_func()->workaround_brokenFBOReadBack) { + QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); + load_glyph_image_region_to_texture(ctx, image(), c.x, c.y, c.w, c.h, m_textureResource->m_texture, c.x, c.y); + return; + } + + QImage mask = textureMapForGlyph(glyph, subPixelPosition); + load_glyph_image_to_texture(ctx, mask, m_textureResource->m_texture, c.x, c.y); +} + +int QOpenGLTextureGlyphCache::glyphPadding() const +{ + return 1; +} + +int QOpenGLTextureGlyphCache::maxTextureWidth() const +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (ctx == nullptr) + return QImageTextureGlyphCache::maxTextureWidth(); + else + return ctx->d_func()->maxTextureSize(); +} + +int QOpenGLTextureGlyphCache::maxTextureHeight() const +{ + QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); + if (ctx == nullptr) + return QImageTextureGlyphCache::maxTextureHeight(); + + if (ctx->d_func()->workaround_brokenTexSubImage) + return qMin(1024, ctx->d_func()->maxTextureSize()); + else + return ctx->d_func()->maxTextureSize(); +} + +void QOpenGLTextureGlyphCache::clear() +{ + if (m_textureResource) + m_textureResource->free(); + m_textureResource = nullptr; + + delete m_blitProgram; + m_blitProgram = nullptr; + + m_w = 0; + m_h = 0; + m_cx = 0; + m_cy = 0; + m_currentRowHeight = 0; + coords.clear(); +} + +QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/qopengltextureglyphcache_p.h index 7c12ce8998..15ecd6209b 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/qopengltextureglyphcache_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QTEXTUREGLYPHCACHE_GL_P_H -#define QTEXTUREGLYPHCACHE_GL_P_H +#ifndef QOPENGLTEXTUREGLYPHCACHE_P_H +#define QOPENGLTEXTUREGLYPHCACHE_P_H // // W A R N I N G @@ -51,46 +51,46 @@ // We mean it. // +#include <QtOpenGL/qtopenglglobal.h> #include <private/qtextureglyphcache_p.h> -#include <private/qgl_p.h> -#include <qglshaderprogram.h> -#include <qglframebufferobject.h> +#include <private/qopenglcontext_p.h> +#include <qopenglshaderprogram.h> #include <qopenglfunctions.h> +#include <qopenglbuffer.h> +#include <qopenglvertexarrayobject.h> // #define QT_GL_TEXTURE_GLYPH_CACHE_DEBUG QT_BEGIN_NAMESPACE -class QGL2PaintEngineExPrivate; +class QOpenGL2PaintEngineExPrivate; -struct QGLGlyphTexture : public QOpenGLSharedResource +class QOpenGLGlyphTexture : public QOpenGLSharedResource { - QGLGlyphTexture(const QGLContext *ctx) - : QOpenGLSharedResource(ctx->contextHandle()->shareGroup()) - , m_fbo(0) +public: + explicit QOpenGLGlyphTexture(QOpenGLContext *ctx) + : QOpenGLSharedResource(ctx->shareGroup()) , m_width(0) , m_height(0) { - if (ctx && QGLFramebufferObject::hasOpenGLFramebufferObjects() && !ctx->d_ptr->workaround_brokenFBOReadBack) - ctx->contextHandle()->functions()->glGenFramebuffers(1, &m_fbo); + if (!ctx->d_func()->workaround_brokenFBOReadBack) + QOpenGLFunctions(ctx).glGenFramebuffers(1, &m_fbo); #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG - qDebug(" -> QGLGlyphTexture() %p for context %p.", this, ctx); + qDebug(" -> QOpenGLGlyphTexture() %p for context %p.", this, ctx); #endif } void freeResource(QOpenGLContext *context) override { - const QGLContext *ctx = QGLContext::fromOpenGLContext(context); + QOpenGLContext *ctx = context; #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG - qDebug("~QGLGlyphTexture() %p for context %p.", this, ctx); -#else - Q_UNUSED(ctx); + qDebug("~QOpenGLGlyphTexture() %p for context %p.", this, ctx); #endif - if (ctx && m_fbo) - ctx->contextHandle()->functions()->glDeleteFramebuffers(1, &m_fbo); + if (!ctx->d_func()->workaround_brokenFBOReadBack) + ctx->functions()->glDeleteFramebuffers(1, &m_fbo); if (m_width || m_height) - ctx->contextHandle()->functions()->glDeleteTextures(1, &m_texture); + ctx->functions()->glDeleteTextures(1, &m_texture); } void invalidateResource() override @@ -107,11 +107,11 @@ struct QGLGlyphTexture : public QOpenGLSharedResource int m_height; }; -class Q_OPENGL_EXPORT QGLTextureGlyphCache : public QImageTextureGlyphCache +class Q_OPENGL_EXPORT QOpenGLTextureGlyphCache : public QImageTextureGlyphCache { public: - QGLTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix); - ~QGLTextureGlyphCache(); + QOpenGLTextureGlyphCache(QFontEngine::GlyphFormat glyphFormat, const QTransform &matrix, const QColor &color = QColor()); + ~QOpenGLTextureGlyphCache(); virtual void createTextureData(int width, int height) override; virtual void resizeTextureData(int width, int height) override; @@ -121,23 +121,23 @@ public: virtual int maxTextureHeight() const override; inline GLuint texture() const { - QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); - QGLGlyphTexture *glyphTexture = that->m_textureResource; + QOpenGLTextureGlyphCache *that = const_cast<QOpenGLTextureGlyphCache *>(this); + QOpenGLGlyphTexture *glyphTexture = that->m_textureResource; return glyphTexture ? glyphTexture->m_texture : 0; } inline int width() const { - QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); - QGLGlyphTexture *glyphTexture = that->m_textureResource; + QOpenGLTextureGlyphCache *that = const_cast<QOpenGLTextureGlyphCache *>(this); + QOpenGLGlyphTexture *glyphTexture = that->m_textureResource; return glyphTexture ? glyphTexture->m_width : 0; } inline int height() const { - QGLTextureGlyphCache *that = const_cast<QGLTextureGlyphCache *>(this); - QGLGlyphTexture *glyphTexture = that->m_textureResource; + QOpenGLTextureGlyphCache *that = const_cast<QOpenGLTextureGlyphCache *>(this); + QOpenGLGlyphTexture *glyphTexture = that->m_textureResource; return glyphTexture ? glyphTexture->m_height : 0; } - inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } + inline void setPaintEnginePrivate(QOpenGL2PaintEngineExPrivate *p) { pex = p; } inline const QOpenGLContextGroup *contextGroup() const { return m_textureResource ? m_textureResource->group() : nullptr; } @@ -152,20 +152,30 @@ public: void clear(); + QOpenGL2PaintEngineExPrivate *paintEnginePrivate() const + { + return pex; + } + private: - QGLGlyphTexture *m_textureResource; + void setupVertexAttribs(); - QGL2PaintEngineExPrivate *pex; - QGLShaderProgram *m_blitProgram; + QOpenGLGlyphTexture *m_textureResource; + + QOpenGL2PaintEngineExPrivate *pex; + QOpenGLShaderProgram *m_blitProgram; FilterMode m_filterMode; GLfloat m_vertexCoordinateArray[8]; GLfloat m_textureCoordinateArray[8]; int m_serialNumber; + + QOpenGLBuffer m_buffer; + QOpenGLVertexArrayObject m_vao; }; QT_END_NAMESPACE -#endif +#endif // QOPENGLTEXTUREGLYPHCACHE_P_H diff --git a/src/opengl/qopengltexturehelper.cpp b/src/opengl/qopengltexturehelper.cpp new file mode 100644 index 0000000000..8f1473ecc9 --- /dev/null +++ b/src/opengl/qopengltexturehelper.cpp @@ -0,0 +1,589 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltexturehelper_p.h" + +#include <QOpenGLContext> +#include <private/qopenglextensions_p.h> + +QT_BEGIN_NAMESPACE + +QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context) +{ + functions = context->functions(); + // Resolve EXT_direct_state_access entry points if present. + + // However, disable it on some systems where DSA is known to be unreliable. + bool allowDSA = true; + const char *renderer = reinterpret_cast<const char *>(context->functions()->glGetString(GL_RENDERER)); + // QTBUG-40653, QTBUG-44988 + if (renderer && strstr(renderer, "AMD Radeon HD")) + allowDSA = false; + + if (allowDSA && !context->isOpenGLES() + && context->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + TextureParameteriEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLenum , GLint )>(context->getProcAddress("glTextureParameteriEXT")); + TextureParameterivEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLenum , const GLint *)>(context->getProcAddress("glTextureParameterivEXT")); + TextureParameterfEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLenum , GLfloat )>(context->getProcAddress("glTextureParameterfEXT")); + TextureParameterfvEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLenum , const GLfloat *)>(context->getProcAddress("glTextureParameterfvEXT")); + GenerateTextureMipmapEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum )>(context->getProcAddress("glGenerateTextureMipmapEXT")); + TextureStorage3DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLenum , GLsizei , GLsizei , GLsizei )>(context->getProcAddress("glTextureStorage3DEXT")); + TextureStorage2DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLenum , GLsizei , GLsizei )>(context->getProcAddress("glTextureStorage2DEXT")); + TextureStorage1DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLenum , GLsizei )>(context->getProcAddress("glTextureStorage1DEXT")); + TextureStorage3DMultisampleEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLenum , GLsizei , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTextureStorage3DMultisampleEXT")); + TextureStorage2DMultisampleEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLenum , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTextureStorage2DMultisampleEXT")); + TextureImage3DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLenum , GLsizei , GLsizei , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTextureImage3DEXT")); + TextureImage2DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLenum , GLsizei , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTextureImage2DEXT")); + TextureImage1DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLenum , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTextureImage1DEXT")); + TextureSubImage3DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTextureSubImage3DEXT")); + TextureSubImage2DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLint , GLint , GLsizei , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTextureSubImage2DEXT")); + TextureSubImage1DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLint , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTextureSubImage1DEXT")); + CompressedTextureSubImage1DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLint , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTextureSubImage1DEXT")); + CompressedTextureSubImage2DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLint , GLint , GLsizei , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTextureSubImage2DEXT")); + CompressedTextureSubImage3DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTextureSubImage3DEXT")); + CompressedTextureImage1DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLenum , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTextureImage1DEXT")); + CompressedTextureImage2DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLenum , GLsizei , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTextureImage2DEXT")); + CompressedTextureImage3DEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint , GLenum , GLsizei , GLsizei , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTextureImage3DEXT")); + + // Use the real DSA functions + TextureParameteri = &QOpenGLTextureHelper::dsa_TextureParameteri; + TextureParameteriv = &QOpenGLTextureHelper::dsa_TextureParameteriv; + TextureParameterf = &QOpenGLTextureHelper::dsa_TextureParameterf; + TextureParameterfv = &QOpenGLTextureHelper::dsa_TextureParameterfv; + GenerateTextureMipmap = &QOpenGLTextureHelper::dsa_GenerateTextureMipmap; + TextureStorage3D = &QOpenGLTextureHelper::dsa_TextureStorage3D; + TextureStorage2D = &QOpenGLTextureHelper::dsa_TextureStorage2D; + TextureStorage1D = &QOpenGLTextureHelper::dsa_TextureStorage1D; + TextureStorage3DMultisample = &QOpenGLTextureHelper::dsa_TextureStorage3DMultisample; + TextureStorage2DMultisample = &QOpenGLTextureHelper::dsa_TextureStorage2DMultisample; + TextureImage3D = &QOpenGLTextureHelper::dsa_TextureImage3D; + TextureImage2D = &QOpenGLTextureHelper::dsa_TextureImage2D; + TextureImage1D = &QOpenGLTextureHelper::dsa_TextureImage1D; + TextureSubImage3D = &QOpenGLTextureHelper::dsa_TextureSubImage3D; + TextureSubImage2D = &QOpenGLTextureHelper::dsa_TextureSubImage2D; + TextureSubImage1D = &QOpenGLTextureHelper::dsa_TextureSubImage1D; + CompressedTextureSubImage1D = &QOpenGLTextureHelper::dsa_CompressedTextureSubImage1D; + CompressedTextureSubImage2D = &QOpenGLTextureHelper::dsa_CompressedTextureSubImage2D; + CompressedTextureSubImage3D = &QOpenGLTextureHelper::dsa_CompressedTextureSubImage3D; + CompressedTextureImage1D = &QOpenGLTextureHelper::dsa_CompressedTextureImage1D; + CompressedTextureImage2D = &QOpenGLTextureHelper::dsa_CompressedTextureImage2D; + CompressedTextureImage3D = &QOpenGLTextureHelper::dsa_CompressedTextureImage3D; + } else { + // Use our own DSA emulation + TextureParameteri = &QOpenGLTextureHelper::qt_TextureParameteri; + TextureParameteriv = &QOpenGLTextureHelper::qt_TextureParameteriv; + TextureParameterf = &QOpenGLTextureHelper::qt_TextureParameterf; + TextureParameterfv = &QOpenGLTextureHelper::qt_TextureParameterfv; + GenerateTextureMipmap = &QOpenGLTextureHelper::qt_GenerateTextureMipmap; + TextureStorage3D = &QOpenGLTextureHelper::qt_TextureStorage3D; + TextureStorage2D = &QOpenGLTextureHelper::qt_TextureStorage2D; + TextureStorage1D = &QOpenGLTextureHelper::qt_TextureStorage1D; + TextureStorage3DMultisample = &QOpenGLTextureHelper::qt_TextureStorage3DMultisample; + TextureStorage2DMultisample = &QOpenGLTextureHelper::qt_TextureStorage2DMultisample; + TextureImage3D = &QOpenGLTextureHelper::qt_TextureImage3D; + TextureImage2D = &QOpenGLTextureHelper::qt_TextureImage2D; + TextureImage1D = &QOpenGLTextureHelper::qt_TextureImage1D; + TextureSubImage3D = &QOpenGLTextureHelper::qt_TextureSubImage3D; + TextureSubImage2D = &QOpenGLTextureHelper::qt_TextureSubImage2D; + TextureSubImage1D = &QOpenGLTextureHelper::qt_TextureSubImage1D; + CompressedTextureSubImage1D = &QOpenGLTextureHelper::qt_CompressedTextureSubImage1D; + CompressedTextureSubImage2D = &QOpenGLTextureHelper::qt_CompressedTextureSubImage2D; + CompressedTextureSubImage3D = &QOpenGLTextureHelper::qt_CompressedTextureSubImage3D; + CompressedTextureImage1D = &QOpenGLTextureHelper::qt_CompressedTextureImage1D; + CompressedTextureImage2D = &QOpenGLTextureHelper::qt_CompressedTextureImage2D; + CompressedTextureImage3D = &QOpenGLTextureHelper::qt_CompressedTextureImage3D; + } + + // Some DSA functions are part of NV_texture_multisample instead + if (!context->isOpenGLES() + && context->hasExtension(QByteArrayLiteral("GL_NV_texture_multisample"))) { + TextureImage3DMultisampleNV = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLint , GLsizei , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTextureImage3DMultisampleNV")); + TextureImage2DMultisampleNV = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLsizei , GLint , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTextureImage2DMultisampleNV")); + + TextureImage3DMultisample = &QOpenGLTextureHelper::dsa_TextureImage3DMultisample; + TextureImage2DMultisample = &QOpenGLTextureHelper::dsa_TextureImage2DMultisample; + } else { + TextureImage3DMultisample = &QOpenGLTextureHelper::qt_TextureImage3DMultisample; + TextureImage2DMultisample = &QOpenGLTextureHelper::qt_TextureImage2DMultisample; + } + +#if defined(QT_OPENGL_ES_2) + // Here we are targeting OpenGL ES 2.0+ only. This is likely using EGL, where, + // similarly to WGL, non-extension functions (i.e. any function that is part of the + // GLES spec) *may* not be queried via eglGetProcAddress. + + // OpenGL 1.0 + TexImage1D = 0; + + // OpenGL 1.1 + TexSubImage1D = 0; + + // OpenGL 1.3 + GetCompressedTexImage = 0; + CompressedTexSubImage1D = 0; + CompressedTexSubImage2D = ::glCompressedTexSubImage2D; + CompressedTexImage1D = 0; + CompressedTexImage2D = ::glCompressedTexImage2D; + ActiveTexture = ::glActiveTexture; + + // OpenGL 3.0 + GenerateMipmap = ::glGenerateMipmap; + + // OpenGL 3.2 + TexImage3DMultisample = 0; + TexImage2DMultisample = 0; + + // OpenGL 4.2 + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx->format().majorVersion() >= 3) { + // OpenGL ES 3.0+ has immutable storage for 2D and 3D at least. + QOpenGLExtraFunctionsPrivate *extra = static_cast<QOpenGLExtensions *>(context->extraFunctions())->d(); + TexStorage3D = extra->f.TexStorage3D; + TexStorage2D = extra->f.TexStorage2D; + } else { + TexStorage3D = 0; + TexStorage2D = 0; + } + TexStorage1D = 0; + + // OpenGL 4.3 + TexStorage3DMultisample = 0; + TexStorage2DMultisample = 0; + TexBufferRange = 0; + TextureView = 0; + + // OpenGL ES 3.1+ has TexStorage2DMultisample + if (ctx->format().version() >= qMakePair(3, 1)) { + QOpenGLExtraFunctionsPrivate *extra = static_cast<QOpenGLExtensions *>(context->extraFunctions())->d(); + TexStorage2DMultisample = extra->f.TexStorage2DMultisample; + } + +#endif + + if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_OES_texture_3D"))) { + TexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*)>(context->getProcAddress("glTexImage3DOES")); + TexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*)>(context->getProcAddress("glTexSubImage3DOES")); + CompressedTexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*)>(context->getProcAddress("glCompressedTexImage3DOES")); + CompressedTexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*)>(context->getProcAddress("glCompressedTexSubImage3DOES")); + } else { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (ctx->isOpenGLES() && ctx->format().majorVersion() >= 3) { + // OpenGL ES 3.0+ has glTexImage3D. + QOpenGLExtraFunctionsPrivate *extra = static_cast<QOpenGLExtensions *>(context->extraFunctions())->d(); + TexImage3D = extra->f.TexImage3D; + TexSubImage3D = extra->f.TexSubImage3D; + CompressedTexImage3D = extra->f.CompressedTexImage3D; + CompressedTexSubImage3D = extra->f.CompressedTexSubImage3D; + } else { + // OpenGL 1.2 + TexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLsizei , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTexImage3D")); + TexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTexSubImage3D")); + + // OpenGL 1.3 + CompressedTexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLenum , GLsizei , GLsizei , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTexImage3D")); + CompressedTexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTexSubImage3D")); + } + } + +#ifndef QT_OPENGL_ES_2 + // OpenGL 1.0 and 1.1 + TexImage1D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTexImage1D")); + TexSubImage1D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress("glTexSubImage1D"));\ + + // OpenGL 1.3 + GetCompressedTexImage = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLvoid *)>(context->getProcAddress("glGetCompressedTexImage")); + CompressedTexSubImage1D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTexSubImage1D")); + CompressedTexSubImage2D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLsizei , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTexSubImage2D")); + CompressedTexImage1D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLenum , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTexImage1D")); + CompressedTexImage2D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLenum , GLsizei , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress("glCompressedTexImage2D")); + ActiveTexture = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum )>(context->getProcAddress("glActiveTexture")); + + // OpenGL 3.0 + GenerateMipmap = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum )>(context->getProcAddress("glGenerateMipmap")); + + // OpenGL 3.2 + TexImage3DMultisample = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLint , GLsizei , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTexImage3DMultisample")); + TexImage2DMultisample = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLint , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTexImage2DMultisample")); + + // OpenGL 4.2 + TexStorage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLenum , GLsizei , GLsizei , GLsizei )>(context->getProcAddress("glTexStorage3D")); + TexStorage2D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLenum , GLsizei , GLsizei )>(context->getProcAddress("glTexStorage2D")); + TexStorage1D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLenum , GLsizei )>(context->getProcAddress("glTexStorage1D")); + + // OpenGL 4.3 + TexStorage3DMultisample = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLenum , GLsizei , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTexStorage3DMultisample")); + TexStorage2DMultisample = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLsizei , GLenum , GLsizei , GLsizei , GLboolean )>(context->getProcAddress("glTexStorage2DMultisample")); + TexBufferRange = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLenum , GLuint , GLintptr , GLsizeiptr )>(context->getProcAddress("glTexBufferRange")); + TextureView = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLuint , GLenum , GLuint , GLuint , GLuint , GLuint )>(context->getProcAddress("glTextureView")); +#endif +} + +void QOpenGLTextureHelper::dsa_TextureParameteri(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param) +{ + Q_UNUSED(bindingTarget); + TextureParameteriEXT(texture, target, pname, param); +} + +void QOpenGLTextureHelper::dsa_TextureParameteriv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLint *params) +{ + Q_UNUSED(bindingTarget); + TextureParameterivEXT(texture, target, pname, params); +} + +void QOpenGLTextureHelper::dsa_TextureParameterf(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLfloat param) +{ + Q_UNUSED(bindingTarget); + TextureParameterfEXT(texture, target, pname, param); +} + +void QOpenGLTextureHelper::dsa_TextureParameterfv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLfloat *params) +{ + Q_UNUSED(bindingTarget); + TextureParameterfvEXT(texture, target, pname, params); +} + +void QOpenGLTextureHelper::dsa_GenerateTextureMipmap(GLuint texture, GLenum target, GLenum bindingTarget) +{ + Q_UNUSED(bindingTarget); + GenerateTextureMipmapEXT(texture, target); +} + +void QOpenGLTextureHelper::dsa_TextureStorage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth) +{ + Q_UNUSED(bindingTarget); + TextureStorage3DEXT(texture, target, levels, internalFormat, width, height, depth); +} + +void QOpenGLTextureHelper::dsa_TextureStorage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) +{ + Q_UNUSED(bindingTarget); + TextureStorage2DEXT(texture, target, levels, internalFormat, width, height); +} + +void QOpenGLTextureHelper::dsa_TextureStorage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width) +{ + Q_UNUSED(bindingTarget); + TextureStorage1DEXT(texture, target, levels, internalFormat, width); +} + +void QOpenGLTextureHelper::dsa_TextureStorage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) +{ + Q_UNUSED(bindingTarget); + TextureStorage3DMultisampleEXT(texture, target, samples, internalFormat, width, height, depth, fixedSampleLocations); +} + +void QOpenGLTextureHelper::dsa_TextureStorage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) +{ + Q_UNUSED(bindingTarget); + TextureStorage2DMultisampleEXT(texture, target, samples, internalFormat, width, height, fixedSampleLocations); +} + +void QOpenGLTextureHelper::dsa_TextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + Q_UNUSED(bindingTarget); + TextureImage3DEXT(texture, target, level, internalFormat, width, height, depth, border, format, type, pixels); +} + +void QOpenGLTextureHelper::dsa_TextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + Q_UNUSED(bindingTarget); + TextureImage2DEXT(texture, target, level, internalFormat, width, height, border, format, type, pixels); +} + +void QOpenGLTextureHelper::dsa_TextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + Q_UNUSED(bindingTarget); + TextureImage1DEXT(texture, target, level, internalFormat, width, border, format, type, pixels); +} + +void QOpenGLTextureHelper::dsa_TextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels) +{ + Q_UNUSED(bindingTarget); + TextureSubImage3DEXT(texture, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); +} + +void QOpenGLTextureHelper::dsa_TextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) +{ + Q_UNUSED(bindingTarget); + TextureSubImage2DEXT(texture, target, level, xoffset, yoffset, width, height, format, type, pixels); +} + +void QOpenGLTextureHelper::dsa_TextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels) +{ + Q_UNUSED(bindingTarget); + TextureSubImage1DEXT(texture, target, level, xoffset, width, format, type, pixels); +} + +void QOpenGLTextureHelper::dsa_TextureImage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) +{ + Q_UNUSED(bindingTarget); + TextureImage3DMultisampleNV(texture, target, samples, internalFormat, width, height, depth, fixedSampleLocations); +} + +void QOpenGLTextureHelper::dsa_TextureImage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) +{ + Q_UNUSED(bindingTarget); + TextureImage2DMultisampleNV(texture, target, samples, internalFormat, width, height, fixedSampleLocations); +} + +void QOpenGLTextureHelper::dsa_CompressedTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits) +{ + Q_UNUSED(bindingTarget); + CompressedTextureSubImage1DEXT(texture, target, level, xoffset, width, format, imageSize, bits); +} + +void QOpenGLTextureHelper::dsa_CompressedTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits) +{ + Q_UNUSED(bindingTarget); + CompressedTextureSubImage2DEXT(texture, target, level, xoffset, yoffset, width, height, format, imageSize, bits); +} + +void QOpenGLTextureHelper::dsa_CompressedTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits) +{ + Q_UNUSED(bindingTarget); + CompressedTextureSubImage3DEXT(texture, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits); +} + +void QOpenGLTextureHelper::dsa_CompressedTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits) +{ + Q_UNUSED(bindingTarget); + CompressedTextureImage1DEXT(texture, target, level, internalFormat, width, border, imageSize, bits); +} + +void QOpenGLTextureHelper::dsa_CompressedTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits) +{ + Q_UNUSED(bindingTarget); + CompressedTextureImage2DEXT(texture, target, level, internalFormat, width, height, border, imageSize, bits); +} + +void QOpenGLTextureHelper::dsa_CompressedTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits) +{ + Q_UNUSED(bindingTarget); + CompressedTextureImage3DEXT(texture, target, level, internalFormat, width, height, depth, border, imageSize, bits); +} + +namespace { + +class TextureBinder +{ +public: + TextureBinder(QOpenGLFunctions *functions, GLuint texture, GLenum target, GLenum bindingTarget) + : m_functions(functions) + { + // For cubemaps we can't use the standard DSA emulation as it is illegal to + // try to bind a texture to one of the cubemap face targets. So we force the + // target and binding target to the cubemap values in this case. + switch (target) { + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + bindingTarget = GL_TEXTURE_BINDING_CUBE_MAP; + m_target = GL_TEXTURE_CUBE_MAP; + break; + + default: + m_target = target; + break; + } + + m_functions->glGetIntegerv(bindingTarget, &m_oldTexture); + m_functions->glBindTexture(m_target, texture); + } + + ~TextureBinder() + { + m_functions->glBindTexture(m_target, m_oldTexture); + } + +private: + QOpenGLFunctions *m_functions; + GLenum m_target; + GLint m_oldTexture; +}; + +} // namespace + +void QOpenGLTextureHelper::qt_TextureParameteri(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glTexParameteri(target, pname, param); +} + +void QOpenGLTextureHelper::qt_TextureParameteriv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLint *params) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glTexParameteriv(target, pname, params); +} + +void QOpenGLTextureHelper::qt_TextureParameterf(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLfloat param) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glTexParameterf(target, pname, param); +} + +void QOpenGLTextureHelper::qt_TextureParameterfv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLfloat *params) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glTexParameterfv(target, pname, params); +} + +void QOpenGLTextureHelper::qt_GenerateTextureMipmap(GLuint texture, GLenum target, GLenum bindingTarget) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glGenerateMipmap(target); +} + +void QOpenGLTextureHelper::qt_TextureStorage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexStorage3D(target, levels, internalFormat, width, height, depth); +} + +void QOpenGLTextureHelper::qt_TextureStorage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexStorage2D(target, levels, internalFormat, width, height); +} + +void QOpenGLTextureHelper::qt_TextureStorage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexStorage1D(target, levels, internalFormat, width); +} + +void QOpenGLTextureHelper::qt_TextureStorage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexStorage3DMultisample(target, samples, internalFormat, width, height, depth, fixedSampleLocations); +} + +void QOpenGLTextureHelper::qt_TextureStorage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexStorage2DMultisample(target, samples, internalFormat, width, height, fixedSampleLocations); +} + +void QOpenGLTextureHelper::qt_TextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels); +} + +void QOpenGLTextureHelper::qt_TextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels); +} + +void QOpenGLTextureHelper::qt_TextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexImage1D(target, level, internalFormat, width, border, format, type, pixels); +} + +void QOpenGLTextureHelper::qt_TextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); +} + +void QOpenGLTextureHelper::qt_TextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + functions->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); +} + +void QOpenGLTextureHelper::qt_TextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexSubImage1D(target, level, xoffset, width, format, type, pixels); +} + +void QOpenGLTextureHelper::qt_TextureImage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexImage3DMultisample(target, samples, internalFormat, width, height, depth, fixedSampleLocations); +} + +void QOpenGLTextureHelper::qt_TextureImage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glTexImage2DMultisample(target, samples, internalFormat, width, height, fixedSampleLocations); +} + +void QOpenGLTextureHelper::qt_CompressedTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glCompressedTexSubImage1D(target, level, xoffset, width, format, imageSize, bits); +} + +void QOpenGLTextureHelper::qt_CompressedTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, bits); +} + +void QOpenGLTextureHelper::qt_CompressedTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits); +} + +void QOpenGLTextureHelper::qt_CompressedTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glCompressedTexImage1D(target, level, internalFormat, width, border, imageSize, bits); +} + +void QOpenGLTextureHelper::qt_CompressedTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, bits); +} + +void QOpenGLTextureHelper::qt_CompressedTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits) +{ + TextureBinder binder(functions, texture, target, bindingTarget); + glCompressedTexImage3D(target, level, internalFormat, width, height, depth, border, imageSize, bits); +} + +QT_END_NAMESPACE diff --git a/src/opengl/qopengltexturehelper_p.h b/src/opengl/qopengltexturehelper_p.h new file mode 100644 index 0000000000..a62a47d029 --- /dev/null +++ b/src/opengl/qopengltexturehelper_p.h @@ -0,0 +1,797 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLTEXTUREHELPER_P_H +#define QOPENGLTEXTUREHELPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt 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. +// + +#include <QtOpenGL/qtopenglglobal.h> + +#ifndef QT_NO_OPENGL + +#include "qopengl.h" +#include "qopenglpixeltransferoptions.h" +#include "qopengltexture.h" +#include "qopenglfunctions.h" + +QT_BEGIN_NAMESPACE + +// Constants for OpenGL and OpenGL ES 3.0+ which are not available with OpenGL ES 2.0. +#ifndef GL_TEXTURE_BASE_LEVEL +#define GL_TEXTURE_BASE_LEVEL 0x813C +#endif +#ifndef GL_TEXTURE_MAX_LEVEL +#define GL_TEXTURE_MAX_LEVEL 0x813D +#endif +#ifndef GL_TEXTURE_COMPARE_MODE +#define GL_TEXTURE_COMPARE_MODE 0x884C +#endif +#ifndef GL_TEXTURE_COMPARE_FUNC +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#endif + +// use GL_APICALL only on Android + __clang__ +#if !defined(Q_OS_ANDROID) || !defined(__clang__) +# undef GL_APICALL +# define GL_APICALL +#elif !defined(GL_APICALL) +# define GL_APICALL +#endif + +class QOpenGLContext; + +class QOpenGLTextureHelper +{ +public: + QOpenGLTextureHelper(QOpenGLContext *context); + + // DSA-like API. Will either use real DSA or our emulation + inline void glTextureParameteri(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param) + { + (this->*TextureParameteri)(texture, target, bindingTarget, pname, param); + } + + inline void glTextureParameteriv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLint *params) + { + (this->*TextureParameteriv)(texture, target, bindingTarget, pname, params); + } + + inline void glTextureParameterf(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLfloat param) + { + (this->*TextureParameterf)(texture, target, bindingTarget, pname, param); + } + + inline void glTextureParameterfv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLfloat *params) + { + (this->*TextureParameterfv)(texture, target, bindingTarget, pname, params); + } + + inline void glGenerateTextureMipmap(GLuint texture, GLenum target, GLenum bindingTarget) + { + (this->*GenerateTextureMipmap)(texture, target, bindingTarget); + } + + inline void glTextureStorage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth) + { + (this->*TextureStorage3D)(texture, target, bindingTarget, levels, internalFormat, width, height, depth); + } + + inline void glTextureStorage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, + GLsizei width, GLsizei height) + { + (this->*TextureStorage2D)(texture, target, bindingTarget, levels, internalFormat, width, height); + } + + inline void glTextureStorage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, + GLsizei width) + { + (this->*TextureStorage1D)(texture, target, bindingTarget, levels, internalFormat, width); + } + + inline void glTextureStorage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) + { + (this->*TextureStorage3DMultisample)(texture, target, bindingTarget, samples, internalFormat, width, height, depth, fixedSampleLocations); + } + + inline void glTextureStorage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, + GLsizei width, GLsizei height, GLboolean fixedSampleLocations) + { + (this->*TextureStorage2DMultisample)(texture, target, bindingTarget, samples, internalFormat, width, height, fixedSampleLocations); + } + + inline void glTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) + { + (this->*TextureImage3D)(texture, target, bindingTarget, level, internalFormat, width, height, depth, border, format, type, pixels); + } + + inline void glTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) + { + (this->*TextureImage2D)(texture, target, bindingTarget, level, internalFormat, width, height, border, format, type, pixels); + } + + inline void glTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels) + { + (this->*TextureImage1D)(texture, target, bindingTarget, level, internalFormat, width, border, format, type, pixels); + } + + inline void glTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, + const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*TextureSubImage3D)(texture, target, bindingTarget, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); + setPixelUploadOptions(oldOptions); + } else { + (this->*TextureSubImage3D)(texture, target, bindingTarget, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); + } + } + + inline void glTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, + const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*TextureSubImage2D)(texture, target, bindingTarget, level, xoffset, yoffset, width, height, format, type, pixels); + setPixelUploadOptions(oldOptions); + } else { + (this->*TextureSubImage2D)(texture, target, bindingTarget, level, xoffset, yoffset, width, height, format, type, pixels); + } + } + + inline void glTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, + GLsizei width, GLenum format, GLenum type, + const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*TextureSubImage1D)(texture, target, bindingTarget, level, xoffset, width, format, type, pixels); + setPixelUploadOptions(oldOptions); + } else { + (this->*TextureSubImage1D)(texture, target, bindingTarget, level, xoffset, width, format, type, pixels); + } + } + + inline void glTextureImage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) + { + (this->*TextureImage3DMultisample)(texture, target, bindingTarget, samples, internalFormat, width, height, depth, fixedSampleLocations); + } + + inline void glTextureImage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, + GLsizei width, GLsizei height, GLboolean fixedSampleLocations) + { + (this->*TextureImage2DMultisample)(texture, target, bindingTarget, samples, internalFormat, width, height, fixedSampleLocations); + } + + inline void glCompressedTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLsizei width, + GLenum format, GLsizei imageSize, const GLvoid *bits, + const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*CompressedTextureSubImage1D)(texture, target, bindingTarget, level, xoffset, width, format, imageSize, bits); + setPixelUploadOptions(oldOptions); + } else { + (this->*CompressedTextureSubImage1D)(texture, target, bindingTarget, level, xoffset, width, format, imageSize, bits); + } + } + + inline void glCompressedTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, const GLvoid *bits, + const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*CompressedTextureSubImage2D)(texture, target, bindingTarget, level, xoffset, yoffset, width, height, format, imageSize, bits); + setPixelUploadOptions(oldOptions); + } else { + (this->*CompressedTextureSubImage2D)(texture, target, bindingTarget, level, xoffset, yoffset, width, height, format, imageSize, bits); + } + } + + inline void glCompressedTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLsizei imageSize, const GLvoid *bits, + const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*CompressedTextureSubImage3D)(texture, target, bindingTarget, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits); + setPixelUploadOptions(oldOptions); + } else { + (this->*CompressedTextureSubImage3D)(texture, target, bindingTarget, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits); + } + } + + inline void glCompressedTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLenum internalFormat, GLsizei width, + GLint border, GLsizei imageSize, const GLvoid *bits, + const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*CompressedTextureImage1D)(texture, target, bindingTarget, level, internalFormat, width, border, imageSize, bits); + setPixelUploadOptions(oldOptions); + } else { + (this->*CompressedTextureImage1D)(texture, target, bindingTarget, level, internalFormat, width, border, imageSize, bits); + } + } + + inline void glCompressedTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *bits, + const QOpenGLPixelTransferOptions * const options = nullptr) + + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*CompressedTextureImage2D)(texture, target, bindingTarget, level, internalFormat, width, height, border, imageSize, bits); + setPixelUploadOptions(oldOptions); + } else { + (this->*CompressedTextureImage2D)(texture, target, bindingTarget, level, internalFormat, width, height, border, imageSize, bits); + } + } + + inline void glCompressedTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLsizei imageSize, const GLvoid *bits, + const QOpenGLPixelTransferOptions * const options = nullptr) + { + if (options) { + QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions(); + setPixelUploadOptions(*options); + (this->*CompressedTextureImage3D)(texture, target, bindingTarget, level, internalFormat, width, height, depth, border, imageSize, bits); + setPixelUploadOptions(oldOptions); + } else { + (this->*CompressedTextureImage3D)(texture, target, bindingTarget, level, internalFormat, width, height, depth, border, imageSize, bits); + } + } + +private: + // DSA wrapper (so we can use pointer to member function as switch) + void dsa_TextureParameteri(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param); + + void dsa_TextureParameteriv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLint *params); + + void dsa_TextureParameterf(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLfloat param); + + void dsa_TextureParameterfv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLfloat *params); + + void dsa_GenerateTextureMipmap(GLuint texture, GLenum target, GLenum bindingTarget); + + void dsa_TextureStorage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth); + + void dsa_TextureStorage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, + GLsizei width, GLsizei height); + + void dsa_TextureStorage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, + GLsizei width); + + void dsa_TextureStorage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + + void dsa_TextureStorage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, + GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + + void dsa_TextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + + void dsa_TextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + + void dsa_TextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + + void dsa_TextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); + + void dsa_TextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); + + void dsa_TextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, + GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); + + void dsa_TextureImage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + + void dsa_TextureImage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, + GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + + void dsa_CompressedTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLsizei width, + GLenum format, GLsizei imageSize, const GLvoid *bits); + + void dsa_CompressedTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, const GLvoid *bits); + + void dsa_CompressedTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLsizei imageSize, const GLvoid *bits); + + void dsa_CompressedTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLenum internalFormat, GLsizei width, + GLint border, GLsizei imageSize, const GLvoid *bits); + + void dsa_CompressedTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *bits); + + void dsa_CompressedTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLsizei imageSize, const GLvoid *bits); + + // DSA emulation API + void qt_TextureParameteri(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param); + + void qt_TextureParameteriv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLint *params); + + void qt_TextureParameterf(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLfloat param); + + void qt_TextureParameterfv(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLfloat *params); + + void qt_GenerateTextureMipmap(GLuint texture, GLenum target, GLenum bindingTarget); + + void qt_TextureStorage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, + GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth); + + void qt_TextureStorage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, + GLenum internalFormat, GLsizei width, GLsizei height); + + void qt_TextureStorage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, + GLenum internalFormat, GLsizei width); + + void qt_TextureStorage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, + GLenum internalFormat, GLsizei width, GLsizei height, + GLsizei depth, GLboolean fixedSampleLocations); + + void qt_TextureStorage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, + GLenum internalFormat, GLsizei width, GLsizei height, + GLboolean fixedSampleLocations); + + void qt_TextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLenum format, GLenum type, + const GLvoid *pixels); + + void qt_TextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, + GLint border, GLenum format, GLenum type, + const GLvoid *pixels); + + void qt_TextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLint border, GLenum format, GLenum type, + const GLvoid *pixels); + + void qt_TextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLenum type, const GLvoid *pixels); + + void qt_TextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid *pixels); + + void qt_TextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLsizei width, + GLenum format, GLenum type, const GLvoid *pixels); + + void qt_TextureImage3DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, + GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, + GLboolean fixedSampleLocations); + + void qt_TextureImage2DMultisample(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, + GLint internalFormat, GLsizei width, GLsizei height, + GLboolean fixedSampleLocations); + + void qt_CompressedTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLsizei width, GLenum format, + GLsizei imageSize, const GLvoid *bits); + + void qt_CompressedTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, const GLvoid *bits); + + void qt_CompressedTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, + GLenum format, GLsizei imageSize, const GLvoid *bits); + + void qt_CompressedTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLint border, + GLsizei imageSize, const GLvoid *bits); + + void qt_CompressedTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid *bits); + + void qt_CompressedTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLsizei imageSize, const GLvoid *bits); + +public: + // Raw OpenGL functions, resolved and used by our DSA-like static functions if no EXT_direct_state_access is available + + // OpenGL 1.0 + inline void glTexImage1D(GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLint border, + GLenum format, GLenum type, const GLvoid *pixels) + { + TexImage1D(target, level, internalFormat, width, border, format, type, pixels); + } + + // OpenGL 1.1 + inline void glTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, + GLenum format, GLenum type, const GLvoid *pixels) + { + TexSubImage1D(target, level, xoffset, width, format, type, pixels); + } + + // OpenGL 1.2 + inline void glTexImage3D(GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum format, GLenum type, const GLvoid *pixels) + { + TexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels); + } + + inline void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels) + { + TexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); + } + + // OpenGL 1.3 + inline void glGetCompressedTexImage(GLenum target, GLint level, GLvoid *img) + { + GetCompressedTexImage(target, level, img); + } + + inline void glCompressedTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, + GLenum format, GLsizei imageSize, const GLvoid *data) + { + CompressedTexSubImage1D(target, level, xoffset, width, format, imageSize, data); + } + + inline void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data) + { + CompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); + } + + inline void glCompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data) + { + CompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); + } + + inline void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, + GLint border, GLsizei imageSize, const GLvoid *data) + { + CompressedTexImage1D(target, level, internalFormat, width, border, imageSize, data); + } + + inline void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *data) + { + CompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data); + } + + inline void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLsizei imageSize, const GLvoid *data) + { + CompressedTexImage3D(target, level, internalFormat, width, height, depth, border, imageSize, data); + } + + inline void glActiveTexture(GLenum texture) + { + ActiveTexture(texture); + } + + // OpenGL 3.0 + inline void glGenerateMipmap(GLenum target) + { + GenerateMipmap(target); + } + + // OpenGL 3.2 + inline void glTexImage3DMultisample(GLenum target, GLsizei samples, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, + GLboolean fixedSampleLocations) + { + TexImage3DMultisample(target, samples, internalFormat, width, height, depth, fixedSampleLocations); + } + + inline void glTexImage2DMultisample(GLenum target, GLsizei samples, GLint internalFormat, + GLsizei width, GLsizei height, + GLboolean fixedSampleLocations) + { + TexImage2DMultisample(target, samples, internalFormat, width, height, fixedSampleLocations); + } + + // OpenGL 4.2 + inline void glTexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth) + { + TexStorage3D(target, levels, internalFormat, width, height, depth); + } + + inline void glTexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) + { + TexStorage2D(target, levels, internalFormat, width, height); + } + + inline void glTexStorage1D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width) + { + TexStorage1D(target, levels, internalFormat, width); + } + + // OpenGL 4.3 + inline void glTexStorage3DMultisample(GLenum target, GLsizei samples, GLenum internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations) + { + TexStorage3DMultisample(target, samples, internalFormat, width, height, depth, fixedSampleLocations); + } + + inline void glTexStorage2DMultisample(GLenum target, GLsizei samples, GLenum internalFormat, + GLsizei width, GLsizei height, GLboolean fixedSampleLocations) + { + TexStorage2DMultisample(target, samples, internalFormat, width, height, fixedSampleLocations); + } + + inline void glTexBufferRange(GLenum target, GLenum internalFormat, GLuint buffer, + GLintptr offset, GLsizeiptr size) + { + TexBufferRange(target, internalFormat, buffer, offset, size); + } + + inline void glTextureView(GLuint texture, GLenum target, GLuint origTexture, GLenum internalFormat, + GLuint minLevel, GLuint numLevels, GLuint minLayer, GLuint numLayers) + { + TextureView(texture, target, origTexture, internalFormat, minLevel, numLevels, minLayer, numLayers); + } + + // Helper functions + inline QOpenGLPixelTransferOptions savePixelUploadOptions() + { + QOpenGLPixelTransferOptions options; + int val = 0; + functions->glGetIntegerv(GL_UNPACK_ALIGNMENT, &val); + options.setAlignment(val); +#if !defined(QT_OPENGL_ES_2) + functions->glGetIntegerv(GL_UNPACK_SKIP_IMAGES, &val); + options.setSkipImages(val); + functions->glGetIntegerv(GL_UNPACK_SKIP_ROWS, &val); + options.setSkipRows(val); + functions->glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &val); + options.setSkipPixels(val); + functions->glGetIntegerv(GL_UNPACK_IMAGE_HEIGHT, &val); + options.setImageHeight(val); + functions->glGetIntegerv(GL_UNPACK_ROW_LENGTH, &val); + options.setRowLength(val); + GLboolean b = GL_FALSE; + functions->glGetBooleanv(GL_UNPACK_LSB_FIRST, &b); + options.setLeastSignificantByteFirst(b); + functions->glGetBooleanv(GL_UNPACK_SWAP_BYTES, &b); + options.setSwapBytesEnabled(b); +#endif + return options; + } + + inline void setPixelUploadOptions(const QOpenGLPixelTransferOptions &options) + { + functions->glPixelStorei(GL_UNPACK_ALIGNMENT, options.alignment()); +#if !defined(QT_OPENGL_ES_2) + functions->glPixelStorei(GL_UNPACK_SKIP_IMAGES, options.skipImages()); + functions->glPixelStorei(GL_UNPACK_SKIP_ROWS, options.skipRows()); + functions->glPixelStorei(GL_UNPACK_SKIP_PIXELS, options.skipPixels()); + functions->glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, options.imageHeight()); + functions->glPixelStorei(GL_UNPACK_ROW_LENGTH, options.rowLength()); + functions->glPixelStorei(GL_UNPACK_LSB_FIRST, options.isLeastSignificantBitFirst()); + functions->glPixelStorei(GL_UNPACK_SWAP_BYTES, options.isSwapBytesEnabled()); +#endif + } + + QOpenGLFunctions *functions; +private: + // Typedefs and pointers to member functions used to switch between EXT_direct_state_access and our own emulated DSA. + // The argument match the corresponding GL function, but there's an extra "GLenum bindingTarget" which gets used with + // the DSA emulation -- it contains the right GL_BINDING_TEXTURE_X to use. + typedef void (QOpenGLTextureHelper::*TextureParameteriMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param); + typedef void (QOpenGLTextureHelper::*TextureParameterivMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLint *params); + typedef void (QOpenGLTextureHelper::*TextureParameterfMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLfloat param); + typedef void (QOpenGLTextureHelper::*TextureParameterfvMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, const GLfloat *params); + typedef void (QOpenGLTextureHelper::*GenerateTextureMipmapMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget); + typedef void (QOpenGLTextureHelper::*TextureStorage3DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth); + typedef void (QOpenGLTextureHelper::*TextureStorage2DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height); + typedef void (QOpenGLTextureHelper::*TextureStorage1DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei levels, GLenum internalFormat, GLsizei width); + typedef void (QOpenGLTextureHelper::*TextureStorage3DMultisampleMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + typedef void (QOpenGLTextureHelper::*TextureStorage2DMultisampleMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + typedef void (QOpenGLTextureHelper::*TextureImage3DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + typedef void (QOpenGLTextureHelper::*TextureImage2DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + typedef void (QOpenGLTextureHelper::*TextureImage1DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + typedef void (QOpenGLTextureHelper::*TextureSubImage3DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); + typedef void (QOpenGLTextureHelper::*TextureSubImage2DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); + typedef void (QOpenGLTextureHelper::*TextureSubImage1DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); + typedef void (QOpenGLTextureHelper::*TextureImage3DMultisampleMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + typedef void (QOpenGLTextureHelper::*TextureImage2DMultisampleMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + typedef void (QOpenGLTextureHelper::*CompressedTextureSubImage1DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits); + typedef void (QOpenGLTextureHelper::*CompressedTextureSubImage2DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits); + typedef void (QOpenGLTextureHelper::*CompressedTextureSubImage3DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits); + typedef void (QOpenGLTextureHelper::*CompressedTextureImage1DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits); + typedef void (QOpenGLTextureHelper::*CompressedTextureImage2DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits); + typedef void (QOpenGLTextureHelper::*CompressedTextureImage3DMemberFunc)(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits); + + + TextureParameteriMemberFunc TextureParameteri; + TextureParameterivMemberFunc TextureParameteriv; + TextureParameterfMemberFunc TextureParameterf; + TextureParameterfvMemberFunc TextureParameterfv; + GenerateTextureMipmapMemberFunc GenerateTextureMipmap; + TextureStorage3DMemberFunc TextureStorage3D; + TextureStorage2DMemberFunc TextureStorage2D; + TextureStorage1DMemberFunc TextureStorage1D; + TextureStorage3DMultisampleMemberFunc TextureStorage3DMultisample; + TextureStorage2DMultisampleMemberFunc TextureStorage2DMultisample; + TextureImage3DMemberFunc TextureImage3D; + TextureImage2DMemberFunc TextureImage2D; + TextureImage1DMemberFunc TextureImage1D; + TextureSubImage3DMemberFunc TextureSubImage3D; + TextureSubImage2DMemberFunc TextureSubImage2D; + TextureSubImage1DMemberFunc TextureSubImage1D; + TextureImage3DMultisampleMemberFunc TextureImage3DMultisample; + TextureImage2DMultisampleMemberFunc TextureImage2DMultisample; + CompressedTextureSubImage1DMemberFunc CompressedTextureSubImage1D; + CompressedTextureSubImage2DMemberFunc CompressedTextureSubImage2D; + CompressedTextureSubImage3DMemberFunc CompressedTextureSubImage3D; + CompressedTextureImage1DMemberFunc CompressedTextureImage1D; + CompressedTextureImage2DMemberFunc CompressedTextureImage2D; + CompressedTextureImage3DMemberFunc CompressedTextureImage3D; + + // Raw function pointers for core and DSA functions + + // EXT_direct_state_access used when DSA is available + void (QOPENGLF_APIENTRYP TextureParameteriEXT)(GLuint texture, GLenum target, GLenum pname, GLint param); + void (QOPENGLF_APIENTRYP TextureParameterivEXT)(GLuint texture, GLenum target, GLenum pname, const GLint *params); + void (QOPENGLF_APIENTRYP TextureParameterfEXT)(GLuint texture, GLenum target, GLenum pname, GLfloat param); + void (QOPENGLF_APIENTRYP TextureParameterfvEXT)(GLuint texture, GLenum target, GLenum pname, const GLfloat *params); + void (QOPENGLF_APIENTRYP GenerateTextureMipmapEXT)(GLuint texture, GLenum target); + void (QOPENGLF_APIENTRYP TextureStorage3DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth); + void (QOPENGLF_APIENTRYP TextureStorage2DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height); + void (QOPENGLF_APIENTRYP TextureStorage1DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width); + void (QOPENGLF_APIENTRYP TextureStorage3DMultisampleEXT)(GLuint texture, GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + void (QOPENGLF_APIENTRYP TextureStorage2DMultisampleEXT)(GLuint texture, GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + void (QOPENGLF_APIENTRYP TextureImage3DEXT)(GLuint texture, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP TextureImage2DEXT)(GLuint texture, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP TextureImage1DEXT)(GLuint texture, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP TextureSubImage3DEXT)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP TextureSubImage2DEXT)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP TextureSubImage1DEXT)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP CompressedTextureSubImage1DEXT)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *bits); + void (QOPENGLF_APIENTRYP CompressedTextureSubImage2DEXT)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *bits); + void (QOPENGLF_APIENTRYP CompressedTextureSubImage3DEXT)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *bits); + void (QOPENGLF_APIENTRYP CompressedTextureImage1DEXT)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits); + void (QOPENGLF_APIENTRYP CompressedTextureImage2DEXT)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits); + void (QOPENGLF_APIENTRYP CompressedTextureImage3DEXT)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits); + + + // Plus some missing ones that are in the NV_texture_multisample extension instead + void (QOPENGLF_APIENTRYP TextureImage3DMultisampleNV)(GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + void (QOPENGLF_APIENTRYP TextureImage2DMultisampleNV)(GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + + // OpenGL 1.0 + void (QOPENGLF_APIENTRYP TexImage1D)(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + + // OpenGL 1.1 + void (QOPENGLF_APIENTRYP TexSubImage1D)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); + + // OpenGL 1.2 + void (QOPENGLF_APIENTRYP TexImage3D)(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + void (QOPENGLF_APIENTRYP TexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); + + // OpenGL 1.3 + void (QOPENGLF_APIENTRYP GetCompressedTexImage)(GLenum target, GLint level, GLvoid *img); + void (QOPENGLF_APIENTRYP CompressedTexSubImage1D)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); + GL_APICALL void (QOPENGLF_APIENTRYP CompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); + void (QOPENGLF_APIENTRYP CompressedTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); + void (QOPENGLF_APIENTRYP CompressedTexImage1D)(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); + GL_APICALL void (QOPENGLF_APIENTRYP CompressedTexImage2D)(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); + void (QOPENGLF_APIENTRYP CompressedTexImage3D)(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); + GL_APICALL void (QOPENGLF_APIENTRYP ActiveTexture)(GLenum texture); + + // OpenGL 3.0 + GL_APICALL void (QOPENGLF_APIENTRYP GenerateMipmap)(GLenum target); + + // OpenGL 3.2 + void (QOPENGLF_APIENTRYP TexImage3DMultisample)(GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + void (QOPENGLF_APIENTRYP TexImage2DMultisample)(GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + + // OpenGL 4.2 + void (QOPENGLF_APIENTRYP TexStorage3D)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth); + void (QOPENGLF_APIENTRYP TexStorage2D)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height); + void (QOPENGLF_APIENTRYP TexStorage1D)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width); + + // OpenGL 4.3 + void (QOPENGLF_APIENTRYP TexStorage3DMultisample)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); + void (QOPENGLF_APIENTRYP TexStorage2DMultisample)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); + void (QOPENGLF_APIENTRYP TexBufferRange)(GLenum target, GLenum internalFormat, GLuint buffer, GLintptr offset, GLsizeiptr size); + void (QOPENGLF_APIENTRYP TextureView)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +}; + +QT_END_NAMESPACE + +#undef Q_CALL_MEMBER_FUNCTION + +#endif // QT_NO_OPENGL + +#endif // QOPENGLTEXTUREHELPER_P_H diff --git a/src/opengl/qopengltextureuploader.cpp b/src/opengl/qopengltextureuploader.cpp new file mode 100644 index 0000000000..469ddc56c1 --- /dev/null +++ b/src/opengl/qopengltextureuploader.cpp @@ -0,0 +1,381 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltextureuploader_p.h" + +#include <qimage.h> +#include <qmath.h> +#include <qopenglfunctions.h> +#include <private/qopenglcontext_p.h> +#include <private/qopenglextensions_p.h> + +#ifndef GL_RED +#define GL_RED 0x1903 +#endif + +#ifndef GL_GREEN +#define GL_GREEN 0x1904 +#endif + +#ifndef GL_BLUE +#define GL_BLUE 0x1905 +#endif + +#ifndef GL_RGB10_A2 +#define GL_RGB10_A2 0x8059 +#endif + +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif + +#ifndef GL_BGR +#define GL_BGR 0x80E0 +#endif + +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif + +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif + +#ifndef GL_TEXTURE_SWIZZLE_R +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#endif + +#ifndef GL_TEXTURE_SWIZZLE_G +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#endif + +#ifndef GL_TEXTURE_SWIZZLE_B +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#endif + +#ifndef GL_TEXTURE_SWIZZLE_A +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#endif + +#ifndef GL_SRGB +#define GL_SRGB 0x8C40 +#endif +#ifndef GL_SRGB_ALPHA +#define GL_SRGB_ALPHA 0x8C42 +#endif + +QT_BEGIN_NAMESPACE + +qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &image, QOpenGLTextureUploader::BindOptions options, QSize maxSize) +{ + QOpenGLContext *context = QOpenGLContext::currentContext(); + QOpenGLExtensions *funcs = static_cast<QOpenGLExtensions*>(context->functions()); + + QImage tx; + GLenum externalFormat; + GLenum internalFormat; + GLuint pixelType; + QImage::Format targetFormat = QImage::Format_Invalid; + const bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); + const bool isOpenGLES3orBetter = context->isOpenGLES() && context->format().majorVersion() >= 3; + const bool sRgbBinding = (options & SRgbBindOption); + Q_ASSERT(isOpenGL12orBetter || context->isOpenGLES()); + Q_ASSERT((options & (SRgbBindOption | UseRedForAlphaAndLuminanceBindOption)) != (SRgbBindOption | UseRedForAlphaAndLuminanceBindOption)); + + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + if (isOpenGL12orBetter) { + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_INT_8_8_8_8_REV; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + // Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian: + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) { + // The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external. + externalFormat = internalFormat = GL_BGRA; + pixelType = GL_UNSIGNED_BYTE; + } else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { + // Is only allowed as an external format like OpenGL. + externalFormat = GL_BGRA; + internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; +#endif + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); +#else + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_GREEN); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_BLUE); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_ALPHA); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_RED); +#endif + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + } else { + // No support for direct ARGB32 upload. + break; + } + targetFormat = image.format(); + break; + case QImage::Format_BGR30: + case QImage::Format_A2BGR30_Premultiplied: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (isOpenGL12orBetter || isOpenGLES3orBetter) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_RGBA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } + break; + case QImage::Format_RGB30: + case QImage::Format_A2RGB30_Premultiplied: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (isOpenGL12orBetter) { + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_BGRA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + externalFormat = GL_RGBA; + internalFormat = GL_RGB10_A2; + targetFormat = image.format(); + } + break; + case QImage::Format_RGB444: + case QImage::Format_RGB555: + case QImage::Format_RGB16: + if (isOpenGL12orBetter || context->isOpenGLES()) { + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_SHORT_5_6_5; + targetFormat = QImage::Format_RGB16; + } + break; + case QImage::Format_RGB666: + case QImage::Format_RGB888: + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_RGB888; + break; + case QImage::Format_BGR888: + if (isOpenGL12orBetter) { + externalFormat = GL_BGR; + internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_BGR888; + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_BGR888; + } + break; + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBA8888_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) + internalFormat = GL_RGBA16; + pixelType = GL_UNSIGNED_SHORT; + targetFormat = image.format(); + break; + case QImage::Format_Indexed8: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedForAlphaAndLuminanceBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } + break; + case QImage::Format_Alpha8: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedForAlphaAndLuminanceBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { + externalFormat = internalFormat = GL_ALPHA; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ALPHA); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ZERO); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ZERO); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ZERO); + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } + break; + case QImage::Format_Grayscale8: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedForAlphaAndLuminanceBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { + externalFormat = internalFormat = GL_LUMINANCE; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = image.format(); + } + break; + case QImage::Format_Grayscale16: + if (sRgbBinding) { + // Always needs conversion + break; + } else if (options & UseRedForAlphaAndLuminanceBindOption) { + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_SHORT; + targetFormat = image.format(); + } else if (context->isOpenGLES() || context->format().profile() != QSurfaceFormat::CoreProfile) { + externalFormat = internalFormat = GL_LUMINANCE; + pixelType = GL_UNSIGNED_SHORT; + targetFormat = image.format(); + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); + externalFormat = internalFormat = GL_RED; + pixelType = GL_UNSIGNED_SHORT; + targetFormat = image.format(); + } + break; + default: + break; + } + + // If no direct upload was detected above, convert to RGBA8888 and upload that + if (targetFormat == QImage::Format_Invalid) { + externalFormat = internalFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + if (!image.hasAlphaChannel()) + targetFormat = QImage::Format_RGBX8888; + else + targetFormat = QImage::Format_RGBA8888; + } + + if (options & PremultipliedAlphaBindOption) { + if (targetFormat == QImage::Format_ARGB32) + targetFormat = QImage::Format_ARGB32_Premultiplied; + else if (targetFormat == QImage::Format_RGBA8888) + targetFormat = QImage::Format_RGBA8888_Premultiplied; + else if (targetFormat == QImage::Format_RGBA64) + targetFormat = QImage::Format_RGBA64_Premultiplied; + } else { + if (targetFormat == QImage::Format_ARGB32_Premultiplied) + targetFormat = QImage::Format_ARGB32; + else if (targetFormat == QImage::Format_RGBA8888_Premultiplied) + targetFormat = QImage::Format_RGBA8888; + else if (targetFormat == QImage::Format_RGBA64_Premultiplied) + targetFormat = QImage::Format_RGBA64; + } + + if (sRgbBinding) { + Q_ASSERT(internalFormat == GL_RGBA || internalFormat == GL_RGB); + if (image.hasAlphaChannel()) + internalFormat = GL_SRGB_ALPHA; + else + internalFormat = GL_SRGB; + } + + if (image.format() != targetFormat) + tx = image.convertToFormat(targetFormat); + else + tx = image; + + QSize newSize = tx.size(); + if (!maxSize.isEmpty()) + newSize = newSize.boundedTo(maxSize); + if (options & PowerOfTwoBindOption) { + newSize.setWidth(qNextPowerOfTwo(newSize.width() - 1)); + newSize.setHeight(qNextPowerOfTwo(newSize.height() - 1)); + } + + if (newSize != tx.size()) + tx = tx.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + // Handle cases where the QImage is actually a sub image of its image data: + qsizetype naturalBpl = ((qsizetype(tx.width()) * tx.depth() + 31) >> 5) << 2; + if (tx.bytesPerLine() != naturalBpl) + tx = tx.copy(tx.rect()); + + funcs->glTexImage2D(target, 0, internalFormat, tx.width(), tx.height(), 0, externalFormat, pixelType, tx.constBits()); + + qsizetype cost = qint64(tx.width()) * tx.height() * tx.depth() / 8; + + return cost; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qgraphicsshadereffect_p.h b/src/opengl/qopengltextureuploader_p.h index 218caa2936..cea6d97658 100644 --- a/src/opengl/qgraphicsshadereffect_p.h +++ b/src/opengl/qopengltextureuploader_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtOpenGL module of the Qt Toolkit. @@ -37,9 +37,6 @@ ** ****************************************************************************/ -#ifndef QGRAPHICSSHADEREFFECT_P_H -#define QGRAPHICSSHADEREFFECT_P_H - // // W A R N I N G // ------------- @@ -51,40 +48,37 @@ // We mean it. // -#include <QtWidgets/qgraphicseffect.h> +#ifndef QOPENGLTEXTUREUPLOADER_P_H +#define QOPENGLTEXTUREUPLOADER_P_H +#include <QtCore/qsize.h> #include <QtOpenGL/qtopenglglobal.h> - -QT_REQUIRE_CONFIG(graphicseffect); +#include <QtGui/private/qopenglcontext_p.h> QT_BEGIN_NAMESPACE -class QGLShaderProgram; -class QGLCustomShaderEffectStage; -class QGraphicsShaderEffectPrivate; +class QImage; -class Q_OPENGL_EXPORT QGraphicsShaderEffect : public QGraphicsEffect +class Q_OPENGL_EXPORT QOpenGLTextureUploader { - Q_OBJECT public: - QGraphicsShaderEffect(QObject *parent = nullptr); - virtual ~QGraphicsShaderEffect(); - - QByteArray pixelShaderFragment() const; - void setPixelShaderFragment(const QByteArray& code); + enum BindOption { + NoBindOption = 0x0000, + PremultipliedAlphaBindOption = 0x0001, + UseRedForAlphaAndLuminanceBindOption = 0x0002, + SRgbBindOption = 0x0004, + PowerOfTwoBindOption = 0x0008 + }; + Q_DECLARE_FLAGS(BindOptions, BindOption) + Q_FLAGS(BindOptions) -protected: - void draw(QPainter *painter) override; - void setUniformsDirty(); - virtual void setUniforms(QGLShaderProgram *program); + static qsizetype textureImage(GLenum target, const QImage &image, BindOptions options, QSize maxSize = QSize()); -private: - Q_DECLARE_PRIVATE(QGraphicsShaderEffect) - Q_DISABLE_COPY_MOVE(QGraphicsShaderEffect) - - friend class QGLCustomShaderEffectStage; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLTextureUploader::BindOptions) + QT_END_NAMESPACE -#endif // QGRAPHICSSHADEREFFECT_P_H +#endif + diff --git a/src/opengl/qopengltimerquery.cpp b/src/opengl/qopengltimerquery.cpp new file mode 100644 index 0000000000..44955d48fc --- /dev/null +++ b/src/opengl/qopengltimerquery.cpp @@ -0,0 +1,880 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopengltimerquery.h" + +#include "qopenglqueryhelper_p.h" +#include <QtCore/private/qobject_p.h> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> + +QT_BEGIN_NAMESPACE + +// Helper class used as fallback if OpenGL <3.3 is being used with EXT_timer_query +class QExtTimerQueryHelper +{ +public: + QExtTimerQueryHelper(QOpenGLContext *context) + { + Q_ASSERT(context); + GetQueryObjectui64vEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLuint64EXT *)>(context->getProcAddress("glGetQueryObjectui64vEXT")); + GetQueryObjecti64vEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint64EXT *)>(context->getProcAddress("glGetQueryObjecti64vEXT")); + } + + inline void glGetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params) + { + GetQueryObjectui64vEXT(id, pname, params); + } + + inline void glGetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params) + { + GetQueryObjecti64vEXT(id, pname, params); + } + +private: + void (QOPENGLF_APIENTRYP GetQueryObjectui64vEXT)(GLuint id, GLenum pname, GLuint64EXT *params); + void (QOPENGLF_APIENTRYP GetQueryObjecti64vEXT)(GLuint id, GLenum pname, GLint64EXT *params); +}; + +class QOpenGLTimerQueryPrivate : public QObjectPrivate +{ +public: + QOpenGLTimerQueryPrivate() + : QObjectPrivate(), + context(nullptr), + ext(nullptr), + timeInterval(0), + timer(0) + { + } + + ~QOpenGLTimerQueryPrivate() + { + delete core; + delete ext; + } + + bool create(); + void destroy(); + void begin(); + void end(); + GLuint64 waitForTimeStamp() const; + void recordTimestamp(); + bool isResultAvailable() const; + GLuint64 result() const; + + // There are several cases we must handle: + // OpenGL >=3.3 includes timer queries as a core feature + // ARB_timer_query has same functionality as above. Requires OpenGL 3.2 + // EXT_timer_query offers limited support. Can be used with OpenGL >=1.5 + // + // Note that some implementations (OS X) provide OpenGL 3.2 but do not expose the + // ARB_timer_query extension. In such situations we must also be able to handle + // using the EXT_timer_query extension with any version of OpenGL. + // + // OpenGL 1.5 or above contains the generic query API and OpenGL 3.3 and + // ARB_timer_query provide the 64-bit query API. These are wrapped by + // QOpenGLQueryHelper. All we need to handle in addition is the EXT_timer_query + // case and to take care not to call the Core/ARB functions when we only + // have EXT_timer_query available. + QOpenGLContext *context; + QOpenGLQueryHelper *core; + QExtTimerQueryHelper *ext; + mutable GLuint64 timeInterval; + GLuint timer; +}; + +bool QOpenGLTimerQueryPrivate::create() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + if (timer && context == ctx) + return true; + + context = ctx; + if (!context) { + qWarning("A current OpenGL context is required to create timer query objects"); + return false; + } + + if (context->isOpenGLES()) { + qWarning("QOpenGLTimerQuery: Not supported on OpenGL ES"); + return false; + } + + // Resolve the functions provided by OpenGL 1.5 and OpenGL 3.3 or ARB_timer_query + core = new QOpenGLQueryHelper(context); + + // Check to see if we also need to resolve the functions for EXT_timer_query + QSurfaceFormat f = context->format(); + if (f.version() <= qMakePair<int, int>(3, 2) + && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query")) + && context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) { + ext = new QExtTimerQueryHelper(context); + } else if (f.version() <= qMakePair<int, int>(3, 2) + && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query")) + && !context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) { + qWarning("QOpenGLTimerQuery requires one of:\n" + " OpenGL 3.3 or newer,\n" + " OpenGL 3.2 and the ARB_timer_query extension\n" + " or the EXT_timer query extension"); + return false; + } + + core->glGenQueries(1, &timer); + return (timer != 0); +} + +void QOpenGLTimerQueryPrivate::destroy() +{ + if (!timer) + return; + + core->glDeleteQueries(1, &timer); + timer = 0; + context = nullptr; +} + +// GL_TIME_ELAPSED_EXT is not defined on OS X 10.6 +#if !defined(GL_TIME_ELAPSED_EXT) +#define GL_TIME_ELAPSED_EXT 0x88BF +#endif + +// GL_TIME_ELAPSED is not defined on OS X 10.7 or 10.8 yet +#if !defined(GL_TIME_ELAPSED) +#define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT +#endif + +void QOpenGLTimerQueryPrivate::begin() +{ + core->glBeginQuery(GL_TIME_ELAPSED, timer); +} + +void QOpenGLTimerQueryPrivate::end() +{ + core->glEndQuery(GL_TIME_ELAPSED); +} + +void QOpenGLTimerQueryPrivate::recordTimestamp() +{ + // Don't call glQueryCounter if we only have EXT_timer_query +#if defined(GL_TIMESTAMP) + if (!ext) + core->glQueryCounter(timer, GL_TIMESTAMP); + else + qWarning("QOpenGLTimerQuery::recordTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query"); +#else + qWarning("QOpenGLTimerQuery::recordTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query"); +#endif +} + +GLuint64 QOpenGLTimerQueryPrivate::waitForTimeStamp() const +{ + GLint64 tmp = 0; +#if defined(GL_TIMESTAMP) + if (!ext) + core->glGetInteger64v(GL_TIMESTAMP, &tmp); + else + qWarning("QOpenGLTimerQuery::waitForTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query"); +#else + qWarning("QOpenGLTimerQuery::waitForTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query"); +#endif + GLuint64 timestamp(tmp); + return timestamp; +} + +bool QOpenGLTimerQueryPrivate::isResultAvailable() const +{ + GLuint available = GL_FALSE; + core->glGetQueryObjectuiv(timer, GL_QUERY_RESULT_AVAILABLE, &available); + return available; +} + +GLuint64 QOpenGLTimerQueryPrivate::result() const +{ + if (!ext) + core->glGetQueryObjectui64v(timer, GL_QUERY_RESULT, &timeInterval); + else + ext->glGetQueryObjectui64vEXT(timer, GL_QUERY_RESULT, &timeInterval); + return timeInterval; +} + +/*! + \class QOpenGLTimerQuery + \brief The QOpenGLTimerQuery class wraps an OpenGL timer query object. + \inmodule QtOpenGL + \since 5.1 + \ingroup painting-3D + + OpenGL timer query objects are OpenGL managed resources to measure the + execution times of sequences of OpenGL commands on the GPU. + + OpenGL offers various levels of support for timer queries, depending on + the version of OpenGL you have and the presence of the ARB_timer_query or + EXT_timer_query extensions. The support can be summarized as: + + \list + \li OpenGL >=3.3 offers full support for all timer query functionality. + \li OpenGL 3.2 with the ARB_timer_query extension offers full support + for all timer query functionality. + \li OpenGL <=3.2 with the EXT_timer_query extension offers limited support + in that the timestamp of the GPU cannot be queried. Places where this + impacts functions provided by Qt classes will be highlighted in the + function documentation. + \li OpenGL ES 2 (and OpenGL ES 3) do not provide any support for OpenGL + timer queries. + \endlist + + OpenGL represents time with a granularity of 1 nanosecond (1e-9 seconds). As a + consequence of this, 32-bit integers would only give a total possible duration + of approximately 4 seconds, which would not be difficult to exceed in poorly + performing or lengthy operations. OpenGL therefore uses 64 bit integer types + to represent times. A GLuint64 variable has enough width to contain a duration + of hundreds of years, which is plenty for real-time rendering needs. + + As with the other Qt OpenGL classes, QOpenGLTimerQuery has a create() + function to create the underlying OpenGL object. This is to allow the developer to + ensure that there is a valid current OpenGL context at the time. + + Once created, timer queries can be issued in one of several ways. The simplest + method is to delimit a block of commands with calls to begin() and end(). This + instructs OpenGL to measure the time taken from completing all commands issued + prior to begin() until the completion of all commands issued prior to end(). + + At the end of a frame we can retrieve the results by calling waitForResult(). + As this function's name implies, it blocks CPU execution until OpenGL notifies + that the timer query result is available. To avoid blocking, you can check + if the query result is available by calling isResultAvailable(). Note that + modern GPUs are deeply pipelined and query results may not become available for + between 1-5 frames after they were issued. + + Note that OpenGL does not permit nesting or interleaving of multiple timer queries + using begin() and end(). Using multiple timer queries and recordTimestamp() avoids + this limitation. When using recordTimestamp() the result can be obtained at + some later time using isResultAvailable() and waitForResult(). Qt provides the + convenience class QOpenGLTimeMonitor that helps with using multiple query objects. + + \sa QOpenGLTimeMonitor +*/ + +/*! + Creates a QOpenGLTimerQuery instance with the given \a parent. You must call create() + with a valid OpenGL context before using. +*/ +QOpenGLTimerQuery::QOpenGLTimerQuery(QObject *parent) + : QObject(*new QOpenGLTimerQueryPrivate, parent) +{ +} + +/*! + Destroys the QOpenGLTimerQuery and the underlying OpenGL resource. +*/ +QOpenGLTimerQuery::~QOpenGLTimerQuery() +{ + QOpenGLContext* ctx = QOpenGLContext::currentContext(); + + Q_D(QOpenGLTimerQuery); + QOpenGLContext *oldContext = nullptr; + if (d->context != ctx) { + oldContext = ctx; + if (d->context->makeCurrent(oldContext->surface())) { + ctx = d->context; + } else { + qWarning("QOpenGLTimerQuery::~QOpenGLTimerQuery() failed to make query objects's context current"); + ctx = nullptr; + } + } + + if (ctx) + destroy(); + + if (oldContext) { + if (!oldContext->makeCurrent(oldContext->surface())) + qWarning("QOpenGLTimerQuery::~QOpenGLTimerQuery() failed to restore current context"); + } +} + +/*! + Creates the underlying OpenGL timer query object. There must be a valid OpenGL context + that supports query objects current for this function to succeed. + + Returns \c true if the OpenGL timer query object was successfully created. +*/ +bool QOpenGLTimerQuery::create() +{ + Q_D(QOpenGLTimerQuery); + return d->create(); +} + +/*! + Destroys the underlying OpenGL timer query object. The context that was current when + create() was called must be current when calling this function. +*/ +void QOpenGLTimerQuery::destroy() +{ + Q_D(QOpenGLTimerQuery); + d->destroy(); +} + +/*! + Returns \c true if the underlying OpenGL query object has been created. If this + returns \c true and the associated OpenGL context is current, then you are able to issue + queries with this object. +*/ +bool QOpenGLTimerQuery::isCreated() const +{ + Q_D(const QOpenGLTimerQuery); + return (d->timer != 0); +} + +/*! + Returns the id of the underlying OpenGL query object. +*/ +GLuint QOpenGLTimerQuery::objectId() const +{ + Q_D(const QOpenGLTimerQuery); + return d->timer; +} + +/*! + Marks the start point in the OpenGL command queue for a sequence of commands to + be timed by this query object. + + This is useful for simple use-cases. Usually it is better to use recordTimestamp(). + + \sa end(), isResultAvailable(), waitForResult(), recordTimestamp() +*/ +void QOpenGLTimerQuery::begin() +{ + Q_D(QOpenGLTimerQuery); + d->begin(); +} + +/*! + Marks the end point in the OpenGL command queue for a sequence of commands to + be timed by this query object. + + This is useful for simple use-cases. Usually it is better to use recordTimestamp(). + + \sa begin(), isResultAvailable(), waitForResult(), recordTimestamp() +*/ +void QOpenGLTimerQuery::end() +{ + Q_D(QOpenGLTimerQuery); + d->end(); +} + +/*! + Places a marker in the OpenGL command queue for the GPU to record the timestamp + when this marker is reached by the GPU. This function is non-blocking and the + result will become available at some later time. + + The availability of the result can be checked with isResultAvailable(). The result + can be fetched with waitForResult() which will block if the result is not yet + available. + + \sa waitForResult(), isResultAvailable(), begin(), end() +*/ +void QOpenGLTimerQuery::recordTimestamp() +{ + Q_D(QOpenGLTimerQuery); + return d->recordTimestamp(); +} + +/*! + Returns the current timestamp of the GPU when all previously issued OpenGL + commands have been received but not necessarily executed by the GPU. + + This function blocks until the result is returned. + + \sa recordTimestamp() +*/ +GLuint64 QOpenGLTimerQuery::waitForTimestamp() const +{ + Q_D(const QOpenGLTimerQuery); + return d->waitForTimeStamp(); +} + +/*! + Returns \c true if the OpenGL timer query result is available. + + This function is non-blocking and ideally should be used to check for the + availability of the query result before calling waitForResult(). + + \sa waitForResult() +*/ +bool QOpenGLTimerQuery::isResultAvailable() const +{ + Q_D(const QOpenGLTimerQuery); + return d->isResultAvailable(); +} + +/*! + Returns the result of the OpenGL timer query. + + This function will block until the result is made available by OpenGL. It is + recommended to call isResultAvailable() to ensure that the result is available + to avoid unnecessary blocking and stalling. + + \sa isResultAvailable() +*/ +GLuint64 QOpenGLTimerQuery::waitForResult() const +{ + Q_D(const QOpenGLTimerQuery); + return d->result(); +} + + +class QOpenGLTimeMonitorPrivate : public QObjectPrivate +{ +public: + QOpenGLTimeMonitorPrivate() + : QObjectPrivate(), + timers(), + timeSamples(), + context(nullptr), + core(nullptr), + ext(nullptr), + requestedSampleCount(2), + currentSample(-1), + timerQueryActive(false) + { + } + + ~QOpenGLTimeMonitorPrivate() + { + delete core; + delete ext; + } + + bool create(); + void destroy(); + void recordSample(); + bool isResultAvailable() const; + QVector<GLuint64> samples() const; + QVector<GLuint64> intervals() const; + void reset(); + + QVector<GLuint> timers; + mutable QVector<GLuint64> timeSamples; + + QOpenGLContext *context; + QOpenGLQueryHelper *core; + QExtTimerQueryHelper *ext; + + int requestedSampleCount; + int currentSample; + mutable bool timerQueryActive; +}; + +bool QOpenGLTimeMonitorPrivate::create() +{ + if (!timers.isEmpty() && timers.at(0) != 0 && timers.size() == requestedSampleCount) + return true; + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (context && context != ctx) { + qWarning("QTimeMonitor: Attempting to use different OpenGL context to recreate timers.\n" + "Please call destroy() first or use the same context to previously create"); + return false; + } + + context = ctx; + if (!context) { + qWarning("A current OpenGL context is required to create timer query objects"); + return false; + } + + // Resize the vectors that hold the timers and the recorded samples + timers.resize(requestedSampleCount); + timeSamples.resize(requestedSampleCount); + + // Resolve the functions provided by OpenGL 1.5 and OpenGL 3.3 or ARB_timer_query + core = new QOpenGLQueryHelper(context); + + // Check to see if we also need to resolve the functions for EXT_timer_query + QSurfaceFormat f = context->format(); + if (f.version() <= qMakePair<int, int>(3, 2) + && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query")) + && context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) { + ext = new QExtTimerQueryHelper(context); + } else if (f.version() <= qMakePair<int, int>(3, 2) + && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query")) + && !context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) { + qWarning("QOpenGLTimeMonitor requires one of:\n" + " OpenGL 3.3 or newer,\n" + " OpenGL 3.2 and the ARB_timer_query extension\n" + " or the EXT_timer query extension"); + return false; + } + + core->glGenQueries(requestedSampleCount, timers.data()); + return (timers.at(0) != 0); +} + +void QOpenGLTimeMonitorPrivate::destroy() +{ + if (timers.isEmpty() || timers.at(0) == 0) + return; + + core->glDeleteQueries(timers.size(), timers.data()); + timers.clear(); + delete core; + core = nullptr; + delete ext; + ext = nullptr; + context = nullptr; +} + +void QOpenGLTimeMonitorPrivate::recordSample() +{ + // Use glQueryCounter() and GL_TIMESTAMP where available. + // Otherwise, simulate it with glBeginQuery()/glEndQuery() + if (!ext) { +#if defined(GL_TIMESTAMP) + core->glQueryCounter(timers.at(++currentSample), GL_TIMESTAMP); +#endif + } else { + if (currentSample == -1) { + core->glBeginQuery(GL_TIME_ELAPSED_EXT, timers.at(++currentSample)); + timerQueryActive = true; + } else if (currentSample < timers.size() - 1) { + core->glEndQuery(GL_TIME_ELAPSED_EXT); + core->glBeginQuery(GL_TIME_ELAPSED_EXT, timers.at(++currentSample)); + } else { + if (timerQueryActive) { + core->glEndQuery(GL_TIME_ELAPSED_EXT); + timerQueryActive = false; + } + } + } +} + +bool QOpenGLTimeMonitorPrivate::isResultAvailable() const +{ + // The OpenGL spec says that if a query result is ready then the results of all queries + // of the same type issued before it must also be ready. Therefore we only need to check + // the availability of the result for the last issued query + GLuint available = GL_FALSE; + core->glGetQueryObjectuiv(timers.at(currentSample), GL_QUERY_RESULT_AVAILABLE, &available); + return available; +} + +QVector<GLuint64> QOpenGLTimeMonitorPrivate::samples() const +{ + // For the Core and ARB options just ask for the timestamp for each timer query. + // For the EXT implementation we cannot obtain timestamps so we defer any result + // collection to the intervals() function + if (!ext) { + for (int i = 0; i <= currentSample; ++i) + core->glGetQueryObjectui64v(timers.at(i), GL_QUERY_RESULT, &timeSamples[i]); + } else { + qWarning("QOpenGLTimeMonitor::samples() requires OpenGL >=3.3\n" + "or OpenGL 3.2 and GL_ARB_timer_query"); + } + return timeSamples; +} + +QVector<GLuint64> QOpenGLTimeMonitorPrivate::intervals() const +{ + QVector<GLuint64> intervals(timers.size() - 1); + if (!ext) { + // Obtain the timestamp samples and calculate the interval durations + const QVector<GLuint64> timeStamps = samples(); + for (int i = 0; i < intervals.size(); ++i) + intervals[i] = timeStamps[i+1] - timeStamps[i]; + } else { + // Stop the last timer if needed + if (timerQueryActive) { + core->glEndQuery(GL_TIME_ELAPSED_EXT); + timerQueryActive = false; + } + + // Obtain the results from all timers apart from the redundant last one. In this + // case the results actually are the intervals not timestamps + for (int i = 0; i < currentSample; ++i) + ext->glGetQueryObjectui64vEXT(timers.at(i), GL_QUERY_RESULT, &intervals[i]); + } + + return intervals; +} + +void QOpenGLTimeMonitorPrivate::reset() +{ + currentSample = -1; + timeSamples.fill(0); +} + + +/*! + \class QOpenGLTimeMonitor + \brief The QOpenGLTimeMonitor class wraps a sequence of OpenGL timer query objects. + \inmodule QtOpenGL + \since 5.1 + \ingroup painting-3D + + The QOpenGLTimeMonitor class is a convenience wrapper around a collection of OpenGL + timer query objects used to measure intervals of time on the GPU to the level of + granularity required by your rendering application. + + The OpenGL timer queries objects are queried in sequence to record the GPU + timestamps at positions of interest in your rendering code. Once the results for + all issues timer queries become available, the results can be fetched and + QOpenGLTimerMonitor will calculate the recorded time intervals for you. + + The typical use case of this class is to either profile your application's rendering + algorithms or to adjust those algorithms in real-time for dynamic performance/quality + balancing. + + Prior to using QOpenGLTimeMonitor in your rendering function you should set the + required number of sample points that you wish to record by calling setSamples(). Note + that measuring N sample points will produce N-1 time intervals. Once you have set the + number of sample points, call the create() function with a valid current OpenGL context + to create the necessary query timer objects. These steps are usually performed just + once in an initialization function. + + Use the recordSample() function to delimit blocks of code containing OpenGL commands + that you wish to time. You can check availability of the resulting time + samples and time intervals with isResultAvailable(). The calculated time intervals and + the raw timestamp samples can be retrieved with the blocking waitForIntervals() and + waitForSamples() functions respectively. + + After retrieving the results and before starting a new round of taking samples + (for example, in the next frame) be sure to call the reset() function which will clear + the cached results and reset the timer index back to the first timer object. + + \sa QOpenGLTimerQuery +*/ + +/*! + Creates a QOpenGLTimeMonitor instance with the given \a parent. You must call create() + with a valid OpenGL context before using. + + \sa setSampleCount(), create() +*/ +QOpenGLTimeMonitor::QOpenGLTimeMonitor(QObject *parent) + : QObject(*new QOpenGLTimeMonitorPrivate, parent) +{ +} + +/*! + Destroys the QOpenGLTimeMonitor and any underlying OpenGL resources. +*/ +QOpenGLTimeMonitor::~QOpenGLTimeMonitor() +{ + QOpenGLContext* ctx = QOpenGLContext::currentContext(); + + Q_D(QOpenGLTimeMonitor); + QOpenGLContext *oldContext = nullptr; + if (d->context != ctx) { + oldContext = ctx; + if (d->context->makeCurrent(oldContext->surface())) { + ctx = d->context; + } else { + qWarning("QOpenGLTimeMonitor::~QOpenGLTimeMonitor() failed to make time monitor's context current"); + ctx = nullptr; + } + } + + if (ctx) + destroy(); + + if (oldContext) { + if (!oldContext->makeCurrent(oldContext->surface())) + qWarning("QOpenGLTimeMonitor::~QOpenGLTimeMonitor() failed to restore current context"); + } +} + +/*! + Sets the number of sample points to \a sampleCount. After setting the number + of samples with this function, you must call create() to instantiate the underlying + OpenGL timer query objects. + + The new \a sampleCount must be at least 2. + + \sa sampleCount(), create(), recordSample() +*/ +void QOpenGLTimeMonitor::setSampleCount(int sampleCount) +{ + // We need at least 2 samples to get an interval + if (sampleCount < 2) + return; + Q_D(QOpenGLTimeMonitor); + d->requestedSampleCount = sampleCount; +} + +/*! + Returns the number of sample points that have been requested with + setSampleCount(). If create was successfully called following setSampleCount(), + then the value returned will be the actual number of sample points + that can be used. + + The default value for sample count is 2, leading to the measurement of a + single interval. + + \sa setSampleCount() +*/ +int QOpenGLTimeMonitor::sampleCount() const +{ + Q_D(const QOpenGLTimeMonitor); + return d->requestedSampleCount; +} + +/*! + Instantiate sampleCount() OpenGL timer query objects that will be used + to track the amount of time taken to execute OpenGL commands between + successive calls to recordSample(). + + Returns \c true if the OpenGL timer query objects could be created. + + \sa destroy(), setSampleCount(), recordSample() +*/ +bool QOpenGLTimeMonitor::create() +{ + Q_D(QOpenGLTimeMonitor); + return d->create(); +} + +/*! + Destroys any OpenGL timer query objects used within this instance. + + \sa create() +*/ +void QOpenGLTimeMonitor::destroy() +{ + Q_D(QOpenGLTimeMonitor); + d->destroy(); +} + +/*! + Returns \c true if the underlying OpenGL query objects have been created. If this + returns \c true and the associated OpenGL context is current, then you are able to record + time samples with this object. +*/ +bool QOpenGLTimeMonitor::isCreated() const +{ + Q_D(const QOpenGLTimeMonitor); + return (!d->timers.isEmpty() && d->timers.at(0) != 0); +} + +/*! + Returns a QVector containing the object Ids of the OpenGL timer query objects. +*/ +QVector<GLuint> QOpenGLTimeMonitor::objectIds() const +{ + Q_D(const QOpenGLTimeMonitor); + return d->timers; +} + +/*! + Issues an OpenGL timer query at this point in the OpenGL command queue. Calling this + function in a sequence in your application's rendering function, will build up + details of the GPU time taken to execute the OpenGL commands between successive + calls to this function. + + \sa setSampleCount(), isResultAvailable(), waitForSamples(), waitForIntervals() +*/ +int QOpenGLTimeMonitor::recordSample() +{ + Q_D(QOpenGLTimeMonitor); + d->recordSample(); + return d->currentSample; +} + +/*! + Returns \c true if the OpenGL timer query results are available. + + \sa waitForSamples(), waitForIntervals() +*/ +bool QOpenGLTimeMonitor::isResultAvailable() const +{ + Q_D(const QOpenGLTimeMonitor); + return d->isResultAvailable(); +} + +/*! + Returns a QVector containing the GPU timestamps taken with recordSample(). + + This function will block until OpenGL indicates the results are available. It + is recommended to check the availability of the result prior to calling this + function with isResultAvailable(). + + \note This function only works on systems that have OpenGL >=3.3 or the + ARB_timer_query extension. See QOpenGLTimerQuery for more details. + + \sa waitForIntervals(), isResultAvailable() +*/ +QVector<GLuint64> QOpenGLTimeMonitor::waitForSamples() const +{ + Q_D(const QOpenGLTimeMonitor); + return d->samples(); +} + +/*! + Returns a QVector containing the time intervals delimited by the calls to + recordSample(). The resulting vector will contain one fewer element as + this represents the intervening intervals rather than the actual timestamp + samples. + + This function will block until OpenGL indicates the results are available. It + is recommended to check the availability of the result prior to calling this + function with isResultAvailable(). + + \sa waitForSamples(), isResultAvailable() +*/ +QVector<GLuint64> QOpenGLTimeMonitor::waitForIntervals() const +{ + Q_D(const QOpenGLTimeMonitor); + return d->intervals(); +} + +/*! + Resets the time monitor ready for use in another frame of rendering. Call + this once you have obtained the previous results and before calling + recordSample() for the first time on the next frame. + + \sa recordSample() +*/ +void QOpenGLTimeMonitor::reset() +{ + Q_D(QOpenGLTimeMonitor); + d->reset(); +} + +QT_END_NAMESPACE diff --git a/src/opengl/qopengltimerquery.h b/src/opengl/qopengltimerquery.h new file mode 100644 index 0000000000..a779240108 --- /dev/null +++ b/src/opengl/qopengltimerquery.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLTIMERQUERY_H +#define QOPENGLTIMERQUERY_H + +#include <QtOpenGL/qtopenglglobal.h> + +#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) + +#include <QtCore/QObject> +#include <QtGui/qopengl.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLTimerQueryPrivate; + +class Q_OPENGL_EXPORT QOpenGLTimerQuery : public QObject +{ + Q_OBJECT + +public: + explicit QOpenGLTimerQuery(QObject *parent = nullptr); + ~QOpenGLTimerQuery(); + + bool create(); + void destroy(); + bool isCreated() const; + GLuint objectId() const; + + void begin(); + void end(); + GLuint64 waitForTimestamp() const; + void recordTimestamp(); + bool isResultAvailable() const; + GLuint64 waitForResult() const; + +private: + Q_DECLARE_PRIVATE(QOpenGLTimerQuery) + Q_DISABLE_COPY(QOpenGLTimerQuery) +}; + + +class QOpenGLTimeMonitorPrivate; + +class Q_OPENGL_EXPORT QOpenGLTimeMonitor : public QObject +{ + Q_OBJECT + +public: + explicit QOpenGLTimeMonitor(QObject *parent = nullptr); + ~QOpenGLTimeMonitor(); + + void setSampleCount(int sampleCount); + int sampleCount() const; + + bool create(); + void destroy(); + bool isCreated() const; + QVector<GLuint> objectIds() const; + + int recordSample(); + + bool isResultAvailable() const; + + QVector<GLuint64> waitForSamples() const; + QVector<GLuint64> waitForIntervals() const; + + void reset(); + +private: + Q_DECLARE_PRIVATE(QOpenGLTimeMonitor) + Q_DISABLE_COPY(QOpenGLTimeMonitor) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QOPENGLTIMERQUERY_H diff --git a/src/opengl/qopenglwidget.cpp b/src/opengl/qopenglwidget.cpp new file mode 100644 index 0000000000..b334ba867b --- /dev/null +++ b/src/opengl/qopenglwidget.cpp @@ -0,0 +1,1473 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglwidget.h" +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFramebufferObject> +#include <QtGui/QOffscreenSurface> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QWindow> +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> +#include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformintegration.h> +#include <QtOpenGL/QOpenGLPaintDevice> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qopenglextensions_p.h> +#include <QtGui/private/qfont_p.h> +#include <QtGui/private/qopenglcontext_p.h> +#include <QtOpenGL/private/qopenglpaintdevice_p.h> + +#include <QtWidgets/private/qwidget_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLWidget + \inmodule QtOpenGL + \since 5.4 + + \brief The QOpenGLWidget class is a widget for rendering OpenGL graphics. + + QOpenGLWidget provides functionality for displaying OpenGL graphics + integrated into a Qt application. It is very simple to use: Make + your class inherit from it and use the subclass like any other + QWidget, except that you have the choice between using QPainter and + standard OpenGL rendering commands. + + QOpenGLWidget provides three convenient virtual functions that you + can reimplement in your subclass to perform the typical OpenGL + tasks: + + \list + \li paintGL() - Renders the OpenGL scene. Gets called whenever the widget + needs to be updated. + \li resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets + called whenever the widget has been resized (and also when it + is shown for the first time because all newly created widgets get a + resize event automatically). + \li initializeGL() - Sets up the OpenGL resources and state. Gets called + once before the first time resizeGL() or paintGL() is called. + \endlist + + If you need to trigger a repaint from places other than paintGL() (a + typical example is when using \l{QTimer}{timers} to animate scenes), + you should call the widget's update() function to schedule an update. + + Your widget's OpenGL rendering context is made current when + paintGL(), resizeGL(), or initializeGL() is called. If you need to + call the standard OpenGL API functions from other places (e.g. in + your widget's constructor or in your own paint functions), you + must call makeCurrent() first. + + All rendering happens into an OpenGL framebuffer + object. makeCurrent() ensure that it is bound in the context. Keep + this in mind when creating and binding additional framebuffer + objects in the rendering code in paintGL(). Never re-bind the + framebuffer with ID 0. Instead, call defaultFramebufferObject() to + get the ID that should be bound. + + QOpenGLWidget allows using different OpenGL versions and profiles + when the platform supports it. Just set the requested format via + setFormat(). Keep in mind however that having multiple QOpenGLWidget + instances in the same window requires that they all use the same + format, or at least formats that do not make the contexts + non-sharable. To overcome this issue, prefer using + QSurfaceFormat::setDefaultFormat() instead of setFormat(). + + \note Calling QSurfaceFormat::setDefaultFormat() before constructing + the QApplication instance is mandatory on some platforms (for example, + \macos) when an OpenGL core profile context is requested. This is to + ensure that resource sharing between contexts stays functional as all + internal contexts are created using the correct version and profile. + + \section1 Painting Techniques + + As described above, subclass QOpenGLWidget to render pure 3D content in the + following way: + + \list + + \li Reimplement the initializeGL() and resizeGL() functions to + set up the OpenGL state and provide a perspective transformation. + + \li Reimplement paintGL() to paint the 3D scene, calling only + OpenGL functions. + + \endlist + + It is also possible to draw 2D graphics onto a QOpenGLWidget subclass using QPainter: + + \list + + \li In paintGL(), instead of issuing OpenGL commands, construct a QPainter + object for use on the widget. + + \li Draw primitives using QPainter's member functions. + + \li Direct OpenGL commands can still be issued. However, you must make sure + these are enclosed by a call to the painter's beginNativePainting() and + endNativePainting(). + + \endlist + + When performing drawing using QPainter only, it is also possible to perform + the painting like it is done for ordinary widgets: by reimplementing paintEvent(). + + \list + + \li Reimplement the paintEvent() function. + + \li Construct a QPainter object targeting the widget. Either pass the widget to the + constructor or the QPainter::begin() function. + + \li Draw primitives using QPainter's member functions. + + \li Painting finishes then the QPainter instance is destroyed. Alternatively, + call QPainter::end() explicitly. + + \endlist + + \section1 OpenGL Function Calls, Headers and QOpenGLFunctions + + When making OpenGL function calls, it is strongly recommended to avoid calling + the functions directly. Instead, prefer using QOpenGLFunctions (when making + portable applications) or the versioned variants (for example, + QOpenGLFunctions_3_2_Core and similar, when targeting modern, desktop-only + OpenGL). This way the application will work correctly in all Qt build + configurations, including the ones that perform dynamic OpenGL implementation + loading which means applications are not directly linking to an GL + implementation and thus direct function calls are not feasible. + + In paintGL() the current context is always accessible by caling + QOpenGLContext::currentContext(). From this context an already initialized, + ready-to-be-used QOpenGLFunctions instance is retrievable by calling + QOpenGLContext::functions(). An alternative to prefixing every GL call is to + inherit from QOpenGLFunctions and call + QOpenGLFunctions::initializeOpenGLFunctions() in initializeGL(). + + As for the OpenGL headers, note that in most cases there will be no need to + directly include any headers like GL.h. The OpenGL-related Qt headers will + include qopengl.h which will in turn include an appropriate header for the + system. This might be an OpenGL ES 3.x or 2.0 header, the highest version that + is available, or a system-provided gl.h. In addition, a copy of the extension + headers (called glext.h on some systems) is provided as part of Qt both for + OpenGL and OpenGL ES. These will get included automatically on platforms where + feasible. This means that constants and function pointer typedefs from ARB, + EXT, OES extensions are automatically available. + + \section1 Code Examples + + To get started, the simplest QOpenGLWidget subclass could like like the following: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 0 + + Alternatively, the prefixing of each and every OpenGL call can be avoided by deriving + from QOpenGLFunctions instead: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 1 + + To get a context compatible with a given OpenGL version or profile, or to + request depth and stencil buffers, call setFormat(): + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 2 + + With OpenGL 3.0+ contexts, when portability is not important, the versioned + QOpenGLFunctions variants give easy access to all the modern OpenGL functions + available in a given version: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 3 + + As described above, it is simpler and more robust to set the requested format + globally so that it applies to all windows and contexts during the lifetime of + the application. Below is an example of this: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 6 + + \section1 Relation to QGLWidget + + The legacy QtOpenGL module (classes prefixed with QGL) provides a widget + called QGLWidget. QOpenGLWidget is intended to be a modern replacement for + it. Therefore, especially in new applications, the general recommendation is + to use QOpenGLWidget. + + While the API is very similar, there is an important difference between the + two: QOpenGLWidget always renders offscreen, using framebuffer + objects. QGLWidget on the other hand uses a native window and surface. The + latter causes issues when using it in complex user interfaces since, depending + on the platform, such native child widgets may have various limitations, + regarding stacking orders for example. QOpenGLWidget avoids this by not + creating a separate native window. + + Due to being backed by a framebuffer object, the behavior of QOpenGLWidget is + very similar to QOpenGLWindow with the update behavior set to \c + PartialUpdateBlit or \c PartialUpdateBlend. This means that the contents are + preserved between paintGL() calls so that incremental rendering is + possible. With QGLWidget (and naturally QOpenGLWindow with the default update + behavior) this is usually not the case because swapping the buffers leaves the + back buffer with undefined contents. + + \note Most applications do not need incremental rendering because they will + render everything in the view on every paint call. In this case it is + important to call glClear() as early as possible in paintGL(). This helps + mobile GPUs that use a tile-based architecture to recognize that the tile + buffer does not need to be reloaded with the framebuffer's previous + contents. Omitting the clear call can lead to significant performance drops on + such systems. + + \note Avoid calling winId() on a QOpenGLWidget. This function triggers the creation of + a native window, resulting in reduced performance and possibly rendering glitches. + + \section1 Differences to QGLWidget + + Besides the main conceptual difference of being backed by a framebuffer object, there + are a number of smaller, internal differences between QOpenGLWidget and the older + QGLWidget: + + \list + + \li OpenGL state when invoking paintGL(). QOpenGLWidget sets up the viewport via + glViewport(). It does not perform any clearing. + + \li Clearing when starting to paint via QPainter. Unlike regular widgets, QGLWidget + defaulted to a value of \c true for + \l{QWidget::autoFillBackground()}{autoFillBackground}. It then performed clearing to the + palette's background color every time QPainter::begin() was used. QOpenGLWidget does not + follow this: \l{QWidget::autoFillBackground()}{autoFillBackground} defaults to false, + like for any other widget. The only exception is when being used as a viewport for other + widgets like QGraphicsView. In such a case autoFillBackground will be automatically set + to true to ensure compatibility with QGLWidget-based viewports. + + \endlist + + \section1 Multisampling + + To enable multisampling, set the number of requested samples on the + QSurfaceFormat that is passed to setFormat(). On systems that do not support + it the request may get ignored. + + Multisampling support requires support for multisampled renderbuffers and + framebuffer blits. On OpenGL ES 2.0 implementations it is likely that these + will not be present. This means that multisampling will not be available. With + modern OpenGL versions and OpenGL ES 3.0 and up this is usually not a problem + anymore. + + \section1 Threading + + Performing offscreen rendering on worker threads, for example to generate + textures that are then used in the GUI/main thread in paintGL(), are supported + by exposing the widget's QOpenGLContext so that additional contexts sharing + with it can be created on each thread. + + Drawing directly to the QOpenGLWidget's framebuffer outside the GUI/main + thread is possible by reimplementing paintEvent() to do nothing. The context's + thread affinity has to be changed via QObject::moveToThread(). After that, + makeCurrent() and doneCurrent() are usable on the worker thread. Be careful to + move the context back to the GUI/main thread afterwards. + + Unlike QGLWidget, triggering a buffer swap just for the QOpenGLWidget is not + possible since there is no real, onscreen native surface for it. Instead, it + is up to the widget stack to manage composition and buffer swaps on the gui + thread. When a thread is done updating the framebuffer, call update() \b{on + the GUI/main thread} to schedule composition. + + Extra care has to be taken to avoid using the framebuffer when the GUI/main + thread is performing compositing. The signals aboutToCompose() and + frameSwapped() will be emitted when the composition is starting and + ending. They are emitted on the GUI/main thread. This means that by using a + direct connection aboutToCompose() can block the GUI/main thread until the + worker thread has finished its rendering. After that, the worker thread must + perform no further rendering until the frameSwapped() signal is emitted. If + this is not acceptable, the worker thread has to implement a double buffering + mechanism. This involves drawing using an alternative render target, that is + fully controlled by the thread, e.g. an additional framebuffer object, and + blitting to the QOpenGLWidget's framebuffer at a suitable time. + + \section1 Context Sharing + + When multiple QOpenGLWidgets are added as children to the same top-level + widget, their contexts will share with each other. This does not apply for + QOpenGLWidget instances that belong to different windows. + + This means that all QOpenGLWidgets in the same window can access each other's + sharable resources, like textures, and there is no need for an extra "global + share" context, as was the case with QGLWidget. + + To set up sharing between QOpenGLWidget instances belonging to different + windows, set the Qt::AA_ShareOpenGLContexts application attribute before + instantiating QApplication. This will trigger sharing between all + QOpenGLWidget instances without any further steps. + + Creating extra QOpenGLContext instances that share resources like textures + with the QOpenGLWidget's context is also possible. Simply pass the pointer + returned from context() to QOpenGLContext::setShareContext() before calling + QOpenGLContext::create(). The resulting context can also be used on a + different thread, allowing threaded generation of textures and asynchronous + texture uploads. + + Note that QOpenGLWidget expects a standard conformant implementation of + resource sharing when it comes to the underlying graphics drivers. For + example, some drivers, in particular for mobile and embedded hardware, have + issues with setting up sharing between an existing context and others that are + created later. Some other drivers may behave in unexpected ways when trying to + utilize shared resources between different threads. + + \section1 Resource Initialization and Cleanup + + The QOpenGLWidget's associated OpenGL context is guaranteed to be current + whenever initializeGL() and paintGL() are invoked. Do not attempt to create + OpenGL resources before initializeGL() is called. For example, attempting to + compile shaders, initialize vertex buffer objects or upload texture data will + fail when done in a subclass's constructor. These operations must be deferred + to initializeGL(). Some of Qt's OpenGL helper classes, like QOpenGLBuffer or + QOpenGLVertexArrayObject, have a matching deferred behavior: they can be + instantiated without a context, but all initialization is deferred until a + create(), or similar, call. This means that they can be used as normal + (non-pointer) member variables in a QOpenGLWidget subclass, but the create() + or similar function can only be called from initializeGL(). Be aware however + that not all classes are designed like this. When in doubt, make the member + variable a pointer and create and destroy the instance dynamically in + initializeGL() and the destructor, respectively. + + Releasing the resources also needs the context to be current. Therefore + destructors that perform such cleanup are expected to call makeCurrent() + before moving on to destroy any OpenGL resources or wrappers. Avoid deferred + deletion via \l{QObject::deleteLater()}{deleteLater()} or the parenting + mechanism of QObject. There is no guarantee the correct context will be + current at the time the instance in question is really destroyed. + + A typical subclass will therefore often look like the following when it comes + to resource initialization and destruction: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 4 + + This is naturally not the only possible solution. One alternative is to use + the \l{QOpenGLContext::aboutToBeDestroyed()}{aboutToBeDestroyed()} signal of + QOpenGLContext. By connecting a slot, using direct connection, to this signal, + it is possible to perform cleanup whenever the underlying native context + handle, or the entire QOpenGLContext instance, is going to be released. The + following snippet is in principle equivalent to the previous one: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 5 + + \note For widgets that change their associated top-level window multiple times + during their lifetime, a combined approach is essential. Whenever the widget + or a parent of it gets reparented so that the top-level window becomes + different, the widget's associated context is destroyed and a new one is + created. This is then followed by a call to initializeGL() where all OpenGL + resources must get reinitialized. Due to this the only option to perform + proper cleanup is to connect to the context's aboutToBeDestroyed() + signal. Note that the context in question may not be the current one when the + signal gets emitted. Therefore it is good practice to call makeCurrent() in + the connected slot. Additionally, the same cleanup steps must be performed + from the derived class' destructor, since the slot connected to the signal + will not get invoked when the widget is being destroyed. + + \note When Qt::AA_ShareOpenGLContexts is set, the widget's context never + changes, not even when reparenting because the widget's associated texture is + guaranteed to be accessible also from the new top-level's context. + + Proper cleanup is especially important due to context sharing. Even though + each QOpenGLWidget's associated context is destroyed together with the + QOpenGLWidget, the sharable resources in that context, like textures, will + stay valid until the top-level window, in which the QOpenGLWidget lived, is + destroyed. Additionally, settings like Qt::AA_ShareOpenGLContexts and some Qt + modules may trigger an even wider scope for sharing contexts, potentially + leading to keeping the resources in question alive for the entire lifetime of + the application. Therefore the safest and most robust is always to perform + explicit cleanup for all resources and resource wrappers used in the + QOpenGLWidget. + + \section1 Limitations + + Putting other widgets underneath and making the QOpenGLWidget transparent will + not lead to the expected results: The widgets underneath will not be + visible. This is because in practice the QOpenGLWidget is drawn before all + other regular, non-OpenGL widgets, and so see-through type of solutions are + not feasible. Other type of layouts, like having widgets on top of the + QOpenGLWidget, will function as expected. + + When absolutely necessary, this limitation can be overcome by setting the + Qt::WA_AlwaysStackOnTop attribute on the QOpenGLWidget. Be aware however that + this breaks stacking order, for example it will not be possible to have other + widgets on top of the QOpenGLWidget, so it should only be used in situations + where a semi-transparent QOpenGLWidget with other widgets visible underneath + is required. + + Note that this does not apply when there are no other widgets underneath and + the intention is to have a semi-transparent window. In that case the + traditional approach of setting Qt::WA_TranslucentBackground + on the top-level window is sufficient. Note that if the transparent areas are + only desired in the QOpenGLWidget, then Qt::WA_NoSystemBackground will need + to be turned back to \c false after enabling Qt::WA_TranslucentBackground. + Additionally, requesting an alpha channel for the QOpenGLWidget's context via + setFormat() may be necessary too, depending on the system. + + QOpenGLWidget supports multiple update behaviors, just like QOpenGLWindow. In + preserved mode the rendered content from the previous paintGL() call is + available in the next one, allowing incremental rendering. In non-preserved + mode the content is lost and paintGL() implementations are expected to redraw + everything in the view. + + Before Qt 5.5 the default behavior of QOpenGLWidget was to preserve the + rendered contents between paintGL() calls. Since Qt 5.5 the default behavior + is non-preserved because this provides better performance and the majority of + applications have no need for the previous content. This also resembles the + semantics of an OpenGL-based QWindow and matches the default behavior of + QOpenGLWindow in that the color and ancillary buffers are invalidated for + each frame. To restore the preserved behavior, call setUpdateBehavior() with + \c PartialUpdate. + + \section1 Alternatives + + Adding a QOpenGLWidget into a window turns on OpenGL-based + compositing for the entire window. In some special cases this may + not be ideal, and the old QGLWidget-style behavior with a separate, + native child window is desired. Desktop applications that understand + the limitations of this approach (for example when it comes to + overlaps, transparency, scroll views and MDI areas), can use + QOpenGLWindow with QWidget::createWindowContainer(). This is a + modern alternative to QGLWidget and is faster than QOpenGLWidget due + to the lack of the additional composition step. It is strongly + recommended to limit the usage of this approach to cases where there + is no other choice. Note that this option is not suitable for most + embedded and mobile platforms, and it is known to have issues on + certain desktop platforms (e.g. \macos) too. The stable, + cross-platform solution is always QOpenGLWidget. + + \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other + countries.} + + \sa QOpenGLFunctions, QOpenGLWindow, Qt::AA_ShareOpenGLContexts, UpdateBehavior +*/ + +/*! + \fn void QOpenGLWidget::aboutToCompose() + + This signal is emitted when the widget's top-level window is about to begin + composing the textures of its QOpenGLWidget children and the other widgets. +*/ + +/*! + \fn void QOpenGLWidget::frameSwapped() + + This signal is emitted after the widget's top-level window has finished + composition and returned from its potentially blocking + QOpenGLContext::swapBuffers() call. +*/ + +/*! + \fn void QOpenGLWidget::aboutToResize() + + This signal is emitted when the widget's size is changed and therefore the + framebuffer object is going to be recreated. +*/ + +/*! + \fn void QOpenGLWidget::resized() + + This signal is emitted right after the framebuffer object has been recreated + due to resizing the widget. +*/ + +/*! + \enum QOpenGLWidget::UpdateBehavior + \since 5.5 + + This enum describes the update semantics of QOpenGLWidget. + + \value NoPartialUpdate QOpenGLWidget will discard the + contents of the color buffer and the ancillary buffers after the + QOpenGLWidget is rendered to screen. This is the same behavior that can be + expected by calling QOpenGLContext::swapBuffers with a default opengl + enabled QWindow as the argument. NoPartialUpdate can have some performance + benefits on certain hardware architectures common in the mobile and + embedded space when a framebuffer object is used as the rendering target. + The framebuffer object is invalidated between frames with + glDiscardFramebufferEXT if supported or a glClear. Please see the + documentation of EXT_discard_framebuffer for more information: + https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt + + \value PartialUpdate The framebuffer objects color buffer and ancillary + buffers are not invalidated between frames. + + \sa updateBehavior(), setUpdateBehavior() +*/ + +class QOpenGLWidgetPaintDevicePrivate : public QOpenGLPaintDevicePrivate +{ +public: + QOpenGLWidgetPaintDevicePrivate(QOpenGLWidget *widget) + : QOpenGLPaintDevicePrivate(QSize()), + w(widget) { } + + void beginPaint() override; + void endPaint() override; + + QOpenGLWidget *w; +}; + +class QOpenGLWidgetPaintDevice : public QOpenGLPaintDevice +{ +public: + QOpenGLWidgetPaintDevice(QOpenGLWidget *widget) + : QOpenGLPaintDevice(*new QOpenGLWidgetPaintDevicePrivate(widget)) { } + void ensureActiveTarget() override; +}; + +class QOpenGLWidgetPrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLWidget) +public: + QOpenGLWidgetPrivate() + : context(nullptr), + fbo(nullptr), + resolvedFbo(nullptr), + surface(nullptr), + initialized(false), + fakeHidden(false), + inBackingStorePaint(false), + hasBeenComposed(false), + flushPending(false), + paintDevice(nullptr), + updateBehavior(QOpenGLWidget::NoPartialUpdate), + requestedSamples(0), + inPaintGL(false), + textureFormat(0) + { + requestedFormat = QSurfaceFormat::defaultFormat(); + } + + void reset(); + void recreateFbo(); + + GLuint textureId() const override; + QPlatformTextureList::Flags textureListFlags() override; + + void initialize(); + void invokeUserPaint(); + void render(); + + void invalidateFbo(); + + QImage grabFramebuffer() override; + void beginBackingStorePainting() override { inBackingStorePaint = true; } + void endBackingStorePainting() override { inBackingStorePaint = false; } + void beginCompose() override; + void endCompose() override; + void initializeViewportFramebuffer() override; + void resizeViewportFramebuffer() override; + void resolveSamples() override; + + QOpenGLContext *context; + QOpenGLFramebufferObject *fbo; + QOpenGLFramebufferObject *resolvedFbo; + QOffscreenSurface *surface; + bool initialized; + bool fakeHidden; + bool inBackingStorePaint; + bool hasBeenComposed; + bool flushPending; + QOpenGLPaintDevice *paintDevice; + QSurfaceFormat requestedFormat; + QOpenGLWidget::UpdateBehavior updateBehavior; + int requestedSamples; + bool inPaintGL; + GLenum textureFormat; +}; + +void QOpenGLWidgetPaintDevicePrivate::beginPaint() +{ + // NB! autoFillBackground is and must be false by default. Otherwise we would clear on + // every QPainter begin() which is not desirable. This is only for legacy use cases, + // like using QOpenGLWidget as the viewport of a graphics view, that expect clearing + // with the palette's background color. + if (w->autoFillBackground()) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + if (w->format().hasAlpha()) { + f->glClearColor(0, 0, 0, 0); + } else { + QColor c = w->palette().brush(w->backgroundRole()).color(); + float alpha = c.alphaF(); + f->glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha); + } + f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } +} + +void QOpenGLWidgetPaintDevicePrivate::endPaint() +{ + QOpenGLWidgetPrivate *wd = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(w)); + if (!wd->initialized) + return; + + if (!wd->inPaintGL) + QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = 0; +} + +void QOpenGLWidgetPaintDevice::ensureActiveTarget() +{ + QOpenGLWidgetPaintDevicePrivate *d = static_cast<QOpenGLWidgetPaintDevicePrivate *>(d_ptr.data()); + QOpenGLWidgetPrivate *wd = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(d->w)); + if (!wd->initialized) + return; + + if (QOpenGLContext::currentContext() != wd->context) + d->w->makeCurrent(); + else + wd->fbo->bind(); + + if (!wd->inPaintGL) + QOpenGLContextPrivate::get(wd->context)->defaultFboRedirect = wd->fbo->handle(); + + // When used as a viewport, drawing is done via opening a QPainter on the widget + // without going through paintEvent(). We will have to make sure a glFlush() is done + // before the texture is accessed also in this case. + wd->flushPending = true; +} + +GLuint QOpenGLWidgetPrivate::textureId() const +{ + return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0); +} + +#ifndef GL_SRGB +#define GL_SRGB 0x8C40 +#endif +#ifndef GL_SRGB8 +#define GL_SRGB8 0x8C41 +#endif +#ifndef GL_SRGB_ALPHA +#define GL_SRGB_ALPHA 0x8C42 +#endif +#ifndef GL_SRGB8_ALPHA8 +#define GL_SRGB8_ALPHA8 0x8C43 +#endif + +QPlatformTextureList::Flags QOpenGLWidgetPrivate::textureListFlags() +{ + QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags(); + switch (textureFormat) { + case GL_SRGB: + case GL_SRGB8: + case GL_SRGB_ALPHA: + case GL_SRGB8_ALPHA8: + flags |= QPlatformTextureList::TextureIsSrgb; + break; + default: + break; + } + return flags; +} + +void QOpenGLWidgetPrivate::reset() +{ + Q_Q(QOpenGLWidget); + + // Destroy the OpenGL resources first. These need the context to be current. + if (initialized) + q->makeCurrent(); + + delete paintDevice; + paintDevice = nullptr; + delete fbo; + fbo = nullptr; + delete resolvedFbo; + resolvedFbo = nullptr; + + if (initialized) + q->doneCurrent(); + + // Delete the context first, then the surface. Slots connected to + // the context's aboutToBeDestroyed() may still call makeCurrent() + // to perform some cleanup. + delete context; + context = nullptr; + delete surface; + surface = nullptr; + initialized = fakeHidden = inBackingStorePaint = false; +} + +void QOpenGLWidgetPrivate::recreateFbo() +{ + Q_Q(QOpenGLWidget); + + emit q->aboutToResize(); + + context->makeCurrent(surface); + + delete fbo; + fbo = nullptr; + delete resolvedFbo; + resolvedFbo = nullptr; + + int samples = requestedSamples; + QOpenGLExtensions *extfuncs = static_cast<QOpenGLExtensions *>(context->functions()); + if (!extfuncs->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) + samples = 0; + + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setSamples(samples); + if (textureFormat) + format.setInternalTextureFormat(textureFormat); + + const QSize deviceSize = q->size() * q->devicePixelRatioF(); + fbo = new QOpenGLFramebufferObject(deviceSize, format); + if (samples > 0) + resolvedFbo = new QOpenGLFramebufferObject(deviceSize); + + textureFormat = fbo->format().internalTextureFormat(); + + fbo->bind(); + context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + flushPending = true; // Make sure the FBO is initialized before use + + paintDevice->setSize(deviceSize); + paintDevice->setDevicePixelRatio(q->devicePixelRatioF()); + + emit q->resized(); +} + +void QOpenGLWidgetPrivate::beginCompose() +{ + Q_Q(QOpenGLWidget); + if (flushPending) { + flushPending = false; + q->makeCurrent(); + static_cast<QOpenGLExtensions *>(context->functions())->flushShared(); + } + hasBeenComposed = true; + emit q->aboutToCompose(); +} + +void QOpenGLWidgetPrivate::endCompose() +{ + Q_Q(QOpenGLWidget); + emit q->frameSwapped(); +} + +void QOpenGLWidgetPrivate::initialize() +{ + Q_Q(QOpenGLWidget); + if (initialized) + return; + + // If no global shared context get our toplevel's context with which we + // will share in order to make the texture usable by the underlying window's backingstore. + QWidget *tlw = q->window(); + QOpenGLContext *shareContext = qt_gl_global_share_context(); + if (!shareContext) + shareContext = get(tlw)->shareContext(); + // If shareContext is null, showing content on-screen will not work. + // However, offscreen rendering and grabFramebuffer() will stay fully functional. + + // Do not include the sample count. Requesting a multisampled context is not necessary + // since we render into an FBO, never to an actual surface. What's more, attempting to + // create a pbuffer with a multisampled config crashes certain implementations. Just + // avoid the entire hassle, the result is the same. + requestedSamples = requestedFormat.samples(); + requestedFormat.setSamples(0); + + QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext); + ctx->setFormat(requestedFormat); + if (shareContext) { + ctx->setShareContext(shareContext); + ctx->setScreen(shareContext->screen()); + } + if (Q_UNLIKELY(!ctx->create())) { + qWarning("QOpenGLWidget: Failed to create context"); + return; + } + + // Propagate settings that make sense only for the tlw. Note that this only + // makes sense for properties that get picked up even after the native + // window is created. + if (tlw->windowHandle()) { + QSurfaceFormat tlwFormat = tlw->windowHandle()->format(); + if (requestedFormat.swapInterval() != tlwFormat.swapInterval()) { + // Most platforms will pick up the changed swap interval on the next + // makeCurrent or swapBuffers. + tlwFormat.setSwapInterval(requestedFormat.swapInterval()); + tlw->windowHandle()->setFormat(tlwFormat); + } + if (requestedFormat.swapBehavior() != tlwFormat.swapBehavior()) { + tlwFormat.setSwapBehavior(requestedFormat.swapBehavior()); + tlw->windowHandle()->setFormat(tlwFormat); + } + } + + // The top-level window's surface is not good enough since it causes way too + // much trouble with regards to the QSurfaceFormat for example. So just like + // in QQuickWidget, use a dedicated QOffscreenSurface. + surface = new QOffscreenSurface; + surface->setFormat(ctx->format()); + surface->setScreen(ctx->screen()); + surface->create(); + + if (Q_UNLIKELY(!ctx->makeCurrent(surface))) { + qWarning("QOpenGLWidget: Failed to make context current"); + return; + } + + paintDevice = new QOpenGLWidgetPaintDevice(q); + paintDevice->setSize(q->size() * q->devicePixelRatioF()); + paintDevice->setDevicePixelRatio(q->devicePixelRatioF()); + + context = ctx.take(); + initialized = true; + + q->initializeGL(); +} + +void QOpenGLWidgetPrivate::resolveSamples() +{ + Q_Q(QOpenGLWidget); + if (resolvedFbo) { + q->makeCurrent(); + QRect rect(QPoint(0, 0), fbo->size()); + QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect); + flushPending = true; + } +} + +void QOpenGLWidgetPrivate::invokeUserPaint() +{ + Q_Q(QOpenGLWidget); + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + Q_ASSERT(ctx && fbo); + + QOpenGLFunctions *f = ctx->functions(); + QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = fbo->handle(); + + f->glViewport(0, 0, q->width() * q->devicePixelRatioF(), q->height() * q->devicePixelRatioF()); + inPaintGL = true; + q->paintGL(); + inPaintGL = false; + flushPending = true; + + QOpenGLContextPrivate::get(ctx)->defaultFboRedirect = 0; +} + +void QOpenGLWidgetPrivate::render() +{ + Q_Q(QOpenGLWidget); + + if (fakeHidden || !initialized) + return; + + q->makeCurrent(); + + if (updateBehavior == QOpenGLWidget::NoPartialUpdate && hasBeenComposed) { + invalidateFbo(); + hasBeenComposed = false; + } + + invokeUserPaint(); +} + +void QOpenGLWidgetPrivate::invalidateFbo() +{ + QOpenGLExtensions *f = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions()); + if (f->hasOpenGLExtension(QOpenGLExtensions::DiscardFramebuffer)) { + const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0 + const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT + const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT +#ifdef Q_OS_WASM + // webgl does not allow separate depth and stencil attachments + // QTBUG-69913 + const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT + + const GLenum attachments[] = { + gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment, gl_depth_stencil_attachment + }; +#else + const GLenum attachments[] = { + gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment + }; +#endif + f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments); + } else { + f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } +} + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +QImage QOpenGLWidgetPrivate::grabFramebuffer() +{ + Q_Q(QOpenGLWidget); + + initialize(); + if (!initialized) + return QImage(); + + if (!fbo) // could be completely offscreen, without ever getting a resize event + recreateFbo(); + + if (!inPaintGL) + render(); + + if (resolvedFbo) { + resolveSamples(); + resolvedFbo->bind(); + } else { + q->makeCurrent(); + } + + const bool hasAlpha = q->format().hasAlpha(); + QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatioF(), hasAlpha, hasAlpha); + res.setDevicePixelRatio(q->devicePixelRatioF()); + + // While we give no guarantees of what is going to be left bound, prefer the + // multisample fbo instead of the resolved one. Clients may continue to + // render straight after calling this function. + if (resolvedFbo) + q->makeCurrent(); + + return res; +} + +void QOpenGLWidgetPrivate::initializeViewportFramebuffer() +{ + Q_Q(QOpenGLWidget); + // Legacy behavior for compatibility with QGLWidget when used as a graphics view + // viewport: enable clearing on each painter begin. + q->setAutoFillBackground(true); +} + +void QOpenGLWidgetPrivate::resizeViewportFramebuffer() +{ + Q_Q(QOpenGLWidget); + if (!initialized) + return; + + if (!fbo || q->size() * q->devicePixelRatioF() != fbo->size()) { + recreateFbo(); + q->update(); + } +} + +/*! + Constructs a widget which is a child of \a parent, with widget flags set to \a f. + */ +QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f) + : QWidget(*(new QOpenGLWidgetPrivate), parent, f) +{ + Q_D(QOpenGLWidget); + if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))) + qWarning("QOpenGLWidget is not supported on this platform."); + else + d->setRenderToTexture(); +} + +/*! + Destroys the QOpenGLWidget instance, freeing its resources. + + The QOpenGLWidget's context is made current in the destructor, allowing for + safe destruction of any child object that may need to release OpenGL + resources belonging to the context provided by this widget. + + \warning if you have objects wrapping OpenGL resources (such as + QOpenGLBuffer, QOpenGLShaderProgram, etc.) as members of a OpenGLWidget + subclass, you may need to add a call to makeCurrent() in that subclass' + destructor as well. Due to the rules of C++ object destruction, those objects + will be destroyed \e{before} calling this function (but after that the + destructor of the subclass has run), therefore making the OpenGL context + current in this function happens too late for their safe disposal. + + \sa makeCurrent +*/ +QOpenGLWidget::~QOpenGLWidget() +{ + Q_D(QOpenGLWidget); + d->reset(); +} + +/*! + Sets this widget's update behavior to \a updateBehavior. + \since 5.5 +*/ +void QOpenGLWidget::setUpdateBehavior(UpdateBehavior updateBehavior) +{ + Q_D(QOpenGLWidget); + d->updateBehavior = updateBehavior; +} + +/*! + \return the update behavior of the widget. + \since 5.5 +*/ +QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const +{ + Q_D(const QOpenGLWidget); + return d->updateBehavior; +} + +/*! + Sets the requested surface \a format. + + When the format is not explicitly set via this function, the format returned by + QSurfaceFormat::defaultFormat() will be used. This means that when having multiple + OpenGL widgets, individual calls to this function can be replaced by one single call to + QSurfaceFormat::setDefaultFormat() before creating the first widget. + + \note Requesting an alpha buffer via this function will not lead to the + desired results when the intention is to make other widgets beneath visible. + Instead, use Qt::WA_AlwaysStackOnTop to enable semi-transparent QOpenGLWidget + instances with other widgets visible underneath. Keep in mind however that + this breaks the stacking order, so it will no longer be possible to have + other widgets on top of the QOpenGLWidget. + + \sa format(), Qt::WA_AlwaysStackOnTop, QSurfaceFormat::setDefaultFormat() + */ +void QOpenGLWidget::setFormat(const QSurfaceFormat &format) +{ + Q_D(QOpenGLWidget); + if (Q_UNLIKELY(d->initialized)) { + qWarning("QOpenGLWidget: Already initialized, setting the format has no effect"); + return; + } + + d->requestedFormat = format; +} + +/*! + Returns the context and surface format used by this widget and its toplevel + window. + + After the widget and its toplevel have both been created, resized and shown, + this function will return the actual format of the context. This may differ + from the requested format if the request could not be fulfilled by the + platform. It is also possible to get larger color buffer sizes than + requested. + + When the widget's window and the related OpenGL resources are not yet + initialized, the return value is the format that has been set via + setFormat(). + + \sa setFormat(), context() + */ +QSurfaceFormat QOpenGLWidget::format() const +{ + Q_D(const QOpenGLWidget); + return d->initialized ? d->context->format() : d->requestedFormat; +} + +/*! + Sets a custom internal texture format of \a texFormat. + + When working with sRGB framebuffers, it will be necessary to specify a + format like \c{GL_SRGB8_ALPHA8}. This can be achieved by calling this + function. + + \note This function has no effect if called after the widget has already + been shown and thus it performed initialization. + + \note This function will typically have to be used in combination with a + QSurfaceFormat::setDefaultFormat() call that sets the color space to + QSurfaceFormat::sRGBColorSpace. + + \since 5.10 + */ +void QOpenGLWidget::setTextureFormat(GLenum texFormat) +{ + Q_D(QOpenGLWidget); + if (Q_UNLIKELY(d->initialized)) { + qWarning("QOpenGLWidget: Already initialized, setting the internal texture format has no effect"); + return; + } + + d->textureFormat = texFormat; +} + +/*! + \return the active internal texture format if the widget has already + initialized, the requested format if one was set but the widget has not yet + been made visible, or \nullptr if setTextureFormat() was not called and the + widget has not yet been made visible. + + \since 5.10 + */ +GLenum QOpenGLWidget::textureFormat() const +{ + Q_D(const QOpenGLWidget); + return d->textureFormat; +} + +/*! + \return \e true if the widget and OpenGL resources, like the context, have + been successfully initialized. Note that the return value is always false + until the widget is shown. +*/ +bool QOpenGLWidget::isValid() const +{ + Q_D(const QOpenGLWidget); + return d->initialized && d->context->isValid(); +} + +/*! + Prepares for rendering OpenGL content for this widget by making the + corresponding context current and binding the framebuffer object in that + context. + + It is not necessary to call this function in most cases, because it + is called automatically before invoking paintGL(). + + \sa context(), paintGL(), doneCurrent() + */ +void QOpenGLWidget::makeCurrent() +{ + Q_D(QOpenGLWidget); + if (!d->initialized) + return; + + d->context->makeCurrent(d->surface); + + if (d->fbo) // there may not be one if we are in reset() + d->fbo->bind(); +} + +/*! + Releases the context. + + It is not necessary to call this function in most cases, since the + widget will make sure the context is bound and released properly + when invoking paintGL(). + */ +void QOpenGLWidget::doneCurrent() +{ + Q_D(QOpenGLWidget); + if (!d->initialized) + return; + + d->context->doneCurrent(); +} + +/*! + \return The QOpenGLContext used by this widget or \c 0 if not yet initialized. + + \note The context and the framebuffer object used by the widget changes when + reparenting the widget via setParent(). + + \sa QOpenGLContext::setShareContext(), defaultFramebufferObject() + */ +QOpenGLContext *QOpenGLWidget::context() const +{ + Q_D(const QOpenGLWidget); + return d->context; +} + +/*! + \return The framebuffer object handle or \c 0 if not yet initialized. + + \note The framebuffer object belongs to the context returned by context() + and may not be accessible from other contexts. + + \note The context and the framebuffer object used by the widget changes when + reparenting the widget via setParent(). In addition, the framebuffer object + changes on each resize. + + \sa context() + */ +GLuint QOpenGLWidget::defaultFramebufferObject() const +{ + Q_D(const QOpenGLWidget); + return d->fbo ? d->fbo->handle() : 0; +} + +/*! + This virtual function is called once before the first call to + paintGL() or resizeGL(). Reimplement it in a subclass. + + This function should set up any required OpenGL resources and state. + + There is no need to call makeCurrent() because this has already been + done when this function is called. Note however that the framebuffer + is not yet available at this stage, so avoid issuing draw calls from + here. Defer such calls to paintGL() instead. + + \sa paintGL(), resizeGL() +*/ +void QOpenGLWidget::initializeGL() +{ +} + +/*! + This virtual function is called whenever the widget has been + resized. Reimplement it in a subclass. The new size is passed in + \a w and \a h. + + There is no need to call makeCurrent() because this has already been + done when this function is called. Additionally, the framebuffer is + also bound. + + \sa initializeGL(), paintGL() +*/ +void QOpenGLWidget::resizeGL(int w, int h) +{ + Q_UNUSED(w); + Q_UNUSED(h); +} + +/*! + This virtual function is called whenever the widget needs to be + painted. Reimplement it in a subclass. + + There is no need to call makeCurrent() because this has already + been done when this function is called. + + Before invoking this function, the context and the framebuffer are + bound, and the viewport is set up by a call to glViewport(). No + other state is set and no clearing or drawing is performed by the + framework. + + \sa initializeGL(), resizeGL() +*/ +void QOpenGLWidget::paintGL() +{ +} + +/*! + Handles resize events that are passed in the \a e event parameter. + Calls the virtual function resizeGL(). + + \note Avoid overriding this function in derived classes. If that is not + feasible, make sure that QOpenGLWidget's implementation is invoked + too. Otherwise the underlying framebuffer object and related resources will + not get resized properly and will lead to incorrect rendering. +*/ +void QOpenGLWidget::resizeEvent(QResizeEvent *e) +{ + Q_D(QOpenGLWidget); + + if (e->size().isEmpty()) { + d->fakeHidden = true; + return; + } + d->fakeHidden = false; + + d->initialize(); + if (!d->initialized) + return; + + d->recreateFbo(); + resizeGL(width(), height()); + d->sendPaintEvent(QRect(QPoint(0, 0), size())); +} + +/*! + Handles paint events. + + Calling QWidget::update() will lead to sending a paint event \a e, + and thus invoking this function. (NB this is asynchronous and will + happen at some point after returning from update()). This function + will then, after some preparation, call the virtual paintGL() to + update the contents of the QOpenGLWidget's framebuffer. The widget's + top-level window will then composite the framebuffer's texture with + the rest of the window. +*/ +void QOpenGLWidget::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e); + Q_D(QOpenGLWidget); + if (!d->initialized) + return; + + if (updatesEnabled()) + d->render(); +} + +/*! + Renders and returns a 32-bit RGB image of the framebuffer. + + \note This is a potentially expensive operation because it relies on glReadPixels() + to read back the pixels. This may be slow and can stall the GPU pipeline. +*/ +QImage QOpenGLWidget::grabFramebuffer() +{ + Q_D(QOpenGLWidget); + return d->grabFramebuffer(); +} + +/*! + \internal +*/ +int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + Q_D(const QOpenGLWidget); + if (d->inBackingStorePaint) + return QWidget::metric(metric); + + auto window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel); + QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen(); + + const float dpmx = qt_defaultDpiX() * 100. / 2.54; + const float dpmy = qt_defaultDpiY() * 100. / 2.54; + + switch (metric) { + case PdmWidth: + return width(); + case PdmHeight: + return height(); + case PdmDepth: + return 32; + case PdmWidthMM: + if (screen) + return width() * screen->physicalSize().width() / screen->geometry().width(); + else + return width() * 1000 / dpmx; + case PdmHeightMM: + if (screen) + return height() * screen->physicalSize().height() / screen->geometry().height(); + else + return height() * 1000 / dpmy; + case PdmNumColors: + return 0; + case PdmDpiX: + if (screen) + return qRound(screen->logicalDotsPerInchX()); + else + return qRound(dpmx * 0.0254); + case PdmDpiY: + if (screen) + return qRound(screen->logicalDotsPerInchY()); + else + return qRound(dpmy * 0.0254); + case PdmPhysicalDpiX: + if (screen) + return qRound(screen->physicalDotsPerInchX()); + else + return qRound(dpmx * 0.0254); + case PdmPhysicalDpiY: + if (screen) + return qRound(screen->physicalDotsPerInchY()); + else + return qRound(dpmy * 0.0254); + case PdmDevicePixelRatio: + if (window) + return int(window->devicePixelRatio()); + else + return 1.0; + case PdmDevicePixelRatioScaled: + if (window) + return int(window->devicePixelRatio() * devicePixelRatioFScale()); + else + return int(devicePixelRatioFScale()); + default: + qWarning("QOpenGLWidget::metric(): unknown metric %d", metric); + return 0; + } +} + +/*! + \internal +*/ +QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const +{ + Q_D(const QOpenGLWidget); + if (d->inBackingStorePaint) + return QWidget::redirected(p); + + return d->paintDevice; +} + +/*! + \internal +*/ +QPaintEngine *QOpenGLWidget::paintEngine() const +{ + Q_D(const QOpenGLWidget); + // QWidget needs to "punch a hole" into the backingstore. This needs the + // normal paint engine and device, not the GL one. So in this mode, behave + // like a normal widget. + if (d->inBackingStorePaint) + return QWidget::paintEngine(); + + if (!d->initialized) + return nullptr; + + return d->paintDevice->paintEngine(); +} + +/*! + \internal +*/ +bool QOpenGLWidget::event(QEvent *e) +{ + Q_D(QOpenGLWidget); + switch (e->type()) { + case QEvent::WindowChangeInternal: + if (QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts)) + break; + if (d->initialized) + d->reset(); + if (isHidden()) + break; + Q_FALLTHROUGH(); + case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too + if (d->initialized && window()->windowHandle() + && d->context->shareContext() != QWidgetPrivate::get(window())->shareContext()) + { + // Special case: did grabFramebuffer() for a hidden widget that then became visible. + // Recreate all resources since the context now needs to share with the TLW's. + if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts)) + d->reset(); + } + if (!d->initialized && !size().isEmpty() && window()->windowHandle()) { + d->initialize(); + if (d->initialized) + d->recreateFbo(); + } + break; + case QEvent::ScreenChangeInternal: + if (d->initialized && d->paintDevice->devicePixelRatioF() != devicePixelRatioF()) + d->recreateFbo(); + break; + default: + break; + } + return QWidget::event(e); +} + +QT_END_NAMESPACE + +#include "moc_qopenglwidget.cpp" diff --git a/src/opengl/qglpaintdevice_p.h b/src/opengl/qopenglwidget.h index bfecdabd53..b266dc359d 100644 --- a/src/opengl/qglpaintdevice_p.h +++ b/src/opengl/qopenglwidget.h @@ -37,77 +37,79 @@ ** ****************************************************************************/ -#ifndef QGLPAINTDEVICE_P_H -#define QGLPAINTDEVICE_P_H +#ifndef QOPENGLWIDGET_H +#define QOPENGLWIDGET_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of the Qt OpenGL module. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// +#include <QtOpenGL/qtopenglglobal.h> +#ifdef QT_WIDGETS_LIB -#include <qpaintdevice.h> -#include <QtOpenGL/qgl.h> - +#include <QtWidgets/QWidget> +#include <QtGui/QSurfaceFormat> +#include <QtGui/qopengl.h> QT_BEGIN_NAMESPACE -class Q_OPENGL_EXPORT QGLPaintDevice : public QPaintDevice +class QOpenGLWidgetPrivate; + +class Q_OPENGL_EXPORT QOpenGLWidget : public QWidget { + Q_OBJECT + Q_DECLARE_PRIVATE(QOpenGLWidget) + public: - QGLPaintDevice(); - virtual ~QGLPaintDevice(); + enum UpdateBehavior { + NoPartialUpdate, + PartialUpdate + }; - int devType() const override {return QInternal::OpenGL;} + explicit QOpenGLWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~QOpenGLWidget(); - virtual void beginPaint(); - virtual void ensureActiveTarget(); - virtual void endPaint(); + void setUpdateBehavior(UpdateBehavior updateBehavior); + UpdateBehavior updateBehavior() const; - virtual QGLContext* context() const = 0; - virtual QGLFormat format() const; - virtual QSize size() const = 0; - virtual bool alphaRequested() const; - virtual bool isFlipped() const; + void setFormat(const QSurfaceFormat &format); + QSurfaceFormat format() const; - // returns the QGLPaintDevice for the given QPaintDevice - static QGLPaintDevice* getDevice(QPaintDevice*); + GLenum textureFormat() const; + void setTextureFormat(GLenum texFormat); -protected: - int metric(QPaintDevice::PaintDeviceMetric metric) const override; - GLuint m_previousFBO; - GLuint m_thisFBO; -}; + bool isValid() const; + void makeCurrent(); + void doneCurrent(); -// Wraps a QGLWidget -class QGLWidget; -class Q_OPENGL_EXPORT QGLWidgetGLPaintDevice : public QGLPaintDevice -{ -public: - QGLWidgetGLPaintDevice(); + QOpenGLContext *context() const; + GLuint defaultFramebufferObject() const; + + QImage grabFramebuffer(); + +Q_SIGNALS: + void aboutToCompose(); + void frameSwapped(); + void aboutToResize(); + void resized(); - virtual QPaintEngine* paintEngine() const override; +protected: + virtual void initializeGL(); + virtual void resizeGL(int w, int h); + virtual void paintGL(); - // QGLWidgets need to do swapBufers in endPaint: - virtual void beginPaint() override; - virtual void endPaint() override; - virtual QSize size() const override; - virtual QGLContext* context() const override; + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + bool event(QEvent *e) override; - void setWidget(QGLWidget*); + int metric(QPaintDevice::PaintDeviceMetric metric) const override; + QPaintDevice *redirected(QPoint *p) const override; + QPaintEngine *paintEngine() const override; private: - friend class QGLWidget; - QGLWidget *glWidget; + Q_DISABLE_COPY(QOpenGLWidget) }; QT_END_NAMESPACE -#endif // QGLPAINTDEVICE_P_H +#endif // QT_WIDGETS_LIB + +#endif // QOPENGLWIDGET_H diff --git a/src/opengl/qopenglwindow.cpp b/src/opengl/qopenglwindow.cpp new file mode 100644 index 0000000000..5da20dc559 --- /dev/null +++ b/src/opengl/qopenglwindow.cpp @@ -0,0 +1,698 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglwindow.h" +#include <QtGui/QOpenGLFramebufferObject> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/private/qpaintdevicewindow_p.h> +#include <QtGui/private/qopenglextensions_p.h> +#include <QtGui/private/qopenglcontext_p.h> +#include <QtGui/QMatrix4x4> +#include <QtGui/QOffscreenSurface> +#include <QtOpenGL/QOpenGLTextureBlitter> +#include <QtOpenGL/QOpenGLPaintDevice> + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLWindow + \inmodule QtOpenGL + \since 5.4 + \brief The QOpenGLWindow class is a convenience subclass of QWindow to perform OpenGL painting. + + QOpenGLWindow is an enhanced QWindow that allows easily creating windows that + perform OpenGL rendering using an API that is compatible with QOpenGLWidget + and is similar to the legacy QGLWidget. Unlike QOpenGLWidget, QOpenGLWindow + has no dependency on the widgets module and offers better performance. + + A typical application will subclass QOpenGLWindow and reimplement the following + virtual functions: + + \list + + \li initializeGL() to perform OpenGL resource initialization + + \li resizeGL() to set up the transformation matrices and other window size dependent resources + + \li paintGL() to issue OpenGL commands or draw using QPainter + + \endlist + + To schedule a repaint, call the update() function. Note that this will not + immediately result in a call to paintGL(). Calling update() multiple times in + a row will not change the behavior in any way. + + This is a slot so it can be connected to a \l QTimer::timeout() signal to + perform animation. Note however that in the modern OpenGL world it is a much + better choice to rely on synchronization to the vertical refresh rate of the + display. See \l{QSurfaceFormat::setSwapInterval()}{setSwapInterval()} on a + description of the swap interval. With a swap interval of \c 1, which is the + case on most systems by default, the + \l{QOpenGLContext::swapBuffers()}{swapBuffers()} call, that is executed + internally by QOpenGLWindow after each repaint, will block and wait for + vsync. This means that whenever the swap is done, an update can be scheduled + again by calling update(), without relying on timers. + + To request a specific configuration for the context, use setFormat() + like for any other QWindow. This allows, among others, requesting a + given OpenGL version and profile, or enabling depth and stencil + buffers. + + Unlike QWindow, QOpenGLWindow allows opening a painter on itself and perform + QPainter-based drawing. + + QOpenGLWindow supports multiple update behaviors. The default, + \c NoPartialUpdate is equivalent to a regular, OpenGL-based QWindow or the + legacy QGLWidget. In contrast, \c PartialUpdateBlit and \c PartialUpdateBlend are + more in line with QOpenGLWidget's way of working, where there is always an + extra, dedicated framebuffer object present. These modes allow, by + sacrificing some performance, redrawing only a smaller area on each paint and + having the rest of the content preserved from of the previous frame. This is + useful for applications than render incrementally using QPainter, because + this way they do not have to redraw the entire window content on each + paintGL() call. + + Similarly to QOpenGLWidget, QOpenGLWindow supports the Qt::AA_ShareOpenGLContexts + attribute. When enabled, the OpenGL contexts of all QOpenGLWindow instances will share + with each other. This allows accessing each other's shareable OpenGL resources. + + For more information on graphics in Qt, see \l {Graphics}. + */ + +/*! + \enum QOpenGLWindow::UpdateBehavior + + This enum describes the update strategy of the QOpenGLWindow. + + \value NoPartialUpdate Indicates that the entire window surface will + redrawn on each update and so no additional framebuffers are needed. + This is the setting used in most cases and is equivalent to how drawing + directly via QWindow would function. + + \value PartialUpdateBlit Indicates that the drawing performed in paintGL() + does not cover the entire window. In this case an extra framebuffer object + is created under the hood, and rendering performed in paintGL() will target + this framebuffer. This framebuffer is then blitted onto the window surface's + default framebuffer after each paint. This allows having QPainter-based drawing + code in paintGL() which only repaints a smaller area at a time, because, unlike + NoPartialUpdate, the previous content is preserved. + + \value PartialUpdateBlend Similar to PartialUpdateBlit, but instead of using + framebuffer blits, the contents of the extra framebuffer is rendered by + drawing a textured quad with blending enabled. This, unlike PartialUpdateBlit, + allows alpha blended content and works even when the glBlitFramebuffer is + not available. Performance-wise this setting is likely to be somewhat slower + than PartialUpdateBlit. + */ + +/*! + \fn void QOpenGLWindow::frameSwapped() + + This signal is emitted after the potentially blocking + \l{QOpenGLContext::swapBuffers()}{buffer swap} has been done. Applications + that wish to continuously repaint synchronized to the vertical refresh, + should issue an update() upon this signal. This allows for a much smoother + experience compared to the traditional usage of timers. +*/ + +// GLES2 builds won't have these constants with the suffixless names +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif + +class QOpenGLWindowPaintDevice : public QOpenGLPaintDevice +{ +public: + QOpenGLWindowPaintDevice(QOpenGLWindow *window) : m_window(window) { } + void ensureActiveTarget() override; + + QOpenGLWindow *m_window; +}; + +class QOpenGLWindowPrivate : public QPaintDeviceWindowPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLWindow) +public: + QOpenGLWindowPrivate(QOpenGLContext *shareContext, QOpenGLWindow::UpdateBehavior updateBehavior) + : updateBehavior(updateBehavior) + , hasFboBlit(false) + , shareContext(shareContext) + { + if (!shareContext) + this->shareContext = qt_gl_global_share_context(); + } + + ~QOpenGLWindowPrivate(); + + static QOpenGLWindowPrivate *get(QOpenGLWindow *w) { return w->d_func(); } + + void bindFBO(); + void initialize(); + + void beginPaint(const QRegion ®ion) override; + void endPaint() override; + void flush(const QRegion ®ion) override; + + QOpenGLWindow::UpdateBehavior updateBehavior; + bool hasFboBlit; + QScopedPointer<QOpenGLContext> context; + QOpenGLContext *shareContext; + QScopedPointer<QOpenGLFramebufferObject> fbo; + QScopedPointer<QOpenGLWindowPaintDevice> paintDevice; + QOpenGLTextureBlitter blitter; + QColor backgroundColor; + QScopedPointer<QOffscreenSurface> offscreenSurface; +}; + +QOpenGLWindowPrivate::~QOpenGLWindowPrivate() +{ + Q_Q(QOpenGLWindow); + if (q->isValid()) { + q->makeCurrent(); // this works even when the platformwindow is destroyed + paintDevice.reset(nullptr); + fbo.reset(nullptr); + blitter.destroy(); + q->doneCurrent(); + } +} + +void QOpenGLWindowPrivate::initialize() +{ + Q_Q(QOpenGLWindow); + + if (context) + return; + + if (!q->handle()) + qWarning("Attempted to initialize QOpenGLWindow without a platform window"); + + context.reset(new QOpenGLContext); + context->setShareContext(shareContext); + context->setFormat(q->requestedFormat()); + if (!context->create()) + qWarning("QOpenGLWindow::beginPaint: Failed to create context"); + if (!context->makeCurrent(q)) + qWarning("QOpenGLWindow::beginPaint: Failed to make context current"); + + paintDevice.reset(new QOpenGLWindowPaintDevice(q)); + if (updateBehavior == QOpenGLWindow::PartialUpdateBlit) + hasFboBlit = QOpenGLFramebufferObject::hasOpenGLFramebufferBlit(); + + q->initializeGL(); +} + +void QOpenGLWindowPrivate::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + Q_Q(QOpenGLWindow); + + initialize(); + context->makeCurrent(q); + + const int deviceWidth = q->width() * q->devicePixelRatio(); + const int deviceHeight = q->height() * q->devicePixelRatio(); + const QSize deviceSize(deviceWidth, deviceHeight); + if (updateBehavior > QOpenGLWindow::NoPartialUpdate) { + if (!fbo || fbo->size() != deviceSize) { + QOpenGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + const int samples = q->requestedFormat().samples(); + if (samples > 0) { + if (updateBehavior != QOpenGLWindow::PartialUpdateBlend) + fboFormat.setSamples(samples); + else + qWarning("QOpenGLWindow: PartialUpdateBlend does not support multisampling"); + } + fbo.reset(new QOpenGLFramebufferObject(deviceSize, fboFormat)); + markWindowAsDirty(); + } + } else { + markWindowAsDirty(); + } + + paintDevice->setSize(QSize(deviceWidth, deviceHeight)); + paintDevice->setDevicePixelRatio(q->devicePixelRatio()); + context->functions()->glViewport(0, 0, deviceWidth, deviceHeight); + + context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject()); + + q->paintUnderGL(); + + if (updateBehavior > QOpenGLWindow::NoPartialUpdate) + fbo->bind(); +} + +void QOpenGLWindowPrivate::endPaint() +{ + Q_Q(QOpenGLWindow); + + if (updateBehavior > QOpenGLWindow::NoPartialUpdate) + fbo->release(); + + context->functions()->glBindFramebuffer(GL_FRAMEBUFFER, context->defaultFramebufferObject()); + + if (updateBehavior == QOpenGLWindow::PartialUpdateBlit && hasFboBlit) { + const int deviceWidth = q->width() * q->devicePixelRatio(); + const int deviceHeight = q->height() * q->devicePixelRatio(); + QOpenGLExtensions extensions(context.data()); + extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle()); + extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, context->defaultFramebufferObject()); + extensions.glBlitFramebuffer(0, 0, deviceWidth, deviceHeight, + 0, 0, deviceWidth, deviceHeight, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } else if (updateBehavior > QOpenGLWindow::NoPartialUpdate) { + if (updateBehavior == QOpenGLWindow::PartialUpdateBlend) { + context->functions()->glEnable(GL_BLEND); + context->functions()->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + if (!blitter.isCreated()) + blitter.create(); + + QRect windowRect(QPoint(0, 0), fbo->size()); + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(windowRect, windowRect); + blitter.bind(); + blitter.blit(fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft); + blitter.release(); + + if (updateBehavior == QOpenGLWindow::PartialUpdateBlend) + context->functions()->glDisable(GL_BLEND); + } + + q->paintOverGL(); +} + +void QOpenGLWindowPrivate::bindFBO() +{ + if (updateBehavior > QOpenGLWindow::NoPartialUpdate) + fbo->bind(); + else + QOpenGLFramebufferObject::bindDefault(); +} + +void QOpenGLWindowPrivate::flush(const QRegion ®ion) +{ + Q_UNUSED(region); + Q_Q(QOpenGLWindow); + context->swapBuffers(q); + emit q->frameSwapped(); +} + +void QOpenGLWindowPaintDevice::ensureActiveTarget() +{ + QOpenGLWindowPrivate::get(m_window)->bindFBO(); +} + +/*! + Constructs a new QOpenGLWindow with the given \a parent and \a updateBehavior. + + \sa QOpenGLWindow::UpdateBehavior + */ +QOpenGLWindow::QOpenGLWindow(QOpenGLWindow::UpdateBehavior updateBehavior, QWindow *parent) + : QPaintDeviceWindow(*(new QOpenGLWindowPrivate(nullptr, updateBehavior)), parent) +{ + setSurfaceType(QSurface::OpenGLSurface); +} + +/*! + Constructs a new QOpenGLWindow with the given \a parent and \a updateBehavior. The QOpenGLWindow's context will share with \a shareContext. + + \sa QOpenGLWindow::UpdateBehavior shareContext +*/ +QOpenGLWindow::QOpenGLWindow(QOpenGLContext *shareContext, UpdateBehavior updateBehavior, QWindow *parent) + : QPaintDeviceWindow(*(new QOpenGLWindowPrivate(shareContext, updateBehavior)), parent) +{ + setSurfaceType(QSurface::OpenGLSurface); +} + +/*! + Destroys the QOpenGLWindow instance, freeing its resources. + + The OpenGLWindow's context is made current in the destructor, allowing for + safe destruction of any child object that may need to release OpenGL + resources belonging to the context provided by this window. + + \warning if you have objects wrapping OpenGL resources (such as + QOpenGLBuffer, QOpenGLShaderProgram, etc.) as members of a QOpenGLWindow + subclass, you may need to add a call to makeCurrent() in that subclass' + destructor as well. Due to the rules of C++ object destruction, those objects + will be destroyed \e{before} calling this function (but after that the + destructor of the subclass has run), therefore making the OpenGL context + current in this function happens too late for their safe disposal. + + \sa makeCurrent + + \since 5.5 +*/ +QOpenGLWindow::~QOpenGLWindow() +{ + makeCurrent(); +} + +/*! + \return the update behavior for this QOpenGLWindow. +*/ +QOpenGLWindow::UpdateBehavior QOpenGLWindow::updateBehavior() const +{ + Q_D(const QOpenGLWindow); + return d->updateBehavior; +} + +/*! + \return \c true if the window's OpenGL resources, like the context, have + been successfully initialized. Note that the return value is always \c false + until the window becomes exposed (shown). +*/ +bool QOpenGLWindow::isValid() const +{ + Q_D(const QOpenGLWindow); + return d->context && d->context->isValid(); +} + +/*! + Prepares for rendering OpenGL content for this window by making the + corresponding context current and binding the framebuffer object, if there is + one, in that context context. + + It is not necessary to call this function in most cases, because it is called + automatically before invoking paintGL(). It is provided nonetheless to support + advanced, multi-threaded scenarios where a thread different than the GUI or main + thread may want to update the surface or framebuffer contents. See QOpenGLContext + for more information on threading related issues. + + This function is suitable for calling also when the underlying platform window + is already destroyed. This means that it is safe to call this function from + a QOpenGLWindow subclass' destructor. If there is no native window anymore, + an offscreen surface is used instead. This ensures that OpenGL resource + cleanup operations in the destructor will always work, as long as + this function is called first. + + \sa QOpenGLContext, context(), paintGL(), doneCurrent() + */ +void QOpenGLWindow::makeCurrent() +{ + Q_D(QOpenGLWindow); + + if (!isValid()) + return; + + // The platform window may be destroyed at this stage and therefore + // makeCurrent() may not safely be called with 'this'. + if (handle()) { + d->context->makeCurrent(this); + } else { + if (!d->offscreenSurface) { + d->offscreenSurface.reset(new QOffscreenSurface(screen())); + d->offscreenSurface->setFormat(d->context->format()); + d->offscreenSurface->create(); + } + d->context->makeCurrent(d->offscreenSurface.data()); + } + + d->bindFBO(); +} + +/*! + Releases the context. + + It is not necessary to call this function in most cases, since the widget + will make sure the context is bound and released properly when invoking + paintGL(). + + \sa makeCurrent() + */ +void QOpenGLWindow::doneCurrent() +{ + Q_D(QOpenGLWindow); + + if (!isValid()) + return; + + d->context->doneCurrent(); +} + +/*! + \return The QOpenGLContext used by this window or \c 0 if not yet initialized. + */ +QOpenGLContext *QOpenGLWindow::context() const +{ + Q_D(const QOpenGLWindow); + return d->context.data(); +} + +/*! + \return The QOpenGLContext requested to be shared with this window's QOpenGLContext. +*/ +QOpenGLContext *QOpenGLWindow::shareContext() const +{ + Q_D(const QOpenGLWindow); + return d->shareContext; +} + +/*! + The framebuffer object handle used by this window. + + When the update behavior is set to \c NoPartialUpdate, there is no separate + framebuffer object. In this case the returned value is the ID of the + default framebuffer. + + Otherwise the value of the ID of the framebuffer object or \c 0 if not + yet initialized. + */ +GLuint QOpenGLWindow::defaultFramebufferObject() const +{ + Q_D(const QOpenGLWindow); + if (d->updateBehavior > NoPartialUpdate && d->fbo) + return d->fbo->handle(); + else if (QOpenGLContext *ctx = QOpenGLContext::currentContext()) + return ctx->defaultFramebufferObject(); + else + return 0; +} + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +/*! + Returns a copy of the framebuffer. + + \note This is a potentially expensive operation because it relies on + glReadPixels() to read back the pixels. This may be slow and can stall the + GPU pipeline. + + \note When used together with update behavior \c NoPartialUpdate, the returned + image may not contain the desired content when called after the front and back + buffers have been swapped (unless preserved swap is enabled in the underlying + windowing system interface). In this mode the function reads from the back + buffer and the contents of that may not match the content on the screen (the + front buffer). In this case the only place where this function can safely be + used is paintGL() or paintOverGL(). + */ +QImage QOpenGLWindow::grabFramebuffer() +{ + if (!isValid()) + return QImage(); + + makeCurrent(); + + const bool hasAlpha = format().hasAlpha(); + QImage img = qt_gl_read_framebuffer(size() * devicePixelRatio(), hasAlpha, hasAlpha); + img.setDevicePixelRatio(devicePixelRatio()); + return img; +} + +/*! + This virtual function is called once before the first call to paintGL() or + resizeGL(). Reimplement it in a subclass. + + This function should set up any required OpenGL resources and state. + + There is no need to call makeCurrent() because this has already been done + when this function is called. Note however that the framebuffer, in case + partial update mode is used, is not yet available at this stage, so avoid + issuing draw calls from here. Defer such calls to paintGL() instead. + + \sa paintGL(), resizeGL() + */ +void QOpenGLWindow::initializeGL() +{ +} + +/*! + This virtual function is called whenever the widget has been resized. + Reimplement it in a subclass. The new size is passed in \a w and \a h. + + \note This is merely a convenience function in order to provide an API that is + compatible with QOpenGLWidget. Unlike with QOpenGLWidget, derived classes are + free to choose to override resizeEvent() instead of this function. + + \note Avoid issuing OpenGL commands from this function as there may not be a + context current when it is invoked. If it cannot be avoided, call makeCurrent(). + + \note Scheduling updates from here is not necessary. The windowing systems + will send expose events that trigger an update automatically. + + \sa initializeGL(), paintGL() + */ +void QOpenGLWindow::resizeGL(int w, int h) +{ + Q_UNUSED(w); + Q_UNUSED(h); +} + +/*! + This virtual function is called whenever the window contents needs to be + painted. Reimplement it in a subclass. + + There is no need to call makeCurrent() because this has already + been done when this function is called. + + Before invoking this function, the context and the framebuffer, if there is + one, are bound, and the viewport is set up by a call to glViewport(). No + other state is set and no clearing or drawing is performed by the framework. + + \note When using a partial update behavior, like \c PartialUpdateBlend, the + output of the previous paintGL() call is preserved and, after the additional + drawing perfomed in the current invocation of the function, the content is + blitted or blended over the content drawn directly to the window in + paintUnderGL(). + + \sa initializeGL(), resizeGL(), paintUnderGL(), paintOverGL(), UpdateBehavior + */ +void QOpenGLWindow::paintGL() +{ +} + +/*! + The virtual function is called before each invocation of paintGL(). + + When the update mode is set to \c NoPartialUpdate, there is no difference + between this function and paintGL(), performing rendering in either of them + leads to the same result. + + The difference becomes significant when using \c PartialUpdateBlend, where an + extra framebuffer object is used. There, paintGL() targets this additional + framebuffer object, which preserves its contents, while paintUnderGL() and + paintOverGL() target the default framebuffer, i.e. directly the window + surface, the contents of which is lost after each displayed frame. + + \note Avoid relying on this function when the update behavior is + \c PartialUpdateBlit. This mode involves blitting the extra framebuffer used by + paintGL() onto the default framebuffer after each invocation of paintGL(), + thus overwriting all drawing generated in this function. + + \sa paintGL(), paintOverGL(), UpdateBehavior + */ +void QOpenGLWindow::paintUnderGL() +{ +} + +/*! + This virtual function is called after each invocation of paintGL(). + + When the update mode is set to NoPartialUpdate, there is no difference + between this function and paintGL(), performing rendering in either of them + leads to the same result. + + Like paintUnderGL(), rendering in this function targets the default + framebuffer of the window, regardless of the update behavior. It gets called + after paintGL() has returned and the blit (PartialUpdateBlit) or quad drawing + (PartialUpdateBlend) has been done. + + \sa paintGL(), paintUnderGL(), UpdateBehavior + */ +void QOpenGLWindow::paintOverGL() +{ +} + +/*! + Paint \a event handler. Calls paintGL(). + + \sa paintGL() + */ +void QOpenGLWindow::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + paintGL(); +} + +/*! + Resize \a event handler. Calls resizeGL(). + + \sa resizeGL() + */ +void QOpenGLWindow::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event); + Q_D(QOpenGLWindow); + d->initialize(); + resizeGL(width(), height()); +} + +/*! + \internal + */ +int QOpenGLWindow::metric(PaintDeviceMetric metric) const +{ + Q_D(const QOpenGLWindow); + + switch (metric) { + case PdmDepth: + if (d->paintDevice) + return d->paintDevice->depth(); + break; + default: + break; + } + return QPaintDeviceWindow::metric(metric); +} + +/*! + \internal + */ +QPaintDevice *QOpenGLWindow::redirected(QPoint *) const +{ + Q_D(const QOpenGLWindow); + if (QOpenGLContext::currentContext() == d->context.data()) + return d->paintDevice.data(); + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/opengl/qopenglwindow.h b/src/opengl/qopenglwindow.h new file mode 100644 index 0000000000..926e4fabfd --- /dev/null +++ b/src/opengl/qopenglwindow.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLWINDOW_H +#define QOPENGLWINDOW_H + +#include <QtOpenGL/qtopenglglobal.h> + +#ifndef QT_NO_OPENGL + +#include <QtGui/QPaintDeviceWindow> +#include <QtGui/QOpenGLContext> +#include <QtGui/QImage> + +QT_BEGIN_NAMESPACE + +class QOpenGLWindowPrivate; + +class Q_OPENGL_EXPORT QOpenGLWindow : public QPaintDeviceWindow +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QOpenGLWindow) + +public: + enum UpdateBehavior { + NoPartialUpdate, + PartialUpdateBlit, + PartialUpdateBlend + }; + + explicit QOpenGLWindow(UpdateBehavior updateBehavior = NoPartialUpdate, QWindow *parent = nullptr); + explicit QOpenGLWindow(QOpenGLContext *shareContext, UpdateBehavior updateBehavior = NoPartialUpdate, QWindow *parent = nullptr); + ~QOpenGLWindow(); + + UpdateBehavior updateBehavior() const; + bool isValid() const; + + void makeCurrent(); + void doneCurrent(); + + QOpenGLContext *context() const; + QOpenGLContext *shareContext() const; + + GLuint defaultFramebufferObject() const; + + QImage grabFramebuffer(); + +Q_SIGNALS: + void frameSwapped(); + +protected: + virtual void initializeGL(); + virtual void resizeGL(int w, int h); + virtual void paintGL(); + virtual void paintUnderGL(); + virtual void paintOverGL(); + + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + int metric(PaintDeviceMetric metric) const override; + QPaintDevice *redirected(QPoint *) const override; + +private: + Q_DISABLE_COPY(QOpenGLWindow) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif |