aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Nichols <andy.nichols@theqtcompany.com>2016-01-03 17:28:51 +0100
committerAndy Nichols <andy.nichols@theqtcompany.com>2016-03-04 13:19:00 +0000
commit6afccbd1e25abd95a9c239e175442b79e12f8c87 (patch)
tree2723c1837c17227568440544149087edcf402917
parent13a6c51f26128015c0a3c335bdae676b46df6ed6 (diff)
Partial update support
This enables partial update support, which leads to only painting into the backingstore what has changed in the scene each frame, and then only flushing the changed regions to the window surface. Change-Id: I2a3c8676bd4539100a4313069f894f157bfb0ea4 Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
-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 0000000000..2963440c41
--- /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 0000000000..e08668f8d8
--- /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 728d5fdac6..3f1d3f921d 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 11b0af9240..42a81757a7 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 9288983a22..d37965a5e6 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 e09ad5862a..c095a66a08 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 6f8dcea91b..e35e8f5eaa 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 582a1867bb..48e45f88e2 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 30cde5b05b..14a19cfd6d 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 0000000000..d054fa9b08
--- /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 0000000000..0890a83817
--- /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 133bf5dd5a..80bcabe62d 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 938cc1a80b..ef9d4fb3d4 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 d660b13b45..565f4a3b9e 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 0000000000..0da19c172a
--- /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 0000000000..4c6fdc765b
--- /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 0000000000..114bd07584
--- /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 0000000000..15e778627d
--- /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 0000000000..316f93ab1c
--- /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 0000000000..d26f0f11a1
--- /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 04ec3318ed..0000000000
--- 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 0000000000..e002a77ce2
--- /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 6cded6c995..8607adfc67 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 cc0acfe1e8..603910b8aa 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 712bb6f647..7064bc854e 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 cd6f1aa41d..0000000000
--- 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 5e3366cce2..0000000000
--- 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