diff options
Diffstat (limited to 'src/declarative/scenegraph/coreapi')
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp | 528 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h | 96 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsggeometry.cpp | 310 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsggeometry.h | 254 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgmaterial.cpp | 199 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgmaterial.h | 141 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.cpp | 380 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.h | 104 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgmatrix4x4stack_p.h | 73 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgnode.cpp | 837 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgnode.h | 363 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgnodeupdater.cpp | 243 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgnodeupdater_p.h | 87 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgrenderer.cpp | 544 | ||||
-rw-r--r-- | src/declarative/scenegraph/coreapi/qsgrenderer_p.h | 219 |
15 files changed, 4378 insertions, 0 deletions
diff --git a/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp new file mode 100644 index 0000000000..4569e7fc32 --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer.cpp @@ -0,0 +1,528 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#define GL_GLEXT_PROTOTYPES + +#include "qsgdefaultrenderer_p.h" +#include "qsgmaterial.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qapplication.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(); +} + +// Minimum heap. +template <typename T, int prealloc = 256> +class Heap +{ +public: + void insert(const T &x); + const T &top() const { return v[0]; } + T 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; } + QVarLengthArray<T, prealloc> v; +}; + +template <typename T, int prealloc> +void Heap<T, prealloc>::insert(const T &x) +{ + int i = v.size(); + v.append(x); + while (i != 0 && v[i] < v[parent(i)]) { + qSwap(v[parent(i)], v[i]); + i = parent(i); + } +} + +template <typename T, int prealloc> +T Heap<T, prealloc>::pop() +{ + T x = top(); + if (v.size() > 1) + qSwap(v[0], v[v.size() - 1]); + v.resize(v.size() - 1); + int i = 0; + while (left(i) < v.size()) { + int low = left(i); + if (right(i) < v.size() && v[right(i)] < v[low]) + low = right(i); + if (!(v[low] < v[i])) + break; + qSwap(v[i], v[low]); + i = low; + } + return x; +} + + +QMLRenderer::QMLRenderer(QSGContext *context) + : QSGRenderer(context) + , 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 QMLRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +{ + QSGRenderer::nodeChanged(node, flags); + + quint32 rebuildFlags = QSGNode::DirtyNodeAdded | QSGNode::DirtyNodeRemoved | QSGNode::DirtyMaterial | QSGNode::DirtyOpacity; + + if (flags & rebuildFlags) + m_rebuild_lists = true; + + if (flags & (rebuildFlags | QSGNode::DirtyClipList)) + m_needs_sorting = true; +} + +void QMLRenderer::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_projectionMatrix = projectMatrix(); + m_projectionMatrix.push(); + m_modelViewMatrix.setToIdentity(); + + m_currentClip = 0; + glDisable(GL_STENCIL_TEST); + + m_currentMaterial = 0; + m_currentProgram = 0; + m_currentMatrix = 0; + + if (m_rebuild_lists) { + m_opaqueNodes.clear(); + m_transparentNodes.clear(); + m_currentRenderOrder = 1; + buildLists(rootNode()); + m_rebuild_lists = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeLists = debugTimer.elapsed(); +#endif + + + if (m_needs_sorting) { + qSort(m_opaqueNodes.begin(), m_opaqueNodes.end(), + 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(); + + m_projectionMatrix.pop(); + +#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 + +} + +class Foo : public QPair<int, QSGGeometryNode *> +{ +public: + Foo() { } + Foo(int i, QSGGeometryNode *n) : QPair<int, QSGGeometryNode *>(i, n) { } + bool operator < (const Foo &other) const { return nodeLessThan(second, other.second); } +}; + +void QMLRenderer::setSortFrontToBackEnabled(bool sort) +{ + printf("setting sorting to... %d\n", sort); + m_sort_front_to_back = sort; +} + +bool QMLRenderer::isSortFrontToBackEnabled() const +{ + return m_sort_front_to_back; +} + +void QMLRenderer::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.append(geomNode); + } else { + geomNode->setRenderOrder(m_currentRenderOrder); + m_opaqueNodes.append(geomNode); + m_currentRenderOrder += 2; + } + } + + int count = node->childCount(); + if (!count) + return; + +#ifdef FORCE_NO_REORDER + static bool reorder = false; +#else + static bool reorder = !qApp->arguments().contains(QLatin1String("--no-reorder")); +#endif + + if (reorder && count > 1 && (node->flags() & QSGNode::ChildrenDoNotOverlap)) { + QVarLengthArray<int, 16> beginIndices(count); + QVarLengthArray<int, 16> endIndices(count); + int baseCount = m_transparentNodes.size(); + for (int i = 0; i < count; ++i) { + beginIndices[i] = m_transparentNodes.size(); + buildLists(node->childAtIndex(i)); + endIndices[i] = m_transparentNodes.size(); + } + + Heap<Foo, 16> heap; + m_tempNodes.clear(); + int childNodeCount = m_transparentNodes.size() - baseCount; + while (childNodeCount) { + for (int i = 0; i < count; ++i) { + if (beginIndices[i] != endIndices[i]) + heap.insert(Foo(i, m_transparentNodes.at(beginIndices[i]++))); + } + while (!heap.isEmpty()) { + Foo foo = heap.pop(); + m_tempNodes.append(foo.second); + --childNodeCount; + int i = foo.first; + if (beginIndices[i] != endIndices[i] && !nodeLessThan(m_transparentNodes.at(beginIndices[i]), foo.second)) + heap.insert(Foo(i, m_transparentNodes.at(beginIndices[i]++))); + } + } + Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount); + + m_transparentNodes.resize(baseCount); + m_transparentNodes << m_tempNodes; + } else { + for (int i = 0; i < count; ++i) + buildLists(node->childAtIndex(i)); + } +} + +void QMLRenderer::renderNodes(const QVector<QSGGeometryNode *> &list) +{ + const float scale = 1.0f / m_currentRenderOrder; + int count = list.count(); + int currentRenderOrder = 0x80000000; + + //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_modelViewMatrix = *m_currentMatrix; + else + m_modelViewMatrix.setToIdentity(); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + bool changeOpacity = m_render_opacity != geomNode->inheritedOpacity(); + if (changeOpacity) { + updates |= QSGMaterialShader::RenderState::DirtyOpacity; + m_render_opacity = geomNode->inheritedOpacity(); + } + + + Q_ASSERT(geomNode->activeMaterial()); + + QSGMaterial *material = geomNode->activeMaterial(); + QSGMaterialShader *program = m_context->prepareMaterial(material); + + bool changeClip = geomNode->clipList() != m_currentClip; + QSGRenderer::ClipType clipType = QSGRenderer::NoClip; + if (changeClip) { + clipType = updateStencilClip(geomNode->clipList()); + m_currentClip = geomNode->clipList(); +#ifdef FORCE_NO_REORDER + glDepthMask(false); +#else + glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_render_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_renderOrderMatrix(2, 3) = currentRenderOrder * scale; + m_projectionMatrix.pop(); + m_projectionMatrix.push(); + m_projectionMatrix *= m_renderOrderMatrix; + 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(); + bindGeometry(program, g); + draw(geomNode); + +#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/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h new file mode 100644 index 0000000000..805388a06f --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLRENDERER_H +#define QMLRENDERER_H + +#include "qsgrenderer_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QMLRenderer : public QSGRenderer +{ + Q_OBJECT +public: + QMLRenderer(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 QVector <QSGGeometryNode *> &list); + + const QSGClipNode *m_currentClip; + QSGMaterial *m_currentMaterial; + QSGMaterialShader *m_currentProgram; + const QMatrix4x4 *m_currentMatrix; + QMatrix4x4 m_renderOrderMatrix; + QVector<QSGGeometryNode *> m_opaqueNodes; + QVector<QSGGeometryNode *> m_transparentNodes; + QVector<QSGGeometryNode *> m_tempNodes; + + 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/declarative/scenegraph/coreapi/qsggeometry.cpp b/src/declarative/scenegraph/coreapi/qsggeometry.cpp new file mode 100644 index 0000000000..1bfffae0bf --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsggeometry.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsggeometry.h" + +QT_BEGIN_NAMESPACE + + +/*! + Convenience function which returns attributes to be used for 2D solid + color drawing. + */ + +const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() +{ + static Attribute data[] = { + { 0, 2, GL_FLOAT } + }; + 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[] = { + { 0, 2, GL_FLOAT }, + { 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[] = { + { 0, 2, GL_FLOAT }, + { 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_owns_data(false) +{ + Q_ASSERT(m_attributes.count > 0); + Q_ASSERT(m_attributes.stride > 0); + + // Because allocate reads m_vertex_count, m_index_count and m_owns_data, these + // need to be set before calling allocate... + allocate(vertexCount, indexCount); +} + +QSGGeometry::~QSGGeometry() +{ + if (m_owns_data) + qFree(m_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; + } + +} + +/*! + 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(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/scenegraph/coreapi/qsggeometry.h b/src/declarative/scenegraph/coreapi/qsggeometry.h new file mode 100644 index 0000000000..514fdc466f --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsggeometry.h @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGGEOMETRY_H +#define QSGGEOMETRY_H + +#include <QtOpenGL/qgl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QSGGeometry +{ +public: + struct Attribute + { + int position; + int tupleSize; + int type; + }; + + 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(); + + 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(); + + 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 stride() const { return m_attributes.stride; } + + static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); + static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + +private: + 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; + + void *m_reserved_pointer; + + uint m_owns_data : 1; + uint m_reserved_bits : 31; + + 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; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGGEOMETRY_H diff --git a/src/declarative/scenegraph/coreapi/qsgmaterial.cpp b/src/declarative/scenegraph/coreapi/qsgmaterial.cpp new file mode 100644 index 0000000000..4c4274419e --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgmaterial.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmaterial.h" +#include "qsgrenderer_p.h" + +QT_BEGIN_NAMESPACE + +QSGMaterialShader::QSGMaterialShader() + : m_compiled(false) +{ +} + +void QSGMaterialShader::activate() +{ + if (!m_compiled) + compile(); + + m_program.bind(); + char const *const *attr = attributeNames(); + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + m_program.enableAttributeArray(i); + } +} + +void QSGMaterialShader::deactivate() +{ + char const *const *attr = attributeNames(); + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + m_program.disableAttributeArray(i); + } +} + +void QSGMaterialShader::updateState(const RenderState &, QSGMaterial *, QSGMaterial *) +{ +} + +void QSGMaterialShader::compile() +{ + Q_ASSERT(!m_compiled); + + m_program.addShaderFromSourceCode(QGLShader::Vertex, vertexShader()); + m_program.addShaderFromSourceCode(QGLShader::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]) + m_program.bindAttributeLocation(attr[i], i); + } +#else + for (int i = 0; attr[i]; ++i) { + if (*attr[i]) + m_program.bindAttributeLocation(attr[i], i); + } +#endif + + if (!m_program.link()) { + qWarning("QSGMaterialShader: Shader compilation failed:"); + qWarning() << m_program.log(); + } + + m_compiled = true; + initialize(); +} + + +float QSGMaterialShader::RenderState::opacity() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->renderOpacity(); +} + +QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->combinedMatrix(); +} + +QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const +{ + Q_ASSERT(m_data); + return const_cast<QSGRenderer *>(static_cast<const QSGRenderer *>(m_data))->modelViewMatrix().top(); +} + +QRect QSGMaterialShader::RenderState::viewportRect() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->viewportRect(); +} + +QRect QSGMaterialShader::RenderState::deviceRect() const +{ + Q_ASSERT(m_data); + return static_cast<const QSGRenderer *>(m_data)->deviceRect(); +} + +const QGLContext *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 + +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 +} + +void QSGMaterial::setFlag(Flags flags, bool set) +{ + if (set) + m_flags |= flags; + else + m_flags &= ~flags; +} + +int QSGMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + return qint64(this) - qint64(other); +} + +QT_END_NAMESPACE diff --git a/src/declarative/scenegraph/coreapi/qsgmaterial.h b/src/declarative/scenegraph/coreapi/qsgmaterial.h new file mode 100644 index 0000000000..c1513956d0 --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgmaterial.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MATERIAL_H +#define MATERIAL_H + +#include <qglshaderprogram.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGMaterial; + +class Q_DECLARATIVE_EXPORT QSGMaterialShader +{ +public: + class Q_DECLARATIVE_EXPORT RenderState { + public: + enum DirtyState + { + DirtyMatrix = 0x0001, + DirtyOpacity = 0x0002 + }; + Q_DECLARE_FLAGS(DirtyStates, DirtyState) + + inline DirtyStates dirtyState() 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; + + const QGLContext *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. + +protected: + void compile(); + virtual void initialize() { } + + virtual const char *vertexShader() const = 0; + virtual const char *fragmentShader() const = 0; + + QGLShaderProgram m_program; + bool m_compiled; + void *m_reserved; +}; + +struct QSGMaterialType { }; + +class Q_DECLARATIVE_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; } + +protected: + void setFlag(Flags flags, bool set); + +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/declarative/scenegraph/coreapi/qsgmatrix4x4stack.cpp b/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.cpp new file mode 100644 index 0000000000..07ba21d17c --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmatrix4x4stack.h" +#include "qsgmatrix4x4stack_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGMatrix4x4Stack + \brief The QSGMatrix4x4Stack class manages stacks of transformation matrices in GL applications. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::enablers + + Transformation matrices are one of the basic building blocks of + 3D applications, allowing object models to be positioned, scaled, + rotated, and projected onto the screen. + + GL systems support several standard kinds of matrices, particularly + modelview and projection matrices. These matrices are typically + organized into stacks, which allow the current matrix state to be + saved with push() and restored later with pop(). + + QSGMatrix4x4Stack assists QGLPainter with the management of matrix + stacks, providing operations to set and modify transformation + matrices in each of the standard matrix stacks. + + In the following example, a standard orthographic projection matrix for a + view is set via the QGLPainter::projectionMatrix() stack, and + then a modelview matrix is set via the QGLPainter::modelViewMatrix() + stack to scale and translate an object prior to drawing: + + \code + QGLPainter painter(this); + + QMatrix4x4 projm; + projm.ortho(window->rect()); + painter.projectionMatrix() = projm; + + painter.modelViewMatrix().setToIdentity(); + painter.modelViewMatrix().translate(-1.0f, 2.0f, 0.0f); + painter.modelViewMatrix().scale(0.5f); + \endcode + + Later, the application can save the current modelview matrix state + and draw a different object with a different modelview matrix: + + \code + painter.modelViewMatrix().push(); + painter.modelViewMatrix().setToIdentity(); + painter.modelViewMatrix().scale(2.0f); + \endcode + + For efficiency, the matrix values are kept client-side until they + are needed by a QGLPainter::draw() operation. Until then, changes + to the matrix will not be reflected in the GL server. The application + can force the GL server to update the server with a call to + QGLPainter::update(). + + QSGMatrix4x4Stack is supported on all GL platforms, including OpenGL/ES 2.0 + which doesn't support matrix stacks natively. On that platform, the + matrix stack is simulated in client memory. When the application + selects a shader program to draw under OpenGL/ES 2.0, it calls + top() to obtain the actual value to be set on the shader program. + + \sa QGLPainter +*/ + +/*! + Creates a matrix stack. +*/ +QSGMatrix4x4Stack::QSGMatrix4x4Stack() + : d_ptr(new QSGMatrix4x4StackPrivate) +{ +} + +/*! + Destroy this matrix stack. +*/ +QSGMatrix4x4Stack::~QSGMatrix4x4Stack() +{ +} + +/*! + Pushes the current matrix onto the matrix stack. The matrix can + be restored with pop(). The new top of stack will have the + same value as the previous top of stack. + + The depths of the traditional \c{GL_MODELVIEW} and \c{GL_PROJECTION} + matrix stacks in the GL server are system-dependent and easy to + overflow in nested rendering code using \c{glPushMatrix()}. + By contrast, the push() function provides an arbitrary-sized stack + in client memory. + + \sa pop(), top() +*/ +void QSGMatrix4x4Stack::push() +{ + Q_D(QSGMatrix4x4Stack); + d->stack.push(d->matrix); +} + +/*! + Pops the top-most matrix from this matrix stack and sets the + current matrix to the next value down. Does nothing if the + matrix stack contains a single entry. + + \sa push() +*/ +void QSGMatrix4x4Stack::pop() +{ + Q_D(QSGMatrix4x4Stack); + if (!d->stack.isEmpty()) + d->matrix = d->stack.pop(); + d->isDirty = true; +} + +/*! + Set the matrix at the top of this matrix stack to the identity matrix. + + \sa operator=() +*/ +void QSGMatrix4x4Stack::setToIdentity() +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.setToIdentity(); + d->isDirty = true; +} + +/*! + Returns a const reference to the current matrix at the top of this + matrix stack. This is typically used to fetch the matrix so it can + be set on user-defined shader programs. + + \sa operator=() +*/ +const QMatrix4x4 &QSGMatrix4x4Stack::top() const +{ + Q_D(const QSGMatrix4x4Stack); + return d->matrix; +} + +/*! + \fn QSGMatrix4x4Stack::operator const QMatrix4x4 &() const + + Returns a const reference to the current matrix at the top of + this matrix stack. + + \sa top() +*/ + +/*! + Assigns \a matrix to the matrix at the top of this matrix stack. + + \sa top() +*/ +QSGMatrix4x4Stack& QSGMatrix4x4Stack::operator=(const QMatrix4x4& matrix) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix = matrix; + d->isDirty = true; + return *this; +} + +/*! + Multiplies the matrix at the top of this matrix stack by \a matrix. + + \sa top() +*/ +QSGMatrix4x4Stack& QSGMatrix4x4Stack::operator*=(const QMatrix4x4& matrix) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix *= matrix; + d->isDirty = true; + return *this; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that translates coordinates by (\a x, \a y, \a z). The following example + translates the modelview matrix by (1, -3, 0): + + \code + QGLPainter painter(this); + painter.modelViewMatrix().translate(1.0f, -3.0f, 0.0f); + \endcode + + \sa scale(), rotate() +*/ +void QSGMatrix4x4Stack::translate(qreal x, qreal y, qreal z) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.translate(x, y, z); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix statck by another + that translates coordinates by the components of \a vector. + + \sa scale(), rotate() +*/ +void QSGMatrix4x4Stack::translate(const QVector3D& vector) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.translate(vector); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that scales coordinates by the components \a x, \a y, and \a z. + The following example scales the modelview matrix by (1, 2, 1): + + \code + QGLPainter painter(this); + painter.modelViewMatrix().scale(1.0f, 2.0f, 1.0f); + \endcode + + \sa translate(), rotate() +*/ +void QSGMatrix4x4Stack::scale(qreal x, qreal y, qreal z) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.scale(x, y, z); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that scales coordinates by the given \a factor. The following example + scales the modelview matrix by a factor of 2: + + \code + QGLPainter painter(this); + painter.modelViewMatrix().scale(2.0f); + \endcode + + \sa translate(), rotate() +*/ +void QSGMatrix4x4Stack::scale(qreal factor) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.scale(factor); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that scales coordinates by the components of \a vector. + + \sa translate(), rotate() +*/ +void QSGMatrix4x4Stack::scale(const QVector3D& vector) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.scale(vector); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that rotates coordinates through \a angle degrees about the vector + (\a x, \a y, \a z). The following example rotates the modelview + matrix by 45 degress about the vector (1, -3, 0): + + \code + QGLPainter painter(this); + painter.modelViewMatrix().rotate(45.0f, 1.0f, -3.0f, 0.0f); + \endcode + + \sa scale(), translate() +*/ +void QSGMatrix4x4Stack::rotate(qreal angle, qreal x, qreal y, qreal z) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.rotate(angle, x, y, z); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that rotates coordinates through \a angle degrees about \a vector. + + \sa scale(), translate() +*/ +void QSGMatrix4x4Stack::rotate(qreal angle, const QVector3D& vector) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.rotate(angle, vector); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by the + \a quaternion. Thus \c {painter->modelViewMatrix().rotate(quaternion)} + is equivalent to the following code: + \code + QMatrix4x4 mat; + mat.rotate(quaternion); + painter->modelViewMatrix() *= mat; + \endcode + which rotates coordinates according to the given \a quaternion. + + \sa scale(), translate() +*/ +void QSGMatrix4x4Stack::rotate(const QQuaternion &quaternion) +{ + Q_D(QSGMatrix4x4Stack); + d->matrix.rotate(quaternion); + d->isDirty = true; +} + +/*! + Returns true if the top of this matrix stack has been modified; + false otherwise. + + \sa setDirty() +*/ +bool QSGMatrix4x4Stack::isDirty() const +{ + Q_D(const QSGMatrix4x4Stack); + return d->isDirty; +} + +/*! + Sets the \a dirty flag on this matrix stack, which indicates + if it has been modified. + + A matrix stack may also be set to dirty by translate(), + scale(), operator*(), etc. + + \sa isDirty() +*/ +void QSGMatrix4x4Stack::setDirty(bool dirty) +{ + Q_D(QSGMatrix4x4Stack); + d->isDirty = dirty; +} + +QT_END_NAMESPACE diff --git a/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.h b/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.h new file mode 100644 index 0000000000..2336598fdc --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGMATRIX4X4STACK_H +#define QSGMATRIX4X4STACK_H + +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGMatrix4x4StackPrivate; + +class Q_DECLARATIVE_EXPORT QSGMatrix4x4Stack +{ +public: + QSGMatrix4x4Stack(); + ~QSGMatrix4x4Stack(); + + const QMatrix4x4 &top() const; + + void push(); + void pop(); + + void setToIdentity(); + + void translate(qreal x, qreal y, qreal z); + void translate(const QVector3D& vector); + void scale(qreal x, qreal y, qreal z); + void scale(qreal factor); + void scale(const QVector3D& vector); + void rotate(qreal angle, qreal x, qreal y, qreal z); + void rotate(qreal angle, const QVector3D& vector); + void rotate(const QQuaternion &quaternion); + + QSGMatrix4x4Stack& operator=(const QMatrix4x4& matrix); + QSGMatrix4x4Stack& operator*=(const QMatrix4x4& matrix); + + operator const QMatrix4x4 &() const; + + bool isDirty() const; + void setDirty(bool dirty); + +private: + Q_DISABLE_COPY(QSGMatrix4x4Stack) + Q_DECLARE_PRIVATE(QSGMatrix4x4Stack) + + QScopedPointer<QSGMatrix4x4StackPrivate> d_ptr; + + friend class QGLPainter; +}; + +inline QSGMatrix4x4Stack::operator const QMatrix4x4 &() const +{ + return top(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack_p.h b/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack_p.h new file mode 100644 index 0000000000..6e5c08ca03 --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgmatrix4x4stack_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGMATRIX4X4STACK_P_H +#define QSGMATRIX4X4STACK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qstack.h> + +QT_BEGIN_NAMESPACE + +class QSGMatrix4x4StackPrivate +{ +public: + QSGMatrix4x4StackPrivate() : isDirty(true) {} + + QMatrix4x4 matrix; + QStack<QMatrix4x4> stack; + bool isDirty; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/scenegraph/coreapi/qsgnode.cpp b/src/declarative/scenegraph/coreapi/qsgnode.cpp new file mode 100644 index 0000000000..5d84086457 --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgnode.cpp @@ -0,0 +1,837 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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 + \bried The QSGNode class is the base class for all nodes in the scene graph. + + The QSGNode class can be used as a child container. Children are added with + the appendChildNode(), prependChildNode(), insertChildNodeBefore() and + insertChildNodeAfter(). Ordering is important as nodes are rendered in + order. 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_nodeFlags(OwnedByParent) + , m_flags(0) +{ +#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("Material 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. + */ + + + + +/*! + \fn bool QSGNode::isSubtreeBlocked() const + + 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. + */ + + +void QSGNode::destroy() +{ + if (m_parent) { + m_parent->removeChildNode(this); + Q_ASSERT(m_parent == 0); + } + for (int ii = m_children.count() - 1; ii >= 0; --ii) { + QSGNode *child = m_children.at(ii); + removeChildNode(child); + Q_ASSERT(child->m_parent == 0); + if (child->flags() & OwnedByParent) + delete child; + } + Q_ASSERT(m_children.isEmpty()); +} + +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 + + m_children.prepend(node); + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +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 + + m_children.append(node); + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +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(node->type() != RootNodeType, "QSGNode::insertChildNodeBefore", "RootNodes cannot be children of other nodes"); + +#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 + + int idx = before?m_children.indexOf(before):-1; + if (idx == -1) + m_children.append(node); + else + m_children.insert(idx, node); + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +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(node->type() != RootNodeType, "QSGNode::insertChildNodeAfter", "RootNodes cannot be children of other nodes"); + +#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 + + int idx = after?m_children.indexOf(after):-1; + if (idx == -1) + m_children.append(node); + else + m_children.insert(idx + 1, node); + node->m_parent = this; + + node->markDirty(DirtyNodeAdded); +} + +void QSGNode::removeChildNode(QSGNode *node) +{ + Q_ASSERT(m_children.contains(node)); + Q_ASSERT(node->parent() == this); + + m_children.removeOne(node); + + node->markDirty(DirtyNodeRemoved); + node->m_parent = 0; +} + + +/*! + 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; +} + + +void QSGNode::markDirty(DirtyFlags flags) +{ + m_flags |= (flags & DirtyPropagationMask); + + DirtyFlags subtreeFlags = DirtyFlags((flags & DirtyPropagationMask) << 16); + QSGNode *p = m_parent; + while (p) { + p->m_flags |= subtreeFlags; + if (p->type() == RootNodeType) + static_cast<QSGRootNode *>(p)->notifyNodeChange(this, flags); + p = p->m_parent; + } +} + +QSGBasicGeometryNode::QSGBasicGeometryNode() + : m_geometry(0) + , m_matrix(0) + , m_clip_list(0) +{ +} + +QSGBasicGeometryNode::~QSGBasicGeometryNode() +{ + destroy(); + if (flags() & OwnsGeometry) + delete m_geometry; +} + +void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) +{ + if (flags() & OwnsGeometry) + delete m_geometry; + m_geometry = geometry; + markDirty(DirtyGeometry); +} + + +QSGGeometryNode::QSGGeometryNode() + : m_render_order(0) + , m_material(0) + , m_opaque_material(0) + , m_opacity(1) +{ +} + +QSGGeometryNode::~QSGGeometryNode() +{ + destroy(); + if (flags() & OwnsMaterial) + delete m_material; + if (flags() & OwnsOpaqueMaterial) + delete m_opaque_material; +} + +/*! + 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. + + GeometryNodes 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; +} + + + +QSGClipNode::QSGClipNode() +{ +} + +QSGClipNode::~QSGClipNode() +{ + destroy(); +} + +/*! + Sets whether this clip node has a rectangular clip to \a rectHint. + */ +void QSGClipNode::setIsRectangular(bool rectHint) +{ + m_is_rectangular = rectHint; +} + + +/*! + 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; +} + + +QSGTransformNode::QSGTransformNode() +{ +} + +QSGTransformNode::~QSGTransformNode() +{ + destroy(); +} + +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; +} + + + +QSGRootNode::~QSGRootNode() +{ + while (!m_renderers.isEmpty()) + m_renderers.last()->setRootNode(0); + destroy(); +} + + +void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyFlags flags) +{ + for (int i=0; i<m_renderers.size(); ++i) { + m_renderers.at(i)->nodeChanged(node, flags); + } +} + +/*! + Constructs an opacity node with a default opacity of 1. + + Opacity accumulate 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() + : m_opacity(1) + , m_combined_opacity(1) +{ +} + + +QSGOpacityNode::~QSGOpacityNode() +{ + destroy(); +} + + +/*! + 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); +} + + +/*! + 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; +} + + +bool QSGOpacityNode::isSubtreeBlocked() const +{ + return m_combined_opacity < 0.001; +} + + +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) +{ + int count = n->childCount(); + for (int i=0; i<count; ++i) { + visitNode(n->childAtIndex(i)); + } +} + + + +#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->stride(); + 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() << dec + << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); +#ifdef QML_RUNTIME_TESTING + d << n->description; +#endif + d << ")"; + break; + } + return d; +} + +void QSGNodeDumper::dump(QSGNode *n) +{ + QSGNodeDumper dump; + dump.visitNode(n); +} + +void QSGNodeDumper::visitNode(QSGNode *n) +{ + if (n->isSubtreeBlocked()) + return; + qDebug() << QString(m_indent * 2, QLatin1Char(' ')) << n; + QSGNodeVisitor::visitNode(n); +} + +void QSGNodeDumper::visitChildren(QSGNode *n) +{ + ++m_indent; + QSGNodeVisitor::visitChildren(n); + --m_indent; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/declarative/scenegraph/coreapi/qsgnode.h b/src/declarative/scenegraph/coreapi/qsgnode.h new file mode 100644 index 0000000000..d6700af32e --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgnode.h @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include "qsggeometry.h" +#include <QtGui/QMatrix4x4> + +#include <float.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +#define QML_RUNTIME_TESTING + +class QSGRenderer; + +class QSGNode; +class QSGRootNode; +class QSGGeometryNode; +class QSGTransformNode; +class QSGClipNode; + +class Q_DECLARATIVE_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, + DirtyRenderOrder = 0x0020, + DirtyMaterial = 0x0100, + DirtyOpacity = 0x0200, + DirtyAll = 0xffff, + + DirtyPropagationMask = DirtyMatrix + | DirtyClipList + | DirtyNodeAdded + | DirtyOpacity, + + }; + 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 prependChildNode(QSGNode *node); + void appendChildNode(QSGNode *node); + void insertChildNodeBefore(QSGNode *node, QSGNode *before); + void insertChildNodeAfter(QSGNode *node, QSGNode *after); + + int childCount() const { return m_children.size(); } + QSGNode *childAtIndex(int i) const { return m_children.at(i); } + + virtual NodeType type() const { return BasicNodeType; } + + void clearDirty() { m_flags = 0; } + void markDirty(DirtyFlags flags); + DirtyFlags dirtyFlags() const { return m_flags; } + + virtual bool isSubtreeBlocked() const { return false; } + + 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: + // When a node is destroyed, it will detach from the scene graph and the renderer will be + // notified about the change. If the node is detached in the base node's destructor, the + // renderer can't check what type the node originally was because the node's type() method is + // virtual and will return the base node type. The renderer might therefore react incorrectly + // to the change. There are a few of ways I can think of to solve the problem: + // - The renderer must take into account that the notify method might be called from a node's + // destructor. + // - The node can have a type property that is set in the constructor. + // - All the node destructors must call a common destroy method. + // I choose the third option since that will allow the renderer to treat the nodes as their + // proper types. + + void destroy(); + +private: + QSGNode *m_parent; + QList<QSGNode *> m_children; + + Flags m_nodeFlags; + DirtyFlags m_flags; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::DirtyFlags); +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::Flags); + +class Q_DECLARATIVE_EXPORT QSGBasicGeometryNode : public QSGNode +{ +public: +// enum UsagePattern { +// Static, +// Dynamic, +// Stream +// }; +// void setUsagePattern(UsagePattern pattern); +// UsagePattern usagePattern() const { return m_pattern; } + + QSGBasicGeometryNode(); + ~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; } + +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_DECLARATIVE_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; + + virtual NodeType type() const { return GeometryNodeType; } + + 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_DECLARATIVE_EXPORT QSGClipNode : public QSGBasicGeometryNode +{ +public: + QSGClipNode(); + ~QSGClipNode(); + + virtual NodeType type() const { return ClipNodeType; } + + 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_DECLARATIVE_EXPORT QSGTransformNode : public QSGNode +{ +public: + QSGTransformNode(); + ~QSGTransformNode(); + + virtual NodeType type() const { return TransformNodeType; } + + 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_DECLARATIVE_EXPORT QSGRootNode : public QSGNode +{ +public: + ~QSGRootNode(); + NodeType type() const { return RootNodeType; } + +private: + void notifyNodeChange(QSGNode *node, DirtyFlags flags); + + friend class QSGRenderer; + friend class QSGNode; + friend class QSGGeometryNode; + + QList<QSGRenderer *> m_renderers; +}; + + +class Q_DECLARATIVE_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; } + + virtual QSGNode::NodeType type() const { return OpacityNodeType; } + + bool isSubtreeBlocked() const; + + +private: + qreal m_opacity; + qreal m_combined_opacity; +}; + +class Q_DECLARATIVE_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_DECLARATIVE_EXPORT QDebug operator<<(QDebug, const QSGNode *n); +Q_DECLARATIVE_EXPORT QDebug operator<<(QDebug, const QSGGeometryNode *n); +Q_DECLARATIVE_EXPORT QDebug operator<<(QDebug, const QSGTransformNode *n); +Q_DECLARATIVE_EXPORT QDebug operator<<(QDebug, const QSGOpacityNode *n); +Q_DECLARATIVE_EXPORT QDebug operator<<(QDebug, const QSGRootNode *n); + +class 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; +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // NODE_H diff --git a/src/declarative/scenegraph/coreapi/qsgnodeupdater.cpp b/src/declarative/scenegraph/coreapi/qsgnodeupdater.cpp new file mode 100644 index 0000000000..d81248be3d --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgnodeupdater.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgnodeupdater_p.h" + +QT_BEGIN_NAMESPACE + +// #define QSG_UPDATER_DEBUG + +QSGNodeUpdater::QSGNodeUpdater() + : m_current_clip(0) + , m_force_update(0) +{ + m_opacity_stack.push(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_matrix_stack.isEmpty()); ### no such function? + 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()) { + m_combined_matrix_stack.push(&t->combinedMatrix()); + + m_matrix_stack.push(); + m_matrix_stack *= t->matrix(); + } + + t->setCombinedMatrix(m_matrix_stack.top()); +} + + +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_matrix_stack.pop(); + m_combined_matrix_stack.pop(); + } + +} + + +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.top(); + 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.top(); + g->m_clip_list = m_current_clip; + g->setInheritedOpacity(m_opacity_stack.top()); +} + +void QSGNodeUpdater::enterOpacityNode(QSGOpacityNode *o) +{ + if (o->dirtyFlags() & QSGNode::DirtyOpacity) + ++m_force_update; + + qreal opacity = m_opacity_stack.top() * o->opacity(); + o->setCombinedOpacity(opacity); + m_opacity_stack.push(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(); +} + +void QSGNodeUpdater::visitChildren(QSGNode *n) +{ + if (!n->isSubtreeBlocked()) + QSGNodeVisitor::visitChildren(n); +} + +void QSGNodeUpdater::visitNode(QSGNode *n) +{ +#ifdef QSG_UPDATER_DEBUG + qDebug() << "enter:" << n; +#endif + + if (n->dirtyFlags() || m_force_update) { + bool forceUpdate = n->dirtyFlags() & (QSGNode::DirtyNodeAdded); + if (forceUpdate) + ++m_force_update; + + QSGNodeVisitor::visitNode(n); + + if (forceUpdate) + --m_force_update; + + n->clearDirty(); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/scenegraph/coreapi/qsgnodeupdater_p.h b/src/declarative/scenegraph/coreapi/qsgnodeupdater_p.h new file mode 100644 index 0000000000..518cf9eff9 --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgnodeupdater_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODEUPDATER_P_H +#define NODEUPDATER_P_H + +#include "qsgnode.h" +#include "qsgmatrix4x4stack.h" +#include <qstack.h> + +QT_BEGIN_NAMESPACE + +class Q_DECLARATIVE_EXPORT QSGNodeUpdater : public QSGNodeVisitor +{ +public: + QSGNodeUpdater(); + + virtual void updateStates(QSGNode *n); + virtual bool isNodeBlocked(QSGNode *n, QSGNode *root) const; + + void setToplevelOpacity(qreal alpha) { m_opacity_stack.top() = alpha; } + qreal toplevelOpacity() const { return m_opacity_stack.top(); } + +protected: + void enterTransformNode(QSGTransformNode *); + void leaveTransformNode(QSGTransformNode *); + void enterClipNode(QSGClipNode *c); + void leaveClipNode(QSGClipNode *c); + void enterOpacityNode(QSGOpacityNode *o); + void leaveOpacityNode(QSGOpacityNode *o); + void enterGeometryNode(QSGGeometryNode *); + + void visitNode(QSGNode *n); + void visitChildren(QSGNode *n); + + + QSGMatrix4x4Stack m_matrix_stack; + QStack<const QMatrix4x4 *> m_combined_matrix_stack; + QStack<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/declarative/scenegraph/coreapi/qsgrenderer.cpp b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp new file mode 100644 index 0000000000..04c0817f8f --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgrenderer.cpp @@ -0,0 +1,544 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrenderer_p.h" +#include "qsgnode.h" +#include "qsgmaterial.h" +#include "qsgnodeupdater_p.h" + +#include "private/qsgadaptationlayer_p.h" + +#include <QGLShaderProgram> +#include <qglframebufferobject.h> +#include <QtGui/qapplication.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 QTime frameTimer; +static int preprocessTime; +static int updatePassTime; +static int frameNumber = 0; +#endif + +void Bindable::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 Bindable::reactivate() const +{ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +BindableFbo::BindableFbo(QGLFramebufferObject *fbo) : m_fbo(fbo) +{ +} + + +void BindableFbo::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_render_opacity(1) + , m_context(context) + , m_root_node(0) + , m_node_updater(0) + , m_changed_emitted(false) + , m_mirrored(false) + , m_is_rendering(false) + , m_bindable(0) +{ + 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 Bindable + { + public: + B() : m_ctx(const_cast<QGLContext *>(QGLContext::currentContext())) { } + void bind() const { QGLFramebufferObject::bindDefault(); } + private: + QGLContext *m_ctx; + } b; + renderScene(b); +} + +void QSGRenderer::renderScene(const Bindable &bindable) +{ + if (!m_root_node) + return; + + m_is_rendering = true; +#ifdef QSG_RENDERER_TIMING + frameTimer.start(); +#endif + + m_bindable = &bindable; + preprocess(); + + bindable.bind(); +#ifdef QSG_RENDERER_TIMING + int bindTime = frameTimer.elapsed(); +#endif + + render(); +#ifdef QSG_RENDERER_TIMING + int renderTime = frameTimer.elapsed(); +#endif + + glDisable(GL_SCISSOR_TEST); + m_is_rendering = false; + m_changed_emitted = false; + m_bindable = 0; + +#ifdef QSG_RENDERER_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::setProjectMatrixToDeviceRect() +{ + setProjectMatrixToRect(m_device_rect); +} + +void QSGRenderer::setProjectMatrixToRect(const QRectF &rect) +{ + QMatrix4x4 matrix; + matrix.ortho(rect.x(), + rect.x() + rect.width(), + rect.y() + rect.height(), + rect.y(), + qreal(0.01), + -1); + setProjectMatrix(matrix); +} + +void QSGRenderer::setProjectMatrix(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; +} + +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 + preprocessTime = frameTimer.elapsed(); +#endif + + nodeUpdater()->setToplevelOpacity(context()->renderAlpha()); + nodeUpdater()->updateStates(m_root_node); + +#ifdef QSG_RENDERER_TIMING + updatePassTime = frameTimer.elapsed(); +#endif + +} + +void QSGRenderer::addNodesToPreprocess(QSGNode *node) +{ + for (int i = 0; i < node->childCount(); ++i) + addNodesToPreprocess(node->childAtIndex(i)); + if (node->flags() & QSGNode::UsePreprocess) + m_nodes_to_preprocess.insert(node); +} + +void QSGRenderer::removeNodesToPreprocess(QSGNode *node) +{ + for (int i = 0; i < node->childCount(); ++i) + removeNodesToPreprocess(node->childAtIndex(i)); + 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 matrix = m_projectionMatrix.top(); + if (clip->matrix()) + matrix *= *clip->matrix(); + + const QMatrix4x4 &m = matrix; + + // TODO: Check for multisampling and pixel grid alignment. + bool canUseScissor = clip->isRectangular() + && qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(0, 2)) + && qFuzzyIsNull(m(1, 0)) && qFuzzyIsNull(m(1, 2)); + + if (canUseScissor) { + QRectF bbox = clip->clipRect(); + qreal invW = 1 / m(3, 3); + qreal fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW; + qreal fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW; + qreal fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW; + qreal fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW; + + 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); + } + + clipRect = clipRect.normalized(); + glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); + } else { + if (!stencilEnabled) { + if (!m_clip_program.isLinked()) { + m_clip_program.addShaderFromSourceCode(QGLShader::Vertex, + "attribute highp vec4 vCoord; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vCoord; \n" + "}"); + m_clip_program.addShaderFromSourceCode(QGLShader::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); + glDisable(GL_DEPTH_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 *geometry = clip->geometry(); + Q_ASSERT(geometry->attributeCount() > 0); + const QSGGeometry::Attribute *a = geometry->attributes(); + + glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, geometry->stride(), geometry->vertexData()); + + m_clip_program.setUniformValue(m_clip_matrix_id, m); + draw(clip); + + ++clipDepth; + } + + clip = clip->clipList(); + } + + if (stencilEnabled) { + m_clip_program.disableAttributeArray(0); + glEnable(GL_DEPTH_TEST); + 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(); + //glDepthMask(GL_TRUE); // must be reset correctly by caller. + } else { + glDisable(GL_STENCIL_TEST); + } + + if (!scissorEnabled) + glDisable(GL_SCISSOR_TEST); + + return stencilEnabled ? StencilClip : ScissorClip; +} + + +/*! + Issues the GL draw call for \a geometryNode. + + The function assumes that attributes have been bound and set up prior + to making this call. + + \internal + */ + +void QSGRenderer::draw(const QSGBasicGeometryNode *node) +{ + const QSGGeometry *g = node->geometry(); + if (g->indexCount()) { + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + } else { + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + } +} + + +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) + }; + return sizes[type - GL_BYTE]; +} + +/*! + Convenience function to set up and bind the vertex data in \a g to the + required attribute positions defined in \a material. + + \internal + */ + +void QSGRenderer::bindGeometry(QSGMaterialShader *material, const QSGGeometry *g) +{ + char const *const *attrNames = material->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->stride(), (char *) g->vertexData() + offset); + offset += a.tupleSize * size_of_type(a.type); + } +} + + +QT_END_NAMESPACE diff --git a/src/declarative/scenegraph/coreapi/qsgrenderer_p.h b/src/declarative/scenegraph/coreapi/qsgrenderer_p.h new file mode 100644 index 0000000000..fcf966d819 --- /dev/null +++ b/src/declarative/scenegraph/coreapi/qsgrenderer_p.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include <qset.h> +#include <qhash.h> + +#include "qsgmatrix4x4stack.h" + +#include <qglfunctions.h> +#include <qglshaderprogram.h> + +#include "qsgnode.h" +#include "qsgmaterial.h" +#include "qsgtexture.h" + +#include <private/qsgcontext_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGMaterialShader; +struct QSGMaterialType; +class QGLFramebufferObject; +class TextureReference; +class Bindable; +class QSGNodeUpdater; + + +class Q_DECLARATIVE_EXPORT QSGRenderer : public QObject, public QGLFunctions +{ + 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; } + + QSGMatrix4x4Stack &projectionMatrix() { return m_projectionMatrix; } + QSGMatrix4x4Stack &modelViewMatrix() { return m_modelViewMatrix; } + QMatrix4x4 combinedMatrix() const { return m_projectionMatrix.top() * m_modelViewMatrix.top(); } + + void setProjectMatrixToDeviceRect(); + void setProjectMatrixToRect(const QRectF &rect); + void setProjectMatrix(const QMatrix4x4 &matrix); + QMatrix4x4 projectMatrix() const { return m_projection_matrix; } + bool isMirrored() const { return m_mirrored; } + + qreal renderOpacity() const { return m_render_opacity; } + + void setClearColor(const QColor &color); + QColor clearColor() const { return m_clear_color; } + + const QGLContext *glContext() const { Q_ASSERT(m_context); return m_context->glContext(); } + + QSGContext *context(); + + void renderScene(); + void renderScene(const Bindable &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 QSGBasicGeometryNode *geometry); + void bindGeometry(QSGMaterialShader *material, const QSGGeometry *g); + + virtual void render() = 0; + QSGRenderer::ClipType updateStencilClip(const QSGClipNode *clip); + + const Bindable *bindable() const { return m_bindable; } + + virtual void preprocess(); + + void addNodesToPreprocess(QSGNode *node); + void removeNodesToPreprocess(QSGNode *node); + + + QColor m_clear_color; + ClearMode m_clear_mode; + QSGMatrix4x4Stack m_projectionMatrix; + QSGMatrix4x4Stack m_modelViewMatrix; + qreal m_render_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; + QGLShaderProgram m_clip_program; + int m_clip_matrix_id; + + bool m_changed_emitted; + bool m_mirrored; + bool m_is_rendering; + + const Bindable *m_bindable; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderer::ClearMode) + +class Q_DECLARATIVE_EXPORT Bindable +{ +public: + virtual ~Bindable() { } + virtual void bind() const = 0; + virtual void clear(QSGRenderer::ClearMode mode) const; + virtual void reactivate() const; +}; + +class BindableFbo : public Bindable +{ +public: + BindableFbo(QGLFramebufferObject *fbo); + virtual void bind() const; +private: + QGLFramebufferObject *m_fbo; +}; + + + +QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState::DirtyStates dirty) const +{ + QSGMaterialShader::RenderState s; + s.m_dirty = dirty; + s.m_data = this; + return s; +} + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // RENDERER_H |