From ba0d8e93d8525bb459e9b6cb384f2fe0701d5a02 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Mon, 1 Sep 2014 16:49:58 +0200 Subject: Reformat project to be a Qt Module Change-Id: I2fe8df530a687247a9cd7ea12c1d8de79fef506e Reviewed-by: Simon Hausmann --- .qmake.conf | 3 + scenegraph-raster.pro | 1 + software-backend.pro | 3 - softwarecontext/context.cpp | 216 ---- softwarecontext/context.h | 98 -- softwarecontext/glyphnode.cpp | 91 -- softwarecontext/glyphnode.h | 49 - softwarecontext/imagenode.cpp | 423 -------- softwarecontext/imagenode.h | 108 -- softwarecontext/ninepatchnode.cpp | 66 -- softwarecontext/ninepatchnode.h | 45 - softwarecontext/painternode.cpp | 189 ---- softwarecontext/painternode.h | 96 -- softwarecontext/pixmaptexture.cpp | 56 - softwarecontext/pixmaptexture.h | 44 - softwarecontext/pluginmain.cpp | 54 - softwarecontext/pluginmain.h | 47 - softwarecontext/rectanglenode.cpp | 219 ---- softwarecontext/rectanglenode.h | 61 -- softwarecontext/renderingvisitor.cpp | 169 --- softwarecontext/renderingvisitor.h | 55 - softwarecontext/renderloop.cpp | 251 ----- softwarecontext/renderloop.h | 72 -- softwarecontext/softwarecontext.json | 3 - softwarecontext/softwarecontext.pro | 43 - softwarecontext/softwarelayer.cpp | 219 ---- softwarecontext/softwarelayer.h | 86 -- softwarecontext/threadedrenderloop.cpp | 1129 -------------------- softwarecontext/threadedrenderloop.h | 97 -- src/plugins/plugins.pro | 2 + src/plugins/scenegraph/scenegraph.pro | 2 + src/plugins/scenegraph/softwarecontext/context.cpp | 216 ++++ src/plugins/scenegraph/softwarecontext/context.h | 98 ++ .../scenegraph/softwarecontext/glyphnode.cpp | 91 ++ src/plugins/scenegraph/softwarecontext/glyphnode.h | 49 + .../scenegraph/softwarecontext/imagenode.cpp | 423 ++++++++ src/plugins/scenegraph/softwarecontext/imagenode.h | 108 ++ .../scenegraph/softwarecontext/ninepatchnode.cpp | 66 ++ .../scenegraph/softwarecontext/ninepatchnode.h | 45 + .../scenegraph/softwarecontext/painternode.cpp | 189 ++++ .../scenegraph/softwarecontext/painternode.h | 96 ++ .../scenegraph/softwarecontext/pixmaptexture.cpp | 56 + .../scenegraph/softwarecontext/pixmaptexture.h | 44 + .../scenegraph/softwarecontext/pluginmain.cpp | 54 + .../scenegraph/softwarecontext/pluginmain.h | 47 + .../scenegraph/softwarecontext/rectanglenode.cpp | 219 ++++ .../scenegraph/softwarecontext/rectanglenode.h | 61 ++ .../softwarecontext/renderingvisitor.cpp | 169 +++ .../scenegraph/softwarecontext/renderingvisitor.h | 55 + .../scenegraph/softwarecontext/renderloop.cpp | 251 +++++ .../scenegraph/softwarecontext/renderloop.h | 72 ++ .../softwarecontext/softwarecontext.json | 3 + .../scenegraph/softwarecontext/softwarecontext.pro | 43 + .../scenegraph/softwarecontext/softwarelayer.cpp | 219 ++++ .../scenegraph/softwarecontext/softwarelayer.h | 86 ++ .../softwarecontext/threadedrenderloop.cpp | 1129 ++++++++++++++++++++ .../softwarecontext/threadedrenderloop.h | 97 ++ src/src.pro | 2 + sync.profile | 13 + 59 files changed, 4009 insertions(+), 3989 deletions(-) create mode 100644 .qmake.conf create mode 100644 scenegraph-raster.pro delete mode 100644 software-backend.pro delete mode 100644 softwarecontext/context.cpp delete mode 100644 softwarecontext/context.h delete mode 100644 softwarecontext/glyphnode.cpp delete mode 100644 softwarecontext/glyphnode.h delete mode 100644 softwarecontext/imagenode.cpp delete mode 100644 softwarecontext/imagenode.h delete mode 100644 softwarecontext/ninepatchnode.cpp delete mode 100644 softwarecontext/ninepatchnode.h delete mode 100644 softwarecontext/painternode.cpp delete mode 100644 softwarecontext/painternode.h delete mode 100644 softwarecontext/pixmaptexture.cpp delete mode 100644 softwarecontext/pixmaptexture.h delete mode 100644 softwarecontext/pluginmain.cpp delete mode 100644 softwarecontext/pluginmain.h delete mode 100644 softwarecontext/rectanglenode.cpp delete mode 100644 softwarecontext/rectanglenode.h delete mode 100644 softwarecontext/renderingvisitor.cpp delete mode 100644 softwarecontext/renderingvisitor.h delete mode 100644 softwarecontext/renderloop.cpp delete mode 100644 softwarecontext/renderloop.h delete mode 100644 softwarecontext/softwarecontext.json delete mode 100644 softwarecontext/softwarecontext.pro delete mode 100644 softwarecontext/softwarelayer.cpp delete mode 100644 softwarecontext/softwarelayer.h delete mode 100644 softwarecontext/threadedrenderloop.cpp delete mode 100644 softwarecontext/threadedrenderloop.h create mode 100644 src/plugins/plugins.pro create mode 100644 src/plugins/scenegraph/scenegraph.pro create mode 100644 src/plugins/scenegraph/softwarecontext/context.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/context.h create mode 100644 src/plugins/scenegraph/softwarecontext/glyphnode.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/glyphnode.h create mode 100644 src/plugins/scenegraph/softwarecontext/imagenode.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/imagenode.h create mode 100644 src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/ninepatchnode.h create mode 100644 src/plugins/scenegraph/softwarecontext/painternode.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/painternode.h create mode 100644 src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/pixmaptexture.h create mode 100644 src/plugins/scenegraph/softwarecontext/pluginmain.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/pluginmain.h create mode 100644 src/plugins/scenegraph/softwarecontext/rectanglenode.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/rectanglenode.h create mode 100644 src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/renderingvisitor.h create mode 100644 src/plugins/scenegraph/softwarecontext/renderloop.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/renderloop.h create mode 100644 src/plugins/scenegraph/softwarecontext/softwarecontext.json create mode 100644 src/plugins/scenegraph/softwarecontext/softwarecontext.pro create mode 100644 src/plugins/scenegraph/softwarecontext/softwarelayer.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/softwarelayer.h create mode 100644 src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp create mode 100644 src/plugins/scenegraph/softwarecontext/threadedrenderloop.h create mode 100644 src/src.pro create mode 100644 sync.profile diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000000..e28797de24 --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,3 @@ +load(qt_build_config) + +MODULE_VERSION = 5.4.0 diff --git a/scenegraph-raster.pro b/scenegraph-raster.pro new file mode 100644 index 0000000000..58c33f27ca --- /dev/null +++ b/scenegraph-raster.pro @@ -0,0 +1 @@ +load(qt_parts) diff --git a/software-backend.pro b/software-backend.pro deleted file mode 100644 index 90289dd979..0000000000 --- a/software-backend.pro +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE=subdirs -SUBDIRS=softwarecontext - diff --git a/softwarecontext/context.cpp b/softwarecontext/context.cpp deleted file mode 100644 index 33dc7e87e4..0000000000 --- a/softwarecontext/context.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "context.h" - -#include "rectanglenode.h" -#include "imagenode.h" -#include "painternode.h" -#include "pixmaptexture.h" -#include "glyphnode.h" -#include "ninepatchnode.h" -#include "renderingvisitor.h" -#include "softwarelayer.h" - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef QSG_NO_RENDERER_TIMING -static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); -#endif - -namespace SoftwareContext -{ - -Renderer::Renderer(QSGRenderContext *context) - : QSGRenderer(context) -{ -} - -void Renderer::renderScene(GLuint fboId) -{ - class B : public QSGBindable - { - public: - void bind() const { } - } bindable; - QSGRenderer::renderScene(bindable); -} - -void Renderer::render() -{ - QWindow *currentWindow = static_cast(m_context)->currentWindow; - if (!m_backingStore) - m_backingStore.reset(new QBackingStore(currentWindow)); - - if (m_backingStore->size() != currentWindow->size()) - m_backingStore->resize(currentWindow->size()); - - const QRect rect(0, 0, currentWindow->width(), currentWindow->height()); - m_backingStore->beginPaint(rect); - - QPaintDevice *device = m_backingStore->paintDevice(); - QPainter painter(device); - painter.setRenderHint(QPainter::Antialiasing); - - painter.fillRect(rect, clearColor()); - RenderingVisitor(&painter).visitChildren(rootNode()); - - m_backingStore->endPaint(); - m_backingStore->flush(rect); -} - -void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) -{ - - QSGRenderer::nodeChanged(node, state); -} - -PixmapRenderer::PixmapRenderer(QSGRenderContext *context) - : QSGRenderer(context) -{ - -} - -void PixmapRenderer::renderScene(GLuint) -{ - Q_UNREACHABLE(); -} - -void PixmapRenderer::render() -{ - Q_UNREACHABLE(); -} - -void PixmapRenderer::render(QPixmap *target) -{ - const QRect rect(0, 0, target->width(), target->height()); - target->fill(clearColor()); - QPainter painter(target); - painter.setRenderHint(QPainter::Antialiasing); - - RenderingVisitor(&painter).visitChildren(rootNode()); -} - -RenderContext::RenderContext(QSGContext *ctx) - : QSGRenderContext(ctx) - , currentWindow(0) - , m_initialized(false) -{ -} -Context::Context(QObject *parent) - : QSGContext(parent) -{ - setDistanceFieldEnabled(false); -} - -QSGRectangleNode *Context::createRectangleNode() -{ - return new RectangleNode(); -} - -QSGImageNode *Context::createImageNode() -{ - return new ImageNode(); -} - -QSGPainterNode *Context::createPainterNode(QQuickPaintedItem *item) -{ - return new PainterNode(item); -} - -QSGGlyphNode *Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) -{ - Q_UNUSED(rc); - Q_UNUSED(preferNativeGlyphNode); - return new GlyphNode(); -} - -QSGNinePatchNode *Context::createNinePatchNode() -{ - return new NinePatchNode(); -} - -QSGLayer *Context::createLayer(QSGRenderContext *renderContext) -{ - return new SoftwareLayer(renderContext); -} - -QSurfaceFormat Context::defaultSurfaceFormat() const -{ - QSurfaceFormat format = QSurfaceFormat::defaultFormat(); - format.setRenderableType(QSurfaceFormat::DefaultRenderableType); - format.setMajorVersion(0); - format.setMinorVersion(0); - return format; -} - -void RenderContext::initialize(QOpenGLContext *context) -{ - Q_UNUSED(context) - Q_UNREACHABLE(); -} - -void RenderContext::initializeIfNeeded() -{ - if (m_initialized) - return; - m_initialized = true; - emit initialized(); -} - -void RenderContext::invalidate() -{ - QSGRenderContext::invalidate(); -} - -QSGTexture *RenderContext::createTexture(const QImage &image) const -{ - return new PixmapTexture(image); -} - -QSGTexture *RenderContext::createTextureNoAtlas(const QImage &image) const -{ - return new PixmapTexture(image); -} - -QSGRenderer *RenderContext::createRenderer() -{ - return new Renderer(this); -} - - -void RenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fbo) -{ - QSGRenderContext::renderNextFrame(renderer, fbo); -} - -} // namespace diff --git a/softwarecontext/context.h b/softwarecontext/context.h deleted file mode 100644 index 88fe4e447f..0000000000 --- a/softwarecontext/context.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef CONTEXT_H -#define CONTEXT_H - -#include -#include -#include -#include -#include -#include - -namespace SoftwareContext -{ - -class Renderer : public QSGRenderer -{ -public: - Renderer(QSGRenderContext *context); - - virtual void renderScene(GLuint fboId = 0); - - virtual void render(); - - void nodeChanged(QSGNode *node, QSGNode::DirtyState state); - - QBackingStore *backingStore() const { return m_backingStore.data(); } - -private: - QScopedPointer m_backingStore; - QRect m_dirtyRect; -}; - -class PixmapRenderer : public QSGRenderer -{ -public: - PixmapRenderer(QSGRenderContext *context); - - virtual void renderScene(GLuint fboId = 0); - - virtual void render(); - - void render(QPixmap *target); -}; - -class RenderContext : public QSGRenderContext -{ -public: - RenderContext(QSGContext *ctx); - void initialize(QOpenGLContext *context); - void initializeIfNeeded(); - void invalidate(); - void renderNextFrame(QSGRenderer *renderer, GLuint fbo); - QSGTexture *createTexture(const QImage &image) const; - QSGTexture *createTextureNoAtlas(const QImage &image) const; - QSGRenderer *createRenderer(); - - QWindow *currentWindow; - bool m_initialized; -}; - -class Context : public QSGContext -{ - Q_OBJECT -public: - explicit Context(QObject *parent = 0); - - QSGRenderContext *createRenderContext() { return new RenderContext(this); } - - virtual QSGRectangleNode *createRectangleNode(); - virtual QSGImageNode *createImageNode(); - virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item); - virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode); - virtual QSGNinePatchNode *createNinePatchNode(); - virtual QSGLayer *createLayer(QSGRenderContext *renderContext); - virtual QSurfaceFormat defaultSurfaceFormat() const; -}; - -} // namespace - -#endif // CONTEXT_H diff --git a/softwarecontext/glyphnode.cpp b/softwarecontext/glyphnode.cpp deleted file mode 100644 index eae8b626cb..0000000000 --- a/softwarecontext/glyphnode.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "glyphnode.h" - -GlyphNode::GlyphNode() - : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) - , m_style(QQuickText::Normal) -{ - setMaterial((QSGMaterial*)1); - setGeometry(&m_geometry); -} - - -void GlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) -{ - m_position = position; - m_glyphRun = glyphs; -} - -void GlyphNode::setColor(const QColor &color) -{ - m_color = color; -} - -void GlyphNode::setStyle(QQuickText::TextStyle style) -{ - m_style = style; -} - -void GlyphNode::setStyleColor(const QColor &color) -{ - m_styleColor = color; -} - -QPointF GlyphNode::baseLine() const -{ - return QPointF(); -} - -void GlyphNode::setPreferredAntialiasingMode(QSGGlyphNode::AntialiasingMode) -{ -} - -void GlyphNode::update() -{ -} - -void GlyphNode::paint(QPainter *painter) -{ - painter->setBrush(QBrush()); - QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent()); - - switch (m_style) { - case QQuickText::Normal: break; - case QQuickText::Outline: - painter->setPen(m_styleColor); - painter->drawGlyphRun(pos + QPointF(0, 1), m_glyphRun); - painter->drawGlyphRun(pos + QPointF(0, -1), m_glyphRun); - painter->drawGlyphRun(pos + QPointF(1, 0), m_glyphRun); - painter->drawGlyphRun(pos + QPointF(-1, 0), m_glyphRun); - break; - case QQuickText::Raised: - painter->setPen(m_styleColor); - painter->drawGlyphRun(pos + QPointF(0, 1), m_glyphRun); - break; - case QQuickText::Sunken: - painter->setPen(m_styleColor); - painter->drawGlyphRun(pos + QPointF(0, -1), m_glyphRun); - break; - } - - painter->setPen(m_color); - painter->drawGlyphRun(pos, m_glyphRun); -} diff --git a/softwarecontext/glyphnode.h b/softwarecontext/glyphnode.h deleted file mode 100644 index d34013be5b..0000000000 --- a/softwarecontext/glyphnode.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef GLYPHNODE_H -#define GLYPHNODE_H - -#include - -class GlyphNode : public QSGGlyphNode -{ -public: - GlyphNode(); - - virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); - virtual void setColor(const QColor &color); - virtual void setStyle(QQuickText::TextStyle style); - virtual void setStyleColor(const QColor &color); - virtual QPointF baseLine() const; - virtual void setPreferredAntialiasingMode(AntialiasingMode); - virtual void update(); - - void paint(QPainter *painter); - -private: - QPointF m_position; - QGlyphRun m_glyphRun; - QColor m_color; - QSGGeometry m_geometry; - QQuickText::TextStyle m_style; - QColor m_styleColor; -}; - -#endif // GLYPHNODE_H diff --git a/softwarecontext/imagenode.cpp b/softwarecontext/imagenode.cpp deleted file mode 100644 index 7718831b65..0000000000 --- a/softwarecontext/imagenode.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "imagenode.h" - -#include "pixmaptexture.h" -#include "softwarelayer.h" -#include -#include - -// Helper from widgets/styles/qdrawutil.cpp - -namespace SoftwareContext { - -void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, - const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, - const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) -{ - QPainter::PixmapFragment d; - d.opacity = 1.0; - d.rotation = 0.0; - - QPixmapFragmentsArray opaqueData; - QPixmapFragmentsArray translucentData; - - // source center - const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); - const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); - const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; - const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; - const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; - const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; - // target center - const int targetCenterTop = targetRect.top() + targetMargins.top(); - const int targetCenterLeft = targetRect.left() + targetMargins.left(); - const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; - const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; - const int targetCenterWidth = targetCenterRight - targetCenterLeft; - const int targetCenterHeight = targetCenterBottom - targetCenterTop; - - QVarLengthArray xTarget; // x-coordinates of target rectangles - QVarLengthArray yTarget; // y-coordinates of target rectangles - - int columns = 3; - int rows = 3; - if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) - columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); - if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) - rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); - - xTarget.resize(columns + 1); - yTarget.resize(rows + 1); - - bool oldAA = painter->testRenderHint(QPainter::Antialiasing); - if (painter->paintEngine()->type() != QPaintEngine::OpenGL - && painter->paintEngine()->type() != QPaintEngine::OpenGL2 - && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { - painter->setRenderHint(QPainter::Antialiasing, false); - } - - xTarget[0] = targetRect.left(); - xTarget[1] = targetCenterLeft; - xTarget[columns - 1] = targetCenterRight; - xTarget[columns] = targetRect.left() + targetRect.width(); - - yTarget[0] = targetRect.top(); - yTarget[1] = targetCenterTop; - yTarget[rows - 1] = targetCenterBottom; - yTarget[rows] = targetRect.top() + targetRect.height(); - - qreal dx = targetCenterWidth; - qreal dy = targetCenterHeight; - - switch (rules.horizontal) { - case Qt::StretchTile: - dx = targetCenterWidth; - break; - case Qt::RepeatTile: - dx = sourceCenterWidth; - break; - case Qt::RoundTile: - dx = targetCenterWidth / qreal(columns - 2); - break; - } - - for (int i = 2; i < columns - 1; ++i) - xTarget[i] = xTarget[i - 1] + dx; - - switch (rules.vertical) { - case Qt::StretchTile: - dy = targetCenterHeight; - break; - case Qt::RepeatTile: - dy = sourceCenterHeight; - break; - case Qt::RoundTile: - dy = targetCenterHeight / qreal(rows - 2); - break; - } - - for (int i = 2; i < rows - 1; ++i) - yTarget[i] = yTarget[i - 1] + dy; - - // corners - if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceRect.top(); - d.width = sourceMargins.left(); - d.height = sourceMargins.top(); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueTopLeft) - opaqueData.append(d); - else - translucentData.append(d); - } - if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceRect.top(); - d.width = sourceMargins.right(); - d.height = sourceMargins.top(); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueTopRight) - opaqueData.append(d); - else - translucentData.append(d); - } - if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceCenterBottom; - d.width = sourceMargins.left(); - d.height = sourceMargins.bottom(); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueBottomLeft) - opaqueData.append(d); - else - translucentData.append(d); - } - if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceCenterBottom; - d.width = sourceMargins.right(); - d.height = sourceMargins.bottom(); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueBottomRight) - opaqueData.append(d); - else - translucentData.append(d); - } - - // horizontal edges - if (targetCenterWidth > 0 && sourceCenterWidth > 0) { - if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceRect.top(); - d.width = sourceCenterWidth; - d.height = sourceMargins.top(); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.scaleX = dx / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); - } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); - } - if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceCenterBottom; - d.width = sourceCenterWidth; - d.height = sourceMargins.bottom(); - d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.scaleX = dx / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); - } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); - } - } - - // vertical edges - if (targetCenterHeight > 0 && sourceCenterHeight > 0) { - if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceCenterTop; - d.width = sourceMargins.left(); - d.height = sourceCenterHeight; - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = dy / d.height; - for (int i = 1; i < rows - 1; ++i) { - d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); - data.append(d); - } - if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); - } - if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceCenterTop; - d.width = sourceMargins.right(); - d.height = sourceCenterHeight; - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = dy / d.height; - for (int i = 1; i < rows - 1; ++i) { - d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); - data.append(d); - } - if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); - } - } - - // center - if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceCenterTop; - d.width = sourceCenterWidth; - d.height = sourceCenterHeight; - d.scaleX = dx / d.width; - d.scaleY = dy / d.height; - - qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; - qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; - - for (int j = 1; j < rows - 1; ++j) { - d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); - for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); - } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = repeatWidth; - } - if (rules.vertical == Qt::RepeatTile) { - for (int i = 1; i < columns - 1; ++i) - data[data.size() - i].height = repeatHeight; - } - } - - if (opaqueData.size()) - painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); - if (translucentData.size()) - painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); - - if (oldAA) - painter->setRenderHint(QPainter::Antialiasing, true); -} - -} - -ImageNode::ImageNode() - : m_innerSourceRect(0, 0, 1, 1) - , m_subSourceRect(0, 0, 1, 1) - , m_texture(0) - , m_mirror(false) - , m_smooth(true) - , m_tileHorizontal(false) - , m_tileVertical(false) -{ - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - - -void ImageNode::setTargetRect(const QRectF &rect) -{ - m_targetRect = rect; -} - -void ImageNode::setInnerTargetRect(const QRectF &rect) -{ - m_innerTargetRect = rect; -} - -void ImageNode::setInnerSourceRect(const QRectF &rect) -{ - m_innerSourceRect = rect; -} - -void ImageNode::setSubSourceRect(const QRectF &rect) -{ - m_subSourceRect = rect; -} - -void ImageNode::setTexture(QSGTexture *texture) -{ - m_texture = texture; -} - -void ImageNode::setMirror(bool mirror) -{ - // ### implement support for mirrored pixmaps - m_mirror = mirror; -} - -void ImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) -{ -} - -void ImageNode::setFiltering(QSGTexture::Filtering filtering) -{ - m_smooth = (filtering == QSGTexture::Nearest); -} - -void ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) -{ - m_tileHorizontal = (wrapMode == QSGTexture::Repeat); -} - -void ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) -{ - m_tileVertical = (wrapMode == QSGTexture::Repeat); -} - -void ImageNode::update() -{ -} - -void ImageNode::preprocess() -{ - bool doDirty = false; - QSGLayer *t = qobject_cast(m_texture); - if (t) { - doDirty = t->updateTexture(); - markDirty(DirtyGeometry); - } - if (doDirty) - markDirty(DirtyMaterial); -} - -static Qt::TileRule getTileRule(qreal factor) -{ - int ifactor = qRound(factor); - if (qFuzzyCompare(factor, ifactor )) { - if (ifactor == 1 || ifactor == 0) - return Qt::StretchTile; - return Qt::RoundTile; - } - return Qt::RepeatTile; -} - - -void ImageNode::paint(QPainter *painter) -{ - painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); - - const QPixmap &pm = pixmap(); - - if (m_innerTargetRect != m_targetRect) { - // border image - QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(), - m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom()); - QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); - SoftwareContext::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), - margins, tilerules, QDrawBorderPixmap::DrawingHints(0)); - return; - } - - if (m_tileHorizontal || m_tileVertical) { - painter->save(); - qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width()); - qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height()); - QMatrix transform(sx, 0, 0, sy, 0, 0); - painter->setMatrix(transform, true); - painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy), - pm, - QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height())); - painter->restore(); - } else { - QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(), - m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height()); - painter->drawPixmap(m_targetRect, pm, sr); - } -} - -const QPixmap &ImageNode::pixmap() const -{ - if (PixmapTexture *pt = qobject_cast(m_texture)) { - return pt->pixmap(); - } else if (SoftwareLayer *layer = qobject_cast(m_texture)) { - return layer->pixmap(); - } else { - qFatal("Image used with invalid texture format."); - } -} diff --git a/softwarecontext/imagenode.h b/softwarecontext/imagenode.h deleted file mode 100644 index a0c059a889..0000000000 --- a/softwarecontext/imagenode.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef IMAGENODE_H -#define IMAGENODE_H - -#include -#include - -typedef QVarLengthArray QPixmapFragmentsArray; - -struct QTileRules -{ - inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) - : horizontal(horizontalRule), vertical(verticalRule) {} - inline QTileRules(Qt::TileRule rule = Qt::StretchTile) - : horizontal(rule), vertical(rule) {} - Qt::TileRule horizontal; - Qt::TileRule vertical; -}; - -#ifndef Q_QDOC -// For internal use only. -namespace QDrawBorderPixmap -{ - enum DrawingHint - { - OpaqueTopLeft = 0x0001, - OpaqueTop = 0x0002, - OpaqueTopRight = 0x0004, - OpaqueLeft = 0x0008, - OpaqueCenter = 0x0010, - OpaqueRight = 0x0020, - OpaqueBottomLeft = 0x0040, - OpaqueBottom = 0x0080, - OpaqueBottomRight = 0x0100, - OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight, - OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom, - OpaqueFrame = OpaqueCorners | OpaqueEdges, - OpaqueAll = OpaqueCenter | OpaqueFrame - }; - - Q_DECLARE_FLAGS(DrawingHints, DrawingHint) -} -#endif - -namespace SoftwareContext { - -void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, - const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, - const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints); - -} - -class ImageNode : public QSGImageNode -{ -public: - ImageNode(); - - virtual void setTargetRect(const QRectF &rect); - virtual void setInnerTargetRect(const QRectF &rect); - virtual void setInnerSourceRect(const QRectF &rect); - virtual void setSubSourceRect(const QRectF &rect); - virtual void setTexture(QSGTexture *texture); - virtual void setMirror(bool mirror); - virtual void setMipmapFiltering(QSGTexture::Filtering filtering); - virtual void setFiltering(QSGTexture::Filtering filtering); - virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode); - virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode); - virtual void update(); - - virtual void preprocess(); - - void paint(QPainter *painter); - -private: - const QPixmap &pixmap() const; - - QRectF m_targetRect; - QRectF m_innerTargetRect; - QRectF m_innerSourceRect; - QRectF m_subSourceRect; - - QSGTexture *m_texture; - - bool m_mirror; - bool m_smooth; - bool m_tileHorizontal; - bool m_tileVertical; -}; - -#endif // IMAGENODE_H diff --git a/softwarecontext/ninepatchnode.cpp b/softwarecontext/ninepatchnode.cpp deleted file mode 100644 index ab503b7af5..0000000000 --- a/softwarecontext/ninepatchnode.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "ninepatchnode.h" -#include "pixmaptexture.h" -#include "imagenode.h" - -NinePatchNode::NinePatchNode() -{ - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - -void NinePatchNode::setTexture(QSGTexture *texture) -{ - PixmapTexture *pt = qobject_cast(texture); - if (!pt) { - qWarning() << "Image used with invalid texture format."; - return; - } - m_pixmap = pt->pixmap(); -} - -void NinePatchNode::setBounds(const QRectF &bounds) -{ - m_bounds = bounds; -} - -void NinePatchNode::setDevicePixelRatio(qreal ratio) -{ - m_pixelRatio = ratio; -} - -void NinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) -{ - m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom)); -} - -void NinePatchNode::update() -{ -} - -void NinePatchNode::paint(QPainter *painter) -{ - if (m_margins.isNull()) - painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); - else - SoftwareContext::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), - m_margins, Qt::StretchTile, QDrawBorderPixmap::DrawingHints(0)); -} diff --git a/softwarecontext/ninepatchnode.h b/softwarecontext/ninepatchnode.h deleted file mode 100644 index 2bcd1214e3..0000000000 --- a/softwarecontext/ninepatchnode.h +++ /dev/null @@ -1,45 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef NINEPATCHNODE_H -#define NINEPATCHNODE_H - -#include - -class NinePatchNode : public QSGNinePatchNode -{ -public: - NinePatchNode(); - - virtual void setTexture(QSGTexture *texture); - virtual void setBounds(const QRectF &bounds); - virtual void setDevicePixelRatio(qreal ratio); - virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom); - virtual void update(); - - void paint(QPainter *painter); - -private: - QPixmap m_pixmap; - QRectF m_bounds; - qreal m_pixelRatio; - QMargins m_margins; -}; - -#endif // NINEPATCHNODE_H diff --git a/softwarecontext/painternode.cpp b/softwarecontext/painternode.cpp deleted file mode 100644 index bf5ec5f202..0000000000 --- a/softwarecontext/painternode.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "painternode.h" -#include "pixmaptexture.h" -#include - -PainterNode::PainterNode(QQuickPaintedItem *item) - : QSGPainterNode() - , m_preferredRenderTarget(QQuickPaintedItem::Image) - , m_actualRenderTarget(QQuickPaintedItem::Image) - , m_item(item) - , m_texture(0) - , m_dirtyContents(false) - , m_opaquePainting(false) - , m_linear_filtering(false) - , m_mipmapping(false) - , m_smoothPainting(false) - , m_extensionsChecked(false) - , m_multisamplingSupported(false) - , m_fastFBOResizing(false) - , m_fillColor(Qt::transparent) - , m_contentsScale(1.0) - , m_dirtyGeometry(false) -{ - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - -PainterNode::~PainterNode() -{ - delete m_texture; -} - -void PainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) -{ - if (m_preferredRenderTarget == target) - return; - - m_preferredRenderTarget = target; -} - -void PainterNode::setSize(const QSize &size) -{ - if (size == m_size) - return; - - m_size = size; - - m_dirtyGeometry = true; -} - -void PainterNode::setDirty(const QRect &dirtyRect) -{ - m_dirtyContents = true; - m_dirtyRect = dirtyRect; - markDirty(DirtyMaterial); -} - -void PainterNode::setOpaquePainting(bool opaque) -{ - if (opaque == m_opaquePainting) - return; - - m_opaquePainting = opaque; -} - -void PainterNode::setLinearFiltering(bool linearFiltering) -{ - if (linearFiltering == m_linear_filtering) - return; - - m_linear_filtering = linearFiltering; -} - -void PainterNode::setMipmapping(bool mipmapping) -{ - if (mipmapping == m_mipmapping) - return; - - m_mipmapping = mipmapping; -} - -void PainterNode::setSmoothPainting(bool s) -{ - if (s == m_smoothPainting) - return; - - m_smoothPainting = s; -} - -void PainterNode::setFillColor(const QColor &c) -{ - if (c == m_fillColor) - return; - - m_fillColor = c; - markDirty(DirtyMaterial); -} - -void PainterNode::setContentsScale(qreal s) -{ - if (s == m_contentsScale) - return; - - m_contentsScale = s; - markDirty(DirtyMaterial); -} - -void PainterNode::setFastFBOResizing(bool dynamic) -{ - m_fastFBOResizing = dynamic; -} - -QImage PainterNode::toImage() const -{ - return m_pixmap.toImage(); -} - -void PainterNode::update() -{ - if (m_dirtyGeometry) { - m_pixmap = QPixmap(m_size); - if (!m_opaquePainting) - m_pixmap.fill(Qt::transparent); - - if (m_texture) - delete m_texture; - m_texture = new PixmapTexture(m_pixmap); - } - - if (m_dirtyContents) - paint(); - - m_dirtyGeometry = false; - m_dirtyContents = false; -} - -void PainterNode::paint(QPainter *painter) -{ - painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap); -} - -void PainterNode::paint() -{ - QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect; - - QPainter painter; - - painter.begin(&m_pixmap); - if (m_smoothPainting) { - painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); - } - - painter.scale(m_contentsScale, m_contentsScale); - - QRect sclip(qFloor(dirtyRect.x()/m_contentsScale), - qFloor(dirtyRect.y()/m_contentsScale), - qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)), - qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale))); - - if (!m_dirtyRect.isNull()) - painter.setClipRect(sclip); - - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.fillRect(sclip, m_fillColor); - painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - - m_item->paint(&painter); - painter.end(); - - m_dirtyRect = QRect(); -} diff --git a/softwarecontext/painternode.h b/softwarecontext/painternode.h deleted file mode 100644 index db0d03e28a..0000000000 --- a/softwarecontext/painternode.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef PAINTERNODE_H -#define PAINTERNODE_H - -#include -#include - -#include - -class PainterNode : public QSGPainterNode -{ -public: - PainterNode(QQuickPaintedItem *item); - ~PainterNode(); - - void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target); - - void setSize(const QSize &size); - QSize size() const { return m_size; } - - void setDirty(const QRect &dirtyRect = QRect()); - - void setOpaquePainting(bool opaque); - bool opaquePainting() const { return m_opaquePainting; } - - void setLinearFiltering(bool linearFiltering); - bool linearFiltering() const { return m_linear_filtering; } - - void setMipmapping(bool mipmapping); - bool mipmapping() const { return m_mipmapping; } - - void setSmoothPainting(bool s); - bool smoothPainting() const { return m_smoothPainting; } - - void setFillColor(const QColor &c); - QColor fillColor() const { return m_fillColor; } - - void setContentsScale(qreal s); - qreal contentsScale() const { return m_contentsScale; } - - void setFastFBOResizing(bool dynamic); - bool fastFBOResizing() const { return m_fastFBOResizing; } - - QImage toImage() const; - void update(); - QSGTexture *texture() const { return m_texture; } - - void paint(QPainter *painter); - - void paint(); - -private: - - QQuickPaintedItem::RenderTarget m_preferredRenderTarget; - QQuickPaintedItem::RenderTarget m_actualRenderTarget; - - QQuickPaintedItem *m_item; - - QPixmap m_pixmap; - QSGTexture *m_texture; - - QSize m_size; - bool m_dirtyContents; - QRect m_dirtyRect; - bool m_opaquePainting; - bool m_linear_filtering; - bool m_mipmapping; - bool m_smoothPainting; - bool m_extensionsChecked; - bool m_multisamplingSupported; - bool m_fastFBOResizing; - QColor m_fillColor; - qreal m_contentsScale; - - bool m_dirtyGeometry; -}; - -#endif // PAINTERNODE_H diff --git a/softwarecontext/pixmaptexture.cpp b/softwarecontext/pixmaptexture.cpp deleted file mode 100644 index d3dd747a9e..0000000000 --- a/softwarecontext/pixmaptexture.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "pixmaptexture.h" - -PixmapTexture::PixmapTexture(const QImage &image) - : m_pixmap(QPixmap::fromImage(image)) -{ -} - -PixmapTexture::PixmapTexture(const QPixmap &pixmap) - : m_pixmap(pixmap) -{ -} - - -int PixmapTexture::textureId() const -{ - return 0; -} - -QSize PixmapTexture::textureSize() const -{ - return m_pixmap.size(); -} - -bool PixmapTexture::hasAlphaChannel() const -{ - return m_pixmap.hasAlphaChannel(); -} - -bool PixmapTexture::hasMipmaps() const -{ - return false; -} - -void PixmapTexture::bind() -{ - Q_UNREACHABLE(); -} diff --git a/softwarecontext/pixmaptexture.h b/softwarecontext/pixmaptexture.h deleted file mode 100644 index c3ee09dfe1..0000000000 --- a/softwarecontext/pixmaptexture.h +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef PIXMAPTEXTURE_H -#define PIXMAPTEXTURE_H - -#include - -class PixmapTexture : public QSGTexture -{ - Q_OBJECT -public: - PixmapTexture(const QImage &image); - PixmapTexture(const QPixmap &pixmap); - - virtual int textureId() const; - virtual QSize textureSize() const; - virtual bool hasAlphaChannel() const; - virtual bool hasMipmaps() const; - virtual void bind(); - - const QPixmap &pixmap() const { return m_pixmap; } - -private: - QPixmap m_pixmap; -}; - -#endif // PIXMAPTEXTURE_H diff --git a/softwarecontext/pluginmain.cpp b/softwarecontext/pluginmain.cpp deleted file mode 100644 index afb91a8ea2..0000000000 --- a/softwarecontext/pluginmain.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "pluginmain.h" -#include "context.h" -#include "renderloop.h" -#include "threadedrenderloop.h" - -ContextPlugin::ContextPlugin(QObject *parent) - : QSGContextPlugin(parent) -{ -} - -QStringList ContextPlugin::keys() const -{ - return QStringList() << QLatin1String("softwarecontext"); -} - -QSGContext *ContextPlugin::create(const QString &) const -{ - if (!instance) - instance = new SoftwareContext::Context(); - return instance; -} - -QSGRenderLoop *ContextPlugin::createWindowManager() -{ - if (qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("basic")) - return new RenderLoop(); - return new ThreadedRenderLoop(); -} - -SoftwareContext::Context *ContextPlugin::instance = 0; - - - diff --git a/softwarecontext/pluginmain.h b/softwarecontext/pluginmain.h deleted file mode 100644 index fd90823322..0000000000 --- a/softwarecontext/pluginmain.h +++ /dev/null @@ -1,47 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PLUGINMAIN_H -#define PLUGINMAIN_H - -#include -#include - -#include - -#include "context.h" - -class ContextPlugin : public QSGContextPlugin -{ - Q_OBJECT - - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSGContextFactoryInterface" FILE "softwarecontext.json") - -public: - ContextPlugin(QObject *parent = 0); - - QStringList keys() const; - QSGContext *create(const QString &key) const; - QSGRenderLoop *createWindowManager(); - - static SoftwareContext::Context *instance; -}; - -#endif // PLUGINMAIN_H diff --git a/softwarecontext/rectanglenode.cpp b/softwarecontext/rectanglenode.cpp deleted file mode 100644 index 054ffd3ecf..0000000000 --- a/softwarecontext/rectanglenode.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "rectanglenode.h" -#include - -#include - - -RectangleNode::RectangleNode() - : m_penWidth(0) - , m_radius(0) - , m_cornerPixmapIsDirty(true) -{ - setMaterial((QSGMaterial*)1); - setGeometry((QSGGeometry*)1); -} - -void RectangleNode::setRect(const QRectF &rect) -{ - if (m_rect != rect) { - m_rect = rect; - } -} - -void RectangleNode::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - m_cornerPixmapIsDirty = true; - } -} - -void RectangleNode::setPenColor(const QColor &color) -{ - if (m_penColor != color) { - m_penColor = color; - m_cornerPixmapIsDirty = true; - } -} - -void RectangleNode::setPenWidth(qreal width) -{ - if (m_penWidth != width) { - m_penWidth = width; - m_cornerPixmapIsDirty = true; - } -} - -void RectangleNode::setGradientStops(const QGradientStops &stops) -{ - m_stops = stops; - m_cornerPixmapIsDirty = true; -} - -void RectangleNode::setRadius(qreal radius) -{ - if (m_radius != radius) { - m_radius = radius; - m_cornerPixmapIsDirty = true; - } -} - -void RectangleNode::setAligned(bool /*aligned*/) -{ -} - -void RectangleNode::update() -{ - if (!m_penWidth || m_penColor == Qt::transparent) { - m_pen = Qt::NoPen; - } else { - m_pen = QPen(m_penColor); - m_pen.setWidthF(m_penWidth); - } - - if (!m_stops.isEmpty()) { - QLinearGradient gradient(QPoint(0,0), QPoint(0,1)); - gradient.setStops(m_stops); - gradient.setCoordinateMode(QGradient::ObjectBoundingMode); - m_brush = QBrush(gradient); - } else { - m_brush = QBrush(m_color); - } - - if (m_cornerPixmapIsDirty) { - //Generate new corner Pixmap - int radius = qRound(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5f, m_radius)); - - m_cornerPixmap = QPixmap(radius * 2, radius * 2); - m_cornerPixmap.fill(Qt::transparent); - - if (radius > 0) { - QPainter cornerPainter(&m_cornerPixmap); - cornerPainter.setRenderHint(QPainter::Antialiasing); - cornerPainter.setCompositionMode(QPainter::CompositionMode_Source); - - //Paint outer cicle - if (m_penWidth > 0) { - cornerPainter.setPen(Qt::NoPen); - cornerPainter.setBrush(m_penColor); - cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius); - } - - //Paint inner circle - if (radius > m_penWidth) { - cornerPainter.setPen(Qt::NoPen); - if (m_stops.isEmpty()) - cornerPainter.setBrush(m_brush); - else - cornerPainter.setBrush(Qt::transparent); - - QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth); - QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins); - cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius); - } - cornerPainter.end(); - } - m_cornerPixmapIsDirty = false; - } -} - -void RectangleNode::paint(QPainter *painter) -{ - //Radius should never exceeds half of the width or half of the height - int radius = qRound(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); - - QPainter::RenderHints previousRenderHints = painter->renderHints(); - painter->setRenderHint(QPainter::Antialiasing, false); - - if (m_penWidth > 0) { - //Borders can not be more than half the height/width of a rect - double borderWidth = qMin(m_penWidth, m_rect.width() * 0.5); - double borderHeight = qMin(m_penWidth, m_rect.height() * 0.5); - - //Fill 4 border Rects - QRectF borderTop(QPointF(m_rect.x() + radius, m_rect.y()), - QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + borderHeight)); - painter->fillRect(borderTop, m_penColor); - QRectF borderBottom(QPointF(m_rect.x() + radius, m_rect.y() + m_rect.height() - borderHeight), - QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + m_rect.height())); - painter->fillRect(borderBottom, m_penColor); - QRectF borderLeft(QPointF(m_rect.x(), m_rect.y() + radius), - QPointF(m_rect.x() + borderWidth, m_rect.y() + m_rect.height() - radius)); - painter->fillRect(borderLeft, m_penColor); - QRectF borderRight(QPointF(m_rect.x() + m_rect.width() - borderWidth, m_rect.y() + radius), - QPointF(m_rect.x() + m_rect.width(), m_rect.y() + m_rect.height() - radius)); - painter->fillRect(borderRight, m_penColor); - } - - if (radius > 0) { - //blit 4 corners to border - QRectF topLeftCorner(QPointF(m_rect.x(), m_rect.y()), - QPointF(m_rect.x() + radius, m_rect.y() + radius)); - painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, radius, radius)); - QRectF topRightCorner(QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y()), - QPointF(m_rect.x() + m_rect.width(), m_rect.y() + radius)); - painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(radius, 0, radius, radius)); - QRectF bottomLeftCorner(QPointF(m_rect.x(), m_rect.y() + m_rect.height() - radius), - QPointF(m_rect.x() + radius, m_rect.y() + m_rect.height())); - painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, radius, radius, radius)); - QRectF bottomRightCorner(QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + m_rect.height() - radius), - QPointF(m_rect.x() + m_rect.width(), m_rect.y() + m_rect.height())); - painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(radius, radius, radius, radius)); - - } - - QRectF brushRect = m_rect.marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth)); - if (brushRect.width() < 0) - brushRect.setWidth(0); - if (brushRect.height() < 0) - brushRect.setHeight(0); - double innerRectRadius = qMax(0.0, radius - m_penWidth); - - //If not completely transparent or has a gradient - if (m_color.alpha() > 0 || !m_stops.empty()) { - if (innerRectRadius > 0) { - //Rounded Rect - if (m_stops.empty()) { - //Rounded Rects without gradient need 3 blits - QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()), - QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height())); - painter->fillRect(centerRect, m_color); - QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius), - QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius)); - painter->fillRect(leftRect, m_color); - QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius), - QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius)); - painter->fillRect(rightRect, m_color); - } else { - //Rounded Rect with gradient (slow) - painter->setPen(Qt::NoPen); - painter->setBrush(m_brush); - painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius); - } - } else { - //non-rounded rects only need 1 blit - painter->fillRect(brushRect, m_brush); - } - } - - painter->setRenderHints(previousRenderHints); -} diff --git a/softwarecontext/rectanglenode.h b/softwarecontext/rectanglenode.h deleted file mode 100644 index 74c0971c82..0000000000 --- a/softwarecontext/rectanglenode.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef RECTANGLENODE_H -#define RECTANGLENODE_H - -#include - -#include -#include -#include - -class RectangleNode : public QSGRectangleNode -{ -public: - RectangleNode(); - - virtual void setRect(const QRectF &rect); - virtual void setColor(const QColor &color); - virtual void setPenColor(const QColor &color); - virtual void setPenWidth(qreal width); - virtual void setGradientStops(const QGradientStops &stops); - virtual void setRadius(qreal radius); - virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) } - virtual void setAligned(bool aligned); - - virtual void update(); - - void paint(QPainter *); - -private: - QRectF m_rect; - QColor m_color; - QColor m_penColor; - double m_penWidth; - QGradientStops m_stops; - double m_radius; - QPen m_pen; - QBrush m_brush; - - bool m_cornerPixmapIsDirty; - QPixmap m_cornerPixmap; -}; - -#endif // RECTANGLENODE_H diff --git a/softwarecontext/renderingvisitor.cpp b/softwarecontext/renderingvisitor.cpp deleted file mode 100644 index 879c30eb4f..0000000000 --- a/softwarecontext/renderingvisitor.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "renderingvisitor.h" - -#include "imagenode.h" -#include "rectanglenode.h" -#include "glyphnode.h" -#include "ninepatchnode.h" -#include "painternode.h" -#include "pixmaptexture.h" - -#include -#include -#include -#include - -RenderingVisitor::RenderingVisitor(QPainter *painter) - : painter(painter) -{ - -} - -bool RenderingVisitor::visit(QSGTransformNode *node) -{ - painter->save(); - painter->setTransform(node->matrix().toTransform(), /*combine*/true); - return true; -} - -void RenderingVisitor::endVisit(QSGTransformNode *) -{ - painter->restore(); -} - -bool RenderingVisitor::visit(QSGClipNode *node) -{ - painter->save(); - painter->setClipRect(node->clipRect(), Qt::IntersectClip); - return true; -} - -void RenderingVisitor::endVisit(QSGClipNode *) -{ - painter->restore(); -} - -bool RenderingVisitor::visit(QSGGeometryNode *node) -{ - if (QSGSimpleRectNode *rectNode = dynamic_cast(node)) { - if (!rectNode->material()->flags() & QSGMaterial::Blending) - painter->setCompositionMode(QPainter::CompositionMode_Source); - painter->fillRect(rectNode->rect(), rectNode->color()); - painter->setCompositionMode(QPainter::CompositionMode_SourceOver); - } else if (QSGSimpleTextureNode *tn = dynamic_cast(node)) { - QSGTexture *texture = tn->texture(); - if (PixmapTexture *pt = dynamic_cast(texture)) { - const QPixmap &pm = pt->pixmap(); - painter->drawPixmap(tn->rect(), pm, QRectF(0, 0, pm.width(), pm.height())); - } else if (QSGPlainTexture *pt = dynamic_cast(texture)) { - const QImage &im = pt->image(); - painter->drawImage(tn->rect(), im, QRectF(0, 0, im.width(), im.height())); - } else { - Q_UNREACHABLE(); - } - } else if (QQuickShaderEffectNode *sn = dynamic_cast(node)) { - Q_UNUSED(sn) - } else { - Q_UNREACHABLE(); - } - return true; -} - -void RenderingVisitor::endVisit(QSGGeometryNode *) -{ -} - -bool RenderingVisitor::visit(QSGOpacityNode *node) -{ - painter->save(); - - const qreal newOpacity = painter->opacity() * node->opacity(); - if (qFuzzyIsNull(newOpacity)) - return false; - - painter->setOpacity(newOpacity); - return true; -} - -void RenderingVisitor::endVisit(QSGOpacityNode *) -{ - painter->restore(); -} - -bool RenderingVisitor::visit(QSGImageNode *node) -{ - static_cast(node)->paint(painter); - return true; -} - -void RenderingVisitor::endVisit(QSGImageNode *) -{ -} - -bool RenderingVisitor::visit(QSGPainterNode *node) -{ - static_cast(node)->paint(painter); - return true; -} - -void RenderingVisitor::endVisit(QSGPainterNode *node) -{ - -} - -bool RenderingVisitor::visit(QSGRectangleNode *node) -{ - static_cast(node)->paint(painter); - return true; -} - -void RenderingVisitor::endVisit(QSGRectangleNode *) -{ -} - -bool RenderingVisitor::visit(QSGGlyphNode *node) -{ - static_cast(node)->paint(painter); - return true; -} - -void RenderingVisitor::endVisit(QSGGlyphNode *) -{ -} - -bool RenderingVisitor::visit(QSGNinePatchNode *node) -{ - static_cast(node)->paint(painter); - return true; -} - -void RenderingVisitor::endVisit(QSGNinePatchNode *) -{ -} - -bool RenderingVisitor::visit(QSGRootNode *) -{ - return true; -} - -void RenderingVisitor::endVisit(QSGRootNode *) -{ -} diff --git a/softwarecontext/renderingvisitor.h b/softwarecontext/renderingvisitor.h deleted file mode 100644 index d65faa4293..0000000000 --- a/softwarecontext/renderingvisitor.h +++ /dev/null @@ -1,55 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef RENDERINGVISITOR_H -#define RENDERINGVISITOR_H - -#include - -class RenderingVisitor : public QSGNodeVisitorEx -{ -public: - RenderingVisitor(QPainter *painter); - - virtual bool visit(QSGTransformNode *node); - virtual void endVisit(QSGTransformNode *); - virtual bool visit(QSGClipNode *node); - virtual void endVisit(QSGClipNode *node); - virtual bool visit(QSGGeometryNode *node); - virtual void endVisit(QSGGeometryNode *node); - virtual bool visit(QSGOpacityNode *node); - virtual void endVisit(QSGOpacityNode *node); - virtual bool visit(QSGImageNode *node); - virtual void endVisit(QSGImageNode *node); - virtual bool visit(QSGPainterNode *node); - virtual void endVisit(QSGPainterNode *node); - virtual bool visit(QSGRectangleNode *node); - virtual void endVisit(QSGRectangleNode *node); - virtual bool visit(QSGGlyphNode *node); - virtual void endVisit(QSGGlyphNode *node); - virtual bool visit(QSGNinePatchNode *node); - virtual void endVisit(QSGNinePatchNode *); - virtual bool visit(QSGRootNode *); - virtual void endVisit(QSGRootNode *); - -private: - QPainter *painter; -}; - -#endif // RENDERINGVISITOR_H diff --git a/softwarecontext/renderloop.cpp b/softwarecontext/renderloop.cpp deleted file mode 100644 index 7dd20342bc..0000000000 --- a/softwarecontext/renderloop.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "renderloop.h" - -#include "context.h" -#include -#include -#include - -// Used for very high-level info about the renderering and gl context -// Includes GL_VERSION, type of render loop, atlas size, etc. -Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.info") - -// Used to debug the renderloop logic. Primarily useful for platform integrators -// and when investigating the render loop logic. -Q_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP, "qt.scenegraph.renderloop") - - -// GLSL shader compilation -Q_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation") - -// polish, animations, sync, render and swap in the render loop -Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop") - -// Texture uploads and swizzling -Q_LOGGING_CATEGORY(QSG_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture") - -// Glyph preparation (only for distance fields atm) -Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph") - -// Timing inside the renderer base class -Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer") - -RenderLoop::RenderLoop() - : eventPending(false) -{ - sg = QSGContext::createDefaultContext(); - rc = sg->createRenderContext(); -} - -RenderLoop::~RenderLoop() -{ - delete rc; - delete sg; -} - -void RenderLoop::show(QQuickWindow *window) -{ - WindowData data; - data.updatePending = false; - data.grabOnly = false; - m_windows[window] = data; - - maybeUpdate(window); -} - -void RenderLoop::hide(QQuickWindow *window) -{ - if (!m_windows.contains(window)) - return; - - m_windows.remove(window); - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - cd->fireAboutToStop(); - cd->cleanupNodesOnShutdown(); - - if (m_windows.size() == 0) { - if (!cd->persistentSceneGraph) { - rc->invalidate(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } - } -} - -void RenderLoop::windowDestroyed(QQuickWindow *window) -{ - hide(window); - if (m_windows.size() == 0) { - rc->invalidate(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } -} - -void RenderLoop::renderWindow(QQuickWindow *window) -{ - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - if (!cd->isRenderable() || !m_windows.contains(window)) - return; - - WindowData &data = const_cast(m_windows[window]); - - // ### create QPainter and set up pointer to current window/painter - SoftwareContext::RenderContext *ctx = static_cast(cd->context); - ctx->currentWindow = window; - ctx->initializeIfNeeded(); - - bool alsoSwap = data.updatePending; - data.updatePending = false; - - if (!data.grabOnly) { - cd->flushDelayedTouchEvent(); - // Event delivery/processing triggered the window to be deleted or stop rendering. - if (!m_windows.contains(window)) - return; - } - QElapsedTimer renderTimer; - qint64 renderTime = 0, syncTime = 0, polishTime = 0; - bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; - if (profileFrames) - renderTimer.start(); - - cd->polishItems(); - - if (profileFrames) { - polishTime = renderTimer.nsecsElapsed(); - Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphPolishFrame, (polishTime)); - } - - emit window->afterAnimating(); - - cd->syncSceneGraph(); - - if (profileFrames) - syncTime = renderTimer.nsecsElapsed(); - - cd->renderSceneGraph(window->size()); - - if (profileFrames) - renderTime = renderTimer.nsecsElapsed(); - - if (data.grabOnly) { - // #### grabContent = qt_gl_read_framebuffer(window->size() * window->devicePixelRatio(), false, false); - data.grabOnly = false; - } - - if (alsoSwap && window->isVisible()) { -// ### gl->swapBuffers(window); - cd->fireFrameSwapped(); - } - - qint64 swapTime = 0; - if (profileFrames) - swapTime = renderTimer.nsecsElapsed(); - - if (QSG_LOG_TIME_RENDERLOOP().isDebugEnabled()) { - static QTime lastFrameTime = QTime::currentTime(); - qCDebug(QSG_LOG_TIME_RENDERLOOP, - "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", - int(swapTime / 1000000), - int(polishTime / 1000000), - int((syncTime - polishTime) / 1000000), - int((renderTime - syncTime) / 1000000), - int((swapTime - renderTime) / 10000000), - int(lastFrameTime.msecsTo(QTime::currentTime()))); - lastFrameTime = QTime::currentTime(); - } - - Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphRenderLoopFrame, ( - syncTime - polishTime, - renderTime - syncTime, - swapTime - renderTime)); - - // Might have been set during syncSceneGraph() - if (data.updatePending) - maybeUpdate(window); -} - -void RenderLoop::exposureChanged(QQuickWindow *window) -{ - if (window->isExposed()) { - m_windows[window].updatePending = true; - renderWindow(window); - } -} - -QImage RenderLoop::grab(QQuickWindow *window) -{ - if (!m_windows.contains(window)) - return QImage(); - - m_windows[window].grabOnly = true; - - renderWindow(window); - - QImage grabbed = grabContent; - grabContent = QImage(); - return grabbed; -} - - - -void RenderLoop::maybeUpdate(QQuickWindow *window) -{ - if (!m_windows.contains(window)) - return; - - m_windows[window].updatePending = true; - - if (!eventPending) { - const int exhaust_delay = 5; - m_update_timer = startTimer(exhaust_delay, Qt::PreciseTimer); - eventPending = true; - } -} - -QSurface::SurfaceType RenderLoop::windowSurfaceType() const -{ - return QSurface::RasterSurface; -} - - - -QSGContext *RenderLoop::sceneGraphContext() const -{ - return sg; -} - - -bool RenderLoop::event(QEvent *e) -{ - if (e->type() == QEvent::Timer) { - eventPending = false; - killTimer(m_update_timer); - m_update_timer = 0; - for (QHash::const_iterator it = m_windows.constBegin(); - it != m_windows.constEnd(); ++it) { - const WindowData &data = it.value(); - if (data.updatePending) - renderWindow(it.key()); - } - return true; - } - return QObject::event(e); -} diff --git a/softwarecontext/renderloop.h b/softwarecontext/renderloop.h deleted file mode 100644 index b6993a830a..0000000000 --- a/softwarecontext/renderloop.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef RENDERLOOP_H -#define RENDERLOOP_H - -#include - -class RenderLoop : public QSGRenderLoop -{ - Q_OBJECT -public: - RenderLoop(); - ~RenderLoop(); - - void show(QQuickWindow *window); - void hide(QQuickWindow *window); - - void windowDestroyed(QQuickWindow *window); - - void renderWindow(QQuickWindow *window); - void exposureChanged(QQuickWindow *window); - QImage grab(QQuickWindow *window); - - void maybeUpdate(QQuickWindow *window); - void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. - - void releaseResources(QQuickWindow *) { } - - virtual QSurface::SurfaceType windowSurfaceType() const; - - QAnimationDriver *animationDriver() const { return 0; } - - QSGContext *sceneGraphContext() const; - QSGRenderContext *createRenderContext(QSGContext *) const { return rc; } - - bool event(QEvent *); - - struct WindowData { - bool updatePending : 1; - bool grabOnly : 1; - }; - - QHash m_windows; - - QSGContext *sg; - QSGRenderContext *rc; - - QImage grabContent; - int m_update_timer; - - bool eventPending; - -}; - -#endif // RENDERLOOP_H diff --git a/softwarecontext/softwarecontext.json b/softwarecontext/softwarecontext.json deleted file mode 100644 index ca87c6c38a..0000000000 --- a/softwarecontext/softwarecontext.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Keys": ["softwarecontext"] -} diff --git a/softwarecontext/softwarecontext.pro b/softwarecontext/softwarecontext.pro deleted file mode 100644 index d60260c1eb..0000000000 --- a/softwarecontext/softwarecontext.pro +++ /dev/null @@ -1,43 +0,0 @@ -TEMPLATE=lib -TARGET=softwarecontext - -CONFIG += plugin - -QT += gui-private core-private quick-private qml-private - -SOURCES += \ - context.cpp \ - pluginmain.cpp \ - renderloop.cpp \ - rectanglenode.cpp \ - imagenode.cpp \ - pixmaptexture.cpp \ - glyphnode.cpp \ - renderingvisitor.cpp \ - ninepatchnode.cpp \ - softwarelayer.cpp \ - threadedrenderloop.cpp \ - painternode.cpp - -HEADERS += \ - context.h \ - pluginmain.h \ - renderloop.h \ - rectanglenode.h \ - imagenode.h \ - pixmaptexture.h \ - glyphnode.h \ - renderingvisitor.h \ - ninepatchnode.h \ - softwarelayer.h \ - threadedrenderloop.h \ - painternode.h - -OTHER_FILES += softwarecontext.json - -target.path += $$[QT_INSTALL_PLUGINS]/scenegraph - -files.path += $$[QT_INSTALL_PLUGINS]/scenegraph - -INSTALLS += target files - diff --git a/softwarecontext/softwarelayer.cpp b/softwarecontext/softwarelayer.cpp deleted file mode 100644 index 70cb73fdaa..0000000000 --- a/softwarecontext/softwarelayer.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "softwarelayer.h" - -#include "context.h" - -SoftwareLayer::SoftwareLayer(QSGRenderContext *renderContext) - : m_item(0) - , m_shaderSourceNode(0) - , m_context(renderContext) - , m_renderer(0) - , m_device_pixel_ratio(1) - , m_live(true) - , m_grab(true) - , m_recursive(false) - , m_dirtyTexture(true) -{ - -} - -SoftwareLayer::~SoftwareLayer() -{ - invalidated(); -} - -int SoftwareLayer::textureId() const -{ - return 0; -} - -QSize SoftwareLayer::textureSize() const -{ - return m_pixmap.size(); -} - -bool SoftwareLayer::hasAlphaChannel() const -{ - return m_pixmap.hasAlphaChannel(); -} - -bool SoftwareLayer::hasMipmaps() const -{ - return false; -} - -void SoftwareLayer::bind() -{ -} - -bool SoftwareLayer::updateTexture() -{ - bool doGrab = (m_live || m_grab) && m_dirtyTexture; - if (doGrab) - grab(); - if (m_grab) - emit scheduledUpdateCompleted(); - m_grab = false; - return doGrab; -} - -void SoftwareLayer::setItem(QSGNode *item) -{ - if (item == m_item) - return; - m_item = item; - - if (m_live && !m_item) - m_pixmap = QPixmap(); - - markDirtyTexture(); -} - -void SoftwareLayer::setShaderSourceNode(QSGNode *node) -{ - m_shaderSourceNode = node; -} - -void SoftwareLayer::setRect(const QRectF &rect) -{ - if (rect == m_rect) - return; - m_rect = rect; - markDirtyTexture(); -} - -void SoftwareLayer::setSize(const QSize &size) -{ - if (size == m_size) - return; - m_size = size; - - if (m_live && m_size.isNull()) - m_pixmap = QPixmap(); - - markDirtyTexture(); -} - -void SoftwareLayer::scheduleUpdate() -{ - if (m_grab) - return; - m_grab = true; - if (m_dirtyTexture) { - emit updateRequested(); - if (m_shaderSourceNode) - m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial); - } -} - -QImage SoftwareLayer::toImage() const -{ - return m_pixmap.toImage(); -} - -void SoftwareLayer::setLive(bool live) -{ - if (live == m_live) - return; - m_live = live; - - if (m_live && (!m_item || m_size.isNull())) - m_pixmap = QPixmap(); - - markDirtyTexture(); -} - -void SoftwareLayer::setRecursive(bool recursive) -{ - m_recursive = recursive; -} - -void SoftwareLayer::setFormat(GLenum) -{ -} - -void SoftwareLayer::setHasMipmaps(bool) -{ -} - -void SoftwareLayer::setDevicePixelRatio(qreal ratio) -{ - m_device_pixel_ratio = ratio; -} - -void SoftwareLayer::markDirtyTexture() -{ - m_dirtyTexture = true; - if (m_live || m_grab) { - emit updateRequested(); - if (m_shaderSourceNode) - m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial); - } -} - -void SoftwareLayer::invalidated() -{ - delete m_renderer; - m_renderer = 0; -} - -void SoftwareLayer::grab() -{ - if (!m_item || m_size.isNull()) { - m_pixmap = QPixmap(); - m_dirtyTexture = false; - return; - } - QSGNode *root = m_item; - while (root->firstChild() && root->type() != QSGNode::RootNodeType) - root = root->firstChild(); - if (root->type() != QSGNode::RootNodeType) - return; - - if (!m_renderer) { - m_renderer = new SoftwareContext::PixmapRenderer(m_context); - connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture())); - } - m_renderer->setDevicePixelRatio(m_device_pixel_ratio); - m_renderer->setRootNode(static_cast(root)); - - if (m_pixmap.size() != m_size) - m_pixmap = QPixmap(m_size); - - // Render texture. - root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update. - m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update. - - m_dirtyTexture = false; - - m_renderer->setDeviceRect(m_size); - m_renderer->setViewportRect(m_size); - QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height()); - m_renderer->setProjectionMatrixToRect(mirrored); - m_renderer->setClearColor(Qt::transparent); - - m_renderer->render(&m_pixmap); - - root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update. - - if (m_recursive) - markDirtyTexture(); // Continuously update if 'live' and 'recursive'. -} diff --git a/softwarecontext/softwarelayer.h b/softwarecontext/softwarelayer.h deleted file mode 100644 index 3352e6085e..0000000000 --- a/softwarecontext/softwarelayer.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef SOFTWARELAYER_H -#define SOFTWARELAYER_H - -#include -#include - -namespace SoftwareContext { -class PixmapRenderer; -} - -class SoftwareLayer : public QSGLayer -{ - Q_OBJECT -public: - SoftwareLayer(QSGRenderContext *renderContext); - ~SoftwareLayer(); - - const QPixmap &pixmap() const { return m_pixmap; } - - // QSGTexture interface -public: - virtual int textureId() const; - virtual QSize textureSize() const; - virtual bool hasAlphaChannel() const; - virtual bool hasMipmaps() const; - virtual void bind(); - - // QSGDynamicTexture interface -public: - virtual bool updateTexture(); - - // QSGLayer interface -public: - virtual void setItem(QSGNode *item); - virtual void setShaderSourceNode(QSGNode *node); - virtual void setRect(const QRectF &rect); - virtual void setSize(const QSize &size); - virtual void scheduleUpdate(); - virtual QImage toImage() const; - virtual void setLive(bool live); - virtual void setRecursive(bool recursive); - virtual void setFormat(GLenum); - virtual void setHasMipmaps(bool); - virtual void setDevicePixelRatio(qreal ratio); - -public slots: - virtual void markDirtyTexture(); - virtual void invalidated(); - -private: - void grab(); - - QSGNode *m_item; - QSGNode *m_shaderSourceNode; - QSGRenderContext *m_context; - SoftwareContext::PixmapRenderer *m_renderer; - QRectF m_rect; - QSize m_size; - QPixmap m_pixmap; - qreal m_device_pixel_ratio; - bool m_live; - bool m_grab; - bool m_recursive; - bool m_dirtyTexture; -}; - -#endif // SOFTWARELAYER_H diff --git a/softwarecontext/threadedrenderloop.cpp b/softwarecontext/threadedrenderloop.cpp deleted file mode 100644 index 6b8991552d..0000000000 --- a/softwarecontext/threadedrenderloop.cpp +++ /dev/null @@ -1,1129 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** Copyright (C) 2014 Jolla Ltd, author: -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "threadedrenderloop.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include "context.h" - -/* - Overall design: - - There are two classes here. ThreadedRenderLoop and - RenderThread. All communication between the two is based on - event passing and we have a number of custom events. - - In this implementation, the render thread is never blocked and the - GUI thread will initiate a polishAndSync which will block and wait - for the render thread to pick it up and release the block only - after the render thread is done syncing. The reason for this - is: - - 1. Clear blocking paradigm. We only have one real "block" point - (polishAndSync()) and all blocking is initiated by GUI and picked - up by Render at specific times based on events. This makes the - execution deterministic. - - 2. Render does not have to interact with GUI. This is done so that - the render thread can run its own animation system which stays - alive even when the GUI thread is blocked doing i/o, object - instantiation, QPainter-painting or any other non-trivial task. - - --- - - There is one thread per window and one opengl context per thread. - - --- - - The render thread has affinity to the GUI thread until a window - is shown. From that moment and until the window is destroyed, it - will have affinity to the render thread. (moved back at the end - of run for cleanup). - - --- - - The render loop is active while any window is exposed. All visible - windows are tracked, but only exposed windows are actually added to - the render thread and rendered. That means that if all windows are - obscured, we might end up cleaning up the SG and GL context (if all - windows have disabled persistency). Especially for multiprocess, - low-end systems, this should be quite important. - - */ - -QT_BEGIN_NAMESPACE - -#define QSG_RT_PAD " (RT)" - -static int get_env_int(const char *name, int defaultValue) -{ - QByteArray content = qgetenv(name); - - bool ok = false; - int value = content.toInt(&ok); - return ok ? value : defaultValue; -} - - -static inline int qsgrl_animation_interval() { - qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate(); - // To work around that some platforms wrongfully return 0 or something - // bogus for refreshrate - if (refreshRate < 1) - return 16; - return int(1000 / refreshRate); -} - - -static QElapsedTimer threadTimer; -static qint64 syncTime; -static qint64 renderTime; -static qint64 sinceLastTime; - -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -// RL: Render Loop -// RT: Render Thread - -// Passed from the RL to the RT when a window is removed obscured and -// should be removed from the render loop. -const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); - -// Passed from the RL to RT when GUI has been locked, waiting for sync -// (updatePaintNode()) -const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); - -// Passed by the RT to itself to trigger another render pass. This is -// typically a result of QQuickWindow::update(). -const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); - -// Passed by the RL to the RT to free up maybe release SG and GL contexts -// if no windows are rendering. -const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); - -// Passed by the RL to the RT when a QQuickWindow::grabWindow() is -// called. -const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); - -template T *windowFor(const QList list, QQuickWindow *window) -{ - for (int i=0; i(&t); - } - return 0; -} - - -class WMWindowEvent : public QEvent -{ -public: - WMWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } - QQuickWindow *window; -}; - -class WMTryReleaseEvent : public WMWindowEvent -{ -public: - WMTryReleaseEvent(QQuickWindow *win, bool destroy, QOffscreenSurface *fallback) - : WMWindowEvent(win, WM_TryRelease) - , inDestructor(destroy) - , fallbackSurface(fallback) - {} - - bool inDestructor; - QOffscreenSurface *fallbackSurface; -}; - -class WMSyncEvent : public WMWindowEvent -{ -public: - WMSyncEvent(QQuickWindow *c, bool inExpose) : WMWindowEvent(c, WM_RequestSync), size(c->size()), syncInExpose(inExpose) { } - QSize size; - bool syncInExpose; -}; - - -class WMGrabEvent : public WMWindowEvent -{ -public: - WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {} - QImage *image; -}; - - -class RenderThreadEventQueue : public QQueue -{ -public: - RenderThreadEventQueue() - : waiting(false) - { - } - - void addEvent(QEvent *e) { - mutex.lock(); - enqueue(e); - if (waiting) - condition.wakeOne(); - mutex.unlock(); - } - - QEvent *takeEvent(bool wait) { - mutex.lock(); - if (size() == 0 && wait) { - waiting = true; - condition.wait(&mutex); - waiting = false; - } - QEvent *e = dequeue(); - mutex.unlock(); - return e; - } - - bool hasMoreEvents() { - mutex.lock(); - bool has = !isEmpty(); - mutex.unlock(); - return has; - } - -private: - QMutex mutex; - QWaitCondition condition; - bool waiting; -}; - - -class RenderThread : public QThread -{ - Q_OBJECT -public: - RenderThread(ThreadedRenderLoop *w, QSGRenderContext *renderContext) - : wm(w) - , sgrc(renderContext) - , animatorDriver(0) - , pendingUpdate(0) - , sleeping(false) - , syncResultedInChanges(false) - , active(false) - , window(0) - , stopEventProcessing(false) - { -#if defined(Q_OS_QNX) && !defined(Q_OS_BLACKBERRY) && defined(Q_PROCESSOR_X86) - // The SDP 6.6.0 x86 MESA driver requires a larger stack than the default. - setStackSize(1024 * 1024); -#endif - vsyncDelta = qsgrl_animation_interval(); - } - - ~RenderThread() - { - delete sgrc; - } - - bool event(QEvent *); - void run(); - - void syncAndRender(); - void sync(bool inExpose); - - void requestRepaint() - { - if (sleeping) - stopEventProcessing = true; - if (window) - pendingUpdate |= RepaintRequest; - } - - void processEventsAndWaitForMore(); - void processEvents(); - void postEvent(QEvent *e); - -public slots: - void sceneGraphChanged() { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "sceneGraphChanged"; - syncResultedInChanges = true; - } - -public: - enum UpdateRequest { - SyncRequest = 0x01, - RepaintRequest = 0x02, - ExposeRequest = 0x04 | RepaintRequest | SyncRequest - }; - - ThreadedRenderLoop *wm; - QSGRenderContext *sgrc; - - QAnimationDriver *animatorDriver; - - uint pendingUpdate; - bool sleeping; - bool syncResultedInChanges; - - volatile bool active; - - float vsyncDelta; - - QMutex mutex; - QWaitCondition waitCondition; - - QElapsedTimer m_timer; - - QQuickWindow *window; // Will be 0 when window is not exposed - QSize windowSize; - - // Local event queue stuff... - bool stopEventProcessing; - RenderThreadEventQueue eventQueue; -}; - -bool RenderThread::event(QEvent *e) -{ - switch ((int) e->type()) { - - case WM_Obscure: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Obscure"; - - Q_ASSERT(!window || window == static_cast(e)->window); - - mutex.lock(); - if (window) { - QQuickWindowPrivate::get(window)->fireAboutToStop(); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window removed"; - window = 0; - } - waitCondition.wakeOne(); - mutex.unlock(); - - return true; } - - case WM_RequestSync: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestSync"; - WMSyncEvent *se = static_cast(e); - if (sleeping) - stopEventProcessing = true; - window = se->window; - windowSize = se->size; - - pendingUpdate |= SyncRequest; - if (se->syncInExpose) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- triggered from expose"; - pendingUpdate |= ExposeRequest; - } - return true; } - - case WM_TryRelease: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_TryRelease"; - mutex.lock(); - wm->m_lockedForSync = true; - WMTryReleaseEvent *wme = static_cast(e); - if (!window || wme->inDestructor) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- setting exit flag and invalidating OpenGL"; - active = false; - Q_ASSERT_X(!wme->inDestructor || !active, "RenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down"); - if (sleeping) - stopEventProcessing = true; - } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- not releasing because window is still active"; - } - waitCondition.wakeOne(); - wm->m_lockedForSync = false; - mutex.unlock(); - return true; - } - - case WM_Grab: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Grab"; - WMGrabEvent *ce = static_cast(e); - Q_ASSERT(ce->window == window); - mutex.lock(); - if (window) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync scene graph"; - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - static_cast(d->context)->currentWindow = window; - d->syncSceneGraph(); - - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering scene graph"; - QQuickWindowPrivate::get(window)->renderSceneGraph(windowSize); - - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- grabbing result"; - *ce->image = static_cast(d->renderer)->backingStore()->handle()->toImage(); - } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- waking gui to handle result"; - waitCondition.wakeOne(); - mutex.unlock(); - return true; - } - - case WM_RequestRepaint: - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestPaint"; - // When GUI posts this event, it is followed by a polishAndSync, so we mustn't - // exit the event loop yet. - pendingUpdate |= RepaintRequest; - break; - - default: - break; - } - return QThread::event(e); -} - -/*! - Enters the mutex lock to make sure GUI is blocking and performs - sync, then wakes GUI. - */ -void RenderThread::sync(bool inExpose) -{ - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "sync()"; - mutex.lock(); - - Q_ASSERT_X(wm->m_lockedForSync, "RenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); - - bool current = false; - if (windowSize.width() > 0 && windowSize.height() > 0) - current = true; - if (current) { - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - static_cast(d->context)->currentWindow = window; - bool hadRenderer = d->renderer != 0; - // If the scene graph was touched since the last sync() make sure it sends the - // changed signal. - if (d->renderer) - d->renderer->clearChangedFlag(); - d->syncSceneGraph(); - if (!hadRenderer && d->renderer) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- renderer was created"; - syncResultedInChanges = true; - connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), Qt::DirectConnection); - } - - // Process deferred deletes now, directly after the sync as - // deleteLater on the GUI must now also have resulted in SG changes - // and the delete is a safe operation. - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window has bad size, sync aborted"; - } - - if (!inExpose) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync complete, waking Gui"; - waitCondition.wakeOne(); - mutex.unlock(); - } -} - -void RenderThread::syncAndRender() -{ - bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; - if (profileFrames) { - sinceLastTime = threadTimer.nsecsElapsed(); - threadTimer.start(); - } - - QElapsedTimer waitTimer; - waitTimer.start(); - - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "syncAndRender()"; - - syncResultedInChanges = false; - - uint pending = pendingUpdate; - pendingUpdate = 0; - - if (pending & SyncRequest) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- updatePending, doing sync"; - sync(pending == ExposeRequest); - } - - if (!syncResultedInChanges && ((pending & RepaintRequest) == 0)) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- no changes, render aborted"; - int waitTime = vsyncDelta - (int) waitTimer.elapsed(); - if (waitTime > 0) - msleep(waitTime); - return; - } - - if (profileFrames) - syncTime = threadTimer.nsecsElapsed(); - - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering started"; - - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - - if (animatorDriver->isRunning()) { - d->animationController->lock(); - animatorDriver->advance(); - d->animationController->unlock(); - } - - bool current = false; - if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0) - current = true; - if (current) { - static_cast(d->context)->currentWindow = window; - d->renderSceneGraph(windowSize); - if (profileFrames) - renderTime = threadTimer.nsecsElapsed(); - // ### used to be swappBuffers here - d->fireFrameSwapped(); - } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window not ready, skipping render"; - } - - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering done"; - - // Though it would be more correct to put this block directly after - // fireFrameSwapped in the if (current) branch above, we don't do - // that to avoid blocking the GUI thread in the case where it - // has started rendering with a bad window, causing makeCurrent to - // fail or if the window has a bad size. - if (pending == ExposeRequest) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- wake Gui after initial expose"; - waitCondition.wakeOne(); - mutex.unlock(); - } - - qCDebug(QSG_LOG_TIME_RENDERLOOP, - "Frame rendered with 'threaded' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)", - int(threadTimer.elapsed()), - int((syncTime/1000000)), - int((renderTime - syncTime) / 1000000), - int(threadTimer.elapsed() - renderTime / 1000000)); - - - Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphRenderLoopFrame, ( - syncTime, - renderTime - syncTime, - threadTimer.nsecsElapsed() - renderTime)); -} - - - -void RenderThread::postEvent(QEvent *e) -{ - eventQueue.addEvent(e); -} - - - -void RenderThread::processEvents() -{ - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEvents()"; - while (eventQueue.hasMoreEvents()) { - QEvent *e = eventQueue.takeEvent(false); - event(e); - delete e; - } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEvents()"; -} - -void RenderThread::processEventsAndWaitForMore() -{ - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEventsAndWaitForMore()"; - stopEventProcessing = false; - while (!stopEventProcessing) { - QEvent *e = eventQueue.takeEvent(true); - event(e); - delete e; - } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEventsAndWaitForMore()"; -} - -void RenderThread::run() -{ - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run()"; - animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(0); - animatorDriver->install(); - QUnifiedTimer::instance(true)->setConsistentTiming(QSGRenderLoop::useConsistentTiming()); - if (QQmlDebugService::isDebuggingEnabled()) - QQuickProfiler::registerAnimationCallback(); - - while (active) { - - if (window) { - static_cast(sgrc)->initializeIfNeeded(); - syncAndRender(); - } - - processEvents(); - QCoreApplication::processEvents(); - - if (active && (pendingUpdate == 0 || !window)) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "done drawing, sleep..."; - sleeping = true; - processEventsAndWaitForMore(); - sleeping = false; - } - } - - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run() completed"; - - delete animatorDriver; - animatorDriver = 0; - - sgrc->moveToThread(wm->thread()); - moveToThread(wm->thread()); -} - -ThreadedRenderLoop::ThreadedRenderLoop() - : sg(QSGContext::createDefaultContext()) - , m_animation_timer(0) -{ -#if defined(QSG_RENDER_LOOP_DEBUG) - qsgrl_timer.start(); -#endif - - m_animation_driver = sg->createAnimationDriver(this); - - m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5); - - connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted())); - connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); - - m_animation_driver->install(); -} - -QSGRenderContext *ThreadedRenderLoop::createRenderContext(QSGContext *sg) const -{ - return sg->createRenderContext(); -} - -void ThreadedRenderLoop::maybePostPolishRequest(Window *w) -{ - if (w->timerId == 0) { - qCDebug(QSG_LOG_RENDERLOOP) << "- posting update"; - w->timerId = startTimer(m_exhaust_delay, Qt::PreciseTimer); - } -} - -QAnimationDriver *ThreadedRenderLoop::animationDriver() const -{ - return m_animation_driver; -} - -QSGContext *ThreadedRenderLoop::sceneGraphContext() const -{ - return sg; -} - -bool ThreadedRenderLoop::anyoneShowing() const -{ - for (int i=0; iisVisible() && c->isExposed()) - return true; - } - return false; -} - -bool ThreadedRenderLoop::interleaveIncubation() const -{ - return m_animation_driver->isRunning() && anyoneShowing(); -} - -void ThreadedRenderLoop::animationStarted() -{ - qCDebug(QSG_LOG_RENDERLOOP) << "- animationStarted()"; - startOrStopAnimationTimer(); - - for (int i=0; i(&m_windows.at(i))); -} - -void ThreadedRenderLoop::animationStopped() -{ - qCDebug(QSG_LOG_RENDERLOOP) << "- animationStopped()"; - startOrStopAnimationTimer(); -} - - -void ThreadedRenderLoop::startOrStopAnimationTimer() -{ - int exposedWindows = 0; - Window *theOne = 0; - for (int i=0; iisVisible() && w.window->isExposed()) { - ++exposedWindows; - theOne = &w; - } - } - - if (m_animation_timer != 0 && (exposedWindows == 1 || !m_animation_driver->isRunning())) { - killTimer(m_animation_timer); - m_animation_timer = 0; - // If animations are running, make sure we keep on animating - if (m_animation_driver->isRunning()) - maybePostPolishRequest(theOne); - - } else if (m_animation_timer == 0 && exposedWindows != 1 && m_animation_driver->isRunning()) { - m_animation_timer = startTimer(qsgrl_animation_interval()); - } -} - -/* - Removes this window from the list of tracked windowes in this - window manager. hide() will trigger obscure, which in turn will - stop rendering. - - This function will be called during QWindow::close() which will - also destroy the QPlatformWindow so it is important that this - triggers handleObscurity() and that rendering for that window - is fully done and over with by the time this function exits. - */ - -void ThreadedRenderLoop::hide(QQuickWindow *window) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "hide()" << window; - - if (window->isExposed()) - handleObscurity(windowFor(m_windows, window)); - - releaseResources(window); -} - - -/*! - If the window is first hide it, then perform a complete cleanup - with releaseResources which will take down the GL context and - exit the rendering thread. - */ -void ThreadedRenderLoop::windowDestroyed(QQuickWindow *window) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "begin windowDestroyed()" << window; - - Window *w = windowFor(m_windows, window); - if (!w) - return; - - handleObscurity(w); - releaseResources(w, true); - - RenderThread *thread = w->thread; - while (thread->isRunning()) - QThread::yieldCurrentThread(); - Q_ASSERT(thread->thread() == QThread::currentThread()); - delete thread; - - for (int i=0; iisExposed()) { - handleExposure(window); - } else { - Window *w = windowFor(m_windows, window); - if (w) - handleObscurity(w); - } -} - -/*! - Will post an event to the render thread that this window should - start to render. - */ -void ThreadedRenderLoop::handleExposure(QQuickWindow *window) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "handleExposure()" << window; - - Window *w = windowFor(m_windows, window); - if (!w) { - qCDebug(QSG_LOG_RENDERLOOP) << "- adding window to list"; - Window win; - win.window = window; - win.actualWindowFormat = window->format(); - win.thread = new RenderThread(this, QQuickWindowPrivate::get(window)->context); - win.timerId = 0; - win.updateDuringSync = false; - m_windows << win; - w = &m_windows.last(); - } - - // set this early as we'll be rendering shortly anyway and this avoids - // specialcasing exposure in polishAndSync. - w->thread->window = window; - - if (w->window->width() <= 0 || w->window->height() <= 0 - || !w->window->geometry().intersects(w->window->screen()->availableGeometry())) { -#ifndef QT_NO_DEBUG - qWarning("ThreadedRenderLoop: expose event received for window with invalid geometry."); -#endif - } - - // Because we are going to bind a GL context to it, make sure it - // is created. - if (!w->window->handle()) - w->window->create(); - - // Start render thread if it is not running - if (!w->thread->isRunning()) { - - qCDebug(QSG_LOG_RENDERLOOP) << "- starting render thread"; - - QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; - if (controller->thread() != w->thread) - controller->moveToThread(w->thread); - - w->thread->active = true; - if (w->thread->thread() == QThread::currentThread()) { - w->thread->sgrc->moveToThread(w->thread); - w->thread->moveToThread(w->thread); - } - w->thread->start(); - - } else { - qCDebug(QSG_LOG_RENDERLOOP) << "- render thread already running"; - } - - polishAndSync(w, true); - qCDebug(QSG_LOG_RENDERLOOP) << "- done with handleExposure()"; - - startOrStopAnimationTimer(); -} - -/*! - This function posts an event to the render thread to remove the window - from the list of windowses to render. - - It also starts up the non-vsync animation tick if no more windows - are showing. - */ -void ThreadedRenderLoop::handleObscurity(Window *w) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "handleObscurity()" << w->window; - if (w->thread->isRunning()) { - w->thread->mutex.lock(); - w->thread->postEvent(new WMWindowEvent(w->window, WM_Obscure)); - w->thread->waitCondition.wait(&w->thread->mutex); - w->thread->mutex.unlock(); - } - startOrStopAnimationTimer(); -} - - -void ThreadedRenderLoop::maybeUpdate(QQuickWindow *window) -{ - Window *w = windowFor(m_windows, window); - if (w) - maybeUpdate(w); -} - -/*! - Called whenever the QML scene has changed. Will post an event to - ourselves that a sync is needed. - */ -void ThreadedRenderLoop::maybeUpdate(Window *w) -{ - if (!QCoreApplication::instance()) - return; - - QThread *current = QThread::currentThread(); - if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) { - qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; - return; - } - - if (!w || !w->thread->isRunning()) { - return; - } - qCDebug(QSG_LOG_RENDERLOOP) << "update from item" << w->window; - - // Call this function from the Gui thread later as startTimer cannot be - // called from the render thread. - if (QThread::currentThread() == w->thread) { - qCDebug(QSG_LOG_RENDERLOOP) << "- on render thread"; - w->updateDuringSync = true; - return; - } - - maybePostPolishRequest(w); -} - -/*! - Called when the QQuickWindow should be explicitly repainted. This function - can also be called on the render thread when the GUI thread is blocked to - keep render thread animations alive. - */ -void ThreadedRenderLoop::update(QQuickWindow *window) -{ - Window *w = windowFor(m_windows, window); - if (!w) - return; - - if (w->thread == QThread::currentThread()) { - qCDebug(QSG_LOG_RENDERLOOP) << "update on window - on render thread" << w->window; - w->thread->requestRepaint(); - return; - } - - qCDebug(QSG_LOG_RENDERLOOP) << "update on window" << w->window; - w->thread->postEvent(new QEvent(WM_RequestRepaint)); - maybeUpdate(w); -} - - -void ThreadedRenderLoop::releaseResources(QQuickWindow *window) -{ - Window *w = windowFor(m_windows, window); - if (w) - releaseResources(w, false); -} - -/*! - * Release resources will post an event to the render thread to - * free up the SG and GL resources and exists the render thread. - */ -void ThreadedRenderLoop::releaseResources(Window *w, bool inDestructor) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "releaseResources()" << (inDestructor ? "in destructor" : "in api-call") << w->window; - - w->thread->mutex.lock(); - if (w->thread->isRunning() && w->thread->active) { - QQuickWindow *window = w->window; - - // The platform window might have been destroyed before - // hide/release/windowDestroyed is called, so we need to have a - // fallback surface to perform the cleanup of the scene graph - // and the OpenGL resources. - // QOffscreenSurface must be created on the GUI thread, so we - // create it here and pass it on to RenderThread::invalidateGL() - QOffscreenSurface *fallback = 0; - if (!window->handle()) { - qCDebug(QSG_LOG_RENDERLOOP) << "- using fallback surface"; - fallback = new QOffscreenSurface(); - fallback->setFormat(w->actualWindowFormat); - fallback->create(); - } - - qCDebug(QSG_LOG_RENDERLOOP) << "- posting release request to render thread"; - w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback)); - w->thread->waitCondition.wait(&w->thread->mutex); - delete fallback; - } - w->thread->mutex.unlock(); -} - - -/* Calls polish on all items, then requests synchronization with the render thread - * and blocks until that is complete. Returns false if it aborted; otherwise true. - */ -void ThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; - - QQuickWindow *window = w->window; - if (!w->thread || !w->thread->window) { - qCDebug(QSG_LOG_RENDERLOOP) << "- not exposed, abort"; - killTimer(w->timerId); - w->timerId = 0; - return; - } - - // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); - // The delivery of the event might have caused the window to stop rendering - w = windowFor(m_windows, window); - if (!w || !w->thread || !w->thread->window) { - qCDebug(QSG_LOG_RENDERLOOP) << "- removed after event flushing, abort"; - killTimer(w->timerId); - w->timerId = 0; - return; - } - - - QElapsedTimer timer; - qint64 polishTime = 0; - qint64 waitTime = 0; - qint64 syncTime = 0; - bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; - if (profileFrames) - timer.start(); - - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - d->polishItems(); - - if (profileFrames) - polishTime = timer.nsecsElapsed(); - - w->updateDuringSync = false; - - emit window->afterAnimating(); - - qCDebug(QSG_LOG_RENDERLOOP) << "- lock for sync"; - w->thread->mutex.lock(); - m_lockedForSync = true; - w->thread->postEvent(new WMSyncEvent(window, inExpose)); - - qCDebug(QSG_LOG_RENDERLOOP) << "- wait for sync"; - if (profileFrames) - waitTime = timer.nsecsElapsed(); - w->thread->waitCondition.wait(&w->thread->mutex); - m_lockedForSync = false; - w->thread->mutex.unlock(); - qCDebug(QSG_LOG_RENDERLOOP) << "- unlock after sync"; - - if (profileFrames) - syncTime = timer.nsecsElapsed(); - - killTimer(w->timerId); - w->timerId = 0; - - if (m_animation_timer == 0 && m_animation_driver->isRunning()) { - qCDebug(QSG_LOG_RENDERLOOP) << "- advancing animations"; - m_animation_driver->advance(); - qCDebug(QSG_LOG_RENDERLOOP) << "- animations done.."; - // We need to trigger another sync to keep animations running... - maybePostPolishRequest(w); - emit timeToIncubate(); - } else if (w->updateDuringSync) { - maybePostPolishRequest(w); - } - - qCDebug(QSG_LOG_TIME_RENDERLOOP()).nospace() - << "Frame prepared with 'threaded' renderloop" - << ", polish=" << (polishTime / 1000000) - << ", lock=" << (waitTime - polishTime) / 1000000 - << ", blockedForSync=" << (syncTime - waitTime) / 1000000 - << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 - << " - (on Gui thread) " << window; - - Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphPolishAndSync, ( - polishTime, - waitTime - polishTime, - syncTime - waitTime, - timer.nsecsElapsed() - syncTime)); -} - -ThreadedRenderLoop::Window *ThreadedRenderLoop::windowForTimer(int timerId) const -{ - for (int i=0; i(&m_windows.at(i)); - break; - } - } - return 0; -} - -bool ThreadedRenderLoop::event(QEvent *e) -{ - switch ((int) e->type()) { - - case QEvent::Timer: { - QTimerEvent *te = static_cast(e); - if (te->timerId() == m_animation_timer) { - qCDebug(QSG_LOG_RENDERLOOP) << "- ticking non-visual timer"; - m_animation_driver->advance(); - emit timeToIncubate(); - } else { - qCDebug(QSG_LOG_RENDERLOOP) << "- polish and sync timer"; - Window *w = windowForTimer(te->timerId()); - if (w) - polishAndSync(w); - else - killTimer(te->timerId()); - } - return true; - } - - default: - break; - } - - return QObject::event(e); -} - - - -/* - Locks down GUI and performs a grab the scene graph, then returns the result. - - Since the QML scene could have changed since the last time it was rendered, - we need to polish and sync the scene graph. This might seem superfluous, but - - QML changes could have triggered deleteLater() which could have removed - textures or other objects from the scene graph, causing render to crash. - - Autotests rely on grab(), setProperty(), grab(), compare behavior. - */ - -QImage ThreadedRenderLoop::grab(QQuickWindow *window) -{ - qCDebug(QSG_LOG_RENDERLOOP) << "grab()" << window; - - Window *w = windowFor(m_windows, window); - Q_ASSERT(w); - - if (!w->thread->isRunning()) - return QImage(); - - if (!window->handle()) - window->create(); - - qCDebug(QSG_LOG_RENDERLOOP) << "- polishing items"; - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - d->polishItems(); - - QImage result; - w->thread->mutex.lock(); - m_lockedForSync = true; - qCDebug(QSG_LOG_RENDERLOOP) << "- posting grab event"; - w->thread->postEvent(new WMGrabEvent(window, &result)); - w->thread->waitCondition.wait(&w->thread->mutex); - m_lockedForSync = false; - w->thread->mutex.unlock(); - - qCDebug(QSG_LOG_RENDERLOOP) << "- grab complete"; - - return result; -} - -#include "threadedrenderloop.moc" diff --git a/softwarecontext/threadedrenderloop.h b/softwarecontext/threadedrenderloop.h deleted file mode 100644 index 7e3bcb2d79..0000000000 --- a/softwarecontext/threadedrenderloop.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc -** Copyright (C) 2014 Jolla Ltd, author: -** All rights reserved. -** For any questions to Digia, please use contact form at http://qt.digia.com -** -** This file is part of the Qt SceneGraph Raster Add-on. -** -** $QT_BEGIN_LICENSE$ -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. -** -** If you have questions regarding the use of this file, please use -** contact form at http://qt.digia.com -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef THREADEDRENDERLOOP_H -#define THREADEDRENDERLOOP_H - -#include - -class RenderThread; - -class ThreadedRenderLoop : public QSGRenderLoop -{ - Q_OBJECT -public: - ThreadedRenderLoop(); - - void show(QQuickWindow *) {} - void hide(QQuickWindow *); - - void windowDestroyed(QQuickWindow *window); - void exposureChanged(QQuickWindow *window); - - QImage grab(QQuickWindow *); - - void update(QQuickWindow *window); - void maybeUpdate(QQuickWindow *window); - QSGContext *sceneGraphContext() const; - QSGRenderContext *createRenderContext(QSGContext *) const; - - QAnimationDriver *animationDriver() const; - - void releaseResources(QQuickWindow *window); - - bool event(QEvent *); - - bool interleaveIncubation() const; - -public Q_SLOTS: - void animationStarted(); - void animationStopped(); - -private: - struct Window { - QQuickWindow *window; - RenderThread *thread; - QSurfaceFormat actualWindowFormat; - int timerId; - uint updateDuringSync : 1; - }; - - friend class RenderThread; - - void releaseResources(Window *window, bool inDestructor); - bool checkAndResetForceUpdate(QQuickWindow *window); - Window *windowForTimer(int timerId) const; - - bool anyoneShowing() const; - void initialize(); - - void startOrStopAnimationTimer(); - void maybePostPolishRequest(Window *w); - void waitForReleaseComplete(); - void polishAndSync(Window *w, bool inExpose = false); - void maybeUpdate(Window *window); - - void handleExposure(QQuickWindow *w); - void handleObscurity(Window *w); - - - QSGContext *sg; - QAnimationDriver *m_animation_driver; - QList m_windows; - - int m_animation_timer; - int m_exhaust_delay; - - bool m_lockedForSync; -}; - -#endif // THREADEDRENDERLOOP_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 0000000000..e0aabc4098 --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += scenegraph diff --git a/src/plugins/scenegraph/scenegraph.pro b/src/plugins/scenegraph/scenegraph.pro new file mode 100644 index 0000000000..807b6871b3 --- /dev/null +++ b/src/plugins/scenegraph/scenegraph.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += softwarecontext diff --git a/src/plugins/scenegraph/softwarecontext/context.cpp b/src/plugins/scenegraph/softwarecontext/context.cpp new file mode 100644 index 0000000000..33dc7e87e4 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/context.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "context.h" + +#include "rectanglenode.h" +#include "imagenode.h" +#include "painternode.h" +#include "pixmaptexture.h" +#include "glyphnode.h" +#include "ninepatchnode.h" +#include "renderingvisitor.h" +#include "softwarelayer.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef QSG_NO_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); +#endif + +namespace SoftwareContext +{ + +Renderer::Renderer(QSGRenderContext *context) + : QSGRenderer(context) +{ +} + +void Renderer::renderScene(GLuint fboId) +{ + class B : public QSGBindable + { + public: + void bind() const { } + } bindable; + QSGRenderer::renderScene(bindable); +} + +void Renderer::render() +{ + QWindow *currentWindow = static_cast(m_context)->currentWindow; + if (!m_backingStore) + m_backingStore.reset(new QBackingStore(currentWindow)); + + if (m_backingStore->size() != currentWindow->size()) + m_backingStore->resize(currentWindow->size()); + + const QRect rect(0, 0, currentWindow->width(), currentWindow->height()); + m_backingStore->beginPaint(rect); + + QPaintDevice *device = m_backingStore->paintDevice(); + QPainter painter(device); + painter.setRenderHint(QPainter::Antialiasing); + + painter.fillRect(rect, clearColor()); + RenderingVisitor(&painter).visitChildren(rootNode()); + + m_backingStore->endPaint(); + m_backingStore->flush(rect); +} + +void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) +{ + + QSGRenderer::nodeChanged(node, state); +} + +PixmapRenderer::PixmapRenderer(QSGRenderContext *context) + : QSGRenderer(context) +{ + +} + +void PixmapRenderer::renderScene(GLuint) +{ + Q_UNREACHABLE(); +} + +void PixmapRenderer::render() +{ + Q_UNREACHABLE(); +} + +void PixmapRenderer::render(QPixmap *target) +{ + const QRect rect(0, 0, target->width(), target->height()); + target->fill(clearColor()); + QPainter painter(target); + painter.setRenderHint(QPainter::Antialiasing); + + RenderingVisitor(&painter).visitChildren(rootNode()); +} + +RenderContext::RenderContext(QSGContext *ctx) + : QSGRenderContext(ctx) + , currentWindow(0) + , m_initialized(false) +{ +} +Context::Context(QObject *parent) + : QSGContext(parent) +{ + setDistanceFieldEnabled(false); +} + +QSGRectangleNode *Context::createRectangleNode() +{ + return new RectangleNode(); +} + +QSGImageNode *Context::createImageNode() +{ + return new ImageNode(); +} + +QSGPainterNode *Context::createPainterNode(QQuickPaintedItem *item) +{ + return new PainterNode(item); +} + +QSGGlyphNode *Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) +{ + Q_UNUSED(rc); + Q_UNUSED(preferNativeGlyphNode); + return new GlyphNode(); +} + +QSGNinePatchNode *Context::createNinePatchNode() +{ + return new NinePatchNode(); +} + +QSGLayer *Context::createLayer(QSGRenderContext *renderContext) +{ + return new SoftwareLayer(renderContext); +} + +QSurfaceFormat Context::defaultSurfaceFormat() const +{ + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + format.setRenderableType(QSurfaceFormat::DefaultRenderableType); + format.setMajorVersion(0); + format.setMinorVersion(0); + return format; +} + +void RenderContext::initialize(QOpenGLContext *context) +{ + Q_UNUSED(context) + Q_UNREACHABLE(); +} + +void RenderContext::initializeIfNeeded() +{ + if (m_initialized) + return; + m_initialized = true; + emit initialized(); +} + +void RenderContext::invalidate() +{ + QSGRenderContext::invalidate(); +} + +QSGTexture *RenderContext::createTexture(const QImage &image) const +{ + return new PixmapTexture(image); +} + +QSGTexture *RenderContext::createTextureNoAtlas(const QImage &image) const +{ + return new PixmapTexture(image); +} + +QSGRenderer *RenderContext::createRenderer() +{ + return new Renderer(this); +} + + +void RenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fbo) +{ + QSGRenderContext::renderNextFrame(renderer, fbo); +} + +} // namespace diff --git a/src/plugins/scenegraph/softwarecontext/context.h b/src/plugins/scenegraph/softwarecontext/context.h new file mode 100644 index 0000000000..88fe4e447f --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/context.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONTEXT_H +#define CONTEXT_H + +#include +#include +#include +#include +#include +#include + +namespace SoftwareContext +{ + +class Renderer : public QSGRenderer +{ +public: + Renderer(QSGRenderContext *context); + + virtual void renderScene(GLuint fboId = 0); + + virtual void render(); + + void nodeChanged(QSGNode *node, QSGNode::DirtyState state); + + QBackingStore *backingStore() const { return m_backingStore.data(); } + +private: + QScopedPointer m_backingStore; + QRect m_dirtyRect; +}; + +class PixmapRenderer : public QSGRenderer +{ +public: + PixmapRenderer(QSGRenderContext *context); + + virtual void renderScene(GLuint fboId = 0); + + virtual void render(); + + void render(QPixmap *target); +}; + +class RenderContext : public QSGRenderContext +{ +public: + RenderContext(QSGContext *ctx); + void initialize(QOpenGLContext *context); + void initializeIfNeeded(); + void invalidate(); + void renderNextFrame(QSGRenderer *renderer, GLuint fbo); + QSGTexture *createTexture(const QImage &image) const; + QSGTexture *createTextureNoAtlas(const QImage &image) const; + QSGRenderer *createRenderer(); + + QWindow *currentWindow; + bool m_initialized; +}; + +class Context : public QSGContext +{ + Q_OBJECT +public: + explicit Context(QObject *parent = 0); + + QSGRenderContext *createRenderContext() { return new RenderContext(this); } + + virtual QSGRectangleNode *createRectangleNode(); + virtual QSGImageNode *createImageNode(); + virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item); + virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode); + virtual QSGNinePatchNode *createNinePatchNode(); + virtual QSGLayer *createLayer(QSGRenderContext *renderContext); + virtual QSurfaceFormat defaultSurfaceFormat() const; +}; + +} // namespace + +#endif // CONTEXT_H diff --git a/src/plugins/scenegraph/softwarecontext/glyphnode.cpp b/src/plugins/scenegraph/softwarecontext/glyphnode.cpp new file mode 100644 index 0000000000..eae8b626cb --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/glyphnode.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "glyphnode.h" + +GlyphNode::GlyphNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) + , m_style(QQuickText::Normal) +{ + setMaterial((QSGMaterial*)1); + setGeometry(&m_geometry); +} + + +void GlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + m_position = position; + m_glyphRun = glyphs; +} + +void GlyphNode::setColor(const QColor &color) +{ + m_color = color; +} + +void GlyphNode::setStyle(QQuickText::TextStyle style) +{ + m_style = style; +} + +void GlyphNode::setStyleColor(const QColor &color) +{ + m_styleColor = color; +} + +QPointF GlyphNode::baseLine() const +{ + return QPointF(); +} + +void GlyphNode::setPreferredAntialiasingMode(QSGGlyphNode::AntialiasingMode) +{ +} + +void GlyphNode::update() +{ +} + +void GlyphNode::paint(QPainter *painter) +{ + painter->setBrush(QBrush()); + QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent()); + + switch (m_style) { + case QQuickText::Normal: break; + case QQuickText::Outline: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, 1), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(0, -1), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(1, 0), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(-1, 0), m_glyphRun); + break; + case QQuickText::Raised: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, 1), m_glyphRun); + break; + case QQuickText::Sunken: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, -1), m_glyphRun); + break; + } + + painter->setPen(m_color); + painter->drawGlyphRun(pos, m_glyphRun); +} diff --git a/src/plugins/scenegraph/softwarecontext/glyphnode.h b/src/plugins/scenegraph/softwarecontext/glyphnode.h new file mode 100644 index 0000000000..d34013be5b --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/glyphnode.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef GLYPHNODE_H +#define GLYPHNODE_H + +#include + +class GlyphNode : public QSGGlyphNode +{ +public: + GlyphNode(); + + virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); + virtual void setColor(const QColor &color); + virtual void setStyle(QQuickText::TextStyle style); + virtual void setStyleColor(const QColor &color); + virtual QPointF baseLine() const; + virtual void setPreferredAntialiasingMode(AntialiasingMode); + virtual void update(); + + void paint(QPainter *painter); + +private: + QPointF m_position; + QGlyphRun m_glyphRun; + QColor m_color; + QSGGeometry m_geometry; + QQuickText::TextStyle m_style; + QColor m_styleColor; +}; + +#endif // GLYPHNODE_H diff --git a/src/plugins/scenegraph/softwarecontext/imagenode.cpp b/src/plugins/scenegraph/softwarecontext/imagenode.cpp new file mode 100644 index 0000000000..7718831b65 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/imagenode.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "imagenode.h" + +#include "pixmaptexture.h" +#include "softwarelayer.h" +#include +#include + +// Helper from widgets/styles/qdrawutil.cpp + +namespace SoftwareContext { + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, + const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, + const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) +{ + QPainter::PixmapFragment d; + d.opacity = 1.0; + d.rotation = 0.0; + + QPixmapFragmentsArray opaqueData; + QPixmapFragmentsArray translucentData; + + // source center + const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); + const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); + const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; + const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; + const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; + const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; + // target center + const int targetCenterTop = targetRect.top() + targetMargins.top(); + const int targetCenterLeft = targetRect.left() + targetMargins.left(); + const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; + const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; + const int targetCenterWidth = targetCenterRight - targetCenterLeft; + const int targetCenterHeight = targetCenterBottom - targetCenterTop; + + QVarLengthArray xTarget; // x-coordinates of target rectangles + QVarLengthArray yTarget; // y-coordinates of target rectangles + + int columns = 3; + int rows = 3; + if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) + columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); + if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) + rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); + + xTarget.resize(columns + 1); + yTarget.resize(rows + 1); + + bool oldAA = painter->testRenderHint(QPainter::Antialiasing); + if (painter->paintEngine()->type() != QPaintEngine::OpenGL + && painter->paintEngine()->type() != QPaintEngine::OpenGL2 + && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { + painter->setRenderHint(QPainter::Antialiasing, false); + } + + xTarget[0] = targetRect.left(); + xTarget[1] = targetCenterLeft; + xTarget[columns - 1] = targetCenterRight; + xTarget[columns] = targetRect.left() + targetRect.width(); + + yTarget[0] = targetRect.top(); + yTarget[1] = targetCenterTop; + yTarget[rows - 1] = targetCenterBottom; + yTarget[rows] = targetRect.top() + targetRect.height(); + + qreal dx = targetCenterWidth; + qreal dy = targetCenterHeight; + + switch (rules.horizontal) { + case Qt::StretchTile: + dx = targetCenterWidth; + break; + case Qt::RepeatTile: + dx = sourceCenterWidth; + break; + case Qt::RoundTile: + dx = targetCenterWidth / qreal(columns - 2); + break; + } + + for (int i = 2; i < columns - 1; ++i) + xTarget[i] = xTarget[i - 1] + dx; + + switch (rules.vertical) { + case Qt::StretchTile: + dy = targetCenterHeight; + break; + case Qt::RepeatTile: + dy = sourceCenterHeight; + break; + case Qt::RoundTile: + dy = targetCenterHeight / qreal(rows - 2); + break; + } + + for (int i = 2; i < rows - 1; ++i) + yTarget[i] = yTarget[i - 1] + dy; + + // corners + if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.left(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.right(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopRight) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.left(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.right(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomRight) + opaqueData.append(d); + else + translucentData.append(d); + } + + // horizontal edges + if (targetCenterWidth > 0 && sourceCenterWidth > 0) { + if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceRect.top(); + d.width = sourceCenterWidth; + d.height = sourceMargins.top(); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterBottom; + d.width = sourceCenterWidth; + d.height = sourceMargins.bottom(); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + } + + // vertical edges + if (targetCenterHeight > 0 && sourceCenterHeight > 0) { + if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.left(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.right(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + } + + // center + if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterTop; + d.width = sourceCenterWidth; + d.height = sourceCenterHeight; + d.scaleX = dx / d.width; + d.scaleY = dy / d.height; + + qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; + qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; + + for (int j = 1; j < rows - 1; ++j) { + d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = repeatWidth; + } + if (rules.vertical == Qt::RepeatTile) { + for (int i = 1; i < columns - 1; ++i) + data[data.size() - i].height = repeatHeight; + } + } + + if (opaqueData.size()) + painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); + if (translucentData.size()) + painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); + + if (oldAA) + painter->setRenderHint(QPainter::Antialiasing, true); +} + +} + +ImageNode::ImageNode() + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_texture(0) + , m_mirror(false) + , m_smooth(true) + , m_tileHorizontal(false) + , m_tileVertical(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + + +void ImageNode::setTargetRect(const QRectF &rect) +{ + m_targetRect = rect; +} + +void ImageNode::setInnerTargetRect(const QRectF &rect) +{ + m_innerTargetRect = rect; +} + +void ImageNode::setInnerSourceRect(const QRectF &rect) +{ + m_innerSourceRect = rect; +} + +void ImageNode::setSubSourceRect(const QRectF &rect) +{ + m_subSourceRect = rect; +} + +void ImageNode::setTexture(QSGTexture *texture) +{ + m_texture = texture; +} + +void ImageNode::setMirror(bool mirror) +{ + // ### implement support for mirrored pixmaps + m_mirror = mirror; +} + +void ImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) +{ +} + +void ImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + m_smooth = (filtering == QSGTexture::Nearest); +} + +void ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + m_tileHorizontal = (wrapMode == QSGTexture::Repeat); +} + +void ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + m_tileVertical = (wrapMode == QSGTexture::Repeat); +} + +void ImageNode::update() +{ +} + +void ImageNode::preprocess() +{ + bool doDirty = false; + QSGLayer *t = qobject_cast(m_texture); + if (t) { + doDirty = t->updateTexture(); + markDirty(DirtyGeometry); + } + if (doDirty) + markDirty(DirtyMaterial); +} + +static Qt::TileRule getTileRule(qreal factor) +{ + int ifactor = qRound(factor); + if (qFuzzyCompare(factor, ifactor )) { + if (ifactor == 1 || ifactor == 0) + return Qt::StretchTile; + return Qt::RoundTile; + } + return Qt::RepeatTile; +} + + +void ImageNode::paint(QPainter *painter) +{ + painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + + const QPixmap &pm = pixmap(); + + if (m_innerTargetRect != m_targetRect) { + // border image + QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(), + m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom()); + QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); + SoftwareContext::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), + margins, tilerules, QDrawBorderPixmap::DrawingHints(0)); + return; + } + + if (m_tileHorizontal || m_tileVertical) { + painter->save(); + qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width()); + qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height()); + QMatrix transform(sx, 0, 0, sy, 0, 0); + painter->setMatrix(transform, true); + painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy), + pm, + QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height())); + painter->restore(); + } else { + QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(), + m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height()); + painter->drawPixmap(m_targetRect, pm, sr); + } +} + +const QPixmap &ImageNode::pixmap() const +{ + if (PixmapTexture *pt = qobject_cast(m_texture)) { + return pt->pixmap(); + } else if (SoftwareLayer *layer = qobject_cast(m_texture)) { + return layer->pixmap(); + } else { + qFatal("Image used with invalid texture format."); + } +} diff --git a/src/plugins/scenegraph/softwarecontext/imagenode.h b/src/plugins/scenegraph/softwarecontext/imagenode.h new file mode 100644 index 0000000000..a0c059a889 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/imagenode.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef IMAGENODE_H +#define IMAGENODE_H + +#include +#include + +typedef QVarLengthArray QPixmapFragmentsArray; + +struct QTileRules +{ + inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) + : horizontal(horizontalRule), vertical(verticalRule) {} + inline QTileRules(Qt::TileRule rule = Qt::StretchTile) + : horizontal(rule), vertical(rule) {} + Qt::TileRule horizontal; + Qt::TileRule vertical; +}; + +#ifndef Q_QDOC +// For internal use only. +namespace QDrawBorderPixmap +{ + enum DrawingHint + { + OpaqueTopLeft = 0x0001, + OpaqueTop = 0x0002, + OpaqueTopRight = 0x0004, + OpaqueLeft = 0x0008, + OpaqueCenter = 0x0010, + OpaqueRight = 0x0020, + OpaqueBottomLeft = 0x0040, + OpaqueBottom = 0x0080, + OpaqueBottomRight = 0x0100, + OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight, + OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom, + OpaqueFrame = OpaqueCorners | OpaqueEdges, + OpaqueAll = OpaqueCenter | OpaqueFrame + }; + + Q_DECLARE_FLAGS(DrawingHints, DrawingHint) +} +#endif + +namespace SoftwareContext { + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, + const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, + const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints); + +} + +class ImageNode : public QSGImageNode +{ +public: + ImageNode(); + + virtual void setTargetRect(const QRectF &rect); + virtual void setInnerTargetRect(const QRectF &rect); + virtual void setInnerSourceRect(const QRectF &rect); + virtual void setSubSourceRect(const QRectF &rect); + virtual void setTexture(QSGTexture *texture); + virtual void setMirror(bool mirror); + virtual void setMipmapFiltering(QSGTexture::Filtering filtering); + virtual void setFiltering(QSGTexture::Filtering filtering); + virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode); + virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode); + virtual void update(); + + virtual void preprocess(); + + void paint(QPainter *painter); + +private: + const QPixmap &pixmap() const; + + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + QSGTexture *m_texture; + + bool m_mirror; + bool m_smooth; + bool m_tileHorizontal; + bool m_tileVertical; +}; + +#endif // IMAGENODE_H diff --git a/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp b/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp new file mode 100644 index 0000000000..ab503b7af5 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/ninepatchnode.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "ninepatchnode.h" +#include "pixmaptexture.h" +#include "imagenode.h" + +NinePatchNode::NinePatchNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void NinePatchNode::setTexture(QSGTexture *texture) +{ + PixmapTexture *pt = qobject_cast(texture); + if (!pt) { + qWarning() << "Image used with invalid texture format."; + return; + } + m_pixmap = pt->pixmap(); +} + +void NinePatchNode::setBounds(const QRectF &bounds) +{ + m_bounds = bounds; +} + +void NinePatchNode::setDevicePixelRatio(qreal ratio) +{ + m_pixelRatio = ratio; +} + +void NinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom)); +} + +void NinePatchNode::update() +{ +} + +void NinePatchNode::paint(QPainter *painter) +{ + if (m_margins.isNull()) + painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); + else + SoftwareContext::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), + m_margins, Qt::StretchTile, QDrawBorderPixmap::DrawingHints(0)); +} diff --git a/src/plugins/scenegraph/softwarecontext/ninepatchnode.h b/src/plugins/scenegraph/softwarecontext/ninepatchnode.h new file mode 100644 index 0000000000..2bcd1214e3 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/ninepatchnode.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef NINEPATCHNODE_H +#define NINEPATCHNODE_H + +#include + +class NinePatchNode : public QSGNinePatchNode +{ +public: + NinePatchNode(); + + virtual void setTexture(QSGTexture *texture); + virtual void setBounds(const QRectF &bounds); + virtual void setDevicePixelRatio(qreal ratio); + virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom); + virtual void update(); + + void paint(QPainter *painter); + +private: + QPixmap m_pixmap; + QRectF m_bounds; + qreal m_pixelRatio; + QMargins m_margins; +}; + +#endif // NINEPATCHNODE_H diff --git a/src/plugins/scenegraph/softwarecontext/painternode.cpp b/src/plugins/scenegraph/softwarecontext/painternode.cpp new file mode 100644 index 0000000000..bf5ec5f202 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/painternode.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "painternode.h" +#include "pixmaptexture.h" +#include + +PainterNode::PainterNode(QQuickPaintedItem *item) + : QSGPainterNode() + , m_preferredRenderTarget(QQuickPaintedItem::Image) + , m_actualRenderTarget(QQuickPaintedItem::Image) + , m_item(item) + , m_texture(0) + , m_dirtyContents(false) + , m_opaquePainting(false) + , m_linear_filtering(false) + , m_mipmapping(false) + , m_smoothPainting(false) + , m_extensionsChecked(false) + , m_multisamplingSupported(false) + , m_fastFBOResizing(false) + , m_fillColor(Qt::transparent) + , m_contentsScale(1.0) + , m_dirtyGeometry(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +PainterNode::~PainterNode() +{ + delete m_texture; +} + +void PainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) +{ + if (m_preferredRenderTarget == target) + return; + + m_preferredRenderTarget = target; +} + +void PainterNode::setSize(const QSize &size) +{ + if (size == m_size) + return; + + m_size = size; + + m_dirtyGeometry = true; +} + +void PainterNode::setDirty(const QRect &dirtyRect) +{ + m_dirtyContents = true; + m_dirtyRect = dirtyRect; + markDirty(DirtyMaterial); +} + +void PainterNode::setOpaquePainting(bool opaque) +{ + if (opaque == m_opaquePainting) + return; + + m_opaquePainting = opaque; +} + +void PainterNode::setLinearFiltering(bool linearFiltering) +{ + if (linearFiltering == m_linear_filtering) + return; + + m_linear_filtering = linearFiltering; +} + +void PainterNode::setMipmapping(bool mipmapping) +{ + if (mipmapping == m_mipmapping) + return; + + m_mipmapping = mipmapping; +} + +void PainterNode::setSmoothPainting(bool s) +{ + if (s == m_smoothPainting) + return; + + m_smoothPainting = s; +} + +void PainterNode::setFillColor(const QColor &c) +{ + if (c == m_fillColor) + return; + + m_fillColor = c; + markDirty(DirtyMaterial); +} + +void PainterNode::setContentsScale(qreal s) +{ + if (s == m_contentsScale) + return; + + m_contentsScale = s; + markDirty(DirtyMaterial); +} + +void PainterNode::setFastFBOResizing(bool dynamic) +{ + m_fastFBOResizing = dynamic; +} + +QImage PainterNode::toImage() const +{ + return m_pixmap.toImage(); +} + +void PainterNode::update() +{ + if (m_dirtyGeometry) { + m_pixmap = QPixmap(m_size); + if (!m_opaquePainting) + m_pixmap.fill(Qt::transparent); + + if (m_texture) + delete m_texture; + m_texture = new PixmapTexture(m_pixmap); + } + + if (m_dirtyContents) + paint(); + + m_dirtyGeometry = false; + m_dirtyContents = false; +} + +void PainterNode::paint(QPainter *painter) +{ + painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap); +} + +void PainterNode::paint() +{ + QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect; + + QPainter painter; + + painter.begin(&m_pixmap); + if (m_smoothPainting) { + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + } + + painter.scale(m_contentsScale, m_contentsScale); + + QRect sclip(qFloor(dirtyRect.x()/m_contentsScale), + qFloor(dirtyRect.y()/m_contentsScale), + qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)), + qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale))); + + if (!m_dirtyRect.isNull()) + painter.setClipRect(sclip); + + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(sclip, m_fillColor); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + m_item->paint(&painter); + painter.end(); + + m_dirtyRect = QRect(); +} diff --git a/src/plugins/scenegraph/softwarecontext/painternode.h b/src/plugins/scenegraph/softwarecontext/painternode.h new file mode 100644 index 0000000000..db0d03e28a --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/painternode.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PAINTERNODE_H +#define PAINTERNODE_H + +#include +#include + +#include + +class PainterNode : public QSGPainterNode +{ +public: + PainterNode(QQuickPaintedItem *item); + ~PainterNode(); + + void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target); + + void setSize(const QSize &size); + QSize size() const { return m_size; } + + void setDirty(const QRect &dirtyRect = QRect()); + + void setOpaquePainting(bool opaque); + bool opaquePainting() const { return m_opaquePainting; } + + void setLinearFiltering(bool linearFiltering); + bool linearFiltering() const { return m_linear_filtering; } + + void setMipmapping(bool mipmapping); + bool mipmapping() const { return m_mipmapping; } + + void setSmoothPainting(bool s); + bool smoothPainting() const { return m_smoothPainting; } + + void setFillColor(const QColor &c); + QColor fillColor() const { return m_fillColor; } + + void setContentsScale(qreal s); + qreal contentsScale() const { return m_contentsScale; } + + void setFastFBOResizing(bool dynamic); + bool fastFBOResizing() const { return m_fastFBOResizing; } + + QImage toImage() const; + void update(); + QSGTexture *texture() const { return m_texture; } + + void paint(QPainter *painter); + + void paint(); + +private: + + QQuickPaintedItem::RenderTarget m_preferredRenderTarget; + QQuickPaintedItem::RenderTarget m_actualRenderTarget; + + QQuickPaintedItem *m_item; + + QPixmap m_pixmap; + QSGTexture *m_texture; + + QSize m_size; + bool m_dirtyContents; + QRect m_dirtyRect; + bool m_opaquePainting; + bool m_linear_filtering; + bool m_mipmapping; + bool m_smoothPainting; + bool m_extensionsChecked; + bool m_multisamplingSupported; + bool m_fastFBOResizing; + QColor m_fillColor; + qreal m_contentsScale; + + bool m_dirtyGeometry; +}; + +#endif // PAINTERNODE_H diff --git a/src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp b/src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp new file mode 100644 index 0000000000..d3dd747a9e --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pixmaptexture.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "pixmaptexture.h" + +PixmapTexture::PixmapTexture(const QImage &image) + : m_pixmap(QPixmap::fromImage(image)) +{ +} + +PixmapTexture::PixmapTexture(const QPixmap &pixmap) + : m_pixmap(pixmap) +{ +} + + +int PixmapTexture::textureId() const +{ + return 0; +} + +QSize PixmapTexture::textureSize() const +{ + return m_pixmap.size(); +} + +bool PixmapTexture::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool PixmapTexture::hasMipmaps() const +{ + return false; +} + +void PixmapTexture::bind() +{ + Q_UNREACHABLE(); +} diff --git a/src/plugins/scenegraph/softwarecontext/pixmaptexture.h b/src/plugins/scenegraph/softwarecontext/pixmaptexture.h new file mode 100644 index 0000000000..c3ee09dfe1 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pixmaptexture.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PIXMAPTEXTURE_H +#define PIXMAPTEXTURE_H + +#include + +class PixmapTexture : public QSGTexture +{ + Q_OBJECT +public: + PixmapTexture(const QImage &image); + PixmapTexture(const QPixmap &pixmap); + + virtual int textureId() const; + virtual QSize textureSize() const; + virtual bool hasAlphaChannel() const; + virtual bool hasMipmaps() const; + virtual void bind(); + + const QPixmap &pixmap() const { return m_pixmap; } + +private: + QPixmap m_pixmap; +}; + +#endif // PIXMAPTEXTURE_H diff --git a/src/plugins/scenegraph/softwarecontext/pluginmain.cpp b/src/plugins/scenegraph/softwarecontext/pluginmain.cpp new file mode 100644 index 0000000000..afb91a8ea2 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pluginmain.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "pluginmain.h" +#include "context.h" +#include "renderloop.h" +#include "threadedrenderloop.h" + +ContextPlugin::ContextPlugin(QObject *parent) + : QSGContextPlugin(parent) +{ +} + +QStringList ContextPlugin::keys() const +{ + return QStringList() << QLatin1String("softwarecontext"); +} + +QSGContext *ContextPlugin::create(const QString &) const +{ + if (!instance) + instance = new SoftwareContext::Context(); + return instance; +} + +QSGRenderLoop *ContextPlugin::createWindowManager() +{ + if (qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("basic")) + return new RenderLoop(); + return new ThreadedRenderLoop(); +} + +SoftwareContext::Context *ContextPlugin::instance = 0; + + + diff --git a/src/plugins/scenegraph/softwarecontext/pluginmain.h b/src/plugins/scenegraph/softwarecontext/pluginmain.h new file mode 100644 index 0000000000..fd90823322 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/pluginmain.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLUGINMAIN_H +#define PLUGINMAIN_H + +#include +#include + +#include + +#include "context.h" + +class ContextPlugin : public QSGContextPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSGContextFactoryInterface" FILE "softwarecontext.json") + +public: + ContextPlugin(QObject *parent = 0); + + QStringList keys() const; + QSGContext *create(const QString &key) const; + QSGRenderLoop *createWindowManager(); + + static SoftwareContext::Context *instance; +}; + +#endif // PLUGINMAIN_H diff --git a/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp b/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp new file mode 100644 index 0000000000..054ffd3ecf --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/rectanglenode.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "rectanglenode.h" +#include + +#include + + +RectangleNode::RectangleNode() + : m_penWidth(0) + , m_radius(0) + , m_cornerPixmapIsDirty(true) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void RectangleNode::setRect(const QRectF &rect) +{ + if (m_rect != rect) { + m_rect = rect; + } +} + +void RectangleNode::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + m_cornerPixmapIsDirty = true; + } +} + +void RectangleNode::setPenColor(const QColor &color) +{ + if (m_penColor != color) { + m_penColor = color; + m_cornerPixmapIsDirty = true; + } +} + +void RectangleNode::setPenWidth(qreal width) +{ + if (m_penWidth != width) { + m_penWidth = width; + m_cornerPixmapIsDirty = true; + } +} + +void RectangleNode::setGradientStops(const QGradientStops &stops) +{ + m_stops = stops; + m_cornerPixmapIsDirty = true; +} + +void RectangleNode::setRadius(qreal radius) +{ + if (m_radius != radius) { + m_radius = radius; + m_cornerPixmapIsDirty = true; + } +} + +void RectangleNode::setAligned(bool /*aligned*/) +{ +} + +void RectangleNode::update() +{ + if (!m_penWidth || m_penColor == Qt::transparent) { + m_pen = Qt::NoPen; + } else { + m_pen = QPen(m_penColor); + m_pen.setWidthF(m_penWidth); + } + + if (!m_stops.isEmpty()) { + QLinearGradient gradient(QPoint(0,0), QPoint(0,1)); + gradient.setStops(m_stops); + gradient.setCoordinateMode(QGradient::ObjectBoundingMode); + m_brush = QBrush(gradient); + } else { + m_brush = QBrush(m_color); + } + + if (m_cornerPixmapIsDirty) { + //Generate new corner Pixmap + int radius = qRound(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5f, m_radius)); + + m_cornerPixmap = QPixmap(radius * 2, radius * 2); + m_cornerPixmap.fill(Qt::transparent); + + if (radius > 0) { + QPainter cornerPainter(&m_cornerPixmap); + cornerPainter.setRenderHint(QPainter::Antialiasing); + cornerPainter.setCompositionMode(QPainter::CompositionMode_Source); + + //Paint outer cicle + if (m_penWidth > 0) { + cornerPainter.setPen(Qt::NoPen); + cornerPainter.setBrush(m_penColor); + cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius); + } + + //Paint inner circle + if (radius > m_penWidth) { + cornerPainter.setPen(Qt::NoPen); + if (m_stops.isEmpty()) + cornerPainter.setBrush(m_brush); + else + cornerPainter.setBrush(Qt::transparent); + + QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth); + QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins); + cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius); + } + cornerPainter.end(); + } + m_cornerPixmapIsDirty = false; + } +} + +void RectangleNode::paint(QPainter *painter) +{ + //Radius should never exceeds half of the width or half of the height + int radius = qRound(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); + + QPainter::RenderHints previousRenderHints = painter->renderHints(); + painter->setRenderHint(QPainter::Antialiasing, false); + + if (m_penWidth > 0) { + //Borders can not be more than half the height/width of a rect + double borderWidth = qMin(m_penWidth, m_rect.width() * 0.5); + double borderHeight = qMin(m_penWidth, m_rect.height() * 0.5); + + //Fill 4 border Rects + QRectF borderTop(QPointF(m_rect.x() + radius, m_rect.y()), + QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + borderHeight)); + painter->fillRect(borderTop, m_penColor); + QRectF borderBottom(QPointF(m_rect.x() + radius, m_rect.y() + m_rect.height() - borderHeight), + QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + m_rect.height())); + painter->fillRect(borderBottom, m_penColor); + QRectF borderLeft(QPointF(m_rect.x(), m_rect.y() + radius), + QPointF(m_rect.x() + borderWidth, m_rect.y() + m_rect.height() - radius)); + painter->fillRect(borderLeft, m_penColor); + QRectF borderRight(QPointF(m_rect.x() + m_rect.width() - borderWidth, m_rect.y() + radius), + QPointF(m_rect.x() + m_rect.width(), m_rect.y() + m_rect.height() - radius)); + painter->fillRect(borderRight, m_penColor); + } + + if (radius > 0) { + //blit 4 corners to border + QRectF topLeftCorner(QPointF(m_rect.x(), m_rect.y()), + QPointF(m_rect.x() + radius, m_rect.y() + radius)); + painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, radius, radius)); + QRectF topRightCorner(QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y()), + QPointF(m_rect.x() + m_rect.width(), m_rect.y() + radius)); + painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(radius, 0, radius, radius)); + QRectF bottomLeftCorner(QPointF(m_rect.x(), m_rect.y() + m_rect.height() - radius), + QPointF(m_rect.x() + radius, m_rect.y() + m_rect.height())); + painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, radius, radius, radius)); + QRectF bottomRightCorner(QPointF(m_rect.x() + m_rect.width() - radius, m_rect.y() + m_rect.height() - radius), + QPointF(m_rect.x() + m_rect.width(), m_rect.y() + m_rect.height())); + painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(radius, radius, radius, radius)); + + } + + QRectF brushRect = m_rect.marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth)); + if (brushRect.width() < 0) + brushRect.setWidth(0); + if (brushRect.height() < 0) + brushRect.setHeight(0); + double innerRectRadius = qMax(0.0, radius - m_penWidth); + + //If not completely transparent or has a gradient + if (m_color.alpha() > 0 || !m_stops.empty()) { + if (innerRectRadius > 0) { + //Rounded Rect + if (m_stops.empty()) { + //Rounded Rects without gradient need 3 blits + QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()), + QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height())); + painter->fillRect(centerRect, m_color); + QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius), + QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius)); + painter->fillRect(leftRect, m_color); + QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius), + QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius)); + painter->fillRect(rightRect, m_color); + } else { + //Rounded Rect with gradient (slow) + painter->setPen(Qt::NoPen); + painter->setBrush(m_brush); + painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius); + } + } else { + //non-rounded rects only need 1 blit + painter->fillRect(brushRect, m_brush); + } + } + + painter->setRenderHints(previousRenderHints); +} diff --git a/src/plugins/scenegraph/softwarecontext/rectanglenode.h b/src/plugins/scenegraph/softwarecontext/rectanglenode.h new file mode 100644 index 0000000000..74c0971c82 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/rectanglenode.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef RECTANGLENODE_H +#define RECTANGLENODE_H + +#include + +#include +#include +#include + +class RectangleNode : public QSGRectangleNode +{ +public: + RectangleNode(); + + virtual void setRect(const QRectF &rect); + virtual void setColor(const QColor &color); + virtual void setPenColor(const QColor &color); + virtual void setPenWidth(qreal width); + virtual void setGradientStops(const QGradientStops &stops); + virtual void setRadius(qreal radius); + virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) } + virtual void setAligned(bool aligned); + + virtual void update(); + + void paint(QPainter *); + +private: + QRectF m_rect; + QColor m_color; + QColor m_penColor; + double m_penWidth; + QGradientStops m_stops; + double m_radius; + QPen m_pen; + QBrush m_brush; + + bool m_cornerPixmapIsDirty; + QPixmap m_cornerPixmap; +}; + +#endif // RECTANGLENODE_H diff --git a/src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp b/src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp new file mode 100644 index 0000000000..879c30eb4f --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderingvisitor.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "renderingvisitor.h" + +#include "imagenode.h" +#include "rectanglenode.h" +#include "glyphnode.h" +#include "ninepatchnode.h" +#include "painternode.h" +#include "pixmaptexture.h" + +#include +#include +#include +#include + +RenderingVisitor::RenderingVisitor(QPainter *painter) + : painter(painter) +{ + +} + +bool RenderingVisitor::visit(QSGTransformNode *node) +{ + painter->save(); + painter->setTransform(node->matrix().toTransform(), /*combine*/true); + return true; +} + +void RenderingVisitor::endVisit(QSGTransformNode *) +{ + painter->restore(); +} + +bool RenderingVisitor::visit(QSGClipNode *node) +{ + painter->save(); + painter->setClipRect(node->clipRect(), Qt::IntersectClip); + return true; +} + +void RenderingVisitor::endVisit(QSGClipNode *) +{ + painter->restore(); +} + +bool RenderingVisitor::visit(QSGGeometryNode *node) +{ + if (QSGSimpleRectNode *rectNode = dynamic_cast(node)) { + if (!rectNode->material()->flags() & QSGMaterial::Blending) + painter->setCompositionMode(QPainter::CompositionMode_Source); + painter->fillRect(rectNode->rect(), rectNode->color()); + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + } else if (QSGSimpleTextureNode *tn = dynamic_cast(node)) { + QSGTexture *texture = tn->texture(); + if (PixmapTexture *pt = dynamic_cast(texture)) { + const QPixmap &pm = pt->pixmap(); + painter->drawPixmap(tn->rect(), pm, QRectF(0, 0, pm.width(), pm.height())); + } else if (QSGPlainTexture *pt = dynamic_cast(texture)) { + const QImage &im = pt->image(); + painter->drawImage(tn->rect(), im, QRectF(0, 0, im.width(), im.height())); + } else { + Q_UNREACHABLE(); + } + } else if (QQuickShaderEffectNode *sn = dynamic_cast(node)) { + Q_UNUSED(sn) + } else { + Q_UNREACHABLE(); + } + return true; +} + +void RenderingVisitor::endVisit(QSGGeometryNode *) +{ +} + +bool RenderingVisitor::visit(QSGOpacityNode *node) +{ + painter->save(); + + const qreal newOpacity = painter->opacity() * node->opacity(); + if (qFuzzyIsNull(newOpacity)) + return false; + + painter->setOpacity(newOpacity); + return true; +} + +void RenderingVisitor::endVisit(QSGOpacityNode *) +{ + painter->restore(); +} + +bool RenderingVisitor::visit(QSGImageNode *node) +{ + static_cast(node)->paint(painter); + return true; +} + +void RenderingVisitor::endVisit(QSGImageNode *) +{ +} + +bool RenderingVisitor::visit(QSGPainterNode *node) +{ + static_cast(node)->paint(painter); + return true; +} + +void RenderingVisitor::endVisit(QSGPainterNode *node) +{ + +} + +bool RenderingVisitor::visit(QSGRectangleNode *node) +{ + static_cast(node)->paint(painter); + return true; +} + +void RenderingVisitor::endVisit(QSGRectangleNode *) +{ +} + +bool RenderingVisitor::visit(QSGGlyphNode *node) +{ + static_cast(node)->paint(painter); + return true; +} + +void RenderingVisitor::endVisit(QSGGlyphNode *) +{ +} + +bool RenderingVisitor::visit(QSGNinePatchNode *node) +{ + static_cast(node)->paint(painter); + return true; +} + +void RenderingVisitor::endVisit(QSGNinePatchNode *) +{ +} + +bool RenderingVisitor::visit(QSGRootNode *) +{ + return true; +} + +void RenderingVisitor::endVisit(QSGRootNode *) +{ +} diff --git a/src/plugins/scenegraph/softwarecontext/renderingvisitor.h b/src/plugins/scenegraph/softwarecontext/renderingvisitor.h new file mode 100644 index 0000000000..d65faa4293 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderingvisitor.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef RENDERINGVISITOR_H +#define RENDERINGVISITOR_H + +#include + +class RenderingVisitor : public QSGNodeVisitorEx +{ +public: + RenderingVisitor(QPainter *painter); + + virtual bool visit(QSGTransformNode *node); + virtual void endVisit(QSGTransformNode *); + virtual bool visit(QSGClipNode *node); + virtual void endVisit(QSGClipNode *node); + virtual bool visit(QSGGeometryNode *node); + virtual void endVisit(QSGGeometryNode *node); + virtual bool visit(QSGOpacityNode *node); + virtual void endVisit(QSGOpacityNode *node); + virtual bool visit(QSGImageNode *node); + virtual void endVisit(QSGImageNode *node); + virtual bool visit(QSGPainterNode *node); + virtual void endVisit(QSGPainterNode *node); + virtual bool visit(QSGRectangleNode *node); + virtual void endVisit(QSGRectangleNode *node); + virtual bool visit(QSGGlyphNode *node); + virtual void endVisit(QSGGlyphNode *node); + virtual bool visit(QSGNinePatchNode *node); + virtual void endVisit(QSGNinePatchNode *); + virtual bool visit(QSGRootNode *); + virtual void endVisit(QSGRootNode *); + +private: + QPainter *painter; +}; + +#endif // RENDERINGVISITOR_H diff --git a/src/plugins/scenegraph/softwarecontext/renderloop.cpp b/src/plugins/scenegraph/softwarecontext/renderloop.cpp new file mode 100644 index 0000000000..7dd20342bc --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderloop.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "renderloop.h" + +#include "context.h" +#include +#include +#include + +// Used for very high-level info about the renderering and gl context +// Includes GL_VERSION, type of render loop, atlas size, etc. +Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.info") + +// Used to debug the renderloop logic. Primarily useful for platform integrators +// and when investigating the render loop logic. +Q_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP, "qt.scenegraph.renderloop") + + +// GLSL shader compilation +Q_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation") + +// polish, animations, sync, render and swap in the render loop +Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop") + +// Texture uploads and swizzling +Q_LOGGING_CATEGORY(QSG_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture") + +// Glyph preparation (only for distance fields atm) +Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph") + +// Timing inside the renderer base class +Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer") + +RenderLoop::RenderLoop() + : eventPending(false) +{ + sg = QSGContext::createDefaultContext(); + rc = sg->createRenderContext(); +} + +RenderLoop::~RenderLoop() +{ + delete rc; + delete sg; +} + +void RenderLoop::show(QQuickWindow *window) +{ + WindowData data; + data.updatePending = false; + data.grabOnly = false; + m_windows[window] = data; + + maybeUpdate(window); +} + +void RenderLoop::hide(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows.remove(window); + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->fireAboutToStop(); + cd->cleanupNodesOnShutdown(); + + if (m_windows.size() == 0) { + if (!cd->persistentSceneGraph) { + rc->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + } +} + +void RenderLoop::windowDestroyed(QQuickWindow *window) +{ + hide(window); + if (m_windows.size() == 0) { + rc->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } +} + +void RenderLoop::renderWindow(QQuickWindow *window) +{ + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable() || !m_windows.contains(window)) + return; + + WindowData &data = const_cast(m_windows[window]); + + // ### create QPainter and set up pointer to current window/painter + SoftwareContext::RenderContext *ctx = static_cast(cd->context); + ctx->currentWindow = window; + ctx->initializeIfNeeded(); + + bool alsoSwap = data.updatePending; + data.updatePending = false; + + if (!data.grabOnly) { + cd->flushDelayedTouchEvent(); + // Event delivery/processing triggered the window to be deleted or stop rendering. + if (!m_windows.contains(window)) + return; + } + QElapsedTimer renderTimer; + qint64 renderTime = 0, syncTime = 0, polishTime = 0; + bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; + if (profileFrames) + renderTimer.start(); + + cd->polishItems(); + + if (profileFrames) { + polishTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphPolishFrame, (polishTime)); + } + + emit window->afterAnimating(); + + cd->syncSceneGraph(); + + if (profileFrames) + syncTime = renderTimer.nsecsElapsed(); + + cd->renderSceneGraph(window->size()); + + if (profileFrames) + renderTime = renderTimer.nsecsElapsed(); + + if (data.grabOnly) { + // #### grabContent = qt_gl_read_framebuffer(window->size() * window->devicePixelRatio(), false, false); + data.grabOnly = false; + } + + if (alsoSwap && window->isVisible()) { +// ### gl->swapBuffers(window); + cd->fireFrameSwapped(); + } + + qint64 swapTime = 0; + if (profileFrames) + swapTime = renderTimer.nsecsElapsed(); + + if (QSG_LOG_TIME_RENDERLOOP().isDebugEnabled()) { + static QTime lastFrameTime = QTime::currentTime(); + qCDebug(QSG_LOG_TIME_RENDERLOOP, + "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", + int(swapTime / 1000000), + int(polishTime / 1000000), + int((syncTime - polishTime) / 1000000), + int((renderTime - syncTime) / 1000000), + int((swapTime - renderTime) / 10000000), + int(lastFrameTime.msecsTo(QTime::currentTime()))); + lastFrameTime = QTime::currentTime(); + } + + Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphRenderLoopFrame, ( + syncTime - polishTime, + renderTime - syncTime, + swapTime - renderTime)); + + // Might have been set during syncSceneGraph() + if (data.updatePending) + maybeUpdate(window); +} + +void RenderLoop::exposureChanged(QQuickWindow *window) +{ + if (window->isExposed()) { + m_windows[window].updatePending = true; + renderWindow(window); + } +} + +QImage RenderLoop::grab(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return QImage(); + + m_windows[window].grabOnly = true; + + renderWindow(window); + + QImage grabbed = grabContent; + grabContent = QImage(); + return grabbed; +} + + + +void RenderLoop::maybeUpdate(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows[window].updatePending = true; + + if (!eventPending) { + const int exhaust_delay = 5; + m_update_timer = startTimer(exhaust_delay, Qt::PreciseTimer); + eventPending = true; + } +} + +QSurface::SurfaceType RenderLoop::windowSurfaceType() const +{ + return QSurface::RasterSurface; +} + + + +QSGContext *RenderLoop::sceneGraphContext() const +{ + return sg; +} + + +bool RenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + eventPending = false; + killTimer(m_update_timer); + m_update_timer = 0; + for (QHash::const_iterator it = m_windows.constBegin(); + it != m_windows.constEnd(); ++it) { + const WindowData &data = it.value(); + if (data.updatePending) + renderWindow(it.key()); + } + return true; + } + return QObject::event(e); +} diff --git a/src/plugins/scenegraph/softwarecontext/renderloop.h b/src/plugins/scenegraph/softwarecontext/renderloop.h new file mode 100644 index 0000000000..b6993a830a --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/renderloop.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef RENDERLOOP_H +#define RENDERLOOP_H + +#include + +class RenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + RenderLoop(); + ~RenderLoop(); + + void show(QQuickWindow *window); + void hide(QQuickWindow *window); + + void windowDestroyed(QQuickWindow *window); + + void renderWindow(QQuickWindow *window); + void exposureChanged(QQuickWindow *window); + QImage grab(QQuickWindow *window); + + void maybeUpdate(QQuickWindow *window); + void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. + + void releaseResources(QQuickWindow *) { } + + virtual QSurface::SurfaceType windowSurfaceType() const; + + QAnimationDriver *animationDriver() const { return 0; } + + QSGContext *sceneGraphContext() const; + QSGRenderContext *createRenderContext(QSGContext *) const { return rc; } + + bool event(QEvent *); + + struct WindowData { + bool updatePending : 1; + bool grabOnly : 1; + }; + + QHash m_windows; + + QSGContext *sg; + QSGRenderContext *rc; + + QImage grabContent; + int m_update_timer; + + bool eventPending; + +}; + +#endif // RENDERLOOP_H diff --git a/src/plugins/scenegraph/softwarecontext/softwarecontext.json b/src/plugins/scenegraph/softwarecontext/softwarecontext.json new file mode 100644 index 0000000000..ca87c6c38a --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarecontext.json @@ -0,0 +1,3 @@ +{ + "Keys": ["softwarecontext"] +} diff --git a/src/plugins/scenegraph/softwarecontext/softwarecontext.pro b/src/plugins/scenegraph/softwarecontext/softwarecontext.pro new file mode 100644 index 0000000000..d60260c1eb --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarecontext.pro @@ -0,0 +1,43 @@ +TEMPLATE=lib +TARGET=softwarecontext + +CONFIG += plugin + +QT += gui-private core-private quick-private qml-private + +SOURCES += \ + context.cpp \ + pluginmain.cpp \ + renderloop.cpp \ + rectanglenode.cpp \ + imagenode.cpp \ + pixmaptexture.cpp \ + glyphnode.cpp \ + renderingvisitor.cpp \ + ninepatchnode.cpp \ + softwarelayer.cpp \ + threadedrenderloop.cpp \ + painternode.cpp + +HEADERS += \ + context.h \ + pluginmain.h \ + renderloop.h \ + rectanglenode.h \ + imagenode.h \ + pixmaptexture.h \ + glyphnode.h \ + renderingvisitor.h \ + ninepatchnode.h \ + softwarelayer.h \ + threadedrenderloop.h \ + painternode.h + +OTHER_FILES += softwarecontext.json + +target.path += $$[QT_INSTALL_PLUGINS]/scenegraph + +files.path += $$[QT_INSTALL_PLUGINS]/scenegraph + +INSTALLS += target files + diff --git a/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp b/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp new file mode 100644 index 0000000000..70cb73fdaa --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarelayer.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "softwarelayer.h" + +#include "context.h" + +SoftwareLayer::SoftwareLayer(QSGRenderContext *renderContext) + : m_item(0) + , m_shaderSourceNode(0) + , m_context(renderContext) + , m_renderer(0) + , m_device_pixel_ratio(1) + , m_live(true) + , m_grab(true) + , m_recursive(false) + , m_dirtyTexture(true) +{ + +} + +SoftwareLayer::~SoftwareLayer() +{ + invalidated(); +} + +int SoftwareLayer::textureId() const +{ + return 0; +} + +QSize SoftwareLayer::textureSize() const +{ + return m_pixmap.size(); +} + +bool SoftwareLayer::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool SoftwareLayer::hasMipmaps() const +{ + return false; +} + +void SoftwareLayer::bind() +{ +} + +bool SoftwareLayer::updateTexture() +{ + bool doGrab = (m_live || m_grab) && m_dirtyTexture; + if (doGrab) + grab(); + if (m_grab) + emit scheduledUpdateCompleted(); + m_grab = false; + return doGrab; +} + +void SoftwareLayer::setItem(QSGNode *item) +{ + if (item == m_item) + return; + m_item = item; + + if (m_live && !m_item) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::setShaderSourceNode(QSGNode *node) +{ + m_shaderSourceNode = node; +} + +void SoftwareLayer::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + markDirtyTexture(); +} + +void SoftwareLayer::setSize(const QSize &size) +{ + if (size == m_size) + return; + m_size = size; + + if (m_live && m_size.isNull()) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::scheduleUpdate() +{ + if (m_grab) + return; + m_grab = true; + if (m_dirtyTexture) { + emit updateRequested(); + if (m_shaderSourceNode) + m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial); + } +} + +QImage SoftwareLayer::toImage() const +{ + return m_pixmap.toImage(); +} + +void SoftwareLayer::setLive(bool live) +{ + if (live == m_live) + return; + m_live = live; + + if (m_live && (!m_item || m_size.isNull())) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void SoftwareLayer::setRecursive(bool recursive) +{ + m_recursive = recursive; +} + +void SoftwareLayer::setFormat(GLenum) +{ +} + +void SoftwareLayer::setHasMipmaps(bool) +{ +} + +void SoftwareLayer::setDevicePixelRatio(qreal ratio) +{ + m_device_pixel_ratio = ratio; +} + +void SoftwareLayer::markDirtyTexture() +{ + m_dirtyTexture = true; + if (m_live || m_grab) { + emit updateRequested(); + if (m_shaderSourceNode) + m_shaderSourceNode->markDirty(QSGNode::DirtyMaterial); + } +} + +void SoftwareLayer::invalidated() +{ + delete m_renderer; + m_renderer = 0; +} + +void SoftwareLayer::grab() +{ + if (!m_item || m_size.isNull()) { + m_pixmap = QPixmap(); + m_dirtyTexture = false; + return; + } + QSGNode *root = m_item; + while (root->firstChild() && root->type() != QSGNode::RootNodeType) + root = root->firstChild(); + if (root->type() != QSGNode::RootNodeType) + return; + + if (!m_renderer) { + m_renderer = new SoftwareContext::PixmapRenderer(m_context); + connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture())); + } + m_renderer->setDevicePixelRatio(m_device_pixel_ratio); + m_renderer->setRootNode(static_cast(root)); + + if (m_pixmap.size() != m_size) + m_pixmap = QPixmap(m_size); + + // Render texture. + root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update. + m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update. + + m_dirtyTexture = false; + + m_renderer->setDeviceRect(m_size); + m_renderer->setViewportRect(m_size); + QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height()); + m_renderer->setProjectionMatrixToRect(mirrored); + m_renderer->setClearColor(Qt::transparent); + + m_renderer->render(&m_pixmap); + + root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update. + + if (m_recursive) + markDirtyTexture(); // Continuously update if 'live' and 'recursive'. +} diff --git a/src/plugins/scenegraph/softwarecontext/softwarelayer.h b/src/plugins/scenegraph/softwarecontext/softwarelayer.h new file mode 100644 index 0000000000..3352e6085e --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/softwarelayer.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef SOFTWARELAYER_H +#define SOFTWARELAYER_H + +#include +#include + +namespace SoftwareContext { +class PixmapRenderer; +} + +class SoftwareLayer : public QSGLayer +{ + Q_OBJECT +public: + SoftwareLayer(QSGRenderContext *renderContext); + ~SoftwareLayer(); + + const QPixmap &pixmap() const { return m_pixmap; } + + // QSGTexture interface +public: + virtual int textureId() const; + virtual QSize textureSize() const; + virtual bool hasAlphaChannel() const; + virtual bool hasMipmaps() const; + virtual void bind(); + + // QSGDynamicTexture interface +public: + virtual bool updateTexture(); + + // QSGLayer interface +public: + virtual void setItem(QSGNode *item); + virtual void setShaderSourceNode(QSGNode *node); + virtual void setRect(const QRectF &rect); + virtual void setSize(const QSize &size); + virtual void scheduleUpdate(); + virtual QImage toImage() const; + virtual void setLive(bool live); + virtual void setRecursive(bool recursive); + virtual void setFormat(GLenum); + virtual void setHasMipmaps(bool); + virtual void setDevicePixelRatio(qreal ratio); + +public slots: + virtual void markDirtyTexture(); + virtual void invalidated(); + +private: + void grab(); + + QSGNode *m_item; + QSGNode *m_shaderSourceNode; + QSGRenderContext *m_context; + SoftwareContext::PixmapRenderer *m_renderer; + QRectF m_rect; + QSize m_size; + QPixmap m_pixmap; + qreal m_device_pixel_ratio; + bool m_live; + bool m_grab; + bool m_recursive; + bool m_dirtyTexture; +}; + +#endif // SOFTWARELAYER_H diff --git a/src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp b/src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp new file mode 100644 index 0000000000..6b8991552d --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/threadedrenderloop.cpp @@ -0,0 +1,1129 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** Copyright (C) 2014 Jolla Ltd, author: +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "threadedrenderloop.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include "context.h" + +/* + Overall design: + + There are two classes here. ThreadedRenderLoop and + RenderThread. All communication between the two is based on + event passing and we have a number of custom events. + + In this implementation, the render thread is never blocked and the + GUI thread will initiate a polishAndSync which will block and wait + for the render thread to pick it up and release the block only + after the render thread is done syncing. The reason for this + is: + + 1. Clear blocking paradigm. We only have one real "block" point + (polishAndSync()) and all blocking is initiated by GUI and picked + up by Render at specific times based on events. This makes the + execution deterministic. + + 2. Render does not have to interact with GUI. This is done so that + the render thread can run its own animation system which stays + alive even when the GUI thread is blocked doing i/o, object + instantiation, QPainter-painting or any other non-trivial task. + + --- + + There is one thread per window and one opengl context per thread. + + --- + + The render thread has affinity to the GUI thread until a window + is shown. From that moment and until the window is destroyed, it + will have affinity to the render thread. (moved back at the end + of run for cleanup). + + --- + + The render loop is active while any window is exposed. All visible + windows are tracked, but only exposed windows are actually added to + the render thread and rendered. That means that if all windows are + obscured, we might end up cleaning up the SG and GL context (if all + windows have disabled persistency). Especially for multiprocess, + low-end systems, this should be quite important. + + */ + +QT_BEGIN_NAMESPACE + +#define QSG_RT_PAD " (RT)" + +static int get_env_int(const char *name, int defaultValue) +{ + QByteArray content = qgetenv(name); + + bool ok = false; + int value = content.toInt(&ok); + return ok ? value : defaultValue; +} + + +static inline int qsgrl_animation_interval() { + qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate(); + // To work around that some platforms wrongfully return 0 or something + // bogus for refreshrate + if (refreshRate < 1) + return 16; + return int(1000 / refreshRate); +} + + +static QElapsedTimer threadTimer; +static qint64 syncTime; +static qint64 renderTime; +static qint64 sinceLastTime; + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +// RL: Render Loop +// RT: Render Thread + +// Passed from the RL to the RT when a window is removed obscured and +// should be removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to RT when GUI has been locked, waiting for sync +// (updatePaintNode()) +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); + +// Passed by the RT to itself to trigger another render pass. This is +// typically a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); + +// Passed by the RL to the RT to free up maybe release SG and GL contexts +// if no windows are rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is +// called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); + +template T *windowFor(const QList list, QQuickWindow *window) +{ + for (int i=0; i(&t); + } + return 0; +} + + +class WMWindowEvent : public QEvent +{ +public: + WMWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class WMTryReleaseEvent : public WMWindowEvent +{ +public: + WMTryReleaseEvent(QQuickWindow *win, bool destroy, QOffscreenSurface *fallback) + : WMWindowEvent(win, WM_TryRelease) + , inDestructor(destroy) + , fallbackSurface(fallback) + {} + + bool inDestructor; + QOffscreenSurface *fallbackSurface; +}; + +class WMSyncEvent : public WMWindowEvent +{ +public: + WMSyncEvent(QQuickWindow *c, bool inExpose) : WMWindowEvent(c, WM_RequestSync), size(c->size()), syncInExpose(inExpose) { } + QSize size; + bool syncInExpose; +}; + + +class WMGrabEvent : public WMWindowEvent +{ +public: + WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {} + QImage *image; +}; + + +class RenderThreadEventQueue : public QQueue +{ +public: + RenderThreadEventQueue() + : waiting(false) + { + } + + void addEvent(QEvent *e) { + mutex.lock(); + enqueue(e); + if (waiting) + condition.wakeOne(); + mutex.unlock(); + } + + QEvent *takeEvent(bool wait) { + mutex.lock(); + if (size() == 0 && wait) { + waiting = true; + condition.wait(&mutex); + waiting = false; + } + QEvent *e = dequeue(); + mutex.unlock(); + return e; + } + + bool hasMoreEvents() { + mutex.lock(); + bool has = !isEmpty(); + mutex.unlock(); + return has; + } + +private: + QMutex mutex; + QWaitCondition condition; + bool waiting; +}; + + +class RenderThread : public QThread +{ + Q_OBJECT +public: + RenderThread(ThreadedRenderLoop *w, QSGRenderContext *renderContext) + : wm(w) + , sgrc(renderContext) + , animatorDriver(0) + , pendingUpdate(0) + , sleeping(false) + , syncResultedInChanges(false) + , active(false) + , window(0) + , stopEventProcessing(false) + { +#if defined(Q_OS_QNX) && !defined(Q_OS_BLACKBERRY) && defined(Q_PROCESSOR_X86) + // The SDP 6.6.0 x86 MESA driver requires a larger stack than the default. + setStackSize(1024 * 1024); +#endif + vsyncDelta = qsgrl_animation_interval(); + } + + ~RenderThread() + { + delete sgrc; + } + + bool event(QEvent *); + void run(); + + void syncAndRender(); + void sync(bool inExpose); + + void requestRepaint() + { + if (sleeping) + stopEventProcessing = true; + if (window) + pendingUpdate |= RepaintRequest; + } + + void processEventsAndWaitForMore(); + void processEvents(); + void postEvent(QEvent *e); + +public slots: + void sceneGraphChanged() { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "sceneGraphChanged"; + syncResultedInChanges = true; + } + +public: + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02, + ExposeRequest = 0x04 | RepaintRequest | SyncRequest + }; + + ThreadedRenderLoop *wm; + QSGRenderContext *sgrc; + + QAnimationDriver *animatorDriver; + + uint pendingUpdate; + bool sleeping; + bool syncResultedInChanges; + + volatile bool active; + + float vsyncDelta; + + QMutex mutex; + QWaitCondition waitCondition; + + QElapsedTimer m_timer; + + QQuickWindow *window; // Will be 0 when window is not exposed + QSize windowSize; + + // Local event queue stuff... + bool stopEventProcessing; + RenderThreadEventQueue eventQueue; +}; + +bool RenderThread::event(QEvent *e) +{ + switch ((int) e->type()) { + + case WM_Obscure: { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Obscure"; + + Q_ASSERT(!window || window == static_cast(e)->window); + + mutex.lock(); + if (window) { + QQuickWindowPrivate::get(window)->fireAboutToStop(); + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window removed"; + window = 0; + } + waitCondition.wakeOne(); + mutex.unlock(); + + return true; } + + case WM_RequestSync: { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestSync"; + WMSyncEvent *se = static_cast(e); + if (sleeping) + stopEventProcessing = true; + window = se->window; + windowSize = se->size; + + pendingUpdate |= SyncRequest; + if (se->syncInExpose) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- triggered from expose"; + pendingUpdate |= ExposeRequest; + } + return true; } + + case WM_TryRelease: { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_TryRelease"; + mutex.lock(); + wm->m_lockedForSync = true; + WMTryReleaseEvent *wme = static_cast(e); + if (!window || wme->inDestructor) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- setting exit flag and invalidating OpenGL"; + active = false; + Q_ASSERT_X(!wme->inDestructor || !active, "RenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down"); + if (sleeping) + stopEventProcessing = true; + } else { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- not releasing because window is still active"; + } + waitCondition.wakeOne(); + wm->m_lockedForSync = false; + mutex.unlock(); + return true; + } + + case WM_Grab: { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Grab"; + WMGrabEvent *ce = static_cast(e); + Q_ASSERT(ce->window == window); + mutex.lock(); + if (window) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync scene graph"; + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + static_cast(d->context)->currentWindow = window; + d->syncSceneGraph(); + + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering scene graph"; + QQuickWindowPrivate::get(window)->renderSceneGraph(windowSize); + + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- grabbing result"; + *ce->image = static_cast(d->renderer)->backingStore()->handle()->toImage(); + } + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- waking gui to handle result"; + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + case WM_RequestRepaint: + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestPaint"; + // When GUI posts this event, it is followed by a polishAndSync, so we mustn't + // exit the event loop yet. + pendingUpdate |= RepaintRequest; + break; + + default: + break; + } + return QThread::event(e); +} + +/*! + Enters the mutex lock to make sure GUI is blocking and performs + sync, then wakes GUI. + */ +void RenderThread::sync(bool inExpose) +{ + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "sync()"; + mutex.lock(); + + Q_ASSERT_X(wm->m_lockedForSync, "RenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); + + bool current = false; + if (windowSize.width() > 0 && windowSize.height() > 0) + current = true; + if (current) { + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + static_cast(d->context)->currentWindow = window; + bool hadRenderer = d->renderer != 0; + // If the scene graph was touched since the last sync() make sure it sends the + // changed signal. + if (d->renderer) + d->renderer->clearChangedFlag(); + d->syncSceneGraph(); + if (!hadRenderer && d->renderer) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- renderer was created"; + syncResultedInChanges = true; + connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), Qt::DirectConnection); + } + + // Process deferred deletes now, directly after the sync as + // deleteLater on the GUI must now also have resulted in SG changes + // and the delete is a safe operation. + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } else { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window has bad size, sync aborted"; + } + + if (!inExpose) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync complete, waking Gui"; + waitCondition.wakeOne(); + mutex.unlock(); + } +} + +void RenderThread::syncAndRender() +{ + bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; + if (profileFrames) { + sinceLastTime = threadTimer.nsecsElapsed(); + threadTimer.start(); + } + + QElapsedTimer waitTimer; + waitTimer.start(); + + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "syncAndRender()"; + + syncResultedInChanges = false; + + uint pending = pendingUpdate; + pendingUpdate = 0; + + if (pending & SyncRequest) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- updatePending, doing sync"; + sync(pending == ExposeRequest); + } + + if (!syncResultedInChanges && ((pending & RepaintRequest) == 0)) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- no changes, render aborted"; + int waitTime = vsyncDelta - (int) waitTimer.elapsed(); + if (waitTime > 0) + msleep(waitTime); + return; + } + + if (profileFrames) + syncTime = threadTimer.nsecsElapsed(); + + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering started"; + + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + + if (animatorDriver->isRunning()) { + d->animationController->lock(); + animatorDriver->advance(); + d->animationController->unlock(); + } + + bool current = false; + if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0) + current = true; + if (current) { + static_cast(d->context)->currentWindow = window; + d->renderSceneGraph(windowSize); + if (profileFrames) + renderTime = threadTimer.nsecsElapsed(); + // ### used to be swappBuffers here + d->fireFrameSwapped(); + } else { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window not ready, skipping render"; + } + + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering done"; + + // Though it would be more correct to put this block directly after + // fireFrameSwapped in the if (current) branch above, we don't do + // that to avoid blocking the GUI thread in the case where it + // has started rendering with a bad window, causing makeCurrent to + // fail or if the window has a bad size. + if (pending == ExposeRequest) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- wake Gui after initial expose"; + waitCondition.wakeOne(); + mutex.unlock(); + } + + qCDebug(QSG_LOG_TIME_RENDERLOOP, + "Frame rendered with 'threaded' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)", + int(threadTimer.elapsed()), + int((syncTime/1000000)), + int((renderTime - syncTime) / 1000000), + int(threadTimer.elapsed() - renderTime / 1000000)); + + + Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphRenderLoopFrame, ( + syncTime, + renderTime - syncTime, + threadTimer.nsecsElapsed() - renderTime)); +} + + + +void RenderThread::postEvent(QEvent *e) +{ + eventQueue.addEvent(e); +} + + + +void RenderThread::processEvents() +{ + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEvents()"; + while (eventQueue.hasMoreEvents()) { + QEvent *e = eventQueue.takeEvent(false); + event(e); + delete e; + } + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEvents()"; +} + +void RenderThread::processEventsAndWaitForMore() +{ + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEventsAndWaitForMore()"; + stopEventProcessing = false; + while (!stopEventProcessing) { + QEvent *e = eventQueue.takeEvent(true); + event(e); + delete e; + } + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEventsAndWaitForMore()"; +} + +void RenderThread::run() +{ + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run()"; + animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(0); + animatorDriver->install(); + QUnifiedTimer::instance(true)->setConsistentTiming(QSGRenderLoop::useConsistentTiming()); + if (QQmlDebugService::isDebuggingEnabled()) + QQuickProfiler::registerAnimationCallback(); + + while (active) { + + if (window) { + static_cast(sgrc)->initializeIfNeeded(); + syncAndRender(); + } + + processEvents(); + QCoreApplication::processEvents(); + + if (active && (pendingUpdate == 0 || !window)) { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "done drawing, sleep..."; + sleeping = true; + processEventsAndWaitForMore(); + sleeping = false; + } + } + + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run() completed"; + + delete animatorDriver; + animatorDriver = 0; + + sgrc->moveToThread(wm->thread()); + moveToThread(wm->thread()); +} + +ThreadedRenderLoop::ThreadedRenderLoop() + : sg(QSGContext::createDefaultContext()) + , m_animation_timer(0) +{ +#if defined(QSG_RENDER_LOOP_DEBUG) + qsgrl_timer.start(); +#endif + + m_animation_driver = sg->createAnimationDriver(this); + + m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5); + + connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted())); + connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); + + m_animation_driver->install(); +} + +QSGRenderContext *ThreadedRenderLoop::createRenderContext(QSGContext *sg) const +{ + return sg->createRenderContext(); +} + +void ThreadedRenderLoop::maybePostPolishRequest(Window *w) +{ + if (w->timerId == 0) { + qCDebug(QSG_LOG_RENDERLOOP) << "- posting update"; + w->timerId = startTimer(m_exhaust_delay, Qt::PreciseTimer); + } +} + +QAnimationDriver *ThreadedRenderLoop::animationDriver() const +{ + return m_animation_driver; +} + +QSGContext *ThreadedRenderLoop::sceneGraphContext() const +{ + return sg; +} + +bool ThreadedRenderLoop::anyoneShowing() const +{ + for (int i=0; iisVisible() && c->isExposed()) + return true; + } + return false; +} + +bool ThreadedRenderLoop::interleaveIncubation() const +{ + return m_animation_driver->isRunning() && anyoneShowing(); +} + +void ThreadedRenderLoop::animationStarted() +{ + qCDebug(QSG_LOG_RENDERLOOP) << "- animationStarted()"; + startOrStopAnimationTimer(); + + for (int i=0; i(&m_windows.at(i))); +} + +void ThreadedRenderLoop::animationStopped() +{ + qCDebug(QSG_LOG_RENDERLOOP) << "- animationStopped()"; + startOrStopAnimationTimer(); +} + + +void ThreadedRenderLoop::startOrStopAnimationTimer() +{ + int exposedWindows = 0; + Window *theOne = 0; + for (int i=0; iisVisible() && w.window->isExposed()) { + ++exposedWindows; + theOne = &w; + } + } + + if (m_animation_timer != 0 && (exposedWindows == 1 || !m_animation_driver->isRunning())) { + killTimer(m_animation_timer); + m_animation_timer = 0; + // If animations are running, make sure we keep on animating + if (m_animation_driver->isRunning()) + maybePostPolishRequest(theOne); + + } else if (m_animation_timer == 0 && exposedWindows != 1 && m_animation_driver->isRunning()) { + m_animation_timer = startTimer(qsgrl_animation_interval()); + } +} + +/* + Removes this window from the list of tracked windowes in this + window manager. hide() will trigger obscure, which in turn will + stop rendering. + + This function will be called during QWindow::close() which will + also destroy the QPlatformWindow so it is important that this + triggers handleObscurity() and that rendering for that window + is fully done and over with by the time this function exits. + */ + +void ThreadedRenderLoop::hide(QQuickWindow *window) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "hide()" << window; + + if (window->isExposed()) + handleObscurity(windowFor(m_windows, window)); + + releaseResources(window); +} + + +/*! + If the window is first hide it, then perform a complete cleanup + with releaseResources which will take down the GL context and + exit the rendering thread. + */ +void ThreadedRenderLoop::windowDestroyed(QQuickWindow *window) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "begin windowDestroyed()" << window; + + Window *w = windowFor(m_windows, window); + if (!w) + return; + + handleObscurity(w); + releaseResources(w, true); + + RenderThread *thread = w->thread; + while (thread->isRunning()) + QThread::yieldCurrentThread(); + Q_ASSERT(thread->thread() == QThread::currentThread()); + delete thread; + + for (int i=0; iisExposed()) { + handleExposure(window); + } else { + Window *w = windowFor(m_windows, window); + if (w) + handleObscurity(w); + } +} + +/*! + Will post an event to the render thread that this window should + start to render. + */ +void ThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "handleExposure()" << window; + + Window *w = windowFor(m_windows, window); + if (!w) { + qCDebug(QSG_LOG_RENDERLOOP) << "- adding window to list"; + Window win; + win.window = window; + win.actualWindowFormat = window->format(); + win.thread = new RenderThread(this, QQuickWindowPrivate::get(window)->context); + win.timerId = 0; + win.updateDuringSync = false; + m_windows << win; + w = &m_windows.last(); + } + + // set this early as we'll be rendering shortly anyway and this avoids + // specialcasing exposure in polishAndSync. + w->thread->window = window; + + if (w->window->width() <= 0 || w->window->height() <= 0 + || !w->window->geometry().intersects(w->window->screen()->availableGeometry())) { +#ifndef QT_NO_DEBUG + qWarning("ThreadedRenderLoop: expose event received for window with invalid geometry."); +#endif + } + + // Because we are going to bind a GL context to it, make sure it + // is created. + if (!w->window->handle()) + w->window->create(); + + // Start render thread if it is not running + if (!w->thread->isRunning()) { + + qCDebug(QSG_LOG_RENDERLOOP) << "- starting render thread"; + + QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; + if (controller->thread() != w->thread) + controller->moveToThread(w->thread); + + w->thread->active = true; + if (w->thread->thread() == QThread::currentThread()) { + w->thread->sgrc->moveToThread(w->thread); + w->thread->moveToThread(w->thread); + } + w->thread->start(); + + } else { + qCDebug(QSG_LOG_RENDERLOOP) << "- render thread already running"; + } + + polishAndSync(w, true); + qCDebug(QSG_LOG_RENDERLOOP) << "- done with handleExposure()"; + + startOrStopAnimationTimer(); +} + +/*! + This function posts an event to the render thread to remove the window + from the list of windowses to render. + + It also starts up the non-vsync animation tick if no more windows + are showing. + */ +void ThreadedRenderLoop::handleObscurity(Window *w) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "handleObscurity()" << w->window; + if (w->thread->isRunning()) { + w->thread->mutex.lock(); + w->thread->postEvent(new WMWindowEvent(w->window, WM_Obscure)); + w->thread->waitCondition.wait(&w->thread->mutex); + w->thread->mutex.unlock(); + } + startOrStopAnimationTimer(); +} + + +void ThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + Window *w = windowFor(m_windows, window); + if (w) + maybeUpdate(w); +} + +/*! + Called whenever the QML scene has changed. Will post an event to + ourselves that a sync is needed. + */ +void ThreadedRenderLoop::maybeUpdate(Window *w) +{ + if (!QCoreApplication::instance()) + return; + + QThread *current = QThread::currentThread(); + if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) { + qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; + return; + } + + if (!w || !w->thread->isRunning()) { + return; + } + qCDebug(QSG_LOG_RENDERLOOP) << "update from item" << w->window; + + // Call this function from the Gui thread later as startTimer cannot be + // called from the render thread. + if (QThread::currentThread() == w->thread) { + qCDebug(QSG_LOG_RENDERLOOP) << "- on render thread"; + w->updateDuringSync = true; + return; + } + + maybePostPolishRequest(w); +} + +/*! + Called when the QQuickWindow should be explicitly repainted. This function + can also be called on the render thread when the GUI thread is blocked to + keep render thread animations alive. + */ +void ThreadedRenderLoop::update(QQuickWindow *window) +{ + Window *w = windowFor(m_windows, window); + if (!w) + return; + + if (w->thread == QThread::currentThread()) { + qCDebug(QSG_LOG_RENDERLOOP) << "update on window - on render thread" << w->window; + w->thread->requestRepaint(); + return; + } + + qCDebug(QSG_LOG_RENDERLOOP) << "update on window" << w->window; + w->thread->postEvent(new QEvent(WM_RequestRepaint)); + maybeUpdate(w); +} + + +void ThreadedRenderLoop::releaseResources(QQuickWindow *window) +{ + Window *w = windowFor(m_windows, window); + if (w) + releaseResources(w, false); +} + +/*! + * Release resources will post an event to the render thread to + * free up the SG and GL resources and exists the render thread. + */ +void ThreadedRenderLoop::releaseResources(Window *w, bool inDestructor) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "releaseResources()" << (inDestructor ? "in destructor" : "in api-call") << w->window; + + w->thread->mutex.lock(); + if (w->thread->isRunning() && w->thread->active) { + QQuickWindow *window = w->window; + + // The platform window might have been destroyed before + // hide/release/windowDestroyed is called, so we need to have a + // fallback surface to perform the cleanup of the scene graph + // and the OpenGL resources. + // QOffscreenSurface must be created on the GUI thread, so we + // create it here and pass it on to RenderThread::invalidateGL() + QOffscreenSurface *fallback = 0; + if (!window->handle()) { + qCDebug(QSG_LOG_RENDERLOOP) << "- using fallback surface"; + fallback = new QOffscreenSurface(); + fallback->setFormat(w->actualWindowFormat); + fallback->create(); + } + + qCDebug(QSG_LOG_RENDERLOOP) << "- posting release request to render thread"; + w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback)); + w->thread->waitCondition.wait(&w->thread->mutex); + delete fallback; + } + w->thread->mutex.unlock(); +} + + +/* Calls polish on all items, then requests synchronization with the render thread + * and blocks until that is complete. Returns false if it aborted; otherwise true. + */ +void ThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; + + QQuickWindow *window = w->window; + if (!w->thread || !w->thread->window) { + qCDebug(QSG_LOG_RENDERLOOP) << "- not exposed, abort"; + killTimer(w->timerId); + w->timerId = 0; + return; + } + + // Flush pending touch events. + QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); + // The delivery of the event might have caused the window to stop rendering + w = windowFor(m_windows, window); + if (!w || !w->thread || !w->thread->window) { + qCDebug(QSG_LOG_RENDERLOOP) << "- removed after event flushing, abort"; + killTimer(w->timerId); + w->timerId = 0; + return; + } + + + QElapsedTimer timer; + qint64 polishTime = 0; + qint64 waitTime = 0; + qint64 syncTime = 0; + bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled; + if (profileFrames) + timer.start(); + + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->polishItems(); + + if (profileFrames) + polishTime = timer.nsecsElapsed(); + + w->updateDuringSync = false; + + emit window->afterAnimating(); + + qCDebug(QSG_LOG_RENDERLOOP) << "- lock for sync"; + w->thread->mutex.lock(); + m_lockedForSync = true; + w->thread->postEvent(new WMSyncEvent(window, inExpose)); + + qCDebug(QSG_LOG_RENDERLOOP) << "- wait for sync"; + if (profileFrames) + waitTime = timer.nsecsElapsed(); + w->thread->waitCondition.wait(&w->thread->mutex); + m_lockedForSync = false; + w->thread->mutex.unlock(); + qCDebug(QSG_LOG_RENDERLOOP) << "- unlock after sync"; + + if (profileFrames) + syncTime = timer.nsecsElapsed(); + + killTimer(w->timerId); + w->timerId = 0; + + if (m_animation_timer == 0 && m_animation_driver->isRunning()) { + qCDebug(QSG_LOG_RENDERLOOP) << "- advancing animations"; + m_animation_driver->advance(); + qCDebug(QSG_LOG_RENDERLOOP) << "- animations done.."; + // We need to trigger another sync to keep animations running... + maybePostPolishRequest(w); + emit timeToIncubate(); + } else if (w->updateDuringSync) { + maybePostPolishRequest(w); + } + + qCDebug(QSG_LOG_TIME_RENDERLOOP()).nospace() + << "Frame prepared with 'threaded' renderloop" + << ", polish=" << (polishTime / 1000000) + << ", lock=" << (waitTime - polishTime) / 1000000 + << ", blockedForSync=" << (syncTime - waitTime) / 1000000 + << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 + << " - (on Gui thread) " << window; + + Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphPolishAndSync, ( + polishTime, + waitTime - polishTime, + syncTime - waitTime, + timer.nsecsElapsed() - syncTime)); +} + +ThreadedRenderLoop::Window *ThreadedRenderLoop::windowForTimer(int timerId) const +{ + for (int i=0; i(&m_windows.at(i)); + break; + } + } + return 0; +} + +bool ThreadedRenderLoop::event(QEvent *e) +{ + switch ((int) e->type()) { + + case QEvent::Timer: { + QTimerEvent *te = static_cast(e); + if (te->timerId() == m_animation_timer) { + qCDebug(QSG_LOG_RENDERLOOP) << "- ticking non-visual timer"; + m_animation_driver->advance(); + emit timeToIncubate(); + } else { + qCDebug(QSG_LOG_RENDERLOOP) << "- polish and sync timer"; + Window *w = windowForTimer(te->timerId()); + if (w) + polishAndSync(w); + else + killTimer(te->timerId()); + } + return true; + } + + default: + break; + } + + return QObject::event(e); +} + + + +/* + Locks down GUI and performs a grab the scene graph, then returns the result. + + Since the QML scene could have changed since the last time it was rendered, + we need to polish and sync the scene graph. This might seem superfluous, but + - QML changes could have triggered deleteLater() which could have removed + textures or other objects from the scene graph, causing render to crash. + - Autotests rely on grab(), setProperty(), grab(), compare behavior. + */ + +QImage ThreadedRenderLoop::grab(QQuickWindow *window) +{ + qCDebug(QSG_LOG_RENDERLOOP) << "grab()" << window; + + Window *w = windowFor(m_windows, window); + Q_ASSERT(w); + + if (!w->thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + qCDebug(QSG_LOG_RENDERLOOP) << "- polishing items"; + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->polishItems(); + + QImage result; + w->thread->mutex.lock(); + m_lockedForSync = true; + qCDebug(QSG_LOG_RENDERLOOP) << "- posting grab event"; + w->thread->postEvent(new WMGrabEvent(window, &result)); + w->thread->waitCondition.wait(&w->thread->mutex); + m_lockedForSync = false; + w->thread->mutex.unlock(); + + qCDebug(QSG_LOG_RENDERLOOP) << "- grab complete"; + + return result; +} + +#include "threadedrenderloop.moc" diff --git a/src/plugins/scenegraph/softwarecontext/threadedrenderloop.h b/src/plugins/scenegraph/softwarecontext/threadedrenderloop.h new file mode 100644 index 0000000000..7e3bcb2d79 --- /dev/null +++ b/src/plugins/scenegraph/softwarecontext/threadedrenderloop.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** Copyright (C) 2014 Jolla Ltd, author: +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt SceneGraph Raster Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef THREADEDRENDERLOOP_H +#define THREADEDRENDERLOOP_H + +#include + +class RenderThread; + +class ThreadedRenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + ThreadedRenderLoop(); + + void show(QQuickWindow *) {} + void hide(QQuickWindow *); + + void windowDestroyed(QQuickWindow *window); + void exposureChanged(QQuickWindow *window); + + QImage grab(QQuickWindow *); + + void update(QQuickWindow *window); + void maybeUpdate(QQuickWindow *window); + QSGContext *sceneGraphContext() const; + QSGRenderContext *createRenderContext(QSGContext *) const; + + QAnimationDriver *animationDriver() const; + + void releaseResources(QQuickWindow *window); + + bool event(QEvent *); + + bool interleaveIncubation() const; + +public Q_SLOTS: + void animationStarted(); + void animationStopped(); + +private: + struct Window { + QQuickWindow *window; + RenderThread *thread; + QSurfaceFormat actualWindowFormat; + int timerId; + uint updateDuringSync : 1; + }; + + friend class RenderThread; + + void releaseResources(Window *window, bool inDestructor); + bool checkAndResetForceUpdate(QQuickWindow *window); + Window *windowForTimer(int timerId) const; + + bool anyoneShowing() const; + void initialize(); + + void startOrStopAnimationTimer(); + void maybePostPolishRequest(Window *w); + void waitForReleaseComplete(); + void polishAndSync(Window *w, bool inExpose = false); + void maybeUpdate(Window *window); + + void handleExposure(QQuickWindow *w); + void handleObscurity(Window *w); + + + QSGContext *sg; + QAnimationDriver *m_animation_driver; + QList m_windows; + + int m_animation_timer; + int m_exhaust_delay; + + bool m_lockedForSync; +}; + +#endif // THREADEDRENDERLOOP_H diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000000..49e04edcca --- /dev/null +++ b/src/src.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += plugins diff --git a/sync.profile b/sync.profile new file mode 100644 index 0000000000..8b9df1c7b1 --- /dev/null +++ b/sync.profile @@ -0,0 +1,13 @@ + +dule dependencies. +# Every module that is required to build this module should have one entry. +# Each of the module version specifiers can take one of the following values: +# - A specific Git revision. +# - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) +# - an empty string to use the same branch under test (dependencies will become "refs/heads/master" if we are in the master branch) +# +%dependencies = ( + "qtbase" => "", + "qtdeclarative" => "", +); + -- cgit v1.2.3