diff options
Diffstat (limited to 'src/quick/scenegraph')
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 |