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