aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/adaptations/software
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/adaptations/software')
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp17
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp13
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp68
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h12
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp (renamed from src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp)42
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h (renamed from src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h)10
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp (renamed from src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp)34
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h (renamed from src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h)10
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp5
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp (renamed from src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp)91
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h145
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp132
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h27
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp70
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h18
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp49
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp37
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h14
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp6
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp139
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h (renamed from src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h)42
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp991
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h118
-rw-r--r--src/quick/scenegraph/adaptations/software/software.pri20
26 files changed, 1933 insertions, 185 deletions
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index eb0e26462a..2ff180ea99 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -67,9 +67,7 @@ QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer()
// Cleanup RenderableNodes
delete m_background;
- for (QSGSoftwareRenderableNode *node : m_nodes.values()) {
- delete node;
- }
+ qDeleteAll(m_nodes);
delete m_nodeUpdater;
}
@@ -149,7 +147,7 @@ void QSGAbstractSoftwareRenderer::buildRenderList()
QSGSoftwareRenderListBuilder(this).visitChildren(rootNode());
}
-void QSGAbstractSoftwareRenderer::optimizeRenderList()
+QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
{
// Iterate through the renderlist from front to back
// Objective is to update the dirty status and rects.
@@ -212,9 +210,13 @@ void QSGAbstractSoftwareRenderer::optimizeRenderList()
m_dirtyRegion += node->dirtyRegion();
}
+ QRegion updateRegion = m_dirtyRegion;
+
// Empty dirtyRegion
m_dirtyRegion = QRegion();
m_obscuredRegion = QRegion();
+
+ return updateRegion;
}
void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
@@ -232,7 +234,7 @@ void QSGAbstractSoftwareRenderer::setBackgroundSize(const QSize &size)
m_background->setRect(0.0f, 0.0f, size.width(), size.height());
renderableNode(m_background)->markGeometryDirty();
// Invalidate the whole scene when the background is resized
- m_dirtyRegion = QRegion(m_background->rect().toRect());
+ markDirty();
}
QColor QSGAbstractSoftwareRenderer::backgroundColor()
@@ -318,4 +320,9 @@ void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node)
m_nodeUpdater->updateNodes(node);
}
+void QSGAbstractSoftwareRenderer::markDirty()
+{
+ m_dirtyRegion = QRegion(m_background->rect().toRect());
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
index a2e953f40d..905577b92a 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -75,10 +75,12 @@ public:
void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+ void markDirty();
+
protected:
QRegion renderNodes(QPainter *painter);
void buildRenderList();
- void optimizeRenderList();
+ QRegion optimizeRenderList();
void setBackgroundColor(const QColor &color);
void setBackgroundSize(const QSize &size);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
index 0e2f4f5382..92c02b4966 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
@@ -40,6 +40,7 @@
#include "qsgsoftwareadaptation_p.h"
#include "qsgsoftwarecontext_p.h"
#include "qsgsoftwarerenderloop_p.h"
+#include "qsgsoftwarethreadedrenderloop_p.h"
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -53,7 +54,7 @@ QSGSoftwareAdaptation::QSGSoftwareAdaptation(QObject *parent)
QStringList QSGSoftwareAdaptation::keys() const
{
- return QStringList() << QLatin1String("software");
+ return QStringList() << QLatin1String("software") << QLatin1String("softwarecontext");
}
QSGContext *QSGSoftwareAdaptation::create(const QString &) const
@@ -73,6 +74,16 @@ QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &)
QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager()
{
+ static bool threaded = false;
+ static bool envChecked = false;
+ if (!envChecked) {
+ envChecked = true;
+ threaded = qgetenv("QSG_RENDER_LOOP") == "threaded";
+ }
+
+ if (threaded)
+ return new QSGSoftwareThreadedRenderLoop;
+
return new QSGSoftwareRenderLoop();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index ce726e342b..80112c1121 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -39,28 +39,21 @@
#include "qsgsoftwarecontext_p.h"
-#include "qsgsoftwarerectanglenode_p.h"
-#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
#include "qsgsoftwarepainternode_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
#include "qsgsoftwareglyphnode_p.h"
-#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
#include "qsgsoftwarelayer_p.h"
#include "qsgsoftwarerenderer_p.h"
+#include "qsgsoftwarespritenode_p.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QElapsedTimer>
#include <QtGui/QWindow>
-
-#include <QtQuick/QSGFlatColorMaterial>
-#include <QtQuick/QSGVertexColorMaterial>
-#include <QtQuick/QSGOpaqueTextureMaterial>
-#include <QtQuick/QSGTextureMaterial>
-
-#ifndef QSG_NO_RENDERER_TIMING
-static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty();
-#endif
+#include <QtQuick/private/qquickwindow_p.h>
// Used for very high-level info about the renderering and gl context
// Includes GL_VERSION, type of render loop, atlas size, etc.
@@ -90,21 +83,23 @@ QT_BEGIN_NAMESPACE
QSGSoftwareRenderContext::QSGSoftwareRenderContext(QSGContext *ctx)
: QSGRenderContext(ctx)
, m_initialized(false)
+ , m_activePainter(nullptr)
{
}
+
QSGSoftwareContext::QSGSoftwareContext(QObject *parent)
: QSGContext(parent)
{
}
-QSGRectangleNode *QSGSoftwareContext::createRectangleNode()
+QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode()
{
- return new QSGSoftwareRectangleNode();
+ return new QSGSoftwareInternalRectangleNode();
}
-QSGImageNode *QSGSoftwareContext::createImageNode()
+QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode()
{
- return new QSGSoftwareImageNode();
+ return new QSGSoftwareInternalImageNode();
}
QSGPainterNode *QSGSoftwareContext::createPainterNode(QQuickPaintedItem *item)
@@ -119,11 +114,6 @@ QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, bool pre
return new QSGSoftwareGlyphNode();
}
-QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode()
-{
- return new QSGSoftwareNinePatchNode();
-}
-
QSGLayer *QSGSoftwareContext::createLayer(QSGRenderContext *renderContext)
{
return new QSGSoftwareLayer(renderContext);
@@ -148,7 +138,8 @@ void QSGSoftwareRenderContext::initializeIfNeeded()
void QSGSoftwareRenderContext::invalidate()
{
- QSGRenderContext::invalidate();
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
}
QSGTexture *QSGSoftwareRenderContext::createTexture(const QImage &image, uint flags) const
@@ -167,12 +158,37 @@ void QSGSoftwareRenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo)
renderer->renderScene(fbo);
}
+int QSGSoftwareRenderContext::maxTextureSize() const
+{
+ return 2048;
+}
+
QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *renderContext)
{
Q_UNUSED(renderContext);
return this;
}
+QSGRectangleNode *QSGSoftwareContext::createRectangleNode()
+{
+ return new QSGSoftwareRectangleNode;
+}
+
+QSGImageNode *QSGSoftwareContext::createImageNode()
+{
+ return new QSGSoftwareImageNode;
+}
+
+QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode()
+{
+ return new QSGSoftwareNinePatchNode;
+}
+
+QSGSpriteNode *QSGSoftwareContext::createSpriteNode()
+{
+ return new QSGSoftwareSpriteNode;
+}
+
QSGRendererInterface::GraphicsApi QSGSoftwareContext::graphicsApi() const
{
return Software;
@@ -193,4 +209,12 @@ QSGRendererInterface::ShaderSourceTypes QSGSoftwareContext::shaderSourceType() c
return 0;
}
+void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) const
+{
+ if (resource == Painter && window && window->isSceneGraphInitialized())
+ return static_cast<QSGSoftwareRenderContext *>(QQuickWindowPrivate::get(window)->context)->m_activePainter;
+
+ return nullptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
index 992f6f5677..dcc137b4b4 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -75,8 +75,10 @@ public:
void renderNextFrame(QSGRenderer *renderer, uint fbo) override;
QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const override;
QSGRenderer *createRenderer() override;
+ int maxTextureSize() const override;
bool m_initialized;
+ QPainter *m_activePainter;
};
class QSGSoftwareContext : public QSGContext, public QSGRendererInterface
@@ -86,19 +88,23 @@ public:
explicit QSGSoftwareContext(QObject *parent = nullptr);
QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); }
- QSGRectangleNode *createRectangleNode() override;
- QSGImageNode *createImageNode() override;
+ QSGInternalRectangleNode *createInternalRectangleNode() override;
+ QSGInternalImageNode *createInternalImageNode() override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
- QSGNinePatchNode *createNinePatchNode() override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
QSurfaceFormat defaultSurfaceFormat() const override;
QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGNinePatchNode *createNinePatchNode() override;
+ QSGSpriteNode *createSpriteNode() override;
GraphicsApi graphicsApi() const override;
ShaderType shaderType() const override;
ShaderCompilationTypes shaderCompilationType() const override;
ShaderSourceTypes shaderSourceType() const override;
+ void *getResource(QQuickWindow *window, Resource resource) const override;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp
index 7dadc1d3d4..10291b9cb5 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
#include "qsgsoftwarelayer_p.h"
@@ -315,7 +315,7 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin
} // QSGSoftwareHelpers namespace
-QSGSoftwareImageNode::QSGSoftwareImageNode()
+QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode()
: m_innerSourceRect(0, 0, 1, 1)
, m_subSourceRect(0, 0, 1, 1)
, m_texture(0)
@@ -330,7 +330,7 @@ QSGSoftwareImageNode::QSGSoftwareImageNode()
}
-void QSGSoftwareImageNode::setTargetRect(const QRectF &rect)
+void QSGSoftwareInternalImageNode::setTargetRect(const QRectF &rect)
{
if (rect == m_targetRect)
return;
@@ -338,7 +338,7 @@ void QSGSoftwareImageNode::setTargetRect(const QRectF &rect)
markDirty(DirtyGeometry);
}
-void QSGSoftwareImageNode::setInnerTargetRect(const QRectF &rect)
+void QSGSoftwareInternalImageNode::setInnerTargetRect(const QRectF &rect)
{
if (rect == m_innerTargetRect)
return;
@@ -346,7 +346,7 @@ void QSGSoftwareImageNode::setInnerTargetRect(const QRectF &rect)
markDirty(DirtyGeometry);
}
-void QSGSoftwareImageNode::setInnerSourceRect(const QRectF &rect)
+void QSGSoftwareInternalImageNode::setInnerSourceRect(const QRectF &rect)
{
if (rect == m_innerSourceRect)
return;
@@ -354,7 +354,7 @@ void QSGSoftwareImageNode::setInnerSourceRect(const QRectF &rect)
markDirty(DirtyGeometry);
}
-void QSGSoftwareImageNode::setSubSourceRect(const QRectF &rect)
+void QSGSoftwareInternalImageNode::setSubSourceRect(const QRectF &rect)
{
if (rect == m_subSourceRect)
return;
@@ -362,16 +362,14 @@ void QSGSoftwareImageNode::setSubSourceRect(const QRectF &rect)
markDirty(DirtyGeometry);
}
-void QSGSoftwareImageNode::setTexture(QSGTexture *texture)
+void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture)
{
- if (m_texture != texture) {
- m_texture = texture;
- m_cachedMirroredPixmapIsDirty = true;
- markDirty(DirtyMaterial);
- }
+ m_texture = texture;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
}
-void QSGSoftwareImageNode::setMirror(bool mirror)
+void QSGSoftwareInternalImageNode::setMirror(bool mirror)
{
if (m_mirror != mirror) {
m_mirror = mirror;
@@ -380,11 +378,11 @@ void QSGSoftwareImageNode::setMirror(bool mirror)
}
}
-void QSGSoftwareImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/)
+void QSGSoftwareInternalImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/)
{
}
-void QSGSoftwareImageNode::setFiltering(QSGTexture::Filtering filtering)
+void QSGSoftwareInternalImageNode::setFiltering(QSGTexture::Filtering filtering)
{
bool smooth = (filtering == QSGTexture::Linear);
if (smooth == m_smooth)
@@ -394,7 +392,7 @@ void QSGSoftwareImageNode::setFiltering(QSGTexture::Filtering filtering)
markDirty(DirtyMaterial);
}
-void QSGSoftwareImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+void QSGSoftwareInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
{
bool tileHorizontal = (wrapMode == QSGTexture::Repeat);
if (tileHorizontal == m_tileHorizontal)
@@ -404,7 +402,7 @@ void QSGSoftwareImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
markDirty(DirtyMaterial);
}
-void QSGSoftwareImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
{
bool tileVertical = (wrapMode == QSGTexture::Repeat);
if (tileVertical == m_tileVertical)
@@ -414,7 +412,7 @@ void QSGSoftwareImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
markDirty(DirtyMaterial);
}
-void QSGSoftwareImageNode::update()
+void QSGSoftwareInternalImageNode::update()
{
if (m_cachedMirroredPixmapIsDirty) {
if (m_mirror) {
@@ -428,7 +426,7 @@ void QSGSoftwareImageNode::update()
}
}
-void QSGSoftwareImageNode::preprocess()
+void QSGSoftwareInternalImageNode::preprocess()
{
bool doDirty = false;
QSGLayer *t = qobject_cast<QSGLayer *>(m_texture);
@@ -452,7 +450,7 @@ static Qt::TileRule getTileRule(qreal factor)
}
-void QSGSoftwareImageNode::paint(QPainter *painter)
+void QSGSoftwareInternalImageNode::paint(QPainter *painter)
{
painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth);
@@ -485,12 +483,12 @@ void QSGSoftwareImageNode::paint(QPainter *painter)
}
}
-QRectF QSGSoftwareImageNode::rect() const
+QRectF QSGSoftwareInternalImageNode::rect() const
{
return m_targetRect;
}
-const QPixmap &QSGSoftwareImageNode::pixmap() const
+const QPixmap &QSGSoftwareInternalImageNode::pixmap() const
{
if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(m_texture)) {
return pt->pixmap();
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
index e42f616757..f21667fdf7 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGSOFTWAREIMAGENODE_H
-#define QSGSOFTWAREIMAGENODE_H
+#ifndef QSGSOFTWAREINTERNALIMAGENODE_H
+#define QSGSOFTWAREINTERNALIMAGENODE_H
//
// W A R N I N G
@@ -101,10 +101,10 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin
} // QSGSoftwareHelpers namespace
-class QSGSoftwareImageNode : public QSGImageNode
+class QSGSoftwareInternalImageNode : public QSGInternalImageNode
{
public:
- QSGSoftwareImageNode();
+ QSGSoftwareInternalImageNode();
void setTargetRect(const QRectF &rect) override;
void setInnerTargetRect(const QRectF &rect) override;
@@ -144,4 +144,4 @@ private:
QT_END_NAMESPACE
-#endif // QSGSOFTWAREIMAGENODE_H
+#endif // QSGSOFTWAREINTERNALIMAGENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
index 7672b14371..f6898b3879 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
@@ -37,14 +37,14 @@
**
****************************************************************************/
-#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
#include <qmath.h>
#include <QtGui/QPainter>
QT_BEGIN_NAMESPACE
-QSGSoftwareRectangleNode::QSGSoftwareRectangleNode()
+QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode()
: m_penWidth(0)
, m_radius(0)
, m_cornerPixmapIsDirty(true)
@@ -56,7 +56,7 @@ QSGSoftwareRectangleNode::QSGSoftwareRectangleNode()
setGeometry((QSGGeometry*)1);
}
-void QSGSoftwareRectangleNode::setRect(const QRectF &rect)
+void QSGSoftwareInternalRectangleNode::setRect(const QRectF &rect)
{
QRect alignedRect = rect.toAlignedRect();
if (m_rect != alignedRect) {
@@ -65,7 +65,7 @@ void QSGSoftwareRectangleNode::setRect(const QRectF &rect)
}
}
-void QSGSoftwareRectangleNode::setColor(const QColor &color)
+void QSGSoftwareInternalRectangleNode::setColor(const QColor &color)
{
if (m_color != color) {
m_color = color;
@@ -74,7 +74,7 @@ void QSGSoftwareRectangleNode::setColor(const QColor &color)
}
}
-void QSGSoftwareRectangleNode::setPenColor(const QColor &color)
+void QSGSoftwareInternalRectangleNode::setPenColor(const QColor &color)
{
if (m_penColor != color) {
m_penColor = color;
@@ -83,7 +83,7 @@ void QSGSoftwareRectangleNode::setPenColor(const QColor &color)
}
}
-void QSGSoftwareRectangleNode::setPenWidth(qreal width)
+void QSGSoftwareInternalRectangleNode::setPenWidth(qreal width)
{
if (m_penWidth != width) {
m_penWidth = width;
@@ -113,11 +113,11 @@ static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGrad
return newStop;
}
-void QSGSoftwareRectangleNode::setGradientStops(const QGradientStops &stops)
+void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &stops)
{
//normalize stops
bool needsNormalization = false;
- foreach (const QGradientStop &stop, stops) {
+ for (const QGradientStop &stop : qAsConst(stops)) {
if (stop.first < 0.0 || stop.first > 1.0) {
needsNormalization = true;
continue;
@@ -186,7 +186,7 @@ void QSGSoftwareRectangleNode::setGradientStops(const QGradientStops &stops)
markDirty(DirtyMaterial);
}
-void QSGSoftwareRectangleNode::setRadius(qreal radius)
+void QSGSoftwareInternalRectangleNode::setRadius(qreal radius)
{
if (m_radius != radius) {
m_radius = radius;
@@ -195,11 +195,11 @@ void QSGSoftwareRectangleNode::setRadius(qreal radius)
}
}
-void QSGSoftwareRectangleNode::setAligned(bool /*aligned*/)
+void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/)
{
}
-void QSGSoftwareRectangleNode::update()
+void QSGSoftwareInternalRectangleNode::update()
{
if (!m_penWidth || m_penColor == Qt::transparent) {
m_pen = Qt::NoPen;
@@ -223,7 +223,7 @@ void QSGSoftwareRectangleNode::update()
}
}
-void QSGSoftwareRectangleNode::paint(QPainter *painter)
+void QSGSoftwareInternalRectangleNode::paint(QPainter *painter)
{
//We can only check for a device pixel ratio change when we know what
//paint device is being used.
@@ -265,7 +265,7 @@ void QSGSoftwareRectangleNode::paint(QPainter *painter)
}
-bool QSGSoftwareRectangleNode::isOpaque() const
+bool QSGSoftwareInternalRectangleNode::isOpaque() const
{
if (m_radius > 0.0f)
return false;
@@ -274,7 +274,7 @@ bool QSGSoftwareRectangleNode::isOpaque() const
if (m_penWidth > 0.0f && m_penColor.alpha() < 255)
return false;
if (m_stops.count() > 0) {
- foreach (QGradientStop stop, m_stops) {
+ for (const QGradientStop &stop : qAsConst(m_stops)) {
if (stop.second.alpha() < 255)
return false;
}
@@ -283,13 +283,13 @@ bool QSGSoftwareRectangleNode::isOpaque() const
return true;
}
-QRectF QSGSoftwareRectangleNode::rect() const
+QRectF QSGSoftwareInternalRectangleNode::rect() const
{
//TODO: double check that this is correct.
return m_rect;
}
-void QSGSoftwareRectangleNode::paintRectangle(QPainter *painter, const QRect &rect)
+void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const QRect &rect)
{
//Radius should never exceeds half of the width or half of the height
int radius = qFloor(qMin(qMin(rect.width(), rect.height()) * 0.5, m_radius));
@@ -411,7 +411,7 @@ void QSGSoftwareRectangleNode::paintRectangle(QPainter *painter, const QRect &re
painter->setRenderHints(previousRenderHints);
}
-void QSGSoftwareRectangleNode::generateCornerPixmap()
+void QSGSoftwareInternalRectangleNode::generateCornerPixmap()
{
//Generate new corner Pixmap
int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius));
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
index 9cc0823325..f363e279e1 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGSOFTWARERECTANGLENODE_H
-#define QSGSOFTWARERECTANGLENODE_H
+#ifndef QSGSOFTWAREINTERNALRECTANGLENODE_H
+#define QSGSOFTWAREINTERNALRECTANGLENODE_H
//
// W A R N I N G
@@ -59,10 +59,10 @@
QT_BEGIN_NAMESPACE
-class QSGSoftwareRectangleNode : public QSGRectangleNode
+class QSGSoftwareInternalRectangleNode : public QSGInternalRectangleNode
{
public:
- QSGSoftwareRectangleNode();
+ QSGSoftwareInternalRectangleNode();
void setRect(const QRectF &rect) override;
void setColor(const QColor &color) override;
@@ -100,4 +100,4 @@ private:
QT_END_NAMESPACE
-#endif // QSGSOFTWARERECTANGLENODE_H
+#endif // QSGSOFTWAREINTERNALRECTANGLENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
index 304106a84d..f8c1a3d90b 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qsgsoftwarepixmaprenderer_p.h"
+#include "qsgsoftwarecontext_p.h"
#include <QtQuick/QSGSimpleRectNode>
@@ -84,6 +85,9 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target)
QPainter painter(target);
painter.setRenderHint(QPainter::Antialiasing);
painter.setWindow(m_projectionRect);
+ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
+ QPainter *prevPainter = rc->m_activePainter;
+ rc->m_activePainter = &painter;
renderTimer.start();
buildRenderList();
@@ -100,6 +104,7 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target)
QRegion paintedRegion = renderNodes(&painter);
qint64 renderTime = renderTimer.elapsed();
+ rc->m_activePainter = prevPainter;
qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
index 36ff1f2229..1fa5234377 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -37,12 +37,99 @@
**
****************************************************************************/
-#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
-#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
QT_BEGIN_NAMESPACE
+QSGSoftwareRectangleNode::QSGSoftwareRectangleNode()
+ : m_color(QColor(255, 255, 255))
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareRectangleNode::paint(QPainter *painter)
+{
+ painter->fillRect(m_rect, m_color);
+}
+
+QSGSoftwareImageNode::QSGSoftwareImageNode()
+ : m_texture(nullptr),
+ m_owns(false),
+ m_filtering(QSGTexture::None),
+ m_transformMode(NoTransform),
+ m_cachedMirroredPixmapIsDirty(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+QSGSoftwareImageNode::~QSGSoftwareImageNode()
+{
+ if (m_owns)
+ delete m_texture;
+}
+
+void QSGSoftwareImageNode::setTexture(QSGTexture *texture)
+{
+ m_texture = texture; markDirty(DirtyMaterial);
+ m_cachedMirroredPixmapIsDirty = true;
+}
+
+void QSGSoftwareImageNode::setTextureCoordinatesTransform(QSGImageNode::TextureCoordinatesTransformMode transformNode)
+{
+ if (m_transformMode == transformNode)
+ return;
+
+ m_transformMode = transformNode;
+ m_cachedMirroredPixmapIsDirty = true;
+
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::paint(QPainter *painter)
+{
+ if (m_cachedMirroredPixmapIsDirty)
+ updateCachedMirroredPixmap();
+
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear));
+
+ if (!m_cachedPixmap.isNull()) {
+ painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect);
+ } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_rect, pm, m_sourceRect);
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ const QImage &im = pt->image();
+ painter->drawImage(m_rect, im, m_sourceRect);
+ }
+}
+
+void QSGSoftwareImageNode::updateCachedMirroredPixmap()
+{
+ if (m_transformMode == NoTransform) {
+ m_cachedPixmap = QPixmap();
+ } else {
+
+ if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ QTransform mirrorTransform;
+ if (m_transformMode.testFlag(MirrorVertically))
+ mirrorTransform = mirrorTransform.scale(1, -1);
+ if (m_transformMode.testFlag(MirrorHorizontally))
+ mirrorTransform = mirrorTransform.scale(-1, 1);
+ m_cachedPixmap = pt->pixmap().transformed(mirrorTransform);
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically)));
+ } else {
+ m_cachedPixmap = QPixmap();
+ }
+ }
+
+ m_cachedMirroredPixmapIsDirty = false;
+}
+
QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode()
{
setMaterial((QSGMaterial*)1);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h
new file mode 100644
index 0000000000..9f1913205b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPUBLICNODES_H
+#define QSGSOFTWAREPUBLICNODES_H
+
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
+#include <QtGui/qpixmap.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGSoftwareRectangleNode();
+
+ void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); }
+ QRectF rect() const override { return m_rect; }
+
+ void setColor(const QColor &color) override { m_color = color; markDirty(DirtyMaterial); }
+ QColor color() const override { return m_color; }
+
+ void paint(QPainter *painter);
+
+private:
+ QRectF m_rect;
+ QColor m_color;
+};
+
+class QSGSoftwareImageNode : public QSGImageNode
+{
+public:
+ QSGSoftwareImageNode();
+ ~QSGSoftwareImageNode();
+
+ void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); }
+ QRectF rect() const override { return m_rect; }
+
+ void setSourceRect(const QRectF &r) override { m_sourceRect = r; }
+ QRectF sourceRect() const override { return m_sourceRect; }
+
+ void setTexture(QSGTexture *texture) override;
+ QSGTexture *texture() const override { return m_texture; }
+
+ void setFiltering(QSGTexture::Filtering filtering) override { m_filtering = filtering; markDirty(DirtyMaterial); }
+ QSGTexture::Filtering filtering() const override { return m_filtering; }
+
+ void setMipmapFiltering(QSGTexture::Filtering) override { }
+ QSGTexture::Filtering mipmapFiltering() const override { return QSGTexture::None; }
+
+ void setTextureCoordinatesTransform(TextureCoordinatesTransformMode transformNode) override;
+ TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return m_transformMode; }
+
+ void setOwnsTexture(bool owns) override { m_owns = owns; }
+ bool ownsTexture() const override { return m_owns; }
+
+ void paint(QPainter *painter);
+
+private:
+ void updateCachedMirroredPixmap();
+
+ QPixmap m_cachedPixmap;
+ QSGTexture *m_texture;
+ QRectF m_rect;
+ QRectF m_sourceRect;
+ bool m_owns;
+ QSGTexture::Filtering m_filtering;
+ TextureCoordinatesTransformMode m_transformMode;
+ bool m_cachedMirroredPixmapIsDirty;
+};
+
+class QSGSoftwareNinePatchNode : public QSGNinePatchNode
+{
+public:
+ QSGSoftwareNinePatchNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setBounds(const QRectF &bounds) override;
+ void setDevicePixelRatio(qreal ratio) override;
+ void setPadding(qreal left, qreal top, qreal right, qreal bottom) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+
+ QRectF bounds() const;
+
+private:
+ QPixmap m_pixmap;
+ QRectF m_bounds;
+ qreal m_pixelRatio;
+ QMargins m_margins;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPUBLICNODES_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index 063242c63b..7b9e749532 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -39,15 +39,17 @@
#include "qsgsoftwarerenderablenode_p.h"
-#include "qsgsoftwareimagenode_p.h"
-#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
#include "qsgsoftwareglyphnode_p.h"
-#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
#include "qsgsoftwarepainternode_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwarespritenode_p.h"
-#include <QtQuick/QSGSimpleRectNode>
-#include <QtQuick/qsgsimpletexturenode.h>
+#include <qsgsimplerectnode.h>
+#include <qsgsimpletexturenode.h>
+#include <private/qsgrendernode_p.h>
#include <private/qsgtexture_p.h>
Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
@@ -58,6 +60,7 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod
: m_nodeType(type)
, m_isOpaque(true)
, m_isDirty(true)
+ , m_hasClipRegion(false)
, m_opacity(1.0f)
{
switch (m_nodeType) {
@@ -68,13 +71,13 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod
m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
break;
case QSGSoftwareRenderableNode::Image:
- m_handle.imageNode = static_cast<QSGSoftwareImageNode*>(node);
+ m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node);
break;
case QSGSoftwareRenderableNode::Painter:
m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
break;
case QSGSoftwareRenderableNode::Rectangle:
- m_handle.rectangleNode = static_cast<QSGSoftwareRectangleNode*>(node);
+ m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node);
break;
case QSGSoftwareRenderableNode::Glyph:
m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
@@ -82,6 +85,18 @@ QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *nod
case QSGSoftwareRenderableNode::NinePatch:
m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
break;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ m_handle.simpleImageNode = static_cast<QSGImageNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::RenderNode:
+ m_handle.renderNode = static_cast<QSGRenderNode*>(node);
+ break;
case QSGSoftwareRenderableNode::Invalid:
m_handle.simpleRectNode = nullptr;
break;
@@ -151,14 +166,46 @@ void QSGSoftwareRenderableNode::update()
boundingRect = m_handle.ninePatchNode->bounds().toRect();
break;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleRectangleNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleImageNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_isOpaque = m_handle.spriteNode->isOpaque();
+ boundingRect = m_handle.spriteNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::RenderNode:
+ if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.renderNode->rect().toRect();
+ break;
default:
break;
}
m_boundingRect = m_transform.mapRect(boundingRect);
- if (m_clipRegion.rectCount() == 1) {
- m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first());
+ if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
+ // If there is a clipRegion, and it is empty, the item wont be rendered
+ if (m_clipRegion.isEmpty())
+ m_boundingRect = QRect();
+ else
+ m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first());
}
// Overrides
@@ -168,15 +215,56 @@ void QSGSoftwareRenderableNode::update()
m_dirtyRegion = QRegion(m_boundingRect);
}
+struct RenderNodeState : public QSGRenderNode::RenderState
+{
+ const QMatrix4x4 *projectionMatrix() const override { return &ident; }
+ QRect scissorRect() const override { return QRect(); }
+ bool scissorEnabled() const override { return false; }
+ int stencilValue() const override { return 0; }
+ bool stencilEnabled() const override { return false; }
+ const QRegion *clipRegion() const override { return &cr; }
+ QMatrix4x4 ident;
+ QRegion cr;
+};
+
QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
{
Q_ASSERT(painter);
// Check for don't paint conditions
- if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
- m_isDirty = false;
- m_dirtyRegion = QRegion();
- return QRegion();
+ if (m_nodeType != RenderNode) {
+ if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ }
+ } else {
+ if (!m_isDirty || qFuzzyIsNull(m_opacity)) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ } else {
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
+ QMatrix4x4 m = m_transform;
+ rd->m_matrix = &m;
+ rd->m_opacity = m_opacity;
+ RenderNodeState rs;
+ rs.cr = m_clipRegion;
+
+ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
+ ? m_boundingRect :
+ QRect(0, 0, painter->device()->width(), painter->device()->height());
+
+ painter->save();
+ painter->setClipRegion(br, Qt::ReplaceClip);
+ m_handle.renderNode->render(&rs);
+ painter->restore();
+
+ m_previousDirtyRegion = QRegion(br);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return br;
+ }
}
painter->save();
@@ -201,10 +289,10 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
QSGTexture *texture = m_handle.simpleTextureNode->texture();
if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(texture)) {
const QPixmap &pm = pt->pixmap();
- painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, QRectF(0, 0, pm.width(), pm.height()));
+ painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect());
} else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) {
const QImage &im = pt->image();
- painter->drawImage(m_handle.simpleTextureNode->rect(), im, QRectF(0, 0, im.width(), im.height()));
+ painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
}
}
break;
@@ -223,6 +311,15 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
case QSGSoftwareRenderableNode::NinePatch:
m_handle.ninePatchNode->paint(painter);
break;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::SpriteNode:
+ static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
+ break;
default:
break;
}
@@ -256,12 +353,13 @@ void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
update();
}
-void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect)
+void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion)
{
- if (m_clipRegion == clipRect)
+ if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion)
return;
m_clipRegion = clipRect;
+ m_hasClipRegion = hasClipRegion;
update();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
index 9a5e0a5683..0626b1e657 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -54,17 +54,21 @@
#include <QtGui/QRegion>
#include <QtCore/QRect>
#include <QtGui/QTransform>
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
QT_BEGIN_NAMESPACE
-class QSGNode;
class QSGSimpleRectNode;
class QSGSimpleTextureNode;
-class QSGSoftwareImageNode;
+class QSGSoftwareInternalImageNode;
class QSGSoftwarePainterNode;
-class QSGSoftwareRectangleNode;
+class QSGSoftwareInternalRectangleNode;
class QSGSoftwareGlyphNode;
class QSGSoftwareNinePatchNode;
+class QSGSoftwareSpriteNode;
+class QSGRenderNode;
class QSGSoftwareRenderableNode
{
@@ -77,7 +81,11 @@ public:
Painter,
Rectangle,
Glyph,
- NinePatch
+ NinePatch,
+ SimpleRectangle,
+ SimpleImage,
+ SpriteNode,
+ RenderNode
};
QSGSoftwareRenderableNode(NodeType type, QSGNode *node);
@@ -93,7 +101,7 @@ public:
bool isDirtyRegionEmpty() const;
void setTransform(const QTransform &transform);
- void setClipRegion(const QRegion &clipRegion);
+ void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true);
void setOpacity(float opacity);
QTransform transform() const { return m_transform; }
QRegion clipRegion() const { return m_clipRegion; }
@@ -112,11 +120,15 @@ private:
union RenderableNodeHandle {
QSGSimpleRectNode *simpleRectNode;
QSGSimpleTextureNode *simpleTextureNode;
- QSGSoftwareImageNode *imageNode;
+ QSGSoftwareInternalImageNode *imageNode;
QSGSoftwarePainterNode *painterNode;
- QSGSoftwareRectangleNode *rectangleNode;
+ QSGSoftwareInternalRectangleNode *rectangleNode;
QSGSoftwareGlyphNode *glpyhNode;
QSGSoftwareNinePatchNode *ninePatchNode;
+ QSGRectangleNode *simpleRectangleNode;
+ QSGImageNode *simpleImageNode;
+ QSGSoftwareSpriteNode *spriteNode;
+ QSGRenderNode *renderNode;
};
const NodeType m_nodeType;
@@ -130,6 +142,7 @@ private:
QTransform m_transform;
QRegion m_clipRegion;
+ bool m_hasClipRegion;
float m_opacity;
QRect m_boundingRect;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
index 3d5fe21f7c..12dbf63353 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
@@ -40,15 +40,16 @@
#include "qsgsoftwarerenderablenodeupdater_p.h"
#include "qsgabstractsoftwarerenderer_p.h"
-#include "qsgsoftwareimagenode_p.h"
-#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
#include "qsgsoftwareglyphnode_p.h"
-#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
#include "qsgsoftwarepainternode_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
-#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimplerectnode.h>
#include <QtQuick/qsgsimpletexturenode.h>
+#include <QtQuick/qsgrendernode.h>
QT_BEGIN_NAMESPACE
@@ -58,6 +59,7 @@ QSGSoftwareRenderableNodeUpdater::QSGSoftwareRenderableNodeUpdater(QSGAbstractSo
m_opacityState.push(1.0f);
// Invalid RectF by default for no clip
m_clipState.push(QRegion());
+ m_hasClip = false;
m_transformState.push(QTransform());
}
@@ -81,10 +83,13 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *)
bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node)
{
// Make sure to translate the clip rect into world coordinates
- if (m_clipState.top().isEmpty()) {
+ if (m_clipState.count() == 1) {
m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect())));
- } else
- m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()).intersected(m_clipState.top())));
+ m_hasClip = true;
+ } else {
+ const QRegion transformedClipRect = m_transformState.top().map(QRegion(node->clipRect().toRect()));
+ m_clipState.push(transformedClipRect.intersected(m_clipState.top()));
+ }
m_stateMap[node] = currentState(node);
return true;
}
@@ -92,6 +97,8 @@ bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node)
void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *)
{
m_clipState.pop();
+ if (m_clipState.count() == 1)
+ m_hasClip = false;
}
bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node)
@@ -100,6 +107,12 @@ bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node)
return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRect, rectNode);
} else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) {
return updateRenderableNode(QSGSoftwareRenderableNode::SimpleTexture, tn);
+ } else if (QSGNinePatchNode *nn = dynamic_cast<QSGNinePatchNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, nn);
+ } else if (QSGRectangleNode *rn = dynamic_cast<QSGRectangleNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRectangle, rn);
+ } else if (QSGImageNode *n = dynamic_cast<QSGImageNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleImage, n);
} else {
// We dont know, so skip
return false;
@@ -122,12 +135,12 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGOpacityNode *)
m_opacityState.pop();
}
-bool QSGSoftwareRenderableNodeUpdater::visit(QSGImageNode *node)
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalImageNode *node)
{
return updateRenderableNode(QSGSoftwareRenderableNode::Image, node);
}
-void QSGSoftwareRenderableNodeUpdater::endVisit(QSGImageNode *)
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalImageNode *)
{
}
@@ -140,12 +153,12 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGPainterNode *)
{
}
-bool QSGSoftwareRenderableNodeUpdater::visit(QSGRectangleNode *node)
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalRectangleNode *node)
{
return updateRenderableNode(QSGSoftwareRenderableNode::Rectangle, node);
}
-void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRectangleNode *)
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalRectangleNode *)
{
}
@@ -158,22 +171,32 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGlyphNode *)
{
}
-bool QSGSoftwareRenderableNodeUpdater::visit(QSGNinePatchNode *node)
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node)
{
- return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, node);
+ m_stateMap[node] = currentState(node);
+ return true;
}
-void QSGSoftwareRenderableNodeUpdater::endVisit(QSGNinePatchNode *)
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *)
{
}
-bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node)
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGSpriteNode *node)
{
- m_stateMap[node] = currentState(node);
- return true;
+ return updateRenderableNode(QSGSoftwareRenderableNode::SpriteNode, node);
}
-void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *)
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *)
+{
+
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRenderNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::RenderNode, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRenderNode *)
{
}
@@ -195,12 +218,13 @@ void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRem
m_opacityState.push(state.opacity);
m_transformState.push(state.transform);
m_clipState.push(state.clip);
-
+ m_hasClip = state.hasClip;
} else {
// There is no parent, and no previous parent, so likely a root node
m_opacityState.push(1.0f);
m_transformState.push(QTransform());
m_clipState.push(QRegion());
+ m_hasClip = false;
}
// If the node is being removed, then cleanup the state data
@@ -256,6 +280,13 @@ void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRem
visitChildren(node);
break;
}
+ case QSGNode::RenderNodeType: {
+ QSGRenderNode *r = static_cast<QSGRenderNode*>(node);
+ if (visit(r))
+ visitChildren(r);
+ endVisit(r);
+ break;
+ }
default:
Q_UNREACHABLE();
break;
@@ -267,6 +298,7 @@ QSGSoftwareRenderableNodeUpdater::NodeState QSGSoftwareRenderableNodeUpdater::cu
NodeState state;
state.opacity = m_opacityState.top();
state.clip = m_clipState.top();
+ state.hasClip = m_hasClip;
state.transform = m_transformState.top();
state.parent = node->parent();
return state;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
index 562d15769e..f204867236 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
@@ -76,18 +76,20 @@ public:
void endVisit(QSGGeometryNode *) override;
bool visit(QSGOpacityNode *) override;
void endVisit(QSGOpacityNode *) override;
- bool visit(QSGImageNode *) override;
- void endVisit(QSGImageNode *) override;
+ bool visit(QSGInternalImageNode *) override;
+ void endVisit(QSGInternalImageNode *) override;
bool visit(QSGPainterNode *) override;
void endVisit(QSGPainterNode *) override;
- bool visit(QSGRectangleNode *) override;
- void endVisit(QSGRectangleNode *) override;
+ bool visit(QSGInternalRectangleNode *) override;
+ void endVisit(QSGInternalRectangleNode *) override;
bool visit(QSGGlyphNode *) override;
void endVisit(QSGGlyphNode *) override;
- bool visit(QSGNinePatchNode *) override;
- void endVisit(QSGNinePatchNode *) override;
bool visit(QSGRootNode *) override;
void endVisit(QSGRootNode *) override;
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+ bool visit(QSGRenderNode *) override;
+ void endVisit(QSGRenderNode *) override;
void updateNodes(QSGNode *node, bool isNodeRemoved = false);
@@ -95,6 +97,7 @@ private:
struct NodeState {
float opacity;
QRegion clip;
+ bool hasClip;
QTransform transform;
QSGNode *parent;
};
@@ -107,6 +110,7 @@ private:
QSGAbstractSoftwareRenderer *m_renderer;
QStack<float> m_opacityState;
QStack<QRegion> m_clipState;
+ bool m_hasClip;
QStack<QTransform> m_transformState;
QHash<QSGNode*,NodeState> m_stateMap;
};
@@ -124,7 +128,7 @@ bool QSGSoftwareRenderableNodeUpdater::updateRenderableNode(QSGSoftwareRenderabl
//Update the node
renderableNode->setTransform(m_transformState.top());
renderableNode->setOpacity(m_opacityState.top());
- renderableNode->setClipRegion(m_clipState.top());
+ renderableNode->setClipRegion(m_clipState.top(), m_hasClip);
renderableNode->update();
m_stateMap[node] = currentState(node);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
index ea00de7a66..cad826fb27 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -45,6 +45,7 @@
#include "qsgsoftwarerenderablenode_p.h"
#include <QtGui/QPaintDevice>
+#include <QtGui/QBackingStore>
#include <QElapsedTimer>
Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer")
@@ -53,6 +54,8 @@ QT_BEGIN_NAMESPACE
QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context)
: QSGAbstractSoftwareRenderer(context)
+ , m_paintDevice(nullptr)
+ , m_backingStore(nullptr)
{
}
@@ -63,6 +66,18 @@ QSGSoftwareRenderer::~QSGSoftwareRenderer()
void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device)
{
m_paintDevice = device;
+ m_backingStore = nullptr;
+}
+
+QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const
+{
+ return m_paintDevice;
+}
+
+void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore)
+{
+ m_backingStore = backingStore;
+ m_paintDevice = nullptr;
}
QRegion QSGSoftwareRenderer::flushRegion() const
@@ -82,18 +97,24 @@ void QSGSoftwareRenderer::renderScene(uint)
void QSGSoftwareRenderer::render()
{
- if (!m_paintDevice)
+ if (!m_paintDevice && !m_backingStore)
return;
+ // If there is a backingstore, set the current paint device
+ if (m_backingStore) {
+ // For HiDPI QBackingStores, the paint device is not valid
+ // until begin() has been called. See: QTBUG-55875
+ m_backingStore->beginPaint(QRegion());
+ m_paintDevice = m_backingStore->paintDevice();
+ m_backingStore->endPaint();
+ }
+
QElapsedTimer renderTimer;
setBackgroundColor(clearColor());
setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(),
m_paintDevice->height() / m_paintDevice->devicePixelRatio()));
- QPainter painter(m_paintDevice);
- painter.setRenderHint(QPainter::Antialiasing);
-
// Build Renderlist
// The renderlist is created by visiting each node in the tree and when a
// renderable node is reach, we find the coorosponding RenderableNode object
@@ -113,13 +134,31 @@ void QSGSoftwareRenderer::render()
// side effect of this is that additional nodes may need to be marked dirty to
// force a repaint. It is also important that any item that needs to be
// repainted only paints what is needed, via the use of clip regions.
- optimizeRenderList();
+ const QRegion updateRegion = optimizeRenderList();
qint64 optimizeRenderListTime = renderTimer.restart();
+ // If Rendering to a backingstore, prepare it to be updated
+ if (m_backingStore != nullptr) {
+ m_backingStore->beginPaint(updateRegion);
+ // It is possible that a QBackingStore's paintDevice() will change
+ // when begin() is called.
+ m_paintDevice = m_backingStore->paintDevice();
+ }
+
+ QPainter painter(m_paintDevice);
+ painter.setRenderHint(QPainter::Antialiasing);
+ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
+ QPainter *prevPainter = rc->m_activePainter;
+ rc->m_activePainter = &painter;
+
// Render the contents Renderlist
m_flushRegion = renderNodes(&painter);
qint64 renderTime = renderTimer.elapsed();
+ if (m_backingStore != nullptr)
+ m_backingStore->endPaint();
+
+ rc->m_activePainter = prevPainter;
qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
index e2b8bcddca..bb28da4ca5 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
@@ -56,6 +56,7 @@
QT_BEGIN_NAMESPACE
class QPaintDevice;
+class QBackingStore;
class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
{
@@ -64,6 +65,8 @@ public:
virtual ~QSGSoftwareRenderer();
void setCurrentPaintDevice(QPaintDevice *device);
+ QPaintDevice *currentPaintDevice() const;
+ void setBackingStore(QBackingStore *backingStore);
QRegion flushRegion() const;
protected:
@@ -72,6 +75,7 @@ protected:
private:
QPaintDevice* m_paintDevice;
+ QBackingStore* m_backingStore;
QRegion m_flushRegion;
};
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
index af81ff61c3..4e34517dad 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
@@ -41,15 +41,16 @@
#include "qsgsoftwarerenderablenode_p.h"
#include "qsgabstractsoftwarerenderer_p.h"
-#include "qsgsoftwareimagenode_p.h"
-#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
#include "qsgsoftwareglyphnode_p.h"
-#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
#include "qsgsoftwarepainternode_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
-#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimplerectnode.h>
#include <QtQuick/qsgsimpletexturenode.h>
+#include <QtQuick/qsgrendernode.h>
QT_BEGIN_NAMESPACE
@@ -95,12 +96,12 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGOpacityNode *)
{
}
-bool QSGSoftwareRenderListBuilder::visit(QSGImageNode *node)
+bool QSGSoftwareRenderListBuilder::visit(QSGInternalImageNode *node)
{
return addRenderableNode(node);
}
-void QSGSoftwareRenderListBuilder::endVisit(QSGImageNode *)
+void QSGSoftwareRenderListBuilder::endVisit(QSGInternalImageNode *)
{
}
@@ -113,12 +114,12 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGPainterNode *)
{
}
-bool QSGSoftwareRenderListBuilder::visit(QSGRectangleNode *node)
+bool QSGSoftwareRenderListBuilder::visit(QSGInternalRectangleNode *node)
{
return addRenderableNode(node);
}
-void QSGSoftwareRenderListBuilder::endVisit(QSGRectangleNode *)
+void QSGSoftwareRenderListBuilder::endVisit(QSGInternalRectangleNode *)
{
}
@@ -131,21 +132,31 @@ void QSGSoftwareRenderListBuilder::endVisit(QSGGlyphNode *)
{
}
-bool QSGSoftwareRenderListBuilder::visit(QSGNinePatchNode *node)
+bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGSpriteNode *node)
{
return addRenderableNode(node);
}
-void QSGSoftwareRenderListBuilder::endVisit(QSGNinePatchNode *)
+void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *)
{
+
}
-bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *)
+bool QSGSoftwareRenderListBuilder::visit(QSGRenderNode *node)
{
- return true;
+ return addRenderableNode(node);
}
-void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *)
+void QSGSoftwareRenderListBuilder::endVisit(QSGRenderNode *)
{
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
index 94b563564d..807cb7fdbe 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
@@ -70,18 +70,20 @@ public:
void endVisit(QSGGeometryNode *) override;
bool visit(QSGOpacityNode *) override;
void endVisit(QSGOpacityNode *) override;
- bool visit(QSGImageNode *) override;
- void endVisit(QSGImageNode *) override;
+ bool visit(QSGInternalImageNode *) override;
+ void endVisit(QSGInternalImageNode *) override;
bool visit(QSGPainterNode *) override;
void endVisit(QSGPainterNode *) override;
- bool visit(QSGRectangleNode *) override;
- void endVisit(QSGRectangleNode *) override;
+ bool visit(QSGInternalRectangleNode *) override;
+ void endVisit(QSGInternalRectangleNode *) override;
bool visit(QSGGlyphNode *) override;
void endVisit(QSGGlyphNode *) override;
- bool visit(QSGNinePatchNode *) override;
- void endVisit(QSGNinePatchNode *) override;
bool visit(QSGRootNode *) override;
void endVisit(QSGRootNode *) override;
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+ bool visit(QSGRenderNode *) override;
+ void endVisit(QSGRenderNode *) override;
private:
bool addRenderableNode(QSGNode *node);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
index 5292e1371f..19a963b403 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
@@ -156,11 +156,9 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window)
//Tell the renderer about the windows backing store
auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
if (softwareRenderer)
- softwareRenderer->setCurrentPaintDevice(m_backingStores[window]->paintDevice());
+ softwareRenderer->setBackingStore(m_backingStores[window]);
- m_backingStores[window]->beginPaint(QRect(0, 0, window->width(), window->height()));
cd->renderSceneGraph(window->size());
- m_backingStores[window]->endPaint();
if (profileFrames)
renderTime = renderTimer.nsecsElapsed();
@@ -185,7 +183,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window)
if (QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled()) {
static QTime lastFrameTime = QTime::currentTime();
qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP,
- "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
+ "Frame rendered with 'software' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
int(swapTime / 1000000),
int(polishTime / 1000000),
int((syncTime - polishTime) / 1000000),
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp
new file mode 100644
index 0000000000..ba7bbc2d11
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarespritenode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include <QtGui/QPainter>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareSpriteNode::QSGSoftwareSpriteNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareSpriteNode::setTexture(QSGTexture *texture)
+{
+ m_texture = qobject_cast<QSGSoftwarePixmapTexture*>(texture);
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareSpriteNode::setTime(float time)
+{
+ if (m_time != time) {
+ m_time = time;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSourceA(const QPoint &source)
+{
+ if (m_sourceA != source) {
+ m_sourceA = source;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSourceB(const QPoint &source)
+{
+ if (m_sourceB != source) {
+ m_sourceB = source;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSpriteSize(const QSize &size)
+{
+ if (m_spriteSize != size) {
+ m_spriteSize = size;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSheetSize(const QSize &size)
+{
+ if (m_sheetSize != size) {
+ m_sheetSize = size;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSize(const QSizeF &size)
+{
+ if (m_size != size) {
+ m_size = size;
+ markDirty(DirtyGeometry);
+ }
+}
+
+void QSGSoftwareSpriteNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ Q_UNUSED(filtering);
+}
+
+void QSGSoftwareSpriteNode::update()
+{
+}
+
+void QSGSoftwareSpriteNode::paint(QPainter *painter)
+{
+ //Get the pixmap handle from the texture
+ if (!m_texture)
+ return;
+
+ const QPixmap &pixmap = m_texture->pixmap();
+
+ // XXX try to do some kind of interpolation between sourceA and sourceB using time
+ painter->drawPixmap(QRectF(0, 0, m_size.width(), m_size.height()),
+ pixmap,
+ QRectF(m_sourceA, m_spriteSize));
+}
+
+bool QSGSoftwareSpriteNode::isOpaque() const
+{
+ return false;
+}
+
+QRectF QSGSoftwareSpriteNode::rect() const
+{
+ return QRectF(0, 0, m_size.width(), m_size.height());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h
index bc7aec1b5a..284ed3dff5 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h
@@ -37,10 +37,8 @@
**
****************************************************************************/
-#ifndef QSGSOFTWARENINEPATCHNODE_H
-#define QSGSOFTWARENINEPATCHNODE_H
-
-#include <private/qsgadaptationlayer_p.h>
+#ifndef QSGSOFTWARESPRITENODE_H
+#define QSGSOFTWARESPRITENODE_H
//
// W A R N I N G
@@ -53,30 +51,42 @@
// We mean it.
//
+#include <private/qsgadaptationlayer_p.h>
+
QT_BEGIN_NAMESPACE
-class QSGSoftwareNinePatchNode : public QSGNinePatchNode
+class QSGSoftwarePixmapTexture;
+class QSGSoftwareSpriteNode : public QSGSpriteNode
{
public:
- QSGSoftwareNinePatchNode();
+ QSGSoftwareSpriteNode();
void setTexture(QSGTexture *texture) override;
- void setBounds(const QRectF &bounds) override;
- void setDevicePixelRatio(qreal ratio) override;
- void setPadding(qreal left, qreal top, qreal right, qreal bottom) override;
+ void setTime(float time) override;
+ void setSourceA(const QPoint &source) override;
+ void setSourceB(const QPoint &source) override;
+ void setSpriteSize(const QSize &size) override;
+ void setSheetSize(const QSize &size) override;
+ void setSize(const QSizeF &size) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
void update() override;
void paint(QPainter *painter);
-
- QRectF bounds() const;
+ bool isOpaque() const;
+ QRectF rect() const;
private:
- QPixmap m_pixmap;
- QRectF m_bounds;
- qreal m_pixelRatio;
- QMargins m_margins;
+
+ QSGSoftwarePixmapTexture *m_texture;
+ float m_time;
+ QPoint m_sourceA;
+ QPoint m_sourceB;
+ QSize m_spriteSize;
+ QSize m_sheetSize;
+ QSizeF m_size;
+
};
QT_END_NAMESPACE
-#endif // QSGSOFTWARENINEPATCHNODE_H
+#endif // QSGSOFTWARESPRITENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
new file mode 100644
index 0000000000..5d5485ed8f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -0,0 +1,991 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarethreadedrenderloop_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderer_p.h"
+
+#include <private/qsgrenderer_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+
+#include <qpa/qplatformbackingstore.h>
+
+#include <QtCore/QQueue>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QBackingStore>
+#include <QtQuick/QQuickWindow>
+
+QT_BEGIN_NAMESPACE
+
+// 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 + 1);
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync.
+const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
+
+// 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 + 3);
+
+// Passed by the RL to the RT to maybe release resource if no windows are
+// rendering.
+const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called.
+const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
+
+// Passed by the window when there is a render job to run.
+const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+
+class QSGSoftwareWindowEvent : public QEvent
+{
+public:
+ QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
+ QQuickWindow *window;
+};
+
+class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
+ : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { }
+ bool destroying;
+};
+
+class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
+ : QSGSoftwareWindowEvent(c, WM_RequestSync)
+ , size(c->size())
+ , dpr(c->effectiveDevicePixelRatio())
+ , syncInExpose(inExpose)
+ , forceRenderPass(force) { }
+ QSize size;
+ float dpr;
+ bool syncInExpose;
+ bool forceRenderPass;
+};
+
+class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
+ : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { }
+ QImage *image;
+};
+
+class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
+ : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { }
+ ~QSGSoftwareJobEvent() { delete job; }
+ QRunnable *job;
+};
+
+class QSGSoftwareEventQueue : public QQueue<QEvent *>
+{
+public:
+ void addEvent(QEvent *e) {
+ mutex.lock();
+ enqueue(e);
+ if (waiting)
+ condition.wakeOne();
+ mutex.unlock();
+ }
+
+ QEvent *takeEvent(bool wait) {
+ mutex.lock();
+ if (isEmpty() && 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 = false;
+};
+
+static inline int qsgrl_animation_interval()
+{
+ const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
+ return refreshRate < 1 ? 16 : int(1000 / refreshRate);
+}
+
+class QSGSoftwareRenderThread : public QThread
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext)
+ : renderLoop(rl)
+ {
+ rc = static_cast<QSGSoftwareRenderContext *>(renderContext);
+ vsyncDelta = qsgrl_animation_interval();
+ }
+
+ ~QSGSoftwareRenderThread()
+ {
+ delete rc;
+ }
+
+ bool event(QEvent *e);
+ void run();
+
+ void syncAndRender();
+ void sync(bool inExpose);
+
+ void requestRepaint()
+ {
+ if (sleeping)
+ stopEventProcessing = true;
+ if (exposedWindow)
+ pendingUpdate |= RepaintRequest;
+ }
+
+ void processEventsAndWaitForMore();
+ void processEvents();
+ void postEvent(QEvent *e);
+
+ enum UpdateRequest {
+ SyncRequest = 0x01,
+ RepaintRequest = 0x02,
+ ExposeRequest = 0x04 | RepaintRequest | SyncRequest
+ };
+
+ QSGSoftwareThreadedRenderLoop *renderLoop;
+ QSGSoftwareRenderContext *rc;
+ QAnimationDriver *rtAnim = nullptr;
+ volatile bool active = false;
+ uint pendingUpdate = 0;
+ bool sleeping = false;
+ bool syncResultedInChanges = false;
+ float vsyncDelta;
+ QMutex mutex;
+ QWaitCondition waitCondition;
+ QQuickWindow *exposedWindow = nullptr;
+ QBackingStore *backingStore = nullptr;
+ bool stopEventProcessing = false;
+ QSGSoftwareEventQueue eventQueue;
+ QElapsedTimer renderThrottleTimer;
+ qint64 syncTime;
+ qint64 renderTime;
+ qint64 sinceLastTime;
+
+public slots:
+ void onSceneGraphChanged() {
+ syncResultedInChanges = true;
+ }
+};
+
+bool QSGSoftwareRenderThread::event(QEvent *e)
+{
+ switch ((int)e->type()) {
+
+ case WM_Obscure:
+ Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGSoftwareWindowEvent *>(e)->window);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow;
+ mutex.lock();
+ if (exposedWindow) {
+ QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed");
+ exposedWindow = nullptr;
+ delete backingStore;
+ backingStore = nullptr;
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+
+ case WM_RequestSync: {
+ QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e);
+ if (sleeping)
+ stopEventProcessing = true;
+ exposedWindow = wme->window;
+ if (backingStore == nullptr)
+ backingStore = new QBackingStore(exposedWindow);
+ if (backingStore->size() != exposedWindow->size())
+ backingStore->resize(exposedWindow->size());
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow;
+ pendingUpdate |= SyncRequest;
+ if (wme->syncInExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose");
+ pendingUpdate |= ExposeRequest;
+ }
+ if (wme->forceRenderPass) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless");
+ pendingUpdate |= RepaintRequest;
+ }
+ return true;
+ }
+
+ case WM_TryRelease: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease");
+ mutex.lock();
+ renderLoop->lockedForSync = true;
+ QSGSoftwareTryReleaseEvent *wme = static_cast<QSGSoftwareTryReleaseEvent *>(e);
+ // Only when no windows are exposed anymore or we are shutting down.
+ if (!exposedWindow || wme->destroying) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc");
+ if (wme->window) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ if (wme->destroying) {
+ // Bye bye nodes...
+ wd->cleanupNodesOnShutdown();
+ }
+ rc->invalidate();
+ QCoreApplication::processEvents();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ if (wme->destroying)
+ delete wd->animationController;
+ }
+ if (wme->destroying)
+ active = false;
+ if (sleeping)
+ stopEventProcessing = true;
+ } else {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active");
+ }
+ waitCondition.wakeOne();
+ renderLoop->lockedForSync = false;
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_Grab: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab");
+ QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e);
+ Q_ASSERT(wme->window);
+ Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
+ mutex.lock();
+ if (wme->window) {
+ // Grabbing is generally done by rendering a frame and reading the
+ // color buffer contents back, without presenting, and then
+ // creating a QImage from the returned data. It is terribly
+ // inefficient since it involves a full blocking wait for the GPU.
+ // However, our hands are tied by the existing, synchronous APIs of
+ // QQuickWindow and such.
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+ wd->renderSceneGraph(wme->window->size());
+ *wme->image = backingStore->handle()->toImage();
+ }
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_PostJob: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob");
+ QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e);
+ Q_ASSERT(wme->window == exposedWindow);
+ if (exposedWindow) {
+ wme->job->run();
+ delete wme->job;
+ wme->job = nullptr;
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done");
+ }
+ return true;
+ }
+
+ case WM_RequestRepaint:
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint");
+ // When GUI posts this event, it is followed by a polishAndSync, so we
+ // must not exit the event loop yet.
+ pendingUpdate |= RepaintRequest;
+ break;
+
+ default:
+ break;
+ }
+
+ return QThread::event(e);
+}
+
+void QSGSoftwareRenderThread::postEvent(QEvent *e)
+{
+ eventQueue.addEvent(e);
+}
+
+void QSGSoftwareRenderThread::processEvents()
+{
+ while (eventQueue.hasMoreEvents()) {
+ QEvent *e = eventQueue.takeEvent(false);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::processEventsAndWaitForMore()
+{
+ stopEventProcessing = false;
+ while (!stopEventProcessing) {
+ QEvent *e = eventQueue.takeEvent(true);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::run()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()");
+
+ rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr);
+ rtAnim->install();
+
+ if (QQmlDebugConnector::service<QQmlProfilerService>())
+ QQuickProfiler::registerAnimationCallback();
+
+ renderThrottleTimer.start();
+
+ while (active) {
+ if (exposedWindow)
+ syncAndRender();
+
+ processEvents();
+ QCoreApplication::processEvents();
+
+ if (pendingUpdate == 0 || !exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep");
+ sleeping = true;
+ processEventsAndWaitForMore();
+ sleeping = false;
+ }
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting");
+
+ delete rtAnim;
+ rtAnim = nullptr;
+
+ rc->moveToThread(renderLoop->thread());
+ moveToThread(renderLoop->thread());
+}
+
+void QSGSoftwareRenderThread::sync(bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync");
+
+ mutex.lock();
+ Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked");
+
+ if (exposedWindow) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+ bool hadRenderer = wd->renderer != nullptr;
+ // If the scene graph was touched since the last sync() make sure it sends the
+ // changed signal.
+ if (wd->renderer)
+ wd->renderer->clearChangedFlag();
+
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+
+ if (!hadRenderer && wd->renderer) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer");
+ syncResultedInChanges = true;
+ connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this,
+ &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
+ }
+
+ // Process deferred deletes now, directly after the sync as deleteLater
+ // on the GUI must now also have resulted in SG changes and the delete
+ // is a safe operation.
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+
+ if (!inExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+}
+
+void QSGSoftwareRenderThread::syncAndRender()
+{
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ QElapsedTimer waitTimer;
+ waitTimer.start();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()");
+
+ syncResultedInChanges = false;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage;
+ const bool syncRequested = pendingUpdate & SyncRequest;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ pendingUpdate = 0;
+
+ if (syncRequested)
+ sync(exposeRequested);
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (!syncResultedInChanges && !repaintRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started");
+
+ if (rtAnim->isRunning()) {
+ wd->animationController->lock();
+ rtAnim->advance();
+ wd->animationController->unlock();
+ }
+
+ bool canRender = wd->renderer != nullptr;
+
+ if (canRender) {
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ wd->renderSceneGraph(exposedWindow->size());
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap()))
+ backingStore->flush(softwareRenderer->flushRegion());
+
+ // Since there is no V-Sync with QBackingStore, throttle rendering the refresh
+ // rate of the current screen the window is on.
+ int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed();
+ if (blockTime > 0) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - blocking for " << blockTime << "ms";
+ msleep(blockTime);
+ }
+ renderThrottleTimer.restart();
+
+ wd->fireFrameSwapped();
+ } else {
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render");
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done");
+
+ if (exposeRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+}
+
+template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window)
+{
+ for (const T &t : list) {
+ if (t.window == window)
+ return const_cast<T *>(&t);
+ }
+ return nullptr;
+}
+
+
+QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor");
+ m_sg = new QSGSoftwareContext;
+ m_anim = m_sg->createAnimationDriver(this);
+ connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
+ connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped);
+ m_anim->install();
+}
+
+QSGSoftwareThreadedRenderLoop::~QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor");
+ delete m_sg;
+}
+
+void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window;
+}
+
+void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
+
+ if (window->isExposed())
+ handleObscurity(windowFor(m_windows, window));
+
+ releaseResources(window);
+}
+
+void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size();
+}
+
+void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ handleObscurity(w);
+ handleResourceRelease(w, true);
+
+ QSGSoftwareRenderThread *thread = w->thread;
+ while (thread->isRunning())
+ QThread::yieldCurrentThread();
+
+ Q_ASSERT(thread->thread() == QThread::currentThread());
+ delete thread;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ if (m_windows.at(i).window == window) {
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window;
+
+ if (window->isExposed()) {
+ handleExposure(window);
+ } else {
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleObscurity(w);
+ }
+}
+
+QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ // Have to support invisible (but created()'ed) windows as well.
+ // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
+ const bool tempExpose = !w;
+ if (tempExpose) {
+ handleExposure(window);
+ w = windowFor(m_windows, window);
+ Q_ASSERT(w);
+ }
+
+ if (!w->thread->isRunning())
+ return QImage();
+
+ if (!window->handle())
+ window->create();
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ QImage result;
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+
+ result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
+
+ if (tempExpose)
+ handleObscurity(w);
+
+ return result;
+}
+
+void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ if (w->thread == QThread::currentThread()) {
+ w->thread->requestRepaint();
+ return;
+ }
+
+ // We set forceRenderPass because we want to make sure the QQuickWindow
+ // actually does a full render pass after the next sync.
+ w->forceRenderPass = true;
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ polishAndSync(w, false);
+}
+
+QAnimationDriver *QSGSoftwareThreadedRenderLoop::animationDriver() const
+{
+ return m_anim;
+}
+
+QSGContext *QSGSoftwareThreadedRenderLoop::sceneGraphContext() const
+{
+ return m_sg;
+}
+
+QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const
+{
+ return m_sg->createRenderContext();
+}
+
+void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleResourceRelease(w, false);
+}
+
+void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w && w->thread && w->thread->exposedWindow)
+ w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
+ else
+ delete job;
+}
+
+QSurface::SurfaceType QSGSoftwareThreadedRenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+bool QSGSoftwareThreadedRenderLoop::interleaveIncubation() const
+{
+ bool somethingVisible = false;
+ for (const WindowData &w : m_windows) {
+ if (w.window->isVisible() && w.window->isExposed()) {
+ somethingVisible = true;
+ break;
+ }
+ }
+ return somethingVisible && m_anim->isRunning();
+}
+
+int QSGSoftwareThreadedRenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+bool QSGSoftwareThreadedRenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent *>(e);
+ if (te->timerId() == animationTimer) {
+ m_anim->advance();
+ emit timeToIncubate();
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStarted()
+{
+ startOrStopAnimationTimer();
+
+ for (const WindowData &w : qAsConst(m_windows))
+ w.window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStopped()
+{
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
+{
+ int exposedWindowCount = 0;
+ const WindowData *exposed = nullptr;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ const WindowData &w(m_windows[i]);
+ if (w.window->isVisible() && w.window->isExposed()) {
+ ++exposedWindowCount;
+ exposed = &w;
+ }
+ }
+
+ if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
+ killTimer(animationTimer);
+ animationTimer = 0;
+ // If animations are running, make sure we keep on animating
+ if (m_anim->isRunning())
+ exposed->window->requestUpdate();
+ } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
+ animationTimer = startTimer(qsgrl_animation_interval());
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
+ WindowData win;
+ win.window = window;
+ QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
+ win.thread = new QSGSoftwareRenderThread(this, rc);
+ win.updateDuringSync = false;
+ win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
+ m_windows.append(win);
+ w = &m_windows.last();
+ }
+
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // special casing exposure in polishAndSync.
+ w->thread->exposedWindow = window;
+
+ if (w->window->size().isEmpty()
+ || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
+#ifndef QT_NO_DEBUG
+ qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window "
+ << w->window << " with invalid geometry: " << w->window->geometry()
+ << " on " << w->window->screen();
+#endif
+ }
+
+ if (!w->window->handle())
+ w->window->create();
+
+ // Start render thread if it is not running
+ if (!w->thread->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
+ // Push a few things to the render thread.
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ if (controller->thread() != w->thread)
+ controller->moveToThread(w->thread);
+ if (w->thread->thread() == QThread::currentThread()) {
+ w->thread->rc->moveToThread(w->thread);
+ w->thread->moveToThread(w->thread);
+ }
+
+ w->thread->active = true;
+ w->thread->start();
+
+ if (!w->thread->isRunning())
+ qFatal("Render thread failed to start, aborting application.");
+ }
+
+ polishAndSync(w, true);
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window;
+
+ if (w->thread->isRunning()) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ if (!QCoreApplication::instance())
+ return;
+
+ if (!w || !w->thread->isRunning())
+ return;
+
+ QThread *current = QThread::currentThread();
+ if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
+ qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
+ return;
+ }
+
+ if (current == w->thread) {
+ w->updateDuringSync = true;
+ return;
+ }
+
+ w->window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
+
+ w->thread->mutex.lock();
+ if (w->thread->isRunning() && w->thread->active) {
+ QQuickWindow *window = w->window;
+
+ // Note that window->handle() is typically null by this time because
+ // the platform window is already destroyed. This should not be a
+ // problem for the D3D cleanup.
+
+ w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+
+ // Avoid a shutdown race condition.
+ // If SG is invalidated and 'active' becomes false, the thread's run()
+ // method will exit. handleExposure() relies on QThread::isRunning() (because it
+ // potentially needs to start the thread again) and our mutex cannot be used to
+ // track the thread stopping, so we wait a few nanoseconds extra so the thread
+ // can exit properly.
+ if (!w->thread->active)
+ w->thread->wait();
+ }
+ w->thread->mutex.unlock();
+}
+
+void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
+
+ QQuickWindow *window = w->window;
+ if (!w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort");
+ return;
+ }
+
+ // Flush pending touch events.
+ QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents();
+ // The delivery of the event might have caused the window to stop rendering
+ w = windowFor(m_windows, window);
+ if (!w || !w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
+ return;
+ }
+
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ w->updateDuringSync = false;
+
+ emit window->afterAnimating();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync");
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass));
+ w->forceRenderPass = false;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ if (!animationTimer && m_anim->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations");
+ m_anim->advance();
+ // We need to trigger another sync to keep animations running...
+ w->window->requestUpdate();
+ emit timeToIncubate();
+ } else if (w->updateDuringSync) {
+ w->window->requestUpdate();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync);
+}
+
+#include "qsgsoftwarethreadedrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
new file mode 100644
index 0000000000..99993d651c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARETHREADEDRENDERLOOP_H
+#define QSGSOFTWARETHREADEDRENDERLOOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderThread;
+class QSGSoftwareContext;
+
+class QSGSoftwareThreadedRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGSoftwareThreadedRenderLoop();
+ ~QSGSoftwareThreadedRenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+ void resize(QQuickWindow *window) override;
+ void windowDestroyed(QQuickWindow *window) override;
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override;
+ void maybeUpdate(QQuickWindow *window) override;
+ void handleUpdateRequest(QQuickWindow *window) override;
+ QAnimationDriver *animationDriver() const override;
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override;
+ void releaseResources(QQuickWindow *window) override;
+ void postJob(QQuickWindow *window, QRunnable *job) override;
+ QSurface::SurfaceType windowSurfaceType() const override;
+ bool interleaveIncubation() const override;
+ int flags() const override;
+
+ bool event(QEvent *e) override;
+
+public Q_SLOTS:
+ void onAnimationStarted();
+ void onAnimationStopped();
+
+private:
+ struct WindowData {
+ QQuickWindow *window;
+ QSGSoftwareRenderThread *thread;
+ uint updateDuringSync : 1;
+ uint forceRenderPass : 1;
+ };
+
+ void startOrStopAnimationTimer();
+ void handleExposure(QQuickWindow *window);
+ void handleObscurity(WindowData *w);
+ void scheduleUpdate(WindowData *w);
+ void handleResourceRelease(WindowData *w, bool destroying);
+ void polishAndSync(WindowData *w, bool inExpose);
+
+ QSGSoftwareContext *m_sg;
+ QAnimationDriver *m_anim;
+ int animationTimer = 0;
+ bool lockedForSync = false;
+ QVector<WindowData> m_windows;
+
+ friend class QSGSoftwareRenderThread;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARETHREADEDRENDERLOOP_H
diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri
index b8cdbc4a25..97644fc36a 100644
--- a/src/quick/scenegraph/adaptations/software/software.pri
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -6,10 +6,10 @@ SOURCES += \
$$PWD/qsgsoftwarecontext.cpp \
$$PWD/qsgabstractsoftwarerenderer.cpp \
$$PWD/qsgsoftwareglyphnode.cpp \
- $$PWD/qsgsoftwareimagenode.cpp \
- $$PWD/qsgsoftwareninepatchnode.cpp \
+ $$PWD/qsgsoftwareinternalimagenode.cpp \
+ $$PWD/qsgsoftwarepublicnodes.cpp \
$$PWD/qsgsoftwarepainternode.cpp \
- $$PWD/qsgsoftwarerectanglenode.cpp \
+ $$PWD/qsgsoftwareinternalrectanglenode.cpp \
$$PWD/qsgsoftwarepixmaprenderer.cpp \
$$PWD/qsgsoftwarepixmaptexture.cpp \
$$PWD/qsgsoftwarerenderablenode.cpp \
@@ -18,22 +18,26 @@ SOURCES += \
$$PWD/qsgsoftwarerenderlistbuilder.cpp \
$$PWD/qsgsoftwarerenderloop.cpp \
$$PWD/qsgsoftwarelayer.cpp \
- $$PWD/qsgsoftwareadaptation.cpp
+ $$PWD/qsgsoftwareadaptation.cpp \
+ $$PWD/qsgsoftwarespritenode.cpp \
+ $$PWD/qsgsoftwarethreadedrenderloop.cpp
HEADERS += \
$$PWD/qsgsoftwarecontext_p.h \
$$PWD/qsgabstractsoftwarerenderer_p.h \
$$PWD/qsgsoftwareglyphnode_p.h \
- $$PWD/qsgsoftwareimagenode_p.h \
- $$PWD/qsgsoftwareninepatchnode_p.h \
+ $$PWD/qsgsoftwareinternalimagenode_p.h \
+ $$PWD/qsgsoftwarepublicnodes_p.h \
$$PWD/qsgsoftwarepainternode_p.h \
$$PWD/qsgsoftwarepixmaprenderer_p.h \
$$PWD/qsgsoftwarepixmaptexture_p.h \
- $$PWD/qsgsoftwarerectanglenode_p.h \
+ $$PWD/qsgsoftwareinternalrectanglenode_p.h \
$$PWD/qsgsoftwarerenderablenode_p.h \
$$PWD/qsgsoftwarerenderablenodeupdater_p.h \
$$PWD/qsgsoftwarerenderer_p.h \
$$PWD/qsgsoftwarerenderlistbuilder_p.h \
$$PWD/qsgsoftwarerenderloop_p.h \
$$PWD/qsgsoftwarelayer_p.h \
- $$PWD/qsgsoftwareadaptation_p.h
+ $$PWD/qsgsoftwareadaptation_p.h \
+ $$PWD/qsgsoftwarespritenode_p.h \
+ $$PWD/qsgsoftwarethreadedrenderloop_p.h