From 3e5aaffb763d143c55f2b90d4b01773bd9f2d388 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 8 Aug 2016 17:07:43 +0200 Subject: Re-enable QML plugin for QtQuick Software backend There needed to be a bit of re-plumbing but now it is again possible to use the software renderer with QtCharts in QtQuick 2. The declarativerendernode now can be other backends than OpenGL if needed, while also not spreading around uncessary references to OpenGL classes. Task-number: QTBUG-55193 Change-Id: I68a44c66c3bfc02f7cf808b21dc83979ed151b59 Reviewed-by: Miikka Heikkinen --- src/chartsqml2/chartsqml2.pro | 11 +- src/chartsqml2/declarativeabstractrendernode.h | 55 +++++ src/chartsqml2/declarativechart.cpp | 19 +- src/chartsqml2/declarativechartnode.cpp | 79 +++--- src/chartsqml2/declarativechartnode.h | 20 +- src/chartsqml2/declarativeopenglrendernode.cpp | 327 +++++++++++++++++++++++++ src/chartsqml2/declarativeopenglrendernode.h | 92 +++++++ src/chartsqml2/declarativerendernode.cpp | 322 ------------------------ src/chartsqml2/declarativerendernode.h | 85 ------- src/src.pro | 2 +- 10 files changed, 543 insertions(+), 469 deletions(-) create mode 100644 src/chartsqml2/declarativeabstractrendernode.h create mode 100644 src/chartsqml2/declarativeopenglrendernode.cpp create mode 100644 src/chartsqml2/declarativeopenglrendernode.h delete mode 100644 src/chartsqml2/declarativerendernode.cpp delete mode 100644 src/chartsqml2/declarativerendernode.h diff --git a/src/chartsqml2/chartsqml2.pro b/src/chartsqml2/chartsqml2.pro index 310eed87..57407e4d 100644 --- a/src/chartsqml2/chartsqml2.pro +++ b/src/chartsqml2/chartsqml2.pro @@ -25,7 +25,6 @@ SOURCES += \ declarativepolarchart.cpp \ declarativeboxplotseries.cpp \ declarativechartnode.cpp \ - declarativerendernode.cpp \ declarativecandlestickseries.cpp HEADERS += \ @@ -44,8 +43,14 @@ HEADERS += \ declarativepolarchart.h \ declarativeboxplotseries.h \ declarativechartnode.h \ - declarativerendernode.h \ - declarativecandlestickseries.h + declarativecandlestickseries.h \ + declarativeabstractrendernode.h + +contains(QT_CONFIG, opengl) { + SOURCES += declarativeopenglrendernode.cpp + HEADERS += declarativeopenglrendernode.h +} + load(qml_plugin) diff --git a/src/chartsqml2/declarativeabstractrendernode.h b/src/chartsqml2/declarativeabstractrendernode.h new file mode 100644 index 00000000..afc8c354 --- /dev/null +++ b/src/chartsqml2/declarativeabstractrendernode.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DECLARATIVEABSTRACTRENDERNODE_H +#define DECLARATIVEABSTRACTRENDERNODE_H + +#include +#include +#include +#include + +QT_CHARTS_BEGIN_NAMESPACE + +class DeclarativeAbstractRenderNode : public QSGRootNode +{ +public: + DeclarativeAbstractRenderNode() {} + + virtual void setTextureSize(const QSize &textureSize) = 0; + virtual QSize textureSize() const = 0; + virtual void setRect(const QRectF &rect) = 0; + virtual void setSeriesData(bool mapDirty, const GLXYDataMap &dataMap) = 0; + +}; + +QT_CHARTS_END_NAMESPACE + + +#endif // DECLARATIVEABSTRACTRENDERNODE_H diff --git a/src/chartsqml2/declarativechart.cpp b/src/chartsqml2/declarativechart.cpp index 66b47722..4dfc3d5d 100644 --- a/src/chartsqml2/declarativechart.cpp +++ b/src/chartsqml2/declarativechart.cpp @@ -38,7 +38,7 @@ #include "declarativecandlestickseries.h" #include "declarativescatterseries.h" #include "declarativechartnode.h" -#include "declarativerendernode.h" +#include "declarativeabstractrendernode.h" #include #include #include @@ -60,6 +60,7 @@ #include #include #include +#include QT_CHARTS_BEGIN_NAMESPACE @@ -521,16 +522,12 @@ QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdateP if (!node) { node = new DeclarativeChartNode(window()); - if (node->glRenderNode()) { - connect(window(), &QQuickWindow::beforeRendering, - node->glRenderNode(), &DeclarativeRenderNode::render); - } } const QRectF &bRect = boundingRect(); - // Update GL data - if (node->glRenderNode() && (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty())) { + // Update renderNode data + if (node->renderNode() && (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty())) { const QRectF &plotArea = m_chart->plotArea(); const QSizeF &chartAreaSize = m_chart->size(); @@ -546,11 +543,11 @@ QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdateP normalizedHeight * bRect.height()); const QSize &adjustedPlotSize = adjustedPlotArea.size().toSize(); - if (adjustedPlotSize != node->glRenderNode()->textureSize()) - node->glRenderNode()->setTextureSize(adjustedPlotSize); + if (adjustedPlotSize != node->renderNode()->textureSize()) + node->renderNode()->setTextureSize(adjustedPlotSize); - node->glRenderNode()->setRect(adjustedPlotArea); - node->glRenderNode()->setSeriesData(m_glXYDataManager->mapDirty(), + node->renderNode()->setRect(adjustedPlotArea); + node->renderNode()->setSeriesData(m_glXYDataManager->mapDirty(), m_glXYDataManager->dataMap()); // Clear dirty flags from original xy data diff --git a/src/chartsqml2/declarativechartnode.cpp b/src/chartsqml2/declarativechartnode.cpp index 3742b8c5..7019ed58 100644 --- a/src/chartsqml2/declarativechartnode.cpp +++ b/src/chartsqml2/declarativechartnode.cpp @@ -28,62 +28,67 @@ ****************************************************************************/ #include "declarativechartnode.h" -#include "declarativerendernode.h" -#include -#include -#include -#include -#include -#include +#include "declarativeabstractrendernode.h" + +#include +#include +#include + +#ifndef QT_NO_OPENGL +# include "declarativeopenglrendernode.h" +#endif QT_CHARTS_BEGIN_NAMESPACE // This node handles displaying of the chart itself DeclarativeChartNode::DeclarativeChartNode(QQuickWindow *window) : - QSGSimpleTextureNode(), - m_texture(0), + QSGRootNode(), m_window(window), - m_textureOptions(0), - m_textureSize(1, 1), - m_glRenderNode(0) + m_renderNode(nullptr), + m_imageNode(nullptr) { - // Our texture node must have a texture, so use a default one pixel texture - QImage dummyImage(QSize(1, 1), QImage::Format_ARGB32); - uchar *imageData = dummyImage.bits(); - imageData[0] = 0; - imageData[1] = 0; - imageData[2] = 0; - imageData[3] = 0; - QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions( - QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture); - m_texture = m_window->createTextureFromImage(dummyImage, defaultTextureOptions); + // Create a DeclarativeRenderNode for correct QtQuick Backend +#ifndef QT_NO_OPENGL + if (m_window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) + m_renderNode = new DeclarativeOpenGLRenderNode(m_window); +#endif - setTexture(m_texture); - setFiltering(QSGTexture::Linear); - - if (QOpenGLContext::currentContext()) { - // Create child node for rendering GL graphics - m_glRenderNode = new DeclarativeRenderNode(m_window); - m_glRenderNode->setFlag(OwnedByParent); - appendChildNode(m_glRenderNode); - m_glRenderNode->setRect(0, 0, 0, 0); // Hide child node by default + if (m_renderNode) { + m_renderNode->setFlag(OwnedByParent); + appendChildNode(m_renderNode); + m_renderNode->setRect(QRectF(0, 0, 0, 0)); // Hide child node by default } } DeclarativeChartNode::~DeclarativeChartNode() { - delete m_texture; } // Must be called on render thread and in context void DeclarativeChartNode::createTextureFromImage(const QImage &chartImage) { - if (chartImage.size() != m_textureSize) - m_textureSize = chartImage.size(); + static auto const defaultTextureOptions = QQuickWindow::CreateTextureOptions(QQuickWindow::TextureHasAlphaChannel | + QQuickWindow::TextureOwnsGLTexture); + + auto texture = m_window->createTextureFromImage(chartImage, defaultTextureOptions); + // Create Image node if needed + if (!m_imageNode) { + m_imageNode = m_window->createImageNode(); + m_imageNode->setFlag(OwnedByParent); + m_imageNode->setOwnsTexture(true); + prependChildNode(m_imageNode); + } + m_imageNode->setTexture(texture); + if (!m_rect.isEmpty()) + m_imageNode->setRect(m_rect); +} + +void DeclarativeChartNode::setRect(const QRectF &rect) +{ + m_rect = rect; - delete m_texture; - m_texture = m_window->createTextureFromImage(chartImage, m_textureOptions); - setTexture(m_texture); + if (m_imageNode) + m_imageNode->setRect(rect); } QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativechartnode.h b/src/chartsqml2/declarativechartnode.h index b718864c..c93307fc 100644 --- a/src/chartsqml2/declarativechartnode.h +++ b/src/chartsqml2/declarativechartnode.h @@ -31,29 +31,29 @@ #define DECLARATIVECHARTNODE_P_H #include -#include +#include #include -#include +#include QT_CHARTS_BEGIN_NAMESPACE -class DeclarativeRenderNode; - -class DeclarativeChartNode : public QSGSimpleTextureNode, QOpenGLFunctions +class DeclarativeAbstractRenderNode; +class DeclarativeChartNode : public QSGRootNode { public: DeclarativeChartNode(QQuickWindow *window); ~DeclarativeChartNode(); void createTextureFromImage(const QImage &chartImage); - DeclarativeRenderNode *glRenderNode() const { return m_glRenderNode; } + DeclarativeAbstractRenderNode *renderNode() const { return m_renderNode; } + + void setRect(const QRectF &rect); private: - QSGTexture *m_texture; + QRectF m_rect; QQuickWindow *m_window; - QQuickWindow::CreateTextureOptions m_textureOptions; - QSize m_textureSize; - DeclarativeRenderNode *m_glRenderNode; + DeclarativeAbstractRenderNode *m_renderNode; + QSGImageNode *m_imageNode; }; QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativeopenglrendernode.cpp b/src/chartsqml2/declarativeopenglrendernode.cpp new file mode 100644 index 00000000..0799e36c --- /dev/null +++ b/src/chartsqml2/declarativeopenglrendernode.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "declarativeopenglrendernode.h" + +#include +#include +#include +#include +#include +#include + +//#define QDEBUG_TRACE_GL_FPS +#ifdef QDEBUG_TRACE_GL_FPS +# include +#endif + +QT_CHARTS_BEGIN_NAMESPACE + +// This node draws the xy series data on a transparent background using OpenGL. +// It is used as a child node of the chart node. +DeclarativeOpenGLRenderNode::DeclarativeOpenGLRenderNode(QQuickWindow *window) : + QObject(), + m_texture(0), + m_imageNode(nullptr), + m_window(window), + m_textureOptions(QQuickWindow::TextureHasAlphaChannel), + m_textureSize(1, 1), + m_recreateFbo(false), + m_fbo(0), + m_program(0), + m_shaderAttribLoc(-1), + m_colorUniformLoc(-1), + m_minUniformLoc(-1), + m_deltaUniformLoc(-1), + m_pointSizeUniformLoc(-1), + m_renderNeeded(true) +{ + initializeOpenGLFunctions(); + + connect(m_window, &QQuickWindow::beforeRendering, + this, &DeclarativeOpenGLRenderNode::render); +} + +DeclarativeOpenGLRenderNode::~DeclarativeOpenGLRenderNode() +{ + delete m_texture; + delete m_fbo; + + delete m_program; + m_program = 0; + + cleanXYSeriesResources(0); +} + +static const char *vertexSource = + "attribute highp vec2 points;\n" + "uniform highp vec2 min;\n" + "uniform highp vec2 delta;\n" + "uniform highp float pointSize;\n" + "uniform highp mat4 matrix;\n" + "void main() {\n" + " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n" + " gl_Position = matrix * vec4(normalPoint, 0, 1);\n" + " gl_PointSize = pointSize;\n" + "}"; +static const char *fragmentSource = + "uniform highp vec3 color;\n" + "void main() {\n" + " gl_FragColor = vec4(color,1);\n" + "}\n"; + +// Must be called on render thread and in context +void DeclarativeOpenGLRenderNode::initGL() +{ + recreateFBO(); + + m_program = new QOpenGLShaderProgram; + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource); + m_program->bindAttributeLocation("points", 0); + m_program->link(); + + m_program->bind(); + m_colorUniformLoc = m_program->uniformLocation("color"); + m_minUniformLoc = m_program->uniformLocation("min"); + m_deltaUniformLoc = m_program->uniformLocation("delta"); + m_pointSizeUniformLoc = m_program->uniformLocation("pointSize"); + m_matrixUniformLoc = m_program->uniformLocation("matrix"); + + // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x + // implementations this is optional and support may not be present + // at all. Nonetheless the below code works in all cases and makes + // sure there is a VAO when one is needed. + m_vao.create(); + QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); + +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) { + // Make it possible to change point primitive size and use textures with them in + // the shaders. These are implicitly enabled in ES2. + // Qt Quick doesn't change these flags, so it should be safe to just enable them + // at initialization. + glEnable(GL_PROGRAM_POINT_SIZE); + } +#endif + + m_program->release(); +} + +void DeclarativeOpenGLRenderNode::recreateFBO() +{ + QOpenGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment); + delete m_fbo; + m_fbo = new QOpenGLFramebufferObject(m_textureSize.width(), + m_textureSize.height(), + fboFormat); + + delete m_texture; + m_texture = m_window->createTextureFromId(m_fbo->texture(), m_textureSize, m_textureOptions); + if (!m_imageNode) { + m_imageNode = m_window->createImageNode(); + m_imageNode->setFiltering(QSGTexture::Linear); + m_imageNode->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically); + m_imageNode->setFlag(OwnedByParent); + if (!m_rect.isEmpty()) + m_imageNode->setRect(m_rect); + appendChildNode(m_imageNode); + } + m_imageNode->setTexture(m_texture); + + m_recreateFbo = false; +} + +// Must be called on render thread and in context +void DeclarativeOpenGLRenderNode::setTextureSize(const QSize &size) +{ + m_textureSize = size; + m_recreateFbo = true; + m_renderNeeded = true; +} + +// Must be called on render thread while gui thread is blocked, and in context +void DeclarativeOpenGLRenderNode::setSeriesData(bool mapDirty, const GLXYDataMap &dataMap) +{ + if (mapDirty) { + // Series have changed, recreate map, but utilize old data where feasible + GLXYDataMap oldMap = m_xyDataMap; + m_xyDataMap.clear(); + + GLXYDataMapIterator i(dataMap); + while (i.hasNext()) { + i.next(); + GLXYSeriesData *data = oldMap.take(i.key()); + const GLXYSeriesData *newData = i.value(); + if (!data || newData->dirty) { + data = new GLXYSeriesData; + *data = *newData; + } + m_xyDataMap.insert(i.key(), data); + } + // Delete remaining old data + i = oldMap; + while (i.hasNext()) { + i.next(); + delete i.value(); + cleanXYSeriesResources(i.key()); + } + } else { + // Series have not changed, so just copy dirty data over + GLXYDataMapIterator i(dataMap); + while (i.hasNext()) { + i.next(); + const GLXYSeriesData *newData = i.value(); + if (i.value()->dirty) { + GLXYSeriesData *data = m_xyDataMap.value(i.key()); + if (data) + *data = *newData; + } + } + } + markDirty(DirtyMaterial); + m_renderNeeded = true; +} + +void DeclarativeOpenGLRenderNode::setRect(const QRectF &rect) +{ + m_rect = rect; + + if (m_imageNode) + m_imageNode->setRect(rect); +} + +void DeclarativeOpenGLRenderNode::renderGL() +{ + glClearColor(0, 0, 0, 0); + + QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); + m_program->bind(); + m_fbo->bind(); + + glClear(GL_COLOR_BUFFER_BIT); + glEnableVertexAttribArray(0); + + glViewport(0, 0, m_textureSize.width(), m_textureSize.height()); + + GLXYDataMapIterator i(m_xyDataMap); + while (i.hasNext()) { + i.next(); + QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key()); + GLXYSeriesData *data = i.value(); + + m_program->setUniformValue(m_colorUniformLoc, data->color); + m_program->setUniformValue(m_minUniformLoc, data->min); + m_program->setUniformValue(m_deltaUniformLoc, data->delta); + m_program->setUniformValue(m_matrixUniformLoc, data->matrix); + + if (!vbo) { + vbo = new QOpenGLBuffer; + m_seriesBufferMap.insert(i.key(), vbo); + vbo->create(); + } + vbo->bind(); + if (data->dirty) { + vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); + data->dirty = false; + } + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + if (data->type == QAbstractSeries::SeriesTypeLine) { + glLineWidth(data->width); + glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); + } else { // Scatter + m_program->setUniformValue(m_pointSizeUniformLoc, data->width); + glDrawArrays(GL_POINTS, 0, data->array.size() / 2); + } + vbo->release(); + } + +#ifdef QDEBUG_TRACE_GL_FPS + static QElapsedTimer stopWatch; + static int frameCount = -1; + if (frameCount == -1) { + stopWatch.start(); + frameCount = 0; + } + frameCount++; + int elapsed = stopWatch.elapsed(); + if (elapsed >= 1000) { + elapsed = stopWatch.restart(); + qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed)))); + qDebug() << "FPS:" << fps; + frameCount = 0; + } +#endif + + markDirty(DirtyMaterial); + m_window->resetOpenGLState(); +} + +// Must be called on render thread as response to beforeRendering signal +void DeclarativeOpenGLRenderNode::render() +{ + if (m_renderNeeded) { + if (m_xyDataMap.size()) { + if (!m_program) + initGL(); + if (m_recreateFbo) + recreateFBO(); + renderGL(); + } else { + if (m_imageNode && m_imageNode->rect() != QRectF()) { + glClearColor(0, 0, 0, 0); + m_fbo->bind(); + glClear(GL_COLOR_BUFFER_BIT); + + // If last series was removed, zero out the node rect + setRect(QRectF()); + } + } + m_renderNeeded = false; + } +} + +void DeclarativeOpenGLRenderNode::cleanXYSeriesResources(const QXYSeries *series) +{ + if (series) { + delete m_seriesBufferMap.take(series); + delete m_xyDataMap.take(series); + } else { + foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values()) + delete buffer; + m_seriesBufferMap.clear(); + foreach (GLXYSeriesData *data, m_xyDataMap.values()) + delete data; + m_xyDataMap.clear(); + } +} + +QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativeopenglrendernode.h b/src/chartsqml2/declarativeopenglrendernode.h new file mode 100644 index 00000000..09ce64c6 --- /dev/null +++ b/src/chartsqml2/declarativeopenglrendernode.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) 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.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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DECLARATIVEOPENGLRENDERNODE_P_H +#define DECLARATIVEOPENGLRENDERNODE_P_H + +#include "declarativeabstractrendernode.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_CHARTS_BEGIN_NAMESPACE + +class DeclarativeOpenGLRenderNode : public QObject, public DeclarativeAbstractRenderNode, QOpenGLFunctions +{ + Q_OBJECT +public: + DeclarativeOpenGLRenderNode(QQuickWindow *window); + ~DeclarativeOpenGLRenderNode(); + + void initGL(); + QSize textureSize() const override { return m_textureSize; } + void setTextureSize(const QSize &size) override; + + void setSeriesData(bool mapDirty, const GLXYDataMap &dataMap) override; + void setRect(const QRectF &rect) override; + +public Q_SLOTS: + void render(); + +private: + void renderGL(); + void recreateFBO(); + void cleanXYSeriesResources(const QXYSeries *series); + + QSGTexture *m_texture; + QSGImageNode *m_imageNode; + QQuickWindow *m_window; + QQuickWindow::CreateTextureOptions m_textureOptions; + QSize m_textureSize; + bool m_recreateFbo; + GLXYDataMap m_xyDataMap; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; + int m_shaderAttribLoc; + int m_colorUniformLoc; + int m_minUniformLoc; + int m_deltaUniformLoc; + int m_pointSizeUniformLoc; + int m_matrixUniformLoc; + QOpenGLVertexArrayObject m_vao; + QHash m_seriesBufferMap; + bool m_renderNeeded; + QRectF m_rect; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // DECLARATIVEOPENGLRENDERNODE_P_H diff --git a/src/chartsqml2/declarativerendernode.cpp b/src/chartsqml2/declarativerendernode.cpp deleted file mode 100644 index c11f6b00..00000000 --- a/src/chartsqml2/declarativerendernode.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Charts module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 or (at your option) 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.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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "declarativerendernode.h" - -#include -#include -#include -#include -#include -#include - -//#define QDEBUG_TRACE_GL_FPS -#ifdef QDEBUG_TRACE_GL_FPS -# include -#endif - -QT_CHARTS_BEGIN_NAMESPACE - -// This node draws the xy series data on a transparent background using OpenGL. -// It is used as a child node of the chart node. -DeclarativeRenderNode::DeclarativeRenderNode(QQuickWindow *window) : - QObject(), - QSGSimpleTextureNode(), - m_texture(0), - m_window(window), - m_textureOptions(QQuickWindow::TextureHasAlphaChannel), - m_textureSize(1, 1), - m_recreateFbo(false), - m_fbo(0), - m_program(0), - m_shaderAttribLoc(-1), - m_colorUniformLoc(-1), - m_minUniformLoc(-1), - m_deltaUniformLoc(-1), - m_pointSizeUniformLoc(-1), - m_renderNeeded(true) -{ - initializeOpenGLFunctions(); - - // Our texture node must have a texture, so use a default one pixel texture - GLuint defaultTexture = 0; - glGenTextures(1, &defaultTexture); - glBindTexture(GL_TEXTURE_2D, defaultTexture); - uchar buf[4] = { 0, 0, 0, 0 }; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf); - - QQuickWindow::CreateTextureOptions defaultTextureOptions = QQuickWindow::CreateTextureOptions( - QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture); - m_texture = m_window->createTextureFromId(defaultTexture, QSize(1, 1), defaultTextureOptions); - - setTexture(m_texture); - setFiltering(QSGTexture::Linear); - setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically); -} - -DeclarativeRenderNode::~DeclarativeRenderNode() -{ - delete m_texture; - delete m_fbo; - - delete m_program; - m_program = 0; - - cleanXYSeriesResources(0); -} - -static const char *vertexSource = - "attribute highp vec2 points;\n" - "uniform highp vec2 min;\n" - "uniform highp vec2 delta;\n" - "uniform highp float pointSize;\n" - "uniform highp mat4 matrix;\n" - "void main() {\n" - " vec2 normalPoint = vec2(-1, -1) + ((points - min) / delta);\n" - " gl_Position = matrix * vec4(normalPoint, 0, 1);\n" - " gl_PointSize = pointSize;\n" - "}"; -static const char *fragmentSource = - "uniform highp vec3 color;\n" - "void main() {\n" - " gl_FragColor = vec4(color,1);\n" - "}\n"; - -// Must be called on render thread and in context -void DeclarativeRenderNode::initGL() -{ - recreateFBO(); - - m_program = new QOpenGLShaderProgram; - m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource); - m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentSource); - m_program->bindAttributeLocation("points", 0); - m_program->link(); - - m_program->bind(); - m_colorUniformLoc = m_program->uniformLocation("color"); - m_minUniformLoc = m_program->uniformLocation("min"); - m_deltaUniformLoc = m_program->uniformLocation("delta"); - m_pointSizeUniformLoc = m_program->uniformLocation("pointSize"); - m_matrixUniformLoc = m_program->uniformLocation("matrix"); - - // Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x - // implementations this is optional and support may not be present - // at all. Nonetheless the below code works in all cases and makes - // sure there is a VAO when one is needed. - m_vao.create(); - QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); - -#if !defined(QT_OPENGL_ES_2) - if (!QOpenGLContext::currentContext()->isOpenGLES()) { - // Make it possible to change point primitive size and use textures with them in - // the shaders. These are implicitly enabled in ES2. - // Qt Quick doesn't change these flags, so it should be safe to just enable them - // at initialization. - glEnable(GL_PROGRAM_POINT_SIZE); - } -#endif - - m_program->release(); -} - -void DeclarativeRenderNode::recreateFBO() -{ - QOpenGLFramebufferObjectFormat fboFormat; - fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment); - delete m_fbo; - m_fbo = new QOpenGLFramebufferObject(m_textureSize.width(), - m_textureSize.height(), - fboFormat); - - delete m_texture; - m_texture = m_window->createTextureFromId(m_fbo->texture(), m_textureSize, m_textureOptions); - setTexture(m_texture); - - m_recreateFbo = false; -} - -// Must be called on render thread and in context -void DeclarativeRenderNode::setTextureSize(const QSize &size) -{ - m_textureSize = size; - m_recreateFbo = true; - m_renderNeeded = true; -} - -// Must be called on render thread while gui thread is blocked, and in context -void DeclarativeRenderNode::setSeriesData(bool mapDirty, const GLXYDataMap &dataMap) -{ - if (mapDirty) { - // Series have changed, recreate map, but utilize old data where feasible - GLXYDataMap oldMap = m_xyDataMap; - m_xyDataMap.clear(); - - GLXYDataMapIterator i(dataMap); - while (i.hasNext()) { - i.next(); - GLXYSeriesData *data = oldMap.take(i.key()); - const GLXYSeriesData *newData = i.value(); - if (!data || newData->dirty) { - data = new GLXYSeriesData; - *data = *newData; - } - m_xyDataMap.insert(i.key(), data); - } - // Delete remaining old data - i = oldMap; - while (i.hasNext()) { - i.next(); - delete i.value(); - cleanXYSeriesResources(i.key()); - } - } else { - // Series have not changed, so just copy dirty data over - GLXYDataMapIterator i(dataMap); - while (i.hasNext()) { - i.next(); - const GLXYSeriesData *newData = i.value(); - if (i.value()->dirty) { - GLXYSeriesData *data = m_xyDataMap.value(i.key()); - if (data) - *data = *newData; - } - } - } - markDirty(DirtyMaterial); - m_renderNeeded = true; -} - -void DeclarativeRenderNode::renderGL() -{ - glClearColor(0, 0, 0, 0); - - QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); - m_program->bind(); - m_fbo->bind(); - - glClear(GL_COLOR_BUFFER_BIT); - glEnableVertexAttribArray(0); - - glViewport(0, 0, m_textureSize.width(), m_textureSize.height()); - - GLXYDataMapIterator i(m_xyDataMap); - while (i.hasNext()) { - i.next(); - QOpenGLBuffer *vbo = m_seriesBufferMap.value(i.key()); - GLXYSeriesData *data = i.value(); - - m_program->setUniformValue(m_colorUniformLoc, data->color); - m_program->setUniformValue(m_minUniformLoc, data->min); - m_program->setUniformValue(m_deltaUniformLoc, data->delta); - m_program->setUniformValue(m_matrixUniformLoc, data->matrix); - - if (!vbo) { - vbo = new QOpenGLBuffer; - m_seriesBufferMap.insert(i.key(), vbo); - vbo->create(); - } - vbo->bind(); - if (data->dirty) { - vbo->allocate(data->array.constData(), data->array.count() * sizeof(GLfloat)); - data->dirty = false; - } - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); - if (data->type == QAbstractSeries::SeriesTypeLine) { - glLineWidth(data->width); - glDrawArrays(GL_LINE_STRIP, 0, data->array.size() / 2); - } else { // Scatter - m_program->setUniformValue(m_pointSizeUniformLoc, data->width); - glDrawArrays(GL_POINTS, 0, data->array.size() / 2); - } - vbo->release(); - } - -#ifdef QDEBUG_TRACE_GL_FPS - static QElapsedTimer stopWatch; - static int frameCount = -1; - if (frameCount == -1) { - stopWatch.start(); - frameCount = 0; - } - frameCount++; - int elapsed = stopWatch.elapsed(); - if (elapsed >= 1000) { - elapsed = stopWatch.restart(); - qreal fps = qreal(0.1 * int(10000.0 * (qreal(frameCount) / qreal(elapsed)))); - qDebug() << "FPS:" << fps; - frameCount = 0; - } -#endif - - markDirty(DirtyMaterial); - m_window->resetOpenGLState(); -} - -// Must be called on render thread as response to beforeRendering signal -void DeclarativeRenderNode::render() -{ - if (m_renderNeeded) { - if (m_xyDataMap.size()) { - if (!m_program) - initGL(); - if (m_recreateFbo) - recreateFBO(); - renderGL(); - } else { - if (rect() != QRectF()) { - glClearColor(0, 0, 0, 0); - m_fbo->bind(); - glClear(GL_COLOR_BUFFER_BIT); - - // If last series was removed, zero out the node rect - setRect(QRectF()); - } - } - m_renderNeeded = false; - } -} - -void DeclarativeRenderNode::cleanXYSeriesResources(const QXYSeries *series) -{ - if (series) { - delete m_seriesBufferMap.take(series); - delete m_xyDataMap.take(series); - } else { - foreach (QOpenGLBuffer *buffer, m_seriesBufferMap.values()) - delete buffer; - m_seriesBufferMap.clear(); - foreach (GLXYSeriesData *data, m_xyDataMap.values()) - delete data; - m_xyDataMap.clear(); - } -} - -QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativerendernode.h b/src/chartsqml2/declarativerendernode.h deleted file mode 100644 index b21fa20b..00000000 --- a/src/chartsqml2/declarativerendernode.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Charts module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 or (at your option) 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.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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef DECLARATIVERENDERNODE_P_H -#define DECLARATIVERENDERNODE_P_H - -#include -#include -#include -#include -#include -#include -#include -#include - -QT_CHARTS_BEGIN_NAMESPACE - -class DeclarativeRenderNode : public QObject, public QSGSimpleTextureNode, QOpenGLFunctions -{ - Q_OBJECT -public: - DeclarativeRenderNode(QQuickWindow *window); - ~DeclarativeRenderNode(); - - void initGL(); - QSize textureSize() const { return m_textureSize; } - void setTextureSize(const QSize &size); - void setSeriesData(bool mapDirty, const GLXYDataMap &dataMap); - -public Q_SLOTS: - void render(); - -private: - void renderGL(); - void recreateFBO(); - void cleanXYSeriesResources(const QXYSeries *series); - - QSGTexture *m_texture; - QQuickWindow *m_window; - QQuickWindow::CreateTextureOptions m_textureOptions; - QSize m_textureSize; - bool m_recreateFbo; - GLXYDataMap m_xyDataMap; - QOpenGLFramebufferObject *m_fbo; - QOpenGLShaderProgram *m_program; - int m_shaderAttribLoc; - int m_colorUniformLoc; - int m_minUniformLoc; - int m_deltaUniformLoc; - int m_pointSizeUniformLoc; - int m_matrixUniformLoc; - QOpenGLVertexArrayObject m_vao; - QHash m_seriesBufferMap; - bool m_renderNeeded; -}; - -QT_CHARTS_END_NAMESPACE - -#endif // DECLARATIVERENDERNODE_P_H diff --git a/src/src.pro b/src/src.pro index af3dcd3b..507c9f56 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs CONFIG += ordered SUBDIRS += charts -qtHaveModule(quick):contains(QT_CONFIG, opengl) { +qtHaveModule(quick) { SUBDIRS += chartsqml2 } -- cgit v1.2.3