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