diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp new file mode 100644 index 0000000000..56a6e0e5d9 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#define GL_GLEXT_PROTOTYPES + +#include "qsgdefaultrenderer_p.h" +#include "qsgmaterial.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qguiapplication.h> +#include <QtCore/qpair.h> +#include <QtCore/QElapsedTimer> + +//#define FORCE_NO_REORDER + +// #define RENDERER_DEBUG +#ifdef RENDERER_DEBUG +#define DEBUG_THRESHOLD 0 +QElapsedTimer debugTimer; +int materialChanges; +int geometryNodesDrawn; +#endif + +QT_BEGIN_NAMESPACE + +static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b) +{ + // Sort by clip... + if (a->clipList() != b->clipList()) + return a->clipList() < b->clipList(); + + // Sort by material definition + QSGMaterialType *aDef = a->material()->type(); + QSGMaterialType *bDef = b->material()->type(); + + if (aDef != bDef) + return aDef < bDef; + + // Sort by material state + int cmp = a->material()->compare(b->material()); + if (cmp != 0) + return cmp < 0; + + return a->matrix() < b->matrix(); +} + +static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b) +{ + // Sort by clip... + if (a->clipList() != b->clipList()) + return a->clipList() < b->clipList(); + + // Sort by material definition + QSGMaterialType *aDef = a->material()->type(); + QSGMaterialType *bDef = b->material()->type(); + + if (!(a->material()->flags() & QSGMaterial::Blending)) { + int aOrder = a->renderOrder(); + int bOrder = b->renderOrder(); + if (aOrder != bOrder) + return aOrder > bOrder; + } + + if (aDef != bDef) + return aDef < bDef; + + // Sort by material state + int cmp = a->material()->compare(b->material()); + if (cmp != 0) + return cmp < 0; + + return a->matrix() < b->matrix(); +} + + +QSGDefaultRenderer::IndexGeometryNodePair::IndexGeometryNodePair(int i, QSGGeometryNode *node) + : QPair<int, QSGGeometryNode *>(i, node) +{ +} + +bool QSGDefaultRenderer::IndexGeometryNodePair::operator < (const QSGDefaultRenderer::IndexGeometryNodePair &other) const +{ + return nodeLessThan(second, other.second); +} + + +QSGDefaultRenderer::IndexGeometryNodePairHeap::IndexGeometryNodePairHeap() + : v(64) +{ +} + +void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRenderer::IndexGeometryNodePair &x) +{ + int i = v.size(); + v.add(x); + while (i != 0 && v.at(i) < v.at(parent(i))) { + qSwap(v.at(parent(i)), v.at(i)); + i = parent(i); + } +} + +QSGDefaultRenderer::IndexGeometryNodePair QSGDefaultRenderer::IndexGeometryNodePairHeap::pop() +{ + IndexGeometryNodePair x = top(); + if (v.size() > 1) + qSwap(v.first(), v.last()); + v.pop_back(); + int i = 0; + while (left(i) < v.size()) { + int low = left(i); + if (right(i) < v.size() && v.at(right(i)) < v.at(low)) + low = right(i); + if (!(v.at(low) < v.at(i))) + break; + qSwap(v.at(i), v.at(low)); + i = low; + } + return x; +} + + +QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context) + : QSGRenderer(context) + , m_opaqueNodes(64) + , m_transparentNodes(64) + , m_tempNodes(64) + , m_rebuild_lists(false) + , m_needs_sorting(false) + , m_sort_front_to_back(false) + , m_currentRenderOrder(1) +{ + QStringList args = qApp->arguments(); +#if defined(QML_RUNTIME_TESTING) + m_render_opaque_nodes = !args.contains(QLatin1String("--no-opaque-nodes")); + m_render_alpha_nodes = !args.contains(QLatin1String("--no-alpha-nodes")); +#endif +} + +void QSGDefaultRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags) +{ + QSGRenderer::nodeChanged(node, flags); + + quint32 rebuildFlags = QSGNode::DirtyNodeAdded | QSGNode::DirtyNodeRemoved + | QSGNode::DirtyMaterial | QSGNode::DirtyOpacity + | QSGNode::DirtyForceUpdate; + + if (flags & rebuildFlags) + m_rebuild_lists = true; + + if (flags & (rebuildFlags | QSGNode::DirtyClipList)) + m_needs_sorting = true; +} + +void QSGDefaultRenderer::render() +{ +#if defined (QML_RUNTIME_TESTING) + static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); + if (dumpTree) { + printf("\n\n"); + QSGNodeDumper::dump(rootNode()); + } +#endif + +#ifdef RENDERER_DEBUG + debugTimer.invalidate(); + debugTimer.start(); + geometryNodesDrawn = 0; + materialChanges = 0; +#endif + + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + + glFrontFace(isMirrored() ? GL_CW : GL_CCW); + glDisable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + glDepthFunc(GL_GREATER); +#if defined(QT_OPENGL_ES) + glClearDepthf(0); +#else + glClearDepth(0); +#endif + + glDisable(GL_SCISSOR_TEST); + glClearColor(m_clear_color.redF(), m_clear_color.greenF(), m_clear_color.blueF(), m_clear_color.alphaF()); + +#ifdef RENDERER_DEBUG + int debugtimeSetup = debugTimer.elapsed(); +#endif + + bindable()->clear(clearMode()); + +#ifdef RENDERER_DEBUG + int debugtimeClear = debugTimer.elapsed(); +#endif + + QRect r = viewportRect(); + glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height()); + m_current_projection_matrix = projectionMatrix(); + m_current_model_view_matrix.setToIdentity(); + + m_currentClip = 0; + glDisable(GL_STENCIL_TEST); + + m_currentMaterial = 0; + m_currentProgram = 0; + m_currentMatrix = 0; + + if (m_rebuild_lists) { + m_opaqueNodes.reset(); + m_transparentNodes.reset(); + m_currentRenderOrder = 1; + buildLists(rootNode()); + m_rebuild_lists = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeLists = debugTimer.elapsed(); +#endif + + + if (m_needs_sorting) { + if (!m_opaqueNodes.isEmpty()) { + qSort(&m_opaqueNodes.first(), &m_opaqueNodes.first() + m_opaqueNodes.size(), + m_sort_front_to_back + ? nodeLessThanWithRenderOrder + : nodeLessThan); + } + m_needs_sorting = false; + } + +#ifdef RENDERER_DEBUG + int debugtimeSorting = debugTimer.elapsed(); +#endif + + m_renderOrderMatrix.setToIdentity(); + m_renderOrderMatrix.scale(1, 1, qreal(1) / m_currentRenderOrder); + + glDisable(GL_BLEND); + glDepthMask(true); +#ifdef QML_RUNTIME_TESTING + if (m_render_opaque_nodes) +#endif + { +#if defined (QML_RUNTIME_TESTING) + if (dumpTree) + qDebug() << "Opaque Nodes:"; +#endif + renderNodes(m_opaqueNodes); + } + +#ifdef RENDERER_DEBUG + int debugtimeOpaque = debugTimer.elapsed(); + int opaqueNodes = geometryNodesDrawn; + int opaqueMaterialChanges = materialChanges; +#endif + + glEnable(GL_BLEND); + glDepthMask(false); +#ifdef QML_RUNTIME_TESTING + if (m_render_alpha_nodes) +#endif + { +#if defined (QML_RUNTIME_TESTING) + if (dumpTree) + qDebug() << "Alpha Nodes:"; +#endif + renderNodes(m_transparentNodes); + } + +#ifdef RENDERER_DEBUG + int debugtimeAlpha = debugTimer.elapsed(); +#endif + + + if (m_currentProgram) + m_currentProgram->deactivate(); + +#ifdef RENDERER_DEBUG + if (debugTimer.elapsed() > DEBUG_THRESHOLD) { + printf(" --- Renderer breakdown:\n" + " - setup=%d, clear=%d, building=%d, sorting=%d, opaque=%d, alpha=%d\n" + " - material changes: opaque=%d, alpha=%d, total=%d\n" + " - geometry ndoes: opaque=%d, alpha=%d, total=%d\n", + debugtimeSetup, + debugtimeClear - debugtimeSetup, + debugtimeLists - debugtimeClear, + debugtimeSorting - debugtimeLists, + debugtimeOpaque - debugtimeSorting, + debugtimeAlpha - debugtimeOpaque, + opaqueMaterialChanges, materialChanges - opaqueMaterialChanges, materialChanges, + opaqueNodes, geometryNodesDrawn - opaqueNodes, geometryNodesDrawn); + } +#endif + +} + +void QSGDefaultRenderer::setSortFrontToBackEnabled(bool sort) +{ + printf("setting sorting to... %d\n", sort); + m_sort_front_to_back = sort; +} + +bool QSGDefaultRenderer::isSortFrontToBackEnabled() const +{ + return m_sort_front_to_back; +} + +void QSGDefaultRenderer::buildLists(QSGNode *node) +{ + if (node->isSubtreeBlocked()) + return; + + if (node->type() == QSGNode::GeometryNodeType) { + QSGGeometryNode *geomNode = static_cast<QSGGeometryNode *>(node); + qreal opacity = geomNode->inheritedOpacity(); + QSGMaterial *m = geomNode->activeMaterial(); + +#ifdef FORCE_NO_REORDER + if (true) { +#else + if ((m->flags() & QSGMaterial::Blending) || opacity < 1) { +#endif + geomNode->setRenderOrder(m_currentRenderOrder - 1); + m_transparentNodes.add(geomNode); + } else { + geomNode->setRenderOrder(m_currentRenderOrder); + m_opaqueNodes.add(geomNode); + m_currentRenderOrder += 2; + } + } + + if (!node->firstChild()) + return; + +#ifdef FORCE_NO_REORDER + static bool reorder = false; +#else + static bool reorder = qApp->arguments().contains(QLatin1String("--reorder")); +#endif + + if (reorder && node->firstChild() != node->lastChild() && (node->flags() & QSGNode::ChildrenDoNotOverlap)) { + QVarLengthArray<int, 16> beginIndices; + QVarLengthArray<int, 16> endIndices; + int baseCount = m_transparentNodes.size(); + int count = 0; + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) { + beginIndices.append(m_transparentNodes.size()); + buildLists(c); + endIndices.append(m_transparentNodes.size()); + ++count; + } + + int childNodeCount = m_transparentNodes.size() - baseCount; + if (childNodeCount) { + m_tempNodes.reset(); + m_tempNodes.reserve(childNodeCount); + while (childNodeCount) { + for (int i = 0; i < count; ++i) { + if (beginIndices[i] != endIndices[i]) + m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++))); + } + while (!m_heap.isEmpty()) { + IndexGeometryNodePair pair = m_heap.pop(); + m_tempNodes.add(pair.second); + --childNodeCount; + int i = pair.first; + if (beginIndices[i] != endIndices[i] && !nodeLessThan(m_transparentNodes.at(beginIndices[i]), pair.second)) + m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++))); + } + } + Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount); + + qMemCopy(&m_transparentNodes.at(baseCount), &m_tempNodes.at(0), m_tempNodes.size() * sizeof(QSGGeometryNode *)); + } + } else { + for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) + buildLists(c); + } +} + +void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list) +{ + const float scale = 1.0f / m_currentRenderOrder; + int count = list.size(); + int currentRenderOrder = 0x80000000; + m_current_projection_matrix.setColumn(2, scale * projectionMatrix().column(2)); + + //int clipChangeCount = 0; + //int programChangeCount = 0; + //int materialChangeCount = 0; + + for (int i = 0; i < count; ++i) { + QSGGeometryNode *geomNode = list.at(i); + + QSGMaterialShader::RenderState::DirtyStates updates; + +#if defined (QML_RUNTIME_TESTING) + static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree")); + if (dumpTree) + qDebug() << geomNode; +#endif + + bool changeMatrix = m_currentMatrix != geomNode->matrix(); + + if (changeMatrix) { + m_currentMatrix = geomNode->matrix(); + if (m_currentMatrix) + m_current_model_view_matrix = *m_currentMatrix; + else + m_current_model_view_matrix.setToIdentity(); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity(); + if (changeOpacity) { + updates |= QSGMaterialShader::RenderState::DirtyOpacity; + m_current_opacity = geomNode->inheritedOpacity(); + } + + Q_ASSERT(geomNode->activeMaterial()); + + QSGMaterial *material = geomNode->activeMaterial(); + QSGMaterialShader *program = m_context->prepareMaterial(material); + Q_ASSERT(program->program()->isLinked()); + + bool changeClip = geomNode->clipList() != m_currentClip; + QSGRenderer::ClipType clipType = QSGRenderer::NoClip; + if (changeClip) { + // The clip function relies on there not being any depth testing.. + glDisable(GL_DEPTH_TEST); + clipType = updateStencilClip(geomNode->clipList()); + glEnable(GL_DEPTH_TEST); + m_currentClip = geomNode->clipList(); +#ifdef FORCE_NO_REORDER + glDepthMask(false); +#else + glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1); +#endif + //++clipChangeCount; + } + + bool changeProgram = (changeClip && clipType == QSGRenderer::StencilClip) || m_currentProgram != program; + if (changeProgram) { + if (m_currentProgram) + m_currentProgram->deactivate(); + m_currentProgram = program; + m_currentProgram->activate(); + //++programChangeCount; + updates |= (QSGMaterialShader::RenderState::DirtyMatrix | QSGMaterialShader::RenderState::DirtyOpacity); + +#ifdef RENDERER_DEBUG + materialChanges++; +#endif + } + + bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder(); + if (changeRenderOrder) { + currentRenderOrder = geomNode->renderOrder(); + m_current_projection_matrix.setColumn(3, projectionMatrix().column(3) + + currentRenderOrder + * m_current_projection_matrix.column(2)); + updates |= QSGMaterialShader::RenderState::DirtyMatrix; + } + + if (changeProgram || m_currentMaterial != material) { + program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial); + m_currentMaterial = material; + //++materialChangeCount; + } + + //glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale); + + const QSGGeometry *g = geomNode->geometry(); + draw(program, g); + +#ifdef RENDERER_DEBUG + geometryNodesDrawn++; +#endif + } + //qDebug("Clip: %i, shader program: %i, material: %i times changed while drawing %s items", + // clipChangeCount, programChangeCount, materialChangeCount, + // &list == &m_transparentNodes ? "transparent" : "opaque"); +} + +QT_END_NAMESPACE |