diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp | 527 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h | 119 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsggeometry.cpp | 466 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsggeometry.h | 294 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsggeometry_p.h | 73 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgmaterial.cpp | 535 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgmaterial.h | 143 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgnode.cpp | 1264 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgnode.h | 346 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgnodeupdater.cpp | 287 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgnodeupdater_p.h | 86 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgrenderer.cpp | 743 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgrenderer_p.h | 232 |
13 files changed, 5115 insertions, 0 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp new file mode 100644 index 0000000000..56a6e0e5d9 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#define GL_GLEXT_PROTOTYPES + +#include "qsgdefaultrenderer_p.h" +#include "qsgmaterial.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qguiapplication.h> +#include <QtCore/qpair.h> +#include <QtCore/QElapsedTimer> + +//#define FORCE_NO_REORDER + +// #define RENDERER_DEBUG +#ifdef RENDERER_DEBUG +#define DEBUG_THRESHOLD 0 +QElapsedTimer debugTimer; +int materialChanges; +int geometryNodesDrawn; +#endif + +QT_BEGIN_NAMESPACE + +static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b) +{ + // Sort by clip... + if (a->clipList() != b->clipList()) + return a->clipList() < b->clipList(); + + // Sort by material definition + QSGMaterialType *aDef = a->material()->type(); + QSGMaterialType *bDef = b->material()->type(); + + if (aDef != bDef) + return aDef < bDef; + + // Sort by material state + int cmp = a->material()->compare(b->material()); + if (cmp != 0) + return cmp < 0; + + return a->matrix() < b->matrix(); +} + +static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b) +{ + // Sort by clip... + if (a->clipList() != b->clipList()) + return a->clipList() < b->clipList(); + + // Sort by material definition + QSGMaterialType *aDef = a->material()->type(); + QSGMaterialType *bDef = b->material()->type(); + + if (!(a->material()->flags() & QSGMaterial::Blending)) { + int aOrder = a->renderOrder(); + int bOrder = b->renderOrder(); + if (aOrder != bOrder) + return aOrder > bOrder; + } + + if (aDef != bDef) + return aDef < bDef; + + // Sort by material state + int cmp = a->material()->compare(b->material()); + if (cmp != 0) + return cmp < 0; + + return a->matrix() < b->matrix(); +} + + +QSGDefaultRenderer::IndexGeometryNodePair::IndexGeometryNodePair(int i, QSGGeometryNode *node) + : QPair<int, QSGGeometryNode *>(i, node) +{ +} + +bool QSGDefaultRenderer::IndexGeometryNodePair::operator < (const QSGDefaultRenderer::IndexGeometryNodePair &other) const +{ + return nodeLessThan(second, other.second); +} + + +QSGDefaultRenderer::IndexGeometryNodePairHeap::IndexGeometryNodePairHeap() + : v(64) +{ +} + +void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRenderer::IndexGeometryNodePair &x) +{ + int i = v.size(); + v.add(x); + while (i != 0 && v.at(i) < v.at(parent(i))) { + qSwap(v.at(parent(i)), v.at(i)); + i = parent(i); + } +} + +QSGDefaultRenderer::IndexGeometryNodePair QSGDefaultRenderer::IndexGeometryNodePairHeap::pop() +{ + IndexGeometryNodePair x = top(); + if (v.size() > 1) + qSwap(v.first(), v.last()); + v.pop_back(); + int i = 0; + while (left(i) < v.size()) { + int low = left(i); + if (right(i) < v.size() && v.at(right(i)) < v.at(low)) + low = right(i); + if (!(v.at(low) < v.at(i))) + break; + qSwap(v.at(i), v.at(low)); + i = low; + } + return x; +} + + +QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context) + : QSGRenderer(context) + , m_opaqueNodes(64) + , m_transparentNodes(64) + , m_tempNodes(64) + , m_rebuild_lists(false) + , m_needs_sorting(false) + , m_sort_front_to_back(false) + , m_currentRenderOrder(1) +{ + QStringList args = qApp->arguments(); +#if defined(QML_RUNTIME_TESTING) + m_render_opaque_nodes = !args.contains(QLatin1String("--no-opaque-nodes")); + m_render_alpha_nodes = !args.contains(QLatin1String("--no-alpha-nodes")); +#endif +} + +void QSGDefaultRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +{ + QSGRenderer::nodeChanged(node, flags); + + quint32 rebuildFlags = QSGNode::DirtyNodeAdded | QSGNode::DirtyNodeRemoved + | QSGNode::DirtyMaterial | QSGNode::DirtyOpacity + | QSGNode::DirtyForceUpdate; + + if (flags & rebuildFlags) + m_rebuild_lists = true; + + if (flags & (rebuildFlags | QSGNode::DirtyClipList)) + m_needs_sorting = true; +} + +void QSGDefaultRenderer::render() +{ +#if defined (QML_RUNTIME_TESTING) + static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); + if (dumpTree) { + printf("\n\n"); + QSGNodeDumper::dump(rootNode()); + } +#endif + +#ifdef RENDERER_DEBUG + debugTimer.invalidate(); + debugTimer.start(); + geometryNodesDrawn = 0; + materialChanges = 0; +#endif + + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + + glFrontFace(isMirrored() ? GL_CW : GL_CCW); + glDisable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + glDepthFunc(GL_GREATER); +#if defined(QT_OPENGL_ES) + glClearDepthf(0); +#else + glClearDepth(0); +#endif + + glDisable(GL_SCISSOR_TEST); + glClearColor(m_clear_color.redF(), m_clear_color.greenF(), m_clear_color.blueF(), m_clear_color.alphaF()); + +#ifdef RENDERER_DEBUG + int debugtimeSetup = debugTimer.elapsed(); +#endif + + bindable()->clear(clearMode()); + +#ifdef RENDERER_DEBUG + int debugtimeClear = debugTimer.elapsed(); +#endif + + QRect r = viewportRect(); + glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height()); + m_current_projection_matrix = projectionMatrix(); + m_current_model_view_matrix.setToIdentity(); + + m_currentClip = 0; + glDisable(GL_STENCIL_TEST); + + m_currentMaterial = 0; + m_currentProgram = 0; + m_currentMatrix = 0; + + if (m_rebuild_lists) { + m_opaqueNodes.reset(); + m_transparentNodes.reset(); + m_currentRenderOrder = 1; + buildLists(rootNode()); + m_rebuild_lists = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeLists = debugTimer.elapsed(); +#endif + + + if (m_needs_sorting) { + if (!m_opaqueNodes.isEmpty()) { + qSort(&m_opaqueNodes.first(), &m_opaqueNodes.first() + m_opaqueNodes.size(), + m_sort_front_to_back + ? nodeLessThanWithRenderOrder + : nodeLessThan); + } + m_needs_sorting = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeSorting = debugTimer.elapsed(); +#endif + + m_renderOrderMatrix.setToIdentity(); + m_renderOrderMatrix.scale(1, 1, qreal(1) / m_currentRenderOrder); + + glDisable(GL_BLEND); + glDepthMask(true); +#ifdef QML_RUNTIME_TESTING + if (m_render_opaque_nodes) +#endif + { +#if defined (QML_RUNTIME_TESTING) + if (dumpTree) + qDebug() << "Opaque Nodes:"; +#endif + renderNodes(m_opaqueNodes); + } + +#ifdef RENDERER_DEBUG + int debugtimeOpaque = debugTimer.elapsed(); + int opaqueNodes = geometryNodesDrawn; + int opaqueMaterialChanges = materialChanges; +#endif + + glEnable(GL_BLEND); + glDepthMask(false); +#ifdef QML_RUNTIME_TESTING + if (m_render_alpha_nodes) +#endif + { +#if defined (QML_RUNTIME_TESTING) + if (dumpTree) + qDebug() << "Alpha Nodes:"; +#endif + renderNodes(m_transparentNodes); + } + +#ifdef RENDERER_DEBUG + int debugtimeAlpha = debugTimer.elapsed(); +#endif + + + if (m_currentProgram) + m_currentProgram->deactivate(); + +#ifdef RENDERER_DEBUG + if (debugTimer.elapsed() > DEBUG_THRESHOLD) { + printf(" --- Renderer breakdown:\n" + " - setup=%d, clear=%d, building=%d, sorting=%d, opaque=%d, alpha=%d\n" + " - material changes: opaque=%d, alpha=%d, total=%d\n" + " - geometry ndoes: opaque=%d, alpha=%d, total=%d\n", + debugtimeSetup, + debugtimeClear - debugtimeSetup, + debugtimeLists - debugtimeClear, + debugtimeSorting - debugtimeLists, + debugtimeOpaque - debugtimeSorting, + debugtimeAlpha - debugtimeOpaque, + opaqueMaterialChanges, materialChanges - opaqueMaterialChanges, materialChanges, + opaqueNodes, geometryNodesDrawn - opaqueNodes, geometryNodesDrawn); + } +#endif + +} + +void QSGDefaultRenderer::setSortFrontToBackEnabled(bool sort) +{ + printf("setting sorting to... %d\n", sort); + m_sort_front_to_back = sort; +} + +bool QSGDefaultRenderer::isSortFrontToBackEnabled() const +{ + return m_sort_front_to_back; +} + +void QSGDefaultRenderer::buildLists(QSGNode *node) +{ + if (node->isSubtreeBlocked()) + return; + + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *geomNode = static_cast<QSGGeometryNode *>(node); + qreal opacity = geomNode->inheritedOpacity(); + QSGMaterial *m = geomNode->activeMaterial(); + +#ifdef FORCE_NO_REORDER + if (true) { +#else + if ((m->flags() & QSGMaterial::Blending) || opacity < 1) { +#endif + geomNode->setRenderOrder(m_currentRenderOrder - 1); + m_transparentNodes.add(geomNode); + } else { + geomNode->setRenderOrder(m_currentRenderOrder); + m_opaqueNodes.add(geomNode); + m_currentRenderOrder += 2; + } + } + + if (!node->firstChild()) + return; + +#ifdef FORCE_NO_REORDER + static bool reorder = false; +#else + static bool reorder = qApp->arguments().contains(QLatin1String("--reorder")); +#endif + + if (reorder && node->firstChild() != node->lastChild() && (node->flags() & QSGNode::ChildrenDoNotOverlap)) { + QVarLengthArray<int, 16> beginIndices; + QVarLengthArray<int, 16> endIndices; + int baseCount = m_transparentNodes.size(); + int count = 0; + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) { + beginIndices.append(m_transparentNodes.size()); + buildLists(c); + endIndices.append(m_transparentNodes.size()); + ++count; + } + + int childNodeCount = m_transparentNodes.size() - baseCount; + if (childNodeCount) { + m_tempNodes.reset(); + m_tempNodes.reserve(childNodeCount); + while (childNodeCount) { + for (int i = 0; i < count; ++i) { + if (beginIndices[i] != endIndices[i]) + m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++))); + } + while (!m_heap.isEmpty()) { + IndexGeometryNodePair pair = m_heap.pop(); + m_tempNodes.add(pair.second); + --childNodeCount; + int i = pair.first; + if (beginIndices[i] != endIndices[i] && !nodeLessThan(m_transparentNodes.at(beginIndices[i]), pair.second)) + m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++))); + } + } + Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount); + + qMemCopy(&m_transparentNodes.at(baseCount), &m_tempNodes.at(0), m_tempNodes.size() * sizeof(QSGGeometryNode *)); + } + } else { + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + buildLists(c); + } +} + +void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list) +{ + const float scale = 1.0f / m_currentRenderOrder; + int count = list.size(); + int currentRenderOrder = 0x80000000; + m_current_projection_matrix.setColumn(2, scale * projectionMatrix().column(2)); + + //int clipChangeCount = 0; + //int programChangeCount = 0; + //int materialChangeCount = 0; + + for (int i = 0; i < count; ++i) { + QSGGeometryNode *geomNode = list.at(i); + + QSGMaterialShader::RenderState::DirtyStates updates; + +#if defined (QML_RUNTIME_TESTING) + static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); + if (dumpTree) + qDebug() << geomNode; +#endif + + bool changeMatrix = m_currentMatrix != geomNode->matrix(); + + if (changeMatrix) { + m_currentMatrix = geomNode->matrix(); + if (m_currentMatrix) + m_current_model_view_matrix = *m_currentMatrix; + else + m_current_model_view_matrix.setToIdentity(); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity(); + if (changeOpacity) { + updates |= QSGMaterialShader::RenderState::DirtyOpacity; + m_current_opacity = geomNode->inheritedOpacity(); + } + + Q_ASSERT(geomNode->activeMaterial()); + + QSGMaterial *material = geomNode->activeMaterial(); + QSGMaterialShader *program = m_context->prepareMaterial(material); + Q_ASSERT(program->program()->isLinked()); + + bool changeClip = geomNode->clipList() != m_currentClip; + QSGRenderer::ClipType clipType = QSGRenderer::NoClip; + if (changeClip) { + // The clip function relies on there not being any depth testing.. + glDisable(GL_DEPTH_TEST); + clipType = updateStencilClip(geomNode->clipList()); + glEnable(GL_DEPTH_TEST); + m_currentClip = geomNode->clipList(); +#ifdef FORCE_NO_REORDER + glDepthMask(false); +#else + glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1); +#endif + //++clipChangeCount; + } + + bool changeProgram = (changeClip && clipType == QSGRenderer::StencilClip) || m_currentProgram != program; + if (changeProgram) { + if (m_currentProgram) + m_currentProgram->deactivate(); + m_currentProgram = program; + m_currentProgram->activate(); + //++programChangeCount; + updates |= (QSGMaterialShader::RenderState::DirtyMatrix | QSGMaterialShader::RenderState::DirtyOpacity); + +#ifdef RENDERER_DEBUG + materialChanges++; +#endif + } + + bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder(); + if (changeRenderOrder) { + currentRenderOrder = geomNode->renderOrder(); + m_current_projection_matrix.setColumn(3, projectionMatrix().column(3) + + currentRenderOrder + * m_current_projection_matrix.column(2)); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + if (changeProgram || m_currentMaterial != material) { + program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial); + m_currentMaterial = material; + //++materialChangeCount; + } + + //glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale); + + const QSGGeometry *g = geomNode->geometry(); + draw(program, g); + +#ifdef RENDERER_DEBUG + geometryNodesDrawn++; +#endif + } + //qDebug("Clip: %i, shader program: %i, material: %i times changed while drawing %s items", + // clipChangeCount, programChangeCount, materialChangeCount, + // &list == &m_transparentNodes ? "transparent" : "opaque"); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h new file mode 100644 index 0000000000..a4c2ffbdbc --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLRENDERER_H +#define QMLRENDERER_H + +#include "qsgrenderer_p.h" + +#include <QtGui/private/qdatabuffer_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGDefaultRenderer : public QSGRenderer +{ + Q_OBJECT +public: + class IndexGeometryNodePair : public QPair<int, QSGGeometryNode *> + { + public: + IndexGeometryNodePair(int i, QSGGeometryNode *n); + bool operator < (const IndexGeometryNodePair &other) const; + }; + + + // Minimum heap. + class IndexGeometryNodePairHeap + { + public: + IndexGeometryNodePairHeap(); + void insert(const IndexGeometryNodePair &x); + const IndexGeometryNodePair &top() const { return v.first(); } + IndexGeometryNodePair pop(); + bool isEmpty() const { return v.isEmpty(); } + private: + static int parent(int i) { return (i - 1) >> 1; } + static int left(int i) { return (i << 1) | 1; } + static int right(int i) { return (i + 1) << 1; } + QDataBuffer<IndexGeometryNodePair> v; + }; + + QSGDefaultRenderer(QSGContext *context); + + void render(); + + void nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags); + + void setSortFrontToBackEnabled(bool sort); + bool isSortFrontToBackEnabled() const; + +private: + void buildLists(QSGNode *node); + void renderNodes(const QDataBuffer<QSGGeometryNode *> &list); + + const QSGClipNode *m_currentClip; + QSGMaterial *m_currentMaterial; + QSGMaterialShader *m_currentProgram; + const QMatrix4x4 *m_currentMatrix; + QMatrix4x4 m_renderOrderMatrix; + QDataBuffer<QSGGeometryNode *> m_opaqueNodes; + QDataBuffer<QSGGeometryNode *> m_transparentNodes; + QDataBuffer<QSGGeometryNode *> m_tempNodes; + IndexGeometryNodePairHeap m_heap; + + bool m_rebuild_lists; + bool m_needs_sorting; + bool m_sort_front_to_back; + int m_currentRenderOrder; + +#ifdef QML_RUNTIME_TESTING + bool m_render_opaque_nodes; + bool m_render_alpha_nodes; +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLRENDERER_H diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp new file mode 100644 index 0000000000..8661c9af93 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt scene graph research project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggeometry.h" +#include "qsggeometry_p.h" + +#include <qopenglcontext.h> +#include <qopenglfunctions.h> +#include <private/qopenglextensions_p.h> + +QT_BEGIN_NAMESPACE + + +QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tupleSize, int primitiveType, bool isPrimitive) +{ + Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, 0 }; + return a; +} + + +/*! + Convenience function which returns attributes to be used for 2D solid + color drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() +{ + static Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true) + }; + static AttributeSet attrs = { 1, sizeof(float) * 2, data }; + return attrs; +} + +/*! + Convenience function which returns attributes to be used for textured 2D drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D() +{ + static Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), + QSGGeometry::Attribute::create(1, 2, GL_FLOAT) + }; + static AttributeSet attrs = { 2, sizeof(float) * 4, data }; + return attrs; +} + +/*! + Convenience function which returns attributes to be used for per vertex colored 2D drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() +{ + static Attribute data[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), + QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE) + }; + static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data }; + return attrs; +} + + +/*! + \class QSGGeometry + \brief The QSGGeometry class provides low-level storage for graphics primitives + in the QML Scene Graph. + + The QSGGeometry class provides a few convenience attributes and attribute accessors + by default. The defaultAttributes_Point2D() function returns attributes to be used + in normal solid color rectangles, while the defaultAttributes_TexturedPoint2D function + returns attributes to be used for the common pixmap usecase. + */ + + +/*! + Constructs a geometry object based on \a attributes. + + The object allocate space for \a vertexCount vertices based on the accumulated + size in \a attributes and for \a indexCount. + + Geometry objects are constructed with GL_TRIANGLE_STRIP as default drawing mode. + + The attribute structure is assumed to be POD and the geometry object + assumes this will not go away. There is no memory management involved. + */ + +QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, + int vertexCount, + int indexCount, + int indexType) + : m_drawing_mode(GL_TRIANGLE_STRIP) + , m_vertex_count(0) + , m_index_count(0) + , m_index_type(indexType) + , m_attributes(attributes) + , m_data(0) + , m_index_data_offset(-1) + , m_server_data(0) + , m_owns_data(false) + , m_index_usage_pattern(AlwaysUploadPattern) + , m_vertex_usage_pattern(AlwaysUploadPattern) +{ + Q_ASSERT(m_attributes.count > 0); + Q_ASSERT(m_attributes.stride > 0); + + Q_ASSERT_X(indexType != GL_UNSIGNED_INT + || static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions()) + ->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint), + "QSGGeometry::QSGGeometry", + "GL_UNSIGNED_INT is not supported, geometry will not render" + ); + + if (indexType != GL_UNSIGNED_BYTE + && indexType != GL_UNSIGNED_SHORT + && indexType != GL_UNSIGNED_INT) { + qFatal("QSGGeometry: Unsupported index type, %x.\n", indexType); + } + + + // Because allocate reads m_vertex_count, m_index_count and m_owns_data, these + // need to be set before calling allocate... + allocate(vertexCount, indexCount); +} + +/*! + \fn int QSGGeometry::sizeOfVertex() const + + Returns the size in bytes of one vertex. + + This value comes from the attributes. + */ + +/*! + \fn int QSGGeometry::sizeOfIndex() const + + Returns the byte size of the index type. + + This value is either 1 when index type is GL_UNSIGNED_BYTE or 2 when + index type is GL_UNSIGNED_SHORT. For Desktop OpenGL, GL_UNSIGNED_INT + with the value 4 is also supported. + */ + +QSGGeometry::~QSGGeometry() +{ + if (m_owns_data) + qFree(m_data); + + if (m_server_data) + delete m_server_data; +} + +/*! + \fn int QSGGeometry::vertexCount() const + + Returns the number of vertices in this geometry object. + */ + +/*! + \fn int QSGGeometry::indexCount() const + + Returns the number of indices in this geometry object. + */ + + + +/*! + \fn void *QSGGeometry::vertexData() + + Returns a pointer to the raw vertex data of this geometry object. + + \sa vertexDataAsPoint2D(), vertexDataAsTexturedPoint2D + */ + +/*! + \fn const void *QSGGeometry::vertexData() const + + Returns a pointer to the raw vertex data of this geometry object. + + \sa vertexDataAsPoint2D(), vertexDataAsTexturedPoint2D + */ + +/*! + Returns a pointer to the raw index data of this geometry object. + + \sa indexDataAsUShort(), indexDataAsUInt() + */ +void *QSGGeometry::indexData() +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +/*! + Returns a pointer to the raw index data of this geometry object. + + \sa indexDataAsUShort(), indexDataAsUInt() + */ +const void *QSGGeometry::indexData() const +{ + return m_index_data_offset < 0 + ? 0 + : ((char *) m_data + m_index_data_offset); +} + +/*! + Sets the drawing mode to be used for this geometry. + + The default value is GL_TRIANGLE_STRIP. + */ +void QSGGeometry::setDrawingMode(GLenum mode) +{ + m_drawing_mode = mode; +} + +/*! + \fn int QSGGeometry::drawingMode() const + + Returns the drawing mode of this geometry. + + The default value is GL_TRIANGLE_STRIP. + */ + +/*! + \fn int QSGGeometry::indexType() const + + Returns the primitive type used for indices in this + geometry object. + */ + + +/*! + Resizes the vertex and index data of this geometry object to fit \a vertexCount + vertices and \a indexCount indices. + + Vertex and index data will be invalidated after this call and the caller must + */ +void QSGGeometry::allocate(int vertexCount, int indexCount) +{ + if (vertexCount == m_vertex_count && indexCount == m_index_count) + return; + + m_vertex_count = vertexCount; + m_index_count = indexCount; + + bool canUsePrealloc = m_index_count <= 0; + int vertexByteSize = m_attributes.stride * m_vertex_count; + + if (m_owns_data) + qFree(m_data); + + if (canUsePrealloc && vertexByteSize <= (int) sizeof(m_prealloc)) { + m_data = (void *) &m_prealloc[0]; + m_index_data_offset = -1; + m_owns_data = false; + } else { + Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT); + int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32)); + m_data = (void *) qMalloc(vertexByteSize + indexByteSize); + m_index_data_offset = vertexByteSize; + m_owns_data = true; + } + + // If we have associated vbo data we could potentially crash later if + // the old buffers are used with the new vertex and index count, so we force + // an update in the renderer in that case. This is really the users responsibility + // but it is cheap for us to enforce this, so why not... + if (m_server_data) { + markIndexDataDirty(); + markVertexDataDirty(); + } + +} + +/*! + Updates the geometry \a g with the coordinates in \a rect. + + The function assumes the geometry object contains a single triangle strip + of QSGGeometry::Point2D vertices + */ +void QSGGeometry::updateRectGeometry(QSGGeometry *g, const QRectF &rect) +{ + Point2D *v = g->vertexDataAsPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + + v[1].x = rect.left(); + v[1].y = rect.bottom(); + + v[2].x = rect.right(); + v[2].y = rect.top(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); +} + +/*! + Updates the geometry \a g with the coordinates in \a rect and texture + coordinates from \a textureRect. + + \a textureRect should be in normalized coordinates. + + \a g is assumed to be a triangle strip of four vertices of type + QSGGeometry::TexturedPoint2D. + */ +void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &textureRect) +{ + TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + v[0].tx = textureRect.left(); + v[0].ty = textureRect.top(); + + v[1].x = rect.left(); + v[1].y = rect.bottom(); + v[1].tx = textureRect.left(); + v[1].ty = textureRect.bottom(); + + v[2].x = rect.right(); + v[2].y = rect.top(); + v[2].tx = textureRect.right(); + v[2].ty = textureRect.top(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); + v[3].tx = textureRect.right(); + v[3].ty = textureRect.bottom(); +} + + + +/*! + \enum QSGGeometry::DataPattern + + The DataPattern enum is used to specify the use pattern for the vertex + and index data in a geometry object. + + \value AlwaysUploadPattern The data is always uploaded. This means that + the user does not need to explicitly mark index and vertex data as + dirty after changing it. This is the default. + + \value DynamicPattern The data is modified repeatedly and drawn many times. + This is a hint that may provide better performance. When set + the user must make sure to mark the data as dirty after changing it. + + \value StaticPattern The data is modified once and drawn many times. This is + a hint that may provide better performance. When set the user must make sure + to mark the data as dirty after changing it. + */ + + +/*! + \fn QSGGeometry::DataPattern QSGGeometry::indexDataPattern() const + + Returns the usage pattern for indices in this geometry. The default + pattern is AlwaysUploadPattern. + */ + +/*! + Sets the usage pattern for indices to \a p. + + The default is AlwaysUploadPattern. When set to anything other than + the default, the user must call markIndexDataDirty() after changing + the index data. + */ + +void QSGGeometry::setIndexDataPattern(DataPattern p) +{ + m_index_usage_pattern = p; +} + + + + +/*! + \fn QSGGeometry::DataPattern QSGGeometry::vertexDataPattern() const + + Returns the usage pattern for vertices in this geometry. The default + pattern is AlwaysUploadPattern. + */ + +/*! + Sets the usage pattern for vertices to \a p. + + The default is AlwaysUploadPattern. When set to anything other than + the default, the user must call markVertexDataDirty() after changing + the vertex data. + */ + +void QSGGeometry::setVertexDataPattern(DataPattern p) +{ + m_vertex_usage_pattern = p; +} + + + + +/*! + Mark that the vertices in this geometry has changed and must be uploaded + again. + + This function only has an effect when the usage pattern for vertices is + StaticData and the renderer that renders this geometry uploads the geometry + into Vertex Buffer Objects (VBOs). + */ +void QSGGeometry::markIndexDataDirty() +{ + m_dirty_index_data = true; +} + + + +/*! + Mark that the vertices in this geometry has changed and must be uploaded + again. + + This function only has an effect when the usage pattern for vertices is + StaticData and the renderer that renders this geometry uploads the geometry + into Vertex Buffer Objects (VBOs). + */ +void QSGGeometry::markVertexDataDirty() +{ + m_dirty_vertex_data = true; +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h new file mode 100644 index 0000000000..aea6f0b94b --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsggeometry.h @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt scene graph research project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_H +#define QSGGEOMETRY_H + +#include <QtQuick/qtquickglobal.h> +#include <QtGui/qopengl.h> +#include <QRectF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGGeometryData; + +class Q_QUICK_EXPORT QSGGeometry +{ +public: + + struct Attribute + { + int position; + int tupleSize; + int type; + + uint isVertexCoordinate : 1; + uint migrateYourCodeToUseTheCreateFunction: 31; // ### Remove before release + + static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false); + }; + + struct AttributeSet { + int count; + int stride; + const Attribute *attributes; + }; + + struct Point2D { + float x, y; + void set(float nx, float ny) { + x = nx; y = ny; + } + }; + struct TexturedPoint2D { + float x, y; + float tx, ty; + void set(float nx, float ny, float ntx, float nty) { + x = nx; y = ny; tx = ntx; ty = nty; + } + }; + struct ColoredPoint2D { + float x, y; + unsigned char r, g, b, a; + void set(float nx, float ny, uchar nr, uchar ng, uchar nb, uchar na) { + x = nx; y = ny; + r = nr; g = ng, b = nb; a = na; + } + }; + + static const AttributeSet &defaultAttributes_Point2D(); + static const AttributeSet &defaultAttributes_TexturedPoint2D(); + static const AttributeSet &defaultAttributes_ColoredPoint2D(); + + enum DataPattern { + AlwaysUploadPattern = 0, + StreamPattern = 1, + DynamicPattern = 2, + StaticPattern = 3 + }; + + QSGGeometry(const QSGGeometry::AttributeSet &attribs, + int vertexCount, + int indexCount = 0, + int indexType = GL_UNSIGNED_SHORT); + virtual ~QSGGeometry(); + + void setDrawingMode(GLenum mode); + inline GLenum drawingMode() const { return m_drawing_mode; } + + void allocate(int vertexCount, int indexCount = 0); + + int vertexCount() const { return m_vertex_count; } + + void *vertexData() { return m_data; } + inline Point2D *vertexDataAsPoint2D(); + inline TexturedPoint2D *vertexDataAsTexturedPoint2D(); + inline ColoredPoint2D *vertexDataAsColoredPoint2D(); + + inline const void *vertexData() const { return m_data; } + inline const Point2D *vertexDataAsPoint2D() const; + inline const TexturedPoint2D *vertexDataAsTexturedPoint2D() const; + inline const ColoredPoint2D *vertexDataAsColoredPoint2D() const; + + inline int indexType() const { return m_index_type; } + + int indexCount() const { return m_index_count; } + + void *indexData(); + inline uint *indexDataAsUInt(); + inline quint16 *indexDataAsUShort(); + + inline int sizeOfIndex() const; + + const void *indexData() const; + inline const uint *indexDataAsUInt() const; + inline const quint16 *indexDataAsUShort() const; + + inline int attributeCount() const { return m_attributes.count; } + inline const Attribute *attributes() const { return m_attributes.attributes; } + inline int sizeOfVertex() const { return m_attributes.stride; } + + static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); + static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + + void setIndexDataPattern(DataPattern p); + DataPattern indexDataPattern() const { return (DataPattern) m_index_usage_pattern; } + + void setVertexDataPattern(DataPattern p); + DataPattern vertexDataPattern() const { return (DataPattern) m_vertex_usage_pattern; } + + void markIndexDataDirty(); + void markVertexDataDirty(); + +private: + friend class QSGGeometryData; + + int m_drawing_mode; + int m_vertex_count; + int m_index_count; + int m_index_type; + const AttributeSet &m_attributes; + void *m_data; + int m_index_data_offset; + + QSGGeometryData *m_server_data; + + uint m_owns_data : 1; + uint m_index_usage_pattern : 2; + uint m_vertex_usage_pattern : 2; + uint m_dirty_index_data : 1; + uint m_dirty_vertex_data : 1; + uint m_reserved_bits : 27; + + float m_prealloc[16]; +}; + +inline uint *QSGGeometry::indexDataAsUInt() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline quint16 *QSGGeometry::indexDataAsUShort() +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline const uint *QSGGeometry::indexDataAsUInt() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + return (uint *) indexData(); +} + +inline const quint16 *QSGGeometry::indexDataAsUShort() const +{ + Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + return (quint16 *) indexData(); +} + +inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (Point2D *) m_data; +} + +inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (TexturedPoint2D *) m_data; +} + +inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (ColoredPoint2D *) m_data; +} + +inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const +{ + Q_ASSERT(m_attributes.count == 1); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].position == 0); + return (const Point2D *) m_data; +} + +inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + return (const TexturedPoint2D *) m_data; +} + +inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() const +{ + Q_ASSERT(m_attributes.count == 2); + Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); + Q_ASSERT(m_attributes.attributes[0].position == 0); + Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); + Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].position == 1); + Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); + Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + return (const ColoredPoint2D *) m_data; +} + +int QSGGeometry::sizeOfIndex() const +{ + if (m_index_type == GL_UNSIGNED_SHORT) return 2; + else if (m_index_type == GL_UNSIGNED_BYTE) return 1; + else if (m_index_type == GL_UNSIGNED_INT) return 4; + return 0; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGGEOMETRY_H diff --git a/src/quick/scenegraph/coreapi/qsggeometry_p.h b/src/quick/scenegraph/coreapi/qsggeometry_p.h new file mode 100644 index 0000000000..ef2935ae4c --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsggeometry_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_P_H +#define QSGGEOMETRY_P_H + +#include "qsggeometry.h" + +QT_BEGIN_NAMESPACE + +class QSGGeometryData +{ +public: + virtual ~QSGGeometryData() {} + + static inline QSGGeometryData *data(const QSGGeometry *g) { + return g->m_server_data; + } + + static inline void install(const QSGGeometry *g, QSGGeometryData *data) { + Q_ASSERT(!g->m_server_data); + const_cast<QSGGeometry *>(g)->m_server_data = data; + } + + static bool inline hasDirtyVertexData(const QSGGeometry *g) { return g->m_dirty_vertex_data; } + static void inline clearDirtyVertexData(const QSGGeometry *g) { const_cast<QSGGeometry *>(g)->m_dirty_vertex_data = false; } + + static bool inline hasDirtyIndexData(const QSGGeometry *g) { return g->m_dirty_vertex_data; } + static void inline clearDirtyIndexData(const QSGGeometry *g) { const_cast<QSGGeometry *>(g)->m_dirty_index_data = false; } + +}; + +QT_END_NAMESPACE + +#endif // QSGGEOMETRY_P_H diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp new file mode 100644 index 0000000000..36b50e89b6 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmaterial.h" +#include "qsgrenderer_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + \class QSGMaterialShader + \brief The QSGMaterialShader class implements a material renders geometry. + + The QSGMaterial and QSGMaterialShader form a tight relationship. For one + scene graph (including nested graphs), there is one unique QSGMaterialShader + instance which encapsulates the QOpenGLShaderProgram the scene graph uses + to render that material, such as a shader to flat coloring of geometry. + Each QSGGeometryNode can have a unique QSGMaterial containing the + how the shader should be configured when drawing that node, such as + the actual color to used to render the geometry. + + An instance of QSGMaterialShader is never created explicitely by the user, + it will be created on demand by the scene graph through + QSGMaterial::createShader(). The scene graph will make sure that there + is only one instance of each shader implementation through a scene graph. + + The source code returned from vertexShader() is used to control what the + material does with the vertiex data that comes in from the geometry. + The source code returned from the fragmentShader() is used to control + what how the material should fill each individual pixel in the geometry. + The vertex and fragment source code is queried once during initialization, + changing what is returned from these functions later will not have + any effect. + + The activate() function is called by the scene graph when a shader is + is starting to be used. The deactivate function is called by the scene + graph when the shader is no longer going to be used. While active, + the scene graph may make one or more calls to updateState() which + will update the state of the shader for each individual geometry to + render. + + The attributeNames() returns the name of the attributes used in the + vertexShader(). These are used in the default implementation of + activate() and deactive() to decide whice vertex registers are enabled. + + The initialize() function is called during program creation to allow + subclasses to prepare for use, such as resolve uniform names in the + vertexShader() and fragmentShader(). + + A minimal example: + \code + class Shader : public QSGMaterialShader + { + public: + const char *vertexShader() const { + return + "attribute highp vec4 vertex; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vertex; \n" + "}"; + } + + const char *fragmentShader() const { + return + "uniform lowp float opacity; \n" + "void main() { \n" + " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n" + "}"; + } + + char const *const *attributeNames() const + { + static char const *const names[] = { "vertex", 0 }; + return names; + } + + void initialize() + { + QSGMaterialShader::initialize(); + m_id_matrix = program()->uniformLocation("matrix"); + m_id_opacity = program()->uniformLocation("opacity"); + } + + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) + { + Q_ASSERT(program()->isLinked()); + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + if (state.isOpacityDirty()) + program()->setUniformValue(m_id_opacity, state.opacity()); + } + + private: + int m_id_matrix; + int m_id_opacity; + }; + \endcode + + \warning Instances of QSGMaterialShader belongs to the Scene Graph rendering + thread, and cannot be used from the GUI thread. + + */ + + + +/*! + Creates a new QSGMaterialShader. + */ +QSGMaterialShader::QSGMaterialShader() +{ +} + + + +/*! + \fn QOpenGLShaderProgram *QSGMaterialShader::program() const + + Returns the shader program used by this QSGMaterialShader. + */ + + + +/*! + This function is called by the scene graph to indicate that geometry is + about to be rendered using this shader. + + State that is global for all uses of the shader, independent of the geometry + that is being drawn, can be setup in this function. + + If reimplemented, make sure to either call the base class implementation to + enable the vertex attribute registers. + */ + +void QSGMaterialShader::activate() +{ + Q_ASSERT(program()->isLinked()); + + program()->bind(); + char const *const *attr = attributeNames(); + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + program()->enableAttributeArray(i); + } +} + + + +/*! + This function is called by the scene graph to indicate that geometry will + no longer to be rendered using this shader. + + If reimplemented, make sure to either call the base class implementation to + disable the vertex attribute registers. + */ + +void QSGMaterialShader::deactivate() +{ + char const *const *attr = attributeNames(); + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + program()->disableAttributeArray(i); + } +} + + + +/*! + This function is called by the scene graph before geometry is rendered + to make sure the shader is in the right state. + + The current rendering \a state is passed from the scene graph. If the state + indicates that any state is dirty, the updateState implementation must + update accordingly for the geometry to render correctly. + + The subclass specific state, such as the color of a flat color material, should + be extracted from \a newMaterial to update the color uniforms accordingly. + + The \a oldMaterial can be used to minimze state changes when updating + material states. The \a oldMaterial is 0 if this shader was just activated. + + \sa activate(), deactivate() + */ + +void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */) +{ +} + + + +/*! + This function is called when the shader is initialized to compile the + actual QOpenGLShaderProgram. Do not call it explicitely. + + The default implementation will extract the vertexShader() and + fragmentShader() and bind the names returned from attributeNames() + to consecutive vertex attribute registers starting at 0. + */ + +void QSGMaterialShader::compile() +{ + Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!"); + + program()->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader()); + program()->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader()); + + char const *const *attr = attributeNames(); +#ifndef QT_NO_DEBUG + int maxVertexAttribs = 0; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); + for (int i = 0; attr[i]; ++i) { + if (i >= maxVertexAttribs) { + qFatal("List of attribute names is either too long or not null-terminated.\n" + "Maximum number of attributes on this hardware is %i.\n" + "Vertex shader:\n%s\n" + "Fragment shader:\n%s\n", + maxVertexAttribs, vertexShader(), fragmentShader()); + } + if (*attr[i]) + program()->bindAttributeLocation(attr[i], i); + } +#else + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + program()->bindAttributeLocation(attr[i], i); + } +#endif + + if (!program()->link()) { + qWarning("QSGMaterialShader: Shader compilation failed:"); + qWarning() << program()->log(); + } +} + + + +/*! + \class QSGMaterialShader::RenderState + \brief The QSGMaterialShader::RenderState encapsulates the current rendering state + during a call to QSGMaterialShader::updateState(). + + The render state contains a number of accessors that the shader needs to respect + in order to conform to the current state of the scene graph. + + The instance is only valid inside a call to QSGMaterialShader::updateState() and + should not be used outisde this function. + */ + + + +/*! + \enum QSGMaterialShader::RenderState::DirtyState + + \value DirtyMatrix Used to indicate that the matrix has changed and must be updated. + + \value DirtyOpacity Used to indicate that the opacity has changed and must be updated. + */ + + + +/*! + \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const + + Convenience function to check if the dirtyStates() indicates that the matrix + needs to be updated. + */ + + + +/*! + \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const + + Conveience function to check if the dirtyStates() indicates that the opacity + needs to be updated. + */ + + + +/*! + \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const + + Returns which rendering states that have changed and needs to be updated + for geometry rendered with this material to conform to the current + rendering state. + */ + + + +/*! + Returns the accumulated opacity to be used for rendering + */ + +float QSGMaterialShader::RenderState::opacity() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->currentOpacity(); +} + + + +/*! + Returns the matrix combined of modelview matrix and project matrix. + */ + +QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix(); +} + + + +/*! + Returns the model view matrix. + */ + +QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix(); +} + + + +/*! + Returns the viewport rect of the surface being rendered to. + */ + +QRect QSGMaterialShader::RenderState::viewportRect() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->viewportRect(); +} + + + +/*! + Returns the device rect of the surface being rendered to + */ + +QRect QSGMaterialShader::RenderState::deviceRect() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->deviceRect(); +} + + + +/*! + Returns the QOpenGLContext that is being used for rendering + */ + +QOpenGLContext *QSGMaterialShader::RenderState::context() const +{ + return static_cast<const QSGRenderer *>(m_data)->glContext(); +} + + +#ifndef QT_NO_DEBUG +static int qt_material_count = 0; + +static void qt_print_material_count() +{ + qDebug("Number of leaked materials: %i", qt_material_count); + qt_material_count = -1; +} +#endif + +/*! + \class QSGMaterialType + \brief The QSGMaterialType class is used as a unique type token in combination with QSGMaterial. + + It serves no purpose outside the QSGMaterial::type() function. + */ + +/*! + \class QSGMaterial + \brief The QSGMaterial class encapsulates rendering state for a shader program. + + The QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For + one scene graph (including nested graphs), there is one unique QSGMaterialShader + instance which encapsulates the QOpenGLShaderProgram the scene graph uses + to render that material, such as a shader to flat coloring of geometry. + Each QSGGeometryNode can have a unique QSGMaterial containing the + how the shader should be configured when drawing that node, such as + the actual color to used to render the geometry. + + The QSGMaterial has two virtual functions that both need to be implemented. + The function type() should return a unique instance for all instances of a + specific subclass. The createShader() function should return a new instance + of QSGMaterialShader, specific to the subclass of QSGMaterial. + + A minimal QSGMaterial implementation could look like this: + \code + class Material : public QSGMaterial + { + public: + QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + QSGMaterialShader *createShader() const { return new Shader; } + }; + \endcode + + \warning Instances of QSGMaterial belongs to the Scene Graph rendering thread, + and cannot be used from the GUI thread. + */ + +QSGMaterial::QSGMaterial() + : m_flags(0) +{ +#ifndef QT_NO_DEBUG + ++qt_material_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_material_count); + atexit_registered = true; + } +#endif +} + +QSGMaterial::~QSGMaterial() +{ +#ifndef QT_NO_DEBUG + --qt_material_count; + if (qt_material_count < 0) + qDebug("Material destroyed after qt_print_material_count() was called."); +#endif +} + + + +/*! + \enum QSGMaterial::Flag + + \value Blending Set this flag to true if the material requires GL_BLEND to be + enabled during rendering. + */ + + + +/*! + Sets the flags \a flags on this material if \a on is true; + otherwise clears the attribute. +*/ + +void QSGMaterial::setFlag(Flags flags, bool on) +{ + if (on) + m_flags |= flags; + else + m_flags &= ~flags; +} + + + +/*! + Compares this material to \a other and returns 0 if they are equal; -1 if + this material should sort before \a other and 1 if \a other should sort + before. + + The scene graph can reorder geometry nodes to minimize state changes. + The compare function is called during the sorting process so that + the materials can be sorted to minimize state changes in each + call to QSGMaterialShader::updateState(). + + The this pointer and \a other is guaranteed to have the same type(). + */ + +int QSGMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + return qint64(this) - qint64(other); +} + + + +/*! + \fn QSGMaterialType QSGMaterial::type() const + + This function is called by the scene graph to return a unique instance + per subclass. + */ + + + +/*! + \fn QSGMaterialShader *QSGMaterial::createShader() const + + This function returns a new instance of a the QSGMaterialShader + implementatation used to render geometry for a specifc implementation + of QSGMaterial. + + The function will be called only once for each material type that + exists in the scene graph and will be cached internally. +*/ + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h new file mode 100644 index 0000000000..d8411b4d88 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MATERIAL_H +#define MATERIAL_H + +#include <QtQuick/qtquickglobal.h> +#include <qopenglshaderprogram.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGMaterial; + +class Q_QUICK_EXPORT QSGMaterialShader +{ +public: + class Q_QUICK_EXPORT RenderState { + public: + enum DirtyState + { + DirtyMatrix = 0x0001, + DirtyOpacity = 0x0002 + }; + Q_DECLARE_FLAGS(DirtyStates, DirtyState) + + inline DirtyStates dirtyStates() const { return m_dirty; } + + inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; } + inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; } + + float opacity() const; + QMatrix4x4 combinedMatrix() const; + QMatrix4x4 modelViewMatrix() const; + QRect viewportRect() const; + QRect deviceRect() const; + + QOpenGLContext *context() const; + + private: + friend class QSGRenderer; + DirtyStates m_dirty; + const void *m_data; + }; + + QSGMaterialShader(); + + virtual void activate(); + virtual void deactivate(); + // First time a material is used, oldMaterial is null. + virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + virtual char const *const *attributeNames() const = 0; // Array must end with null. + + inline QOpenGLShaderProgram *program() { return &m_program; } + +protected: + + friend class QSGContext; + + virtual void compile(); + virtual void initialize() { } + + virtual const char *vertexShader() const = 0; + virtual const char *fragmentShader() const = 0; + +private: + QOpenGLShaderProgram m_program; + void *m_reserved; +}; + +struct QSGMaterialType { }; + +class Q_QUICK_EXPORT QSGMaterial +{ +public: + enum Flag { + Blending = 0x0001 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QSGMaterial(); + virtual ~QSGMaterial(); + + virtual QSGMaterialType *type() const = 0; + virtual QSGMaterialShader *createShader() const = 0; + virtual int compare(const QSGMaterial *other) const; + + QSGMaterial::Flags flags() const { return m_flags; } + void setFlag(Flags flags, bool on = true); + +private: + Flags m_flags; + void *m_reserved; + Q_DISABLE_COPY(QSGMaterial) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterial::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp new file mode 100644 index 0000000000..2d6828323c --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -0,0 +1,1264 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgnode.h" +#include "qsgrenderer_p.h" +#include "qsgnodeupdater_p.h" +#include "qsgmaterial.h" + +#include "limits.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG +static int qt_node_count = 0; + +static void qt_print_node_count() +{ + qDebug("Number of leaked nodes: %i", qt_node_count); + qt_node_count = -1; +} +#endif + +/*! + \class QSGNode + \brief The QSGNode class is the base class for all nodes in the scene graph. + + \inmodule QtQuick + + The QSGNode class can be used as a child container. Children are added with + the appendChildNode(), prependChildNode(), insertChildNodeBefore() and + insertChildNodeAfter(). Ordering of nodes is important as geometry nodes + will be rendered in the order they are added to the scene graph. + Actually, the scene may reorder nodes freely, but the resulting visual + order is still guaranteed. + + If nodes change every frame, the preprocess() function can be used to + apply changes to a node for every frame its rendered. The use of preprocess() + must be explicitly enabled by setting the QSGNode::UsePreprocess flag + on the node. + + The virtual isSubtreeBlocked() function can be used to disable a subtree all + together. Nodes in a blocked subtree will not be preprocessed() and not + rendered. + + Anything related to QSGNode should happen on the scene graph rendering thread. + */ + +QSGNode::QSGNode() + : m_parent(0) + , m_type(BasicNodeType) + , m_firstChild(0) + , m_lastChild(0) + , m_nextSibling(0) + , m_previousSibling(0) + , m_subtreeGeometryCount(0) + , m_nodeFlags(OwnedByParent) + , m_flags(0) +{ + init(); +} + +QSGNode::QSGNode(NodeType type) + : m_parent(0) + , m_type(type) + , m_firstChild(0) + , m_lastChild(0) + , m_nextSibling(0) + , m_previousSibling(0) + , m_subtreeGeometryCount(type == GeometryNodeType ? 1 : 0) + , m_nodeFlags(OwnedByParent) + , m_flags(0) +{ + init(); +} + +void QSGNode::init() +{ +#ifndef QT_NO_DEBUG + ++qt_node_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_node_count); + atexit_registered = true; + } +#endif +} + +QSGNode::~QSGNode() +{ +#ifndef QT_NO_DEBUG + --qt_node_count; + if (qt_node_count < 0) + qDebug("Node destroyed after qt_print_node_count() was called."); +#endif + destroy(); +} + + +/*! + \fn void QSGNode::preprocess() + + Override this function to do processing on the node before it is rendered. + + Preprocessing needs to be explicitly enabled by setting the flag + QSGNode::UsePreprocess. The flag needs to be set before the node is added + to the scene graph and will cause the preprocess() function to be called + for every frame the node is rendered. + + The preprocess function is called before the update pass that propegates + opacity and transformations through the scene graph. That means that + functions like QSGOpacityNode::combinedOpacity() and + QSGTransformNode::combinedMatrix() will not contain up-to-date values. + If such values are changed during the preprocess, these changes will be + propegated through the scene graph before it is rendered. + + \warning Beware of deleting nodes while they are being preprocessed. It is + possible, with a small performance hit, to delete a single node during its + own preprocess call. Deleting a subtree which has nodes that also use + preprocessing may result in a segmentation fault. This is done for + performance reasons. + */ + + + + +/*! + Returns whether this node and its subtree is available for use. + + Blocked subtrees will not get their dirty states updated and they + will not be rendered. + + The QSGOpacityNode will return a blocked subtree when accumulated opacity + is 0, for instance. + */ + +bool QSGNode::isSubtreeBlocked() const +{ + return m_subtreeGeometryCount == 0; +} + +/*! + \internal + Detaches the node from the scene graph and deletes any children it owns. + + This function is called from QSGNode's and QSGRootNode's destructor. It + should not be called explicitly in user code. QSGRootNode needs to call + destroy() because destroy() calls removeChildNode() which in turn calls + markDirty() which type-casts the node to QSGRootNode. This type-cast is not + valid at the time QSGNode's destructor is called because the node will + already be partially destroyed at that point. +*/ + +void QSGNode::destroy() +{ + if (m_parent) { + m_parent->removeChildNode(this); + Q_ASSERT(m_parent == 0); + } + while (m_firstChild) { + QSGNode *child = m_firstChild; + removeChildNode(child); + Q_ASSERT(child->m_parent == 0); + if (child->flags() & OwnedByParent) + delete child; + } + + Q_ASSERT(m_firstChild == 0 && m_lastChild == 0); +} + + +/*! + Prepends \a node to this node's the list of children. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::prependChildNode(QSGNode *node) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode", "QSGNode already has a parent"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::prependChildNode", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode", "QSGGeometryNode is missing geometry"); + } +#endif + + if (m_firstChild) + m_firstChild->m_previousSibling = node; + else + m_lastChild = node; + node->m_nextSibling = m_firstChild; + m_firstChild = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +/*! + Appends \a node to this node's list of children. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::appendChildNode(QSGNode *node) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode", "QSGNode already has a parent"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::appendChildNode", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode", "QSGGeometryNode is missing geometry"); + } +#endif + + if (m_lastChild) + m_lastChild->m_nextSibling = node; + else + m_firstChild = node; + node->m_previousSibling = m_lastChild; + m_lastChild = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Inserts \a node to this node's list of children before the node specified with \a before. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore", "QSGNode already has a parent"); + Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing geometry"); + } +#endif + + QSGNode *previous = before->m_previousSibling; + if (previous) + previous->m_nextSibling = node; + else + m_firstChild = node; + node->m_previousSibling = previous; + node->m_nextSibling = before; + before->m_previousSibling = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Inserts \a node to this node's list of children after the node specified with \a after. + + Ordering of nodes is important as geometry nodes will be rendered in the + order they are added to the scene graph. + */ + +void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after) +{ + //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!"); + Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter", "QSGNode already has a parent"); + Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong"); + +#ifndef QT_NO_DEBUG + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node); + Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing material"); + Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing geometry"); + } +#endif + + QSGNode *next = after->m_nextSibling; + if (next) + next->m_previousSibling = node; + else + m_lastChild = node; + node->m_nextSibling = next; + node->m_previousSibling = after; + after->m_nextSibling = node; + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + + + +/*! + Removes \a node from this node's list of children. + */ + +void QSGNode::removeChildNode(QSGNode *node) +{ + //Q_ASSERT(m_children.contains(node)); + Q_ASSERT(node->parent() == this); + + QSGNode *previous = node->m_previousSibling; + QSGNode *next = node->m_nextSibling; + if (previous) + previous->m_nextSibling = next; + else + m_firstChild = next; + if (next) + next->m_previousSibling = previous; + else + m_lastChild = previous; + node->m_previousSibling = 0; + node->m_nextSibling = 0; + + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; +} + + +/*! + Removes all child nodes from this node's list of children. + */ + +void QSGNode::removeAllChildNodes() +{ + while (m_firstChild) { + QSGNode *node = m_firstChild; + m_firstChild = node->m_nextSibling; + node->m_nextSibling = 0; + if (m_firstChild) + m_firstChild->m_previousSibling = 0; + else + m_lastChild = 0; + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; + } +} + + +int QSGNode::childCount() const +{ + int count = 0; + QSGNode *n = m_firstChild; + while (n) { + ++count; + n = n->m_nextSibling; + } + return count; +} + + +QSGNode *QSGNode::childAtIndex(int i) const +{ + QSGNode *n = m_firstChild; + while (i && n) { + --i; + n = n->m_nextSibling; + } + return n; +} + + +/*! + Sets the flag \a f on this node if \a enabled is true; + otherwise clears the flag. + + \sa flags() +*/ + +void QSGNode::setFlag(Flag f, bool enabled) +{ + if (enabled) + m_nodeFlags |= f; + else + m_nodeFlags &= ~f; +} + + +/*! + Sets the flags \a f on this node if \a enabled is true; + otherwise clears the flags. + + \sa flags() +*/ + +void QSGNode::setFlags(Flags f, bool enabled) +{ + if (enabled) + m_nodeFlags |= f; + else + m_nodeFlags &= ~f; +} + + + +/*! + Marks this node with the states in \a flags as dirty. + + When a node is marked dirty, it recursively mark the parent chain + as dirty and notify all connected renderers that the has dirty states. + */ + +void QSGNode::markDirty(DirtyFlags flags) +{ + m_flags |= (flags & DirtyPropagationMask); + + DirtyFlags subtreeFlags = DirtyFlags((flags & DirtyPropagationMask) << 16); + + int geometryCountDiff = 0; + if (flags & DirtyNodeAdded) + geometryCountDiff += m_subtreeGeometryCount; + if (flags & DirtyNodeRemoved) + geometryCountDiff -= m_subtreeGeometryCount; + + QSGNode *p = m_parent; + while (p) { + p->m_flags |= subtreeFlags; + p->m_subtreeGeometryCount += geometryCountDiff; + if (p->type() == RootNodeType) + static_cast<QSGRootNode *>(p)->notifyNodeChange(this, flags); + p = p->m_parent; + } +} + + + +/*! + \class QSGBasicGeometryNode + \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes + + \inmodule QtQuick + + The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates + shared functionality between the QSGGeometryNode and QSGClipNode classes. + */ + + +/*! + Creates a new basic geometry node. + */ +QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) + : QSGNode(type) + , m_geometry(0) + , m_matrix(0) + , m_clip_list(0) +{ +} + + +/*! + Deletes this QSGBasicGeometryNode. + + If the node has the flag QSGNode::OwnsGeometry set, it will also delete the + geometry object it is pointing to. This flag is not set by default. + */ + +QSGBasicGeometryNode::~QSGBasicGeometryNode() +{ + if (flags() & OwnsGeometry) + delete m_geometry; +} + + +/*! + \fn QSGGeometry *QSGBasicGeometryNode::geometry() const + + Returns this node's geometry. + + The geometry is null by default. + */ + + +/*! + Sets the geometry of this node to \a geometry. + + If the node has the flag QSGNode::OwnsGeometry set, it will also delete the + geometry object it is pointing to. This flag is not set by default. + */ + +void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) +{ + if (flags() & OwnsGeometry) + delete m_geometry; + m_geometry = geometry; + markDirty(DirtyGeometry); +} + + + +/*! + \class QSGGeometryNode + \brief The QSGGeometryNode class is used for all rendered content in the scene graph. + + \inmodule QtQuick + + The QSGGeometryNode consists of geometry and material. The geometry defines the mesh, + the vertices and their structure, to be drawn. The Material defines how the shape is + filled. + + A geometry node must have both geometry and a normal material before it is added to + the scene graph. + + The geometry node supports two types of materials, the opaqueMaterial and the normal + material. The opaqueMaterial is used when the accumulated scene graph opacity at the + time of rendering is 1. The primary usecase is to special case opaque rendering + to avoid an extra operation in the fragment shader can have significant performance + impact on embedded graphics chips. The opaque material is optional. + + */ + + +/*! + Creates a new geometry node without geometry and material. + */ + +QSGGeometryNode::QSGGeometryNode() + : QSGBasicGeometryNode(GeometryNodeType) + , m_render_order(0) + , m_material(0) + , m_opaque_material(0) + , m_opacity(1) +{ +} + + +/*! + Deletes this geometry node. + + The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and + QSGNode::OwnsGeometry decides weither the geometry node should also + delete the materials and geometry. By default, these flags are disabled. + */ + +QSGGeometryNode::~QSGGeometryNode() +{ + if (flags() & OwnsMaterial) + delete m_material; + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; +} + + + +/*! + \fn int QSGGeometryNode::renderOrder() const + + Returns the render order of this geometry node. + + \internal + */ + + +/*! + Sets the render order of this node to be \a order. + + GeometryNodes are rendered in an order that visually looks like + low order nodes are rendered prior to high order nodes. For opaque + geometry there is little difference as z-testing will handle + the discard, but for translucent objects, the rendering should + normally be specified in the order of back-to-front. + + The default render order is 0. + + \internal + */ +void QSGGeometryNode::setRenderOrder(int order) +{ + m_render_order = order; +} + + + +/*! + Sets the material of this geometry node to \a material. + + Geometry nodes must have a material before they can be added to the + scene graph. + */ +void QSGGeometryNode::setMaterial(QSGMaterial *material) +{ + if (flags() & OwnsMaterial) + delete m_material; + m_material = material; +#ifndef QT_NO_DEBUG + if (m_material != 0 && m_opaque_material == m_material) + qWarning("QSGGeometryNode: using same material for both opaque and translucent"); +#endif + markDirty(DirtyMaterial); +} + + + +/*! + Sets the opaque material of this geometry to \a material. + + The opaque material will be preferred by the renderer over the + default material, as returned by the material() function, if + it is not null and the geometry item has an inherited opacity of + 1. + + The opaqueness refers to scene graph opacity, the material is still + allowed to set QSGMaterial::Blending to true and draw transparent + pixels. + */ +void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material) +{ + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; + m_opaque_material = material; +#ifndef QT_NO_DEBUG + if (m_opaque_material != 0 && m_opaque_material == m_material) + qWarning("QSGGeometryNode: using same material for both opaque and translucent"); +#endif + + markDirty(DirtyMaterial); +} + + + +/*! + Returns the material which should currently be used for geometry node. + + If the inherited opacity of the node is 1 and there is an opaque material + set on this node, it will be returned; otherwise, the default material + will be returned. + + \warning This function requires the scene graph above this item to be + completely free of dirty states, so it can only be called during rendering + + \internal + + \sa setMaterial, setOpaqueMaterial + */ +QSGMaterial *QSGGeometryNode::activeMaterial() const +{ + Q_ASSERT_X(dirtyFlags() == 0, "QSGGeometryNode::activeMaterial()", "function assumes that all dirty states are cleaned up"); + if (m_opaque_material && m_opacity > 0.999) + return m_opaque_material; + return m_material; +} + + +/*! + Sets the inherited opacity of this geometry to \a opacity. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ +void QSGGeometryNode::setInheritedOpacity(qreal opacity) +{ + Q_ASSERT(opacity >= 0 && opacity <= 1); + m_opacity = opacity; +} + + +/*! + \class QSGClipNode + \brief The QSGClipNode class implements the clipping functionality in the scene graph. + + \inmodule QtQuick + + Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be + accumulated by intersecting all their geometries. The accumulation happens + as part of the rendering. + + Clip nodes must have a geometry before they can be added to the scene graph. + + Clipping is usually implemented by using the stencil buffer. + */ + + + +/*! + Creates a new QSGClipNode without a geometry. + + The clip node must have a geometry before it can be added to the + scene graph. + */ + +QSGClipNode::QSGClipNode() + : QSGBasicGeometryNode(ClipNodeType) +{ +} + + + +/*! + Deletes this QSGClipNode. + + If the flag QSGNode::OwnsGeometry is set, the geometry will also be + deleted. + */ + +QSGClipNode::~QSGClipNode() +{ +} + + + +/*! + \fn bool QSGClipNode::isRectangular() const + + Returns if this clip node has a rectangular clip. + */ + + + +/*! + Sets whether this clip node has a rectangular clip to \a rectHint. + + This is an optimization hint which means that the renderer can + use scissoring instead of stencil, which is significnatly faster. + + When this hint is and it is applicable, the clip region will be + generated from clipRect() rather than geometry(). + */ + +void QSGClipNode::setIsRectangular(bool rectHint) +{ + m_is_rectangular = rectHint; +} + + + +/*! + \fn void QSGClipNode::clipRect() const + + Returns the clip rect of this node. + */ + + +/*! + Sets the clip rect of this clip node to \a rect. + + When a rectangular clip is set in combination with setIsRectangular + the renderer may in some cases use a more optimal clip method. + */ +void QSGClipNode::setClipRect(const QRectF &rect) +{ + m_clip_rect = rect; +} + + +/*! + \class QSGTransformNode + \brief The QSGTransformNode class implements transformations in the scene graph + + \inmodule QtQuick + + Transformations apply the node's subtree and can be nested. Multiple transform nodes + will be accumulated by intersecting all their matrices. The accumulation happens + as part of the rendering. + + The transform nodes implement a 4x4 matrix which in theory supports full 3D + transformations. However, because the renderer optimizes for 2D use-cases rather + than 3D use-cases, rendering a scene with full 3D transformations needs to + be done with some care. + */ + +QSGTransformNode::QSGTransformNode() + : QSGNode(TransformNodeType) +{ +} + + + +/*! + Deletes this transform node. + */ + +QSGTransformNode::~QSGTransformNode() +{ +} + + + +/*! + \fn QMatrix4x4 QSGTransformNode::matrix() const + + Returns this transform node's matrix. + */ + + + +/*! + Sets this transform node's matrix to \a matrix. + */ + +void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix) +{ + m_matrix = matrix; + markDirty(DirtyMatrix); +} + + +/*! + Sets the combined matrix of this matrix to \a transform. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ +void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix) +{ + m_combined_matrix = matrix; +} + + + +/*! + \class QSGRootNode + \brief The QSGRootNode is the toplevel root of any scene graph. + + The root node is used to attach a scene graph to a renderer. + + \internal + */ + + + +/*! + \fn QSGRootNode::QSGRootNode() + + Creates a new root node. + */ + +QSGRootNode::QSGRootNode() + : QSGNode(RootNodeType) +{ +} + + +/*! + Deletes the root node. + + When a root node is deleted it removes itself from all of renderers + that are referencing it. + */ + +QSGRootNode::~QSGRootNode() +{ + while (!m_renderers.isEmpty()) + m_renderers.last()->setRootNode(0); + destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. +} + + + +/*! + Called to notify all renderers that \a node has been marked as dirty + with \a flags. + */ + +void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyFlags flags) +{ + for (int i=0; i<m_renderers.size(); ++i) { + m_renderers.at(i)->nodeChanged(node, flags); + } +} + + + +/*! + \class QSGOpacityNode + \brief The QSGOpacityNode class is used to change opacity of nodes. + + \inmodule QtQuick + + Opacity applies to its subtree and can be nested. Multiple opacity nodes + will be accumulated by multiplying their opacity. The accumulation happens + as part of the rendering. + + When nested opacity gets below a certain threshold, the subtree might + be marked as blocked, causing isSubtreeBlocked() to return true. This + is done for performance reasons. + + */ + + + +/*! + Constructs an opacity node with a default opacity of 1. + + Opacity accumulates downwards in the scene graph so a node with two + QSGOpacityNode instances above it, both with opacity of 0.5, will have + effective opacity of 0.25. + + The default opacity of nodes is 1. + */ +QSGOpacityNode::QSGOpacityNode() + : QSGNode(OpacityNodeType) + , m_opacity(1) + , m_combined_opacity(1) +{ +} + + + +/*! + Deletes the opacity node. + */ + +QSGOpacityNode::~QSGOpacityNode() +{ +} + + + +/*! + \fn qreal QSGOpacityNode::opacity() const + + Returns this opacity node's opacity. + */ + + + +/*! + Sets the opacity of this node to \a opacity. + + Before rendering the graph, the renderer will do an update pass + over the subtree to propegate the opacity to its children. + + The value will be bounded to the range 0 to 1. + */ + +void QSGOpacityNode::setOpacity(qreal opacity) +{ + opacity = qBound<qreal>(0, opacity, 1); + if (m_opacity == opacity) + return; + m_opacity = opacity; + markDirty(DirtyOpacity); +} + + + +/*! + \fn qreal QSGOpacityNode::combinedOpacity() const + + Returns this node's accumulated opacity. + + This vaule is calculated during rendering and only stored + in the opacity node temporarily. + + \internal + */ + + + +/*! + Sets the combined opacity of this node to \a opacity. + + This function is meant to be called by the node preprocessing + prior to rendering the tree, so it will not mark the tree as + dirty. + + \internal + */ + +void QSGOpacityNode::setCombinedOpacity(qreal opacity) +{ + m_combined_opacity = opacity; +} + + + +/*! + For performance reasons, we block the subtree when the opacity + is below a certain threshold. + + \internal + */ + +bool QSGOpacityNode::isSubtreeBlocked() const +{ + return QSGNode::isSubtreeBlocked() || m_opacity < 0.001; +} + + +/*! + \class QSGNodeVisitor + \brief The QSGNodeVisitor class is a helper class for traversing the scene graph. + + \internal + */ + +QSGNodeVisitor::~QSGNodeVisitor() +{ + +} + + +void QSGNodeVisitor::visitNode(QSGNode *n) +{ + switch (n->type()) { + case QSGNode::TransformNodeType: { + QSGTransformNode *t = static_cast<QSGTransformNode *>(n); + enterTransformNode(t); + visitChildren(t); + leaveTransformNode(t); + break; } + case QSGNode::GeometryNodeType: { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n); + enterGeometryNode(g); + visitChildren(g); + leaveGeometryNode(g); + break; } + case QSGNode::ClipNodeType: { + QSGClipNode *c = static_cast<QSGClipNode *>(n); + enterClipNode(c); + visitChildren(c); + leaveClipNode(c); + break; } + case QSGNode::OpacityNodeType: { + QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n); + enterOpacityNode(o); + visitChildren(o); + leaveOpacityNode(o); + break; } + default: + visitChildren(n); + break; + } +} + +void QSGNodeVisitor::visitChildren(QSGNode *n) +{ + for (QSGNode *c = n->firstChild(); c; c = c->nextSibling()) + visitNode(c); +} + + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const QSGGeometryNode *n) +{ + if (!n) { + d << "QSGGeometryNode(null)"; + return d; + } + d << "QSGGeometryNode(" << hex << (void *) n << dec; + + const QSGGeometry *g = n->geometry(); + + if (!g) { + d << "no geometry"; + } else { + + switch (g->drawingMode()) { + case GL_TRIANGLE_STRIP: d << "strip"; break; + case GL_TRIANGLE_FAN: d << "fan"; break; + case GL_TRIANGLES: d << "triangles"; break; + default: break; + } + + d << g->vertexCount(); + + if (g->attributeCount() > 0 && g->attributes()->type == GL_FLOAT) { + float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10; + int stride = g->sizeOfVertex(); + for (int i = 0; i < g->vertexCount(); ++i) { + float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0]; + float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1]; + + x1 = qMin(x1, x); + x2 = qMax(x2, x); + y1 = qMin(y1, y); + y2 = qMax(y2, y); + } + + d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2; + } + } + + d << "order=" << n->renderOrder(); + if (n->material()) + d << "effect=" << n->material() << "type=" << n->material()->type(); + + + d << ")"; +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec; + return d; +} + +QDebug operator<<(QDebug d, const QSGClipNode *n) +{ + if (!n) { + d << "QSGClipNode(null)"; + return d; + } + d << "QSGClipNode(" << hex << (void *) n << dec; + + if (n->childCount()) + d << "children=" << n->childCount(); + + d << "is rect?" << (n->isRectangular() ? "yes" : "no"); + + d << ")"; +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); + return d; +} + +QDebug operator<<(QDebug d, const QSGTransformNode *n) +{ + if (!n) { + d << "QSGTransformNode(null)"; + return d; + } + const QMatrix4x4 m = n->matrix(); + d << "QSGTransformNode("; + d << hex << (void *) n << dec; + if (m.isIdentity()) + d << "identity"; + else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1) + d << "translate" << m(0, 3) << m(1, 3) << m(2, 3); + else + d << "det=" << n->matrix().determinant(); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); + d << ")"; + return d; +} + +QDebug operator<<(QDebug d, const QSGOpacityNode *n) +{ + if (!n) { + d << "QSGOpacityNode(null)"; + return d; + } + d << "QSGOpacityNode("; + d << hex << (void *) n << dec; + d << "opacity=" << n->opacity() + << "combined=" << n->combinedOpacity() + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << "dirty=" << hex << (int) n->dirtyFlags() << dec; + d << ")"; + return d; +} + + +QDebug operator<<(QDebug d, const QSGRootNode *n) +{ + if (!n) { + d << "QSGRootNode(null)"; + return d; + } + d << "QSGRootNode" << hex << (void *) n << "dirty=" << (int) n->dirtyFlags() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + return d; +} + + + +QDebug operator<<(QDebug d, const QSGNode *n) +{ + if (!n) { + d << "QSGNode(null)"; + return d; + } + switch (n->type()) { + case QSGNode::GeometryNodeType: + d << static_cast<const QSGGeometryNode *>(n); + break; + case QSGNode::TransformNodeType: + d << static_cast<const QSGTransformNode *>(n); + break; + case QSGNode::ClipNodeType: + d << static_cast<const QSGClipNode *>(n); + break; + case QSGNode::RootNodeType: + d << static_cast<const QSGRootNode *>(n); + break; + case QSGNode::OpacityNodeType: + d << static_cast<const QSGOpacityNode *>(n); + break; + default: + d << "QSGNode(" << hex << (void *) n << dec + << "dirty=" << hex << (int) n->dirtyFlags() + << "flags=" << (int) n->flags() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + break; + } + return d; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h new file mode 100644 index 0000000000..41c63e27b6 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include "qsggeometry.h" +#include <QtGui/QMatrix4x4> + +#include <float.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define QML_RUNTIME_TESTING + +class QSGRenderer; + +class QSGNode; +class QSGRootNode; +class QSGGeometryNode; +class QSGTransformNode; +class QSGClipNode; + +class Q_QUICK_EXPORT QSGNode +{ +public: + enum NodeType { + BasicNodeType, + RootNodeType, + GeometryNodeType, + TransformNodeType, + ClipNodeType, + OpacityNodeType, + UserNodeType = 1024 + }; + + enum DirtyFlag { + DirtyMatrix = 0x0001, + DirtyClipList = 0x0002, + DirtyNodeAdded = 0x0004, + DirtyNodeRemoved = 0x0008, + DirtyGeometry = 0x0010, + DirtyMaterial = 0x0040, + DirtyOpacity = 0x0080, + DirtyForceUpdate = 0x0100, + + DirtyPropagationMask = DirtyMatrix + | DirtyClipList + | DirtyNodeAdded + | DirtyOpacity + | DirtyForceUpdate + + }; + Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag) + + enum Flag { + // Lower 16 bites reserved for general node + OwnedByParent = 0x0001, + UsePreprocess = 0x0002, + ChildrenDoNotOverlap = 0x0004, + + // Upper 16 bits reserved for node subclasses + + // QSGBasicGeometryNode + OwnsGeometry = 0x00010000, + OwnsMaterial = 0x00020000, + OwnsOpaqueMaterial = 0x00040000 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QSGNode(); + virtual ~QSGNode(); + + QSGNode *parent() const { return m_parent; } + + void removeChildNode(QSGNode *node); + void removeAllChildNodes(); + void prependChildNode(QSGNode *node); + void appendChildNode(QSGNode *node); + void insertChildNodeBefore(QSGNode *node, QSGNode *before); + void insertChildNodeAfter(QSGNode *node, QSGNode *after); + + int childCount() const; + QSGNode *childAtIndex(int i) const; + QSGNode *firstChild() const { return m_firstChild; } + QSGNode *lastChild() const { return m_lastChild; } + QSGNode *nextSibling() const { return m_nextSibling; } + QSGNode* previousSibling() const { return m_previousSibling; } + + inline NodeType type() const { return m_type; } + + void clearDirty() { m_flags = 0; } + void markDirty(DirtyFlags flags); + DirtyFlags dirtyFlags() const { return m_flags; } + + virtual bool isSubtreeBlocked() const; + + Flags flags() const { return m_nodeFlags; } + void setFlag(Flag, bool = true); + void setFlags(Flags, bool = true); + + virtual void preprocess() { } + +#ifdef QML_RUNTIME_TESTING + QString description; +#endif + +protected: + QSGNode(NodeType type); + +private: + friend class QSGRootNode; + + void init(); + void destroy(); + + QSGNode *m_parent; + NodeType m_type; + QSGNode *m_firstChild; + QSGNode *m_lastChild; + QSGNode *m_nextSibling; + QSGNode *m_previousSibling; + int m_subtreeGeometryCount; + + Flags m_nodeFlags; + DirtyFlags m_flags; + + void *m_reserved; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::DirtyFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::Flags) + +class Q_QUICK_EXPORT QSGBasicGeometryNode : public QSGNode +{ +public: +// enum UsagePattern { +// Static, +// Dynamic, +// Stream +// }; +// void setUsagePattern(UsagePattern pattern); +// UsagePattern usagePattern() const { return m_pattern; } + + ~QSGBasicGeometryNode(); + + void setGeometry(QSGGeometry *geometry); + const QSGGeometry *geometry() const { return m_geometry; } + QSGGeometry *geometry() { return m_geometry; } + + const QMatrix4x4 *matrix() const { return m_matrix; } + const QSGClipNode *clipList() const { return m_clip_list; } + +protected: + QSGBasicGeometryNode(NodeType type); + +private: + friend class QSGNodeUpdater; + QSGGeometry *m_geometry; + + int m_reserved_start_index; + int m_reserved_end_index; + + const QMatrix4x4 *m_matrix; + const QSGClipNode *m_clip_list; + +// UsagePattern m_pattern; +}; + +class QSGMaterial; + +class Q_QUICK_EXPORT QSGGeometryNode : public QSGBasicGeometryNode +{ +public: + QSGGeometryNode(); + ~QSGGeometryNode(); + + void setMaterial(QSGMaterial *material); + QSGMaterial *material() const { return m_material; } + + void setOpaqueMaterial(QSGMaterial *material); + QSGMaterial *opaqueMaterial() const { return m_opaque_material; } + + QSGMaterial *activeMaterial() const; + + void setRenderOrder(int order); + int renderOrder() const { return m_render_order; } + + void setInheritedOpacity(qreal opacity); + qreal inheritedOpacity() const { return m_opacity; } + +private: + friend class QSGNodeUpdater; + + int m_render_order; + QSGMaterial *m_material; + QSGMaterial *m_opaque_material; + + qreal m_opacity; +}; + +class Q_QUICK_EXPORT QSGClipNode : public QSGBasicGeometryNode +{ +public: + QSGClipNode(); + ~QSGClipNode(); + + void setIsRectangular(bool rectHint); + bool isRectangular() const { return m_is_rectangular; } + + void setClipRect(const QRectF &); + QRectF clipRect() const { return m_clip_rect; } + +private: + uint m_is_rectangular : 1; + uint m_reserved : 31; + + QRectF m_clip_rect; +}; + + +class Q_QUICK_EXPORT QSGTransformNode : public QSGNode +{ +public: + QSGTransformNode(); + ~QSGTransformNode(); + + void setMatrix(const QMatrix4x4 &matrix); + const QMatrix4x4 &matrix() const { return m_matrix; } + + void setCombinedMatrix(const QMatrix4x4 &matrix); + const QMatrix4x4 &combinedMatrix() const { return m_combined_matrix; } + +private: + QMatrix4x4 m_matrix; + QMatrix4x4 m_combined_matrix; +}; + + +class Q_QUICK_EXPORT QSGRootNode : public QSGNode +{ +public: + QSGRootNode(); + ~QSGRootNode(); + +private: + void notifyNodeChange(QSGNode *node, DirtyFlags flags); + + friend class QSGRenderer; + friend class QSGNode; + friend class QSGGeometryNode; + + QList<QSGRenderer *> m_renderers; +}; + + +class Q_QUICK_EXPORT QSGOpacityNode : public QSGNode +{ +public: + QSGOpacityNode(); + ~QSGOpacityNode(); + + void setOpacity(qreal opacity); + qreal opacity() const { return m_opacity; } + + void setCombinedOpacity(qreal opacity); + qreal combinedOpacity() const { return m_combined_opacity; } + + bool isSubtreeBlocked() const; + +private: + qreal m_opacity; + qreal m_combined_opacity; +}; + +class Q_QUICK_EXPORT QSGNodeVisitor { +public: + virtual ~QSGNodeVisitor(); + +protected: + virtual void enterTransformNode(QSGTransformNode *) {} + virtual void leaveTransformNode(QSGTransformNode *) {} + virtual void enterClipNode(QSGClipNode *) {} + virtual void leaveClipNode(QSGClipNode *) {} + virtual void enterGeometryNode(QSGGeometryNode *) {} + virtual void leaveGeometryNode(QSGGeometryNode *) {} + virtual void enterOpacityNode(QSGOpacityNode *) {} + virtual void leaveOpacityNode(QSGOpacityNode *) {} + virtual void visitNode(QSGNode *n); + virtual void visitChildren(QSGNode *n); +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGGeometryNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGTransformNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGOpacityNode *n); +Q_QUICK_EXPORT QDebug operator<<(QDebug, const QSGRootNode *n); + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // NODE_H diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp new file mode 100644 index 0000000000..a7e5b08dc1 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgnodeupdater_p.h" + +QT_BEGIN_NAMESPACE + +// #define QSG_UPDATER_DEBUG + +QSGNodeUpdater::QSGNodeUpdater() + : m_combined_matrix_stack(64) + , m_opacity_stack(64) + , m_current_clip(0) + , m_force_update(0) +{ + m_opacity_stack.add(1); +} + +void QSGNodeUpdater::updateStates(QSGNode *n) +{ + m_current_clip = 0; + m_force_update = 0; + + Q_ASSERT(m_opacity_stack.size() == 1); // The one we added in the constructr... + Q_ASSERT(m_combined_matrix_stack.isEmpty()); + + visitNode(n); +} + +/*! + \fn void QSGNodeUpdater::setToplevelOpacity(qreal opacity) + + Sets the toplevel opacity that will be multiplied with the + + The default opacity is 1. Any other value will cause artifacts, and is + primarily useful for debug purposes. + + The changing the value during an update pass will have undefined results + */ + +/*! + \fn qreal QSGNodeUpdater::toplevelOpacity() const + + Returns the toplevel opacity for the node updater. The default + value is 1. + */ + + +/*! + Returns true if \a node is has something that blocks it in the chain from + \a node to \a root doing a full state update pass. + + This function does not process dirty states, simply does a simple traversion + up to the top. + + The function assumes that \a root exists in the parent chain of \a node. + */ + +bool QSGNodeUpdater::isNodeBlocked(QSGNode *node, QSGNode *root) const +{ + qreal opacity = 1; + while (node != root) { + if (node->type() == QSGNode::OpacityNodeType) { + opacity *= static_cast<QSGOpacityNode *>(node)->opacity(); + if (opacity < 0.001) + return true; + } + node = node->parent(); + + Q_ASSERT_X(node, "QSGNodeUpdater::isNodeBlocked", "node is not in the subtree of root"); + } + + return false; +} + + +void QSGNodeUpdater::enterTransformNode(QSGTransformNode *t) +{ + if (t->dirtyFlags() & QSGNode::DirtyMatrix) + ++m_force_update; + +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter transform:" << t << "force=" << m_force_update; +#endif + + if (!t->matrix().isIdentity()) { + if (!m_combined_matrix_stack.isEmpty()) { + t->setCombinedMatrix(*m_combined_matrix_stack.last() * t->matrix()); + } else { + t->setCombinedMatrix(t->matrix()); + } + m_combined_matrix_stack.add(&t->combinedMatrix()); + } else { + if (!m_combined_matrix_stack.isEmpty()) { + t->setCombinedMatrix(*m_combined_matrix_stack.last()); + } else { + t->setCombinedMatrix(QMatrix4x4()); + } + } +} + + +void QSGNodeUpdater::leaveTransformNode(QSGTransformNode *t) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave transform:" << t; +#endif + + if (t->dirtyFlags() & QSGNode::DirtyMatrix) + --m_force_update; + + if (!t->matrix().isIdentity()) { + m_combined_matrix_stack.pop_back(); + } + +} + + +void QSGNodeUpdater::enterClipNode(QSGClipNode *c) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter clip:" << c; +#endif + + if (c->dirtyFlags() & QSGNode::DirtyClipList) + ++m_force_update; + + c->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last(); + c->m_clip_list = m_current_clip; + m_current_clip = c; +} + + +void QSGNodeUpdater::leaveClipNode(QSGClipNode *c) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave clip:" << c; +#endif + + if (c->dirtyFlags() & QSGNode::DirtyClipList) + --m_force_update; + + m_current_clip = c->m_clip_list; +} + + +void QSGNodeUpdater::enterGeometryNode(QSGGeometryNode *g) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter geometry:" << g; +#endif + + g->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last(); + g->m_clip_list = m_current_clip; + g->setInheritedOpacity(m_opacity_stack.last()); +} + +void QSGNodeUpdater::leaveGeometryNode(QSGGeometryNode *g) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave geometry" << g; +#else + Q_UNUSED(g) +#endif +} + +void QSGNodeUpdater::enterOpacityNode(QSGOpacityNode *o) +{ + if (o->dirtyFlags() & QSGNode::DirtyOpacity) + ++m_force_update; + + qreal opacity = m_opacity_stack.last() * o->opacity(); + o->setCombinedOpacity(opacity); + m_opacity_stack.add(opacity); + +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter opacity" << o; +#endif +} + +void QSGNodeUpdater::leaveOpacityNode(QSGOpacityNode *o) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "leave opacity" << o; +#endif + if (o->flags() & QSGNode::DirtyOpacity) + --m_force_update; + + m_opacity_stack.pop_back(); +} + +void QSGNodeUpdater::visitChildren(QSGNode *n) +{ + for (QSGNode *c = n->firstChild(); c; c = c->nextSibling()) + visitNode(c); +} + +void QSGNodeUpdater::visitNode(QSGNode *n) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter:" << n; +#endif + + if (!n->dirtyFlags() && !m_force_update) + return; + if (n->isSubtreeBlocked()) + return; + + bool forceUpdate = n->dirtyFlags() & (QSGNode::DirtyNodeAdded | QSGNode::DirtyForceUpdate); + if (forceUpdate) + ++m_force_update; + + switch (n->type()) { + case QSGNode::TransformNodeType: { + QSGTransformNode *t = static_cast<QSGTransformNode *>(n); + enterTransformNode(t); + visitChildren(t); + leaveTransformNode(t); + break; } + case QSGNode::GeometryNodeType: { + QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n); + enterGeometryNode(g); + visitChildren(g); + leaveGeometryNode(g); + break; } + case QSGNode::ClipNodeType: { + QSGClipNode *c = static_cast<QSGClipNode *>(n); + enterClipNode(c); + visitChildren(c); + leaveClipNode(c); + break; } + case QSGNode::OpacityNodeType: { + QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n); + enterOpacityNode(o); + visitChildren(o); + leaveOpacityNode(o); + break; } + default: + visitChildren(n); + break; + } + + if (forceUpdate) + --m_force_update; + + n->clearDirty(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h new file mode 100644 index 0000000000..446bdefdc5 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODEUPDATER_P_H +#define NODEUPDATER_P_H + +#include "qsgnode.h" +#include <QtGui/private/qdatabuffer_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGNodeUpdater +{ +public: + QSGNodeUpdater(); + + virtual void updateStates(QSGNode *n); + virtual bool isNodeBlocked(QSGNode *n, QSGNode *root) const; + + void setToplevelOpacity(qreal alpha) { m_opacity_stack.last() = alpha; } + qreal toplevelOpacity() const { return m_opacity_stack.last(); } + +protected: + virtual void enterTransformNode(QSGTransformNode *); + virtual void leaveTransformNode(QSGTransformNode *); + void enterClipNode(QSGClipNode *c); + void leaveClipNode(QSGClipNode *c); + void enterOpacityNode(QSGOpacityNode *o); + void leaveOpacityNode(QSGOpacityNode *o); + void enterGeometryNode(QSGGeometryNode *); + void leaveGeometryNode(QSGGeometryNode *); + + void visitNode(QSGNode *n); + void visitChildren(QSGNode *n); + + + QDataBuffer<const QMatrix4x4 *> m_combined_matrix_stack; + QDataBuffer<qreal> m_opacity_stack; + const QSGClipNode *m_current_clip; + + int m_force_update; + + qreal m_toplevel_alpha; +}; + +QT_END_NAMESPACE + +#endif // NODEUPDATER_P_H diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp new file mode 100644 index 0000000000..b22631afae --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -0,0 +1,743 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrenderer_p.h" +#include "qsgnode.h" +#include "qsgmaterial.h" +#include "qsgnodeupdater_p.h" +#include "qsggeometry_p.h" + +#include <private/qsgadaptationlayer_p.h> + +#include <QOpenGLShaderProgram> +#include <qopenglframebufferobject.h> +#include <QtGui/qguiapplication.h> + +#include <qdatetime.h> + +QT_BEGIN_NAMESPACE + +//#define RENDERER_DEBUG +//#define QT_GL_NO_SCISSOR_TEST + + + +#define QSG_RENDERER_TIMING +#ifdef QSG_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); +static QTime frameTimer; +static int preprocessTime; +static int updatePassTime; +#endif + +void QSGBindable::clear(QSGRenderer::ClearMode mode) const +{ + GLuint bits = 0; + if (mode & QSGRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT; + if (mode & QSGRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT; + if (mode & QSGRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT; + glClear(bits); +} + +// Reactivate the color buffer after switching to the stencil. +void QSGBindable::reactivate() const +{ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +QSGBindableFbo::QSGBindableFbo(QOpenGLFramebufferObject *fbo) : m_fbo(fbo) +{ +} + + +void QSGBindableFbo::bind() const +{ + m_fbo->bind(); +} + +/*! + \class QSGRenderer + \brief The renderer class is the abstract baseclass use for rendering the + QML scene graph. + + The renderer is not tied to any particular surface. It expects a context to + be current and will render into that surface according to how the device rect, + viewport rect and projection transformation are set up. + + Rendering is a sequence of steps initiated by calling renderScene(). This will + effectively draw the scene graph starting at the root node. The QSGNode::preprocess() + function will be called for all the nodes in the graph, followed by an update + pass which updates all matrices, opacity, clip states and similar in the graph. + Because the update pass is called after preprocess, it is safe to modify the graph + during preprocess. To run a custom update pass over the graph, install a custom + QSGNodeUpdater using setNodeUpdater(). Once all the graphs dirty states are updated, + the virtual render() function is called. + + The render() function is implemented by QSGRenderer subclasses to render the graph + in the most optimal way for a given hardware. + + The renderer can make use of stencil, depth and color buffers in addition to the + scissor rect. + + \internal + */ + + +QSGRenderer::QSGRenderer(QSGContext *context) + : QObject() + , m_clear_color(Qt::transparent) + , m_clear_mode(ClearColorBuffer | ClearDepthBuffer) + , m_current_opacity(1) + , m_context(context) + , m_root_node(0) + , m_node_updater(0) + , m_bindable(0) + , m_changed_emitted(false) + , m_mirrored(false) + , m_is_rendering(false) + , m_vertex_buffer_bound(false) + , m_index_buffer_bound(false) +{ + initializeGLFunctions(); +} + + +QSGRenderer::~QSGRenderer() +{ + setRootNode(0); + delete m_node_updater; +} + +/*! + Returns the scene graph context for this renderer. + + \internal + */ + +QSGContext *QSGRenderer::context() +{ + return m_context; +} + + + + +/*! + Returns the node updater that this renderer uses to update states in the + scene graph. + + If no updater is specified a default one is constructed. + */ + +QSGNodeUpdater *QSGRenderer::nodeUpdater() const +{ + if (!m_node_updater) + const_cast<QSGRenderer *>(this)->m_node_updater = new QSGNodeUpdater(); + return m_node_updater; +} + + +/*! + Sets the node updater that this renderer uses to update states in the + scene graph. + + This will delete and override any existing node updater + */ +void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater) +{ + if (m_node_updater) + delete m_node_updater; + m_node_updater = updater; +} + + +void QSGRenderer::setRootNode(QSGRootNode *node) +{ + if (m_root_node == node) + return; + if (m_root_node) { + m_root_node->m_renderers.removeOne(this); + nodeChanged(m_root_node, QSGNode::DirtyNodeRemoved); + } + m_root_node = node; + if (m_root_node) { + Q_ASSERT(!m_root_node->m_renderers.contains(this)); + m_root_node->m_renderers << this; + nodeChanged(m_root_node, QSGNode::DirtyNodeAdded); + } +} + + +void QSGRenderer::renderScene() +{ + class B : public QSGBindable + { + public: + void bind() const { QOpenGLFramebufferObject::bindDefault(); } + } b; + renderScene(b); +} + +void QSGRenderer::renderScene(const QSGBindable &bindable) +{ + if (!m_root_node) + return; + + m_is_rendering = true; + + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + frameTimer.start(); + int bindTime; + int renderTime; +#endif + + m_bindable = &bindable; + preprocess(); + + bindable.bind(); +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + bindTime = frameTimer.elapsed(); +#endif + +#ifndef QT_NO_DEBUG + // Sanity check that attribute registers are disabled + { + GLint count; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count); + GLint enabled; + for (int i=0; i<count; ++i) { + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled); + if (enabled) { + qWarning("QSGRenderer: attribute %d is enabled, this can lead to memory corruption and crashes.", i); + } + } + } +#endif + + render(); +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + renderTime = frameTimer.elapsed(); +#endif + + glDisable(GL_SCISSOR_TEST); + m_is_rendering = false; + m_changed_emitted = false; + m_bindable = 0; + + if (m_vertex_buffer_bound) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_vertex_buffer_bound = false; + } + + if (m_index_buffer_bound) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + m_index_buffer_bound = false; + } + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) { + printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n", + preprocessTime, + updatePassTime - preprocessTime, + bindTime - updatePassTime, + renderTime - bindTime, + renderTime); + } +#endif +} + +void QSGRenderer::setProjectionMatrixToDeviceRect() +{ + setProjectionMatrixToRect(m_device_rect); +} + +void QSGRenderer::setProjectionMatrixToRect(const QRectF &rect) +{ + QMatrix4x4 matrix; + matrix.ortho(rect.x(), + rect.x() + rect.width(), + rect.y() + rect.height(), + rect.y(), + qreal(0.01), + -1); + setProjectionMatrix(matrix); +} + +void QSGRenderer::setProjectionMatrix(const QMatrix4x4 &matrix) +{ + m_projection_matrix = matrix; + // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. + m_mirrored = matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0; +} + +void QSGRenderer::setClearColor(const QColor &color) +{ + m_clear_color = color; +} + +/*! + Updates internal data structures and emits the sceneGraphChanged() signal. + + If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be + in the process of being destroyed. It is then not safe to downcast the node + pointer. +*/ + +void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +{ + Q_UNUSED(node); + Q_UNUSED(flags); + + if (flags & QSGNode::DirtyNodeAdded) + addNodesToPreprocess(node); + if (flags & QSGNode::DirtyNodeRemoved) + removeNodesToPreprocess(node); + + if (!m_changed_emitted && !m_is_rendering) { + // Premature overoptimization to avoid excessive signal emissions + m_changed_emitted = true; + emit sceneGraphChanged(); + } +} + +void QSGRenderer::materialChanged(QSGGeometryNode *, QSGMaterial *, QSGMaterial *) +{ +} + +void QSGRenderer::preprocess() +{ + Q_ASSERT(m_root_node); + + // We need to take a copy here, in case any of the preprocess calls deletes a node that + // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs + // For the default case, when this does not happen, the cost is neglishible. + QSet<QSGNode *> items = m_nodes_to_preprocess; + + for (QSet<QSGNode *>::const_iterator it = items.constBegin(); + it != items.constEnd(); ++it) { + QSGNode *n = *it; + if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) { + n->preprocess(); + } + } + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + preprocessTime = frameTimer.elapsed(); +#endif + + nodeUpdater()->setToplevelOpacity(context()->renderAlpha()); + nodeUpdater()->updateStates(m_root_node); + +#ifdef QSG_RENDERER_TIMING + if (qsg_render_timing) + updatePassTime = frameTimer.elapsed(); +#endif + +} + +void QSGRenderer::addNodesToPreprocess(QSGNode *node) +{ + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + addNodesToPreprocess(c); + if (node->flags() & QSGNode::UsePreprocess) + m_nodes_to_preprocess.insert(node); +} + +void QSGRenderer::removeNodesToPreprocess(QSGNode *node) +{ + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + removeNodesToPreprocess(c); + if (node->flags() & QSGNode::UsePreprocess) + m_nodes_to_preprocess.remove(node); +} + + +/*! + Convenience function to set up the stencil buffer for clipping based on \a clip. + + If the clip is a pixel aligned rectangle, this function will use glScissor instead + of stencil. + */ + +QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip) +{ + if (!clip) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + return NoClip; + } + + bool stencilEnabled = false; + bool scissorEnabled = false; + + glDisable(GL_SCISSOR_TEST); + + int clipDepth = 0; + QRect clipRect; + while (clip) { + QMatrix4x4 m = m_current_projection_matrix; + if (clip->matrix()) + m *= *clip->matrix(); + + // TODO: Check for multisampling and pixel grid alignment. + bool isRectangleWithNoPerspective = clip->isRectangular() + && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1)); + bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0)); + bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1)); + + if (isRectangleWithNoPerspective && (noRotate || isRotate90)) { + QRectF bbox = clip->clipRect(); + qreal invW = 1 / m(3, 3); + qreal fx1, fy1, fx2, fy2; + if (noRotate) { + fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW; + fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW; + fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW; + fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW; + } else { + Q_ASSERT(isRotate90); + fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW; + fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW; + fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW; + fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW; + } + + if (fx1 > fx2) + qSwap(fx1, fx2); + if (fy1 > fy2) + qSwap(fy1, fy2); + + GLint ix1 = qRound((fx1 + 1) * m_device_rect.width() * qreal(0.5)); + GLint iy1 = qRound((fy1 + 1) * m_device_rect.height() * qreal(0.5)); + GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5)); + GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5)); + + if (!scissorEnabled) { + clipRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); + glEnable(GL_SCISSOR_TEST); + scissorEnabled = true; + } else { + clipRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); + } + + glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + } else { + if (!stencilEnabled) { + if (!m_clip_program.isLinked()) { + m_clip_program.addShaderFromSourceCode(QOpenGLShader::Vertex, + "attribute highp vec4 vCoord; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vCoord; \n" + "}"); + m_clip_program.addShaderFromSourceCode(QOpenGLShader::Fragment, + "void main() { \n" + " gl_FragColor = vec4(0.81, 0.83, 0.12, 1.0); \n" // Trolltech green ftw! + "}"); + m_clip_program.bindAttributeLocation("vCoord", 0); + m_clip_program.link(); + m_clip_matrix_id = m_clip_program.uniformLocation("matrix"); + } + + glStencilMask(0xff); // write mask + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + + m_clip_program.bind(); + m_clip_program.enableAttributeArray(0); + + stencilEnabled = true; + } + + glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass + + const QSGGeometry *g = clip->geometry(); + Q_ASSERT(g->attributeCount() > 0); + const QSGGeometry::Attribute *a = g->attributes(); + glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), g->vertexData()); + + m_clip_program.setUniformValue(m_clip_matrix_id, m); + if (g->indexCount()) { + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + } else { + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + } + + ++clipDepth; + } + + clip = clip->clipList(); + } + + if (stencilEnabled) { + m_clip_program.disableAttributeArray(0); + glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass + glStencilMask(0); // write mask + bindable()->reactivate(); + } else { + glDisable(GL_STENCIL_TEST); + } + + if (!scissorEnabled) + glDisable(GL_SCISSOR_TEST); + + return stencilEnabled ? StencilClip : ScissorClip; +} + + + +static inline int size_of_type(GLenum type) +{ + static int sizes[] = { + sizeof(char), + sizeof(unsigned char), + sizeof(short), + sizeof(unsigned short), + sizeof(int), + sizeof(unsigned int), + sizeof(float), + 2, + 3, + 4, + sizeof(double) + }; + Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE + return sizes[type - GL_BYTE]; +} + + +class QSGRendererVBOGeometryData : public QSGGeometryData +{ +public: + QSGRendererVBOGeometryData() + : vertexBuffer(0) + , indexBuffer(0) + { + } + + ~QSGRendererVBOGeometryData() + { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return; + QOpenGLFunctions *func = ctx->functions(); + if (vertexBuffer) + func->glDeleteBuffers(1, &vertexBuffer); + if (indexBuffer) + func->glDeleteBuffers(1, &indexBuffer); + } + + GLuint vertexBuffer; + GLuint indexBuffer; + + static QSGRendererVBOGeometryData *get(const QSGGeometry *g) { + QSGRendererVBOGeometryData *gd = static_cast<QSGRendererVBOGeometryData *>(QSGGeometryData::data(g)); + if (!gd) { + gd = new QSGRendererVBOGeometryData; + QSGGeometryData::install(g, gd); + } + return gd; + } + +}; + +static inline GLenum qt_drawTypeForPattern(QSGGeometry::DataPattern p) +{ + Q_ASSERT(p > 0 && p <= 3); + static GLenum drawTypes[] = { 0, + GL_STREAM_DRAW, + GL_DYNAMIC_DRAW, + GL_STATIC_DRAW + }; + return drawTypes[p]; +} + + +/*! + Issues the GL draw call for the geometry \a g using the material \a shader. + + The function assumes that attributes have been bound and set up prior + to making this call. + + \internal + */ + +void QSGRenderer::draw(const QSGMaterialShader *shader, const QSGGeometry *g) +{ + // ### remove before final release... + static bool use_vbo = !QGuiApplication::arguments().contains(QLatin1String("--no-vbo")); + + const void *vertexData; + int vertexByteSize = g->vertexCount() * g->sizeOfVertex(); + if (use_vbo && g->vertexDataPattern() != QSGGeometry::AlwaysUploadPattern && vertexByteSize > 1024) { + + // The base pointer for a VBO is 0 + vertexData = 0; + + bool updateData = QSGGeometryData::hasDirtyVertexData(g); + QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g); + if (!gd->vertexBuffer) { + glGenBuffers(1, &gd->vertexBuffer); + updateData = true; + } + + glBindBuffer(GL_ARRAY_BUFFER, gd->vertexBuffer); + m_vertex_buffer_bound = true; + + if (updateData) { + glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(), + qt_drawTypeForPattern(g->vertexDataPattern())); + QSGGeometryData::clearDirtyVertexData(g); + } + + } else { + if (m_vertex_buffer_bound) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_vertex_buffer_bound = false; + } + vertexData = g->vertexData(); + } + + // Bind the vertices to attributes... + char const *const *attrNames = shader->attributeNames(); + int offset = 0; + for (int j = 0; attrNames[j]; ++j) { + if (!*attrNames[j]) + continue; + Q_ASSERT_X(j < g->attributeCount(), "QSGRenderer::bindGeometry()", "Geometry lacks attribute required by material"); + const QSGGeometry::Attribute &a = g->attributes()[j]; + Q_ASSERT_X(j == a.position, "QSGRenderer::bindGeometry()", "Geometry does not have continuous attribute positions"); + +#if defined(QT_OPENGL_ES_2) + GLboolean normalize = a.type != GL_FLOAT; +#else + GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE; +#endif + glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (char *) vertexData + offset); + offset += a.tupleSize * size_of_type(a.type); + } + + // Set up the indices... + const void *indexData; + if (use_vbo && g->indexDataPattern() != QSGGeometry::AlwaysUploadPattern && g->indexCount() > 512) { + + // Base pointer for a VBO is 0 + indexData = 0; + + bool updateData = QSGGeometryData::hasDirtyIndexData(g); + QSGRendererVBOGeometryData *gd = QSGRendererVBOGeometryData::get(g); + if (!gd->indexBuffer) { + glGenBuffers(1, &gd->indexBuffer); + updateData = true; + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gd->indexBuffer); + m_index_buffer_bound = true; + + if (updateData) { + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + g->indexCount() * g->sizeOfIndex(), + g->indexData(), + qt_drawTypeForPattern(g->indexDataPattern())); + QSGGeometryData::clearDirtyIndexData(g); + } + + } else { + if (m_index_buffer_bound) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + m_index_buffer_bound = false; + } + indexData = g->indexData(); + } + + + // draw the stuff... + if (g->indexCount()) { + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), indexData); + } else { + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + } + + // We leave buffers bound for now... They will be reset by bind on next draw() or + // set back to 0 if next draw is not using VBOs + +} + +/*! + \class QSGNodeDumper + \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console. + + This class is solely for debugging purposes. + + \internal + */ + +void QSGNodeDumper::dump(QSGNode *n) +{ + QSGNodeDumper dump; + dump.visitNode(n); +} + +void QSGNodeDumper::visitNode(QSGNode *n) +{ + qDebug() << QString(m_indent * 2, QLatin1Char(' ')) << n; + QSGNodeVisitor::visitNode(n); +} + +void QSGNodeDumper::visitChildren(QSGNode *n) +{ + ++m_indent; + QSGNodeVisitor::visitChildren(n); + --m_indent; +} + + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h new file mode 100644 index 0000000000..e5667c710a --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include <qset.h> +#include <qhash.h> + +#include <qcolor.h> +#include <qopenglfunctions.h> +#include <qopenglshaderprogram.h> + +#include "qsgnode.h" +#include "qsgmaterial.h" +#include <QtQuick/qsgtexture.h> + +#include <QtQuick/private/qsgcontext_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGMaterialShader; +struct QSGMaterialType; +class QOpenGLFramebufferObject; +class TextureReference; +class QSGBindable; +class QSGNodeUpdater; + +class Q_QUICK_EXPORT QSGRenderer : public QObject, public QOpenGLFunctions +{ + Q_OBJECT +public: + enum ClipType + { + NoClip, + ScissorClip, + StencilClip + }; + + enum ClearModeBit + { + ClearColorBuffer = 0x0001, + ClearDepthBuffer = 0x0002, + ClearStencilBuffer = 0x0004 + }; + Q_DECLARE_FLAGS(ClearMode, ClearModeBit) + + QSGRenderer(QSGContext *context); + virtual ~QSGRenderer(); + + void setRootNode(QSGRootNode *node); + QSGRootNode *rootNode() const { return m_root_node; } + + void setDeviceRect(const QRect &rect) { m_device_rect = rect; } + inline void setDeviceRect(const QSize &size) { setDeviceRect(QRect(QPoint(), size)); } + QRect deviceRect() const { return m_device_rect; } + + void setViewportRect(const QRect &rect) { m_viewport_rect = rect; } + inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); } + QRect viewportRect() const { return m_viewport_rect; } + + // Accessed by QSGMaterialShader::RenderState. + QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; } + QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; } + QMatrix4x4 currentCombinedMatrix() const { return m_current_projection_matrix * m_current_model_view_matrix; } + qreal currentOpacity() const { return m_current_opacity; } + + void setProjectionMatrixToDeviceRect(); + void setProjectionMatrixToRect(const QRectF &rect); + void setProjectionMatrix(const QMatrix4x4 &matrix); + QMatrix4x4 projectionMatrix() const { return m_projection_matrix; } + bool isMirrored() const { return m_mirrored; } + + void setClearColor(const QColor &color); + QColor clearColor() const { return m_clear_color; } + + QOpenGLContext *glContext() const { Q_ASSERT(m_context); return m_context->glContext(); } + + QSGContext *context(); + + void renderScene(); + void renderScene(const QSGBindable &bindable); + virtual void nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags); + virtual void materialChanged(QSGGeometryNode *node, QSGMaterial *from, QSGMaterial *to); + + QSGNodeUpdater *nodeUpdater() const; + void setNodeUpdater(QSGNodeUpdater *updater); + + inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const; + + void setClearMode(ClearMode mode) { m_clear_mode = mode; } + ClearMode clearMode() const { return m_clear_mode; } + +signals: + void sceneGraphChanged(); // Add, remove, ChangeFlags changes... + +protected: + void draw(const QSGMaterialShader *material, const QSGGeometry *g); + + virtual void render() = 0; + QSGRenderer::ClipType updateStencilClip(const QSGClipNode *clip); + + const QSGBindable *bindable() const { return m_bindable; } + + virtual void preprocess(); + + void addNodesToPreprocess(QSGNode *node); + void removeNodesToPreprocess(QSGNode *node); + + + QColor m_clear_color; + ClearMode m_clear_mode; + QMatrix4x4 m_current_projection_matrix; + QMatrix4x4 m_current_model_view_matrix; + qreal m_current_opacity; + + QSGContext *m_context; + +private: + QSGRootNode *m_root_node; + QSGNodeUpdater *m_node_updater; + + QRect m_device_rect; + QRect m_viewport_rect; + + QSet<QSGNode *> m_nodes_to_preprocess; + + QMatrix4x4 m_projection_matrix; + QOpenGLShaderProgram m_clip_program; + int m_clip_matrix_id; + + const QSGBindable *m_bindable; + + uint m_changed_emitted : 1; + uint m_mirrored : 1; + uint m_is_rendering : 1; + + uint m_vertex_buffer_bound : 1; + uint m_index_buffer_bound : 1; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderer::ClearMode) + +class Q_QUICK_EXPORT QSGBindable +{ +public: + virtual ~QSGBindable() { } + virtual void bind() const = 0; + virtual void clear(QSGRenderer::ClearMode mode) const; + virtual void reactivate() const; +}; + +class QSGBindableFbo : public QSGBindable +{ +public: + QSGBindableFbo(QOpenGLFramebufferObject *fbo); + virtual void bind() const; +private: + QOpenGLFramebufferObject *m_fbo; +}; + + + +QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState::DirtyStates dirty) const +{ + QSGMaterialShader::RenderState s; + s.m_dirty = dirty; + s.m_data = this; + return s; +} + + +class Q_QUICK_EXPORT QSGNodeDumper : public QSGNodeVisitor { + +public: + static void dump(QSGNode *n); + + QSGNodeDumper() : m_indent(0) {} + void visitNode(QSGNode *n); + void visitChildren(QSGNode *n); + +private: + int m_indent; +}; + + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // RENDERER_H |