aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph')
-rw-r--r--src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp8
-rw-r--r--src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h9
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp34
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h9
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp21
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h10
-rw-r--r--src/quick/scenegraph/coreapi/qsgnodeupdater_p.h10
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp17
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h10
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode_p.h4
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp29
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h8
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp24
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h8
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp2
-rw-r--r--src/quick/scenegraph/qsgcontextplugin_p.h10
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp26
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.h10
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p_p.h6
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode_p.h8
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode.cpp3
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode_p.h8
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h10
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h6
-rw-r--r--src/quick/scenegraph/qsgflashnode_p.h4
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp382
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h87
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h4
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp1113
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h124
-rw-r--r--src/quick/scenegraph/scenegraph.pri10
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.h8
-rw-r--r--src/quick/scenegraph/util/qsgpainternode.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgpainternode_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.h4
-rw-r--r--src/quick/scenegraph/util/qsgsimplerectnode.h8
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.h4
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp114
-rw-r--r--src/quick/scenegraph/util/qsgtexture.h4
-rw-r--r--src/quick/scenegraph/util/qsgtexture_p.h1
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.h10
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgtextureprovider.h4
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.h8
49 files changed, 2001 insertions, 204 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp
index e662c2bcba..448ec55a82 100644
--- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp
@@ -131,13 +131,12 @@ QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context)
, m_transparentNodes(64)
, m_renderGroups(4)
, m_rebuild_lists(false)
- , m_needs_sorting(false)
, m_sort_front_to_back(false)
, m_render_node_added(false)
, m_currentRenderOrder(1)
{
- QStringList args = qApp->arguments();
#if defined(QML_RUNTIME_TESTING)
+ QStringList args = qApp->arguments();
m_render_opaque_nodes = !args.contains(QLatin1String("--no-opaque-nodes"));
m_render_alpha_nodes = !args.contains(QLatin1String("--no-alpha-nodes"));
#endif
@@ -212,6 +211,8 @@ void QSGDefaultRenderer::render()
m_currentProgram = 0;
m_currentMatrix = 0;
+ bool sortNodes = m_rebuild_lists;
+
if (m_rebuild_lists) {
m_opaqueNodes.reset();
m_transparentNodes.reset();
@@ -228,7 +229,7 @@ void QSGDefaultRenderer::render()
int debugtimeLists = debugTimer.elapsed();
#endif
- if (m_needs_sorting) {
+ if (sortNodes) {
if (!m_opaqueNodes.isEmpty()) {
bool (*lessThan)(QSGNode *, QSGNode *);
lessThan = m_sort_front_to_back ? nodeLessThanWithRenderOrder : nodeLessThan;
@@ -240,7 +241,6 @@ void QSGDefaultRenderer::render()
start = end;
}
}
- m_needs_sorting = false;
}
#ifdef RENDERER_DEBUG
diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h
index 5bd6eeeb1e..6fba0d18bc 100644
--- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h
@@ -39,16 +39,14 @@
**
****************************************************************************/
-#ifndef QMLRENDERER_H
-#define QMLRENDERER_H
+#ifndef QSGDEFAULTRENDERER_P_H
+#define QSGDEFAULTRENDERER_P_H
#include "qsgrenderer_p.h"
#include <QtGui/private/qdatabuffer_p.h>
#include "qsgrendernode_p.h"
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderer : public QSGRenderer
@@ -78,7 +76,6 @@ private:
QDataBuffer<RenderGroup> m_renderGroups;
bool m_rebuild_lists;
- bool m_needs_sorting;
bool m_sort_front_to_back;
bool m_render_node_added;
int m_currentRenderOrder;
@@ -91,6 +88,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QMLRENDERER_H
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index 27d4ed413a..818b9b26aa 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -284,7 +284,7 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
setIndexDataPattern() functions. Whether this hint is respected or
not is implementation specific.
- \sa QSGGeometryNode, {Custom Geometry Example}
+ \sa QSGGeometryNode, {Scene Graph - Custom Geometry}
*/
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index cfed30cb36..78ad03e411 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -46,8 +46,6 @@
#include <QtGui/qopengl.h>
#include <QtCore/QRectF>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGGeometryData;
@@ -295,6 +293,4 @@ int QSGGeometry::sizeOfIndex() const
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGGEOMETRY_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 0e40a01311..11ce987959 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -44,6 +44,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DEBUG
+static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty();
+#endif
+
/*!
\group qtquick-scenegraph-materials
\title Qt Quick Scene Graph Material Classes
@@ -438,6 +442,16 @@ QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
}
+/*!
+ Returns the projection matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
/*!
@@ -538,11 +552,13 @@ 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;
+ if (qsg_leak_check) {
+ ++qt_material_count;
+ static bool atexit_registered = false;
+ if (!atexit_registered) {
+ atexit(qt_print_material_count);
+ atexit_registered = true;
+ }
}
#endif
}
@@ -555,9 +571,11 @@ QSGMaterial::QSGMaterial()
QSGMaterial::~QSGMaterial()
{
#ifndef QT_NO_DEBUG
- --qt_material_count;
- if (qt_material_count < 0)
- qDebug("Material destroyed after qt_print_material_count() was called.");
+ if (qsg_leak_check) {
+ --qt_material_count;
+ if (qt_material_count < 0)
+ qDebug("Material destroyed after qt_print_material_count() was called.");
+ }
#endif
}
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index 56109d32a0..38862e694d 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.h
@@ -39,14 +39,12 @@
**
****************************************************************************/
-#ifndef MATERIAL_H
-#define MATERIAL_H
+#ifndef QSGMATERIAL_H
+#define QSGMATERIAL_H
#include <QtQuick/qtquickglobal.h>
#include <QtGui/qopenglshaderprogram.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGMaterial;
@@ -71,6 +69,7 @@ public:
float opacity() const;
QMatrix4x4 combinedMatrix() const;
QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
QRect viewportRect() const;
QRect deviceRect() const;
float determinant() const;
@@ -143,6 +142,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 5995dc862d..6a22e0e7f9 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -49,6 +49,7 @@
QT_BEGIN_NAMESPACE
#ifndef QT_NO_DEBUG
+static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty();
static int qt_node_count = 0;
static void qt_print_node_count()
@@ -271,11 +272,13 @@ QSGNode::QSGNode(NodeType type)
void QSGNode::init()
{
#ifndef QT_NO_DEBUG
- ++qt_node_count;
- static bool atexit_registered = false;
- if (!atexit_registered) {
- atexit(qt_print_node_count);
- atexit_registered = true;
+ if (qsg_leak_check) {
+ ++qt_node_count;
+ static bool atexit_registered = false;
+ if (!atexit_registered) {
+ atexit(qt_print_node_count);
+ atexit_registered = true;
+ }
}
#endif
}
@@ -289,9 +292,11 @@ void QSGNode::init()
QSGNode::~QSGNode()
{
#ifndef QT_NO_DEBUG
- --qt_node_count;
- if (qt_node_count < 0)
- qDebug("Node destroyed after qt_print_node_count() was called.");
+ if (qsg_leak_check) {
+ --qt_node_count;
+ if (qt_node_count < 0)
+ qDebug("Node destroyed after qt_print_node_count() was called.");
+ }
#endif
destroy();
}
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 82bb66fa77..3fa2f7fc04 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -39,16 +39,14 @@
**
****************************************************************************/
-#ifndef NODE_H
-#define NODE_H
+#ifndef QSGNODE_H
+#define QSGNODE_H
#include <QtQuick/qsggeometry.h>
#include <QtGui/QMatrix4x4>
#include <float.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
//#define QML_RUNTIME_TESTING
@@ -335,6 +333,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::Flags)
QT_END_NAMESPACE
-QT_END_HEADER
-
-#endif // NODE_H
+#endif // QSGNODE_H
diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
index 88eb814766..eb612ff877 100644
--- a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
+++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h
@@ -39,14 +39,12 @@
**
****************************************************************************/
-#ifndef NODEUPDATER_P_H
-#define NODEUPDATER_P_H
+#ifndef QSGNODEUPDATER_P_H
+#define QSGNODEUPDATER_P_H
#include <private/qtquickglobal_p.h>
#include <QtGui/private/qdatabuffer_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGNode;
@@ -96,6 +94,4 @@ protected:
QT_END_NAMESPACE
-QT_END_HEADER
-
-#endif // NODEUPDATER_P_H
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index 6f9d380eb5..02eec70952 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -60,8 +60,7 @@ QT_BEGIN_NAMESPACE
-#define QSG_RENDERER_TIMING
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty();
static QTime frameTimer;
static int preprocessTime;
@@ -237,7 +236,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
m_is_rendering = true;
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
if (qsg_render_timing)
frameTimer.start();
int bindTime = 0;
@@ -248,7 +247,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
preprocess();
bindable.bind();
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
if (qsg_render_timing)
bindTime = frameTimer.elapsed();
#endif
@@ -269,7 +268,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
#endif
render();
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
if (qsg_render_timing)
renderTime = frameTimer.elapsed();
#endif
@@ -289,9 +288,9 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
m_index_buffer_bound = false;
}
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
if (qsg_render_timing) {
- printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n",
+ printf(" - Breakdown of render time: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n",
preprocessTime,
updatePassTime - preprocessTime,
bindTime - updatePassTime,
@@ -379,7 +378,7 @@ void QSGRenderer::preprocess()
}
}
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
if (qsg_render_timing)
preprocessTime = frameTimer.elapsed();
#endif
@@ -387,7 +386,7 @@ void QSGRenderer::preprocess()
nodeUpdater()->setToplevelOpacity(context()->renderAlpha());
nodeUpdater()->updateStates(m_root_node);
-#ifdef QSG_RENDERER_TIMING
+#ifndef QSG_NO_RENDERER_TIMING
if (qsg_render_timing)
updatePassTime = frameTimer.elapsed();
#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 92072e2e3e..844e8b1d8c 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef RENDERER_H
-#define RENDERER_H
+#ifndef QSGRENDERER_P_H
+#define QSGRENDERER_P_H
#include <qset.h>
#include <qhash.h>
@@ -55,8 +55,6 @@
#include <QtQuick/private/qsgcontext_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGMaterialShader;
@@ -241,6 +239,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
-#endif // RENDERER_H
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
index 6c3e03b8b3..1f1bc23123 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
@@ -56,8 +56,6 @@
#include "qsgnode.h"
#include <private/qtquickglobal_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QSGRenderNode : public QSGNode
@@ -110,6 +108,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 87b13d86d3..4e8bafbe95 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -48,8 +48,14 @@
#include <QtGui/qguiapplication.h>
#include <qdir.h>
+#include <QElapsedTimer>
+
QT_BEGIN_NAMESPACE
+#ifndef QSG_NO_RENDERER_TIMING
+static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty();
+static QElapsedTimer qsg_render_timer;
+#endif
QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
@@ -155,6 +161,11 @@ void QSGDistanceFieldGlyphCache::update()
if (m_pendingGlyphs.isEmpty())
return;
+#ifndef QSG_NO_RENDERER_TIMING
+ if (qsg_render_timing)
+ qsg_render_timer.start();
+#endif
+
QHash<glyph_t, QImage> distanceFields;
for (int i = 0; i < m_pendingGlyphs.size(); ++i) {
@@ -164,9 +175,27 @@ void QSGDistanceFieldGlyphCache::update()
distanceFields.insert(glyphIndex, distanceField);
}
+#ifndef QSG_NO_RENDERER_TIMING
+ int renderTime = 0;
+ int count = m_pendingGlyphs.size();
+ if (qsg_render_timing)
+ renderTime = qsg_render_timer.elapsed();
+#endif
+
m_pendingGlyphs.reset();
storeGlyphs(distanceFields);
+
+#ifndef QSG_NO_RENDERER_TIMING
+ if (qsg_render_timing) {
+ printf(" - glyphs: count=%d, render=%d, store=%d, total=%d\n",
+ count,
+ renderTime,
+ (int) qsg_render_timer.elapsed() - renderTime,
+ (int) qsg_render_timer.elapsed());
+
+ }
+#endif
}
void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 8b37deb1c1..cc22bfa61f 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef ADAPTATIONINTERFACES_H
-#define ADAPTATIONINTERFACES_H
+#ifndef QSGADAPTATIONLAYER_P_H
+#define QSGADAPTATIONLAYER_P_H
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgtexture.h>
@@ -59,8 +59,6 @@
// ### remove
#include <QtQuick/private/qquicktext_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGNode;
@@ -292,6 +290,4 @@ inline bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph)
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index c9ac190e86..705b12c4d4 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -71,6 +71,12 @@ DEFINE_BOOL_CONFIG_OPTION(qmlFlashMode, QML_FLASH_MODE)
DEFINE_BOOL_CONFIG_OPTION(qmlTranslucentMode, QML_TRANSLUCENT_MODE)
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+
+#ifndef QSG_NO_RENDERER_TIMING
+static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty();
+static QElapsedTimer qsg_renderer_timer;
+#endif
+
/*
Comments about this class from Gunnar:
@@ -243,6 +249,14 @@ void QSGContext::initialize(QOpenGLContext *context)
{
Q_D(QSGContext);
+ // Sanity check the surface format, in case it was overridden by the application
+ QSurfaceFormat requested = defaultSurfaceFormat();
+ QSurfaceFormat actual = context->format();
+ if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
+ qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
+ if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
+ qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
+
Q_ASSERT(!d->gl);
d->gl = context;
@@ -463,11 +477,21 @@ QSGMaterialShader *QSGContext::prepareMaterial(QSGMaterial *material)
if (shader)
return shader;
+#ifndef QSG_NO_RENDERER_TIMING
+ if (qsg_render_timing)
+ qsg_renderer_timer.start();
+#endif
+
shader = material->createShader();
shader->compile();
shader->initialize();
d->materials[type] = shader;
+#ifndef QSG_NO_RENDERER_TIMING
+ if (qsg_render_timing)
+ printf(" - compiling material: %dms\n", (int) qsg_renderer_timer.elapsed());
+#endif
+
return shader;
}
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 1ae3ada3d8..b069c53dd3 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -54,8 +54,6 @@
#include <QtQuick/qsgnode.h>
#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
@@ -69,7 +67,7 @@ class QQuickWindow;
class QSGTexture;
class QSGMaterial;
class QSGMaterialShader;
-class QQuickWindowManager;
+class QSGRenderLoop;
class QOpenGLContext;
class QOpenGLFramebufferObject;
@@ -127,7 +125,7 @@ public:
virtual QAnimationDriver *createAnimationDriver(QObject *parent);
static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image);
- static QQuickWindowManager *createWindowManager();
+ static QSGRenderLoop *createWindowManager();
public slots:
@@ -140,6 +138,4 @@ signals:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGCONTEXT_H
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index 76b4e63816..545762aa72 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -157,7 +157,7 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im
specific window manager.
*/
-QQuickWindowManager *QSGContext::createWindowManager()
+QSGRenderLoop *QSGContext::createWindowManager()
{
QSGAdaptionPluginData *plugin = contextFactory();
if (plugin->factory)
diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h
index 4e5be6edce..be3987d823 100644
--- a/src/quick/scenegraph/qsgcontextplugin_p.h
+++ b/src/quick/scenegraph/qsgcontextplugin_p.h
@@ -47,20 +47,18 @@
#include <QtCore/qplugin.h>
#include <QtCore/qfactoryinterface.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGContext;
-class QQuickWindowManager;
+class QSGRenderLoop;
struct Q_QUICK_PRIVATE_EXPORT QSGContextFactoryInterface : public QFactoryInterface
{
virtual QSGContext *create(const QString &key) const = 0;
virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image) = 0;
- virtual QQuickWindowManager *createWindowManager() = 0;
+ virtual QSGRenderLoop *createWindowManager() = 0;
};
#define QSGContextFactoryInterface_iid \
@@ -79,11 +77,9 @@ public:
virtual QSGContext *create(const QString &key) const = 0;
virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) { return 0; }
- virtual QQuickWindowManager *createWindowManager() { return 0; }
+ virtual QSGRenderLoop *createWindowManager() { return 0; }
};
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGCONTEXTPLUGIN_H
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 210e9f23f5..9e0cfca069 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -178,7 +178,7 @@ void QSGTextMaskMaterialData::updateState(const RenderState &state, QSGMaterial
1.0 / material->cacheTextureHeight()));
glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
- // Set the mag/min filters to be linear. We only need to do this when the texture
+ // Set the mag/min filters to be nearest. We only need to do this when the texture
// has been recreated.
if (updated) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -186,8 +186,28 @@ void QSGTextMaskMaterialData::updateState(const RenderState &state, QSGMaterial
}
}
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ if (state.isMatrixDirty()) {
+ QMatrix4x4 transform = state.modelViewMatrix();
+ qreal xTranslation = transform(0, 3);
+ qreal yTranslation = transform(1, 3);
+
+ // Remove translation and check identity to see if matrix is only translating.
+ // If it is, we can round the translation to make sure the text is pixel aligned,
+ // which is the only thing that works with GL_NEAREST filtering. Adding rotations
+ // and scales to native rendered text is not a prioritized use case, since the
+ // default rendering type is designed for that.
+ transform(0, 3) = 0.0;
+ transform(1, 3) = 0.0;
+ if (transform.isIdentity()) {
+ transform(0, 3) = qRound(xTranslation);
+ transform(1, 3) = qRound(yTranslation);
+
+ transform = state.projectionMatrix() * transform;
+ program()->setUniformValue(m_matrix_id, transform);
+ } else {
+ program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ }
+ }
}
QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font)
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
index 8f764b2d91..a3d5d7c3ae 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
@@ -39,14 +39,12 @@
**
****************************************************************************/
-#ifndef DEFAULT_GLYPHNODE_H
-#define DEFAULT_GLYPHNODE_H
+#ifndef QSGDEFAULTGLYPHNODE_P_H
+#define QSGDEFAULTGLYPHNODE_P_H
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/qsgnode.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QGlyphs;
@@ -80,6 +78,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
-#endif // DEFAULT_GLYPHNODE_H
+#endif
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
index efa96a1805..263523221e 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef TEXTMASKMATERIAL_H
-#define TEXTMASKMATERIAL_H
+#ifndef QSGDEFAULTGLYPHNODE_P_P_H
+#define QSGDEFAULTGLYPHNODE_P_P_H
#include <qcolor.h>
#include <QtQuick/qsgmaterial.h>
@@ -93,4 +93,4 @@ private:
QT_END_NAMESPACE
-#endif // TEXTMASKMATERIAL_H
+#endif
diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h
index 07cfa2c925..7d299faee3 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h
@@ -40,14 +40,12 @@
****************************************************************************/
-#ifndef DEFAULT_PIXMAPNODE_H
-#define DEFAULT_PIXMAPNODE_H
+#ifndef QSGDEFAULTIMAGENODE_P_H
+#define QSGDEFAULTIMAGENODE_P_H
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/qsgtexturematerial.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class SmoothTextureMaterial : public QSGTextureMaterial
@@ -103,6 +101,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
index ebfe4a74a0..f642ca8d16 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
@@ -338,6 +338,9 @@ void QSGDefaultRectangleNode::update()
updateGeometry();
m_dirty_geometry = false;
}
+ m_material.setFlag(QSGMaterial::Blending, (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
+ || m_color.alpha() < 255
+ || (m_pen_width > 0 && m_border_color.alpha() < 255));
}
void QSGDefaultRectangleNode::updateGeometry()
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
index f42a1fa14a..24bdbb3d34 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
@@ -40,15 +40,13 @@
****************************************************************************/
-#ifndef DEFAULT_RECTANGLENODE_H
-#define DEFAULT_RECTANGLENODE_H
+#ifndef QSGDEFAULTRECTANGLENODE_P_H
+#define QSGDEFAULTRECTANGLENODE_P_H
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/qsgvertexcolormaterial.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGContext;
@@ -104,6 +102,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index bb423820b4..4f03ab52e2 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -39,16 +39,14 @@
**
****************************************************************************/
-#ifndef DISTANCEFIELD_GLYPHNODE_H
-#define DISTANCEFIELD_GLYPHNODE_H
+#ifndef QSGDISTANCEFIELDGLYPHNODE_P_H
+#define QSGDISTANCEFIELDGLYPHNODE_P_H
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/qsgtexture.h>
#include <QtQuick/private/qquicktext_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGContext;
@@ -112,8 +110,6 @@ private:
uint m_dirtyMaterial: 1;
};
-QT_END_HEADER
-
QT_END_NAMESPACE
-#endif // DISTANCEFIELD_GLYPHNODE_H
+#endif
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
index fe66776896..d6aa38affa 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef DISTANCEFIELDTEXTMATERIAL_H
-#define DISTANCEFIELDTEXTMATERIAL_H
+#ifndef QSGDISTANCEFIELDGLYPHNODE_P_P_H
+#define QSGDISTANCEFIELDGLYPHNODE_P_P_H
#include <QtQuick/qsgmaterial.h>
#include "qsgdistancefieldglyphnode_p.h"
@@ -141,4 +141,4 @@ public:
QT_END_NAMESPACE
-#endif // DISTANCEFIELDTEXTMATERIAL_H
+#endif
diff --git a/src/quick/scenegraph/qsgflashnode_p.h b/src/quick/scenegraph/qsgflashnode_p.h
index 84ea734a25..d8d2ef4a24 100644
--- a/src/quick/scenegraph/qsgflashnode_p.h
+++ b/src/quick/scenegraph/qsgflashnode_p.h
@@ -44,8 +44,6 @@
#include <QtQuick/QSGSimpleRectNode>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGFlashNode : public QSGSimpleRectNode
@@ -61,7 +59,5 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGFLASHNODE_H
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
new file mode 100644
index 0000000000..a55658bf71
--- /dev/null
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -0,0 +1,382 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrenderloop_p.h"
+#include "qsgthreadedrenderloop_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTime>
+#include <QtCore/private/qabstractanimation_p.h>
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+
+#include <QtQml/private/qqmlglobal_p.h>
+
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/private/qquickwindow_p.h>
+#include <QtQuick/private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(qquick_render_timing, QML_RENDER_TIMING)
+
+extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+
+/*!
+ expectations for this manager to work:
+ - one opengl context to render multiple windows
+ - OpenGL pipeline will not block for vsync in swap
+ - OpenGL pipeline will block based on a full buffer queue.
+ - Multiple screens can share the OpenGL context
+ - Animations are advanced for all windows once per swap
+ */
+
+DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
+DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
+
+QSGRenderLoop *QSGRenderLoop::s_instance = 0;
+
+QSGRenderLoop::~QSGRenderLoop()
+{
+}
+
+class QSGGuiThreadRenderLoop : public QObject, public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGGuiThreadRenderLoop();
+
+ void show(QQuickWindow *window);
+ void hide(QQuickWindow *window);
+
+ void windowDestroyed(QQuickWindow *window);
+
+ void initializeGL();
+ void renderWindow(QQuickWindow *window);
+ void exposureChanged(QQuickWindow *window);
+ QImage grab(QQuickWindow *window);
+ void resize(QQuickWindow *window, const QSize &size);
+
+ void maybeUpdate(QQuickWindow *window);
+ void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation.
+
+ void releaseResources(QQuickWindow *) { }
+
+ QAnimationDriver *animationDriver() const { return 0; }
+
+ QSGContext *sceneGraphContext() const;
+
+ bool event(QEvent *);
+
+ struct WindowData {
+ bool updatePending : 1;
+ bool grabOnly : 1;
+ };
+
+ QHash<QQuickWindow *, WindowData> m_windows;
+
+ QOpenGLContext *gl;
+ QSGContext *sg;
+
+ QImage grabContent;
+
+ bool eventPending;
+};
+
+
+QSGRenderLoop *QSGRenderLoop::instance()
+{
+ if (!s_instance) {
+
+ s_instance = QSGContext::createWindowManager();
+
+ bool bufferQueuing = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL);
+#ifdef Q_OS_WIN
+ bool fancy = false; // QTBUG-28037
+#else
+ bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
+#endif
+ if (qmlNoThreadedRenderer())
+ fancy = false;
+ else if (qmlForceThreadedRenderer())
+ fancy = true;
+
+ // Enable fixed animation steps...
+ QByteArray fixed = qgetenv("QML_FIXED_ANIMATION_STEP");
+ bool fixedAnimationSteps = bufferQueuing;
+ if (fixed == "no")
+ fixedAnimationSteps = false;
+ else if (fixed.length())
+ fixedAnimationSteps = true;
+ if (fixedAnimationSteps)
+ QUnifiedTimer::instance(true)->setConsistentTiming(true);
+
+ if (!s_instance) {
+ s_instance = fancy
+ ? (QSGRenderLoop*) new QSGThreadedRenderLoop
+ : (QSGRenderLoop*) new QSGGuiThreadRenderLoop;
+ }
+ }
+ return s_instance;
+}
+
+void QSGRenderLoop::setInstance(QSGRenderLoop *instance)
+{
+ Q_ASSERT(!s_instance);
+ s_instance = instance;
+}
+
+QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop()
+ : gl(0)
+ , eventPending(false)
+{
+ sg = QSGContext::createDefaultContext();
+}
+
+
+void QSGGuiThreadRenderLoop::show(QQuickWindow *window)
+{
+ WindowData data;
+ data.updatePending = false;
+ data.grabOnly = false;
+ m_windows[window] = data;
+
+ maybeUpdate(window);
+}
+
+void QSGGuiThreadRenderLoop::hide(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows.remove(window);
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ cd->cleanupNodesOnShutdown();
+
+ if (m_windows.size() == 0) {
+ if (!cd->persistentSceneGraph) {
+ sg->invalidate();
+ if (!cd->persistentGLContext) {
+ delete gl;
+ gl = 0;
+ }
+ }
+ }
+}
+
+void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ hide(window);
+ if (m_windows.size() == 0) {
+ sg->invalidate();
+ delete gl;
+ gl = 0;
+ }
+}
+
+void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
+{
+ bool renderWithoutShowing = QQuickWindowPrivate::get(window)->renderWithoutShowing;
+ if ((!window->isExposed() && !renderWithoutShowing) || !m_windows.contains(window))
+ return;
+
+ WindowData &data = const_cast<WindowData &>(m_windows[window]);
+
+ QQuickWindow *masterWindow = 0;
+ if (!window->isVisible() && !renderWithoutShowing) {
+ // Find a "proper surface" to bind...
+ for (QHash<QQuickWindow *, WindowData>::const_iterator it = m_windows.constBegin();
+ it != m_windows.constEnd() && !masterWindow; ++it) {
+ if (it.key()->isVisible())
+ masterWindow = it.key();
+ }
+ } else {
+ masterWindow = window;
+ }
+
+ if (!masterWindow)
+ return;
+
+ if (!QQuickWindowPrivate::get(masterWindow)->isRenderable()) {
+ qWarning().nospace()
+ << "Unable to find a renderable master window "
+ << masterWindow << "when trying to render"
+ << window << " (" << window->geometry() << ").";
+ return;
+ }
+
+ bool current = false;
+
+ if (!gl) {
+ gl = new QOpenGLContext();
+ gl->setFormat(masterWindow->requestedFormat());
+ if (!gl->create()) {
+ delete gl;
+ gl = 0;
+ }
+ current = gl->makeCurrent(masterWindow);
+ if (current)
+ sg->initialize(gl);
+ } else {
+ current = gl->makeCurrent(masterWindow);
+ }
+
+ bool alsoSwap = data.updatePending;
+ data.updatePending = false;
+
+ if (!current)
+ return;
+
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ cd->polishItems();
+
+ int renderTime = 0, syncTime = 0;
+ QTime renderTimer;
+ if (qquick_render_timing())
+ renderTimer.start();
+
+ cd->syncSceneGraph();
+
+ if (qquick_render_timing())
+ syncTime = renderTimer.elapsed();
+
+ cd->renderSceneGraph(window->size());
+
+ if (qquick_render_timing())
+ renderTime = renderTimer.elapsed() - syncTime;
+
+ if (data.grabOnly) {
+ grabContent = qt_gl_read_framebuffer(window->size(), false, false);
+ data.grabOnly = false;
+ }
+
+ if (alsoSwap && window->isVisible()) {
+ gl->swapBuffers(window);
+ cd->fireFrameSwapped();
+ }
+
+ if (qquick_render_timing()) {
+ static QTime lastFrameTime = QTime::currentTime();
+ const int swapTime = renderTimer.elapsed() - renderTime - syncTime;
+ qDebug() << "- Breakdown of frame time; sync:" << syncTime
+ << "ms render:" << renderTime << "ms swap:" << swapTime
+ << "ms total:" << swapTime + renderTime + syncTime
+ << "ms time since last frame:" << (lastFrameTime.msecsTo(QTime::currentTime()))
+ << "ms";
+ lastFrameTime = QTime::currentTime();
+ }
+
+ // Might have been set during syncSceneGraph()
+ if (data.updatePending)
+ maybeUpdate(window);
+}
+
+void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ if (window->isExposed()) {
+ m_windows[window].updatePending = true;
+ renderWindow(window);
+ }
+}
+
+QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return QImage();
+
+ m_windows[window].grabOnly = true;
+
+ renderWindow(window);
+
+ QImage grabbed = grabContent;
+ grabContent = QImage();
+ return grabbed;
+}
+
+
+
+void QSGGuiThreadRenderLoop::resize(QQuickWindow *, const QSize &)
+{
+}
+
+
+
+void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows[window].updatePending = true;
+
+ if (!eventPending) {
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ eventPending = true;
+ }
+}
+
+
+
+QSGContext *QSGGuiThreadRenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+
+bool QSGGuiThreadRenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ eventPending = false;
+ for (QHash<QQuickWindow *, WindowData>::const_iterator it = m_windows.constBegin();
+ it != m_windows.constEnd(); ++it) {
+ const WindowData &data = it.value();
+ if (data.updatePending)
+ renderWindow(it.key());
+ }
+ return true;
+ }
+ return QObject::event(e);
+}
+
+#include "qsgrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
new file mode 100644
index 0000000000..2ec6de9411
--- /dev/null
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRenderLoop_P_H
+#define QSGRenderLoop_P_H
+
+#include <QtGui/QImage>
+#include <private/qtquickglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickWindow;
+class QSGContext;
+class QAnimationDriver;
+
+class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop
+{
+public:
+ virtual ~QSGRenderLoop();
+
+ virtual void show(QQuickWindow *window) = 0;
+ virtual void hide(QQuickWindow *window) = 0;
+
+ virtual void windowDestroyed(QQuickWindow *window) = 0;
+
+ virtual void exposureChanged(QQuickWindow *window) = 0;
+ virtual QImage grab(QQuickWindow *window) = 0;
+ virtual void resize(QQuickWindow *window, const QSize &size) = 0;
+
+ virtual void update(QQuickWindow *window) = 0;
+ virtual void maybeUpdate(QQuickWindow *window) = 0;
+
+ virtual QAnimationDriver *animationDriver() const = 0;
+
+ virtual QSGContext *sceneGraphContext() const = 0;
+
+ virtual void releaseResources(QQuickWindow *window) = 0;
+
+ // ### make this less of a singleton
+ static QSGRenderLoop *instance();
+ static void setInstance(QSGRenderLoop *instance);
+
+private:
+ static QSGRenderLoop *s_instance;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRenderLoop_P_H
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
index 0286408116..ac613c16f4 100644
--- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
@@ -46,8 +46,6 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qqmlguard_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QPlatformSharedGraphicsCache;
@@ -132,6 +130,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGSHAREDDISTANCEFIELDGLYPHCACHE_H
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
new file mode 100644
index 0000000000..ef50d5a89d
--- /dev/null
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -0,0 +1,1113 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QAnimationDriver>
+#include <QtCore/QQueue>
+
+#include <QtGui/QGuiApplication>
+#include <QtGui/QScreen>
+
+#include <QtQuick/QQuickWindow>
+#include <private/qquickwindow_p.h>
+
+#include <QtQuick/private/qsgrenderer_p.h>
+
+#include "qsgthreadedrenderloop_p.h"
+
+/*
+ Overall design:
+
+ There are two classes here. QSGThreadedRenderLoop and
+ QSGRenderThread. All communication between the two is based on
+ event passing and we have a number of custom events.
+
+ In this implementation, the render thread is never blocked and the
+ GUI thread will initiate a polishAndSync which will block and wait
+ for the render thread to pick it up and release the block only
+ after the render thread is done syncing. The reason for this
+ is:
+
+ 1. Clear blocking paradigm. We only have one real "block" point
+ (polishAndSync()) and all blocking is initiated by GUI and picked
+ up by Render at specific times based on events. This makes the
+ execution deterministic.
+
+ 2. Render does not have to interact with GUI. This is done so that
+ the render thread can run its own animation system which stays
+ alive even when the GUI thread is blocked doing i/o, object
+ instantiation, QPainter-painting or any other non-trivial task.
+
+ ---
+
+ The render loop is active while any window is exposed. All visible
+ windows are tracked, but only exposed windows are actually added to
+ the render thread and rendered. That means that if all windows are
+ obscured, we might end up cleaning up the SG and GL context (if all
+ windows have disabled persistency). Especially for multiprocess,
+ low-end systems, this should be quite important.
+
+ */
+
+QT_BEGIN_NAMESPACE
+
+
+// #define QSG_RENDER_LOOP_DEBUG
+// #define QSG_RENDER_LOOP_DEBUG_FULL
+#ifdef QSG_RENDER_LOOP_DEBUG
+#define QSG_RENDER_LOOP_DEBUG_BASIC
+#endif
+
+#ifdef QSG_RENDER_LOOP_DEBUG_FULL
+#define QSG_RENDER_LOOP_DEBUG_BASIC
+#endif
+
+#if defined (QSG_RENDER_LOOP_DEBUG_FULL)
+QElapsedTimer qsgrl_timer;
+# define RLDEBUG1(x) qDebug("(%6d) %s : %4d - %s", (int) qsgrl_timer.elapsed(), __FILE__, __LINE__, x);
+# define RLDEBUG(x) qDebug("(%6d) %s : %4d - %s", (int) qsgrl_timer.elapsed(), __FILE__, __LINE__, x);
+#elif defined (QSG_RENDER_LOOP_DEBUG_BASIC)
+QElapsedTimer qsgrl_timer;
+# define RLDEBUG1(x) qDebug("(%6d) %s : %4d - %s", (int) qsgrl_timer.elapsed(), __FILE__, __LINE__, x);
+# define RLDEBUG(x)
+#else
+# define RLDEBUG1(x)
+# define RLDEBUG(x)
+#endif
+
+
+static int get_env_int(const char *name, int defaultValue)
+{
+ QByteArray content = qgetenv(name);
+
+ bool ok = false;
+ int value = content.toInt(&ok);
+ return ok ? value : defaultValue;
+}
+
+
+static inline int qsgrl_animation_interval() {
+ qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate();
+ // To work around that some platforms wrongfully return 0 or something
+ // bogus for refreshrate
+ if (refreshRate < 1)
+ return 16;
+ return int(1000 / refreshRate);
+}
+
+
+#ifndef QSG_NO_WINDOW_TIMING
+static bool qquick_window_timing = !qgetenv("QML_WINDOW_TIMING").isEmpty();
+static QTime threadTimer;
+static int syncTime;
+static int renderTime;
+static int sinceLastTime;
+#endif
+
+extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+
+// RL: Render Loop
+// RT: Render Thread
+
+// Passed from the RL to the RT when a window is rendeirng on screen
+// and should be added to the render loop.
+const QEvent::Type WM_Expose = QEvent::Type(QEvent::User + 1);
+
+// Passed from the RL to the RT when a window is removed obscured and
+// should be removed from the render loop.
+const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 2);
+
+// Passed from the RL to itself to initiate a polishAndSync() call.
+const QEvent::Type WM_LockAndSync = QEvent::Type(QEvent::User + 3);
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync
+// (updatePaintNode())
+const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 4);
+
+// Passed by the RT to itself to trigger another render pass. This is
+// typically a result of QQuickWindow::update().
+const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 5);
+
+// Passed by the RL to the RT when a window has changed size.
+const QEvent::Type WM_Resize = QEvent::Type(QEvent::User + 6);
+
+// Passed by the RL to the RT to free up maybe release SG and GL contexts
+// if no windows are rendering.
+const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 7);
+
+// Passed by the RL to the RL when maybeUpdate is called on the RT to
+// just replay the maybeUpdate later. This typically happens when
+// updatePaintNode() results in a call to QQuickItem::update().
+const QEvent::Type WM_UpdateLater = QEvent::Type(QEvent::User + 8);
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is
+// called.
+const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 9);
+
+template <typename T> T *windowFor(const QList<T> list, QQuickWindow *window)
+{
+ for (int i=0; i<list.size(); ++i) {
+ const T &t = list.at(i);
+ if (t.window == window)
+ return const_cast<T *>(&t);
+ }
+ return 0;
+}
+
+
+class WMWindowEvent : public QEvent
+{
+public:
+ WMWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
+ QQuickWindow *window;
+};
+
+class WMTryReleaseEvent : public WMWindowEvent
+{
+public:
+ WMTryReleaseEvent(QQuickWindow *win, bool destroy)
+ : WMWindowEvent(win, WM_TryRelease)
+ , inDestructor(destroy)
+ {}
+
+ bool inDestructor;
+};
+
+class WMResizeEvent : public WMWindowEvent
+{
+public:
+ WMResizeEvent(QQuickWindow *c, const QSize &s) : WMWindowEvent(c, WM_Resize), size(s) { }
+ QSize size;
+};
+
+
+class WMExposeEvent : public WMWindowEvent
+{
+public:
+ WMExposeEvent(QQuickWindow *c) : WMWindowEvent(c, WM_Expose), size(c->size()) { }
+ QSize size;
+};
+
+
+class WMGrabEvent : public WMWindowEvent
+{
+public:
+ WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {}
+ QImage *image;
+};
+
+
+class QSGRenderThreadEventQueue : public QQueue<QEvent *>
+{
+public:
+ QSGRenderThreadEventQueue()
+ : waiting(false)
+ {
+ }
+
+ void addEvent(QEvent *e) {
+ mutex.lock();
+ enqueue(e);
+ if (waiting)
+ condition.wakeOne();
+ mutex.unlock();
+ }
+
+ QEvent *takeEvent(bool wait) {
+ mutex.lock();
+ if (size() == 0 && wait) {
+ waiting = true;
+ condition.wait(&mutex);
+ waiting = false;
+ }
+ QEvent *e = dequeue();
+ mutex.unlock();
+ return e;
+ }
+
+ bool hasMoreEvents() {
+ mutex.lock();
+ bool has = !isEmpty();
+ mutex.unlock();
+ return has;
+ }
+
+private:
+ QMutex mutex;
+ QWaitCondition condition;
+ bool waiting;
+};
+
+
+class QSGRenderThread : public QThread
+{
+ Q_OBJECT
+public:
+
+ QSGRenderThread(QSGThreadedRenderLoop *w)
+ : wm(w)
+ , gl(0)
+ , sg(QSGContext::createDefaultContext())
+ , pendingUpdate(0)
+ , sleeping(false)
+ , syncResultedInChanges(false)
+ , guiIsLocked(false)
+ , shouldExit(false)
+ , stopEventProcessing(false)
+ {
+ sg->moveToThread(this);
+ vsyncDelta = qsgrl_animation_interval();
+ }
+
+
+ void invalidateOpenGL(QQuickWindow *window, bool inDestructor);
+ void initializeOpenGL();
+
+ bool event(QEvent *);
+ void run();
+
+ void syncAndRender();
+ void sync();
+
+ void requestRepaint()
+ {
+ if (sleeping)
+ stopEventProcessing = true;
+ if (m_windows.size() > 0)
+ pendingUpdate |= RepaintRequest;
+ }
+
+ void processEventsAndWaitForMore();
+ void processEvents();
+ void postEvent(QEvent *e);
+
+public slots:
+ void sceneGraphChanged() {
+ RLDEBUG(" Render: sceneGraphChanged()");
+ syncResultedInChanges = true;
+ }
+
+public:
+ enum UpdateRequest {
+ SyncRequest = 0x01,
+ RepaintRequest = 0x02
+ };
+
+ QSGThreadedRenderLoop *wm;
+ QOpenGLContext *gl;
+ QSGContext *sg;
+
+ QEventLoop eventLoop;
+
+ uint pendingUpdate : 2;
+ uint sleeping : 1;
+ uint syncResultedInChanges : 1;
+
+ volatile bool guiIsLocked;
+ volatile bool shouldExit;
+
+ float vsyncDelta;
+
+ QMutex mutex;
+ QWaitCondition waitCondition;
+
+ QElapsedTimer m_timer;
+
+ struct Window {
+ QQuickWindow *window;
+ QSize size;
+ };
+ QList<Window> m_windows;
+
+ // Local event queue stuff...
+ bool stopEventProcessing;
+ QSGRenderThreadEventQueue eventQueue;
+};
+
+bool QSGRenderThread::event(QEvent *e)
+{
+ switch ((int) e->type()) {
+
+ case WM_Expose: {
+ RLDEBUG1(" Render: WM_Expose");
+ WMExposeEvent *se = static_cast<WMExposeEvent *>(e);
+
+ pendingUpdate |= RepaintRequest;
+
+ if (windowFor(m_windows, se->window)) {
+ RLDEBUG1(" Render: - window already added...");
+ return true;
+ }
+
+ Window window;
+ window.window = se->window;
+ window.size = se->size;
+ m_windows << window;
+ return true; }
+
+ case WM_Obscure: {
+ RLDEBUG1(" Render: WM_Obscure");
+ WMWindowEvent *ce = static_cast<WMWindowEvent *>(e);
+ for (int i=0; i<m_windows.size(); ++i) {
+ if (m_windows.at(i).window == ce->window) {
+ RLDEBUG1(" Render: - removed one...");
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+
+ if (sleeping && m_windows.size())
+ stopEventProcessing = true;
+
+ return true; }
+
+ case WM_RequestSync:
+ RLDEBUG(" Render: WM_RequestSync");
+ if (sleeping)
+ stopEventProcessing = true;
+ if (m_windows.size() > 0)
+ pendingUpdate |= SyncRequest;
+ return true;
+
+ case WM_Resize: {
+ RLDEBUG(" Render: WM_Resize");
+ WMResizeEvent *re = static_cast<WMResizeEvent *>(e);
+ Window *w = windowFor(m_windows, re->window);
+ w->size = re->size;
+ // No need to wake up here as we will get a sync shortly.. (see QSGThreadedRenderLoop::resize());
+ return true; }
+
+ case WM_TryRelease:
+ RLDEBUG1(" Render: WM_TryRelease");
+ mutex.lock();
+ if (m_windows.size() == 0) {
+ WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
+ RLDEBUG1(" Render: - setting exit flag and invalidating GL");
+ invalidateOpenGL(wme->window, wme->inDestructor);
+ shouldExit = !gl;
+ if (sleeping)
+ stopEventProcessing = true;
+ } else {
+ RLDEBUG1(" Render: - not releasing anything because we have active windows...");
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+
+ case WM_Grab: {
+ RLDEBUG1(" Render: WM_Grab");
+ WMGrabEvent *ce = static_cast<WMGrabEvent *>(e);
+ Window *w = windowFor(m_windows, ce->window);
+ mutex.lock();
+ if (w) {
+ gl->makeCurrent(ce->window);
+
+ RLDEBUG1(" Render: - syncing scene graph");
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window);
+ d->syncSceneGraph();
+
+ RLDEBUG1(" Render: - rendering scene graph");
+ QQuickWindowPrivate::get(ce->window)->renderSceneGraph(w->size);
+
+ RLDEBUG1(" Render: - grabbing result...");
+ *ce->image = qt_gl_read_framebuffer(w->size, false, false);
+ }
+ RLDEBUG1(" Render: - waking gui to handle grab result");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_RequestRepaint:
+ // When GUI posts this event, it is followed by a polishAndSync, so we mustn't
+ // exit the event loop yet.
+ pendingUpdate |= RepaintRequest;
+ break;
+
+ default:
+ break;
+ }
+ return QThread::event(e);
+}
+
+void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor)
+{
+ RLDEBUG1(" Render: invalidateOpenGL()");
+
+ if (!gl)
+ return;
+
+ if (!window) {
+ qWarning("QSGThreadedRenderLoop:QSGRenderThread: no window to make current...");
+ return;
+ }
+
+
+ bool persistentGL = false;
+ bool persistentSG = false;
+
+ // GUI is locked so accessing the wm and window here is safe
+ for (int i=0; i<wm->m_windows.size(); ++i) {
+ const QSGThreadedRenderLoop::Window &w = wm->m_windows.at(i);
+ if (!inDestructor || w.window != window) {
+ persistentSG |= w.window->isPersistentSceneGraph();
+ persistentGL |= w.window->isPersistentOpenGLContext();
+ }
+ }
+
+ gl->makeCurrent(window);
+
+ // The canvas nodes must be cleanded up regardless if we are in the destructor..
+ if (!persistentSG || inDestructor) {
+ QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
+ dd->cleanupNodesOnShutdown();
+ }
+
+ // We're not doing any cleanup in this case...
+ if (persistentSG) {
+ RLDEBUG1(" Render: - persistent SG, avoiding cleanup");
+ return;
+ }
+
+ sg->invalidate();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ gl->doneCurrent();
+ RLDEBUG1(" Render: - invalidated scenegraph..");
+
+ if (!persistentGL) {
+ delete gl;
+ gl = 0;
+ RLDEBUG1(" Render: - invalidated OpenGL");
+ } else {
+ RLDEBUG1(" Render: - persistent GL, avoiding cleanup");
+ }
+}
+
+/*!
+ Enters the mutex lock to make sure GUI is blocking and performs
+ sync, then wakes GUI.
+ */
+void QSGRenderThread::sync()
+{
+ RLDEBUG(" Render: sync()");
+ mutex.lock();
+
+ Q_ASSERT_X(guiIsLocked, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
+ pendingUpdate = 0;
+
+ for (int i=0; i<m_windows.size(); ++i) {
+ Window &w = const_cast<Window &>(m_windows.at(i));
+ if (w.size.width() == 0 || w.size.height() == 0) {
+ RLDEBUG(" Render: - window has bad size, waiting...");
+ continue;
+ }
+ gl->makeCurrent(w.window);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
+ bool hadRenderer = d->renderer != 0;
+ d->syncSceneGraph();
+ if (!hadRenderer && d->renderer) {
+ RLDEBUG(" Render: - renderer was created, hooking up changed signal");
+ syncResultedInChanges = true;
+ connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), Qt::DirectConnection);
+ }
+ }
+
+ RLDEBUG(" Render: - unlocking after sync");
+
+ waitCondition.wakeOne();
+ mutex.unlock();
+
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+}
+
+
+void QSGRenderThread::syncAndRender()
+{
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ sinceLastTime = threadTimer.restart();
+#endif
+ QElapsedTimer waitTimer;
+ waitTimer.start();
+
+ RLDEBUG(" Render: syncAndRender()");
+
+ syncResultedInChanges = false;
+
+ bool repaintRequested = pendingUpdate & RepaintRequest;
+
+ if (pendingUpdate & SyncRequest) {
+ RLDEBUG(" Render: - update pending, doing sync");
+ sync();
+ }
+
+ if (!syncResultedInChanges && !(repaintRequested)) {
+ RLDEBUG(" Render: - no changes, rendering aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
+
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ syncTime = threadTimer.elapsed();
+#endif
+ RLDEBUG(" Render: - rendering starting");
+
+ for (int i=0; i<m_windows.size(); ++i) {
+ Window &w = const_cast<Window &>(m_windows.at(i));
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
+ if (!d->renderer || w.size.width() == 0 || w.size.height() == 0) {
+ RLDEBUG(" Render: - Window not yet ready, skipping render...");
+ continue;
+ }
+ gl->makeCurrent(w.window);
+ d->renderSceneGraph(w.size);
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing && i == 0)
+ renderTime = threadTimer.elapsed();
+#endif
+ gl->swapBuffers(w.window);
+ d->fireFrameSwapped();
+ }
+ RLDEBUG(" Render: - rendering done");
+
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ qDebug("window Time: sinceLast=%d, sync=%d, first render=%d, after final swap=%d",
+ sinceLastTime,
+ syncTime,
+ renderTime - syncTime,
+ threadTimer.elapsed() - renderTime);
+#endif
+}
+
+
+
+void QSGRenderThread::postEvent(QEvent *e)
+{
+ eventQueue.addEvent(e);
+}
+
+
+
+void QSGRenderThread::processEvents()
+{
+ RLDEBUG(" Render: processEvents()");
+ while (eventQueue.hasMoreEvents()) {
+ QEvent *e = eventQueue.takeEvent(false);
+ event(e);
+ delete e;
+ }
+ RLDEBUG(" Render: - done with processEvents()");
+}
+
+void QSGRenderThread::processEventsAndWaitForMore()
+{
+ RLDEBUG(" Render: processEventsAndWaitForMore()");
+ stopEventProcessing = false;
+ while (!stopEventProcessing) {
+ QEvent *e = eventQueue.takeEvent(true);
+ event(e);
+ delete e;
+ }
+ RLDEBUG(" Render: - done with processEventsAndWaitForMore()");
+}
+
+void QSGRenderThread::run()
+{
+ RLDEBUG1(" Render: run()");
+ while (!shouldExit) {
+
+ if (m_windows.size() > 0) {
+ if (!sg->isReady()) {
+ gl->makeCurrent(m_windows.at(0).window);
+ sg->initialize(gl);
+ }
+ syncAndRender();
+ }
+
+ processEvents();
+ QCoreApplication::processEvents();
+
+ if (!shouldExit
+ && (pendingUpdate == 0 || m_windows.size() == 0)) {
+ RLDEBUG(" Render: enter event loop (going to sleep)");
+ sleeping = true;
+ processEventsAndWaitForMore();
+ sleeping = false;
+ }
+
+ }
+
+ Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread...");
+
+ RLDEBUG1(" Render: run() completed...");
+}
+
+QSGThreadedRenderLoop::QSGThreadedRenderLoop()
+ : m_animation_timer(0)
+ , m_update_timer(0)
+ , m_sync_triggered_update(false)
+{
+#if defined(QSG_RENDER_LOOP_DEBUG_BASIC) || defined (QSG_RENDER_LOOP_DEBUG_FULL)
+ qsgrl_timer.start();
+#endif
+
+ m_thread = new QSGRenderThread(this);
+ m_thread->moveToThread(m_thread);
+
+ m_animation_driver = m_thread->sg->createAnimationDriver(this);
+
+ m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5);
+
+ connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted()));
+ connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped()));
+
+ m_animation_driver->install();
+ RLDEBUG1("GUI: QSGThreadedRenderLoop() created");
+}
+
+void QSGThreadedRenderLoop::maybePostPolishRequest()
+{
+ if (m_update_timer == 0) {
+ RLDEBUG("GUI: - posting update");
+ m_update_timer = startTimer(m_exhaust_delay, Qt::PreciseTimer);
+ }
+}
+
+QAnimationDriver *QSGThreadedRenderLoop::animationDriver() const
+{
+ return m_animation_driver;
+}
+
+QSGContext *QSGThreadedRenderLoop::sceneGraphContext() const
+{
+ return m_thread->sg;
+}
+
+bool QSGThreadedRenderLoop::anyoneShowing()
+{
+ for (int i=0; i<m_windows.size(); ++i) {
+ QQuickWindow *c = m_windows.at(i).window;
+ if (c->isVisible() && c->isExposed())
+ return true;
+ }
+ return false;
+}
+
+void QSGThreadedRenderLoop::animationStarted()
+{
+ RLDEBUG("GUI: animationStarted()");
+ if (!anyoneShowing() && m_animation_timer == 0)
+ m_animation_timer = startTimer(qsgrl_animation_interval());
+ maybePostPolishRequest();
+}
+
+void QSGThreadedRenderLoop::animationStopped()
+{
+ RLDEBUG("GUI: animationStopped()");
+ if (!anyoneShowing()) {
+ killTimer(m_animation_timer);
+ m_animation_timer = 0;
+ }
+}
+
+
+
+/*
+ Adds this window to the list of tracked windowes in this window
+ manager. show() does not trigger rendering to start, that happens
+ in expose.
+ */
+
+void QSGThreadedRenderLoop::show(QQuickWindow *window)
+{
+ RLDEBUG1("GUI: show()");
+
+ Window win;
+ win.window = window;
+ m_windows << win;
+}
+
+
+
+/*
+ Removes this window from the list of tracked windowes in this
+ window manager. hide() will trigger obscure, which in turn will
+ stop rendering.
+ */
+
+void QSGThreadedRenderLoop::hide(QQuickWindow *window)
+{
+ RLDEBUG1("GUI: hide()");
+
+ if (window->isExposed())
+ handleObscurity(window);
+
+ releaseResources(window);
+
+ for (int i=0; i<m_windows.size(); ++i) {
+ if (m_windows.at(i).window == window) {
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+}
+
+
+/*!
+ If the window is first hide it, then perform a complete cleanup
+ with releaseResources which will take down the GL context and
+ exit the rendering thread.
+ */
+void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ RLDEBUG1("GUI: windowDestroyed()");
+
+ if (window->isVisible())
+ hide(window);
+ releaseResources(window, true);
+
+ RLDEBUG1("GUI: - done with windowDestroyed()");
+}
+
+
+void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ RLDEBUG1("GUI: exposureChanged()");
+ if (windowFor(m_windows, window) == 0)
+ return;
+
+ if (window->isExposed()) {
+ handleExposure(window);
+ } else {
+ handleObscurity(window);
+ }
+}
+
+
+/*!
+ Will post an event to the render thread that this window should
+ start to render.
+ */
+void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
+{
+ RLDEBUG1("GUI: handleExposure");
+
+ // Because we are going to bind a GL context to it, make sure it
+ // is created.
+ if (!window->handle())
+ window->create();
+
+ m_thread->postEvent(new WMExposeEvent(window));
+
+ // Start render thread if it is not running
+ if (!m_thread->isRunning()) {
+ m_thread->shouldExit = false;
+
+ RLDEBUG1("GUI: - starting render thread...");
+
+ if (!m_thread->gl) {
+ QOpenGLContext *ctx = new QOpenGLContext();
+ ctx->setFormat(window->requestedFormat());
+ ctx->create();
+ ctx->moveToThread(m_thread);
+ m_thread->gl = ctx;
+ }
+
+ m_thread->start();
+
+ } else {
+ RLDEBUG1("GUI: - render thread already running");
+ }
+
+ polishAndSync();
+
+ // Kill non-visual animation timer if it is running
+ if (m_animation_timer) {
+ killTimer(m_animation_timer);
+ m_animation_timer = 0;
+ }
+
+}
+
+/*!
+ This function posts an event to the render thread to remove the window
+ from the list of windowses to render.
+
+ It also starts up the non-vsync animation tick if no more windows
+ are showing.
+ */
+void QSGThreadedRenderLoop::handleObscurity(QQuickWindow *window)
+{
+ RLDEBUG1("GUI: handleObscurity");
+ if (m_thread->isRunning())
+ m_thread->postEvent(new WMWindowEvent(window, WM_Obscure));
+
+ if (!anyoneShowing() && m_animation_driver->isRunning() && m_animation_timer == 0) {
+ m_animation_timer = startTimer(qsgrl_animation_interval());
+ }
+}
+
+
+/*!
+ Called whenever the QML scene has changed. Will post an event to
+ ourselves that a sync is needed.
+ */
+void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_thread->guiIsLocked,
+ "QQuickItem::update()",
+ "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
+
+ RLDEBUG("GUI: maybeUpdate...");
+ Window *w = windowFor(m_windows, window);
+ if (!w || !m_thread->isRunning()) {
+ return;
+ }
+
+ // Call this function from the Gui thread later as startTimer cannot be
+ // called from the render thread.
+ if (QThread::currentThread() == m_thread) {
+ RLDEBUG("GUI: - on render thread, will update later..");
+ m_sync_triggered_update = true;
+ return;
+ }
+
+ maybePostPolishRequest();
+}
+
+/*!
+ Called when the QQuickWindow should be explicitly repainted. This function
+ can also be called on the render thread when the GUI thread is blocked to
+ keep render thread animations alive.
+ */
+void QSGThreadedRenderLoop::update(QQuickWindow *window)
+{
+ if (QThread::currentThread() == m_thread) {
+ RLDEBUG("Gui: update called on render thread");
+ m_thread->requestRepaint();
+ return;
+ }
+
+ RLDEBUG("Gui: update called");
+ m_thread->postEvent(new QEvent(WM_RequestRepaint));
+ maybeUpdate(window);
+}
+
+
+
+/*!
+ * Release resources will post an event to the render thread to
+ * free up the SG and GL resources and exists the render thread.
+ */
+void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestructor)
+{
+ RLDEBUG1("GUI: releaseResources requested...");
+
+ m_thread->mutex.lock();
+ if (m_thread->isRunning() && !m_thread->shouldExit) {
+ RLDEBUG1("GUI: - posting release request to render thread");
+ m_thread->postEvent(new WMTryReleaseEvent(window, inDestructor));
+ m_thread->waitCondition.wait(&m_thread->mutex);
+ }
+ m_thread->mutex.unlock();
+}
+
+
+
+void QSGThreadedRenderLoop::polishAndSync()
+{
+ if (!anyoneShowing())
+ return;
+
+ RLDEBUG("GUI: polishAndSync()");
+
+#ifndef QSG_NO_WINDOW_TIMING
+ QElapsedTimer timer;
+ int polishTime = 0;
+ int waitTime = 0;
+ int syncTime;
+ if (qquick_window_timing)
+ timer.start();
+#endif
+
+ // Polish as the last thing we do before we allow the sync to take place
+ for (int i=0; i<m_windows.size(); ++i) {
+ const Window &w = m_windows.at(i);
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window);
+ d->polishItems();
+ }
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ polishTime = timer.elapsed();
+#endif
+
+ m_sync_triggered_update = false;
+
+ RLDEBUG("GUI: - lock for sync...");
+ m_thread->mutex.lock();
+ m_thread->guiIsLocked = true;
+ m_thread->postEvent(new QEvent(WM_RequestSync));
+
+ RLDEBUG("GUI: - wait for sync...");
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ waitTime = timer.elapsed();
+#endif
+ m_thread->waitCondition.wait(&m_thread->mutex);
+ m_thread->guiIsLocked = false;
+ m_thread->mutex.unlock();
+ RLDEBUG("GUI: - unlocked after sync...");
+
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ syncTime = timer.elapsed();
+#endif
+
+ killTimer(m_update_timer);
+ m_update_timer = 0;
+
+ if (m_animation_driver->isRunning()) {
+ RLDEBUG("GUI: - animations advancing");
+ m_animation_driver->advance();
+ RLDEBUG("GUI: - animations done");
+
+ // We need to trigger another sync to keep animations running...
+ maybePostPolishRequest();
+ } else if (m_sync_triggered_update) {
+ maybePostPolishRequest();
+ }
+
+#ifndef QSG_NO_WINDOW_TIMING
+ if (qquick_window_timing)
+ qDebug(" - polish=%d, wait=%d, sync=%d -- animations=%d", polishTime, waitTime - polishTime, syncTime - waitTime, int(timer.elapsed() - syncTime));
+#endif
+}
+
+bool QSGThreadedRenderLoop::event(QEvent *e)
+{
+ switch ((int) e->type()) {
+
+ case QEvent::Timer:
+ if (static_cast<QTimerEvent *>(e)->timerId() == m_animation_timer) {
+ RLDEBUG("GUI: QEvent::Timer -> non-visual animation");
+ m_animation_driver->advance();
+ } else if (static_cast<QTimerEvent *>(e)->timerId() == m_update_timer) {
+ RLDEBUG("GUI: QEvent::Timer -> Polish & Sync");
+ polishAndSync();
+ }
+ return true;
+
+ default:
+ break;
+ }
+
+ return QObject::event(e);
+}
+
+
+
+/*
+ Locks down GUI and performs a grab the scene graph, then returns the result.
+
+ Since the QML scene could have changed since the last time it was rendered,
+ we need to polish and sync the scene graph. This might seem superfluous, but
+ - QML changes could have triggered deleteLater() which could have removed
+ textures or other objects from the scene graph, causing render to crash.
+ - Autotests rely on grab(), setProperty(), grab(), compare behavior.
+ */
+
+QImage QSGThreadedRenderLoop::grab(QQuickWindow *window)
+{
+ RLDEBUG("GUI: grab");
+ if (!m_thread->isRunning())
+ return QImage();
+
+ if (!window->handle())
+ window->create();
+
+ RLDEBUG1("GUI: - polishing items...");
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
+ d->polishItems();
+
+ QImage result;
+ m_thread->mutex.lock();
+ RLDEBUG1("GUI: - locking, posting grab event");
+ m_thread->postEvent(new WMGrabEvent(window, &result));
+ m_thread->waitCondition.wait(&m_thread->mutex);
+ RLDEBUG1("GUI: - locking, grab done, unlocking");
+ m_thread->mutex.unlock();
+
+ RLDEBUG1("Gui: - grab complete");
+
+ return result;
+}
+
+/*
+ Notify the render thread that the window is now a new size. Then
+ locks GUI until render has adapted.
+ */
+
+void QSGThreadedRenderLoop::resize(QQuickWindow *w, const QSize &size)
+{
+ RLDEBUG1("GUI: resize");
+
+ if (!m_thread->isRunning() || !m_windows.size() || !w->isExposed() || windowFor(m_windows, w) == 0) {
+ return;
+ }
+
+ if (size.width() == 0 || size.height() == 0)
+ return;
+
+ RLDEBUG("GUI: - posting resize event...");
+ m_thread->postEvent(new WMResizeEvent(w, size));
+
+ polishAndSync();
+}
+
+#include "qsgthreadedrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
new file mode 100644
index 0000000000..63b2b442e6
--- /dev/null
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 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, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTHREADEDRENDERLOOP_P_H
+#define QSGTHREADEDRENDERLOOP_P_H
+
+#include <QtCore/QThread>
+#include <QtGui/QOpenGLContext>
+#include <private/qsgcontext_p.h>
+
+#include "qsgrenderloop_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGRenderThread;
+
+class QSGThreadedRenderLoop : public QObject, public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGThreadedRenderLoop();
+
+ void show(QQuickWindow *window);
+ void hide(QQuickWindow *window);
+
+ void windowDestroyed(QQuickWindow *window);
+ void exposureChanged(QQuickWindow *window);
+
+ void handleExposure(QQuickWindow *window);
+ void handleObscurity(QQuickWindow *window);
+
+ QImage grab(QQuickWindow *);
+
+ void resize(QQuickWindow *, const QSize &);
+
+ void update(QQuickWindow *window);
+ void maybeUpdate(QQuickWindow *window);
+ QSGContext *sceneGraphContext() const;
+
+ QAnimationDriver *animationDriver() const;
+
+ void releaseResources(QQuickWindow *window) { releaseResources(window, false); }
+
+ bool event(QEvent *);
+
+ void wakeup();
+
+public slots:
+ void animationStarted();
+ void animationStopped();
+
+private:
+ friend class QSGRenderThread;
+
+ void releaseResources(QQuickWindow *window, bool inDestructor);
+ bool checkAndResetForceUpdate(QQuickWindow *window);
+
+ bool anyoneShowing();
+ void initialize();
+
+ void maybePostPolishRequest();
+
+ void waitForReleaseComplete();
+
+ void polishAndSync();
+
+ struct Window {
+ QQuickWindow *window;
+ };
+
+ QSGRenderThread *m_thread;
+ QAnimationDriver *m_animation_driver;
+ QList<Window> m_windows;
+
+ int m_animation_timer;
+ int m_update_timer;
+ int m_exhaust_delay;
+
+ bool m_sync_triggered_update;
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif // QSGTHREADEDRENDERLOOP_P_H
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index a58e9b46ad..8c87e23ac9 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -65,7 +65,10 @@ HEADERS += \
$$PWD/qsgdefaultimagenode_p.h \
$$PWD/qsgdefaultrectanglenode_p.h \
$$PWD/qsgflashnode_p.h \
- $$PWD/qsgshareddistancefieldglyphcache_p.h
+ $$PWD/qsgshareddistancefieldglyphcache_p.h \
+ $$PWD/qsgrenderloop_p.h \
+ $$PWD/qsgthreadedrenderloop_p.h
+
SOURCES += \
$$PWD/qsgadaptationlayer.cpp \
@@ -79,7 +82,10 @@ SOURCES += \
$$PWD/qsgdefaultimagenode.cpp \
$$PWD/qsgdefaultrectanglenode.cpp \
$$PWD/qsgflashnode.cpp \
- $$PWD/qsgshareddistancefieldglyphcache.cpp
+ $$PWD/qsgshareddistancefieldglyphcache.cpp \
+ $$PWD/qsgrenderloop.cpp \
+ $$PWD/qsgthreadedrenderloop.cpp
+
diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h
index 4ec628288b..1255001d0c 100644
--- a/src/quick/scenegraph/util/qsgareaallocator_p.h
+++ b/src/quick/scenegraph/util/qsgareaallocator_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef AREAALLOCATOR_H
-#define AREAALLOCATOR_H
+#ifndef QSGAREAALLOCATOR_P_H
+#define QSGAREAALLOCATOR_P_H
#include <private/qtquickglobal_p.h>
#include <QtCore/qsize.h>
diff --git a/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h b/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h
index 479c8e2d1f..f3fb92cbc5 100644
--- a/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h
+++ b/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h
@@ -48,8 +48,6 @@
#include <QtCore/qsharedpointer.h>
#include <QtCore/qhash.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGDepthStencilBufferManager;
@@ -137,6 +135,4 @@ extern uint qHash(const QSGDepthStencilBuffer::Format &format);
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.h b/src/quick/scenegraph/util/qsgflatcolormaterial.h
index b24e5e7205..12a37f2ae2 100644
--- a/src/quick/scenegraph/util/qsgflatcolormaterial.h
+++ b/src/quick/scenegraph/util/qsgflatcolormaterial.h
@@ -39,14 +39,12 @@
**
****************************************************************************/
-#ifndef FLATCOLORMATERIAL_H
-#define FLATCOLORMATERIAL_H
+#ifndef QSGFLATCOLORMATERIAL_H
+#define QSGFLATCOLORMATERIAL_H
#include <QtQuick/qsgmaterial.h>
#include <QtGui/qcolor.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QSGFlatColorMaterial : public QSGMaterial
@@ -67,6 +65,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // FLATCOLORMATERIAL_H
diff --git a/src/quick/scenegraph/util/qsgpainternode.cpp b/src/quick/scenegraph/util/qsgpainternode.cpp
index df226455e9..e5cf6b8295 100644
--- a/src/quick/scenegraph/util/qsgpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgpainternode.cpp
@@ -70,7 +70,7 @@ static inline int qt_next_power_of_two(int v)
QSGPainterTexture::QSGPainterTexture()
: QSGPlainTexture()
{
-
+ m_retain_image = true;
}
#ifdef QT_OPENGL_ES
diff --git a/src/quick/scenegraph/util/qsgpainternode_p.h b/src/quick/scenegraph/util/qsgpainternode_p.h
index 8c799810e4..387ca9bf88 100644
--- a/src/quick/scenegraph/util/qsgpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgpainternode_p.h
@@ -50,8 +50,6 @@
#include <QtGui/qcolor.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
@@ -151,8 +149,6 @@ private:
bool m_dirtyTexture;
};
-QT_END_HEADER
-
QT_END_NAMESPACE
#endif // QSGPAINTERNODE_P_H
diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp
index bed1b710ca..ee7a272fbe 100644
--- a/src/quick/scenegraph/util/qsgsimplematerial.cpp
+++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp
@@ -142,7 +142,7 @@
the unique QSGSimpleMaterialShader implementation must be
instantiated with a unique C++ type.
- \sa {Simple Material Example}
+ \sa {Scene Graph - Simple Material}
*/
/*!
diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h
index 877ea1c4b5..a1e5aa23be 100644
--- a/src/quick/scenegraph/util/qsgsimplematerial.h
+++ b/src/quick/scenegraph/util/qsgsimplematerial.h
@@ -44,8 +44,6 @@
#include <QtQuick/qsgmaterial.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
template <typename State>
@@ -215,7 +213,5 @@ Q_INLINE_TEMPLATE void QSGSimpleMaterialShader<State>::updateState(const RenderS
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.h b/src/quick/scenegraph/util/qsgsimplerectnode.h
index 2808354089..bed2d98078 100644
--- a/src/quick/scenegraph/util/qsgsimplerectnode.h
+++ b/src/quick/scenegraph/util/qsgsimplerectnode.h
@@ -39,14 +39,12 @@
**
****************************************************************************/
-#ifndef SOLIDRECTNODE_H
-#define SOLIDRECTNODE_H
+#ifndef QSGSIMPLERECTNODE_H
+#define QSGSIMPLERECTNODE_H
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgflatcolormaterial.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QSGSimpleRectNode : public QSGGeometryNode
@@ -70,6 +68,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // SOLIDRECTNODE_H
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.h b/src/quick/scenegraph/util/qsgsimpletexturenode.h
index a2ce80802a..ffd10210ae 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.h
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.h
@@ -46,8 +46,6 @@
#include <QtQuick/qsggeometry.h>
#include <QtQuick/qsgtexturematerial.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QSGSimpleTextureNode : public QSGGeometryNode
@@ -75,6 +73,4 @@ private:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGSIMPLETEXTURENODE_H
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index 16cc46113d..cd95d9f445 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/util/qsgtexture.cpp
@@ -48,7 +48,7 @@
#include <private/qqmlprofilerservice_p.h>
#include <private/qqmlglobal_p.h>
-#if defined(Q_OS_LINUX) && !defined(Q_OS_LINUX_ANDROID)
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
#define CAN_BACKTRACE_EXECINFO
#endif
@@ -65,6 +65,20 @@
#include <QHash>
#endif
+#ifndef QT_NO_DEBUG
+static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty();
+#endif
+
+#ifndef QSG_NO_RENDERER_TIMING
+static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty();
+static QElapsedTimer qsg_renderer_timer;
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+
QT_BEGIN_NAMESPACE
inline static bool isPowerOfTwo(int x)
@@ -172,7 +186,7 @@ static void qt_debug_remove_texture(QSGTexture* texture)
--qt_debug_texture_count;
if (qt_debug_texture_count < 0)
- qDebug("Material destroyed after qt_debug_print_texture_count() was called.");
+ qDebug("Texture destroyed after qt_debug_print_texture_count() was called.");
}
#endif // QT_NO_DEBUG
@@ -218,6 +232,8 @@ static void qt_debug_remove_texture(QSGTexture* texture)
If the texture is used in such a way that atlas is not preferable,
the function removedFromAtlas() can be used to extract a
non-atlassed copy.
+
+ \sa {Scene Graph - Rendering FBOs}, {Scene Graph - Rendering FBOs in a thread}
*/
/*!
@@ -259,7 +275,8 @@ QSGTexture::QSGTexture()
: QObject(*(new QSGTexturePrivate))
{
#ifndef QT_NO_DEBUG
- qt_debug_add_texture(this);
+ if (qsg_leak_check)
+ qt_debug_add_texture(this);
#endif
}
@@ -269,7 +286,8 @@ QSGTexture::QSGTexture()
QSGTexture::~QSGTexture()
{
#ifndef QT_NO_DEBUG
- qt_debug_remove_texture(this);
+ if (qsg_leak_check)
+ qt_debug_remove_texture(this);
#endif
}
@@ -513,6 +531,7 @@ QSGPlainTexture::QSGPlainTexture()
, m_dirty_bind_options(false)
, m_owns_texture(true)
, m_mipmaps_generated(false)
+ , m_retain_image(false)
{
}
@@ -523,7 +542,6 @@ QSGPlainTexture::~QSGPlainTexture()
glDeleteTextures(1, &m_texture_id);
}
-#ifdef QT_OPENGL_ES
void qsg_swizzleBGRAToRGBA(QImage *image)
{
const int width = image->width();
@@ -534,7 +552,6 @@ void qsg_swizzleBGRAToRGBA(QImage *image)
p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
}
}
-#endif
void QSGPlainTexture::setImage(const QImage &image)
{
@@ -596,14 +613,30 @@ void QSGPlainTexture::bind()
m_dirty_texture = false;
+#ifndef QSG_NO_RENDERER_TIMING
+ if (qsg_render_timing)
+ qsg_renderer_timer.start();
+#endif
if (m_image.isNull()) {
- if (m_texture_id && m_owns_texture)
+ if (m_texture_id && m_owns_texture) {
glDeleteTextures(1, &m_texture_id);
+#ifndef QSG_NO_RENDERER_TIMING
+ if (qsg_render_timing) {
+ printf(" - texture deleted in %dms (size: %dx%d)\n",
+ (int) qsg_renderer_timer.elapsed(),
+ m_texture_size.width(),
+ m_texture_size.height());
+ }
+#endif
+ }
m_texture_id = 0;
m_texture_size = QSize();
m_has_mipmaps = false;
m_has_alpha = false;
+
+
+
return;
}
@@ -611,6 +644,12 @@ void QSGPlainTexture::bind()
glGenTextures(1, &m_texture_id);
glBindTexture(GL_TEXTURE_2D, m_texture_id);
+#ifndef QSG_NO_RENDERER_TIMING
+ int bindTime = 0;
+ if (qsg_render_timing)
+ bindTime = qsg_renderer_timer.elapsed();
+#endif
+
// ### TODO: check for out-of-memory situations...
int w = m_image.width();
int h = m_image.height();
@@ -619,14 +658,46 @@ void QSGPlainTexture::bind()
? m_image
: m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+#ifndef QSG_NO_RENDERER_TIMING
+ int convertTime = 0;
+ if (qsg_render_timing)
+ convertTime = qsg_renderer_timer.elapsed();
+#endif
+
updateBindOptions(m_dirty_bind_options);
+ GLenum externalFormat = GL_RGBA;
+ GLenum internalFormat = GL_RGBA;
+
+ const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
+ if (strstr(extensions, "GL_EXT_bgra")) {
+ externalFormat = GL_BGRA;
#ifdef QT_OPENGL_ES
+ internalFormat = GL_BGRA;
+#endif
+ } else if (strstr(extensions, "GL_APPLE_texture_format_BGRA8888")) {
+ externalFormat = GL_BGRA;
+ } else if (strstr(extensions, "GL_EXT_texture_format_BGRA8888")
+ || strstr(extensions, "GL_IMG_texture_format_BGRA8888")) {
+ externalFormat = GL_BGRA;
+ internalFormat = GL_BGRA;
+ } else {
qsg_swizzleBGRAToRGBA(&tmp);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits());
-#else
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits());
+ }
+
+#ifndef QSG_NO_RENDERER_TIMING
+ int swizzleTime = 0;
+ if (qsg_render_timing)
+ swizzleTime = qsg_renderer_timer.elapsed();
#endif
+ glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits());
+
+#ifndef QSG_NO_RENDERER_TIMING
+ int uploadTime = 0;
+ if (qsg_render_timing)
+ uploadTime = qsg_renderer_timer.elapsed();
+#endif
+
if (m_has_mipmaps) {
QOpenGLContext *ctx = QOpenGLContext::currentContext();
@@ -634,10 +705,33 @@ void QSGPlainTexture::bind()
m_mipmaps_generated = true;
}
+#ifndef QSG_NO_RENDERER_TIMING
+ int mipmapTime = 0;
+ if (qsg_render_timing) {
+ mipmapTime = qsg_renderer_timer.elapsed();
+
+ printf(" - plaintexture(%dx%d) bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d, total=%d\n",
+ m_texture_size.width(), m_texture_size.height(),
+ bindTime,
+ convertTime - bindTime,
+ swizzleTime - convertTime,
+ externalFormat == GL_BGRA ? "BGRA" : "RGBA",
+ internalFormat == GL_BGRA ? "BGRA" : "RGBA",
+ uploadTime - swizzleTime,
+ mipmapTime - uploadTime,
+ (int) qsg_renderer_timer.elapsed());
+
+ }
+
+#endif
+
+
m_texture_size = QSize(w, h);
m_texture_rect = QRectF(0, 0, 1, 1);
m_dirty_bind_options = false;
+ if (!m_retain_image)
+ m_image = QImage();
}
diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h
index 4dcea63175..299ffc27e8 100644
--- a/src/quick/scenegraph/util/qsgtexture.h
+++ b/src/quick/scenegraph/util/qsgtexture.h
@@ -46,8 +46,6 @@
#include <QtCore/QObject>
#include <QtGui/QImage>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class QSGTexturePrivate;
@@ -127,6 +125,4 @@ public:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h
index ed1d782c35..6430a93ed8 100644
--- a/src/quick/scenegraph/util/qsgtexture_p.h
+++ b/src/quick/scenegraph/util/qsgtexture_p.h
@@ -112,6 +112,7 @@ protected:
uint m_dirty_bind_options : 1;
uint m_owns_texture : 1;
uint m_mipmaps_generated : 1;
+ uint m_retain_image: 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.h b/src/quick/scenegraph/util/qsgtexturematerial.h
index 822084b463..b842779716 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.h
+++ b/src/quick/scenegraph/util/qsgtexturematerial.h
@@ -39,14 +39,12 @@
**
****************************************************************************/
-#ifndef TEXTUREMATERIAL_H
-#define TEXTUREMATERIAL_H
+#ifndef QSGTEXTUREMATERIAL_H
+#define QSGTEXTUREMATERIAL_H
#include <QtQuick/qsgmaterial.h>
#include <QtQuick/qsgtexture.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QSGOpaqueTextureMaterial : public QSGMaterial
@@ -94,6 +92,4 @@ public:
QT_END_NAMESPACE
-QT_END_HEADER
-
-#endif // TEXTUREMATERIAL_H
+#endif
diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h
index 346004be35..a07cb7925e 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial_p.h
+++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h
@@ -45,8 +45,6 @@
#include "qsgtexturematerial.h"
#include <private/qtquickglobal_p.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QSGOpaqueTextureMaterialShader : public QSGMaterialShader
@@ -81,6 +79,4 @@ protected:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // QSGTEXTUREMATERIAL_P_H
diff --git a/src/quick/scenegraph/util/qsgtextureprovider.h b/src/quick/scenegraph/util/qsgtextureprovider.h
index f4925612f9..608b3c6e0f 100644
--- a/src/quick/scenegraph/util/qsgtextureprovider.h
+++ b/src/quick/scenegraph/util/qsgtextureprovider.h
@@ -45,8 +45,6 @@
#include <QtQuick/qsgtexture.h>
#include <QtCore/qobject.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QSGTextureProvider : public QObject
@@ -61,6 +59,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif
diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.h b/src/quick/scenegraph/util/qsgvertexcolormaterial.h
index 7e1eb43193..81da0650f1 100644
--- a/src/quick/scenegraph/util/qsgvertexcolormaterial.h
+++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.h
@@ -39,13 +39,11 @@
**
****************************************************************************/
-#ifndef VERTEXCOLORMATERIAL_H
-#define VERTEXCOLORMATERIAL_H
+#ifndef QSGVERTEXCOLORMATERIAL_H
+#define QSGVERTEXCOLORMATERIAL_H
#include <QtQuick/qsgmaterial.h>
-QT_BEGIN_HEADER
-
QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QSGVertexColorMaterial : public QSGMaterial
@@ -62,6 +60,4 @@ protected:
QT_END_NAMESPACE
-QT_END_HEADER
-
#endif // VERTEXCOLORMATERIAL_H