summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp311
-rw-r--r--src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h88
-rw-r--r--src/plugins/scenegraph/softwarecontext/context.cpp80
-rw-r--r--src/plugins/scenegraph/softwarecontext/context.h39
-rw-r--r--src/plugins/scenegraph/softwarecontext/glyphnode.cpp1
-rw-r--r--src/plugins/scenegraph/softwarecontext/imagenode.cpp5
-rw-r--r--src/plugins/scenegraph/softwarecontext/imagenode.h2
-rw-r--r--src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp5
-rw-r--r--src/plugins/scenegraph/softwarecontext/ninepatchnode.h2
-rw-r--r--src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp101
-rw-r--r--src/plugins/scenegraph/softwarecontext/pixmaprenderer.h56
-rw-r--r--src/plugins/scenegraph/softwarecontext/pluginmain.cpp7
-rw-r--r--src/plugins/scenegraph/softwarecontext/rectanglenode.cpp24
-rw-r--r--src/plugins/scenegraph/softwarecontext/rectanglenode.h2
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderablenode.cpp312
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderablenode.h119
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp265
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h116
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderer.cpp148
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderer.h68
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp182
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp154
-rw-r--r--src/plugins/scenegraph/softwarecontext/renderlistbuilder.h (renamed from src/plugins/scenegraph/softwarecontext/renderingvisitor.h)50
-rw-r--r--src/plugins/scenegraph/softwarecontext/softwarecontext.pro23
-rw-r--r--src/plugins/scenegraph/softwarecontext/softwarelayer.cpp6
-rw-r--r--src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp1185
-rw-r--r--src/plugins/scenegraph/softwarecontext/threadedrenderloop.h112
27 files changed, 1833 insertions, 1630 deletions
diff --git a/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp
new file mode 100644
index 0000000..2963440
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "abstractsoftwarerenderer.h"
+
+#include "renderablenodeupdater.h"
+#include "renderlistbuilder.h"
+#include "context.h"
+#include "renderablenode.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtGui/QWindow>
+#include <QtQuick/QSGSimpleRectNode>
+
+Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer")
+
+namespace SoftwareContext{
+
+AbstractSoftwareRenderer::AbstractSoftwareRenderer(QSGRenderContext *context)
+ : QSGRenderer(context)
+ , m_background(new QSGSimpleRectNode)
+ , m_nodeUpdater(new RenderableNodeUpdater(this))
+{
+ // Setup special background node
+ auto backgroundRenderable = new RenderableNode(RenderableNode::SimpleRect, m_background);
+ addNodeMapping(m_background, backgroundRenderable);
+}
+
+AbstractSoftwareRenderer::~AbstractSoftwareRenderer()
+{
+ // Cleanup RenderableNodes
+ delete m_background;
+
+ for (RenderableNode *node : m_nodes.values()) {
+ delete node;
+ }
+
+ delete m_nodeUpdater;
+}
+
+RenderableNode *AbstractSoftwareRenderer::renderableNode(QSGNode *node) const
+{
+ return m_nodes.value(node, nullptr);
+}
+
+void AbstractSoftwareRenderer::addNodeMapping(QSGNode *node, RenderableNode *renderableNode)
+{
+ m_nodes.insert(node, renderableNode);
+}
+
+void AbstractSoftwareRenderer::appendRenderableNode(RenderableNode *node)
+{
+ m_renderableNodes.append(node);
+}
+
+void AbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+{
+ if (state & QSGNode::DirtyGeometry) {
+ nodeGeometryUpdated(node);
+ }
+ if (state & QSGNode::DirtyMaterial) {
+ nodeMaterialUpdated(node);
+ }
+ if (state & QSGNode::DirtyMatrix) {
+ nodeMatrixUpdated(node);
+ }
+ if (state & QSGNode::DirtyNodeAdded) {
+ nodeAdded(node);
+ }
+ if (state & QSGNode::DirtyNodeRemoved) {
+ nodeRemoved(node);
+ }
+ if (state & QSGNode::DirtyOpacity) {
+ nodeOpacityUpdated(node);
+ }
+ if (state & QSGNode::DirtySubtreeBlocked) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ if (state & QSGNode::DirtyForceUpdate) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ QSGRenderer::nodeChanged(node, state);
+}
+
+QRegion AbstractSoftwareRenderer::renderNodes(QPainter *painter)
+{
+ QRegion dirtyRegion;
+ // If there are no nodes, do nothing
+ if (m_renderableNodes.isEmpty())
+ return dirtyRegion;
+
+ auto iterator = m_renderableNodes.begin();
+ // First node is the background and needs to painted without blending
+ auto backgroundNode = *iterator;
+ dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ true);
+ iterator++;
+
+ for (; iterator != m_renderableNodes.end(); ++iterator) {
+ auto node = *iterator;
+ dirtyRegion += node->renderNode(painter);
+ }
+
+ return dirtyRegion;
+}
+
+void AbstractSoftwareRenderer::buildRenderList()
+{
+ // Clear the previous renderlist
+ m_renderableNodes.clear();
+ // Add the background renderable (always first)
+ m_renderableNodes.append(renderableNode(m_background));
+ // Build the renderlist
+ RenderListBuilder(this).visitChildren(rootNode());
+}
+
+void AbstractSoftwareRenderer::optimizeRenderList()
+{
+ // Iterate through the renderlist from front to back
+ // Objective is to update the dirty status and rects.
+ for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) {
+ auto node = *i;
+ if (!m_dirtyRegion.isEmpty()) {
+ // See if the current dirty regions apply to the current node
+ node->addDirtyRegion(m_dirtyRegion, true);
+ }
+
+ if (!m_obscuredRegion.isEmpty()) {
+ // Don't try to paint things that are covered by opaque objects
+ node->subtractDirtyRegion(m_obscuredRegion);
+ }
+
+ // Keep up with obscured regions
+ if (node->isOpaque()) {
+ m_obscuredRegion += QRegion(node->boundingRect());
+ }
+
+ if (node->isDirty()) {
+ // Don't paint things outside of the rendering area
+ if (!m_background->rect().toRect().contains(node->boundingRect(), /*proper*/ true)) {
+ // Some part(s) of node is(are) outside of the rendering area
+ QRegion renderArea(m_background->rect().toRect());
+ QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea);
+ if (!outsideRegions.isEmpty())
+ node->subtractDirtyRegion(outsideRegions);
+ }
+
+ // Get the dirty region's to pass to the next nodes
+ if (node->isOpaque()) {
+ // if isOpaque, subtract node's dirty rect from m_dirtyRegion
+ m_dirtyRegion -= node->dirtyRegion();
+ } else {
+ // if isAlpha, add node's dirty rect to m_dirtyRegion
+ m_dirtyRegion += node->dirtyRegion();
+ }
+ // if previousDirtyRegion has content outside of boundingRect add to m_dirtyRegion
+ QRegion prevDirty = node->previousDirtyRegion();
+ if (!prevDirty.isNull())
+ m_dirtyRegion += prevDirty;
+ }
+ }
+
+ // Empty dirtyRegion (for second pass)
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+
+ // Iterate through the renderlist from back to front
+ // Objective is to make sure all non-opaque items are painted when an item under them is dirty
+ for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) {
+ auto node = *j;
+
+ if (!node->isOpaque() && !m_dirtyRegion.isEmpty()) {
+ // Only blended nodes need to be updated
+ node->addDirtyRegion(m_dirtyRegion, true);
+ }
+
+ m_dirtyRegion += node->dirtyRegion();
+ }
+
+ // Empty dirtyRegion
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+}
+
+void AbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
+{
+ if (m_background->color() == color)
+ return;
+ m_background->setColor(color);
+ renderableNode(m_background)->markMaterialDirty();
+}
+
+void AbstractSoftwareRenderer::setBackgroundSize(const QSize &size)
+{
+ if (m_background->rect().size().toSize() == size)
+ return;
+ 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());
+}
+
+QColor AbstractSoftwareRenderer::backgroundColor()
+{
+ return m_background->color();
+}
+
+QSize AbstractSoftwareRenderer::backgroundSize()
+{
+ return m_background->rect().size().toSize();
+}
+
+void AbstractSoftwareRenderer::nodeAdded(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeAdded" << (void*)node;
+
+ m_nodeUpdater->updateNodes(node);
+}
+
+void AbstractSoftwareRenderer::nodeRemoved(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeRemoved" << (void*)node;
+
+ auto renderable = renderableNode(node);
+ // remove mapping
+ if (renderable != nullptr) {
+ // Need to mark this region dirty in the other nodes
+ QRegion dirtyRegion = renderable->previousDirtyRegion();
+ if (dirtyRegion.isEmpty())
+ dirtyRegion = renderable->boundingRect();
+ m_dirtyRegion += dirtyRegion;
+ m_nodes.remove(node);
+ delete renderable;
+ }
+
+ // Remove all children nodes as well
+ for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
+ nodeRemoved(child);
+ }
+
+ m_nodeUpdater->updateNodes(node, true);
+}
+
+void AbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeGeometryUpdated";
+
+ // Mark node as dirty
+ auto renderable = renderableNode(node);
+ if (renderable != nullptr) {
+ renderable->markGeometryDirty();
+ } else {
+ m_nodeUpdater->updateNodes(node);
+ }
+}
+
+void AbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeMaterialUpdated";
+
+ // Mark node as dirty
+ auto renderable = renderableNode(node);
+ if (renderable != nullptr) {
+ renderable->markMaterialDirty();
+ } else {
+ m_nodeUpdater->updateNodes(node);
+ }
+}
+
+void AbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeMaterialUpdated";
+
+ // Update children nodes
+ m_nodeUpdater->updateNodes(node);
+}
+
+void AbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeOpacityUpdated";
+
+ // Update children nodes
+ m_nodeUpdater->updateNodes(node);
+}
+
+}
diff --git a/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h
new file mode 100644
index 0000000..e08668f
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/abstractsoftwarerenderer.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ABSTRACTSOFTWARERENDERER_H
+#define ABSTRACTSOFTWARERENDERER_H
+
+#include <private/qsgrenderer_p.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QLinkedList>
+
+class QSGSimpleRectNode;
+
+namespace SoftwareContext{
+
+class RenderableNode;
+class RenderableNodeUpdater;
+
+class AbstractSoftwareRenderer : public QSGRenderer
+{
+public:
+ AbstractSoftwareRenderer(QSGRenderContext *context);
+ virtual ~AbstractSoftwareRenderer();
+
+ RenderableNode *renderableNode(QSGNode *node) const;
+ void addNodeMapping(QSGNode *node, RenderableNode *renderableNode);
+ void appendRenderableNode(RenderableNode *node);
+
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+
+protected:
+ QRegion renderNodes(QPainter *painter);
+ void buildRenderList();
+ void optimizeRenderList();
+
+ void setBackgroundColor(const QColor &color);
+ void setBackgroundSize(const QSize &size);
+ QColor backgroundColor();
+ QSize backgroundSize();
+
+private:
+ void nodeAdded(QSGNode *node);
+ void nodeRemoved(QSGNode *node);
+ void nodeGeometryUpdated(QSGNode *node);
+ void nodeMaterialUpdated(QSGNode *node);
+ void nodeMatrixUpdated(QSGNode *node);
+ void nodeOpacityUpdated(QSGNode *node);
+
+ QHash<QSGNode*, RenderableNode*> m_nodes;
+ QLinkedList<RenderableNode*> m_renderableNodes;
+
+ QSGSimpleRectNode *m_background;
+
+ QRegion m_dirtyRegion;
+ QRegion m_obscuredRegion;
+
+ RenderableNodeUpdater *m_nodeUpdater;
+};
+
+} // namespace
+
+#endif // ABSTRACTSOFTWARERENDERER_H
diff --git a/src/plugins/scenegraph/softwarecontext/context.cpp b/src/plugins/scenegraph/softwarecontext/context.cpp
index 728d5fd..3f1d3f9 100644
--- a/src/plugins/scenegraph/softwarecontext/context.cpp
+++ b/src/plugins/scenegraph/softwarecontext/context.cpp
@@ -35,8 +35,8 @@
#include "pixmaptexture.h"
#include "glyphnode.h"
#include "ninepatchnode.h"
-#include "renderingvisitor.h"
#include "softwarelayer.h"
+#include "renderer.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QElapsedTimer>
@@ -82,84 +82,6 @@ Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER, "qt.scenegraph.time.rende
namespace SoftwareContext
{
-Renderer::Renderer(QSGRenderContext *context)
- : QSGRenderer(context)
-{
-}
-
-void Renderer::renderScene(GLuint fboId)
-{
- Q_UNUSED(fboId)
-
- class B : public QSGBindable
- {
- public:
- void bind() const { }
- } bindable;
- QSGRenderer::renderScene(bindable);
-}
-
-void Renderer::render()
-{
- QWindow *currentWindow = static_cast<RenderContext*>(m_context)->currentWindow;
- if (!m_backingStore)
- m_backingStore.reset(new QBackingStore(currentWindow));
-
- if (m_backingStore->size() != currentWindow->size())
- m_backingStore->resize(currentWindow->size());
-
- const QRect rect(0, 0, currentWindow->width(), currentWindow->height());
- m_backingStore->beginPaint(rect);
-
- QPaintDevice *device = m_backingStore->paintDevice();
- QPainter painter(device);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setCompositionMode(QPainter::CompositionMode_Source);
- painter.fillRect(rect, clearColor());
- painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
- RenderingVisitor(&painter).visitChildren(rootNode());
-
- m_backingStore->endPaint();
- m_backingStore->flush(rect);
-}
-
-void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
-{
-
- QSGRenderer::nodeChanged(node, state);
-}
-
-PixmapRenderer::PixmapRenderer(QSGRenderContext *context)
- : QSGRenderer(context)
-{
-
-}
-
-void PixmapRenderer::renderScene(GLuint)
-{
- class B : public QSGBindable
- {
- public:
- void bind() const { }
- } bindable;
- QSGRenderer::renderScene(bindable);
-}
-
-void PixmapRenderer::render()
-{
-
-}
-
-void PixmapRenderer::render(QPixmap *target)
-{
- target->fill(clearColor());
- QPainter painter(target);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setWindow(m_projectionRect);
-
- RenderingVisitor(&painter).visitChildren(rootNode());
-}
-
RenderContext::RenderContext(QSGContext *ctx)
: QSGRenderContext(ctx)
, currentWindow(0)
diff --git a/src/plugins/scenegraph/softwarecontext/context.h b/src/plugins/scenegraph/softwarecontext/context.h
index 11b0af9..42a8175 100644
--- a/src/plugins/scenegraph/softwarecontext/context.h
+++ b/src/plugins/scenegraph/softwarecontext/context.h
@@ -31,11 +31,7 @@
#define CONTEXT_H
#include <private/qsgcontext_p.h>
-#include <private/qsgrenderer_p.h>
#include <private/qsgadaptationlayer_p.h>
-#include <QtCore/QElapsedTimer>
-#include <QtGui/QOpenGLShaderProgram>
-#include <QtGui/QBackingStore>
Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP)
Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION)
@@ -48,38 +44,6 @@ Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP)
namespace SoftwareContext
{
-class Renderer : public QSGRenderer
-{
-public:
- Renderer(QSGRenderContext *context);
-
- void renderScene(GLuint fboId = 0) override;
-
- void render() override;
-
- void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
-
- QBackingStore *backingStore() const { return m_backingStore.data(); }
-
-private:
- QScopedPointer<QBackingStore> m_backingStore;
- QRect m_dirtyRect;
-};
-
-class PixmapRenderer : public QSGRenderer
-{
-public:
- PixmapRenderer(QSGRenderContext *context);
-
- void renderScene(GLuint fboId = 0) override;
-
- void render() override;
-
- void render(QPixmap *target);
-
- QRect m_projectionRect;
-};
-
class RenderContext : public QSGRenderContext
{
public:
@@ -99,10 +63,9 @@ class Context : public QSGContext
{
Q_OBJECT
public:
- explicit Context(QObject *parent = 0);
+ explicit Context(QObject *parent = nullptr);
QSGRenderContext *createRenderContext() override { return new RenderContext(this); }
-
QSGRectangleNode *createRectangleNode() override;
QSGImageNode *createImageNode() override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
diff --git a/src/plugins/scenegraph/softwarecontext/glyphnode.cpp b/src/plugins/scenegraph/softwarecontext/glyphnode.cpp
index 9288983..d37965a 100644
--- a/src/plugins/scenegraph/softwarecontext/glyphnode.cpp
+++ b/src/plugins/scenegraph/softwarecontext/glyphnode.cpp
@@ -42,6 +42,7 @@ void GlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
{
m_position = position;
m_glyphRun = glyphs;
+ m_bounding_rect = glyphs.boundingRect().translated(m_position - QPointF(0.0, glyphs.rawFont().ascent()));
}
void GlyphNode::setColor(const QColor &color)
diff --git a/src/plugins/scenegraph/softwarecontext/imagenode.cpp b/src/plugins/scenegraph/softwarecontext/imagenode.cpp
index e09ad58..c095a66 100644
--- a/src/plugins/scenegraph/softwarecontext/imagenode.cpp
+++ b/src/plugins/scenegraph/softwarecontext/imagenode.cpp
@@ -474,6 +474,11 @@ void ImageNode::paint(QPainter *painter)
}
}
+QRectF ImageNode::rect() const
+{
+ return m_targetRect;
+}
+
const QPixmap &ImageNode::pixmap() const
{
if (PixmapTexture *pt = qobject_cast<PixmapTexture*>(m_texture)) {
diff --git a/src/plugins/scenegraph/softwarecontext/imagenode.h b/src/plugins/scenegraph/softwarecontext/imagenode.h
index 6f8dcea..e35e8f5 100644
--- a/src/plugins/scenegraph/softwarecontext/imagenode.h
+++ b/src/plugins/scenegraph/softwarecontext/imagenode.h
@@ -99,6 +99,8 @@ public:
void paint(QPainter *painter);
+ QRectF rect() const;
+
private:
const QPixmap &pixmap() const;
diff --git a/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp b/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp
index 582a186..48e45f8 100644
--- a/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp
+++ b/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp
@@ -88,3 +88,8 @@ void NinePatchNode::paint(QPainter *painter)
SoftwareContext::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()),
m_margins, Qt::StretchTile, QDrawBorderPixmap::DrawingHints(0));
}
+
+QRectF NinePatchNode::bounds() const
+{
+ return m_bounds;
+}
diff --git a/src/plugins/scenegraph/softwarecontext/ninepatchnode.h b/src/plugins/scenegraph/softwarecontext/ninepatchnode.h
index 30cde5b..14a19cf 100644
--- a/src/plugins/scenegraph/softwarecontext/ninepatchnode.h
+++ b/src/plugins/scenegraph/softwarecontext/ninepatchnode.h
@@ -45,6 +45,8 @@ public:
void paint(QPainter *painter);
+ QRectF bounds() const;
+
private:
QPixmap m_pixmap;
QRectF m_bounds;
diff --git a/src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp
new file mode 100644
index 0000000..d054fa9
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "pixmaprenderer.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcPixmapRenderer, "qt.scenegraph.softwarecontext.pixmapRenderer")
+
+namespace SoftwareContext {
+
+PixmapRenderer::PixmapRenderer(QSGRenderContext *context)
+ : AbstractSoftwareRenderer(context)
+{
+
+}
+
+PixmapRenderer::~PixmapRenderer()
+{
+
+}
+
+void PixmapRenderer::renderScene(GLuint)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void PixmapRenderer::render()
+{
+
+}
+
+void PixmapRenderer::render(QPixmap *target)
+{
+ QElapsedTimer renderTimer;
+
+ // Setup background item
+ setBackgroundSize(target->size());
+ setBackgroundColor(clearColor());
+
+ QPainter painter(target);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setWindow(m_projectionRect);
+
+ renderTimer.start();
+ buildRenderList();
+ qint64 buildRenderListTime = renderTimer.restart();
+
+ // Optimize Renderlist
+ // Right now there is an assumption that when possible the same pixmap will
+ // be reused. So we can treat it like a backing store in that we can assume
+ // that when the pixmap is not being resized, the data can be reused. What is
+ // different though is that everything should be marked as dirty on a resize.
+ optimizeRenderList();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ QRegion paintedRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
+}
+
+void PixmapRenderer::setProjectionRect(const QRect &projectionRect)
+{
+ m_projectionRect = projectionRect;
+}
+
+} // namespace
diff --git a/src/plugins/scenegraph/softwarecontext/pixmaprenderer.h b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.h
new file mode 100644
index 0000000..0890a83
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/pixmaprenderer.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PIXMAPRENDERER_H
+#define PIXMAPRENDERER_H
+
+#include "abstractsoftwarerenderer.h"
+
+namespace SoftwareContext {
+
+class PixmapRenderer : public AbstractSoftwareRenderer
+{
+public:
+ PixmapRenderer(QSGRenderContext *context);
+ virtual ~PixmapRenderer();
+
+ void renderScene(GLuint fboId = 0) final;
+ void render() final;
+
+ void render(QPixmap *target);
+ void setProjectionRect(const QRect &projectionRect);
+
+private:
+ QRect m_projectionRect;
+};
+
+
+} // namespace
+
+#endif // PIXMAPRENDERER_H
diff --git a/src/plugins/scenegraph/softwarecontext/pluginmain.cpp b/src/plugins/scenegraph/softwarecontext/pluginmain.cpp
index 133bf5d..80bcabe 100644
--- a/src/plugins/scenegraph/softwarecontext/pluginmain.cpp
+++ b/src/plugins/scenegraph/softwarecontext/pluginmain.cpp
@@ -30,7 +30,6 @@
#include "pluginmain.h"
#include "context.h"
#include "renderloop.h"
-#include "threadedrenderloop.h"
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -54,11 +53,7 @@ QSGContext *ContextPlugin::create(const QString &) const
QSGRenderLoop *ContextPlugin::createWindowManager()
{
- if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps) ||
- qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("basic"))
- return new RenderLoop();
-
- return new ThreadedRenderLoop();
+ return new RenderLoop();
}
SoftwareContext::Context *ContextPlugin::instance = 0;
diff --git a/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp b/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp
index 938cc1a..ef9d4fb 100644
--- a/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp
+++ b/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp
@@ -253,6 +253,30 @@ void RectangleNode::paint(QPainter *painter)
}
+bool RectangleNode::isOpaque() const
+{
+ if (m_radius > 0.0f)
+ return false;
+ if (m_color.alpha() < 255)
+ return false;
+ if (m_penWidth > 0.0f && m_penColor.alpha() < 255)
+ return false;
+ if (m_stops.count() > 0) {
+ foreach (QGradientStop stop, m_stops) {
+ if (stop.second.alpha() < 255)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QRectF RectangleNode::rect() const
+{
+ //TODO: double check that this is correct.
+ return m_rect;
+}
+
void RectangleNode::paintRectangle(QPainter *painter, const QRect &rect)
{
//Radius should never exceeds half of the width or half of the height
diff --git a/src/plugins/scenegraph/softwarecontext/rectanglenode.h b/src/plugins/scenegraph/softwarecontext/rectanglenode.h
index d660b13..565f4a3 100644
--- a/src/plugins/scenegraph/softwarecontext/rectanglenode.h
+++ b/src/plugins/scenegraph/softwarecontext/rectanglenode.h
@@ -54,6 +54,8 @@ public:
void paint(QPainter *);
+ bool isOpaque() const;
+ QRectF rect() const;
private:
void paintRectangle(QPainter *painter, const QRect &rect);
void generateCornerPixmap();
diff --git a/src/plugins/scenegraph/softwarecontext/renderablenode.cpp b/src/plugins/scenegraph/softwarecontext/renderablenode.cpp
new file mode 100644
index 0000000..0da19c1
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderablenode.cpp
@@ -0,0 +1,312 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderablenode.h"
+
+#include "imagenode.h"
+#include "rectanglenode.h"
+#include "glyphnode.h"
+#include "ninepatchnode.h"
+#include "painternode.h"
+#include "pixmaptexture.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimpletexturenode.h>
+#include <private/qsgtexture_p.h>
+#include <private/qquickshadereffectnode_p.h>
+
+Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
+
+namespace SoftwareContext{
+
+RenderableNode::RenderableNode(NodeType type, QSGNode *node)
+ : m_nodeType(type)
+ , m_isOpaque(true)
+ , m_isDirty(true)
+ , m_opacity(1.0f)
+{
+ switch (m_nodeType) {
+ case RenderableNode::SimpleRect:
+ m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
+ break;
+ case RenderableNode::SimpleTexture:
+ m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
+ break;
+ case RenderableNode::Image:
+ m_handle.imageNode = static_cast<ImageNode*>(node);
+ break;
+ case RenderableNode::Painter:
+ m_handle.painterNode = static_cast<PainterNode*>(node);
+ break;
+ case RenderableNode::Rectangle:
+ m_handle.rectangleNode = static_cast<RectangleNode*>(node);
+ break;
+ case RenderableNode::Glyph:
+ m_handle.glpyhNode = static_cast<GlyphNode*>(node);
+ break;
+ case RenderableNode::NinePatch:
+ m_handle.ninePatchNode = static_cast<NinePatchNode*>(node);
+ break;
+ case RenderableNode::Invalid:
+ m_handle.simpleRectNode = nullptr;
+ break;
+ }
+}
+
+RenderableNode::~RenderableNode()
+{
+
+}
+
+void RenderableNode::update()
+{
+ // Update the Node properties
+ m_isDirty = true;
+
+ QRect boundingRect;
+
+ switch (m_nodeType) {
+ case RenderableNode::SimpleRect:
+ if (m_handle.simpleRectNode->color().alpha() == 255 && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleRectNode->rect().toRect();
+ break;
+ case RenderableNode::SimpleTexture:
+ if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleTextureNode->rect().toRect();
+ break;
+ case RenderableNode::Image:
+ // There isn't a way to tell, so assume it's not
+ m_isOpaque = false;
+
+ boundingRect = m_handle.imageNode->rect().toRect();
+ break;
+ case RenderableNode::Painter:
+ if (m_handle.painterNode->opaquePainting() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = QRect(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
+ break;
+ case RenderableNode::Rectangle:
+ if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.rectangleNode->rect().toRect();
+ break;
+ case RenderableNode::Glyph:
+ // Always has alpha
+ m_isOpaque = false;
+
+ boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect();
+ break;
+ case RenderableNode::NinePatch:
+ // Difficult to tell, assume non-opaque
+ m_isOpaque = false;
+
+ boundingRect = m_handle.ninePatchNode->bounds().toRect();
+ break;
+ default:
+ break;
+ }
+
+ m_boundingRect = m_transform.mapRect(boundingRect);
+
+ if (m_clipRect.isValid()) {
+ m_boundingRect = m_boundingRect.intersected(m_clipRect.toRect());
+ }
+
+ // Overrides
+ if (m_opacity < 1.0f)
+ m_isOpaque = false;
+
+ m_dirtyRegion = QRegion(m_boundingRect);
+}
+
+QRegion RenderableNode::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();
+ }
+
+ painter->save();
+ painter->setOpacity(m_opacity);
+
+ // Set clipRegion to m_dirtyRegion (in world coordinates)
+ // as m_dirtyRegion already accounts for clipRegion
+ painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
+
+ painter->setTransform(m_transform, false); //precalculated worldTransform
+ if (forceOpaquePainting || m_isOpaque)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+
+ switch (m_nodeType) {
+ case RenderableNode::SimpleRect:
+ painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color());
+ break;
+ case RenderableNode::SimpleTexture:
+ {
+ QSGTexture *texture = m_handle.simpleTextureNode->texture();
+ if (PixmapTexture *pt = dynamic_cast<PixmapTexture *>(texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, QRectF(0, 0, pm.width(), pm.height()));
+ } 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()));
+ }
+ }
+ break;
+ case RenderableNode::Image:
+ m_handle.imageNode->paint(painter);
+ break;
+ case RenderableNode::Painter:
+ m_handle.painterNode->paint(painter);
+ break;
+ case RenderableNode::Rectangle:
+ m_handle.rectangleNode->paint(painter);
+ break;
+ case RenderableNode::Glyph:
+ m_handle.glpyhNode->paint(painter);
+ break;
+ case RenderableNode::NinePatch:
+ m_handle.ninePatchNode->paint(painter);
+ break;
+ default:
+ break;
+ }
+
+ painter->restore();
+
+ QRegion areaToBeFlushed = m_dirtyRegion;
+ m_previousDirtyRegion = QRegion(m_boundingRect);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+
+ return areaToBeFlushed;
+}
+
+QRect RenderableNode::boundingRect() const
+{
+ // This returns the bounding area of a renderable node in world coordinates
+ return m_boundingRect;
+}
+
+bool RenderableNode::isDirtyRegionEmpty() const
+{
+ return m_dirtyRegion.isEmpty();
+}
+
+void RenderableNode::setTransform(const QTransform &transform)
+{
+ if (m_transform == transform)
+ return;
+ m_transform = transform;
+ update();
+}
+
+void RenderableNode::setClipRect(const QRectF &clipRect)
+{
+ if (m_clipRect == clipRect)
+ return;
+
+ m_clipRect = clipRect;
+ update();
+}
+
+void RenderableNode::setOpacity(float opacity)
+{
+ if (qFuzzyCompare(m_opacity, opacity))
+ return;
+
+ m_opacity = opacity;
+ update();
+}
+
+void RenderableNode::markGeometryDirty()
+{
+ update();
+}
+
+void RenderableNode::markMaterialDirty()
+{
+ update();
+}
+
+void RenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
+{
+ // Check if the dirty region applys to this node
+ QRegion prev = m_dirtyRegion;
+ if (dirtyRegion.intersects(boundingRect())) {
+ if (forceDirty)
+ m_isDirty = true;
+ m_dirtyRegion += dirtyRegion.intersected(boundingRect());
+ }
+ qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
+}
+
+void RenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
+{
+ QRegion prev = m_dirtyRegion;
+ if (m_isDirty) {
+ // Check if this rect concerns us
+ if (dirtyRegion.intersects(QRegion(boundingRect()))) {
+ m_dirtyRegion -= dirtyRegion;
+ if (m_dirtyRegion.isEmpty())
+ m_isDirty = false;
+ }
+ }
+ qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion;
+}
+
+QRegion RenderableNode::previousDirtyRegion() const
+{
+ return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect));
+}
+
+QRegion RenderableNode::dirtyRegion() const
+{
+ return m_dirtyRegion;
+}
+
+} // namespace
diff --git a/src/plugins/scenegraph/softwarecontext/renderablenode.h b/src/plugins/scenegraph/softwarecontext/renderablenode.h
new file mode 100644
index 0000000..4c6fdc7
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderablenode.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef RENDERABLENODE_H
+#define RENDERABLENODE_H
+
+#include <QtGui/QRegion>
+#include <QtCore/QRect>
+#include <QtGui/QTransform>
+
+class QSGNode;
+class QSGSimpleRectNode;
+class QSGSimpleTextureNode;
+class ImageNode;
+class PainterNode;
+class RectangleNode;
+class GlyphNode;
+class NinePatchNode;
+
+namespace SoftwareContext{
+
+class RenderableNode
+{
+public:
+ enum NodeType {
+ Invalid = -1,
+ SimpleRect,
+ SimpleTexture,
+ Image,
+ Painter,
+ Rectangle,
+ Glyph,
+ NinePatch
+ };
+
+ RenderableNode(NodeType type, QSGNode *node);
+ ~RenderableNode();
+
+ void update();
+
+ QRegion renderNode(QPainter *painter, bool forceOpaquePainting = false);
+ QRect boundingRect() const;
+ NodeType type() const { return m_nodeType; }
+ bool isOpaque() const { return m_isOpaque; }
+ bool isDirty() const { return m_isDirty; }
+ bool isDirtyRegionEmpty() const;
+
+ void setTransform(const QTransform &transform);
+ void setClipRect(const QRectF &clipRect);
+ void setOpacity(float opacity);
+ QTransform transform() const { return m_transform; }
+ QRectF clipRect() const { return m_clipRect; }
+ float opacity() const { return m_opacity; }
+
+ void markGeometryDirty();
+ void markMaterialDirty();
+
+ void addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty = true);
+ void subtractDirtyRegion(const QRegion &dirtyRegion);
+
+ QRegion previousDirtyRegion() const;
+ QRegion dirtyRegion() const;
+
+private:
+ union RenderableNodeHandle {
+ QSGSimpleRectNode *simpleRectNode;
+ QSGSimpleTextureNode *simpleTextureNode;
+ ImageNode *imageNode;
+ PainterNode *painterNode;
+ RectangleNode *rectangleNode;
+ GlyphNode *glpyhNode;
+ NinePatchNode *ninePatchNode;
+ };
+
+ const NodeType m_nodeType;
+ RenderableNodeHandle m_handle;
+
+ bool m_isOpaque;
+
+ bool m_isDirty;
+ QRegion m_dirtyRegion;
+ QRegion m_previousDirtyRegion;
+
+ QTransform m_transform;
+ QRectF m_clipRect;
+ float m_opacity;
+
+ QRect m_boundingRect;
+};
+
+} // namespace
+
+#endif // RENDERABLENODE_H
diff --git a/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp
new file mode 100644
index 0000000..114bd07
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.cpp
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderablenodeupdater.h"
+
+#include "abstractsoftwarerenderer.h"
+#include "imagenode.h"
+#include "rectanglenode.h"
+#include "glyphnode.h"
+#include "ninepatchnode.h"
+#include "painternode.h"
+#include "pixmaptexture.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimpletexturenode.h>
+
+namespace SoftwareContext {
+
+RenderableNodeUpdater::RenderableNodeUpdater(AbstractSoftwareRenderer *renderer)
+ : m_renderer(renderer)
+{
+ m_opacityState.push(1.0f);
+ // Invalid RectF by default for no clip
+ m_clipState.push(QRectF(0.0f, 0.0f, -1.0f, -1.0f));
+ m_transformState.push(QTransform());
+}
+
+RenderableNodeUpdater::~RenderableNodeUpdater()
+{
+
+}
+
+bool RenderableNodeUpdater::visit(QSGTransformNode *node)
+{
+ m_transformState.push(node->matrix().toTransform() * m_transformState.top());
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void RenderableNodeUpdater::endVisit(QSGTransformNode *)
+{
+ m_transformState.pop();
+}
+
+bool RenderableNodeUpdater::visit(QSGClipNode *node)
+{
+ // Make sure to translate the clip rect into world coordinates
+ if (!m_clipState.top().isValid())
+ m_clipState.push(m_transformState.top().mapRect(node->clipRect()));
+ else
+ m_clipState.push(m_transformState.top().mapRect(node->clipRect()).intersected(m_clipState.top()));
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void RenderableNodeUpdater::endVisit(QSGClipNode *)
+{
+ m_clipState.pop();
+}
+
+bool RenderableNodeUpdater::visit(QSGGeometryNode *node)
+{
+ if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) {
+ return updateRenderableNode(RenderableNode::SimpleRect, rectNode);
+ } else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) {
+ return updateRenderableNode(RenderableNode::SimpleTexture, tn);
+ } else {
+ // We dont know, so skip
+ return false;
+ }
+}
+
+void RenderableNodeUpdater::endVisit(QSGGeometryNode *)
+{
+}
+
+bool RenderableNodeUpdater::visit(QSGOpacityNode *node)
+{
+ m_opacityState.push(m_opacityState.top() * node->opacity());
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void RenderableNodeUpdater::endVisit(QSGOpacityNode *)
+{
+ m_opacityState.pop();
+}
+
+bool RenderableNodeUpdater::visit(QSGImageNode *node)
+{
+ return updateRenderableNode(RenderableNode::Image, node);
+}
+
+void RenderableNodeUpdater::endVisit(QSGImageNode *)
+{
+}
+
+bool RenderableNodeUpdater::visit(QSGPainterNode *node)
+{
+ return updateRenderableNode(RenderableNode::Painter, node);
+}
+
+void RenderableNodeUpdater::endVisit(QSGPainterNode *)
+{
+}
+
+bool RenderableNodeUpdater::visit(QSGRectangleNode *node)
+{
+ return updateRenderableNode(RenderableNode::Rectangle, node);
+}
+
+void RenderableNodeUpdater::endVisit(QSGRectangleNode *)
+{
+}
+
+bool RenderableNodeUpdater::visit(QSGGlyphNode *node)
+{
+ return updateRenderableNode(RenderableNode::Glyph, node);
+}
+
+void RenderableNodeUpdater::endVisit(QSGGlyphNode *)
+{
+}
+
+bool RenderableNodeUpdater::visit(QSGNinePatchNode *node)
+{
+ return updateRenderableNode(RenderableNode::NinePatch, node);
+}
+
+void RenderableNodeUpdater::endVisit(QSGNinePatchNode *)
+{
+}
+
+bool RenderableNodeUpdater::visit(QSGRootNode *node)
+{
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void RenderableNodeUpdater::endVisit(QSGRootNode *)
+{
+}
+
+void RenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRemoved)
+{
+ m_opacityState.clear();
+ m_clipState.clear();
+ m_transformState.clear();
+
+ auto parentNode = node->parent();
+ // If the node was deleted, it will have no parent
+ // check if the state map has the previous parent
+ if ((!parentNode || isNodeRemoved ) && m_stateMap.contains(node))
+ parentNode = m_stateMap[node].parent;
+
+ // If we find a parent, use its state for updating the new children
+ if (parentNode && m_stateMap.contains(parentNode)) {
+ auto state = m_stateMap[parentNode];
+ m_opacityState.push(state.opacity);
+ m_transformState.push(state.transform);
+ m_clipState.push(state.clip);
+
+ } 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(QRectF(0.0f, 0.0f, -1.0f, -1.0f));
+ }
+
+ // If the node is being removed, then cleanup the state data
+ // Then just visit the children without visiting the now removed node
+ if (isNodeRemoved) {
+ m_stateMap.remove(node);
+ return;
+ }
+
+ // Visit the current node itself first
+ switch (node->type()) {
+ case QSGNode::ClipNodeType: {
+ QSGClipNode *c = static_cast<QSGClipNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::TransformNodeType: {
+ QSGTransformNode *c = static_cast<QSGTransformNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::OpacityNodeType: {
+ QSGOpacityNode *c = static_cast<QSGOpacityNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::GeometryNodeType: {
+ if (node->flags() & QSGNode::IsVisitableNode) {
+ QSGVisitableNode *v = static_cast<QSGVisitableNode*>(node);
+ v->accept(this);
+ } else {
+ QSGGeometryNode *c = static_cast<QSGGeometryNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ }
+ break;
+ }
+ case QSGNode::RootNodeType: {
+ QSGRootNode *root = static_cast<QSGRootNode*>(node);
+ if (visit(root))
+ visitChildren(root);
+ endVisit(root);
+ break;
+ }
+ case QSGNode::BasicNodeType: {
+ visitChildren(node);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+RenderableNodeUpdater::NodeState RenderableNodeUpdater::currentState(QSGNode *node) const
+{
+ NodeState state;
+ state.opacity = m_opacityState.top();
+ state.clip = m_clipState.top();
+ state.transform = m_transformState.top();
+ state.parent = node->parent();
+ return state;
+}
+
+} // namespace
diff --git a/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h
new file mode 100644
index 0000000..15e7786
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderablenodeupdater.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef RENDERABLENODEUPDATER_H
+#define RENDERABLENODEUPDATER_H
+
+#include "renderablenode.h"
+#include "abstractsoftwarerenderer.h"
+
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QTransform>
+#include <QStack>
+#include <QRectF>
+
+namespace SoftwareContext {
+
+class RenderableNodeUpdater : public QSGNodeVisitorEx
+{
+public:
+ RenderableNodeUpdater(AbstractSoftwareRenderer *renderer);
+ virtual ~RenderableNodeUpdater();
+
+ bool visit(QSGTransformNode *) override;
+ void endVisit(QSGTransformNode *) override;
+ bool visit(QSGClipNode *) override;
+ void endVisit(QSGClipNode *) override;
+ bool visit(QSGGeometryNode *) override;
+ void endVisit(QSGGeometryNode *) override;
+ bool visit(QSGOpacityNode *) override;
+ void endVisit(QSGOpacityNode *) override;
+ bool visit(QSGImageNode *) override;
+ void endVisit(QSGImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGRectangleNode *) override;
+ void endVisit(QSGRectangleNode *) 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;
+
+ void updateNodes(QSGNode *node, bool isNodeRemoved = false);
+
+private:
+ struct NodeState {
+ float opacity;
+ QRectF clip;
+ QTransform transform;
+ QSGNode *parent;
+ };
+
+ NodeState currentState(QSGNode *node) const;
+
+ template<class NODE>
+ bool updateRenderableNode(RenderableNode::NodeType type, NODE *node);
+
+ AbstractSoftwareRenderer *m_renderer;
+ QStack<float> m_opacityState;
+ QStack<QRectF> m_clipState;
+ QStack<QTransform> m_transformState;
+ QHash<QSGNode*,NodeState> m_stateMap;
+};
+
+template<class NODE>
+bool RenderableNodeUpdater::updateRenderableNode(RenderableNode::NodeType type, NODE *node)
+{
+ //Check if we already know about node
+ auto renderableNode = m_renderer->renderableNode(node);
+ if (renderableNode == nullptr) {
+ renderableNode = new RenderableNode(type, node);
+ m_renderer->addNodeMapping(node, renderableNode);
+ }
+
+ //Update the node
+ renderableNode->setTransform(m_transformState.top());
+ renderableNode->setOpacity(m_opacityState.top());
+ renderableNode->setClipRect(m_clipState.top());
+
+ renderableNode->update();
+ m_stateMap[node] = currentState(node);
+
+ return true;
+}
+
+} // namespace
+
+#endif // RENDERABLENODEUPDATER_H
diff --git a/src/plugins/scenegraph/softwarecontext/renderer.cpp b/src/plugins/scenegraph/softwarecontext/renderer.cpp
new file mode 100644
index 0000000..316f93a
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderer.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderer.h"
+
+#include "renderablenodeupdater.h"
+#include "renderlistbuilder.h"
+#include "context.h"
+#include "renderablenode.h"
+
+#include <QtGui/QWindow>
+#include <QtQuick/QSGSimpleRectNode>
+
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer")
+
+namespace SoftwareContext {
+
+Renderer::Renderer(QSGRenderContext *context)
+ : AbstractSoftwareRenderer(context)
+{
+}
+
+Renderer::~Renderer()
+{
+}
+
+void Renderer::renderScene(GLuint)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void Renderer::render()
+{
+ QElapsedTimer renderTimer;
+
+ QWindow *currentWindow = static_cast<RenderContext*>(m_context)->currentWindow;
+ if (!m_backingStore)
+ m_backingStore.reset(new QBackingStore(currentWindow));
+
+ if (m_backingStore->size() != currentWindow->size()) {
+ m_backingStore->resize(currentWindow->size());
+ }
+
+ setBackgroundColor(clearColor());
+ setBackgroundSize(currentWindow->size());
+
+ const QRect rect(0, 0, currentWindow->width(), currentWindow->height());
+ m_backingStore->beginPaint(rect);
+
+ QPaintDevice *device = m_backingStore->paintDevice();
+#ifndef QTQUICK2D_DEBUG_FLUSH
+ QPainter painter(device);
+#else
+ if (m_outputBuffer.size() != m_backingStore->size()) {
+ m_outputBuffer = QImage(m_backingStore->size(), QImage::Format_ARGB32_Premultiplied);
+ m_outputBuffer.fill(Qt::transparent);
+ }
+ QPainter painter(&m_outputBuffer);
+#endif
+ 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
+ // and append it to the renderlist. At this point the RenderableNode object
+ // should not need any further updating so it is just a matter of appending
+ // RenderableNodes
+ renderTimer.start();
+ buildRenderList();
+ qint64 buildRenderListTime = renderTimer.restart();
+
+ // Optimize Renderlist
+ // This is a pass through the renderlist to determine what actually needs to
+ // be painted. Without this pass the renderlist will simply render each item
+ // from back to front, with a high potential for overdraw. It would also lead
+ // to the entire window being flushed every frame. The objective of the
+ // optimization pass is to only paint dirty nodes that are not occuluded. A
+ // 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();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ // Render the contents Renderlist
+ QRegion dirtyRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ qCDebug(lcRenderer) << "render" << dirtyRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
+
+#ifdef QTQUICK2D_DEBUG_FLUSH
+ // Keep up with the last 5 flushes
+ if (m_previousFlushes.count() == 5)
+ m_previousFlushes.pop_front();
+ m_previousFlushes.append(dirtyRegion);
+
+ QPainter backingStorePainter(device);
+ backingStorePainter.drawImage(QRect(0, 0, m_backingStore->size().width(), m_backingStore->size().height()), m_outputBuffer, m_outputBuffer.rect());
+ QPen pen(Qt::NoPen);
+ QBrush brush(QColor(255, 0, 0, 50));
+ backingStorePainter.setPen(pen);
+ backingStorePainter.setBrush(brush);
+ for (auto region : qAsConst(m_previousFlushes)) {
+ backingStorePainter.drawRects(region.rects());
+ }
+ m_backingStore->endPaint();
+
+ m_backingStore->flush(rect);
+#else
+ m_backingStore->endPaint();
+ // Flush the updated regions to the window
+ m_backingStore->flush(dirtyRegion);
+#endif
+}
+
+} // namespace
diff --git a/src/plugins/scenegraph/softwarecontext/renderer.h b/src/plugins/scenegraph/softwarecontext/renderer.h
new file mode 100644
index 0000000..d26f0f1
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderer.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef RENDERER_H
+#define RENDERER_H
+
+#include "abstractsoftwarerenderer.h"
+
+#include <QtGui/QBackingStore>
+
+#ifdef QTQUICK2D_DEBUG_FLUSH
+#include <QtGui/QImage>
+#endif
+
+class QSGSimpleRectNode;
+
+namespace SoftwareContext{
+
+class Renderer : public AbstractSoftwareRenderer
+{
+public:
+ Renderer(QSGRenderContext *context);
+ virtual ~Renderer();
+
+ QBackingStore *backingStore() const { return m_backingStore.data(); }
+
+protected:
+ void renderScene(GLuint fboId = 0) final;
+ void render() final;
+
+private:
+ QScopedPointer<QBackingStore> m_backingStore;
+
+#ifdef QTQUICK2D_DEBUG_FLUSH
+ QVector<QRegion> m_previousFlushes;
+ QImage m_outputBuffer;
+#endif
+};
+
+} // namespace
+
+#endif // RENDERER_H
diff --git a/src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp b/src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp
deleted file mode 100644
index 04ec331..0000000
--- a/src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL$
-** 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 General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) 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.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-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "renderingvisitor.h"
-
-#include "imagenode.h"
-#include "rectanglenode.h"
-#include "glyphnode.h"
-#include "ninepatchnode.h"
-#include "painternode.h"
-#include "pixmaptexture.h"
-
-#include <QtQuick/QSGSimpleRectNode>
-#include <QtQuick/qsgsimpletexturenode.h>
-#include <private/qsgtexture_p.h>
-#include <private/qquickshadereffectnode_p.h>
-
-RenderingVisitor::RenderingVisitor(QPainter *painter)
- : painter(painter)
-{
-
-}
-
-bool RenderingVisitor::visit(QSGTransformNode *node)
-{
- painter->save();
- painter->setTransform(node->matrix().toTransform(), /*combine*/true);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGTransformNode *)
-{
- painter->restore();
-}
-
-bool RenderingVisitor::visit(QSGClipNode *node)
-{
- painter->save();
- painter->setClipRect(node->clipRect(), Qt::IntersectClip);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGClipNode *)
-{
- painter->restore();
-}
-
-bool RenderingVisitor::visit(QSGGeometryNode *node)
-{
- if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) {
- if (!(rectNode->material()->flags() & QSGMaterial::Blending))
- painter->setCompositionMode(QPainter::CompositionMode_Source);
- painter->fillRect(rectNode->rect(), rectNode->color());
- painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
- } else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) {
- QSGTexture *texture = tn->texture();
- if (PixmapTexture *pt = dynamic_cast<PixmapTexture *>(texture)) {
- const QPixmap &pm = pt->pixmap();
-#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
- painter->drawPixmap(tn->rect(), pm, tn->sourceRect());
-#else
- painter->drawPixmap(tn->rect(), pm, QRectF(0, 0, pm.width(), pm.height()));
-#endif
- } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) {
- const QImage &im = pt->image();
- painter->drawImage(tn->rect(), im, QRectF(0, 0, im.width(), im.height()));
- } else {
- //Do nothing
- }
- } else if (QQuickShaderEffectNode *sn = dynamic_cast<QQuickShaderEffectNode *>(node)) {
- Q_UNUSED(sn)
- } else {
- //Do nothing
- }
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGGeometryNode *)
-{
-}
-
-bool RenderingVisitor::visit(QSGOpacityNode *node)
-{
- painter->save();
-
- const qreal newOpacity = painter->opacity() * node->opacity();
- if (qFuzzyIsNull(newOpacity))
- return false;
-
- painter->setOpacity(newOpacity);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGOpacityNode *)
-{
- painter->restore();
-}
-
-bool RenderingVisitor::visit(QSGImageNode *node)
-{
- static_cast<ImageNode*>(node)->paint(painter);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGImageNode *)
-{
-}
-
-bool RenderingVisitor::visit(QSGPainterNode *node)
-{
- static_cast<PainterNode*>(node)->paint(painter);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGPainterNode *)
-{
-}
-
-bool RenderingVisitor::visit(QSGRectangleNode *node)
-{
- static_cast<RectangleNode*>(node)->paint(painter);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGRectangleNode *)
-{
-}
-
-bool RenderingVisitor::visit(QSGGlyphNode *node)
-{
- static_cast<GlyphNode*>(node)->paint(painter);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGGlyphNode *)
-{
-}
-
-bool RenderingVisitor::visit(QSGNinePatchNode *node)
-{
- static_cast<NinePatchNode*>(node)->paint(painter);
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGNinePatchNode *)
-{
-}
-
-bool RenderingVisitor::visit(QSGRootNode *)
-{
- return true;
-}
-
-void RenderingVisitor::endVisit(QSGRootNode *)
-{
-}
diff --git a/src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp
new file mode 100644
index 0000000..e002a77
--- /dev/null
+++ b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderlistbuilder.h"
+
+#include "renderablenode.h"
+#include "abstractsoftwarerenderer.h"
+#include "imagenode.h"
+#include "rectanglenode.h"
+#include "glyphnode.h"
+#include "ninepatchnode.h"
+#include "painternode.h"
+#include "pixmaptexture.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimpletexturenode.h>
+
+namespace SoftwareContext {
+
+
+RenderListBuilder::RenderListBuilder(AbstractSoftwareRenderer *renderer)
+ : m_renderer(renderer)
+{
+
+}
+
+bool RenderListBuilder::visit(QSGTransformNode *)
+{
+ return true;
+}
+
+void RenderListBuilder::endVisit(QSGTransformNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGClipNode *)
+{
+ return true;
+}
+
+void RenderListBuilder::endVisit(QSGClipNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGGeometryNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void RenderListBuilder::endVisit(QSGGeometryNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGOpacityNode *)
+{
+ return true;
+}
+
+void RenderListBuilder::endVisit(QSGOpacityNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGImageNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void RenderListBuilder::endVisit(QSGImageNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGPainterNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void RenderListBuilder::endVisit(QSGPainterNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGRectangleNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void RenderListBuilder::endVisit(QSGRectangleNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGGlyphNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void RenderListBuilder::endVisit(QSGGlyphNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGNinePatchNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void RenderListBuilder::endVisit(QSGNinePatchNode *)
+{
+}
+
+bool RenderListBuilder::visit(QSGRootNode *)
+{
+ return true;
+}
+
+void RenderListBuilder::endVisit(QSGRootNode *)
+{
+}
+
+bool RenderListBuilder::addRenderableNode(QSGNode *node)
+{
+ auto renderableNode = m_renderer->renderableNode(node);
+ if (renderableNode == nullptr) {
+ // Not a node we can render
+ return false;
+ }
+ m_renderer->appendRenderableNode(renderableNode);
+ return true;
+}
+
+} // namespace
diff --git a/src/plugins/scenegraph/softwarecontext/renderingvisitor.h b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.h
index 6cded6c..8607adf 100644
--- a/src/plugins/scenegraph/softwarecontext/renderingvisitor.h
+++ b/src/plugins/scenegraph/softwarecontext/renderlistbuilder.h
@@ -27,39 +27,47 @@
**
****************************************************************************/
-#ifndef RENDERINGVISITOR_H
-#define RENDERINGVISITOR_H
+#ifndef RENDERLISTBUILDER_H
+#define RENDERLISTBUILDER_H
#include <private/qsgadaptationlayer_p.h>
-class RenderingVisitor : public QSGNodeVisitorEx
+namespace SoftwareContext {
+
+class AbstractSoftwareRenderer;
+
+class RenderListBuilder : public QSGNodeVisitorEx
{
public:
- RenderingVisitor(QPainter *painter);
+ RenderListBuilder(AbstractSoftwareRenderer *renderer);
- bool visit(QSGTransformNode *node) override;
+ bool visit(QSGTransformNode *) override;
void endVisit(QSGTransformNode *) override;
- bool visit(QSGClipNode *node) override;
- void endVisit(QSGClipNode *node) override;
- bool visit(QSGGeometryNode *node) override;
- void endVisit(QSGGeometryNode *node) override;
- bool visit(QSGOpacityNode *node) override;
- void endVisit(QSGOpacityNode *node) override;
- bool visit(QSGImageNode *node) override;
- void endVisit(QSGImageNode *node) override;
- bool visit(QSGPainterNode *node) override;
+ bool visit(QSGClipNode *) override;
+ void endVisit(QSGClipNode *) override;
+ bool visit(QSGGeometryNode *) override;
+ void endVisit(QSGGeometryNode *) override;
+ bool visit(QSGOpacityNode *) override;
+ void endVisit(QSGOpacityNode *) override;
+ bool visit(QSGImageNode *) override;
+ void endVisit(QSGImageNode *) override;
+ bool visit(QSGPainterNode *) override;
void endVisit(QSGPainterNode *) override;
- bool visit(QSGRectangleNode *node) override;
- void endVisit(QSGRectangleNode *node) override;
- bool visit(QSGGlyphNode *node) override;
- void endVisit(QSGGlyphNode *node) override;
- bool visit(QSGNinePatchNode *node) override;
+ bool visit(QSGRectangleNode *) override;
+ void endVisit(QSGRectangleNode *) 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;
private:
- QPainter *painter;
+ bool addRenderableNode(QSGNode *node);
+
+ AbstractSoftwareRenderer *m_renderer;
};
-#endif // RENDERINGVISITOR_H
+}
+
+#endif // RENDERLISTBUILDER_H
diff --git a/src/plugins/scenegraph/softwarecontext/softwarecontext.pro b/src/plugins/scenegraph/softwarecontext/softwarecontext.pro
index cc0acfe..603910b 100644
--- a/src/plugins/scenegraph/softwarecontext/softwarecontext.pro
+++ b/src/plugins/scenegraph/softwarecontext/softwarecontext.pro
@@ -9,6 +9,9 @@ load(qt_plugin)
QMAKE_TARGET_PRODUCT = "Qt Quick 2D Renderer (Qt $$QT_VERSION)"
QMAKE_TARGET_DESCRIPTION = "Quick 2D Renderer for Qt."
+
+#DEFINES += QTQUICK2D_DEBUG_FLUSH
+
SOURCES += \
context.cpp \
pluginmain.cpp \
@@ -17,11 +20,15 @@ SOURCES += \
imagenode.cpp \
pixmaptexture.cpp \
glyphnode.cpp \
- renderingvisitor.cpp \
ninepatchnode.cpp \
softwarelayer.cpp \
- threadedrenderloop.cpp \
- painternode.cpp
+ painternode.cpp \
+ renderablenode.cpp \
+ renderer.cpp \
+ pixmaprenderer.cpp \
+ renderablenodeupdater.cpp \
+ renderlistbuilder.cpp \
+ abstractsoftwarerenderer.cpp
HEADERS += \
context.h \
@@ -31,11 +38,15 @@ HEADERS += \
imagenode.h \
pixmaptexture.h \
glyphnode.h \
- renderingvisitor.h \
ninepatchnode.h \
softwarelayer.h \
- threadedrenderloop.h \
- painternode.h
+ painternode.h \
+ renderablenode.h \
+ renderer.h \
+ pixmaprenderer.h \
+ renderablenodeupdater.h \
+ renderlistbuilder.h \
+ abstractsoftwarerenderer.h
OTHER_FILES += softwarecontext.json
diff --git a/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp b/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp
index 712bb6f..7064bc8 100644
--- a/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp
+++ b/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp
@@ -30,6 +30,7 @@
#include "softwarelayer.h"
#include "context.h"
+#include "pixmaprenderer.h"
SoftwareLayer::SoftwareLayer(QSGRenderContext *renderContext)
: m_item(0)
@@ -216,6 +217,9 @@ void SoftwareLayer::grab()
if (m_pixmap.size() != m_size) {
m_pixmap = QPixmap(m_size);
m_pixmap.setDevicePixelRatio(m_device_pixel_ratio);
+ // This fill here is wasteful, but necessary because it is the only way
+ // to force a QImage based pixmap to have an alpha channel.
+ m_pixmap.fill(Qt::transparent);
}
// Render texture.
@@ -230,7 +234,7 @@ void SoftwareLayer::grab()
m_mirrorVertical ? m_rect.top() * m_device_pixel_ratio : m_rect.bottom() * m_device_pixel_ratio,
m_mirrorHorizontal ? -m_rect.width() * m_device_pixel_ratio : m_rect.width() * m_device_pixel_ratio,
m_mirrorVertical ? m_rect.height() * m_device_pixel_ratio : -m_rect.height() * m_device_pixel_ratio);
- m_renderer->m_projectionRect = mirrored;
+ m_renderer->setProjectionRect(mirrored);
m_renderer->setClearColor(Qt::transparent);
m_renderer->renderScene();
diff --git a/src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp b/src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp
deleted file mode 100644
index cd6f1aa..0000000
--- a/src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp
+++ /dev/null
@@ -1,1185 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL$
-** 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 General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) 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.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-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "threadedrenderloop.h"
-
-#include <QtCore/QCoreApplication>
-#include <QtCore/QMutex>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QAnimationDriver>
-#include <QtCore/QQueue>
-#include <QtCore/QTime>
-
-#include <QtGui/QGuiApplication>
-#include <QtGui/QScreen>
-#include <QtGui/QOffscreenSurface>
-
-#include <qpa/qwindowsysteminterface.h>
-#include <qpa/qplatformbackingstore.h>
-
-#include <QtQuick/QQuickWindow>
-#include <private/qquickwindow_p.h>
-
-#include <QtQuick/private/qsgrenderer_p.h>
-
-#include <private/qquickanimatorcontroller_p.h>
-
-#include <private/qquickprofiler_p.h>
-#include <private/qqmldebugserviceinterfaces_p.h>
-#include <private/qqmldebugconnector_p.h>
-#include "context.h"
-
-/*
- Overall design:
-
- There are two classes here. ThreadedRenderLoop and
- RenderThread. All communication between the two is based on
- event passing and we have a number of custom events.
-
- In this implementation, the render thread is never blocked and the
- GUI thread will initiate a polishAndSync which will block and wait
- for the render thread to pick it up and release the block only
- after the render thread is done syncing. The reason for this
- is:
-
- 1. Clear blocking paradigm. We only have one real "block" point
- (polishAndSync()) and all blocking is initiated by GUI and picked
- up by Render at specific times based on events. This makes the
- execution deterministic.
-
- 2. Render does not have to interact with GUI. This is done so that
- the render thread can run its own animation system which stays
- alive even when the GUI thread is blocked doing i/o, object
- instantiation, QPainter-painting or any other non-trivial task.
-
- ---
-
- There is one thread per window and one opengl context per thread.
-
- ---
-
- The render thread has affinity to the GUI thread until a window
- is shown. From that moment and until the window is destroyed, it
- will have affinity to the render thread. (moved back at the end
- of run for cleanup).
-
- ---
-
- The render loop is active while any window is exposed. All visible
- windows are tracked, but only exposed windows are actually added to
- the render thread and rendered. That means that if all windows are
- obscured, we might end up cleaning up the SG and GL context (if all
- windows have disabled persistency). Especially for multiprocess,
- low-end systems, this should be quite important.
-
- */
-
-QT_BEGIN_NAMESPACE
-
-#define QSG_RT_PAD " (RT)"
-
-static inline int qsgrl_animation_interval() {
- qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate();
- // To work around that some platforms wrongfully return 0 or something
- // bogus for refreshrate
- if (refreshRate < 1)
- return 16;
- return int(1000 / refreshRate);
-}
-
-
-static QElapsedTimer threadTimer;
-static qint64 syncTime;
-static qint64 renderTime;
-static qint64 sinceLastTime;
-
-extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
-
-// RL: Render Loop
-// RT: Render Thread
-
-// Passed from the RL to the RT when a window is 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
-// (updatePaintNode())
-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 free up maybe release SG and GL contexts
-// 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);
-
-template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window)
-{
- for (int i=0; i<list.size(); ++i) {
- const T &t = list.at(i);
- if (t.window == window)
- return const_cast<T *>(&t);
- }
- return 0;
-}
-
-
-class WMWindowEvent : public QEvent
-{
-public:
- WMWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
- QQuickWindow *window;
-};
-
-class WMTryReleaseEvent : public WMWindowEvent
-{
-public:
- WMTryReleaseEvent(QQuickWindow *win, bool destroy, QOffscreenSurface *fallback)
- : WMWindowEvent(win, WM_TryRelease)
- , inDestructor(destroy)
- , fallbackSurface(fallback)
- {}
-
- bool inDestructor;
- QOffscreenSurface *fallbackSurface;
-};
-
-class WMSyncEvent : public WMWindowEvent
-{
-public:
- WMSyncEvent(QQuickWindow *c, bool inExpose, bool force)
- : WMWindowEvent(c, WM_RequestSync)
- , size(c->size())
- , syncInExpose(inExpose)
- , forceRenderPass(force)
- {}
- QSize size;
- bool syncInExpose;
- bool forceRenderPass;
-};
-
-
-class WMGrabEvent : public WMWindowEvent
-{
-public:
- WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {}
- QImage *image;
-};
-
-class WMJobEvent : public WMWindowEvent
-{
-public:
- WMJobEvent(QQuickWindow *c, QRunnable *postedJob)
- : WMWindowEvent(c, WM_PostJob), job(postedJob) {}
- ~WMJobEvent() { delete job; }
- QRunnable *job;
-};
-
-class RenderThreadEventQueue : public QQueue<QEvent *>
-{
-public:
- RenderThreadEventQueue()
- : waiting(false)
- {
- }
-
- void addEvent(QEvent *e) {
- mutex.lock();
- enqueue(e);
- if (waiting)
- condition.wakeOne();
- mutex.unlock();
- }
-
- QEvent *takeEvent(bool wait) {
- mutex.lock();
- if (size() == 0 && wait) {
- waiting = true;
- condition.wait(&mutex);
- waiting = false;
- }
- QEvent *e = dequeue();
- mutex.unlock();
- return e;
- }
-
- bool hasMoreEvents() {
- mutex.lock();
- bool has = !isEmpty();
- mutex.unlock();
- return has;
- }
-
-private:
- QMutex mutex;
- QWaitCondition condition;
- bool waiting;
-};
-
-
-class RenderThread : public QThread
-{
- Q_OBJECT
-public:
- RenderThread(ThreadedRenderLoop *w, QSGRenderContext *renderContext)
- : wm(w)
- , sgrc(renderContext)
- , animatorDriver(0)
- , pendingUpdate(0)
- , sleeping(false)
- , syncResultedInChanges(false)
- , active(false)
- , window(0)
- , stopEventProcessing(false)
- {
-#if defined(Q_OS_QNX) && !defined(Q_OS_BLACKBERRY) && defined(Q_PROCESSOR_X86)
- // The SDP 6.6.0 x86 MESA driver requires a larger stack than the default.
- setStackSize(1024 * 1024);
-#endif
- vsyncDelta = qsgrl_animation_interval();
- }
-
- ~RenderThread()
- {
- delete sgrc;
- }
-
- bool event(QEvent *);
- void run();
-
- void syncAndRender();
- void sync(bool inExpose);
-
- void requestRepaint()
- {
- if (sleeping)
- stopEventProcessing = true;
- if (window)
- pendingUpdate |= RepaintRequest;
- }
-
- void processEventsAndWaitForMore();
- void processEvents();
- void postEvent(QEvent *e);
-
-public slots:
- void sceneGraphChanged() {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "sceneGraphChanged";
- syncResultedInChanges = true;
- }
-
-public:
- enum UpdateRequest {
- SyncRequest = 0x01,
- RepaintRequest = 0x02,
- ExposeRequest = 0x04 | RepaintRequest | SyncRequest
- };
-
- ThreadedRenderLoop *wm;
- QSGRenderContext *sgrc;
-
- QAnimationDriver *animatorDriver;
-
- uint pendingUpdate;
- bool sleeping;
- bool syncResultedInChanges;
-
- volatile bool active;
-
- float vsyncDelta;
-
- QMutex mutex;
- QWaitCondition waitCondition;
-
- QElapsedTimer m_timer;
-
- QQuickWindow *window; // Will be 0 when window is not exposed
- QSize windowSize;
-
- // Local event queue stuff...
- bool stopEventProcessing;
- RenderThreadEventQueue eventQueue;
-};
-
-bool RenderThread::event(QEvent *e)
-{
- switch ((int) e->type()) {
-
- case WM_Obscure: {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Obscure";
-
- Q_ASSERT(!window || window == static_cast<WMWindowEvent *>(e)->window);
-
- mutex.lock();
- if (window) {
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- d->fireAboutToStop();
- d->cleanupNodesOnShutdown();
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- window removed";
- window = 0;
- }
- waitCondition.wakeOne();
- mutex.unlock();
-
- return true; }
-
- case WM_RequestSync: {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestSync";
- WMSyncEvent *se = static_cast<WMSyncEvent *>(e);
- if (sleeping)
- stopEventProcessing = true;
- window = se->window;
- windowSize = se->size;
-
- pendingUpdate |= SyncRequest;
- if (se->syncInExpose) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- triggered from expose";
- pendingUpdate |= ExposeRequest;
- }
- if (se->forceRenderPass) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- repaint regardless";
- pendingUpdate |= RepaintRequest;
- }
- return true; }
-
- case WM_TryRelease: {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_TryRelease";
- mutex.lock();
- wm->m_lockedForSync = true;
- WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
- if (!window || wme->inDestructor) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- setting exit flag and invalidating OpenGL";
- active = false;
- Q_ASSERT_X(!wme->inDestructor || !active, "RenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down");
- if (sleeping)
- stopEventProcessing = true;
- } else {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- not releasing because window is still active";
- }
- waitCondition.wakeOne();
- wm->m_lockedForSync = false;
- mutex.unlock();
- return true;
- }
-
- case WM_Grab: {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Grab";
- WMGrabEvent *ce = static_cast<WMGrabEvent *>(e);
- Q_ASSERT(ce->window);
- Q_ASSERT(ce->window == window || !window);
- mutex.lock();
- if (window) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync scene graph";
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- static_cast<SoftwareContext::RenderContext*>(d->context)->currentWindow = window;
- d->syncSceneGraph();
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering scene graph";
- QQuickWindowPrivate::get(window)->renderSceneGraph(windowSize);
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- grabbing result";
- *ce->image = static_cast<SoftwareContext::Renderer*>(d->renderer)->backingStore()->handle()->toImage();
- }
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- waking gui to handle result";
- waitCondition.wakeOne();
- mutex.unlock();
- return true;
- }
-
- case WM_PostJob: {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_PostJob";
- WMJobEvent *ce = static_cast<WMJobEvent *>(e);
- Q_ASSERT(ce->window == window);
- if (window) {
- ce->job->run();
- delete ce->job;
- ce->job = 0;
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- job done";
- }
- return true;
- }
-
- case WM_RequestRepaint:
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestPaint";
- // When GUI posts this event, it is followed by a polishAndSync, so we mustn't
- // exit the event loop yet.
- pendingUpdate |= RepaintRequest;
- break;
-
- default:
- break;
- }
- return QThread::event(e);
-}
-
-/*!
- Enters the mutex lock to make sure GUI is blocking and performs
- sync, then wakes GUI.
- */
-void RenderThread::sync(bool inExpose)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "sync()";
- mutex.lock();
-
- Q_ASSERT_X(wm->m_lockedForSync, "RenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
-
- bool current = false;
- if (windowSize.width() > 0 && windowSize.height() > 0)
- current = true;
- if (current) {
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- static_cast<SoftwareContext::RenderContext*>(d->context)->currentWindow = window;
- bool hadRenderer = d->renderer != 0;
- // If the scene graph was touched since the last sync() make sure it sends the
- // changed signal.
- if (d->renderer)
- d->renderer->clearChangedFlag();
- d->syncSceneGraph();
- if (!hadRenderer && d->renderer) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- renderer was created";
- syncResultedInChanges = true;
- connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), 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);
- } else {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- window has bad size, sync aborted";
- }
-
- if (!inExpose) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync complete, waking Gui";
- waitCondition.wakeOne();
- mutex.unlock();
- }
-}
-
-void RenderThread::syncAndRender()
-{
- bool profileFrames = QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled();
- if (profileFrames) {
- sinceLastTime = threadTimer.nsecsElapsed();
- threadTimer.start();
- }
- Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
-
- QElapsedTimer waitTimer;
- waitTimer.start();
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "syncAndRender()";
-
- syncResultedInChanges = false;
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
-
- bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage;
- bool syncRequested = pendingUpdate & SyncRequest;
- bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
- pendingUpdate = 0;
-
- if (syncRequested) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- updatePending, doing sync";
- sync(exposeRequested);
- }
-#ifndef QSG_NO_RENDER_TIMING
- if (profileFrames)
- syncTime = threadTimer.nsecsElapsed();
-#endif
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
-
- if (!syncResultedInChanges && !repaintRequested) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- no changes, render aborted";
- int waitTime = vsyncDelta - (int) waitTimer.elapsed();
- if (waitTime > 0)
- msleep(waitTime);
- return;
- }
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering started";
-
-
- if (animatorDriver->isRunning()) {
- d->animationController->lock();
- animatorDriver->advance();
- d->animationController->unlock();
- }
-
- bool current = false;
- if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0)
- current = true;
- if (current) {
- static_cast<SoftwareContext::RenderContext*>(d->context)->currentWindow = window;
- d->renderSceneGraph(windowSize);
- if (profileFrames)
- renderTime = threadTimer.nsecsElapsed();
- // ### used to be swappBuffers here
- d->fireFrameSwapped();
- } else {
- Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1);
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- window not ready, skipping render";
- }
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering done";
-
- // Though it would be more correct to put this block directly after
- // fireFrameSwapped in the if (current) branch above, we don't do
- // that to avoid blocking the GUI thread in the case where it
- // has started rendering with a bad window, causing makeCurrent to
- // fail or if the window has a bad size.
- if (exposeRequested) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "- wake Gui after initial expose";
- waitCondition.wakeOne();
- mutex.unlock();
- }
-
- qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP,
- "Frame rendered with 'threaded' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)",
- int(threadTimer.elapsed()),
- int((syncTime/1000000)),
- int((renderTime - syncTime) / 1000000),
- int(threadTimer.elapsed() - renderTime / 1000000));
-
-
- Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
-}
-
-
-
-void RenderThread::postEvent(QEvent *e)
-{
- eventQueue.addEvent(e);
-}
-
-
-
-void RenderThread::processEvents()
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEvents()";
- while (eventQueue.hasMoreEvents()) {
- QEvent *e = eventQueue.takeEvent(false);
- event(e);
- delete e;
- }
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEvents()";
-}
-
-void RenderThread::processEventsAndWaitForMore()
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEventsAndWaitForMore()";
- stopEventProcessing = false;
- while (!stopEventProcessing) {
- QEvent *e = eventQueue.takeEvent(true);
- event(e);
- delete e;
- }
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEventsAndWaitForMore()";
-}
-
-void RenderThread::run()
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "run()";
- animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(0);
- animatorDriver->install();
- if (QQmlDebugConnector::service<QQmlProfilerService>())
- QQuickProfiler::registerAnimationCallback();
-
- while (active) {
-
- if (window) {
- static_cast<SoftwareContext::RenderContext*>(sgrc)->initializeIfNeeded();
- syncAndRender();
- }
-
- processEvents();
- QCoreApplication::processEvents();
-
- if (active && (pendingUpdate == 0 || !window)) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "done drawing, sleep...";
- sleeping = true;
- processEventsAndWaitForMore();
- sleeping = false;
- }
- }
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << QSG_RT_PAD << "run() completed";
-
- delete animatorDriver;
- animatorDriver = 0;
-
- sgrc->moveToThread(wm->thread());
- moveToThread(wm->thread());
-}
-
-ThreadedRenderLoop::ThreadedRenderLoop()
- : sg(QSGContext::createDefaultContext())
- , m_animation_timer(0)
-{
-#if defined(QSG_RENDER_LOOP_DEBUG)
- qsgrl_timer.start();
-#endif
-
- m_animation_driver = sg->createAnimationDriver(this);
-
- connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted()));
- connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped()));
-
- m_animation_driver->install();
-}
-
-QSGRenderContext *ThreadedRenderLoop::createRenderContext(QSGContext *sg) const
-{
- return sg->createRenderContext();
-}
-
-void ThreadedRenderLoop::maybePostPolishRequest(Window *w)
-{
- w->window->requestUpdate();
-}
-
-QAnimationDriver *ThreadedRenderLoop::animationDriver() const
-{
- return m_animation_driver;
-}
-
-QSGContext *ThreadedRenderLoop::sceneGraphContext() const
-{
- return sg;
-}
-
-bool ThreadedRenderLoop::anyoneShowing() const
-{
- for (int i=0; i<m_windows.size(); ++i) {
- QQuickWindow *c = m_windows.at(i).window;
- if (c->isVisible() && c->isExposed())
- return true;
- }
- return false;
-}
-
-bool ThreadedRenderLoop::interleaveIncubation() const
-{
- return m_animation_driver->isRunning() && anyoneShowing();
-}
-
-void ThreadedRenderLoop::animationStarted()
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- animationStarted()";
- startOrStopAnimationTimer();
-
- for (int i=0; i<m_windows.size(); ++i)
- maybePostPolishRequest(const_cast<Window *>(&m_windows.at(i)));
-}
-
-void ThreadedRenderLoop::animationStopped()
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- animationStopped()";
- startOrStopAnimationTimer();
-}
-
-
-void ThreadedRenderLoop::startOrStopAnimationTimer()
-{
- int exposedWindows = 0;
- const Window *theOne = 0;
- for (int i=0; i<m_windows.size(); ++i) {
- const Window &w = m_windows.at(i);
- if (w.window->isVisible() && w.window->isExposed()) {
- ++exposedWindows;
- theOne = &w;
- }
- }
-
- if (m_animation_timer != 0 && (exposedWindows == 1 || !m_animation_driver->isRunning())) {
- killTimer(m_animation_timer);
- m_animation_timer = 0;
- // If animations are running, make sure we keep on animating
- if (m_animation_driver->isRunning())
- maybePostPolishRequest(const_cast<Window *>(theOne));
-
- } else if (m_animation_timer == 0 && exposedWindows != 1 && m_animation_driver->isRunning()) {
- m_animation_timer = startTimer(qsgrl_animation_interval());
- }
-}
-
-/*
- Removes this window from the list of tracked windowes in this
- window manager. hide() will trigger obscure, which in turn will
- stop rendering.
-
- This function will be called during QWindow::close() which will
- also destroy the QPlatformWindow so it is important that this
- triggers handleObscurity() and that rendering for that window
- is fully done and over with by the time this function exits.
- */
-
-void ThreadedRenderLoop::hide(QQuickWindow *window)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide()" << window;
-
- if (window->isExposed())
- handleObscurity(windowFor(m_windows, window));
-
- releaseResources(window);
-}
-
-
-/*!
- If the window is first hide it, then perform a complete cleanup
- with releaseResources which will take down the GL context and
- exit the rendering thread.
- */
-void ThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "begin windowDestroyed()" << window;
-
- Window *w = windowFor(m_windows, window);
- if (!w)
- return;
-
- handleObscurity(w);
- releaseResources(w, true);
-
- RenderThread *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;
- }
- }
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "done windowDestroyed()" << window;
-}
-
-
-void ThreadedRenderLoop::exposureChanged(QQuickWindow *window)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposureChanged()" << window;
- if (window->isExposed()) {
- handleExposure(window);
- } else {
- Window *w = windowFor(m_windows, window);
- if (w)
- handleObscurity(w);
- }
-}
-
-/*!
- Will post an event to the render thread that this window should
- start to render.
- */
-void ThreadedRenderLoop::handleExposure(QQuickWindow *window)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure()" << window;
-
- Window *w = windowFor(m_windows, window);
- if (!w) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- adding window to list";
- Window win;
- win.window = window;
- win.actualWindowFormat = window->format();
- win.thread = new RenderThread(this, QQuickWindowPrivate::get(window)->context);
- win.updateDuringSync = false;
- win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
- m_windows << win;
- w = &m_windows.last();
- }
-
- // set this early as we'll be rendering shortly anyway and this avoids
- // specialcasing exposure in polishAndSync.
- w->thread->window = window;
-
- if (w->window->width() <= 0 || w->window->height() <= 0
- || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
-#ifndef QT_NO_DEBUG
- qWarning().noquote().nospace() << "ThreadedRenderLoop: expose event received for window "
- << w->window << " with invalid geometry: " << w->window->geometry()
- << " on " << w->window->screen();
-#endif
- }
-
- // Because we are going to bind a GL context to it, make sure it
- // is created.
- 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";
-
- QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
- if (controller->thread() != w->thread)
- controller->moveToThread(w->thread);
-
- w->thread->active = true;
- if (w->thread->thread() == QThread::currentThread()) {
- w->thread->sgrc->moveToThread(w->thread);
- w->thread->moveToThread(w->thread);
- }
- w->thread->start();
- if (!w->thread->isRunning())
- qFatal("Render thread failed to start, aborting application.");
-
- } else {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- render thread already running";
- }
-
- polishAndSync(w, true);
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- done with handleExposure()";
-
- startOrStopAnimationTimer();
-}
-
-/*!
- This function posts an event to the render thread to remove the window
- from the list of windowses to render.
-
- It also starts up the non-vsync animation tick if no more windows
- are showing.
- */
-void ThreadedRenderLoop::handleObscurity(Window *w)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity()" << w->window;
- if (w->thread->isRunning()) {
- w->thread->mutex.lock();
- w->thread->postEvent(new WMWindowEvent(w->window, WM_Obscure));
- w->thread->waitCondition.wait(&w->thread->mutex);
- w->thread->mutex.unlock();
- }
- startOrStopAnimationTimer();
-}
-
-
-void ThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- polish and sync update request";
- Window *w = windowFor(m_windows, window);
- if (w)
- polishAndSync(w);
-}
-
-void ThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
-{
- Window *w = windowFor(m_windows, window);
- if (w)
- maybeUpdate(w);
-}
-
-/*!
- Called whenever the QML scene has changed. Will post an event to
- ourselves that a sync is needed.
- */
-void ThreadedRenderLoop::maybeUpdate(Window *w)
-{
- if (!QCoreApplication::instance())
- return;
-
- if (!w || !w->thread->isRunning())
- return;
-
- QThread *current = QThread::currentThread();
- if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
- qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
- return;
- }
-
- if (!w || !w->thread->isRunning()) {
- return;
- }
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "update from item" << w->window;
-
- // Call this function from the Gui thread later as startTimer cannot be
- // called from the render thread.
- if (current == w->thread) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- on render thread";
- w->updateDuringSync = true;
- return;
- }
-
- maybePostPolishRequest(w);
-}
-
-/*!
- Called when the QQuickWindow should be explicitly repainted. This function
- can also be called on the render thread when the GUI thread is blocked to
- keep render thread animations alive.
- */
-void ThreadedRenderLoop::update(QQuickWindow *window)
-{
- Window *w = windowFor(m_windows, window);
- if (!w)
- return;
-
- if (w->thread == QThread::currentThread()) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "update on window - on render thread" << w->window;
- w->thread->requestRepaint();
- return;
- }
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "update on window" << w->window;
- // We set forceRenderPass because we want to make sure the QQuickWindow
- // actually does a full render pass after the next sync.
- w->forceRenderPass = true;
- maybeUpdate(w);
-}
-
-
-void ThreadedRenderLoop::releaseResources(QQuickWindow *window)
-{
- Window *w = windowFor(m_windows, window);
- if (w)
- releaseResources(w, false);
-}
-
-/*!
- * Release resources will post an event to the render thread to
- * free up the SG and GL resources and exists the render thread.
- */
-void ThreadedRenderLoop::releaseResources(Window *w, bool inDestructor)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources()" << (inDestructor ? "in destructor" : "in api-call") << w->window;
-
- w->thread->mutex.lock();
- if (w->thread->isRunning() && w->thread->active) {
- QQuickWindow *window = w->window;
-
- // The platform window might have been destroyed before
- // hide/release/windowDestroyed is called, so we need to have a
- // fallback surface to perform the cleanup of the scene graph
- // and the OpenGL resources.
- // QOffscreenSurface must be created on the GUI thread, so we
- // create it here and pass it on to RenderThread::invalidateGL()
- QOffscreenSurface *fallback = 0;
- if (!window->handle()) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- using fallback surface";
- fallback = new QOffscreenSurface();
- fallback->setFormat(w->actualWindowFormat);
- fallback->create();
- }
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- posting release request to render thread";
- w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback));
- w->thread->waitCondition.wait(&w->thread->mutex);
- delete fallback;
-
- // 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) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << " - waiting for render thread to exit" << w->window;
- w->thread->wait();
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << " - render thread finished" << w->window;
- }
- }
- w->thread->mutex.unlock();
-}
-
-
-/* Calls polish on all items, then requests synchronization with the render thread
- * and blocks until that is complete. Returns false if it aborted; otherwise true.
- */
-void ThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
-
- QQuickWindow *window = w->window;
- if (!w->thread || !w->thread->window) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- not exposed, abort";
- return;
- }
-
- // Flush pending touch events.
- QQuickWindowPrivate::get(window)->flushDelayedTouchEvent();
- // The delivery of the event might have caused the window to stop rendering
- w = windowFor(m_windows, window);
- if (!w || !w->thread || !w->thread->window) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- removed after event flushing, abort";
- return;
- }
-
-
- QElapsedTimer timer;
- qint64 polishTime = 0;
- qint64 waitTime = 0;
- qint64 syncTime = 0;
- bool profileFrames = QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled();
- if (profileFrames)
- timer.start();
- Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
-
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- d->polishItems();
-
- if (profileFrames)
- polishTime = timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
-
- w->updateDuringSync = false;
-
- emit window->afterAnimating();
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- lock for sync";
- w->thread->mutex.lock();
- m_lockedForSync = true;
- w->thread->postEvent(new WMSyncEvent(window, inExpose, w->forceRenderPass));
- w->forceRenderPass = false;
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- wait for sync";
- if (profileFrames)
- waitTime = timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
- w->thread->waitCondition.wait(&w->thread->mutex);
- m_lockedForSync = false;
- w->thread->mutex.unlock();
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- unlock after sync";
-
- if (profileFrames)
- syncTime = timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
-
- if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- advancing animations";
- m_animation_driver->advance();
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- animations done..";
- // We need to trigger another sync to keep animations running...
- maybePostPolishRequest(w);
- emit timeToIncubate();
- } else if (w->updateDuringSync) {
- maybePostPolishRequest(w);
- }
-
- qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP()).nospace()
- << "Frame prepared with 'threaded' renderloop"
- << ", polish=" << (polishTime / 1000000)
- << ", lock=" << (waitTime - polishTime) / 1000000
- << ", blockedForSync=" << (syncTime - waitTime) / 1000000
- << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000
- << " - (on Gui thread) " << window;
-
- Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync);
-}
-
-bool ThreadedRenderLoop::event(QEvent *e)
-{
- switch ((int) e->type()) {
-
- case QEvent::Timer: {
- QTimerEvent *te = static_cast<QTimerEvent *>(e);
- if (te->timerId() == m_animation_timer) {
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- ticking non-visual timer";
- m_animation_driver->advance();
- emit timeToIncubate();
- return true;
- }
- }
-
- default:
- break;
- }
-
- return QObject::event(e);
-}
-
-
-
-/*
- Locks down GUI and performs a grab the scene graph, then returns the result.
-
- Since the QML scene could have changed since the last time it was rendered,
- we need to polish and sync the scene graph. This might seem superfluous, but
- - QML changes could have triggered deleteLater() which could have removed
- textures or other objects from the scene graph, causing render to crash.
- - Autotests rely on grab(), setProperty(), grab(), compare behavior.
- */
-
-QImage ThreadedRenderLoop::grab(QQuickWindow *window)
-{
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab()" << window;
-
- Window *w = windowFor(m_windows, window);
- Q_ASSERT(w);
-
- if (!w->thread->isRunning())
- return QImage();
-
- if (!window->handle())
- window->create();
-
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- polishing items";
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- d->polishItems();
-
- QImage result;
- w->thread->mutex.lock();
- m_lockedForSync = true;
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- posting grab event";
- w->thread->postEvent(new WMGrabEvent(window, &result));
- w->thread->waitCondition.wait(&w->thread->mutex);
- m_lockedForSync = false;
- w->thread->mutex.unlock();
- qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "- grab complete";
-
- return result;
-}
-
-/*!
- * Posts a new job event to the render thread.
- * Returns true if posting succeeded.
- */
-void ThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
-{
- Window *w = windowFor(m_windows, window);
- if (w && w->thread && w->thread->window)
- w->thread->postEvent(new WMJobEvent(window, job));
- else
- delete job;
-}
-
-#include "threadedrenderloop.moc"
-
-QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/softwarecontext/threadedrenderloop.h b/src/plugins/scenegraph/softwarecontext/threadedrenderloop.h
deleted file mode 100644
index 5e3366c..0000000
--- a/src/plugins/scenegraph/softwarecontext/threadedrenderloop.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Quick 2D Renderer module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL$
-** 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 General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) 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.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-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef THREADEDRENDERLOOP_H
-#define THREADEDRENDERLOOP_H
-
-#include <private/qsgrenderloop_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class RenderThread;
-
-class ThreadedRenderLoop : public QSGRenderLoop
-{
- Q_OBJECT
-public:
- ThreadedRenderLoop();
-
- void show(QQuickWindow *) override {}
- void hide(QQuickWindow *) override;
-
- void windowDestroyed(QQuickWindow *window) override;
- void exposureChanged(QQuickWindow *window) override;
-
- QImage grab(QQuickWindow *) override;
-
- void update(QQuickWindow *window) override;
- void maybeUpdate(QQuickWindow *window) override;
- void handleUpdateRequest(QQuickWindow *window) override;
-
- QSGContext *sceneGraphContext() const override;
- QSGRenderContext *createRenderContext(QSGContext *) const override;
-
- QAnimationDriver *animationDriver() const override;
-
- void releaseResources(QQuickWindow *window) override;
-
- bool event(QEvent *) override;
- void postJob(QQuickWindow *window, QRunnable *job) override;
-
- bool interleaveIncubation() const override;
-
-public Q_SLOTS:
- void animationStarted();
- void animationStopped();
-
-private:
- struct Window {
- QQuickWindow *window;
- RenderThread *thread;
- QSurfaceFormat actualWindowFormat;
- uint updateDuringSync : 1;
- uint forceRenderPass : 1;
- };
-
- friend class RenderThread;
-
- void releaseResources(Window *window, bool inDestructor);
- bool checkAndResetForceUpdate(QQuickWindow *window);
-
- bool anyoneShowing() const;
- void initialize();
-
- void startOrStopAnimationTimer();
- void maybePostPolishRequest(Window *w);
- void waitForReleaseComplete();
- void polishAndSync(Window *w, bool inExpose = false);
- void maybeUpdate(Window *window);
-
- void handleExposure(QQuickWindow *w);
- void handleObscurity(Window *w);
-
-
- QSGContext *sg;
- QAnimationDriver *m_animation_driver;
- QList<Window> m_windows;
-
- int m_animation_timer;
-
- bool m_lockedForSync;
-};
-
-QT_END_NAMESPACE
-
-#endif // THREADEDRENDERLOOP_H