/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2016 Jolla Ltd, author: ** Copyright (C) 2016 Robin Burchell ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:COMM$ ** ** 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. ** ** $QT_END_LICENSE$ ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ****************************************************************************/ #include "qsgopenglvisualizer_p.h" #include #include 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(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; dsdrawSets.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(m_visualizeProgram); QSGClipNode *clipNode = static_cast(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(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(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(m_visualizeProgram); QSGGeometryNode *gn = static_cast(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(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(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(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