| /**************************************************************************** |
| ** |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> |
| ** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtQuick 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 "qsgopenglvisualizer_p.h" |
| #include <qmath.h> |
| #include <private/qsgshadersourcebuilder_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| namespace QSGBatchRenderer |
| { |
| |
| #define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling()) |
| #define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling()) |
| #define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \ |
| | QSGNode::DirtyOpacity \ |
| | QSGNode::DirtyMatrix \ |
| | QSGNode::DirtyNodeRemoved) |
| |
| QMatrix4x4 qsg_matrixForRoot(Node *node); |
| |
| class VisualizeShader : public QOpenGLShaderProgram |
| { |
| public: |
| int color; |
| int matrix; |
| int rotation; |
| int pattern; |
| int projection; |
| }; |
| |
| OpenGLVisualizer::OpenGLVisualizer(Renderer *renderer) |
| : Visualizer(renderer), |
| m_funcs(QOpenGLContext::currentContext()->functions()), |
| m_visualizeProgram(nullptr) |
| { |
| } |
| |
| OpenGLVisualizer::~OpenGLVisualizer() |
| { |
| releaseResources(); |
| } |
| |
| void OpenGLVisualizer::releaseResources() |
| { |
| delete m_visualizeProgram; |
| m_visualizeProgram = nullptr; |
| } |
| |
| void OpenGLVisualizer::prepareVisualize() |
| { |
| // nothing to do here |
| } |
| |
| void OpenGLVisualizer::visualizeDrawGeometry(const QSGGeometry *g) |
| { |
| if (g->attributeCount() < 1) |
| return; |
| const QSGGeometry::Attribute *a = g->attributes(); |
| m_funcs->glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData()); |
| if (g->indexCount()) |
| m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); |
| else |
| m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount()); |
| |
| } |
| |
| void OpenGLVisualizer::visualizeBatch(Batch *b) |
| { |
| VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); |
| |
| if (b->positionAttribute != 0) |
| return; |
| |
| QSGGeometryNode *gn = b->first->node; |
| QSGGeometry *g = gn->geometry(); |
| const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute]; |
| |
| m_funcs->glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id); |
| |
| QMatrix4x4 matrix(m_renderer->m_current_projection_matrix); |
| if (b->root) |
| matrix = matrix * qsg_matrixForRoot(b->root); |
| |
| shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1)); |
| |
| QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0); |
| float cr = color.redF(); |
| float cg = color.greenF(); |
| float cb = color.blueF(); |
| shader->setUniformValue(shader->color, cr, cg, cb, 1.0); |
| |
| if (b->merged) { |
| shader->setUniformValue(shader->matrix, matrix); |
| const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data; |
| for (int ds=0; ds<b->drawSets.size(); ++ds) { |
| const DrawSet &set = b->drawSets.at(ds); |
| m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), |
| (void *) (qintptr) (set.vertices)); |
| m_funcs->glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, |
| (void *)(qintptr)(dataStart + set.indices)); |
| } |
| } else { |
| Element *e = b->first; |
| int offset = 0; |
| while (e) { |
| gn = e->node; |
| g = gn->geometry(); |
| shader->setUniformValue(shader->matrix, matrix * *gn->matrix()); |
| m_funcs->glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), |
| (void *) (qintptr) offset); |
| if (g->indexCount()) |
| m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); |
| else |
| m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount()); |
| offset += g->sizeOfVertex() * g->vertexCount(); |
| e = e->nextInBatch; |
| } |
| } |
| } |
| |
| void OpenGLVisualizer::visualizeClipping(QSGNode *node) |
| { |
| if (node->type() == QSGNode::ClipNodeType) { |
| VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); |
| QSGClipNode *clipNode = static_cast<QSGClipNode *>(node); |
| QMatrix4x4 matrix = m_renderer->m_current_projection_matrix; |
| if (clipNode->matrix()) |
| matrix = matrix * *clipNode->matrix(); |
| shader->setUniformValue(shader->matrix, matrix); |
| visualizeDrawGeometry(clipNode->geometry()); |
| } |
| |
| QSGNODE_TRAVERSE(node) { |
| visualizeClipping(child); |
| } |
| } |
| |
| void OpenGLVisualizer::visualizeChanges(Node *n) |
| { |
| |
| if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChangeSet.contains(n)) { |
| uint dirty = m_visualizeChangeSet.value(n); |
| bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0; |
| |
| VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); |
| QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0); |
| float ca = 0.5; |
| float cr = color.redF() * ca; |
| float cg = color.greenF() * ca; |
| float cb = color.blueF() * ca; |
| shader->setUniformValue(shader->color, cr, cg, cb, ca); |
| shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0)); |
| |
| QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); |
| |
| QMatrix4x4 matrix = m_renderer->m_current_projection_matrix; |
| if (n->element()->batch->root) |
| matrix = matrix * qsg_matrixForRoot(n->element()->batch->root); |
| matrix = matrix * *gn->matrix(); |
| shader->setUniformValue(shader->matrix, matrix); |
| visualizeDrawGeometry(gn->geometry()); |
| |
| // This is because many changes don't propegate their dirty state to the |
| // parent so the node updater will not unset these states. They are |
| // not used for anything so, unsetting it should have no side effects. |
| n->dirtyState = {}; |
| } |
| |
| SHADOWNODE_TRAVERSE(n) { |
| visualizeChanges(child); |
| } |
| } |
| |
| void OpenGLVisualizer::visualizeOverdraw_helper(Node *node) |
| { |
| if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) { |
| VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); |
| QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode); |
| |
| QMatrix4x4 matrix = m_renderer->m_current_projection_matrix; |
| matrix(2, 2) = m_renderer->m_zRange; |
| matrix(2, 3) = 1.0f - node->element()->order * m_renderer->m_zRange; |
| |
| if (node->element()->batch->root) |
| matrix = matrix * qsg_matrixForRoot(node->element()->batch->root); |
| matrix = matrix * *gn->matrix(); |
| shader->setUniformValue(shader->matrix, matrix); |
| |
| QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3); |
| float ca = 0.33f; |
| shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca); |
| |
| visualizeDrawGeometry(gn->geometry()); |
| } |
| |
| SHADOWNODE_TRAVERSE(node) { |
| visualizeOverdraw_helper(child); |
| } |
| } |
| |
| void OpenGLVisualizer::visualizeOverdraw() |
| { |
| VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); |
| shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f); |
| shader->setUniformValue(shader->projection, 1); |
| |
| m_funcs->glBlendFunc(GL_ONE, GL_ONE); |
| |
| static float step = 0; |
| step += static_cast<float>(M_PI * 2 / 1000.); |
| if (step > M_PI * 2) |
| step = 0; |
| float angle = 80.0 * std::sin(step); |
| |
| QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0); |
| QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1); |
| QMatrix4x4 tx; tx.translate(0, 0, 1); |
| |
| QMatrix4x4 m; |
| |
| // m.rotate(180, 0, 1, 0); |
| |
| m.translate(0, 0.5, 4); |
| m.scale(2, 2, 1); |
| |
| m.rotate(-30, 1, 0, 0); |
| m.rotate(angle, 0, 1, 0); |
| m.translate(0, 0, -1); |
| |
| shader->setUniformValue(shader->rotation, m); |
| |
| float box[] = { |
| // lower |
| -1, 1, 0, 1, 1, 0, |
| -1, 1, 0, -1, -1, 0, |
| 1, 1, 0, 1, -1, 0, |
| -1, -1, 0, 1, -1, 0, |
| |
| // upper |
| -1, 1, 1, 1, 1, 1, |
| -1, 1, 1, -1, -1, 1, |
| 1, 1, 1, 1, -1, 1, |
| -1, -1, 1, 1, -1, 1, |
| |
| // sides |
| -1, -1, 0, -1, -1, 1, |
| 1, -1, 0, 1, -1, 1, |
| -1, 1, 0, -1, 1, 1, |
| 1, 1, 0, 1, 1, 1 |
| }; |
| m_funcs->glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box); |
| m_funcs->glLineWidth(2); |
| m_funcs->glDrawArrays(GL_LINES, 0, 24); |
| |
| visualizeOverdraw_helper(m_renderer->m_nodes.value(m_renderer->rootNode())); |
| } |
| |
| void OpenGLVisualizer::visualize() |
| { |
| if (m_visualizeMode == VisualizeNothing) |
| return; |
| |
| if (!m_visualizeProgram) { |
| VisualizeShader *prog = new VisualizeShader(); |
| QSGShaderSourceBuilder::initializeProgramFromFiles( |
| prog, |
| QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"), |
| QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag")); |
| prog->bindAttributeLocation("v", 0); |
| prog->link(); |
| prog->bind(); |
| prog->color = prog->uniformLocation("color"); |
| prog->pattern = prog->uniformLocation("pattern"); |
| prog->projection = prog->uniformLocation("projection"); |
| prog->matrix = prog->uniformLocation("matrix"); |
| prog->rotation = prog->uniformLocation("rotation"); |
| m_visualizeProgram = prog; |
| } else { |
| m_visualizeProgram->bind(); |
| } |
| VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); |
| |
| m_funcs->glDisable(GL_DEPTH_TEST); |
| m_funcs->glEnable(GL_BLEND); |
| m_funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| m_funcs->glEnableVertexAttribArray(0); |
| |
| // Blacken out the actual rendered content... |
| float bgOpacity = 0.8f; |
| if (m_visualizeMode == VisualizeBatches) |
| bgOpacity = 1.0; |
| float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 }; |
| shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity); |
| shader->setUniformValue(shader->matrix, QMatrix4x4()); |
| shader->setUniformValue(shader->rotation, QMatrix4x4()); |
| shader->setUniformValue(shader->pattern, 0.0f); |
| shader->setUniformValue(shader->projection, false); |
| m_funcs->glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v); |
| m_funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| if (m_visualizeMode == VisualizeBatches) { |
| srand(0); // To force random colors to be roughly the same every time.. |
| for (int i = 0; i < m_renderer->m_opaqueBatches.size(); ++i) |
| visualizeBatch(m_renderer->m_opaqueBatches.at(i)); |
| for (int i = 0; i < m_renderer->m_alphaBatches.size(); ++i) |
| visualizeBatch(m_renderer->m_alphaBatches.at(i)); |
| } else if (m_visualizeMode == VisualizeClipping) { |
| shader->setUniformValue(shader->pattern, 0.5f); |
| shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f); |
| visualizeClipping(m_renderer->rootNode()); |
| } else if (m_visualizeMode == VisualizeChanges) { |
| visualizeChanges(m_renderer->m_nodes.value(m_renderer->rootNode())); |
| m_visualizeChangeSet.clear(); |
| } else if (m_visualizeMode == VisualizeOverdraw) { |
| visualizeOverdraw(); |
| } |
| |
| // Reset state back to defaults.. |
| m_funcs->glDisable(GL_BLEND); |
| m_funcs->glDisableVertexAttribArray(0); |
| shader->release(); |
| } |
| |
| } |
| |
| QT_END_NAMESPACE |