diff options
Diffstat (limited to 'src/quick/scenegraph')
125 files changed, 13033 insertions, 1958 deletions
diff --git a/src/quick/scenegraph/adaptations/adaptations.pri b/src/quick/scenegraph/adaptations/adaptations.pri new file mode 100644 index 0000000000..40fa739e15 --- /dev/null +++ b/src/quick/scenegraph/adaptations/adaptations.pri @@ -0,0 +1 @@ +include(software/software.pri) diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp new file mode 100644 index 0000000000..2ff180ea99 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgabstractsoftwarerenderer_p.h" + +#include "qsgsoftwarerenderablenodeupdater_p.h" +#include "qsgsoftwarerenderlistbuilder_p.h" +#include "qsgsoftwarecontext_p.h" +#include "qsgsoftwarerenderablenode_p.h" + +#include <QtCore/QLoggingCategory> +#include <QtGui/QWindow> +#include <QtQuick/QSGSimpleRectNode> + +Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer") + +QT_BEGIN_NAMESPACE + +QSGAbstractSoftwareRenderer::QSGAbstractSoftwareRenderer(QSGRenderContext *context) + : QSGRenderer(context) + , m_background(new QSGSimpleRectNode) + , m_nodeUpdater(new QSGSoftwareRenderableNodeUpdater(this)) +{ + // Setup special background node + auto backgroundRenderable = new QSGSoftwareRenderableNode(QSGSoftwareRenderableNode::SimpleRect, m_background); + addNodeMapping(m_background, backgroundRenderable); +} + +QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer() +{ + // Cleanup RenderableNodes + delete m_background; + + qDeleteAll(m_nodes); + + delete m_nodeUpdater; +} + +QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *node) const +{ + return m_nodes.value(node, nullptr); +} + +void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode) +{ + m_nodes.insert(node, renderableNode); +} + +void QSGAbstractSoftwareRenderer::appendRenderableNode(QSGSoftwareRenderableNode *node) +{ + m_renderableNodes.append(node); +} + +void QSGAbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) +{ + if (state & QSGNode::DirtyGeometry) { + nodeGeometryUpdated(node); + } + if (state & QSGNode::DirtyMaterial) { + nodeMaterialUpdated(node); + } + if (state & QSGNode::DirtyMatrix) { + nodeMatrixUpdated(node); + } + if (state & QSGNode::DirtyNodeAdded) { + nodeAdded(node); + } + if (state & QSGNode::DirtyNodeRemoved) { + nodeRemoved(node); + } + if (state & QSGNode::DirtyOpacity) { + nodeOpacityUpdated(node); + } + if (state & QSGNode::DirtySubtreeBlocked) { + m_nodeUpdater->updateNodes(node); + } + if (state & QSGNode::DirtyForceUpdate) { + m_nodeUpdater->updateNodes(node); + } + QSGRenderer::nodeChanged(node, state); +} + +QRegion QSGAbstractSoftwareRenderer::renderNodes(QPainter *painter) +{ + QRegion dirtyRegion; + // If there are no nodes, do nothing + if (m_renderableNodes.isEmpty()) + return dirtyRegion; + + auto iterator = m_renderableNodes.begin(); + // First node is the background and needs to painted without blending + auto backgroundNode = *iterator; + dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ true); + iterator++; + + for (; iterator != m_renderableNodes.end(); ++iterator) { + auto node = *iterator; + dirtyRegion += node->renderNode(painter); + } + + return dirtyRegion; +} + +void QSGAbstractSoftwareRenderer::buildRenderList() +{ + // Clear the previous renderlist + m_renderableNodes.clear(); + // Add the background renderable (always first) + m_renderableNodes.append(renderableNode(m_background)); + // Build the renderlist + QSGSoftwareRenderListBuilder(this).visitChildren(rootNode()); +} + +QRegion QSGAbstractSoftwareRenderer::optimizeRenderList() +{ + // Iterate through the renderlist from front to back + // Objective is to update the dirty status and rects. + for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) { + auto node = *i; + if (!m_dirtyRegion.isEmpty()) { + // See if the current dirty regions apply to the current node + node->addDirtyRegion(m_dirtyRegion, true); + } + + if (!m_obscuredRegion.isEmpty()) { + // Don't try to paint things that are covered by opaque objects + node->subtractDirtyRegion(m_obscuredRegion); + } + + // Keep up with obscured regions + if (node->isOpaque()) { + m_obscuredRegion += QRegion(node->boundingRect()); + } + + if (node->isDirty()) { + // Don't paint things outside of the rendering area + if (!m_background->rect().toRect().contains(node->boundingRect(), /*proper*/ true)) { + // Some part(s) of node is(are) outside of the rendering area + QRegion renderArea(m_background->rect().toRect()); + QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea); + if (!outsideRegions.isEmpty()) + node->subtractDirtyRegion(outsideRegions); + } + + // Get the dirty region's to pass to the next nodes + if (node->isOpaque()) { + // if isOpaque, subtract node's dirty rect from m_dirtyRegion + m_dirtyRegion -= node->dirtyRegion(); + } else { + // if isAlpha, add node's dirty rect to m_dirtyRegion + m_dirtyRegion += node->dirtyRegion(); + } + // if previousDirtyRegion has content outside of boundingRect add to m_dirtyRegion + QRegion prevDirty = node->previousDirtyRegion(); + if (!prevDirty.isNull()) + m_dirtyRegion += prevDirty; + } + } + + // Empty dirtyRegion (for second pass) + m_dirtyRegion = QRegion(); + m_obscuredRegion = QRegion(); + + // Iterate through the renderlist from back to front + // Objective is to make sure all non-opaque items are painted when an item under them is dirty + for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) { + auto node = *j; + + if (!node->isOpaque() && !m_dirtyRegion.isEmpty()) { + // Only blended nodes need to be updated + node->addDirtyRegion(m_dirtyRegion, true); + } + + m_dirtyRegion += node->dirtyRegion(); + } + + QRegion updateRegion = m_dirtyRegion; + + // Empty dirtyRegion + m_dirtyRegion = QRegion(); + m_obscuredRegion = QRegion(); + + return updateRegion; +} + +void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color) +{ + if (m_background->color() == color) + return; + m_background->setColor(color); + renderableNode(m_background)->markMaterialDirty(); +} + +void QSGAbstractSoftwareRenderer::setBackgroundSize(const QSize &size) +{ + if (m_background->rect().size().toSize() == size) + return; + m_background->setRect(0.0f, 0.0f, size.width(), size.height()); + renderableNode(m_background)->markGeometryDirty(); + // Invalidate the whole scene when the background is resized + markDirty(); +} + +QColor QSGAbstractSoftwareRenderer::backgroundColor() +{ + return m_background->color(); +} + +QSize QSGAbstractSoftwareRenderer::backgroundSize() +{ + return m_background->rect().size().toSize(); +} + +void QSGAbstractSoftwareRenderer::nodeAdded(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeAdded" << (void*)node; + + m_nodeUpdater->updateNodes(node); +} + +void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeRemoved" << (void*)node; + + auto renderable = renderableNode(node); + // remove mapping + if (renderable != nullptr) { + // Need to mark this region dirty in the other nodes + QRegion dirtyRegion = renderable->previousDirtyRegion(true); + if (dirtyRegion.isEmpty()) + dirtyRegion = renderable->boundingRect(); + m_dirtyRegion += dirtyRegion; + m_nodes.remove(node); + delete renderable; + } + + // Remove all children nodes as well + for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) { + nodeRemoved(child); + } + + m_nodeUpdater->updateNodes(node, true); +} + +void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeGeometryUpdated"; + + // Mark node as dirty + auto renderable = renderableNode(node); + if (renderable != nullptr) { + renderable->markGeometryDirty(); + } else { + m_nodeUpdater->updateNodes(node); + } +} + +void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeMaterialUpdated"; + + // Mark node as dirty + auto renderable = renderableNode(node); + if (renderable != nullptr) { + renderable->markMaterialDirty(); + } else { + m_nodeUpdater->updateNodes(node); + } +} + +void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeMaterialUpdated"; + + // Update children nodes + m_nodeUpdater->updateNodes(node); +} + +void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node) +{ + qCDebug(lc2DRender) << "nodeOpacityUpdated"; + + // Update children nodes + m_nodeUpdater->updateNodes(node); +} + +void QSGAbstractSoftwareRenderer::markDirty() +{ + m_dirtyRegion = QRegion(m_background->rect().toRect()); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h new file mode 100644 index 0000000000..905577b92a --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGABSTRACTSOFTWARERENDERER_H +#define QSGABSTRACTSOFTWARERENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgrenderer_p.h> + +#include <QtCore/QHash> +#include <QtCore/QLinkedList> + +QT_BEGIN_NAMESPACE + +class QSGSimpleRectNode; + +class QSGSoftwareRenderableNode; +class QSGSoftwareRenderableNodeUpdater; + +class QSGAbstractSoftwareRenderer : public QSGRenderer +{ +public: + QSGAbstractSoftwareRenderer(QSGRenderContext *context); + virtual ~QSGAbstractSoftwareRenderer(); + + QSGSoftwareRenderableNode *renderableNode(QSGNode *node) const; + void addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode); + void appendRenderableNode(QSGSoftwareRenderableNode *node); + + void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override; + + void markDirty(); + +protected: + QRegion renderNodes(QPainter *painter); + void buildRenderList(); + QRegion optimizeRenderList(); + + void setBackgroundColor(const QColor &color); + void setBackgroundSize(const QSize &size); + QColor backgroundColor(); + QSize backgroundSize(); + +private: + void nodeAdded(QSGNode *node); + void nodeRemoved(QSGNode *node); + void nodeGeometryUpdated(QSGNode *node); + void nodeMaterialUpdated(QSGNode *node); + void nodeMatrixUpdated(QSGNode *node); + void nodeOpacityUpdated(QSGNode *node); + + QHash<QSGNode*, QSGSoftwareRenderableNode*> m_nodes; + QLinkedList<QSGSoftwareRenderableNode*> m_renderableNodes; + + QSGSimpleRectNode *m_background; + + QRegion m_dirtyRegion; + QRegion m_obscuredRegion; + + QSGSoftwareRenderableNodeUpdater *m_nodeUpdater; +}; + +QT_END_NAMESPACE + +#endif // QSGABSTRACTSOFTWARERENDERER_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp new file mode 100644 index 0000000000..8ad9b50b09 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwareadaptation_p.h" +#include "qsgsoftwarecontext_p.h" +#include "qsgsoftwarerenderloop_p.h" +#include "qsgsoftwarethreadedrenderloop_p.h" + +#include <private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> + +QT_BEGIN_NAMESPACE + +QSGSoftwareAdaptation::QSGSoftwareAdaptation(QObject *parent) + : QSGContextPlugin(parent) +{ +} + +QStringList QSGSoftwareAdaptation::keys() const +{ + return QStringList() << QLatin1String("software") << QLatin1String("softwarecontext"); +} + +QSGContext *QSGSoftwareAdaptation::create(const QString &) const +{ + if (!instance) + instance = new QSGSoftwareContext(); + return instance; +} + +QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &) const +{ + // Claim we support adaptable shader effects, then return null for the + // shader effect node. The result is shader effects not being rendered, + // with the application working fine in all other respects. + return QSGContextFactoryInterface::SupportsShaderEffectNode; +} + +QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager() +{ + static bool threaded = false; + static bool envChecked = false; + if (!envChecked) { + envChecked = true; + threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded"); + } + + if (threaded) + return new QSGSoftwareThreadedRenderLoop; + + return new QSGSoftwareRenderLoop(); +} + +QSGSoftwareContext *QSGSoftwareAdaptation::instance = 0; + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h new file mode 100644 index 0000000000..ffe54b5d4b --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLUGINMAIN_H +#define PLUGINMAIN_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgcontextplugin_p.h> + +QT_BEGIN_NAMESPACE + +class QSGContext; +class QSGRenderLoop; +class QSGSoftwareContext; + +class QSGSoftwareAdaptation : public QSGContextPlugin +{ +public: + QSGSoftwareAdaptation(QObject *parent = 0); + + QStringList keys() const override; + QSGContext *create(const QString &key) const override; + QSGContextFactoryInterface::Flags flags(const QString &key) const override; + QSGRenderLoop *createWindowManager() override; +private: + static QSGSoftwareContext *instance; +}; + +QT_END_NAMESPACE + +#endif // PLUGINMAIN_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp new file mode 100644 index 0000000000..05d5daa686 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarecontext_p.h" + +#include "qsgsoftwareinternalrectanglenode_p.h" +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwarepainternode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#include "qsgsoftwareglyphnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarelayer_p.h" +#include "qsgsoftwarerenderer_p.h" +#if QT_CONFIG(quick_sprite) +#include "qsgsoftwarespritenode_p.h" +#endif + +#include <QtCore/QCoreApplication> +#include <QtCore/QElapsedTimer> + +#include <QtGui/QWindow> +#include <QtQuick/private/qquickwindow_p.h> + +// Used for very high-level info about the renderering and gl context +// Includes GL_VERSION, type of render loop, atlas size, etc. +Q_LOGGING_CATEGORY(QSG_RASTER_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_RASTER_LOG_RENDERLOOP, "qt.scenegraph.renderloop") + +// GLSL shader compilation +Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation") + +// polish, animations, sync, render and swap in the render loop +Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop") + +// Texture uploads and swizzling +Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture") + +// Glyph preparation (only for distance fields atm) +Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph") + +// Timing inside the renderer base class +Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer") + +QT_BEGIN_NAMESPACE + +QSGSoftwareRenderContext::QSGSoftwareRenderContext(QSGContext *ctx) + : QSGRenderContext(ctx) + , m_initialized(false) + , m_activePainter(nullptr) +{ +} + +QSGSoftwareContext::QSGSoftwareContext(QObject *parent) + : QSGContext(parent) +{ +} + +QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode() +{ + return new QSGSoftwareInternalRectangleNode(); +} + +QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode() +{ + return new QSGSoftwareInternalImageNode(); +} + +QSGPainterNode *QSGSoftwareContext::createPainterNode(QQuickPaintedItem *item) +{ + return new QSGSoftwarePainterNode(item); +} + +QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) +{ + Q_UNUSED(rc); + Q_UNUSED(preferNativeGlyphNode); + return new QSGSoftwareGlyphNode(); +} + +QSGLayer *QSGSoftwareContext::createLayer(QSGRenderContext *renderContext) +{ + return new QSGSoftwareLayer(renderContext); +} + +QSurfaceFormat QSGSoftwareContext::defaultSurfaceFormat() const +{ + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + format.setRenderableType(QSurfaceFormat::DefaultRenderableType); + format.setMajorVersion(0); + format.setMinorVersion(0); + return format; +} + +void QSGSoftwareRenderContext::initializeIfNeeded() +{ + if (m_initialized) + return; + m_initialized = true; + emit initialized(); +} + +void QSGSoftwareRenderContext::invalidate() +{ + m_sg->renderContextInvalidated(this); + emit invalidated(); +} + +QSGTexture *QSGSoftwareRenderContext::createTexture(const QImage &image, uint flags) const +{ + return new QSGSoftwarePixmapTexture(image, flags); +} + +QSGRenderer *QSGSoftwareRenderContext::createRenderer() +{ + return new QSGSoftwareRenderer(this); +} + + +void QSGSoftwareRenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo) +{ + renderer->renderScene(fbo); +} + +int QSGSoftwareRenderContext::maxTextureSize() const +{ + return 2048; +} + +QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *renderContext) +{ + Q_UNUSED(renderContext); + return this; +} + +QSGRectangleNode *QSGSoftwareContext::createRectangleNode() +{ + return new QSGSoftwareRectangleNode; +} + +QSGImageNode *QSGSoftwareContext::createImageNode() +{ + return new QSGSoftwareImageNode; +} + +QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode() +{ + return new QSGSoftwareNinePatchNode; +} + +#if QT_CONFIG(quick_sprite) +QSGSpriteNode *QSGSoftwareContext::createSpriteNode() +{ + return new QSGSoftwareSpriteNode; +} +#endif + +QSGRendererInterface::GraphicsApi QSGSoftwareContext::graphicsApi() const +{ + return Software; +} + +QSGRendererInterface::ShaderType QSGSoftwareContext::shaderType() const +{ + return UnknownShadingLanguage; +} + +QSGRendererInterface::ShaderCompilationTypes QSGSoftwareContext::shaderCompilationType() const +{ + return 0; +} + +QSGRendererInterface::ShaderSourceTypes QSGSoftwareContext::shaderSourceType() const +{ + return 0; +} + +void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) const +{ + if (resource == Painter && window && window->isSceneGraphInitialized()) + return static_cast<QSGSoftwareRenderContext *>(QQuickWindowPrivate::get(window)->context)->m_activePainter; + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h new file mode 100644 index 0000000000..1f14717416 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARECONTEXT_H +#define QSGSOFTWARECONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgcontext_p.h> +#include <private/qsgadaptationlayer_p.h> +#include "qsgrendererinterface.h" + +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP) +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION) +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_TEXTURE) +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_GLYPH) +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER) +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO) +Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP) + +QT_BEGIN_NAMESPACE + +class QSGSoftwareRenderContext : public QSGRenderContext +{ + Q_OBJECT +public: + QSGSoftwareRenderContext(QSGContext *ctx); + void initializeIfNeeded(); + void invalidate() override; + void renderNextFrame(QSGRenderer *renderer, uint fbo) override; + QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const override; + QSGRenderer *createRenderer() override; + int maxTextureSize() const override; + + bool m_initialized; + QPainter *m_activePainter; +}; + +class QSGSoftwareContext : public QSGContext, public QSGRendererInterface +{ + Q_OBJECT +public: + explicit QSGSoftwareContext(QObject *parent = nullptr); + + QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); } + QSGInternalRectangleNode *createInternalRectangleNode() override; + QSGInternalImageNode *createInternalImageNode() override; + QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; + QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override; + QSGLayer *createLayer(QSGRenderContext *renderContext) override; + QSurfaceFormat defaultSurfaceFormat() const override; + QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGNinePatchNode *createNinePatchNode() override; +#if QT_CONFIG(quick_sprite) + QSGSpriteNode *createSpriteNode() override; +#endif + + GraphicsApi graphicsApi() const override; + ShaderType shaderType() const override; + ShaderCompilationTypes shaderCompilationType() const override; + ShaderSourceTypes shaderSourceType() const override; + void *getResource(QQuickWindow *window, Resource resource) const override; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARECONTEXT_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp new file mode 100644 index 0000000000..21f20c66cd --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwareglyphnode_p.h" + +QT_BEGIN_NAMESPACE + +QSGSoftwareGlyphNode::QSGSoftwareGlyphNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) + , m_style(QQuickText::Normal) +{ + setMaterial((QSGMaterial*)1); + setGeometry(&m_geometry); +} + + +void QSGSoftwareGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + m_position = position; + m_glyphRun = glyphs; + m_bounding_rect = glyphs.boundingRect().translated(m_position - QPointF(0.0, glyphs.rawFont().ascent())); +} + +void QSGSoftwareGlyphNode::setColor(const QColor &color) +{ + m_color = color; +} + +void QSGSoftwareGlyphNode::setStyle(QQuickText::TextStyle style) +{ + m_style = style; +} + +void QSGSoftwareGlyphNode::setStyleColor(const QColor &color) +{ + m_styleColor = color; +} + +QPointF QSGSoftwareGlyphNode::baseLine() const +{ + return QPointF(); +} + +void QSGSoftwareGlyphNode::setPreferredAntialiasingMode(QSGGlyphNode::AntialiasingMode) +{ +} + +void QSGSoftwareGlyphNode::update() +{ +} + +void QSGSoftwareGlyphNode::paint(QPainter *painter) +{ + painter->setBrush(QBrush()); + QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent()); + + qreal offset = 1.0; + if (painter->device()->devicePixelRatio() != 0) + offset = 1.0 / painter->device()->devicePixelRatio(); + + switch (m_style) { + case QQuickText::Normal: break; + case QQuickText::Outline: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, offset), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(0, -offset), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(offset, 0), m_glyphRun); + painter->drawGlyphRun(pos + QPointF(-offset, 0), m_glyphRun); + break; + case QQuickText::Raised: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, offset), m_glyphRun); + break; + case QQuickText::Sunken: + painter->setPen(m_styleColor); + painter->drawGlyphRun(pos + QPointF(0, -offset), m_glyphRun); + break; + } + + painter->setPen(m_color); + painter->drawGlyphRun(pos, m_glyphRun); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h new file mode 100644 index 0000000000..559b156bf3 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREGLYPHNODE_H +#define QSGSOFTWAREGLYPHNODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QSGSoftwareGlyphNode : public QSGGlyphNode +{ +public: + QSGSoftwareGlyphNode(); + + void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override; + void setColor(const QColor &color) override; + void setStyle(QQuickText::TextStyle style) override; + void setStyleColor(const QColor &color) override; + QPointF baseLine() const override; + void setPreferredAntialiasingMode(AntialiasingMode) override; + void update() override; + + void paint(QPainter *painter); + +private: + QPointF m_position; + QGlyphRun m_glyphRun; + QColor m_color; + QSGGeometry m_geometry; + QQuickText::TextStyle m_style; + QColor m_styleColor; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREGLYPHNODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp new file mode 100644 index 0000000000..10291b9cb5 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp @@ -0,0 +1,501 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwareinternalimagenode_p.h" + +#include "qsgsoftwarepixmaptexture_p.h" +#include "qsgsoftwarelayer_p.h" +#include <QPainter> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +namespace QSGSoftwareHelpers { +// Helper from widgets/styles/qdrawutil.cpp + +static inline QMargins normalizedMargins(const QMargins &m) +{ + return QMargins(qMax(m.left(), 0), qMax(m.top(), 0), qMax(m.right(), 0), qMax(m.bottom(), 0)); +} + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMarginsIn, + const QPixmap &pixmap, const QRect &sourceRect, const QMargins &sourceMarginsIn, + const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) +{ + QPainter::PixmapFragment d; + d.opacity = 1.0; + d.rotation = 0.0; + + QPixmapFragmentsArray opaqueData; + QPixmapFragmentsArray translucentData; + + QMargins sourceMargins = normalizedMargins(sourceMarginsIn); + QMargins targetMargins = normalizedMargins(targetMarginsIn); + + // 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<qreal, 16> xTarget; // x-coordinates of target rectangles + QVarLengthArray<qreal, 16> 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); +} + +} // QSGSoftwareHelpers namespace + +QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode() + : 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) + , m_cachedMirroredPixmapIsDirty(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + + +void QSGSoftwareInternalImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + markDirty(DirtyGeometry); +} + +void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture) +{ + m_texture = texture; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::setMirror(bool mirror) +{ + if (m_mirror != mirror) { + m_mirror = mirror; + m_cachedMirroredPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) +{ +} + +void QSGSoftwareInternalImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + bool smooth = (filtering == QSGTexture::Linear); + if (smooth == m_smooth) + return; + + m_smooth = smooth; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + bool tileHorizontal = (wrapMode == QSGTexture::Repeat); + if (tileHorizontal == m_tileHorizontal) + return; + + m_tileHorizontal = tileHorizontal; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + bool tileVertical = (wrapMode == QSGTexture::Repeat); + if (tileVertical == m_tileVertical) + return; + + m_tileVertical = (wrapMode == QSGTexture::Repeat); + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalImageNode::update() +{ + if (m_cachedMirroredPixmapIsDirty) { + if (m_mirror) { + m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0)); + } else { + //Cleanup cached pixmap if necessary + if (!m_cachedMirroredPixmap.isNull()) + m_cachedMirroredPixmap = QPixmap(); + } + m_cachedMirroredPixmapIsDirty = false; + } +} + +void QSGSoftwareInternalImageNode::preprocess() +{ + bool doDirty = false; + QSGLayer *t = qobject_cast<QSGLayer *>(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 QSGSoftwareInternalImageNode::paint(QPainter *painter) +{ + painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + + const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : 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()); + QSGSoftwareHelpers::QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height())); + QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()), + margins, tilerules, QSGSoftwareHelpers::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); + } +} + +QRectF QSGSoftwareInternalImageNode::rect() const +{ + return m_targetRect; +} + +const QPixmap &QSGSoftwareInternalImageNode::pixmap() const +{ + if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(m_texture)) { + return pt->pixmap(); + } else { + QSGSoftwareLayer *layer = qobject_cast<QSGSoftwareLayer*>(m_texture); + return layer->pixmap(); + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h new file mode 100644 index 0000000000..f21667fdf7 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREINTERNALIMAGENODE_H +#define QSGSOFTWAREINTERNALIMAGENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> +#include <private/qsgtexturematerial_p.h> + +QT_BEGIN_NAMESPACE + +namespace QSGSoftwareHelpers { + +typedef QVarLengthArray<QPainter::PixmapFragment, 16> 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 + +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); + +} // QSGSoftwareHelpers namespace + +class QSGSoftwareInternalImageNode : public QSGInternalImageNode +{ +public: + QSGSoftwareInternalImageNode(); + + void setTargetRect(const QRectF &rect) override; + void setInnerTargetRect(const QRectF &rect) override; + void setInnerSourceRect(const QRectF &rect) override; + void setSubSourceRect(const QRectF &rect) override; + void setTexture(QSGTexture *texture) override; + void setMirror(bool mirror) override; + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; + void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; + void update() override; + + void preprocess() override; + + void paint(QPainter *painter); + + QRectF rect() const; + +private: + const QPixmap &pixmap() const; + + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + QSGTexture *m_texture; + QPixmap m_cachedMirroredPixmap; + + bool m_mirror; + bool m_smooth; + bool m_tileHorizontal; + bool m_tileVertical; + bool m_cachedMirroredPixmapIsDirty; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREINTERNALIMAGENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp new file mode 100644 index 0000000000..f6898b3879 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwareinternalrectanglenode_p.h" +#include <qmath.h> + +#include <QtGui/QPainter> + +QT_BEGIN_NAMESPACE + +QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode() + : m_penWidth(0) + , m_radius(0) + , m_cornerPixmapIsDirty(true) + , m_devicePixelRatio(1) +{ + m_pen.setJoinStyle(Qt::MiterJoin); + m_pen.setMiterLimit(0); + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareInternalRectangleNode::setRect(const QRectF &rect) +{ + QRect alignedRect = rect.toAlignedRect(); + if (m_rect != alignedRect) { + m_rect = alignedRect; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setPenColor(const QColor &color) +{ + if (m_penColor != color) { + m_penColor = color; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setPenWidth(qreal width) +{ + if (m_penWidth != width) { + m_penWidth = width; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +//Move first stop by pos relative to seconds +static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGradientStop &secondStop, double newPos) +{ + double distance = secondStop.first - firstStop.first; + double distanceDelta = newPos - firstStop.first; + double modifierValue = distanceDelta / distance; + int redDelta = (secondStop.second.red() - firstStop.second.red()) * modifierValue; + int greenDelta = (secondStop.second.green() - firstStop.second.green()) * modifierValue; + int blueDelta = (secondStop.second.blue() - firstStop.second.blue()) * modifierValue; + int alphaDelta = (secondStop.second.alpha() - firstStop.second.alpha()) * modifierValue; + + QGradientStop newStop; + newStop.first = newPos; + newStop.second = QColor(firstStop.second.red() + redDelta, + firstStop.second.green() + greenDelta, + firstStop.second.blue() + blueDelta, + firstStop.second.alpha() + alphaDelta); + + return newStop; +} + +void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &stops) +{ + //normalize stops + bool needsNormalization = false; + for (const QGradientStop &stop : qAsConst(stops)) { + if (stop.first < 0.0 || stop.first > 1.0) { + needsNormalization = true; + continue; + } + } + + if (needsNormalization) { + QGradientStops normalizedStops; + if (stops.count() == 1) { + //If there is only one stop, then the position does not matter + //It is just treated as a color + QGradientStop stop = stops.at(0); + stop.first = 0.0; + normalizedStops.append(stop); + } else { + //Clip stops to only the first below 0.0 and above 1.0 + int below = -1; + int above = -1; + QVector<int> between; + for (int i = 0; i < stops.count(); ++i) { + if (stops.at(i).first < 0.0) { + below = i; + } else if (stops.at(i).first > 1.0) { + above = i; + break; + } else { + between.append(i); + } + } + + //Interpoloate new color values for above and below + if (below != -1 ) { + //If there are more than one stops left, interpolate + if (below + 1 < stops.count()) { + normalizedStops.append(interpolateStop(stops.at(below), stops.at(below + 1), 0.0)); + } else { + QGradientStop singleStop; + singleStop.first = 0.0; + singleStop.second = stops.at(below).second; + normalizedStops.append(singleStop); + } + } + + for (int i = 0; i < between.count(); ++i) + normalizedStops.append(stops.at(between.at(i))); + + if (above != -1) { + //If there stops before above, interpolate + if (above >= 1) { + normalizedStops.append(interpolateStop(stops.at(above), stops.at(above - 1), 1.0)); + } else { + QGradientStop singleStop; + singleStop.first = 1.0; + singleStop.second = stops.at(above).second; + normalizedStops.append(singleStop); + } + } + } + + m_stops = normalizedStops; + + } else { + m_stops = stops; + } + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); +} + +void QSGSoftwareInternalRectangleNode::setRadius(qreal radius) +{ + if (m_radius != radius) { + m_radius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/) +{ +} + +void QSGSoftwareInternalRectangleNode::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) { + generateCornerPixmap(); + m_cornerPixmapIsDirty = false; + } +} + +void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) +{ + //We can only check for a device pixel ratio change when we know what + //paint device is being used. + if (painter->device()->devicePixelRatio() != m_devicePixelRatio) { + m_devicePixelRatio = painter->device()->devicePixelRatio(); + generateCornerPixmap(); + } + + if (painter->transform().isRotating()) { + //Rotated rectangles lose the benefits of direct rendering, and have poor rendering + //quality when using only blits and fills. + + if (m_radius == 0 && m_penWidth == 0) { + //Non-Rounded Rects without borders (fall back to drawRect) + //Most common case + painter->setPen(Qt::NoPen); + painter->setBrush(m_brush); + painter->drawRect(m_rect); + } else { + //Rounded Rects and Rects with Borders + //Avoids broken behaviors of QPainter::drawRect/roundedRect + QPixmap pixmap = QPixmap(m_rect.width() * m_devicePixelRatio, m_rect.height() * m_devicePixelRatio); + pixmap.fill(Qt::transparent); + pixmap.setDevicePixelRatio(m_devicePixelRatio); + QPainter pixmapPainter(&pixmap); + paintRectangle(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height())); + + QPainter::RenderHints previousRenderHints = painter->renderHints(); + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->drawPixmap(m_rect, pixmap); + painter->setRenderHints(previousRenderHints); + } + + + } else { + //Paint directly + paintRectangle(painter, m_rect); + } + +} + +bool QSGSoftwareInternalRectangleNode::isOpaque() const +{ + if (m_radius > 0.0f) + return false; + if (m_color.alpha() < 255) + return false; + if (m_penWidth > 0.0f && m_penColor.alpha() < 255) + return false; + if (m_stops.count() > 0) { + for (const QGradientStop &stop : qAsConst(m_stops)) { + if (stop.second.alpha() < 255) + return false; + } + } + + return true; +} + +QRectF QSGSoftwareInternalRectangleNode::rect() const +{ + //TODO: double check that this is correct. + return m_rect; +} + +void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const QRect &rect) +{ + //Radius should never exceeds half of the width or half of the height + int radius = qFloor(qMin(qMin(rect.width(), rect.height()) * 0.5, m_radius)); + + QPainter::RenderHints previousRenderHints = painter->renderHints(); + painter->setRenderHint(QPainter::Antialiasing, false); + + if (m_penWidth > 0) { + //Fill border Rects + + //Borders can not be more than half the height/width of a rect + double borderWidth = qMin(m_penWidth, rect.width() * 0.5); + double borderHeight = qMin(m_penWidth, rect.height() * 0.5); + + + + if (borderWidth > radius) { + //4 Rects + QRectF borderTopOutside(QPointF(rect.x() + radius, rect.y()), + QPointF(rect.x() + rect.width() - radius, rect.y() + radius)); + QRectF borderTopInside(QPointF(rect.x() + borderWidth, rect.y() + radius), + QPointF(rect.x() + rect.width() - borderWidth, rect.y() + borderHeight)); + QRectF borderBottomOutside(QPointF(rect.x() + radius, rect.y() + rect.height() - radius), + QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height())); + QRectF borderBottomInside(QPointF(rect.x() + borderWidth, rect.y() + rect.height() - borderHeight), + QPointF(rect.x() + rect.width() - borderWidth, rect.y() + rect.height() - radius)); + + if (borderTopOutside.isValid()) + painter->fillRect(borderTopOutside, m_penColor); + if (borderTopInside.isValid()) + painter->fillRect(borderTopInside, m_penColor); + if (borderBottomOutside.isValid()) + painter->fillRect(borderBottomOutside, m_penColor); + if (borderBottomInside.isValid()) + painter->fillRect(borderBottomInside, m_penColor); + + } else { + //2 Rects + QRectF borderTop(QPointF(rect.x() + radius, rect.y()), + QPointF(rect.x() + rect.width() - radius, rect.y() + borderHeight)); + QRectF borderBottom(QPointF(rect.x() + radius, rect.y() + rect.height() - borderHeight), + QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height())); + if (borderTop.isValid()) + painter->fillRect(borderTop, m_penColor); + if (borderBottom.isValid()) + painter->fillRect(borderBottom, m_penColor); + } + QRectF borderLeft(QPointF(rect.x(), rect.y() + radius), + QPointF(rect.x() + borderWidth, rect.y() + rect.height() - radius)); + QRectF borderRight(QPointF(rect.x() + rect.width() - borderWidth, rect.y() + radius), + QPointF(rect.x() + rect.width(), rect.y() + rect.height() - radius)); + if (borderLeft.isValid()) + painter->fillRect(borderLeft, m_penColor); + if (borderRight.isValid()) + painter->fillRect(borderRight, m_penColor); + } + + + if (radius > 0) { + + if (radius * 2 >= rect.width() && radius * 2 >= rect.height()) { + //Blit whole pixmap for circles + painter->drawPixmap(rect, m_cornerPixmap, m_cornerPixmap.rect()); + } else { + + //blit 4 corners to border + int scaledRadius = radius * m_devicePixelRatio; + QRectF topLeftCorner(QPointF(rect.x(), rect.y()), + QPointF(rect.x() + radius, rect.y() + radius)); + painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, scaledRadius, scaledRadius)); + QRectF topRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y()), + QPointF(rect.x() + rect.width(), rect.y() + radius)); + painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(scaledRadius, 0, scaledRadius, scaledRadius)); + QRectF bottomLeftCorner(QPointF(rect.x(), rect.y() + rect.height() - radius), + QPointF(rect.x() + radius, rect.y() + rect.height())); + painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, scaledRadius, scaledRadius, scaledRadius)); + QRectF bottomRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height() - radius), + QPointF(rect.x() + rect.width(), rect.y() + rect.height())); + painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(scaledRadius, scaledRadius, scaledRadius, scaledRadius)); + + } + + } + + QRectF brushRect = QRectF(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); +} + +void QSGSoftwareInternalRectangleNode::generateCornerPixmap() +{ + //Generate new corner Pixmap + int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); + + m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio); + m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio); + 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(); + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h new file mode 100644 index 0000000000..f363e279e1 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREINTERNALRECTANGLENODE_H +#define QSGSOFTWAREINTERNALRECTANGLENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +#include <QPen> +#include <QBrush> +#include <QPixmap> + +QT_BEGIN_NAMESPACE + +class QSGSoftwareInternalRectangleNode : public QSGInternalRectangleNode +{ +public: + QSGSoftwareInternalRectangleNode(); + + void setRect(const QRectF &rect) override; + void setColor(const QColor &color) override; + void setPenColor(const QColor &color) override; + void setPenWidth(qreal width) override; + void setGradientStops(const QGradientStops &stops) override; + void setRadius(qreal radius) override; + void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) } + void setAligned(bool aligned) override; + + void update() override; + + void paint(QPainter *); + + bool isOpaque() const; + QRectF rect() const; +private: + void paintRectangle(QPainter *painter, const QRect &rect); + void generateCornerPixmap(); + + QRect 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; + + int m_devicePixelRatio; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREINTERNALRECTANGLENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp new file mode 100644 index 0000000000..7020283898 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarelayer_p.h" + +#include "qsgsoftwarecontext_p.h" +#include "qsgsoftwarepixmaprenderer_p.h" + +QT_BEGIN_NAMESPACE + +QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext) + : m_item(0) + , m_context(renderContext) + , m_renderer(0) + , m_device_pixel_ratio(1) + , m_mirrorHorizontal(false) + , m_mirrorVertical(false) + , m_live(true) + , m_grab(true) + , m_recursive(false) + , m_dirtyTexture(true) +{ + +} + +QSGSoftwareLayer::~QSGSoftwareLayer() +{ + invalidated(); +} + +int QSGSoftwareLayer::textureId() const +{ + return 0; +} + +QSize QSGSoftwareLayer::textureSize() const +{ + return m_pixmap.size(); +} + +bool QSGSoftwareLayer::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool QSGSoftwareLayer::hasMipmaps() const +{ + return false; +} + +void QSGSoftwareLayer::bind() +{ +} + +bool QSGSoftwareLayer::updateTexture() +{ + bool doGrab = (m_live || m_grab) && m_dirtyTexture; + if (doGrab) + grab(); + if (m_grab) + emit scheduledUpdateCompleted(); + m_grab = false; + return doGrab; +} + +void QSGSoftwareLayer::setItem(QSGNode *item) +{ + if (item == m_item) + return; + m_item = item; + + if (m_live && !m_item) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void QSGSoftwareLayer::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + markDirtyTexture(); +} + +void QSGSoftwareLayer::setSize(const QSize &size) +{ + if (size == m_size) + return; + m_size = size; + + if (m_live && m_size.isNull()) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void QSGSoftwareLayer::scheduleUpdate() +{ + if (m_grab) + return; + m_grab = true; + if (m_dirtyTexture) { + emit updateRequested(); + } +} + +QImage QSGSoftwareLayer::toImage() const +{ + return m_pixmap.toImage(); +} + +void QSGSoftwareLayer::setLive(bool live) +{ + if (live == m_live) + return; + m_live = live; + + if (m_live && (!m_item || m_size.isNull())) + m_pixmap = QPixmap(); + + markDirtyTexture(); +} + +void QSGSoftwareLayer::setRecursive(bool recursive) +{ + m_recursive = recursive; +} + +void QSGSoftwareLayer::setFormat(uint) +{ +} + +void QSGSoftwareLayer::setHasMipmaps(bool) +{ +} + +void QSGSoftwareLayer::setDevicePixelRatio(qreal ratio) +{ + m_device_pixel_ratio = ratio; +} + +void QSGSoftwareLayer::setMirrorHorizontal(bool mirror) +{ + if (m_mirrorHorizontal == mirror) + return; + m_mirrorHorizontal = mirror; + markDirtyTexture(); +} + +void QSGSoftwareLayer::setMirrorVertical(bool mirror) +{ + if (m_mirrorVertical == mirror) + return; + m_mirrorVertical = mirror; + markDirtyTexture(); +} + +void QSGSoftwareLayer::markDirtyTexture() +{ + m_dirtyTexture = true; + if (m_live || m_grab) { + emit updateRequested(); + } +} + +void QSGSoftwareLayer::invalidated() +{ + delete m_renderer; + m_renderer = 0; +} + +void QSGSoftwareLayer::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 QSGSoftwarePixmapRenderer(m_context); + connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture())); + } + m_renderer->setDevicePixelRatio(m_device_pixel_ratio); + m_renderer->setRootNode(static_cast<QSGRootNode *>(root)); + + if (m_pixmap.size() != m_size) { + m_pixmap = QPixmap(m_size); + m_pixmap.setDevicePixelRatio(m_device_pixel_ratio); + // This fill here is wasteful, but necessary because it is the only way + // to force a QImage based pixmap to have an alpha channel. + m_pixmap.fill(Qt::transparent); + } + + // Render texture. + 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); + QRect mirrored(m_mirrorHorizontal ? m_rect.right() * m_device_pixel_ratio : m_rect.left() * m_device_pixel_ratio, + m_mirrorVertical ? m_rect.top() * m_device_pixel_ratio : m_rect.bottom() * m_device_pixel_ratio, + m_mirrorHorizontal ? -m_rect.width() * m_device_pixel_ratio : m_rect.width() * m_device_pixel_ratio, + m_mirrorVertical ? m_rect.height() * m_device_pixel_ratio : -m_rect.height() * m_device_pixel_ratio); + m_renderer->setProjectionRect(mirrored); + m_renderer->setClearColor(Qt::transparent); + + m_renderer->renderScene(); + 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'. +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h new file mode 100644 index 0000000000..d3f13e40b1 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARELAYER_H +#define QSGSOFTWARELAYER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> +#include <private/qsgcontext_p.h> + +QT_BEGIN_NAMESPACE + +class QSGSoftwarePixmapRenderer; + +class QSGSoftwareLayer : public QSGLayer +{ + Q_OBJECT +public: + QSGSoftwareLayer(QSGRenderContext *renderContext); + ~QSGSoftwareLayer(); + + const QPixmap &pixmap() const { return m_pixmap; } + + // QSGTexture interface +public: + int textureId() const override; + QSize textureSize() const override; + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + void bind() override; + + // QSGDynamicTexture interface +public: + bool updateTexture() override; + + // QSGLayer interface +public: + void setItem(QSGNode *item) override; + void setRect(const QRectF &rect) override; + void setSize(const QSize &size) override; + void scheduleUpdate() override; + QImage toImage() const override; + void setLive(bool live) override; + void setRecursive(bool recursive) override; + void setFormat(uint) override; + void setHasMipmaps(bool) override; + void setDevicePixelRatio(qreal ratio) override; + void setMirrorHorizontal(bool mirror) override; + void setMirrorVertical(bool mirror) override; + +public slots: + void markDirtyTexture() override; + void invalidated() override; + +private: + void grab(); + + QSGNode *m_item; + QSGRenderContext *m_context; + QSGSoftwarePixmapRenderer *m_renderer; + QRectF m_rect; + QSize m_size; + QPixmap m_pixmap; + qreal m_device_pixel_ratio; + bool m_mirrorHorizontal; + bool m_mirrorVertical; + bool m_live; + bool m_grab; + bool m_recursive; + bool m_dirtyTexture; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARELAYER_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp new file mode 100644 index 0000000000..34b0cd5b72 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarepainternode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +QSGSoftwarePainterNode::QSGSoftwarePainterNode(QQuickPaintedItem *item) + : QSGPainterNode() + , m_preferredRenderTarget(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_fastFBOResizing(false) + , m_fillColor(Qt::transparent) + , m_contentsScale(1.0) + , m_dirtyGeometry(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +QSGSoftwarePainterNode::~QSGSoftwarePainterNode() +{ + delete m_texture; +} + +void QSGSoftwarePainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) +{ + if (m_preferredRenderTarget == target) + return; + + m_preferredRenderTarget = target; +} + +void QSGSoftwarePainterNode::setSize(const QSize &size) +{ + if (size == m_size) + return; + + m_size = size; + + m_dirtyGeometry = true; +} + +void QSGSoftwarePainterNode::setDirty(const QRect &dirtyRect) +{ + m_dirtyContents = true; + m_dirtyRect = dirtyRect; + markDirty(DirtyMaterial); +} + +void QSGSoftwarePainterNode::setOpaquePainting(bool opaque) +{ + if (opaque == m_opaquePainting) + return; + + m_opaquePainting = opaque; +} + +void QSGSoftwarePainterNode::setLinearFiltering(bool linearFiltering) +{ + if (linearFiltering == m_linear_filtering) + return; + + m_linear_filtering = linearFiltering; +} + +void QSGSoftwarePainterNode::setMipmapping(bool mipmapping) +{ + if (mipmapping == m_mipmapping) + return; + + m_mipmapping = mipmapping; +} + +void QSGSoftwarePainterNode::setSmoothPainting(bool s) +{ + if (s == m_smoothPainting) + return; + + m_smoothPainting = s; +} + +void QSGSoftwarePainterNode::setFillColor(const QColor &c) +{ + if (c == m_fillColor) + return; + + m_fillColor = c; + markDirty(DirtyMaterial); +} + +void QSGSoftwarePainterNode::setContentsScale(qreal s) +{ + if (s == m_contentsScale) + return; + + m_contentsScale = s; + markDirty(DirtyMaterial); +} + +void QSGSoftwarePainterNode::setFastFBOResizing(bool dynamic) +{ + m_fastFBOResizing = dynamic; +} + +QImage QSGSoftwarePainterNode::toImage() const +{ + return m_pixmap.toImage(); +} + +void QSGSoftwarePainterNode::update() +{ + if (m_dirtyGeometry) { + m_pixmap = QPixmap(m_textureSize); + if (!m_opaquePainting) + m_pixmap.fill(Qt::transparent); + + if (m_texture) + delete m_texture; + m_texture = new QSGSoftwarePixmapTexture(m_pixmap); + } + + if (m_dirtyContents) + paint(); + + m_dirtyGeometry = false; + m_dirtyContents = false; +} + +void QSGSoftwarePainterNode::paint(QPainter *painter) +{ + painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap); +} + +void QSGSoftwarePainterNode::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); + } + + QRect clipRect; + + if (m_contentsScale == 1) { + qreal scaleX = m_textureSize.width() / (qreal) m_size.width(); + qreal scaleY = m_textureSize.height() / (qreal) m_size.height(); + painter.scale(scaleX, scaleY); + clipRect = dirtyRect; + } else { + 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))); + + clipRect = sclip; + } + + if (!m_dirtyRect.isNull()) + painter.setClipRect(clipRect); + + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(clipRect, m_fillColor); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + m_item->paint(&painter); + painter.end(); + + m_dirtyRect = QRect(); +} + + +void QSGSoftwarePainterNode::setTextureSize(const QSize &size) +{ + if (size == m_textureSize) + return; + + m_textureSize = size; + m_dirtyGeometry = true; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h new file mode 100644 index 0000000000..67b45354a2 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREPAINTERNODE_H +#define QSGSOFTWAREPAINTERNODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> +#include <QtQuick/qquickpainteditem.h> + +#include <QtGui/QPixmap> + +QT_BEGIN_NAMESPACE + +class QSGSoftwarePainterNode : public QSGPainterNode +{ +public: + QSGSoftwarePainterNode(QQuickPaintedItem *item); + ~QSGSoftwarePainterNode(); + + void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) override; + + void setSize(const QSize &size) override; + QSize size() const { return m_size; } + + void setDirty(const QRect &dirtyRect = QRect()) override; + + void setOpaquePainting(bool opaque) override; + bool opaquePainting() const { return m_opaquePainting; } + + void setLinearFiltering(bool linearFiltering) override; + bool linearFiltering() const { return m_linear_filtering; } + + void setMipmapping(bool mipmapping) override; + bool mipmapping() const { return m_mipmapping; } + + void setSmoothPainting(bool s) override; + bool smoothPainting() const { return m_smoothPainting; } + + void setFillColor(const QColor &c) override; + QColor fillColor() const { return m_fillColor; } + + void setContentsScale(qreal s) override; + qreal contentsScale() const { return m_contentsScale; } + + void setFastFBOResizing(bool dynamic) override; + bool fastFBOResizing() const { return m_fastFBOResizing; } + + QImage toImage() const override; + void update() override; + QSGTexture *texture() const override { return m_texture; } + + void paint(QPainter *painter); + + void paint(); + + void setTextureSize(const QSize &size) override; + QSize textureSize() const { return m_textureSize; } + +private: + + QQuickPaintedItem::RenderTarget m_preferredRenderTarget; + + 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_fastFBOResizing; + QColor m_fillColor; + qreal m_contentsScale; + QSize m_textureSize; + + bool m_dirtyGeometry; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREPAINTERNODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp new file mode 100644 index 0000000000..f8c1a3d90b --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarepixmaprenderer_p.h" +#include "qsgsoftwarecontext_p.h" + +#include <QtQuick/QSGSimpleRectNode> + +#include <QElapsedTimer> + +Q_LOGGING_CATEGORY(lcPixmapRenderer, "qt.scenegraph.softwarecontext.pixmapRenderer") + +QT_BEGIN_NAMESPACE + +QSGSoftwarePixmapRenderer::QSGSoftwarePixmapRenderer(QSGRenderContext *context) + : QSGAbstractSoftwareRenderer(context) +{ + +} + +QSGSoftwarePixmapRenderer::~QSGSoftwarePixmapRenderer() +{ + +} + +void QSGSoftwarePixmapRenderer::renderScene(uint) +{ + class B : public QSGBindable + { + public: + void bind() const { } + } bindable; + QSGRenderer::renderScene(bindable); +} + +void QSGSoftwarePixmapRenderer::render() +{ + +} + +void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) +{ + QElapsedTimer renderTimer; + + // Setup background item + setBackgroundSize(QSize(target->width(), target->height())); + setBackgroundColor(clearColor()); + + QPainter painter(target); + painter.setRenderHint(QPainter::Antialiasing); + painter.setWindow(m_projectionRect); + auto rc = static_cast<QSGSoftwareRenderContext *>(context()); + QPainter *prevPainter = rc->m_activePainter; + rc->m_activePainter = &painter; + + renderTimer.start(); + buildRenderList(); + qint64 buildRenderListTime = renderTimer.restart(); + + // Optimize Renderlist + // Right now there is an assumption that when possible the same pixmap will + // be reused. So we can treat it like a backing store in that we can assume + // that when the pixmap is not being resized, the data can be reused. What is + // different though is that everything should be marked as dirty on a resize. + optimizeRenderList(); + qint64 optimizeRenderListTime = renderTimer.restart(); + + QRegion paintedRegion = renderNodes(&painter); + qint64 renderTime = renderTimer.elapsed(); + + rc->m_activePainter = prevPainter; + qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime; +} + +void QSGSoftwarePixmapRenderer::setProjectionRect(const QRect &projectionRect) +{ + m_projectionRect = projectionRect; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h new file mode 100644 index 0000000000..3b4fb304e7 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREPIXMAPRENDERER_H +#define QSGSOFTWAREPIXMAPRENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsgabstractsoftwarerenderer_p.h" + +QT_BEGIN_NAMESPACE + +class QSGSoftwarePixmapRenderer : public QSGAbstractSoftwareRenderer +{ +public: + QSGSoftwarePixmapRenderer(QSGRenderContext *context); + virtual ~QSGSoftwarePixmapRenderer(); + + void renderScene(uint fboId = 0) final; + void render() final; + + void render(QPaintDevice *target); + void setProjectionRect(const QRect &projectionRect); + +private: + QRect m_projectionRect; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREPIXMAPRENDERER_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp new file mode 100644 index 0000000000..534a0a4ec6 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarepixmaptexture_p.h" + +QT_BEGIN_NAMESPACE + +QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint flags) +{ + // Prevent pixmap format conversion to reduce memory consumption + // and surprises in calling code. (See QTBUG-47328) + if (flags & QSGRenderContext::CreateTexture_Alpha) { + //If texture should have an alpha + m_pixmap = QPixmap::fromImage(image, Qt::NoFormatConversion); + } else { + //Force opaque texture + m_pixmap = QPixmap::fromImage(image.convertToFormat(QImage::Format_RGB32), Qt::NoFormatConversion); + } +} + +QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QPixmap &pixmap) + : m_pixmap(pixmap) +{ +} + + +int QSGSoftwarePixmapTexture::textureId() const +{ + return 0; +} + +QSize QSGSoftwarePixmapTexture::textureSize() const +{ + return m_pixmap.size(); +} + +bool QSGSoftwarePixmapTexture::hasAlphaChannel() const +{ + return m_pixmap.hasAlphaChannel(); +} + +bool QSGSoftwarePixmapTexture::hasMipmaps() const +{ + return false; +} + +void QSGSoftwarePixmapTexture::bind() +{ + Q_UNREACHABLE(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h new file mode 100644 index 0000000000..034fa25da9 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREPIXMAPTEXTURE_H +#define QSGSOFTWAREPIXMAPTEXTURE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgtexture_p.h> + +QT_BEGIN_NAMESPACE + +class QSGSoftwarePixmapTexture : public QSGTexture +{ + Q_OBJECT +public: + QSGSoftwarePixmapTexture(const QImage &image, uint flags); + QSGSoftwarePixmapTexture(const QPixmap &pixmap); + + int textureId() const override; + QSize textureSize() const override; + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + void bind() override; + + const QPixmap &pixmap() const { return m_pixmap; } + +private: + QPixmap m_pixmap; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREPIXMAPTEXTURE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp new file mode 100644 index 0000000000..1fa5234377 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#include "qsgsoftwareinternalimagenode_p.h" + +QT_BEGIN_NAMESPACE + +QSGSoftwareRectangleNode::QSGSoftwareRectangleNode() + : m_color(QColor(255, 255, 255)) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareRectangleNode::paint(QPainter *painter) +{ + painter->fillRect(m_rect, m_color); +} + +QSGSoftwareImageNode::QSGSoftwareImageNode() + : m_texture(nullptr), + m_owns(false), + m_filtering(QSGTexture::None), + m_transformMode(NoTransform), + m_cachedMirroredPixmapIsDirty(false) +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +QSGSoftwareImageNode::~QSGSoftwareImageNode() +{ + if (m_owns) + delete m_texture; +} + +void QSGSoftwareImageNode::setTexture(QSGTexture *texture) +{ + m_texture = texture; markDirty(DirtyMaterial); + m_cachedMirroredPixmapIsDirty = true; +} + +void QSGSoftwareImageNode::setTextureCoordinatesTransform(QSGImageNode::TextureCoordinatesTransformMode transformNode) +{ + if (m_transformMode == transformNode) + return; + + m_transformMode = transformNode; + m_cachedMirroredPixmapIsDirty = true; + + markDirty(DirtyGeometry); +} + +void QSGSoftwareImageNode::paint(QPainter *painter) +{ + if (m_cachedMirroredPixmapIsDirty) + updateCachedMirroredPixmap(); + + painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear)); + + if (!m_cachedPixmap.isNull()) { + painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect); + } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) { + const QPixmap &pm = pt->pixmap(); + painter->drawPixmap(m_rect, pm, m_sourceRect); + } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) { + const QImage &im = pt->image(); + painter->drawImage(m_rect, im, m_sourceRect); + } +} + +void QSGSoftwareImageNode::updateCachedMirroredPixmap() +{ + if (m_transformMode == NoTransform) { + m_cachedPixmap = QPixmap(); + } else { + + if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) { + QTransform mirrorTransform; + if (m_transformMode.testFlag(MirrorVertically)) + mirrorTransform = mirrorTransform.scale(1, -1); + if (m_transformMode.testFlag(MirrorHorizontally)) + mirrorTransform = mirrorTransform.scale(-1, 1); + m_cachedPixmap = pt->pixmap().transformed(mirrorTransform); + } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) { + m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically))); + } else { + m_cachedPixmap = QPixmap(); + } + } + + m_cachedMirroredPixmapIsDirty = false; +} + +QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareNinePatchNode::setTexture(QSGTexture *texture) +{ + QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(texture); + if (!pt) { + qWarning() << "Image used with invalid texture format."; + return; + } + m_pixmap = pt->pixmap(); + markDirty(DirtyMaterial); +} + +void QSGSoftwareNinePatchNode::setBounds(const QRectF &bounds) +{ + if (m_bounds == bounds) + return; + + m_bounds = bounds; + markDirty(DirtyGeometry); +} + +void QSGSoftwareNinePatchNode::setDevicePixelRatio(qreal ratio) +{ + if (m_pixelRatio == ratio) + return; + + m_pixelRatio = ratio; + markDirty(DirtyGeometry); +} + +void QSGSoftwareNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + QMargins margins(qRound(left), qRound(top), qRound(right), qRound(bottom)); + if (m_margins == margins) + return; + + m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom)); + markDirty(DirtyGeometry); +} + +void QSGSoftwareNinePatchNode::update() +{ +} + +void QSGSoftwareNinePatchNode::paint(QPainter *painter) +{ + if (m_margins.isNull()) + painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); + else + QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()), + m_margins, Qt::StretchTile, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0)); +} + +QRectF QSGSoftwareNinePatchNode::bounds() const +{ + return m_bounds; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h new file mode 100644 index 0000000000..9f1913205b --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWAREPUBLICNODES_H +#define QSGSOFTWAREPUBLICNODES_H + +#include <QtQuick/qsgrectanglenode.h> +#include <QtQuick/qsgimagenode.h> +#include <QtQuick/qsgninepatchnode.h> +#include <QtGui/qpixmap.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QSGSoftwareRectangleNode : public QSGRectangleNode +{ +public: + QSGSoftwareRectangleNode(); + + void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); } + QRectF rect() const override { return m_rect; } + + void setColor(const QColor &color) override { m_color = color; markDirty(DirtyMaterial); } + QColor color() const override { return m_color; } + + void paint(QPainter *painter); + +private: + QRectF m_rect; + QColor m_color; +}; + +class QSGSoftwareImageNode : public QSGImageNode +{ +public: + QSGSoftwareImageNode(); + ~QSGSoftwareImageNode(); + + void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); } + QRectF rect() const override { return m_rect; } + + void setSourceRect(const QRectF &r) override { m_sourceRect = r; } + QRectF sourceRect() const override { return m_sourceRect; } + + void setTexture(QSGTexture *texture) override; + QSGTexture *texture() const override { return m_texture; } + + void setFiltering(QSGTexture::Filtering filtering) override { m_filtering = filtering; markDirty(DirtyMaterial); } + QSGTexture::Filtering filtering() const override { return m_filtering; } + + void setMipmapFiltering(QSGTexture::Filtering) override { } + QSGTexture::Filtering mipmapFiltering() const override { return QSGTexture::None; } + + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode transformNode) override; + TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return m_transformMode; } + + void setOwnsTexture(bool owns) override { m_owns = owns; } + bool ownsTexture() const override { return m_owns; } + + void paint(QPainter *painter); + +private: + void updateCachedMirroredPixmap(); + + QPixmap m_cachedPixmap; + QSGTexture *m_texture; + QRectF m_rect; + QRectF m_sourceRect; + bool m_owns; + QSGTexture::Filtering m_filtering; + TextureCoordinatesTransformMode m_transformMode; + bool m_cachedMirroredPixmapIsDirty; +}; + +class QSGSoftwareNinePatchNode : public QSGNinePatchNode +{ +public: + QSGSoftwareNinePatchNode(); + + void setTexture(QSGTexture *texture) override; + void setBounds(const QRectF &bounds) override; + void setDevicePixelRatio(qreal ratio) override; + void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; + void update() override; + + void paint(QPainter *painter); + + QRectF bounds() const; + +private: + QPixmap m_pixmap; + QRectF m_bounds; + qreal m_pixelRatio; + QMargins m_margins; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWAREPUBLICNODES_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp new file mode 100644 index 0000000000..59c47db0c4 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -0,0 +1,434 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarerenderablenode_p.h" + +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" +#include "qsgsoftwareglyphnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarepainternode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#if QT_CONFIG(quick_sprite) +#include "qsgsoftwarespritenode_p.h" +#endif + +#include <qsgsimplerectnode.h> +#include <qsgsimpletexturenode.h> +#include <private/qsgrendernode_p.h> +#include <private/qsgtexture_p.h> + +Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable") + +QT_BEGIN_NAMESPACE + +QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node) + : m_nodeType(type) + , m_isOpaque(true) + , m_isDirty(true) + , m_hasClipRegion(false) + , m_opacity(1.0f) +{ + switch (m_nodeType) { + case QSGSoftwareRenderableNode::SimpleRect: + m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node); + break; + case QSGSoftwareRenderableNode::SimpleTexture: + m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node); + break; + case QSGSoftwareRenderableNode::Image: + m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node); + break; + case QSGSoftwareRenderableNode::Painter: + m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node); + break; + case QSGSoftwareRenderableNode::Rectangle: + m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node); + break; + case QSGSoftwareRenderableNode::Glyph: + m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node); + break; + case QSGSoftwareRenderableNode::NinePatch: + m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node); + break; + case QSGSoftwareRenderableNode::SimpleRectangle: + m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node); + break; + case QSGSoftwareRenderableNode::SimpleImage: + m_handle.simpleImageNode = static_cast<QSGImageNode*>(node); + break; +#if QT_CONFIG(quick_sprite) + case QSGSoftwareRenderableNode::SpriteNode: + m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node); + break; +#endif + case QSGSoftwareRenderableNode::RenderNode: + m_handle.renderNode = static_cast<QSGRenderNode*>(node); + break; + case QSGSoftwareRenderableNode::Invalid: + m_handle.simpleRectNode = nullptr; + break; + } +} + +QSGSoftwareRenderableNode::~QSGSoftwareRenderableNode() +{ + +} + +void QSGSoftwareRenderableNode::update() +{ + // Update the Node properties + m_isDirty = true; + + QRect boundingRect; + + switch (m_nodeType) { + case QSGSoftwareRenderableNode::SimpleRect: + if (m_handle.simpleRectNode->color().alpha() == 255 && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.simpleRectNode->rect().toRect(); + break; + case QSGSoftwareRenderableNode::SimpleTexture: + if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.simpleTextureNode->rect().toRect(); + break; + case QSGSoftwareRenderableNode::Image: + // There isn't a way to tell, so assume it's not + m_isOpaque = false; + + boundingRect = m_handle.imageNode->rect().toRect(); + break; + case QSGSoftwareRenderableNode::Painter: + if (m_handle.painterNode->opaquePainting() && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = QRect(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height()); + break; + case QSGSoftwareRenderableNode::Rectangle: + if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.rectangleNode->rect().toRect(); + break; + case QSGSoftwareRenderableNode::Glyph: + // Always has alpha + m_isOpaque = false; + + boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect(); + break; + case QSGSoftwareRenderableNode::NinePatch: + // Difficult to tell, assume non-opaque + m_isOpaque = false; + + boundingRect = m_handle.ninePatchNode->bounds().toRect(); + break; + case QSGSoftwareRenderableNode::SimpleRectangle: + if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.simpleRectangleNode->rect().toRect(); + break; + case QSGSoftwareRenderableNode::SimpleImage: + if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.simpleImageNode->rect().toRect(); + break; +#if QT_CONFIG(quick_sprite) + case QSGSoftwareRenderableNode::SpriteNode: + m_isOpaque = m_handle.spriteNode->isOpaque(); + boundingRect = m_handle.spriteNode->rect().toRect(); + break; +#endif + case QSGSoftwareRenderableNode::RenderNode: + if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating()) + m_isOpaque = true; + else + m_isOpaque = false; + + boundingRect = m_handle.renderNode->rect().toRect(); + break; + default: + break; + } + + m_boundingRect = m_transform.mapRect(boundingRect); + + if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) { + // If there is a clipRegion, and it is empty, the item wont be rendered + if (m_clipRegion.isEmpty()) + m_boundingRect = QRect(); + else + m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first()); + } + + // Overrides + if (m_opacity < 1.0f) + m_isOpaque = false; + + m_dirtyRegion = QRegion(m_boundingRect); +} + +struct RenderNodeState : public QSGRenderNode::RenderState +{ + const QMatrix4x4 *projectionMatrix() const override { return &ident; } + QRect scissorRect() const override { return QRect(); } + bool scissorEnabled() const override { return false; } + int stencilValue() const override { return 0; } + bool stencilEnabled() const override { return false; } + const QRegion *clipRegion() const override { return &cr; } + QMatrix4x4 ident; + QRegion cr; +}; + +QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting) +{ + Q_ASSERT(painter); + + // Check for don't paint conditions + if (m_nodeType != RenderNode) { + if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) { + m_isDirty = false; + m_dirtyRegion = QRegion(); + return QRegion(); + } + } else { + if (!m_isDirty || qFuzzyIsNull(m_opacity)) { + m_isDirty = false; + m_dirtyRegion = QRegion(); + return QRegion(); + } else { + QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode); + QMatrix4x4 m = m_transform; + rd->m_matrix = &m; + rd->m_opacity = m_opacity; + RenderNodeState rs; + rs.cr = m_clipRegion; + + const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) + ? m_boundingRect : + QRect(0, 0, painter->device()->width(), painter->device()->height()); + + painter->save(); + painter->setClipRegion(br, Qt::ReplaceClip); + m_handle.renderNode->render(&rs); + painter->restore(); + + m_previousDirtyRegion = QRegion(br); + m_isDirty = false; + m_dirtyRegion = QRegion(); + return br; + } + } + + painter->save(); + painter->setOpacity(m_opacity); + + // Set clipRegion to m_dirtyRegion (in world coordinates) + // as m_dirtyRegion already accounts for clipRegion + painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip); + if (m_clipRegion.rectCount() > 1) + painter->setClipRegion(m_clipRegion, Qt::IntersectClip); + + painter->setTransform(m_transform, false); //precalculated worldTransform + if (forceOpaquePainting || m_isOpaque) + painter->setCompositionMode(QPainter::CompositionMode_Source); + + switch (m_nodeType) { + case QSGSoftwareRenderableNode::SimpleRect: + painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color()); + break; + case QSGSoftwareRenderableNode::SimpleTexture: + { + QSGTexture *texture = m_handle.simpleTextureNode->texture(); + if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(texture)) { + const QPixmap &pm = pt->pixmap(); + painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect()); + } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) { + const QImage &im = pt->image(); + painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect()); + } + } + break; + case QSGSoftwareRenderableNode::Image: + m_handle.imageNode->paint(painter); + break; + case QSGSoftwareRenderableNode::Painter: + m_handle.painterNode->paint(painter); + break; + case QSGSoftwareRenderableNode::Rectangle: + m_handle.rectangleNode->paint(painter); + break; + case QSGSoftwareRenderableNode::Glyph: + m_handle.glpyhNode->paint(painter); + break; + case QSGSoftwareRenderableNode::NinePatch: + m_handle.ninePatchNode->paint(painter); + break; + case QSGSoftwareRenderableNode::SimpleRectangle: + static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter); + break; + case QSGSoftwareRenderableNode::SimpleImage: + static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter); + break; +#if QT_CONFIG(quick_sprite) + case QSGSoftwareRenderableNode::SpriteNode: + static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter); + break; +#endif + default: + break; + } + + painter->restore(); + + QRegion areaToBeFlushed = m_dirtyRegion; + m_previousDirtyRegion = QRegion(m_boundingRect); + m_isDirty = false; + m_dirtyRegion = QRegion(); + + return areaToBeFlushed; +} + +QRect QSGSoftwareRenderableNode::boundingRect() const +{ + // This returns the bounding area of a renderable node in world coordinates + return m_boundingRect; +} + +bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const +{ + return m_dirtyRegion.isEmpty(); +} + +void QSGSoftwareRenderableNode::setTransform(const QTransform &transform) +{ + if (m_transform == transform) + return; + m_transform = transform; + update(); +} + +void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion) +{ + if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion) + return; + + m_clipRegion = clipRect; + m_hasClipRegion = hasClipRegion; + update(); +} + +void QSGSoftwareRenderableNode::setOpacity(float opacity) +{ + if (qFuzzyCompare(m_opacity, opacity)) + return; + + m_opacity = opacity; + update(); +} + +void QSGSoftwareRenderableNode::markGeometryDirty() +{ + update(); +} + +void QSGSoftwareRenderableNode::markMaterialDirty() +{ + update(); +} + +void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty) +{ + // Check if the dirty region applys to this node + QRegion prev = m_dirtyRegion; + if (dirtyRegion.intersects(boundingRect())) { + if (forceDirty) + m_isDirty = true; + m_dirtyRegion += dirtyRegion.intersected(boundingRect()); + } + qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion; +} + +void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion) +{ + QRegion prev = m_dirtyRegion; + if (m_isDirty) { + // Check if this rect concerns us + if (dirtyRegion.intersects(QRegion(boundingRect()))) { + m_dirtyRegion -= dirtyRegion; + if (m_dirtyRegion.isEmpty()) + m_isDirty = false; + } + } + qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion; +} + +QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const +{ + // When removing a node, the boundingRect shouldn't be subtracted + // because a deleted node has no valid boundingRect + if (wasRemoved) + return m_previousDirtyRegion; + + return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect)); +} + +QRegion QSGSoftwareRenderableNode::dirtyRegion() const +{ + return m_dirtyRegion; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h new file mode 100644 index 0000000000..473578c185 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARERENDERABLENODE_H +#define QSGSOFTWARERENDERABLENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qtquickglobal_p.h> + +#include <QtGui/QRegion> +#include <QtCore/QRect> +#include <QtGui/QTransform> +#include <QtQuick/qsgrectanglenode.h> +#include <QtQuick/qsgimagenode.h> +#include <QtQuick/qsgninepatchnode.h> + +QT_BEGIN_NAMESPACE + +class QSGSimpleRectNode; +class QSGSimpleTextureNode; +class QSGSoftwareInternalImageNode; +class QSGSoftwarePainterNode; +class QSGSoftwareInternalRectangleNode; +class QSGSoftwareGlyphNode; +class QSGSoftwareNinePatchNode; +class QSGSoftwareSpriteNode; +class QSGRenderNode; + +class QSGSoftwareRenderableNode +{ +public: + enum NodeType { + Invalid = -1, + SimpleRect, + SimpleTexture, + Image, + Painter, + Rectangle, + Glyph, + NinePatch, + SimpleRectangle, + SimpleImage, +#if QT_CONFIG(quick_sprite) + SpriteNode, +#endif + RenderNode + }; + + QSGSoftwareRenderableNode(NodeType type, QSGNode *node); + ~QSGSoftwareRenderableNode(); + + void update(); + + QRegion renderNode(QPainter *painter, bool forceOpaquePainting = false); + QRect boundingRect() const; + NodeType type() const { return m_nodeType; } + bool isOpaque() const { return m_isOpaque; } + bool isDirty() const { return m_isDirty; } + bool isDirtyRegionEmpty() const; + + void setTransform(const QTransform &transform); + void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true); + void setOpacity(float opacity); + QTransform transform() const { return m_transform; } + QRegion clipRegion() const { return m_clipRegion; } + float opacity() const { return m_opacity; } + + void markGeometryDirty(); + void markMaterialDirty(); + + void addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty = true); + void subtractDirtyRegion(const QRegion &dirtyRegion); + + QRegion previousDirtyRegion(bool wasRemoved = false) const; + QRegion dirtyRegion() const; + +private: + union RenderableNodeHandle { + QSGSimpleRectNode *simpleRectNode; + QSGSimpleTextureNode *simpleTextureNode; + QSGSoftwareInternalImageNode *imageNode; + QSGSoftwarePainterNode *painterNode; + QSGSoftwareInternalRectangleNode *rectangleNode; + QSGSoftwareGlyphNode *glpyhNode; + QSGSoftwareNinePatchNode *ninePatchNode; + QSGRectangleNode *simpleRectangleNode; + QSGImageNode *simpleImageNode; + QSGSoftwareSpriteNode *spriteNode; + QSGRenderNode *renderNode; + }; + + const NodeType m_nodeType; + RenderableNodeHandle m_handle; + + bool m_isOpaque; + + bool m_isDirty; + QRegion m_dirtyRegion; + QRegion m_previousDirtyRegion; + + QTransform m_transform; + QRegion m_clipRegion; + bool m_hasClipRegion; + float m_opacity; + + QRect m_boundingRect; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARERENDERABLENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp new file mode 100644 index 0000000000..4937565aa9 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarerenderablenodeupdater_p.h" + +#include "qsgabstractsoftwarerenderer_p.h" +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" +#include "qsgsoftwareglyphnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarepainternode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" + +#include <QtQuick/qsgsimplerectnode.h> +#include <QtQuick/qsgsimpletexturenode.h> +#include <QtQuick/qsgrendernode.h> + +QT_BEGIN_NAMESPACE + +QSGSoftwareRenderableNodeUpdater::QSGSoftwareRenderableNodeUpdater(QSGAbstractSoftwareRenderer *renderer) + : m_renderer(renderer) +{ + m_opacityState.push(1.0f); + // Invalid RectF by default for no clip + m_clipState.push(QRegion()); + m_hasClip = false; + m_transformState.push(QTransform()); +} + +QSGSoftwareRenderableNodeUpdater::~QSGSoftwareRenderableNodeUpdater() +{ + +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGTransformNode *node) +{ + m_transformState.push(node->matrix().toTransform() * m_transformState.top()); + m_stateMap[node] = currentState(node); + return true; +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *) +{ + m_transformState.pop(); +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) +{ + // Make sure to translate the clip rect into world coordinates + if (m_clipState.count() == 1) { + m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()))); + m_hasClip = true; + } else { + const QRegion transformedClipRect = m_transformState.top().map(QRegion(node->clipRect().toRect())); + m_clipState.push(transformedClipRect.intersected(m_clipState.top())); + } + m_stateMap[node] = currentState(node); + return true; +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *) +{ + m_clipState.pop(); + if (m_clipState.count() == 1) + m_hasClip = false; +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node) +{ + if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRect, rectNode); + } else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::SimpleTexture, tn); + } else if (QSGNinePatchNode *nn = dynamic_cast<QSGNinePatchNode *>(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, nn); + } else if (QSGRectangleNode *rn = dynamic_cast<QSGRectangleNode *>(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRectangle, rn); + } else if (QSGImageNode *n = dynamic_cast<QSGImageNode *>(node)) { + return updateRenderableNode(QSGSoftwareRenderableNode::SimpleImage, n); + } else { + // We dont know, so skip + return false; + } +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGeometryNode *) +{ +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGOpacityNode *node) +{ + m_opacityState.push(m_opacityState.top() * node->opacity()); + m_stateMap[node] = currentState(node); + return true; +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGOpacityNode *) +{ + m_opacityState.pop(); +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalImageNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::Image, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalImageNode *) +{ +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGPainterNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::Painter, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGPainterNode *) +{ +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalRectangleNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::Rectangle, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalRectangleNode *) +{ +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGGlyphNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::Glyph, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGlyphNode *) +{ +} + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node) +{ + m_stateMap[node] = currentState(node); + return true; +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *) +{ +} + +#if QT_CONFIG(quick_sprite) +bool QSGSoftwareRenderableNodeUpdater::visit(QSGSpriteNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::SpriteNode, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *) +{ + +} +#endif + +bool QSGSoftwareRenderableNodeUpdater::visit(QSGRenderNode *node) +{ + return updateRenderableNode(QSGSoftwareRenderableNode::RenderNode, node); +} + +void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRenderNode *) +{ +} + +void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRemoved) +{ + m_opacityState.clear(); + m_clipState.clear(); + m_transformState.clear(); + + auto parentNode = node->parent(); + // If the node was deleted, it will have no parent + // check if the state map has the previous parent + if ((!parentNode || isNodeRemoved ) && m_stateMap.contains(node)) + parentNode = m_stateMap[node].parent; + + // If we find a parent, use its state for updating the new children + if (parentNode && m_stateMap.contains(parentNode)) { + auto state = m_stateMap[parentNode]; + m_opacityState.push(state.opacity); + m_transformState.push(state.transform); + m_clipState.push(state.clip); + m_hasClip = state.hasClip; + } else { + // There is no parent, and no previous parent, so likely a root node + m_opacityState.push(1.0f); + m_transformState.push(QTransform()); + m_clipState.push(QRegion()); + m_hasClip = false; + } + + // If the node is being removed, then cleanup the state data + // Then just visit the children without visiting the now removed node + if (isNodeRemoved) { + m_stateMap.remove(node); + return; + } + + // Visit the current node itself first + switch (node->type()) { + case QSGNode::ClipNodeType: { + QSGClipNode *c = static_cast<QSGClipNode*>(node); + if (visit(c)) + visitChildren(c); + endVisit(c); + break; + } + case QSGNode::TransformNodeType: { + QSGTransformNode *c = static_cast<QSGTransformNode*>(node); + if (visit(c)) + visitChildren(c); + endVisit(c); + break; + } + case QSGNode::OpacityNodeType: { + QSGOpacityNode *c = static_cast<QSGOpacityNode*>(node); + if (visit(c)) + visitChildren(c); + endVisit(c); + break; + } + case QSGNode::GeometryNodeType: { + if (node->flags() & QSGNode::IsVisitableNode) { + QSGVisitableNode *v = static_cast<QSGVisitableNode*>(node); + v->accept(this); + } else { + QSGGeometryNode *c = static_cast<QSGGeometryNode*>(node); + if (visit(c)) + visitChildren(c); + endVisit(c); + } + break; + } + case QSGNode::RootNodeType: { + QSGRootNode *root = static_cast<QSGRootNode*>(node); + if (visit(root)) + visitChildren(root); + endVisit(root); + break; + } + case QSGNode::BasicNodeType: { + visitChildren(node); + break; + } + case QSGNode::RenderNodeType: { + QSGRenderNode *r = static_cast<QSGRenderNode*>(node); + if (visit(r)) + visitChildren(r); + endVisit(r); + break; + } + default: + Q_UNREACHABLE(); + break; + } +} + +QSGSoftwareRenderableNodeUpdater::NodeState QSGSoftwareRenderableNodeUpdater::currentState(QSGNode *node) const +{ + NodeState state; + state.opacity = m_opacityState.top(); + state.clip = m_clipState.top(); + state.hasClip = m_hasClip; + state.transform = m_transformState.top(); + state.parent = node->parent(); + return state; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h new file mode 100644 index 0000000000..ca16f77c22 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARERENDERABLENODEUPDATER_H +#define QSGSOFTWARERENDERABLENODEUPDATER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsgsoftwarerenderablenode_p.h" +#include "qsgabstractsoftwarerenderer_p.h" + +#include <private/qsgadaptationlayer_p.h> + +#include <QTransform> +#include <QStack> +#include <QRectF> + +QT_BEGIN_NAMESPACE + +class QSGSoftwareRenderableNodeUpdater : public QSGNodeVisitorEx +{ +public: + QSGSoftwareRenderableNodeUpdater(QSGAbstractSoftwareRenderer *renderer); + virtual ~QSGSoftwareRenderableNodeUpdater(); + + bool visit(QSGTransformNode *) override; + void endVisit(QSGTransformNode *) override; + bool visit(QSGClipNode *) override; + void endVisit(QSGClipNode *) override; + bool visit(QSGGeometryNode *) override; + void endVisit(QSGGeometryNode *) override; + bool visit(QSGOpacityNode *) override; + void endVisit(QSGOpacityNode *) override; + bool visit(QSGInternalImageNode *) override; + void endVisit(QSGInternalImageNode *) override; + bool visit(QSGPainterNode *) override; + void endVisit(QSGPainterNode *) override; + bool visit(QSGInternalRectangleNode *) override; + void endVisit(QSGInternalRectangleNode *) override; + bool visit(QSGGlyphNode *) override; + void endVisit(QSGGlyphNode *) override; + bool visit(QSGRootNode *) override; + void endVisit(QSGRootNode *) override; +#if QT_CONFIG(quick_sprite) + bool visit(QSGSpriteNode *) override; + void endVisit(QSGSpriteNode *) override; +#endif + bool visit(QSGRenderNode *) override; + void endVisit(QSGRenderNode *) override; + + void updateNodes(QSGNode *node, bool isNodeRemoved = false); + +private: + struct NodeState { + float opacity; + QRegion clip; + bool hasClip; + QTransform transform; + QSGNode *parent; + }; + + NodeState currentState(QSGNode *node) const; + + template<class NODE> + bool updateRenderableNode(QSGSoftwareRenderableNode::NodeType type, NODE *node); + + QSGAbstractSoftwareRenderer *m_renderer; + QStack<float> m_opacityState; + QStack<QRegion> m_clipState; + bool m_hasClip; + QStack<QTransform> m_transformState; + QHash<QSGNode*,NodeState> m_stateMap; +}; + +template<class NODE> +bool QSGSoftwareRenderableNodeUpdater::updateRenderableNode(QSGSoftwareRenderableNode::NodeType type, NODE *node) +{ + //Check if we already know about node + auto renderableNode = m_renderer->renderableNode(node); + if (renderableNode == nullptr) { + renderableNode = new QSGSoftwareRenderableNode(type, node); + m_renderer->addNodeMapping(node, renderableNode); + } + + //Update the node + renderableNode->setTransform(m_transformState.top()); + renderableNode->setOpacity(m_opacityState.top()); + renderableNode->setClipRegion(m_clipState.top(), m_hasClip); + + renderableNode->update(); + m_stateMap[node] = currentState(node); + + return true; +} + +QT_END_NAMESPACE + +#endif // QSGSOFTWARERENDERABLENODEUPDATER_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp new file mode 100644 index 0000000000..cad826fb27 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarerenderer_p.h" + +#include "qsgsoftwarerenderablenodeupdater_p.h" +#include "qsgsoftwarerenderlistbuilder_p.h" +#include "qsgsoftwarecontext_p.h" +#include "qsgsoftwarerenderablenode_p.h" + +#include <QtGui/QPaintDevice> +#include <QtGui/QBackingStore> +#include <QElapsedTimer> + +Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer") + +QT_BEGIN_NAMESPACE + +QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context) + : QSGAbstractSoftwareRenderer(context) + , m_paintDevice(nullptr) + , m_backingStore(nullptr) +{ +} + +QSGSoftwareRenderer::~QSGSoftwareRenderer() +{ +} + +void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device) +{ + m_paintDevice = device; + m_backingStore = nullptr; +} + +QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const +{ + return m_paintDevice; +} + +void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore) +{ + m_backingStore = backingStore; + m_paintDevice = nullptr; +} + +QRegion QSGSoftwareRenderer::flushRegion() const +{ + return m_flushRegion; +} + +void QSGSoftwareRenderer::renderScene(uint) +{ + class B : public QSGBindable + { + public: + void bind() const { } + } bindable; + QSGRenderer::renderScene(bindable); +} + +void QSGSoftwareRenderer::render() +{ + if (!m_paintDevice && !m_backingStore) + return; + + // If there is a backingstore, set the current paint device + if (m_backingStore) { + // For HiDPI QBackingStores, the paint device is not valid + // until begin() has been called. See: QTBUG-55875 + m_backingStore->beginPaint(QRegion()); + m_paintDevice = m_backingStore->paintDevice(); + m_backingStore->endPaint(); + } + + QElapsedTimer renderTimer; + + setBackgroundColor(clearColor()); + setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(), + m_paintDevice->height() / m_paintDevice->devicePixelRatio())); + + // Build Renderlist + // The renderlist is created by visiting each node in the tree and when a + // renderable node is reach, we find the coorosponding RenderableNode object + // and append it to the renderlist. At this point the RenderableNode object + // should not need any further updating so it is just a matter of appending + // RenderableNodes + renderTimer.start(); + buildRenderList(); + qint64 buildRenderListTime = renderTimer.restart(); + + // Optimize Renderlist + // This is a pass through the renderlist to determine what actually needs to + // be painted. Without this pass the renderlist will simply render each item + // from back to front, with a high potential for overdraw. It would also lead + // to the entire window being flushed every frame. The objective of the + // optimization pass is to only paint dirty nodes that are not occuluded. A + // side effect of this is that additional nodes may need to be marked dirty to + // force a repaint. It is also important that any item that needs to be + // repainted only paints what is needed, via the use of clip regions. + const QRegion updateRegion = optimizeRenderList(); + qint64 optimizeRenderListTime = renderTimer.restart(); + + // If Rendering to a backingstore, prepare it to be updated + if (m_backingStore != nullptr) { + m_backingStore->beginPaint(updateRegion); + // It is possible that a QBackingStore's paintDevice() will change + // when begin() is called. + m_paintDevice = m_backingStore->paintDevice(); + } + + QPainter painter(m_paintDevice); + painter.setRenderHint(QPainter::Antialiasing); + auto rc = static_cast<QSGSoftwareRenderContext *>(context()); + QPainter *prevPainter = rc->m_activePainter; + rc->m_activePainter = &painter; + + // Render the contents Renderlist + m_flushRegion = renderNodes(&painter); + qint64 renderTime = renderTimer.elapsed(); + + if (m_backingStore != nullptr) + m_backingStore->endPaint(); + + rc->m_activePainter = prevPainter; + qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h new file mode 100644 index 0000000000..bb28da4ca5 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARERENDERER_H +#define QSGSOFTWARERENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsgabstractsoftwarerenderer_p.h" + +QT_BEGIN_NAMESPACE + +class QPaintDevice; +class QBackingStore; + +class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer +{ +public: + QSGSoftwareRenderer(QSGRenderContext *context); + virtual ~QSGSoftwareRenderer(); + + void setCurrentPaintDevice(QPaintDevice *device); + QPaintDevice *currentPaintDevice() const; + void setBackingStore(QBackingStore *backingStore); + QRegion flushRegion() const; + +protected: + void renderScene(uint fboId = 0) final; + void render() final; + +private: + QPaintDevice* m_paintDevice; + QBackingStore* m_backingStore; + QRegion m_flushRegion; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARERENDERER_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp new file mode 100644 index 0000000000..70dce71801 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarerenderlistbuilder_p.h" + +#include "qsgsoftwarerenderablenode_p.h" +#include "qsgabstractsoftwarerenderer_p.h" +#include "qsgsoftwareinternalimagenode_p.h" +#include "qsgsoftwareinternalrectanglenode_p.h" +#include "qsgsoftwareglyphnode_p.h" +#include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarepainternode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" + +#include <QtQuick/qsgsimplerectnode.h> +#include <QtQuick/qsgsimpletexturenode.h> +#include <QtQuick/qsgrendernode.h> + +QT_BEGIN_NAMESPACE + +QSGSoftwareRenderListBuilder::QSGSoftwareRenderListBuilder(QSGAbstractSoftwareRenderer *renderer) + : m_renderer(renderer) +{ + +} + +bool QSGSoftwareRenderListBuilder::visit(QSGTransformNode *) +{ + return true; +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGTransformNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGClipNode *) +{ + return true; +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGClipNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGGeometryNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGGeometryNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGOpacityNode *) +{ + return true; +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGOpacityNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGInternalImageNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGInternalImageNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGPainterNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGPainterNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGInternalRectangleNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGInternalRectangleNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGGlyphNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGGlyphNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *) +{ + return true; +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *) +{ +} + +#if QT_CONFIG(quick_sprite) +bool QSGSoftwareRenderListBuilder::visit(QSGSpriteNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *) +{ + +} +#endif + +bool QSGSoftwareRenderListBuilder::visit(QSGRenderNode *node) +{ + return addRenderableNode(node); +} + +void QSGSoftwareRenderListBuilder::endVisit(QSGRenderNode *) +{ +} + +bool QSGSoftwareRenderListBuilder::addRenderableNode(QSGNode *node) +{ + auto renderableNode = m_renderer->renderableNode(node); + if (renderableNode == nullptr) { + // Not a node we can render + return false; + } + m_renderer->appendRenderableNode(renderableNode); + return true; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h new file mode 100644 index 0000000000..f5db30269f --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARERENDERLISTBUILDER_H +#define QSGSOFTWARERENDERLISTBUILDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QSGAbstractSoftwareRenderer; + +class QSGSoftwareRenderListBuilder : public QSGNodeVisitorEx +{ +public: + QSGSoftwareRenderListBuilder(QSGAbstractSoftwareRenderer *renderer); + + bool visit(QSGTransformNode *) override; + void endVisit(QSGTransformNode *) override; + bool visit(QSGClipNode *) override; + void endVisit(QSGClipNode *) override; + bool visit(QSGGeometryNode *) override; + void endVisit(QSGGeometryNode *) override; + bool visit(QSGOpacityNode *) override; + void endVisit(QSGOpacityNode *) override; + bool visit(QSGInternalImageNode *) override; + void endVisit(QSGInternalImageNode *) override; + bool visit(QSGPainterNode *) override; + void endVisit(QSGPainterNode *) override; + bool visit(QSGInternalRectangleNode *) override; + void endVisit(QSGInternalRectangleNode *) override; + bool visit(QSGGlyphNode *) override; + void endVisit(QSGGlyphNode *) override; + bool visit(QSGRootNode *) override; + void endVisit(QSGRootNode *) override; +#if QT_CONFIG(quick_sprite) + bool visit(QSGSpriteNode *) override; + void endVisit(QSGSpriteNode *) override; +#endif + bool visit(QSGRenderNode *) override; + void endVisit(QSGRenderNode *) override; + +private: + bool addRenderableNode(QSGNode *node); + + QSGAbstractSoftwareRenderer *m_renderer; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARERENDERLISTBUILDER_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp new file mode 100644 index 0000000000..19a963b403 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarerenderloop_p.h" + +#include "qsgsoftwarecontext_p.h" + +#include <QtCore/QCoreApplication> + +#include <private/qquickwindow_p.h> +#include <QElapsedTimer> +#include <private/qquickprofiler_p.h> +#include <private/qsgsoftwarerenderer_p.h> +#include <qpa/qplatformbackingstore.h> + +#include <QtGui/QBackingStore> + +QT_BEGIN_NAMESPACE + +QSGSoftwareRenderLoop::QSGSoftwareRenderLoop() +{ + sg = new QSGSoftwareContext(); + rc = sg->createRenderContext(); +} + +QSGSoftwareRenderLoop::~QSGSoftwareRenderLoop() +{ + delete rc; + delete sg; +} + +void QSGSoftwareRenderLoop::show(QQuickWindow *window) +{ + WindowData data; + data.updatePending = false; + data.grabOnly = false; + m_windows[window] = data; + + if (m_backingStores[window] == nullptr) { + m_backingStores[window] = new QBackingStore(window); + } + + maybeUpdate(window); +} + +void QSGSoftwareRenderLoop::hide(QQuickWindow *window) +{ + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->fireAboutToStop(); +} + +void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window) +{ + m_windows.remove(window); + delete m_backingStores[window]; + m_backingStores.remove(window); + hide(window); + + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->cleanupNodesOnShutdown(); + + if (m_windows.size() == 0) { + rc->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } +} + +void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) +{ + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!m_windows.contains(window)) + return; + + WindowData &data = const_cast<WindowData &>(m_windows[window]); + + //If were not in grabOnly mode, dont render a non-renderable window + if (!data.grabOnly && !cd->isRenderable()) + return; + + //Resize the backing store if necessary + if (m_backingStores[window]->size() != window->size()) { + m_backingStores[window]->resize(window->size()); + } + + // ### create QPainter and set up pointer to current window/painter + QSGSoftwareRenderContext *ctx = static_cast<QSGSoftwareRenderContext*>(cd->context); + ctx->initializeIfNeeded(); + + bool alsoSwap = data.updatePending; + data.updatePending = false; + + if (!data.grabOnly) { + cd->flushFrameSynchronousEvents(); + // 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_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled(); + if (profileFrames) + renderTimer.start(); + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); + + cd->polishItems(); + + if (profileFrames) + polishTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, + QQuickProfiler::SceneGraphRenderLoopFrame); + + emit window->afterAnimating(); + + cd->syncSceneGraph(); + + if (profileFrames) + syncTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + //Tell the renderer about the windows backing store + auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer); + if (softwareRenderer) + softwareRenderer->setBackingStore(m_backingStores[window]); + + cd->renderSceneGraph(window->size()); + + if (profileFrames) + renderTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (data.grabOnly) { + grabContent = m_backingStores[window]->handle()->toImage(); + data.grabOnly = false; + } + + if (alsoSwap && window->isVisible()) { + //Flush backingstore to window + m_backingStores[window]->flush(softwareRenderer->flushRegion()); + cd->fireFrameSwapped(); + } + + qint64 swapTime = 0; + if (profileFrames) + swapTime = renderTimer.nsecsElapsed(); + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled()) { + static QTime lastFrameTime = QTime::currentTime(); + qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP, + "Frame rendered with 'software' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d", + int(swapTime / 1000000), + int(polishTime / 1000000), + int((syncTime - polishTime) / 1000000), + int((renderTime - syncTime) / 1000000), + int((swapTime - renderTime) / 10000000), + int(lastFrameTime.msecsTo(QTime::currentTime()))); + lastFrameTime = QTime::currentTime(); + } + + // Might have been set during syncSceneGraph() + if (data.updatePending) + maybeUpdate(window); +} + +void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window) +{ + if (window->isExposed()) { + m_windows[window].updatePending = true; + renderWindow(window); + } +} + +QImage QSGSoftwareRenderLoop::grab(QQuickWindow *window) +{ + //If the window was never shown, create a new backing store + if (!m_backingStores.contains(window)) { + m_backingStores[window] = new QBackingStore(window); + // Call create on window to make sure platform window is created + window->create(); + } + + //If there is no WindowData, add one + if (!m_windows.contains(window)) { + WindowData data; + data.updatePending = false; + m_windows[window] = data; + } + + m_windows[window].grabOnly = true; + + renderWindow(window); + + QImage grabbed = grabContent; + grabbed.detach(); + grabContent = QImage(); + return grabbed; +} + + + +void QSGSoftwareRenderLoop::maybeUpdate(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows[window].updatePending = true; + window->requestUpdate(); +} + +QSurface::SurfaceType QSGSoftwareRenderLoop::windowSurfaceType() const +{ + return QSurface::RasterSurface; +} + + + +QSGContext *QSGSoftwareRenderLoop::sceneGraphContext() const +{ + return sg; +} + + +void QSGSoftwareRenderLoop::handleUpdateRequest(QQuickWindow *window) +{ + renderWindow(window); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h new file mode 100644 index 0000000000..02dcf4eefa --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARERENDERLOOP_H +#define QSGSOFTWARERENDERLOOP_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgrenderloop_p.h> + +QT_BEGIN_NAMESPACE + +class QBackingStore; + +class QSGSoftwareRenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + QSGSoftwareRenderLoop(); + ~QSGSoftwareRenderLoop(); + + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; + + void windowDestroyed(QQuickWindow *window) override; + + void renderWindow(QQuickWindow *window); + void exposureChanged(QQuickWindow *window) override; + QImage grab(QQuickWindow *window) override; + + void maybeUpdate(QQuickWindow *window) override; + void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation. + void handleUpdateRequest(QQuickWindow *) override; + + void releaseResources(QQuickWindow *) override { } + + QSurface::SurfaceType windowSurfaceType() const override; + + QAnimationDriver *animationDriver() const override { return 0; } + + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; } + + struct WindowData { + bool updatePending : 1; + bool grabOnly : 1; + }; + + QHash<QQuickWindow *, WindowData> m_windows; + QHash<QQuickWindow *, QBackingStore *> m_backingStores; + + QSGContext *sg; + QSGRenderContext *rc; + + QImage grabContent; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARERENDERLOOP_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp new file mode 100644 index 0000000000..ba7bbc2d11 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarespritenode_p.h" +#include "qsgsoftwarepixmaptexture_p.h" +#include <QtGui/QPainter> + +QT_BEGIN_NAMESPACE + +QSGSoftwareSpriteNode::QSGSoftwareSpriteNode() +{ + setMaterial((QSGMaterial*)1); + setGeometry((QSGGeometry*)1); +} + +void QSGSoftwareSpriteNode::setTexture(QSGTexture *texture) +{ + m_texture = qobject_cast<QSGSoftwarePixmapTexture*>(texture); + markDirty(DirtyMaterial); +} + +void QSGSoftwareSpriteNode::setTime(float time) +{ + if (m_time != time) { + m_time = time; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSourceA(const QPoint &source) +{ + if (m_sourceA != source) { + m_sourceA = source; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSourceB(const QPoint &source) +{ + if (m_sourceB != source) { + m_sourceB = source; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSpriteSize(const QSize &size) +{ + if (m_spriteSize != size) { + m_spriteSize = size; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSheetSize(const QSize &size) +{ + if (m_sheetSize != size) { + m_sheetSize = size; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareSpriteNode::setSize(const QSizeF &size) +{ + if (m_size != size) { + m_size = size; + markDirty(DirtyGeometry); + } +} + +void QSGSoftwareSpriteNode::setFiltering(QSGTexture::Filtering filtering) +{ + Q_UNUSED(filtering); +} + +void QSGSoftwareSpriteNode::update() +{ +} + +void QSGSoftwareSpriteNode::paint(QPainter *painter) +{ + //Get the pixmap handle from the texture + if (!m_texture) + return; + + const QPixmap &pixmap = m_texture->pixmap(); + + // XXX try to do some kind of interpolation between sourceA and sourceB using time + painter->drawPixmap(QRectF(0, 0, m_size.width(), m_size.height()), + pixmap, + QRectF(m_sourceA, m_spriteSize)); +} + +bool QSGSoftwareSpriteNode::isOpaque() const +{ + return false; +} + +QRectF QSGSoftwareSpriteNode::rect() const +{ + return QRectF(0, 0, m_size.width(), m_size.height()); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h new file mode 100644 index 0000000000..577a30c051 --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARESPRITENODE_H +#define QSGSOFTWARESPRITENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtquickglobal_p.h> + +QT_REQUIRE_CONFIG(quick_sprite); + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QSGSoftwarePixmapTexture; +class QSGSoftwareSpriteNode : public QSGSpriteNode +{ +public: + QSGSoftwareSpriteNode(); + + void setTexture(QSGTexture *texture) override; + void setTime(float time) override; + void setSourceA(const QPoint &source) override; + void setSourceB(const QPoint &source) override; + void setSpriteSize(const QSize &size) override; + void setSheetSize(const QSize &size) override; + void setSize(const QSizeF &size) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void update() override; + + void paint(QPainter *painter); + bool isOpaque() const; + QRectF rect() const; + +private: + + QSGSoftwarePixmapTexture *m_texture; + float m_time; + QPoint m_sourceA; + QPoint m_sourceB; + QSize m_spriteSize; + QSize m_sheetSize; + QSizeF m_size; + +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARESPRITENODE_H diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp new file mode 100644 index 0000000000..5d5485ed8f --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp @@ -0,0 +1,991 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgsoftwarethreadedrenderloop_p.h" +#include "qsgsoftwarecontext_p.h" +#include "qsgsoftwarerenderer_p.h" + +#include <private/qsgrenderer_p.h> +#include <private/qquickwindow_p.h> +#include <private/qquickprofiler_p.h> +#include <private/qquickanimatorcontroller_p.h> +#include <private/qquickprofiler_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmldebugconnector_p.h> + +#include <qpa/qplatformbackingstore.h> + +#include <QtCore/QQueue> +#include <QtCore/QElapsedTimer> +#include <QtCore/QThread> +#include <QtCore/QMutex> +#include <QtCore/QWaitCondition> +#include <QtGui/QGuiApplication> +#include <QtGui/QBackingStore> +#include <QtQuick/QQuickWindow> + +QT_BEGIN_NAMESPACE + +// Passed from the RL to the RT when a window is removed obscured and should be +// removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to RT when GUI has been locked, waiting for sync. +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); + +// Passed by the RT to itself to trigger another render pass. This is typically +// a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); + +// Passed by the RL to the RT to maybe release resource if no windows are +// rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); + +// Passed by the window when there is a render job to run. +const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); + +class QSGSoftwareWindowEvent : public QEvent +{ +public: + QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy) + : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { } + bool destroying; +}; + +class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force) + : QSGSoftwareWindowEvent(c, WM_RequestSync) + , size(c->size()) + , dpr(c->effectiveDevicePixelRatio()) + , syncInExpose(inExpose) + , forceRenderPass(force) { } + QSize size; + float dpr; + bool syncInExpose; + bool forceRenderPass; +}; + +class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result) + : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { } + QImage *image; +}; + +class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent +{ +public: + QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob) + : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { } + ~QSGSoftwareJobEvent() { delete job; } + QRunnable *job; +}; + +class QSGSoftwareEventQueue : public QQueue<QEvent *> +{ +public: + void addEvent(QEvent *e) { + mutex.lock(); + enqueue(e); + if (waiting) + condition.wakeOne(); + mutex.unlock(); + } + + QEvent *takeEvent(bool wait) { + mutex.lock(); + if (isEmpty() && wait) { + waiting = true; + condition.wait(&mutex); + waiting = false; + } + QEvent *e = dequeue(); + mutex.unlock(); + return e; + } + + bool hasMoreEvents() { + mutex.lock(); + bool has = !isEmpty(); + mutex.unlock(); + return has; + } + +private: + QMutex mutex; + QWaitCondition condition; + bool waiting = false; +}; + +static inline int qsgrl_animation_interval() +{ + const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0; + return refreshRate < 1 ? 16 : int(1000 / refreshRate); +} + +class QSGSoftwareRenderThread : public QThread +{ + Q_OBJECT +public: + QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext) + : renderLoop(rl) + { + rc = static_cast<QSGSoftwareRenderContext *>(renderContext); + vsyncDelta = qsgrl_animation_interval(); + } + + ~QSGSoftwareRenderThread() + { + delete rc; + } + + bool event(QEvent *e); + void run(); + + void syncAndRender(); + void sync(bool inExpose); + + void requestRepaint() + { + if (sleeping) + stopEventProcessing = true; + if (exposedWindow) + pendingUpdate |= RepaintRequest; + } + + void processEventsAndWaitForMore(); + void processEvents(); + void postEvent(QEvent *e); + + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02, + ExposeRequest = 0x04 | RepaintRequest | SyncRequest + }; + + QSGSoftwareThreadedRenderLoop *renderLoop; + QSGSoftwareRenderContext *rc; + QAnimationDriver *rtAnim = nullptr; + volatile bool active = false; + uint pendingUpdate = 0; + bool sleeping = false; + bool syncResultedInChanges = false; + float vsyncDelta; + QMutex mutex; + QWaitCondition waitCondition; + QQuickWindow *exposedWindow = nullptr; + QBackingStore *backingStore = nullptr; + bool stopEventProcessing = false; + QSGSoftwareEventQueue eventQueue; + QElapsedTimer renderThrottleTimer; + qint64 syncTime; + qint64 renderTime; + qint64 sinceLastTime; + +public slots: + void onSceneGraphChanged() { + syncResultedInChanges = true; + } +}; + +bool QSGSoftwareRenderThread::event(QEvent *e) +{ + switch ((int)e->type()) { + + case WM_Obscure: + Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGSoftwareWindowEvent *>(e)->window); + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow; + mutex.lock(); + if (exposedWindow) { + QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop(); + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed"); + exposedWindow = nullptr; + delete backingStore; + backingStore = nullptr; + } + waitCondition.wakeOne(); + mutex.unlock(); + return true; + + case WM_RequestSync: { + QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e); + if (sleeping) + stopEventProcessing = true; + exposedWindow = wme->window; + if (backingStore == nullptr) + backingStore = new QBackingStore(exposedWindow); + if (backingStore->size() != exposedWindow->size()) + backingStore->resize(exposedWindow->size()); + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow; + pendingUpdate |= SyncRequest; + if (wme->syncInExpose) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose"); + pendingUpdate |= ExposeRequest; + } + if (wme->forceRenderPass) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless"); + pendingUpdate |= RepaintRequest; + } + return true; + } + + case WM_TryRelease: { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease"); + mutex.lock(); + renderLoop->lockedForSync = true; + QSGSoftwareTryReleaseEvent *wme = static_cast<QSGSoftwareTryReleaseEvent *>(e); + // Only when no windows are exposed anymore or we are shutting down. + if (!exposedWindow || wme->destroying) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc"); + if (wme->window) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + if (wme->destroying) { + // Bye bye nodes... + wd->cleanupNodesOnShutdown(); + } + rc->invalidate(); + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + if (wme->destroying) + delete wd->animationController; + } + if (wme->destroying) + active = false; + if (sleeping) + stopEventProcessing = true; + } else { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active"); + } + waitCondition.wakeOne(); + renderLoop->lockedForSync = false; + mutex.unlock(); + return true; + } + + case WM_Grab: { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab"); + QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e); + Q_ASSERT(wme->window); + Q_ASSERT(wme->window == exposedWindow || !exposedWindow); + mutex.lock(); + if (wme->window) { + // Grabbing is generally done by rendering a frame and reading the + // color buffer contents back, without presenting, and then + // creating a QImage from the returned data. It is terribly + // inefficient since it involves a full blocking wait for the GPU. + // However, our hands are tied by the existing, synchronous APIs of + // QQuickWindow and such. + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); + auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer); + if (softwareRenderer) + softwareRenderer->setBackingStore(backingStore); + rc->initialize(nullptr); + wd->syncSceneGraph(); + wd->renderSceneGraph(wme->window->size()); + *wme->image = backingStore->handle()->toImage(); + } + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result"); + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + case WM_PostJob: { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob"); + QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e); + Q_ASSERT(wme->window == exposedWindow); + if (exposedWindow) { + wme->job->run(); + delete wme->job; + wme->job = nullptr; + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done"); + } + return true; + } + + case WM_RequestRepaint: + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint"); + // When GUI posts this event, it is followed by a polishAndSync, so we + // must not exit the event loop yet. + pendingUpdate |= RepaintRequest; + break; + + default: + break; + } + + return QThread::event(e); +} + +void QSGSoftwareRenderThread::postEvent(QEvent *e) +{ + eventQueue.addEvent(e); +} + +void QSGSoftwareRenderThread::processEvents() +{ + while (eventQueue.hasMoreEvents()) { + QEvent *e = eventQueue.takeEvent(false); + event(e); + delete e; + } +} + +void QSGSoftwareRenderThread::processEventsAndWaitForMore() +{ + stopEventProcessing = false; + while (!stopEventProcessing) { + QEvent *e = eventQueue.takeEvent(true); + event(e); + delete e; + } +} + +void QSGSoftwareRenderThread::run() +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()"); + + rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr); + rtAnim->install(); + + if (QQmlDebugConnector::service<QQmlProfilerService>()) + QQuickProfiler::registerAnimationCallback(); + + renderThrottleTimer.start(); + + while (active) { + if (exposedWindow) + syncAndRender(); + + processEvents(); + QCoreApplication::processEvents(); + + if (pendingUpdate == 0 || !exposedWindow) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep"); + sleeping = true; + processEventsAndWaitForMore(); + sleeping = false; + } + } + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting"); + + delete rtAnim; + rtAnim = nullptr; + + rc->moveToThread(renderLoop->thread()); + moveToThread(renderLoop->thread()); +} + +void QSGSoftwareRenderThread::sync(bool inExpose) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync"); + + mutex.lock(); + Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked"); + + if (exposedWindow) { + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + bool hadRenderer = wd->renderer != nullptr; + // If the scene graph was touched since the last sync() make sure it sends the + // changed signal. + if (wd->renderer) + wd->renderer->clearChangedFlag(); + + rc->initialize(nullptr); + wd->syncSceneGraph(); + + if (!hadRenderer && wd->renderer) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer"); + syncResultedInChanges = true; + connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this, + &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection); + } + + // Process deferred deletes now, directly after the sync as deleteLater + // on the GUI must now also have resulted in SG changes and the delete + // is a safe operation. + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + + if (!inExpose) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui"); + waitCondition.wakeOne(); + mutex.unlock(); + } +} + +void QSGSoftwareRenderThread::syncAndRender() +{ + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); + + QElapsedTimer waitTimer; + waitTimer.start(); + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()"); + + syncResultedInChanges = false; + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow); + + const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage; + const bool syncRequested = pendingUpdate & SyncRequest; + const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest; + pendingUpdate = 0; + + if (syncRequested) + sync(exposeRequested); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (!syncResultedInChanges && !repaintRequested) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted"); + int waitTime = vsyncDelta - (int) waitTimer.elapsed(); + if (waitTime > 0) + msleep(waitTime); + return; + } + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started"); + + if (rtAnim->isRunning()) { + wd->animationController->lock(); + rtAnim->advance(); + wd->animationController->unlock(); + } + + bool canRender = wd->renderer != nullptr; + + if (canRender) { + auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer); + if (softwareRenderer) + softwareRenderer->setBackingStore(backingStore); + wd->renderSceneGraph(exposedWindow->size()); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame); + + if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap())) + backingStore->flush(softwareRenderer->flushRegion()); + + // Since there is no V-Sync with QBackingStore, throttle rendering the refresh + // rate of the current screen the window is on. + int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed(); + if (blockTime > 0) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - blocking for " << blockTime << "ms"; + msleep(blockTime); + } + renderThrottleTimer.restart(); + + wd->fireFrameSwapped(); + } else { + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1); + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render"); + } + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done"); + + if (exposeRequested) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose"); + waitCondition.wakeOne(); + mutex.unlock(); + } + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); +} + +template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window) +{ + for (const T &t : list) { + if (t.window == window) + return const_cast<T *>(&t); + } + return nullptr; +} + + +QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop() +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor"); + m_sg = new QSGSoftwareContext; + m_anim = m_sg->createAnimationDriver(this); + connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted); + connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped); + m_anim->install(); +} + +QSGSoftwareThreadedRenderLoop::~QSGSoftwareThreadedRenderLoop() +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor"); + delete m_sg; +} + +void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window; +} + +void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window; + + if (window->isExposed()) + handleObscurity(windowFor(m_windows, window)); + + releaseResources(window); +} + +void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window) +{ + if (!window->isExposed() || window->size().isEmpty()) + return; + + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size(); +} + +void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window; + + WindowData *w = windowFor(m_windows, window); + if (!w) + return; + + handleObscurity(w); + handleResourceRelease(w, true); + + QSGSoftwareRenderThread *thread = w->thread; + while (thread->isRunning()) + QThread::yieldCurrentThread(); + + Q_ASSERT(thread->thread() == QThread::currentThread()); + delete thread; + + for (int i = 0; i < m_windows.size(); ++i) { + if (m_windows.at(i).window == window) { + m_windows.removeAt(i); + break; + } + } +} + +void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window; + + if (window->isExposed()) { + handleExposure(window); + } else { + WindowData *w = windowFor(m_windows, window); + if (w) + handleObscurity(w); + } +} + +QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window; + + WindowData *w = windowFor(m_windows, window); + // Have to support invisible (but created()'ed) windows as well. + // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible. + const bool tempExpose = !w; + if (tempExpose) { + handleExposure(window); + w = windowFor(m_windows, window); + Q_ASSERT(w); + } + + if (!w->thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + QImage result; + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result)); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + + result.setDevicePixelRatio(window->effectiveDevicePixelRatio()); + + if (tempExpose) + handleObscurity(w); + + return result; +} + +void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window) +{ + WindowData *w = windowFor(m_windows, window); + if (!w) + return; + + if (w->thread == QThread::currentThread()) { + w->thread->requestRepaint(); + return; + } + + // We set forceRenderPass because we want to make sure the QQuickWindow + // actually does a full render pass after the next sync. + w->forceRenderPass = true; + scheduleUpdate(w); +} + +void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + WindowData *w = windowFor(m_windows, window); + if (w) + scheduleUpdate(w); +} + +void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window; + + WindowData *w = windowFor(m_windows, window); + if (w) + polishAndSync(w, false); +} + +QAnimationDriver *QSGSoftwareThreadedRenderLoop::animationDriver() const +{ + return m_anim; +} + +QSGContext *QSGSoftwareThreadedRenderLoop::sceneGraphContext() const +{ + return m_sg; +} + +QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const +{ + return m_sg->createRenderContext(); +} + +void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window; + + WindowData *w = windowFor(m_windows, window); + if (w) + handleResourceRelease(w, false); +} + +void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job) +{ + WindowData *w = windowFor(m_windows, window); + if (w && w->thread && w->thread->exposedWindow) + w->thread->postEvent(new QSGSoftwareJobEvent(window, job)); + else + delete job; +} + +QSurface::SurfaceType QSGSoftwareThreadedRenderLoop::windowSurfaceType() const +{ + return QSurface::RasterSurface; +} + +bool QSGSoftwareThreadedRenderLoop::interleaveIncubation() const +{ + bool somethingVisible = false; + for (const WindowData &w : m_windows) { + if (w.window->isVisible() && w.window->isExposed()) { + somethingVisible = true; + break; + } + } + return somethingVisible && m_anim->isRunning(); +} + +int QSGSoftwareThreadedRenderLoop::flags() const +{ + return SupportsGrabWithoutExpose; +} + +bool QSGSoftwareThreadedRenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast<QTimerEvent *>(e); + if (te->timerId() == animationTimer) { + m_anim->advance(); + emit timeToIncubate(); + return true; + } + } + + return QObject::event(e); +} + +void QSGSoftwareThreadedRenderLoop::onAnimationStarted() +{ + startOrStopAnimationTimer(); + + for (const WindowData &w : qAsConst(m_windows)) + w.window->requestUpdate(); +} + +void QSGSoftwareThreadedRenderLoop::onAnimationStopped() +{ + startOrStopAnimationTimer(); +} + +void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer() +{ + int exposedWindowCount = 0; + const WindowData *exposed = nullptr; + + for (int i = 0; i < m_windows.size(); ++i) { + const WindowData &w(m_windows[i]); + if (w.window->isVisible() && w.window->isExposed()) { + ++exposedWindowCount; + exposed = &w; + } + } + + if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) { + killTimer(animationTimer); + animationTimer = 0; + // If animations are running, make sure we keep on animating + if (m_anim->isRunning()) + exposed->window->requestUpdate(); + } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) { + animationTimer = startTimer(qsgrl_animation_interval()); + } +} + +void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window; + + WindowData *w = windowFor(m_windows, window); + if (!w) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list"); + WindowData win; + win.window = window; + QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership + win.thread = new QSGSoftwareRenderThread(this, rc); + win.updateDuringSync = false; + win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt + m_windows.append(win); + w = &m_windows.last(); + } + + // set this early as we'll be rendering shortly anyway and this avoids + // special casing exposure in polishAndSync. + w->thread->exposedWindow = window; + + if (w->window->size().isEmpty() + || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) { +#ifndef QT_NO_DEBUG + qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window " + << w->window << " with invalid geometry: " << w->window->geometry() + << " on " << w->window->screen(); +#endif + } + + if (!w->window->handle()) + w->window->create(); + + // Start render thread if it is not running + if (!w->thread->isRunning()) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread"); + // Push a few things to the render thread. + QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; + if (controller->thread() != w->thread) + controller->moveToThread(w->thread); + if (w->thread->thread() == QThread::currentThread()) { + w->thread->rc->moveToThread(w->thread); + w->thread->moveToThread(w->thread); + } + + w->thread->active = true; + w->thread->start(); + + if (!w->thread->isRunning()) + qFatal("Render thread failed to start, aborting application."); + } + + polishAndSync(w, true); + + startOrStopAnimationTimer(); +} + +void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window; + + if (w->thread->isRunning()) { + w->thread->mutex.lock(); + w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure)); + w->thread->waitCondition.wait(&w->thread->mutex); + w->thread->mutex.unlock(); + } + + startOrStopAnimationTimer(); +} + +void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w) +{ + if (!QCoreApplication::instance()) + return; + + if (!w || !w->thread->isRunning()) + return; + + QThread *current = QThread::currentThread(); + if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) { + qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; + return; + } + + if (current == w->thread) { + w->updateDuringSync = true; + return; + } + + w->window->requestUpdate(); +} + +void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window; + + w->thread->mutex.lock(); + if (w->thread->isRunning() && w->thread->active) { + QQuickWindow *window = w->window; + + // Note that window->handle() is typically null by this time because + // the platform window is already destroyed. This should not be a + // problem for the D3D cleanup. + + w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying)); + w->thread->waitCondition.wait(&w->thread->mutex); + + // Avoid a shutdown race condition. + // If SG is invalidated and 'active' becomes false, the thread's run() + // method will exit. handleExposure() relies on QThread::isRunning() (because it + // potentially needs to start the thread again) and our mutex cannot be used to + // track the thread stopping, so we wait a few nanoseconds extra so the thread + // can exit properly. + if (!w->thread->active) + w->thread->wait(); + } + w->thread->mutex.unlock(); +} + +void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose) +{ + qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window; + + QQuickWindow *window = w->window; + if (!w->thread || !w->thread->exposedWindow) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort"); + return; + } + + // Flush pending touch events. + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); + // The delivery of the event might have caused the window to stop rendering + w = windowFor(m_windows, window); + if (!w || !w->thread || !w->thread->exposedWindow) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort"); + return; + } + + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + wd->polishItems(); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + w->updateDuringSync = false; + + emit window->afterAnimating(); + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync"); + w->thread->mutex.lock(); + lockedForSync = true; + w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass)); + w->forceRenderPass = false; + + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync"); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + w->thread->waitCondition.wait(&w->thread->mutex); + lockedForSync = false; + w->thread->mutex.unlock(); + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync"); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync); + + if (!animationTimer && m_anim->isRunning()) { + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations"); + m_anim->advance(); + // We need to trigger another sync to keep animations running... + w->window->requestUpdate(); + emit timeToIncubate(); + } else if (w->updateDuringSync) { + w->window->requestUpdate(); + } + + Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync); +} + +#include "qsgsoftwarethreadedrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h new file mode 100644 index 0000000000..99993d651c --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGSOFTWARETHREADEDRENDERLOOP_H +#define QSGSOFTWARETHREADEDRENDERLOOP_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgrenderloop_p.h> + +QT_BEGIN_NAMESPACE + +class QSGSoftwareRenderThread; +class QSGSoftwareContext; + +class QSGSoftwareThreadedRenderLoop : public QSGRenderLoop +{ + Q_OBJECT +public: + QSGSoftwareThreadedRenderLoop(); + ~QSGSoftwareThreadedRenderLoop(); + + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; + void resize(QQuickWindow *window) override; + void windowDestroyed(QQuickWindow *window) override; + void exposureChanged(QQuickWindow *window) override; + QImage grab(QQuickWindow *window) override; + void update(QQuickWindow *window) override; + void maybeUpdate(QQuickWindow *window) override; + void handleUpdateRequest(QQuickWindow *window) override; + QAnimationDriver *animationDriver() const override; + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override; + void releaseResources(QQuickWindow *window) override; + void postJob(QQuickWindow *window, QRunnable *job) override; + QSurface::SurfaceType windowSurfaceType() const override; + bool interleaveIncubation() const override; + int flags() const override; + + bool event(QEvent *e) override; + +public Q_SLOTS: + void onAnimationStarted(); + void onAnimationStopped(); + +private: + struct WindowData { + QQuickWindow *window; + QSGSoftwareRenderThread *thread; + uint updateDuringSync : 1; + uint forceRenderPass : 1; + }; + + void startOrStopAnimationTimer(); + void handleExposure(QQuickWindow *window); + void handleObscurity(WindowData *w); + void scheduleUpdate(WindowData *w); + void handleResourceRelease(WindowData *w, bool destroying); + void polishAndSync(WindowData *w, bool inExpose); + + QSGSoftwareContext *m_sg; + QAnimationDriver *m_anim; + int animationTimer = 0; + bool lockedForSync = false; + QVector<WindowData> m_windows; + + friend class QSGSoftwareRenderThread; +}; + +QT_END_NAMESPACE + +#endif // QSGSOFTWARETHREADEDRENDERLOOP_H diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri new file mode 100644 index 0000000000..de5f01cdee --- /dev/null +++ b/src/quick/scenegraph/adaptations/software/software.pri @@ -0,0 +1,48 @@ +QT += gui-private core-private qml-private + +#DEFINES += QTQUICK2D_DEBUG_FLUSH + +SOURCES += \ + $$PWD/qsgsoftwarecontext.cpp \ + $$PWD/qsgabstractsoftwarerenderer.cpp \ + $$PWD/qsgsoftwareglyphnode.cpp \ + $$PWD/qsgsoftwareinternalimagenode.cpp \ + $$PWD/qsgsoftwarepublicnodes.cpp \ + $$PWD/qsgsoftwarepainternode.cpp \ + $$PWD/qsgsoftwareinternalrectanglenode.cpp \ + $$PWD/qsgsoftwarepixmaprenderer.cpp \ + $$PWD/qsgsoftwarepixmaptexture.cpp \ + $$PWD/qsgsoftwarerenderablenode.cpp \ + $$PWD/qsgsoftwarerenderablenodeupdater.cpp \ + $$PWD/qsgsoftwarerenderer.cpp \ + $$PWD/qsgsoftwarerenderlistbuilder.cpp \ + $$PWD/qsgsoftwarerenderloop.cpp \ + $$PWD/qsgsoftwarelayer.cpp \ + $$PWD/qsgsoftwareadaptation.cpp \ + $$PWD/qsgsoftwarethreadedrenderloop.cpp + +HEADERS += \ + $$PWD/qsgsoftwarecontext_p.h \ + $$PWD/qsgabstractsoftwarerenderer_p.h \ + $$PWD/qsgsoftwareglyphnode_p.h \ + $$PWD/qsgsoftwareinternalimagenode_p.h \ + $$PWD/qsgsoftwarepublicnodes_p.h \ + $$PWD/qsgsoftwarepainternode_p.h \ + $$PWD/qsgsoftwarepixmaprenderer_p.h \ + $$PWD/qsgsoftwarepixmaptexture_p.h \ + $$PWD/qsgsoftwareinternalrectanglenode_p.h \ + $$PWD/qsgsoftwarerenderablenode_p.h \ + $$PWD/qsgsoftwarerenderablenodeupdater_p.h \ + $$PWD/qsgsoftwarerenderer_p.h \ + $$PWD/qsgsoftwarerenderlistbuilder_p.h \ + $$PWD/qsgsoftwarerenderloop_p.h \ + $$PWD/qsgsoftwarelayer_p.h \ + $$PWD/qsgsoftwareadaptation_p.h \ + $$PWD/qsgsoftwarethreadedrenderloop_p.h + +qtConfig(quick-sprite) { + SOURCES += \ + $$PWD/qsgsoftwarespritenode.cpp + HEADERS += \ + $$PWD/qsgsoftwarespritenode_p.h +} diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h index d0a22c8c10..eb9e7cea7c 100644 --- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h +++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h @@ -42,6 +42,10 @@ #include <QtQuick/qsgnode.h> +#ifndef GLuint +#define GLuint uint +#endif + QT_BEGIN_NAMESPACE class QSGAbstractRendererPrivate; diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index d91004fbee..49bbbf0ba8 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -154,8 +154,8 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material) p->bindAttributeLocation(attr[i], i); } p->bindAttributeLocation("_qt_order", i); - context->compile(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), 0); - context->initialize(s); + context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), 0); + context->initializeShader(s); if (!p->isLinked()) return 0; @@ -188,8 +188,8 @@ ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *mate Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame); QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader()); - context->compile(s, material); - context->initialize(s); + context->compileShader(s, material); + context->initializeShader(s); shader = new Shader(); shader->program = s; @@ -356,17 +356,17 @@ void Updater::visitClipNode(Node *n) if (m_roots.last() && m_added > 0) renderer->registerBatchRoot(n, m_roots.last()); - cn->m_clip_list = m_current_clip; + cn->setRendererClipList(m_current_clip); m_current_clip = cn; m_roots << n; m_rootMatrices.add(m_rootMatrices.last() * *m_combined_matrix_stack.last()); extra->matrix = m_rootMatrices.last(); - cn->m_matrix = &extra->matrix; + cn->setRendererMatrix(&extra->matrix); m_combined_matrix_stack << &m_identityMatrix; SHADOWNODE_TRAVERSE(n) visitNode(child); - m_current_clip = cn->m_clip_list; + m_current_clip = cn->clipList(); m_rootMatrices.pop_back(); m_combined_matrix_stack.pop_back(); m_roots.pop_back(); @@ -459,8 +459,8 @@ void Updater::visitGeometryNode(Node *n) { QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); - gn->m_matrix = m_combined_matrix_stack.last(); - gn->m_clip_list = m_current_clip; + gn->setRendererMatrix(m_combined_matrix_stack.last()); + gn->setRendererClipList(m_current_clip); gn->setInheritedOpacity(m_opacity_stack.last()); if (m_added) { @@ -746,8 +746,9 @@ static int qsg_countNodesInBatches(const QDataBuffer<Batch *> &batches) return sum; } -Renderer::Renderer(QSGRenderContext *ctx) +Renderer::Renderer(QSGDefaultRenderContext *ctx) : QSGRenderer(ctx) + , m_context(ctx) , m_opaqueRenderList(64) , m_alphaRenderList(64) , m_nextRenderOrder(0) @@ -807,7 +808,7 @@ Renderer::Renderer(QSGRenderContext *ctx) // If rendering with an OpenGL Core profile context, we need to create a VAO // to hold our vertex specification state. - if (context()->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) { + if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) { m_vao = new QOpenGLVertexArrayObject(this); m_vao->create(); } @@ -845,7 +846,7 @@ Renderer::~Renderer() for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this); } - foreach (Node *n, m_nodes.values()) + for (Node *n : qAsConst(m_nodes)) m_nodeAllocator.release(n); // Remaining elements... @@ -1032,11 +1033,13 @@ void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent) m_rebuild |= FullRebuild; } else if (node->type() == QSGNode::RenderNodeType) { - RenderNodeElement *e = new RenderNodeElement(static_cast<QSGRenderNode *>(node)); + QSGRenderNode *rn = static_cast<QSGRenderNode *>(node); + RenderNodeElement *e = new RenderNodeElement(rn); snode->data = e; - Q_ASSERT(!m_renderNodeElements.contains(static_cast<QSGRenderNode *>(node))); + Q_ASSERT(!m_renderNodeElements.contains(rn)); m_renderNodeElements.insert(e->renderNode, e); - m_useDepthBuffer = false; + if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering)) + m_useDepthBuffer = false; m_rebuild |= FullRebuild; } @@ -1101,7 +1104,7 @@ void Renderer::nodeWasRemoved(Node *node) if (m_renderNodeElements.isEmpty()) { static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); - m_useDepthBuffer = useDepth && context()->openglContext()->format().depthBufferSize() > 0; + m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0; } } } @@ -2760,6 +2763,22 @@ void Renderer::render() m_vao->release(); } +struct RenderNodeState : public QSGRenderNode::RenderState +{ + const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; } + QRect scissorRect() const override { return m_scissorRect; } + bool scissorEnabled() const override { return m_scissorEnabled; } + int stencilValue() const override { return m_stencilValue; } + bool stencilEnabled() const override { return m_stencilEnabled; } + const QRegion *clipRegion() const override { return nullptr; } + + const QMatrix4x4 *m_projectionMatrix; + QRect m_scissorRect; + int m_stencilValue; + bool m_scissorEnabled; + bool m_stencilEnabled; +}; + void Renderer::renderRenderNode(Batch *batch) { if (Q_UNLIKELY(debug_render())) @@ -2771,24 +2790,25 @@ void Renderer::renderRenderNode(Batch *batch) setActiveShader(0, 0); QSGNode *clip = e->renderNode->parent(); - e->renderNode->m_clip_list = 0; + QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode); + rd->m_clip_list = 0; while (clip != rootNode()) { if (clip->type() == QSGNode::ClipNodeType) { - e->renderNode->m_clip_list = static_cast<QSGClipNode *>(clip); + rd->m_clip_list = static_cast<QSGClipNode *>(clip); break; } clip = clip->parent(); } - updateClip(e->renderNode->m_clip_list, batch); + updateClip(rd->m_clip_list, batch); - QSGRenderNode::RenderState state; + RenderNodeState state; QMatrix4x4 pm = projectionMatrix(); - state.projectionMatrix = ± - state.scissorEnabled = m_currentClipType & ScissorClip; - state.stencilEnabled = m_currentClipType & StencilClip; - state.scissorRect = m_currentScissorRect; - state.stencilValue = m_currentStencilValue; + state.m_projectionMatrix = ± + state.m_scissorEnabled = m_currentClipType & ScissorClip; + state.m_stencilEnabled = m_currentClipType & StencilClip; + state.m_scissorRect = m_currentScissorRect; + state.m_stencilValue = m_currentStencilValue; QSGNode *xform = e->renderNode->parent(); QMatrix4x4 matrix; @@ -2804,13 +2824,13 @@ void Renderer::renderRenderNode(Batch *batch) } xform = xform->parent(); } - e->renderNode->m_matrix = &matrix; + rd->m_matrix = &matrix; QSGNode *opacity = e->renderNode->parent(); - e->renderNode->m_opacity = 1.0; + rd->m_opacity = 1.0; while (opacity != rootNode()) { if (opacity->type() == QSGNode::OpacityNodeType) { - e->renderNode->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity(); + rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity(); break; } opacity = opacity->parent(); @@ -2822,12 +2842,17 @@ void Renderer::renderRenderNode(Batch *batch) glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - e->renderNode->render(state); + QSGRenderNode::StateFlags changes = e->renderNode->changedStates(); - e->renderNode->m_matrix = 0; - e->renderNode->m_clip_list = 0; + GLuint prevFbo = 0; + if (changes & QSGRenderNode::RenderTargetState) + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); + + e->renderNode->render(&state); + + rd->m_matrix = 0; + rd->m_clip_list = 0; - QSGRenderNode::StateFlags changes = e->renderNode->changedStates(); if (changes & QSGRenderNode::ViewportState) { QRect r = viewportRect(); glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height()); @@ -2861,6 +2886,8 @@ void Renderer::renderRenderNode(Batch *batch) glDisable(GL_CULL_FACE); } + if (changes & QSGRenderNode::RenderTargetState) + glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); } class VisualizeShader : public QOpenGLShaderProgram diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 8bf4a13af6..01e517e65b 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -54,13 +54,15 @@ // #include <private/qsgrenderer_p.h> +#include <private/qsgdefaultrendercontext_p.h> #include <private/qsgnodeupdater_p.h> -#include <private/qdatabuffer_p.h> - #include <private/qsgrendernode_p.h> +#include <private/qdatabuffer_p.h> #include <QtCore/QBitArray> +#include <QtGui/QOpenGLFunctions> + QT_BEGIN_NAMESPACE class QOpenGLVertexArrayObject; @@ -527,7 +529,7 @@ public: float lastOpacity; }; - ShaderManager(QSGRenderContext *ctx) : visualizeProgram(0), blitProgram(0), context(ctx) { } + ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(0), blitProgram(0), context(ctx) { } ~ShaderManager() { qDeleteAll(rewrittenShaders); qDeleteAll(stockShaders); @@ -547,13 +549,13 @@ private: QHash<QSGMaterialType *, Shader *> stockShaders; QOpenGLShaderProgram *blitProgram; - QSGRenderContext *context; + QSGDefaultRenderContext *context; }; class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions { public: - Renderer(QSGRenderContext *); + Renderer(QSGDefaultRenderContext *); ~Renderer(); enum VisualizeMode { @@ -637,6 +639,7 @@ private: void visualizeDrawGeometry(const QSGGeometry *g); void setCustomRenderMode(const QByteArray &mode) Q_DECL_OVERRIDE; + QSGDefaultRenderContext *m_context; QSet<Node *> m_taggedRoots; QDataBuffer<Element *> m_opaqueRenderList; QDataBuffer<Element *> m_alphaRenderList; diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp index 5012f6a31b..b43a2bc2ba 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.cpp +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -39,10 +39,11 @@ #include "qsggeometry.h" #include "qsggeometry_p.h" - -#include <qopenglcontext.h> -#include <qopenglfunctions.h> -#include <private/qopenglextensions_p.h> +#ifndef QT_NO_OPENGL +# include <qopenglcontext.h> +# include <qopenglfunctions.h> +# include <private/qopenglextensions_p.h> +#endif #ifdef Q_OS_QNX #include <malloc.h> @@ -53,10 +54,21 @@ QT_BEGIN_NAMESPACE QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tupleSize, int primitiveType, bool isPrimitive) { - Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, 0 }; + Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, UnknownAttribute, 0 }; return a; } +QSGGeometry::Attribute QSGGeometry::Attribute::createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType) +{ + Attribute a; + a.position = pos; + a.tupleSize = tupleSize; + a.type = primitiveType; + a.isVertexCoordinate = attributeType == PositionAttribute; + a.attributeType = attributeType; + a.reserved = 0; + return a; +} /*! Convenience function which returns attributes to be used for 2D solid @@ -66,7 +78,7 @@ QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tu const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() { static Attribute data[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true) + Attribute::createWithAttributeType(0, 2, FloatType, PositionAttribute) }; static AttributeSet attrs = { 1, sizeof(float) * 2, data }; return attrs; @@ -79,8 +91,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D() const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D() { static Attribute data[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), - QSGGeometry::Attribute::create(1, 2, GL_FLOAT) + Attribute::createWithAttributeType(0, 2, FloatType, PositionAttribute), + Attribute::createWithAttributeType(1, 2, FloatType, TexCoordAttribute) }; static AttributeSet attrs = { 2, sizeof(float) * 4, data }; return attrs; @@ -93,8 +105,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D( const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() { static Attribute data[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), - QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE) + Attribute::createWithAttributeType(0, 2, FloatType, PositionAttribute), + Attribute::createWithAttributeType(1, 4, UnsignedByteType, ColorAttribute) }; static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data }; return attrs; @@ -122,12 +134,31 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() \fn QSGGeometry::Attribute QSGGeometry::Attribute::create(int pos, int tupleSize, int primitiveType, bool isPosition) Creates a new QSGGeometry::Attribute for attribute register \a pos with \a - tupleSize. The \a primitiveType can be any of the supported OpenGL types, - such as \c GL_FLOAT or \c GL_UNSIGNED_BYTE. + tupleSize. The \a primitiveType can be any of the supported types from + QSGGeometry::Type, such as QSGGeometry::FloatType or + QSGGeometry::UnsignedByteType. - If the attribute describes the position for the vertex, the \a isPosition hint - should be set to \c true. The scene graph renderer may use this information - to perform optimizations. + If the attribute describes the position for the vertex, the \a isPosition + hint should be set to \c true. The scene graph renderer may use this + information to perform optimizations. + + \note Scene graph backends for APIs other than OpenGL may require an + accurate description of attributes' usage, and therefore it is recommended + to use createWithAttributeType() instead. + + Use the create function to construct the attribute, rather than an + initialization list, to ensure that all fields are initialized. + */ + +/*! + \fn QSGGeometry::Attribute QSGGeometry::Attribute::createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType) + + Creates a new QSGGeometry::Attribute for attribute register \a pos with \a + tupleSize. The \a primitiveType can be any of the supported types from + QSGGeometry::Type, such as QSGGeometry::FloatType or + QSGGeometry::UnsignedByteType. + + \a attributeType describes the intended use of the attribute. Use the create function to construct the attribute, rather than an initialization list, to ensure that all fields are initialized. @@ -206,9 +237,9 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() The QSGGeometry class stores the geometry of the primitives rendered with the scene graph. It contains vertex data and optionally index data. The mode used to draw the geometry is - specified with setDrawingMode(), which maps directly to the OpenGL + specified with setDrawingMode(), which maps directly to the graphics API's drawing mode, such as \c GL_TRIANGLE_STRIP, \c GL_TRIANGLES, or - \c GL_POINTS. + \c GL_POINTS in case of OpenGL. Vertices can be as simple as points defined by x and y values or can be more complex where each vertex contains a normal, texture @@ -394,7 +425,7 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, int vertexCount, int indexCount, int indexType) - : m_drawing_mode(GL_TRIANGLE_STRIP) + : m_drawing_mode(DrawTriangleStrip) , m_vertex_count(0) , m_index_count(0) , m_index_type(indexType) @@ -410,21 +441,20 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, Q_UNUSED(m_reserved_bits); Q_ASSERT(m_attributes.count > 0); Q_ASSERT(m_attributes.stride > 0); - +#ifndef QT_NO_OPENGL Q_ASSERT_X(indexType != GL_UNSIGNED_INT || static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions()) ->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint), "QSGGeometry::QSGGeometry", "GL_UNSIGNED_INT is not supported, geometry will not render" ); - - if (indexType != GL_UNSIGNED_BYTE - && indexType != GL_UNSIGNED_SHORT - && indexType != GL_UNSIGNED_INT) { +#endif + if (indexType != UnsignedByteType + && indexType != UnsignedShortType + && indexType != UnsignedIntType) { qFatal("QSGGeometry: Unsupported index type, %x.\n", indexType); } - // Because allocate reads m_vertex_count, m_index_count and m_owns_data, these // need to be set before calling allocate... allocate(vertexCount, indexCount); @@ -516,23 +546,63 @@ const void *QSGGeometry::indexData() const } /*! + \enum QSGGeometry::DrawingMode + + The values correspond to OpenGL enum values like \c GL_POINTS, \c GL_LINES, + etc. QSGGeometry provies its own type in order to be able to provide the + same API with non-OpenGL backends as well. + + \value DrawPoints + \value DrawLines + \value DrawLineLoop + \value DrawLineStrip + \value DrawTriangles + \value DrawTriangleStrip + \value DrawTriangleFan + */ + +/*! + \enum QSGGeometry::Type + + The values correspond to OpenGL type constants like \c GL_BYTE, \c + GL_UNSIGNED_BYTE, etc. QSGGeometry provies its own type in order to be able + to provide the same API with non-OpenGL backends as well. + + \value ByteType + \value UnsignedByteType + \value ShortType + \value UnsignedShortType + \value IntType + \value UnsignedIntType + \value FloatType + */ + +/*! Sets the \a mode to be used for drawing this geometry. - The default value is \c GL_TRIANGLE_STRIP. + The default value is QSGGeometry::DrawTriangleStrip. + + \sa DrawingMode */ -void QSGGeometry::setDrawingMode(GLenum mode) +void QSGGeometry::setDrawingMode(unsigned int mode) { m_drawing_mode = mode; } /*! - Gets the current line or point width or to be used for this geometry. This property - only applies to line width when the drawingMode is \c GL_LINES, \c GL_LINE_STRIP, or - \c GL_LINE_LOOP. For desktop OpenGL, it also applies to point size when the drawingMode - is \c GL_POINTS. + Gets the current line or point width or to be used for this geometry. This + property only applies to line width when the drawingMode is DrawLines, + DarwLineStrip, or DrawLineLoop. For desktop OpenGL, it also applies to + point size when the drawingMode is DrawPoints. The default value is \c 1.0 + \note When not using OpenGL, support for point and line drawing may be + limited. For example, some APIs do not support point sprites and so setting + a size other than 1 is not possible. Some backends may be able implement + support via geometry shaders, but this is not guaranteed to be always + available. + \sa setLineWidth(), drawingMode() */ float QSGGeometry::lineWidth() const @@ -541,14 +611,15 @@ float QSGGeometry::lineWidth() const } /*! - Sets the line or point width to be used for this geometry to \a width. This property - only applies to line width when the drawingMode is \c GL_LINES, \c GL_LINE_STRIP, or - \c GL_LINE_LOOP. For Desktop OpenGL, it also applies to point size when the drawingMode - is \c GL_POINTS. + Sets the line or point width to be used for this geometry to \a width. This + property only applies to line width when the drawingMode is DrawLines, + DrawLineStrip, or DrawLineLoop. For Desktop OpenGL, it also applies to + point size when the drawingMode is DrawPoints. - \note How line width and point size are treated is implementation dependent: The application - should not rely on these, but rather create triangles or similar to draw areas. On OpenGL ES, - line width support is limited and point size is unsupported. + \note How line width and point size are treated is implementation + dependent: The application should not rely on these, but rather create + triangles or similar to draw areas. On OpenGL ES, line width support is + limited and point size is unsupported. \sa lineWidth(), drawingMode() */ @@ -601,8 +672,8 @@ void QSGGeometry::allocate(int vertexCount, int indexCount) m_index_data_offset = -1; m_owns_data = false; } else { - Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT); - int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32)); + Q_ASSERT(m_index_type == UnsignedIntType || m_index_type == UnsignedShortType); + int indexByteSize = indexCount * (m_index_type == UnsignedShortType ? sizeof(quint16) : sizeof(quint32)); m_data = (void *) malloc(vertexByteSize + indexByteSize); m_index_data_offset = vertexByteSize; m_owns_data = true; @@ -674,6 +745,27 @@ void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, v[3].ty = textureRect.bottom(); } +/*! + Updates the geometry \a g with the coordinates in \a rect. + + The function assumes the geometry object contains a single triangle strip + of QSGGeometry::ColoredPoint2D vertices + */ +void QSGGeometry::updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect) +{ + ColoredPoint2D *v = g->vertexDataAsColoredPoint2D(); + v[0].x = rect.left(); + v[0].y = rect.top(); + + v[1].x = rect.left(); + v[1].y = rect.bottom(); + + v[2].x = rect.right(); + v[2].y = rect.top(); + + v[3].x = rect.right(); + v[3].y = rect.bottom(); +} /*! diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h index 5773b6abd1..7a916610e3 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.h +++ b/src/quick/scenegraph/coreapi/qsggeometry.h @@ -51,6 +51,45 @@ class QSGGeometryData; class Q_QUICK_EXPORT QSGGeometry { public: + enum AttributeType { + UnknownAttribute, + PositionAttribute, + ColorAttribute, + TexCoordAttribute, + TexCoord1Attribute, + TexCoord2Attribute + }; + + enum DataPattern { + AlwaysUploadPattern = 0, + StreamPattern = 1, + DynamicPattern = 2, + StaticPattern = 3 + }; + + // Equivalents to GL_* drawing modes. + // Keep in sync with GL headers. + enum DrawingMode { + DrawPoints = 0x0000, + DrawLines = 0x0001, + DrawLineLoop = 0x0002, + DrawLineStrip = 0x0003, + DrawTriangles = 0x0004, + DrawTriangleStrip = 0x0005, + DrawTriangleFan = 0x0006 + }; + + // Equivalents to GL_BYTE and similar type constants. + // Keep in sync with GL headers. + enum Type { + ByteType = 0x1400, + UnsignedByteType = 0x1401, + ShortType = 0x1402, + UnsignedShortType = 0x1403, + IntType = 0x1404, + UnsignedIntType = 0x1405, + FloatType = 0x1406 + }; struct Q_QUICK_EXPORT Attribute { @@ -60,9 +99,12 @@ public: uint isVertexCoordinate : 1; - uint reserved : 31; + AttributeType attributeType : 4; + + uint reserved : 27; static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false); + static Attribute createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType); }; struct AttributeSet { @@ -97,21 +139,15 @@ public: static const AttributeSet &defaultAttributes_TexturedPoint2D(); static const AttributeSet &defaultAttributes_ColoredPoint2D(); - enum DataPattern { - AlwaysUploadPattern = 0, - StreamPattern = 1, - DynamicPattern = 2, - StaticPattern = 3 - }; - QSGGeometry(const QSGGeometry::AttributeSet &attribs, int vertexCount, int indexCount = 0, - int indexType = GL_UNSIGNED_SHORT); + int indexType = UnsignedShortType); virtual ~QSGGeometry(); - void setDrawingMode(GLenum mode); - inline GLenum drawingMode() const { return m_drawing_mode; } + // must use unsigned int to be compatible with the old GLenum to keep BC + void setDrawingMode(unsigned int mode); + inline unsigned int drawingMode() const { return m_drawing_mode; } void allocate(int vertexCount, int indexCount = 0); @@ -147,6 +183,7 @@ public: static void updateRectGeometry(QSGGeometry *g, const QRectF &rect); static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect); + static void updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect); void setIndexDataPattern(DataPattern p); DataPattern indexDataPattern() const { return DataPattern(m_index_usage_pattern); } @@ -187,25 +224,25 @@ private: inline uint *QSGGeometry::indexDataAsUInt() { - Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + Q_ASSERT(m_index_type == UnsignedIntType); return static_cast<uint *>(indexData()); } inline quint16 *QSGGeometry::indexDataAsUShort() { - Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + Q_ASSERT(m_index_type == UnsignedShortType); return static_cast<quint16 *>(indexData()); } inline const uint *QSGGeometry::indexDataAsUInt() const { - Q_ASSERT(m_index_type == GL_UNSIGNED_INT); + Q_ASSERT(m_index_type == UnsignedIntType); return static_cast<const uint *>(indexData()); } inline const quint16 *QSGGeometry::indexDataAsUShort() const { - Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT); + Q_ASSERT(m_index_type == UnsignedShortType); return static_cast<const quint16 *>(indexData()); } @@ -214,7 +251,7 @@ inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() Q_ASSERT(m_attributes.count == 1); Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].type == FloatType); Q_ASSERT(m_attributes.attributes[0].position == 0); return static_cast<Point2D *>(m_data); } @@ -225,10 +262,10 @@ inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D() Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); Q_ASSERT(m_attributes.attributes[0].position == 0); Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].type == FloatType); Q_ASSERT(m_attributes.attributes[1].position == 1); Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].type == FloatType); return static_cast<TexturedPoint2D *>(m_data); } @@ -238,10 +275,10 @@ inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D() Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); Q_ASSERT(m_attributes.attributes[0].position == 0); Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].type == FloatType); Q_ASSERT(m_attributes.attributes[1].position == 1); Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); - Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + Q_ASSERT(m_attributes.attributes[1].type == UnsignedByteType); return static_cast<ColoredPoint2D *>(m_data); } @@ -250,7 +287,7 @@ inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const Q_ASSERT(m_attributes.count == 1); Q_ASSERT(m_attributes.stride == 2 * sizeof(float)); Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].type == FloatType); Q_ASSERT(m_attributes.attributes[0].position == 0); return static_cast<const Point2D *>(m_data); } @@ -261,10 +298,10 @@ inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoin Q_ASSERT(m_attributes.stride == 4 * sizeof(float)); Q_ASSERT(m_attributes.attributes[0].position == 0); Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].type == FloatType); Q_ASSERT(m_attributes.attributes[1].position == 1); Q_ASSERT(m_attributes.attributes[1].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[1].type == FloatType); return static_cast<const TexturedPoint2D *>(m_data); } @@ -274,18 +311,18 @@ inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2 Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char)); Q_ASSERT(m_attributes.attributes[0].position == 0); Q_ASSERT(m_attributes.attributes[0].tupleSize == 2); - Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT); + Q_ASSERT(m_attributes.attributes[0].type == FloatType); Q_ASSERT(m_attributes.attributes[1].position == 1); Q_ASSERT(m_attributes.attributes[1].tupleSize == 4); - Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE); + Q_ASSERT(m_attributes.attributes[1].type == UnsignedByteType); return static_cast<const ColoredPoint2D *>(m_data); } int QSGGeometry::sizeOfIndex() const { - if (m_index_type == GL_UNSIGNED_SHORT) return 2; - else if (m_index_type == GL_UNSIGNED_BYTE) return 1; - else if (m_index_type == GL_UNSIGNED_INT) return 4; + if (m_index_type == UnsignedShortType) return 2; + else if (m_index_type == UnsignedByteType) return 1; + else if (m_index_type == UnsignedIntType) return 4; return 0; } diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index ceb53d0d14..13598bbe1d 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -40,7 +40,13 @@ #include "qsgmaterial.h" #include "qsgrenderer_p.h" #include "qsgmaterialshader_p.h" -#include <private/qsgshadersourcebuilder_p.h> +#ifndef QT_NO_OPENGL +# include <private/qsgshadersourcebuilder_p.h> +# include <private/qsgdefaultcontext_p.h> +# include <private/qsgdefaultrendercontext_p.h> +# include <QtGui/QOpenGLFunctions> +# include <QtGui/QOpenGLContext> +#endif QT_BEGIN_NAMESPACE @@ -58,16 +64,17 @@ void qsg_set_material_failure() qsg_material_failure = true; } #endif - +#ifndef QT_NO_OPENGL const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const { - QStringList files = m_sourceFiles[type]; + const QStringList files = m_sourceFiles[type]; QSGShaderSourceBuilder builder; - Q_FOREACH (const QString &file, files) + for (const QString &file : files) builder.appendSourceFile(file); m_sources[type] = builder.source(); return m_sources[type].constData(); } +#endif #ifndef QT_NO_DEBUG static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); @@ -220,7 +227,7 @@ QSGMaterialShader::~QSGMaterialShader() defines the attribute register position in the vertex shader. */ - +#ifndef QT_NO_OPENGL /*! \fn const char *QSGMaterialShader::vertexShader() const @@ -256,7 +263,7 @@ const char *QSGMaterialShader::fragmentShader() const Returns the shader program used by this QSGMaterialShader. */ - +#endif /*! \fn void QSGMaterialShader::initialize() @@ -313,6 +320,7 @@ void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial { } +#ifndef QT_NO_OPENGL /*! Sets the GLSL source file for the shader stage \a type to \a sourceFile. The default implementation of the vertexShader() and fragmentShader() functions @@ -388,7 +396,7 @@ void QSGMaterialShader::compile() } } - +#endif /*! \class QSGMaterialShader::RenderState @@ -542,7 +550,7 @@ QRect QSGMaterialShader::RenderState::deviceRect() const return static_cast<const QSGRenderer *>(m_data)->deviceRect(); } - +#ifndef QT_NO_OPENGL /*! Returns the QOpenGLContext that is being used for rendering @@ -550,9 +558,15 @@ QRect QSGMaterialShader::RenderState::deviceRect() const QOpenGLContext *QSGMaterialShader::RenderState::context() const { - return static_cast<const QSGRenderer *>(m_data)->context()->openglContext(); + // Only the QSGDefaultRenderContext will have an OpenGL Context to query + auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context()); + if (openGLRenderContext != nullptr) + return openGLRenderContext->openglContext(); + else + return nullptr; } +#endif #ifndef QT_NO_DEBUG static int qt_material_count = 0; @@ -667,7 +681,6 @@ QSGMaterial::~QSGMaterial() the full matrix of the geometry nodes for rendering. \value CustomCompileStep Starting with Qt 5.2, the scene graph will not always call - QSGMaterialShader::compile() when its shader program is compiled and linked. Set this flag to enforce that the function is called. diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h index 0a6a340092..114651653f 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.h +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -41,7 +41,11 @@ #define QSGMATERIAL_H #include <QtQuick/qtquickglobal.h> -#include <QtGui/qopenglshaderprogram.h> +#ifndef QT_NO_OPENGL +# include <QtGui/qopenglshaderprogram.h> +#endif +#include <QtGui/QMatrix4x4> +#include <QtCore/QRect> QT_BEGIN_NAMESPACE @@ -59,8 +63,10 @@ public: public: enum DirtyState { - DirtyMatrix = 0x0001, - DirtyOpacity = 0x0002 + DirtyMatrix = 0x0001, + DirtyOpacity = 0x0002, + DirtyCachedMaterialData = 0x0004, + DirtyAll = 0xFFFF }; Q_DECLARE_FLAGS(DirtyStates, DirtyState) @@ -68,6 +74,7 @@ public: inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; } inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; } + bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; } float opacity() const; QMatrix4x4 combinedMatrix() const; @@ -77,9 +84,9 @@ public: QRect deviceRect() const; float determinant() const; float devicePixelRatio() const; - +#ifndef QT_NO_OPENGL QOpenGLContext *context() const; - +#endif private: friend class QSGRenderer; DirtyStates m_dirty; @@ -94,27 +101,30 @@ public: // First time a material is used, oldMaterial is null. virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); virtual char const *const *attributeNames() const = 0; // Array must end with null. - +#ifndef QT_NO_OPENGL inline QOpenGLShaderProgram *program() { return &m_program; } - +#endif protected: Q_DECLARE_PRIVATE(QSGMaterialShader) QSGMaterialShader(QSGMaterialShaderPrivate &dd); - friend class QSGRenderContext; + friend class QSGDefaultRenderContext; friend class QSGBatchRenderer::ShaderManager; - +#ifndef QT_NO_OPENGL void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile); void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles); virtual void compile(); +#endif virtual void initialize() { } - +#ifndef QT_NO_OPENGL virtual const char *vertexShader() const; virtual const char *fragmentShader() const; - +#endif private: +#ifndef QT_NO_OPENGL QOpenGLShaderProgram m_program; +#endif QScopedPointer<QSGMaterialShaderPrivate> d_ptr; }; diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h index 291c0cc57c..0dbce010db 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h +++ b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h @@ -59,10 +59,12 @@ QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QSGMaterialShaderPrivate { public: +#ifndef QT_NO_OPENGL const char *loadShaderSource(QOpenGLShader::ShaderType type) const; QHash<QOpenGLShader::ShaderType, QStringList> m_sourceFiles; mutable QHash<QOpenGLShader::ShaderType, QByteArray> m_sources; +#endif }; #ifndef QT_NO_DEBUG diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index 53798f0e07..a1e1ef8c27 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -778,6 +778,18 @@ QSGBasicGeometryNode::~QSGBasicGeometryNode() \internal */ +/*! + \fn void QSGBasicGeometryNode::setRendererMatrix(const QMatrix4x4 *m) + + \internal + */ + +/*! + \fn void QSGBasicGeometryNode::setRendererClipList(const QSGClipNode *c) + + \internal + */ + /*! Sets the geometry of this node to \a geometry. @@ -1256,7 +1268,7 @@ QSGRootNode::QSGRootNode() QSGRootNode::~QSGRootNode() { while (!m_renderers.isEmpty()) - m_renderers.last()->setRootNode(0); + m_renderers.constLast()->setRootNode(0); destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. } @@ -1454,8 +1466,6 @@ void QSGNodeVisitor::visitChildren(QSGNode *n) visitNode(c); } - - #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QSGGeometryNode *n) { @@ -1472,15 +1482,15 @@ QDebug operator<<(QDebug d, const QSGGeometryNode *n) } else { switch (g->drawingMode()) { - case GL_TRIANGLE_STRIP: d << "strip"; break; - case GL_TRIANGLE_FAN: d << "fan"; break; - case GL_TRIANGLES: d << "triangles"; break; + case QSGGeometry::DrawTriangleStrip: d << "strip"; break; + case QSGGeometry::DrawTriangleFan: d << "fan"; break; + case QSGGeometry::DrawTriangles: d << "triangles"; break; default: break; } d << "#V:" << g->vertexCount() << "#I:" << g->indexCount(); - if (g->attributeCount() > 0 && g->attributes()->type == GL_FLOAT) { + if (g->attributeCount() > 0 && g->attributes()->type == QSGGeometry::FloatType) { float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10; int stride = g->sizeOfVertex(); for (int i = 0; i < g->vertexCount(); ++i) { diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index 9d14ae04df..f7ea6dbe23 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -79,8 +79,8 @@ public: OpacityNodeType, #ifndef qdoc RootNodeType, - RenderNodeType #endif + RenderNodeType }; enum Flag { @@ -204,13 +204,15 @@ public: const QMatrix4x4 *matrix() const { return m_matrix; } const QSGClipNode *clipList() const { return m_clip_list; } + void setRendererMatrix(const QMatrix4x4 *m) { m_matrix = m; } + void setRendererClipList(const QSGClipNode *c) { m_clip_list = c; } + protected: QSGBasicGeometryNode(NodeType type); QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type); private: friend class QSGNodeUpdater; - friend class QSGBatchRenderer::Updater; QSGGeometry *m_geometry; diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp index 372ffce9d6..d6d533307e 100644 --- a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp @@ -175,9 +175,10 @@ void QSGNodeUpdater::enterRenderNode(QSGRenderNode *r) qDebug() << "enter render:" << r; #endif - r->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last(); - r->m_clip_list = m_current_clip; - r->setInheritedOpacity(m_opacity_stack.last()); + QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(r); + rd->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last(); + rd->m_clip_list = m_current_clip; + rd->m_opacity = m_opacity_stack.last(); } void QSGNodeUpdater::leaveRenderNode(QSGRenderNode *r) diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index ace10661c0..220e6ab212 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -39,11 +39,15 @@ #include "qsgrenderer_p.h" #include "qsgnodeupdater_p.h" - -#include <qopenglframebufferobject.h> - +#ifndef QT_NO_OPENGL +# include <QtGui/QOpenGLFramebufferObject> +# include <QtGui/QOpenGLContext> +# include <QtGui/QOpenGLFunctions> +#endif #include <private/qquickprofiler_p.h> +#include <QtCore/QElapsedTimer> + QT_BEGIN_NAMESPACE static const bool qsg_sanity_check = qEnvironmentVariableIntValue("QSG_SANITY_CHECK"); @@ -63,19 +67,25 @@ int qt_sg_envInt(const char *name, int defaultValue) void QSGBindable::clear(QSGAbstractRenderer::ClearMode mode) const { +#ifndef QT_NO_OPENGL GLuint bits = 0; if (mode & QSGAbstractRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT; if (mode & QSGAbstractRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT; if (mode & QSGAbstractRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT; QOpenGLContext::currentContext()->functions()->glClear(bits); +#else + Q_UNUSED(mode) +#endif } // Reactivate the color buffer after switching to the stencil. void QSGBindable::reactivate() const { +#ifndef QT_NO_OPENGL QOpenGLContext::currentContext()->functions()->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +#endif } - +#ifndef QT_NO_OPENGL QSGBindableFboId::QSGBindableFboId(GLuint id) : m_id(id) { @@ -86,7 +96,7 @@ void QSGBindableFboId::bind() const { QOpenGLContext::currentContext()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_id); } - +#endif /*! \class QSGRenderer \brief The renderer class is the abstract baseclass use for rendering the @@ -169,8 +179,9 @@ bool QSGRenderer::isMirrored() const return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0; } -void QSGRenderer::renderScene(GLuint fboId) +void QSGRenderer::renderScene(uint fboId) { +#ifndef QT_NO_OPENGL if (fboId) { QSGBindableFboId bindable(fboId); renderScene(bindable); @@ -182,7 +193,11 @@ void QSGRenderer::renderScene(GLuint fboId) } bindable; renderScene(bindable); } +#else + Q_UNUSED(fboId) +#endif } + void QSGRenderer::renderScene(const QSGBindable &bindable) { if (!rootNode()) @@ -207,6 +222,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) bindTime = frameTimer.nsecsElapsed(); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame); +#ifndef QT_NO_OPENGL // Sanity check that attribute registers are disabled if (qsg_sanity_check) { GLint count = 0; @@ -219,6 +235,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) } } } +#endif render(); if (profileFrames) diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 5c7a32c161..94b78a85b4 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -86,14 +86,12 @@ public: bool isMirrored() const; void renderScene(const QSGBindable &bindable); - virtual void renderScene(GLuint fboId = 0) Q_DECL_OVERRIDE; + virtual void renderScene(uint fboId = 0) Q_DECL_OVERRIDE; virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) Q_DECL_OVERRIDE; QSGNodeUpdater *nodeUpdater() const; void setNodeUpdater(QSGNodeUpdater *updater); - inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const; - virtual void setCustomRenderMode(const QByteArray &) { }; void clearChangedFlag() { m_changed_emitted = false; } @@ -135,7 +133,7 @@ public: virtual void clear(QSGAbstractRenderer::ClearMode mode) const; virtual void reactivate() const; }; - +#ifndef QT_NO_OPENGL class QSGBindableFboId : public QSGBindable { public: @@ -144,7 +142,7 @@ public: private: GLuint m_id; }; - +#endif QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState::DirtyStates dirty) const diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp new file mode 100644 index 0000000000..fa543aecad --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrendererinterface.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGRendererInterface + \brief An interface providing access to some of the graphics API specific internals + of the scenegraph. + \inmodule QtQuick + \since 5.8 + + Renderer interfaces allow accessing graphics API specific functionality in + the scenegraph. Such internals are not typically exposed. However, when + integrating custom rendering via QSGRenderNode for example, it may become + necessary to query certain values, for instance the graphics device (e.g. + the Direct3D or Vulkan device) that is used by the scenegraph. + + QSGRendererInterface's functions have varying availability. API and + language queries, like graphicsApi() or shaderType() are always available, + meaning it is sufficient to construct a QQuickWindow or QQuickView, and the + graphics API or shading language in use can be queried right after via + QQuickWindow::rendererInterface(). This guarantees that utilities like the + GraphicsInfo QML type are able to report the correct values as early as + possible, without having conditional property values - depending on for + instance shaderType() - evaluate to unexpected values. + + Engine-specific accessors, like getResource(), are however available only + after the scenegraph is initialized. Additionally, there may be + backend-specific limitations on when such functions can be called. The only + way that is guaranteed to succeed is calling them when the rendering of a + node (i.e. the preparation of the command list for the next frame) is + active. In practice this typically means QSGRenderNode::render(). + */ + +/*! + \enum QSGRendererInterface::GraphicsApi + \value Unknown An unknown graphics API is in use + \value Software The Qt Quick 2D Renderer is in use + \value OpenGL OpenGL ES 2.0 or higher + \value Direct3D12 Direct3D 12 + */ + +/*! + \enum QSGRendererInterface::Resource + \value Device The graphics device, when applicable. + \value CommandQueue The graphics command queue used by the scenegraph, when applicable. + \value CommandList The command list or buffer used by the scenegraph, when applicable. + \value Painter The active QPainter used by the scenegraph, when running with the software backend. + */ + +/*! + \enum QSGRendererInterface::ShaderType + \value UnknownShadingLanguage Not yet known due to no window and scenegraph associated + \value GLSL GLSL or GLSL ES + \value HLSL HLSL + */ + +/*! + \enum QSGRendererInterface::ShaderCompilationType + \value RuntimeCompilation Runtime compilation of shader source code is supported + \value OfflineCompilation Pre-compiled bytecode supported + */ + +/*! + \enum QSGRendererInterface::ShaderSourceType + + \value ShaderSourceString Shader source can be provided as a string in + the corresponding properties of ShaderEffect + + \value ShaderSourceFile Local or resource files containing shader source + code are supported + + \value ShaderByteCode Local or resource files containing shader bytecode are + supported + */ + +QSGRendererInterface::~QSGRendererInterface() +{ +} + +/*! + \fn QSGRendererInterface::GraphicsApi QSGRendererInterface::graphicsApi() const + + Returns the graphics API that is in use by the Qt Quick scenegraph. + + \note This function can be called on any thread. + */ + +/*! + Queries a graphics \a resource. Returns null when the resource in question is + not supported or not available. + + When successful, the returned pointer is either a direct pointer to an + interface (and can be cast, for example, to \c{ID3D12Device *}) or a + pointer to an opaque handle that needs to be dereferenced first (for + example, \c{VkDevice dev = *static_cast<VkDevice *>(result)}). The latter + is necessary since such handles may have sizes different from a pointer. + + \note The ownership of the returned pointer is never transferred to the caller. + + \note This function must only be called on the render thread. + */ +void *QSGRendererInterface::getResource(QQuickWindow *window, Resource resource) const +{ + Q_UNUSED(window); + Q_UNUSED(resource); + return nullptr; +} + +/*! + Queries a graphics resource. \a resource is a backend-specific key. This + allows supporting any future resources that are not listed in the + Resource enum. + + \note The ownership of the returned pointer is never transferred to the caller. + + \note This function must only be called on the render thread. + */ +void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resource) const +{ + Q_UNUSED(window); + Q_UNUSED(resource); + return nullptr; +} + +/*! + \fn QSGRendererInterface::ShaderType QSGRendererInterface::shaderType() const + + \return the shading language supported by the Qt Quick backend the + application is using. + + \note This function can be called on any thread. + + \sa QtQuick::GraphicsInfo + */ + +/*! + \fn QSGRendererInterface::ShaderCompilationTypes QSGRendererInterface::shaderCompilationType() const + + \return a bitmask of the shader compilation approaches supported by the Qt + Quick backend the application is using. + + \note This function can be called on any thread. + + \sa QtQuick::GraphicsInfo + */ + +/*! + \fn QSGRendererInterface::ShaderSourceTypes QSGRendererInterface::shaderSourceType() const + + \return a bitmask of the supported ways of providing shader sources in ShaderEffect items. + + \note This function can be called on any thread. + + \sa QtQuick::GraphicsInfo + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h new file mode 100644 index 0000000000..a50b362aeb --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGRENDERERINTERFACE_H +#define QSGRENDERERINTERFACE_H + +#include <QtQuick/qsgnode.h> + +QT_BEGIN_NAMESPACE + +class QQuickWindow; + +class Q_QUICK_EXPORT QSGRendererInterface +{ +public: + enum GraphicsApi { + Unknown, + Software, + OpenGL, + Direct3D12 + }; + + enum Resource { + Device, + CommandQueue, + CommandList, + Painter + }; + + enum ShaderType { + UnknownShadingLanguage, + GLSL, + HLSL + }; + + enum ShaderCompilationType { + RuntimeCompilation = 0x01, + OfflineCompilation = 0x02 + }; + Q_DECLARE_FLAGS(ShaderCompilationTypes, ShaderCompilationType) + + enum ShaderSourceType { + ShaderSourceString = 0x01, + ShaderSourceFile = 0x02, + ShaderByteCode = 0x04 + }; + Q_DECLARE_FLAGS(ShaderSourceTypes, ShaderSourceType) + + virtual ~QSGRendererInterface(); + + virtual GraphicsApi graphicsApi() const = 0; + + virtual void *getResource(QQuickWindow *window, Resource resource) const; + virtual void *getResource(QQuickWindow *window, const char *resource) const; + + virtual ShaderType shaderType() const = 0; + virtual ShaderCompilationTypes shaderCompilationType() const = 0; + virtual ShaderSourceTypes shaderSourceType() const = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderCompilationTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderSourceTypes) + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 1a38f6495e..5915d51f2b 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -37,28 +37,51 @@ ** ****************************************************************************/ +#include "qsgrendernode.h" #include "qsgrendernode_p.h" QT_BEGIN_NAMESPACE +/*! + \class QSGRenderNode + \brief The QSGRenderNode class represents a set of custom rendering commands + targeting the graphics API that is in use by the scenegraph. + \inmodule QtQuick + \since 5.8 + */ + QSGRenderNode::QSGRenderNode() - : QSGNode(RenderNodeType) - , m_matrix(0) - , m_clip_list(0) - , m_opacity(1) + : QSGNode(RenderNodeType), + d(new QSGRenderNodePrivate) { } -void QSGRenderNode::setInheritedOpacity(qreal opacity) +/*! + Destructs the render node. Derived classes are expected to perform cleanup + similar to releaseResources() in here. + + When a low-level graphics API is in use, the scenegraph will make sure + there is a CPU-side wait for the GPU to complete all work submitted to the + scenegraph's graphics command queue before the scenegraph's nodes are + deleted. Therefore there is no need to issue additional waits here, unless + the render() implementation is using additional command queues. + + \sa releaseResources() + */ +QSGRenderNode::~QSGRenderNode() { - Q_ASSERT(opacity >= 0 && opacity <= 1); - m_opacity = opacity; + delete d; } -/*! - \fn QSGRenderNode::StateFlags QSGRenderNode::changedStates() +QSGRenderNodePrivate::QSGRenderNodePrivate() + : m_matrix(0) + , m_clip_list(0) + , m_opacity(1) +{ +} - This function should return a mask where each bit represents OpenGL states changed by +/*! + This function should return a mask where each bit represents graphics states changed by the \l render() function: \list \li DepthState - depth write mask, depth test enabled, depth comparison function @@ -69,30 +92,71 @@ void QSGRenderNode::setInheritedOpacity(qreal opacity) \li BlendState - blend enabled, blend function \li CullState - front face, cull face enabled \li ViewportState - viewport + \li RenderTargetState - render target \endlist - The function is called by the renderer so it can reset the OpenGL states after rendering this - node. + The function is called by the renderer so it can reset the states after + rendering this node. This makes the implementation of render() simpler + since it does not have to query and restore these states. + + The default implementation returns 0, meaning no relevant state was changed + in render(). + + With APIs other than OpenGL the relevant states are only those that are set + via the command list (for example, OMSetRenderTargets, RSSetViewports, + RSSetScissorRects, OMSetBlendFactor, OMSetStencilRef in case of D3D12), and + only when such commands were added to the scenegraph's command list queried + via the QSGRendererInterface::CommandList resource enum. States set in + pipeline state objects do not need to be reported here. Similarly, draw + call related settings (root signature, descriptor heaps, etc.) are always + set again by the scenegraph so render() can freely change them. - \internal + The software backend exposes its QPainter and saves and restores before and + after invoking render(). Therefore reporting any changed states from here + is not necessary. + + \note This function may be called before render(). */ +QSGRenderNode::StateFlags QSGRenderNode::changedStates() const +{ + return 0; +} /*! - \fn void QSGRenderNode::render(const RenderState &state) + \fn void QSGRenderNode::render(const RenderState *state) - This function is called by the renderer and should paint this node with OpenGL commands. - - The states necessary for clipping has already been set before the function is called. - The clip is a combination of a stencil clip and scissor clip. Information about the clip is - found in \a state. + This function is called by the renderer and should paint this node with + directly invoking commands in the graphics API (OpenGL, Direct3D, etc.) + currently in use. The effective opacity can be retrieved with \l inheritedOpacity(). - The projection matrix is available through \a state, while the model-view matrix can be - fetched with \l matrix(). The combined matrix is then the projection matrix times the - model-view matrix. + The projection matrix is available through \a state, while the model-view + matrix can be fetched with \l matrix(). The combined matrix is then the + projection matrix times the model-view matrix. The correct stacking of the + items in the scene is ensured by the projection matrix. + + When using the provided matrices, the coordinate system for vertex data + follows the usual QQuickItem conventions: top-left is (0, 0), bottom-right + is the corresponding QQuickItem's width() and height() minus one. For + example, assuming a two float (x-y) per vertex coordinate layout, a + triangle covering half of the item can be specified as (width - 1, height - 1), + (0, 0), (0, height - 1) using counter-clockwise direction. + + \note QSGRenderNode is provided as a means to implement custom 2D or 2.5D + Qt Quick items. It is not intended for integrating true 3D content into the + Qt Quick scene. That use case is better supported by + QQuickFramebufferObject, QQuickWindow::beforeRendering(), or the + equivalents of those for APIs other than OpenGL. - The following states are set before this function is called: + Clip information is calculated before the function is called, it is however + not enabled. Implementations wishing to take clipping into account can set + up scissoring or stencil based on the information in \a state. Some + scenegraph backends, software in particular, use no scissor or stencil. + There the clip region is provided as an ordinary QRegion. + + For OpenGL the following states are set on the render thread's context + before this function is called: \list \li glDepthMask(false) \li glDisable(GL_DEPTH_TEST) @@ -107,14 +171,209 @@ void QSGRenderNode::setInheritedOpacity(qreal opacity) \li glDisable(GL_CULL_FACE) \endlist - States that are not listed above, but are included in \l StateFlags, can have arbitrary - values. + States that are not listed above, but are included in \l StateFlags, can + have arbitrary values. + + \l changedStates() should return which states this function changes. If a + state is not covered by \l StateFlags, the state should be set to the + default value according to the OpenGL specification. For other APIs, see + the documentation for changedStates() for more information. + + \note Depth writes are disabled when this function is called (for example, + glDepthMask(false) in case of OpenGL). Enabling depth writes can lead to + unexpected results, depending on the scenegraph backend in use, so nodes + should avoid this. - \l changedStates() should return which states this function has changed. If a state is not - covered by \l StateFlags, the state should be set to the default value according to the - OpenGL specification. + For APIs other than OpenGL, it will likely be necessary to query certain + API-specific resources (for example, the graphics device or the command + list/buffer to add the commands to). This is done via QSGRendererInterface. - \internal + \sa QSGRendererInterface, QQuickWindow::rendererInterface() */ +/*! + This function is called when all custom graphics resources allocated by + this node have to be freed immediately. In case the node does not directly + allocate graphics resources (buffers, textures, render targets, fences, + etc.) through the graphics API that is in use, there is nothing to do here. + + Failing to release all custom resources can lead to incorrect behavior in + graphics device loss scenarios on some systems since subsequent + reinitialization of the graphics system may fail. + + \note Some scenegraph backends may choose not to call this function. + Therefore it is expected that QSGRenderNode implementations perform cleanup + both in their destructor and in releaseResources(). + + Unlike with the destructor, it is expected that render() can reinitialize + all resources it needs when called after a call to releaseResources(). + + With OpenGL, the scenegraph's OpenGL context will be current both when + calling the destructor and this function. + */ +void QSGRenderNode::releaseResources() +{ +} + +/*! + \enum QSGRenderNode::RenderingFlag + + Possible values for the bitmask returned from flags(). + + \value BoundedRectRendering Indicates that the implementation of render() + does not render outside the area reported from rect() in item + coordinates. Such node implementations can lead to more efficient rendering, + depending on the scenegraph backend. For example, the software backend can + continue to use the more optimal partial update path when all render nodes + in the scene have this flag set. + + \value DepthAwareRendering Indicates that the implementations of render() + conforms to scenegraph expectations by only generating a Z value of 0 in + scene coordinates which is then transformed by the matrices retrieved from + RenderState::projectionMatrix() and matrix(), as described in the notes for + render(). Such node implementations can lead to more efficient rendering, + depending on the scenegraph backend. For example, the batching OpenGL + renderer can continue to use a more optimal path when all render nodes in + the scene have this flag set. + + \value OpaqueRendering Indicates that the implementation of render() writes + out opaque pixels for the entire area reported from rect(). By default the + renderers must assume that render() can also output semi or fully + transparent pixels. Setting this flag can improve performance in some + cases. + + \sa render(), rect() + */ + +/*! + \return flags describing the behavior of this render node. + + The default implementation returns 0. + + \sa RenderingFlag, rect() + */ +QSGRenderNode::RenderingFlags QSGRenderNode::flags() const +{ + return 0; +} + +/*! + \return the bounding rectangle in item coordinates for the area render() + touches. The value is only in use when flags() includes + BoundedRectRendering, ignored otherwise. + + Reporting the rectangle in combination with BoundedRectRendering is + particularly important with the \c software backend because otherwise + having a rendernode in the scene would trigger fullscreen updates, skipping + all partial update optimizations. + + For rendernodes covering the entire area of a corresponding QQuickItem the + return value will be (0, 0, item->width(), item->height()). + + \sa flags() +*/ +QRectF QSGRenderNode::rect() const +{ + return QRectF(); +} + +/*! + \return pointer to the current model-view matrix. + */ +const QMatrix4x4 *QSGRenderNode::matrix() const +{ + return d->m_matrix; +} + +/*! + \return the current clip list. + */ +const QSGClipNode *QSGRenderNode::clipList() const +{ + return d->m_clip_list; +} + +/*! + \return the current effective opacity. + */ +qreal QSGRenderNode::inheritedOpacity() const +{ + return d->m_opacity; +} + +QSGRenderNode::RenderState::~RenderState() +{ +} + +/*! + \fn const QMatrix4x4 *QSGRenderNode::RenderState::projectionMatrix() const + + \return pointer to the current projection matrix. + + The model-view matrix can be retrieved with QSGRenderNode::matrix(). + Typically \c{projection * modelview} is the matrix that is then used in the + vertex shader to transform the vertices. + */ + +/*! + \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorRect() const + + \return the current scissor rectangle when clipping is active. + + \note Be aware of the differences between graphics APIs: for some the + scissor rect is only active when scissoring is enabled (for example, + OpenGL), while for others the scissor rect is equal to the viewport rect + when there is no need to scissor away anything (for example, Direct3D 12). + */ + +/*! + \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorEnabled() const + + \return the current state of scissoring. + + \note Only relevant for graphics APIs that have a dedicated on/off state of + scissoring. + */ + +/*! + \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilValue() const + + \return the current stencil reference value when clipping is active. + */ + +/*! + \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilEnabled() const + + \return the current state of stencil testing. + + \note With graphics APIs where stencil testing is enabled in pipeline state + objects, instead of individual state-setting commands, it is up to the + implementation of render() to enable stencil testing with operations + \c KEEP, comparison function \c EQUAL, and a read and write mask of \c 0xFF. + */ + +/*! + \fn const QRegion *QSGRenderNode::clipRegion() const + + \return the current clip region or null for backends where clipping is + implemented via stencil or scissoring. + + The software backend uses no projection, scissor or stencil, meaning most + of the render state is not in use. However, the clip region that can be set + on the QPainter still has to be communicated since reconstructing this + manually in render() is not reasonable. It can therefore be queried via + this function. + */ + +/*! + \return pointer to a \a state value. + + Reserved for future use. + */ +void *QSGRenderNode::RenderState::get(const char *state) const +{ + Q_UNUSED(state); + return nullptr; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h new file mode 100644 index 0000000000..f6bc40d3ee --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrendernode.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGRENDERNODE_H +#define QSGRENDERNODE_H + +#include <QtQuick/qsgnode.h> + +QT_BEGIN_NAMESPACE + +class QSGRenderNodePrivate; + +class Q_QUICK_EXPORT QSGRenderNode : public QSGNode +{ +public: + enum StateFlag { + DepthState = 0x01, + StencilState = 0x02, + ScissorState = 0x04, + ColorState = 0x08, + BlendState = 0x10, + CullState = 0x20, + ViewportState = 0x40, + RenderTargetState = 0x80 + }; + Q_DECLARE_FLAGS(StateFlags, StateFlag) + + enum RenderingFlag { + BoundedRectRendering = 0x01, + DepthAwareRendering = 0x02, + OpaqueRendering = 0x04 + }; + Q_DECLARE_FLAGS(RenderingFlags, RenderingFlag) + + struct Q_QUICK_EXPORT RenderState { + virtual ~RenderState(); + virtual const QMatrix4x4 *projectionMatrix() const = 0; + virtual QRect scissorRect() const = 0; + virtual bool scissorEnabled() const = 0; + virtual int stencilValue() const = 0; + virtual bool stencilEnabled() const = 0; + virtual const QRegion *clipRegion() const = 0; + virtual void *get(const char *state) const; + }; + + QSGRenderNode(); + ~QSGRenderNode(); + + virtual StateFlags changedStates() const; + virtual void render(const RenderState *state) = 0; + virtual void releaseResources(); + virtual RenderingFlags flags() const; + virtual QRectF rect() const; + + const QMatrix4x4 *matrix() const; + const QSGClipNode *clipList() const; + qreal inheritedOpacity() const; + +private: + QSGRenderNodePrivate *d; + friend class QSGRenderNodePrivate; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::RenderingFlags) + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h index 8659b0e62c..5c42e55689 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h @@ -51,64 +51,23 @@ // We mean it. // -#include "qsgnode.h" -#include <private/qtquickglobal_p.h> +#include <QtQuick/qsgnode.h> +#include <QtQuick/qsgrendernode.h> QT_BEGIN_NAMESPACE -namespace QSGBatchRenderer { - class Renderer; -} - -class Q_QUICK_PRIVATE_EXPORT QSGRenderNode : public QSGNode +class QSGRenderNodePrivate { public: - enum StateFlag - { - DepthState = 0x01, // depth mask, depth test enable, depth func, clear depth - StencilState = 0x02, // stencil mask, stencil test enable, stencil op, stencil func, clear stencil - ScissorState = 0x04, // scissor enable, scissor test enable - ColorState = 0x08, // clear color, color mask - BlendState = 0x10, // blend enable, blend func - CullState = 0x20, // front face, cull face enable - ViewportState = 0x40 // viewport - }; - Q_DECLARE_FLAGS(StateFlags, StateFlag) - - struct RenderState - { - // The model-view matrix can be retrieved with matrix(). - // The opacity can be retrieved with inheritedOpacity(). - const QMatrix4x4 *projectionMatrix; - QRect scissorRect; - int stencilValue; - - bool stencilEnabled; - bool scissorEnabled; - }; - - QSGRenderNode(); + QSGRenderNodePrivate(); - virtual StateFlags changedStates() = 0; - virtual void render(const RenderState &state) = 0; - - const QMatrix4x4 *matrix() const { return m_matrix; } - const QSGClipNode *clipList() const { return m_clip_list; } - - void setInheritedOpacity(qreal opacity); - qreal inheritedOpacity() const { return m_opacity; } - -private: - friend class QSGNodeUpdater; - friend class QSGBatchRenderer::Renderer; + static QSGRenderNodePrivate *get(QSGRenderNode *node) { return node->d; } const QMatrix4x4 *m_matrix; const QSGClipNode *m_clip_list; qreal m_opacity; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags) - QT_END_NAMESPACE #endif diff --git a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp index 97d7e69407..3a35632d5c 100644 --- a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp +++ b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp @@ -89,9 +89,6 @@ void Tokenizer::initialize(const char *input) identifier = input; } -#define foo - - Tokenizer::Token Tokenizer::next() { while (*pos != 0) { diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 682423726e..9923fa6e24 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -46,6 +46,7 @@ #include <private/qrawfont_p.h> #include <QtGui/qguiapplication.h> #include <qdir.h> +#include <qsgrendernode.h> #include <private/qquickprofiler_p.h> #include <QElapsedTimer> @@ -65,15 +66,18 @@ QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCach QRawFontPrivate *fontD = QRawFontPrivate::get(font); m_glyphCount = fontD->fontEngine->glyphCount(); - m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT; + m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT(); m_referenceFont = font; // we set the same pixel size as used by the distance field internally. // this allows us to call pathForGlyph once and reuse the result. m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution) * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution)); Q_ASSERT(m_referenceFont.isValid()); - +#ifndef QT_NO_OPENGL m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile); +#else + Q_UNUSED(c) +#endif } QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() @@ -118,7 +122,7 @@ void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs) int count = glyphs.count(); for (int i = 0; i < count; ++i) { glyph_t glyphIndex = glyphs.at(i); - if ((int) glyphIndex >= glyphCount()) { + if ((int) glyphIndex >= glyphCount() && glyphCount() > 0) { qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex); continue; } @@ -192,7 +196,7 @@ void QSGDistanceFieldGlyphCache::update() storeGlyphs(distanceFields); #if defined(QSG_DISTANCEFIELD_CACHE_DEBUG) - foreach (Texture texture, m_textures) + for (Texture texture : qAsConst(m_textures)) saveTexture(texture.textureId, texture.size.width(), texture.size.height()); #endif @@ -291,7 +295,7 @@ void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyp m_pendingGlyphs.add(glyphs.at(i)); } -void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize) +void QSGDistanceFieldGlyphCache::updateTexture(uint oldTex, uint newTex, const QSize &newTexSize) { int count = m_textures.count(); for (int i = 0; i < count; ++i) { @@ -509,6 +513,13 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node) visitChildren(child); break; } + case QSGNode::RenderNodeType: { + QSGRenderNode *r = static_cast<QSGRenderNode*>(child); + if (visit(r)) + visitChildren(r); + endVisit(r); + break; + } default: Q_UNREACHABLE(); break; @@ -516,4 +527,43 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node) } } +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p) +{ + QDebugStateSaver saver(debug); + debug.space(); + debug << p.semanticName << "semindex" << p.semanticIndex; + return debug; +} + +QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v) +{ + QDebugStateSaver saver(debug); + debug.space(); + debug << v.name; + switch (v.type) { + case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant: + debug << "cvar" << "offset" << v.offset << "size" << v.size; + break; + case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler: + debug << "sampler" << "bindpoint" << v.bindPoint; + break; + case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture: + debug << "texture" << "bindpoint" << v.bindPoint; + break; + default: + break; + } + return debug; +} + +QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd) +{ + QDebugStateSaver saver(debug); + debug.space(); + debug << vd.specialType; + return debug; +} +#endif + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 8579b0a57b..5880c67af0 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -76,12 +76,13 @@ class TextureReference; class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldGlyphNode; class QOpenGLContext; -class QSGImageNode; +class QSGInternalImageNode; class QSGPainterNode; -class QSGRectangleNode; +class QSGInternalRectangleNode; class QSGGlyphNode; -class QSGNinePatchNode; class QSGRootNode; +class QSGSpriteNode; +class QSGRenderNode; class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx { @@ -97,18 +98,22 @@ public: virtual void endVisit(QSGGeometryNode *) = 0; virtual bool visit(QSGOpacityNode *) = 0; virtual void endVisit(QSGOpacityNode *) = 0; - virtual bool visit(QSGImageNode *) = 0; - virtual void endVisit(QSGImageNode *) = 0; + virtual bool visit(QSGInternalImageNode *) = 0; + virtual void endVisit(QSGInternalImageNode *) = 0; virtual bool visit(QSGPainterNode *) = 0; virtual void endVisit(QSGPainterNode *) = 0; - virtual bool visit(QSGRectangleNode *) = 0; - virtual void endVisit(QSGRectangleNode *) = 0; + virtual bool visit(QSGInternalRectangleNode *) = 0; + virtual void endVisit(QSGInternalRectangleNode *) = 0; virtual bool visit(QSGGlyphNode *) = 0; virtual void endVisit(QSGGlyphNode *) = 0; - virtual bool visit(QSGNinePatchNode *) = 0; - virtual void endVisit(QSGNinePatchNode *) = 0; virtual bool visit(QSGRootNode *) = 0; virtual void endVisit(QSGRootNode *) = 0; +#if QT_CONFIG(quick_sprite) + virtual bool visit(QSGSpriteNode *) = 0; + virtual void endVisit(QSGSpriteNode *) = 0; +#endif + virtual bool visit(QSGRenderNode *) = 0; + virtual void endVisit(QSGRenderNode *) = 0; void visitChildren(QSGNode *node); }; @@ -122,7 +127,7 @@ public: virtual void accept(QSGNodeVisitorEx *) = 0; }; -class Q_QUICK_PRIVATE_EXPORT QSGRectangleNode : public QSGVisitableNode +class Q_QUICK_PRIVATE_EXPORT QSGInternalRectangleNode : public QSGVisitableNode { public: virtual void setRect(const QRectF &rect) = 0; @@ -140,7 +145,7 @@ public: }; -class Q_QUICK_PRIVATE_EXPORT QSGImageNode : public QSGVisitableNode +class Q_QUICK_PRIVATE_EXPORT QSGInternalImageNode : public QSGVisitableNode { public: virtual void setTargetRect(const QRectF &rect) = 0; @@ -186,19 +191,6 @@ public: virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } }; -class Q_QUICK_PRIVATE_EXPORT QSGNinePatchNode : public QSGVisitableNode -{ -public: - virtual void setTexture(QSGTexture *texture) = 0; - virtual void setBounds(const QRectF &bounds) = 0; - virtual void setDevicePixelRatio(qreal ratio) = 0; - virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom) = 0; - - virtual void update() = 0; - - virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } -}; - class Q_QUICK_EXPORT QSGLayer : public QSGDynamicTexture { Q_OBJECT @@ -210,7 +202,7 @@ public: virtual QImage toImage() const = 0; virtual void setLive(bool live) = 0; virtual void setRecursive(bool recursive) = 0; - virtual void setFormat(GLenum format) = 0; + virtual void setFormat(uint format) = 0; virtual void setHasMipmaps(bool mipmap) = 0; virtual void setDevicePixelRatio(qreal ratio) = 0; virtual void setMirrorHorizontal(bool mirror) = 0; @@ -223,6 +215,152 @@ Q_SIGNALS: void scheduledUpdateCompleted(); }; +#if QT_CONFIG(quick_sprite) + +class Q_QUICK_PRIVATE_EXPORT QSGSpriteNode : public QSGVisitableNode +{ +public: + virtual void setTexture(QSGTexture *texture) = 0; + virtual void setTime(float time) = 0; + virtual void setSourceA(const QPoint &source) = 0; + virtual void setSourceB(const QPoint &source) = 0; + virtual void setSpriteSize(const QSize &size) = 0; + virtual void setSheetSize(const QSize &size) = 0; + virtual void setSize(const QSizeF &size) = 0; + virtual void setFiltering(QSGTexture::Filtering filtering) = 0; + + virtual void update() = 0; + + virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } +}; + +#endif + +class Q_QUICK_PRIVATE_EXPORT QSGGuiThreadShaderEffectManager : public QObject +{ + Q_OBJECT + +public: + enum Status { + Compiled, + Uncompiled, + Error + }; + + virtual bool hasSeparateSamplerAndTextureObjects() const = 0; + + virtual QString log() const = 0; + virtual Status status() const = 0; + + struct ShaderInfo { + enum Type { + TypeVertex, + TypeFragment, + TypeOther + }; + enum VariableType { + Constant, // cbuffer members or uniforms + Sampler, + Texture // for APIs with separate texture and sampler objects + }; + struct InputParameter { + InputParameter() : semanticIndex(0) { } + // Semantics use the D3D keys (POSITION, TEXCOORD). + // Attribute name based APIs can map based on pre-defined names. + QByteArray semanticName; + int semanticIndex; + }; + struct Variable { + Variable() : type(Constant), offset(0), size(0), bindPoint(0) { } + VariableType type; + QByteArray name; + uint offset; // for cbuffer members + uint size; // for cbuffer members + int bindPoint; // for textures and samplers; for register-based APIs + }; + + QByteArray blob; // source or bytecode + Type type; + QVector<InputParameter> inputParameters; + QVector<Variable> variables; + uint constantDataSize; + }; + + virtual void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) = 0; + +Q_SIGNALS: + void shaderCodePrepared(bool ok, ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result); + void textureChanged(); + void logAndStatusChanged(); +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v); +#endif + +class Q_QUICK_PRIVATE_EXPORT QSGShaderEffectNode : public QSGVisitableNode +{ +public: + enum DirtyShaderFlag { + DirtyShaders = 0x01, + DirtyShaderConstant = 0x02, + DirtyShaderTexture = 0x04, + DirtyShaderGeometry = 0x08, + DirtyShaderMesh = 0x10, + + DirtyShaderAll = 0xFF + }; + Q_DECLARE_FLAGS(DirtyShaderFlags, DirtyShaderFlag) + + enum CullMode { // must match ShaderEffect + NoCulling, + BackFaceCulling, + FrontFaceCulling + }; + + struct VariableData { + enum SpecialType { None, Unused, Source, SubRect, Opacity, Matrix }; + + QVariant value; + SpecialType specialType; + }; + + struct ShaderData { + ShaderData() : hasShaderCode(false) { } + bool hasShaderCode; + QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo; + QVector<VariableData> varData; + }; + + struct SyncData { + DirtyShaderFlags dirty; + CullMode cullMode; + bool blending; + struct ShaderSyncData { + const ShaderData *shader; + const QSet<int> *dirtyConstants; + const QSet<int> *dirtyTextures; + }; + ShaderSyncData vertex; + ShaderSyncData fragment; + }; + + // Each ShaderEffect item has one node (render thread) and one manager (gui thread). + QSGShaderEffectNode(QSGGuiThreadShaderEffectManager *) { } + + virtual QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures) = 0; + virtual void syncMaterial(SyncData *syncData) = 0; + + void accept(QSGNodeVisitorEx *visitor) override { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGShaderEffectNode::DirtyShaderFlags) + +#ifndef QT_NO_DEBUG_STREAM +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd); +#endif + class Q_QUICK_PRIVATE_EXPORT QSGGlyphNode : public QSGVisitableNode { public: @@ -295,7 +433,7 @@ public: }; struct Texture { - GLuint textureId; + uint textureId; QSize size; Texture() : textureId(0), size(QSize()) { } @@ -359,10 +497,10 @@ protected: void markGlyphsToRender(const QVector<glyph_t> &glyphs); inline void removeGlyph(glyph_t glyph); - void updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize); + void updateTexture(uint oldTex, uint newTex, const QSize &newTexSize); inline bool containsGlyph(glyph_t glyph); - GLuint textureIdForGlyph(glyph_t glyph) const; + uint textureIdForGlyph(glyph_t glyph) const; GlyphData &glyphData(glyph_t glyph); @@ -412,7 +550,8 @@ inline bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) return glyphData(glyph).texCoord.isValid(); } - QT_END_NAMESPACE +Q_DECLARE_METATYPE(QSGGuiThreadShaderEffectManager::ShaderInfo::Type) + #endif diff --git a/src/quick/scenegraph/qsgbasicglyphnode.cpp b/src/quick/scenegraph/qsgbasicglyphnode.cpp new file mode 100644 index 0000000000..38f650a82c --- /dev/null +++ b/src/quick/scenegraph/qsgbasicglyphnode.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgbasicglyphnode_p.h" +#include <qsgmaterial.h> // just so that we can safely do delete m_material in the dtor + +QT_BEGIN_NAMESPACE + +QSGBasicGlyphNode::QSGBasicGlyphNode() + : m_style(QQuickText::Normal) + , m_material(0) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) +{ + m_geometry.setDrawingMode(QSGGeometry::DrawTriangles); + setGeometry(&m_geometry); +} + +QSGBasicGlyphNode::~QSGBasicGlyphNode() +{ + delete m_material; +} + +void QSGBasicGlyphNode::setColor(const QColor &color) +{ + m_color = color; + if (m_material != 0) { + setMaterialColor(color); + markDirty(DirtyMaterial); + } +} + +void QSGBasicGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + if (m_material != 0) + delete m_material; + + m_position = position; + m_glyphs = glyphs; + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("glyphs")); +#endif +} + +void QSGBasicGlyphNode::setStyle(QQuickText::TextStyle style) +{ + if (m_style == style) + return; + m_style = style; +} + +void QSGBasicGlyphNode::setStyleColor(const QColor &color) +{ + if (m_styleColor == color) + return; + m_styleColor = color; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicglyphnode_p.h b/src/quick/scenegraph/qsgbasicglyphnode_p.h new file mode 100644 index 0000000000..1d09367ea5 --- /dev/null +++ b/src/quick/scenegraph/qsgbasicglyphnode_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGBASICGLYPHNODE_P_H +#define QSGBASICGLYPHNODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QSGMaterial; + +class Q_QUICK_PRIVATE_EXPORT QSGBasicGlyphNode: public QSGGlyphNode +{ +public: + QSGBasicGlyphNode(); + virtual ~QSGBasicGlyphNode(); + + virtual QPointF baseLine() const { return m_baseLine; } + virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); + virtual void setColor(const QColor &color); + + virtual void setPreferredAntialiasingMode(AntialiasingMode) { } + virtual void setStyle(QQuickText::TextStyle); + virtual void setStyleColor(const QColor &); + + virtual void setMaterialColor(const QColor &color) = 0; + virtual void update() = 0; + +protected: + QGlyphRun m_glyphs; + QPointF m_position; + QColor m_color; + QQuickText::TextStyle m_style; + QColor m_styleColor; + + QPointF m_baseLine; + QSGMaterial *m_material; + + QSGGeometry m_geometry; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp new file mode 100644 index 0000000000..c8699218ba --- /dev/null +++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgbasicinternalimagenode_p.h" + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace +{ + struct SmoothVertex + { + float x, y, u, v; + float dx, dy, du, dv; + }; + + const QSGGeometry::AttributeSet &smoothAttributeSet() + { + static QSGGeometry::Attribute data[] = { + QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), + QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute), + QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::TexCoord1Attribute), + QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::TexCoord2Attribute) + }; + static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; + return attrs; + } +} + +QSGBasicInternalImageNode::QSGBasicInternalImageNode() + : m_innerSourceRect(0, 0, 1, 1) + , m_subSourceRect(0, 0, 1, 1) + , m_antialiasing(false) + , m_mirror(false) + , m_dirtyGeometry(false) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_dynamicTexture(nullptr) +{ + setGeometry(&m_geometry); + +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("internalimage")); +#endif +} + +void QSGBasicInternalImageNode::setTargetRect(const QRectF &rect) +{ + if (rect == m_targetRect) + return; + m_targetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setInnerTargetRect(const QRectF &rect) +{ + if (rect == m_innerTargetRect) + return; + m_innerTargetRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setInnerSourceRect(const QRectF &rect) +{ + if (rect == m_innerSourceRect) + return; + m_innerSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setSubSourceRect(const QRectF &rect) +{ + if (rect == m_subSourceRect) + return; + m_subSourceRect = rect; + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + + setMaterialTexture(texture); + updateMaterialBlending(); + + markDirty(DirtyMaterial); + + // Because the texture can be a different part of the atlas, we need to update it... + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setAntialiasing(bool antialiasing) +{ + if (antialiasing == m_antialiasing) + return; + m_antialiasing = antialiasing; + if (m_antialiasing) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + setFlag(OwnsGeometry, true); + } else { + setGeometry(&m_geometry); + setFlag(OwnsGeometry, false); + } + updateMaterialAntialiasing(); + m_dirtyGeometry = true; +} + +void QSGBasicInternalImageNode::setMirror(bool mirror) +{ + if (mirror == m_mirror) + return; + m_mirror = mirror; + m_dirtyGeometry = true; +} + + +void QSGBasicInternalImageNode::update() +{ + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGBasicInternalImageNode::preprocess() +{ + bool doDirty = false; + QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture()); + if (t) { + doDirty = t->updateTexture(); + if (doDirty) { + // The geometry may need updating. This is expensive however, so do + // it only when something relevant has changed. + if (t != m_dynamicTexture + || t->textureSize() != m_dynamicTextureSize + || t->normalizedTextureSubRect() != m_dynamicTextureSubRect) { + updateGeometry(); + m_dynamicTextureSize = t->textureSize(); + m_dynamicTextureSubRect = t->normalizedTextureSubRect(); + } + } + } + m_dynamicTexture = t; + + if (updateMaterialBlending()) + doDirty = true; + + if (doDirty) + markDirty(DirtyMaterial); +} + +namespace { + struct X { float x, tx; }; + struct Y { float y, ty; }; +} + +static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, + quint16 bottomLeft, quint16 bottomRight) +{ + *(*indices)++ = topLeft; + *(*indices)++ = bottomLeft; + *(*indices)++ = bottomRight; + *(*indices)++ = bottomRight; + *(*indices)++ = topRight; + *(*indices)++ = topLeft; +} + +QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, + const QRectF &innerTargetRect, + const QRectF &sourceRect, + const QRectF &innerSourceRect, + const QRectF &subSourceRect, + QSGGeometry *geometry, + bool mirror, + bool antialiasing) +{ + int floorLeft = qFloor(subSourceRect.left()); + int ceilRight = qCeil(subSourceRect.right()); + int floorTop = qFloor(subSourceRect.top()); + int ceilBottom = qCeil(subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + int hCells = hTiles; + int vCells = vTiles; + if (innerTargetRect.width() == 0) + hCells = 0; + if (innerTargetRect.left() != targetRect.left()) + ++hCells; + if (innerTargetRect.right() != targetRect.right()) + ++hCells; + if (innerTargetRect.height() == 0) + vCells = 0; + if (innerTargetRect.top() != targetRect.top()) + ++vCells; + if (innerTargetRect.bottom() != targetRect.bottom()) + ++vCells; + QVarLengthArray<X, 32> xData(2 * hCells); + QVarLengthArray<Y, 32> yData(2 * vCells); + X *xs = xData.data(); + Y *ys = yData.data(); + + if (innerTargetRect.left() != targetRect.left()) { + xs[0].x = targetRect.left(); + xs[0].tx = sourceRect.left(); + xs[1].x = innerTargetRect.left(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + if (innerTargetRect.width() != 0) { + xs[0].x = innerTargetRect.left(); + xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width(); + ++xs; + float b = innerTargetRect.width() / subSourceRect.width(); + float a = innerTargetRect.x() - subSourceRect.x() * b; + for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { + xs[0].x = xs[1].x = a + b * i; + xs[0].tx = innerSourceRect.right(); + xs[1].tx = innerSourceRect.left(); + xs += 2; + } + xs[0].x = innerTargetRect.right(); + xs[0].tx = innerSourceRect.x() + (subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); + ++xs; + } + if (innerTargetRect.right() != targetRect.right()) { + xs[0].x = innerTargetRect.right(); + xs[0].tx = innerSourceRect.right(); + xs[1].x = targetRect.right(); + xs[1].tx = sourceRect.right(); + xs += 2; + } + Q_ASSERT(xs == xData.data() + xData.size()); + if (mirror) { + float leftPlusRight = targetRect.left() + targetRect.right(); + int count = xData.size(); + xs = xData.data(); + for (int i = 0; i < count >> 1; ++i) + qSwap(xs[i], xs[count - 1 - i]); + for (int i = 0; i < count; ++i) + xs[i].x = leftPlusRight - xs[i].x; + } + + if (innerTargetRect.top() != targetRect.top()) { + ys[0].y = targetRect.top(); + ys[0].ty = sourceRect.top(); + ys[1].y = innerTargetRect.top(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + if (innerTargetRect.height() != 0) { + ys[0].y = innerTargetRect.top(); + ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height(); + ++ys; + float b = innerTargetRect.height() / subSourceRect.height(); + float a = innerTargetRect.y() - subSourceRect.y() * b; + for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { + ys[0].y = ys[1].y = a + b * i; + ys[0].ty = innerSourceRect.bottom(); + ys[1].ty = innerSourceRect.top(); + ys += 2; + } + ys[0].y = innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.y() + (subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); + ++ys; + } + if (innerTargetRect.bottom() != targetRect.bottom()) { + ys[0].y = innerTargetRect.bottom(); + ys[0].ty = innerSourceRect.bottom(); + ys[1].y = targetRect.bottom(); + ys[1].ty = sourceRect.bottom(); + ys += 2; + } + Q_ASSERT(ys == yData.data() + yData.size()); + + if (antialiasing) { + QSGGeometry *g = geometry; + Q_ASSERT(g); + + g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + g->setDrawingMode(QSGGeometry::DrawTriangles); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); + quint16 *indices = g->indexDataAsUShort(); + + // The deltas are how much the fuzziness can reach into the image. + // Only the border vertices are moved by the vertex shader, so the fuzziness + // can't reach further into the image than the closest interior vertices. + float leftDx = xData.at(1).x - xData.at(0).x; + float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; + float topDy = yData.at(1).y - yData.at(0).y; + float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; + + float leftDu = xData.at(1).tx - xData.at(0).tx; + float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; + float topDv = yData.at(1).ty - yData.at(0).ty; + float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; + + if (hCells == 1) { + leftDx = rightDx *= 0.5f; + leftDu = rightDu *= 0.5f; + } + if (vCells == 1) { + topDy = bottomDy *= 0.5f; + topDv = bottomDv *= 0.5f; + } + + // This delta is how much the fuzziness can reach out from the image. + float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height()) + ? targetRect.width() : targetRect.height()) * 0.5f; + + quint16 index = 0; + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + bool isTop = j == 0; + bool isBottom = j == vCells - 1; + for (int i = 0; i < hCells; ++i, xs += 2) { + bool isLeft = i == 0; + bool isRight = i == hCells - 1; + + SmoothVertex *v = vertices + index; + + quint16 topLeft = index; + for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 topRight = index; + for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[0].y; + v->v = ys[0].ty; + } + + quint16 bottomLeft = index; + for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { + v->x = xs[0].x; + v->u = xs[0].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + quint16 bottomRight = index; + for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { + v->x = xs[1].x; + v->u = xs[1].tx; + v->y = ys[1].y; + v->v = ys[1].ty; + } + + appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + + if (isTop) { + vertices[topLeft].dy = vertices[topRight].dy = topDy; + vertices[topLeft].dv = vertices[topRight].dv = topDv; + vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; + appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); + } + + if (isBottom) { + vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; + vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; + vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; + appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); + } + + if (isLeft) { + vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; + vertices[topLeft].du = vertices[bottomLeft].du = leftDu; + vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; + appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); + } + + if (isRight) { + vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; + vertices[topRight].du = vertices[bottomRight].du = -rightDu; + vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; + appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); + } + } + } + + Q_ASSERT(index == g->vertexCount()); + Q_ASSERT(indices - g->indexCount() == g->indexData()); + } else { + if (!geometry) { + geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), + hCells * vCells * 4, hCells * vCells * 6, + QSGGeometry::UnsignedShortType); + } else { + geometry->allocate(hCells * vCells * 4, hCells * vCells * 6); + } + geometry->setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometry::TexturedPoint2D *vertices = geometry->vertexDataAsTexturedPoint2D(); + ys = yData.data(); + for (int j = 0; j < vCells; ++j, ys += 2) { + xs = xData.data(); + for (int i = 0; i < hCells; ++i, xs += 2) { + vertices[0].x = vertices[2].x = xs[0].x; + vertices[0].tx = vertices[2].tx = xs[0].tx; + vertices[1].x = vertices[3].x = xs[1].x; + vertices[1].tx = vertices[3].tx = xs[1].tx; + + vertices[0].y = vertices[1].y = ys[0].y; + vertices[0].ty = vertices[1].ty = ys[0].ty; + vertices[2].y = vertices[3].y = ys[1].y; + vertices[2].ty = vertices[3].ty = ys[1].ty; + + vertices += 4; + } + } + + quint16 *indices = geometry->indexDataAsUShort(); + for (int i = 0; i < 4 * vCells * hCells; i += 4) + appendQuad(&indices, i, i + 1, i + 2, i + 3); + } + return geometry; +} + +void QSGBasicInternalImageNode::updateGeometry() +{ + Q_ASSERT(!m_targetRect.isEmpty()); + const QSGTexture *t = materialTexture(); + if (!t) { + QSGGeometry *g = geometry(); + g->allocate(4); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memset(g->vertexData(), 0, g->sizeOfVertex() * 4); + } else { + QRectF sourceRect = t->normalizedTextureSubRect(); + + QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), + sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), + m_innerSourceRect.width() * sourceRect.width(), + m_innerSourceRect.height() * sourceRect.height()); + + bool hasMargins = m_targetRect != m_innerTargetRect; + + int floorLeft = qFloor(m_subSourceRect.left()); + int ceilRight = qCeil(m_subSourceRect.right()); + int floorTop = qFloor(m_subSourceRect.top()); + int ceilBottom = qCeil(m_subSourceRect.bottom()); + int hTiles = ceilRight - floorLeft; + int vTiles = ceilBottom - floorTop; + + bool hasTiles = hTiles != 1 || vTiles != 1; + bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); + + // An image can be rendered as a single quad if: + // - There are no margins, and either: + // - the image isn't repeated + // - the source rectangle fills the entire texture so that texture wrapping can be used, + // and NPOT is supported + if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) { + QRectF sr; + if (!fullTexture) { + sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), + innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), + m_subSourceRect.width() * innerSourceRect.width(), + m_subSourceRect.height() * innerSourceRect.height()); + } else { + sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, + m_subSourceRect.width(), m_subSourceRect.height()); + } + if (m_mirror) { + qreal oldLeft = sr.left(); + sr.setLeft(sr.right()); + sr.setRight(oldLeft); + } + + if (m_antialiasing) { + QSGGeometry *g = geometry(); + Q_ASSERT(g != &m_geometry); + g->allocate(8, 14); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); + float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) + ? m_targetRect.width() : m_targetRect.height()) * 0.5f; + float sx = float(sr.width() / m_targetRect.width()); + float sy = float(sr.height() / m_targetRect.height()); + for (int d = -1; d <= 1; d += 2) { + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 2; ++i, ++vertices) { + vertices->x = m_targetRect.x() + i * m_targetRect.width(); + vertices->y = m_targetRect.y() + j * m_targetRect.height(); + vertices->u = sr.x() + i * sr.width(); + vertices->v = sr.y() + j * sr.height(); + vertices->dx = (i == 0 ? delta : -delta) * d; + vertices->dy = (j == 0 ? delta : -delta) * d; + vertices->du = (d < 0 ? 0 : vertices->dx * sx); + vertices->dv = (d < 0 ? 0 : vertices->dy * sy); + } + } + } + Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); + static const quint16 indices[] = { + 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, + 4, 6, 5, 7 + }; + Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); + memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); + } else { + m_geometry.allocate(4); + m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); + } + } else { + QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry; + updateGeometry(m_targetRect, m_innerTargetRect, + sourceRect, innerSourceRect, m_subSourceRect, + g, m_mirror, m_antialiasing); + } + } + markDirty(DirtyGeometry); + m_dirtyGeometry = false; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode_p.h b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h new file mode 100644 index 0000000000..a5689b20aa --- /dev/null +++ b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGBASICINTERNALIMAGENODE_P_H +#define QSGBASICINTERNALIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalImageNode : public QSGInternalImageNode +{ +public: + QSGBasicInternalImageNode(); + + void setTargetRect(const QRectF &rect) override; + void setInnerTargetRect(const QRectF &rect) override; + void setInnerSourceRect(const QRectF &rect) override; + void setSubSourceRect(const QRectF &rect) override; + void setTexture(QSGTexture *texture) override; + void setAntialiasing(bool antialiasing) override; + void setMirror(bool mirror) override; + void update() override; + void preprocess() override; + + static QSGGeometry *updateGeometry(const QRectF &targetRect, + const QRectF &innerTargetRect, + const QRectF &sourceRect, + const QRectF &innerSourceRect, + const QRectF &subSourceRect, + QSGGeometry *geometry, + bool mirror = false, + bool antialiasing = false); + +protected: + virtual void updateMaterialAntialiasing() = 0; + virtual void setMaterialTexture(QSGTexture *texture) = 0; + virtual QSGTexture *materialTexture() const = 0; + virtual bool updateMaterialBlending() = 0; + virtual bool supportsWrap(const QSize &size) const = 0; + + void updateGeometry(); + + QRectF m_targetRect; + QRectF m_innerTargetRect; + QRectF m_innerSourceRect; + QRectF m_subSourceRect; + + uint m_antialiasing : 1; + uint m_mirror : 1; + uint m_dirtyGeometry : 1; + + QSGGeometry m_geometry; + + QSGDynamicTexture *m_dynamicTexture; + QSize m_dynamicTextureSize; + QRectF m_dynamicTextureSubRect; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp index 5ef52e8722..df124cce35 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp +++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. @@ -38,17 +37,9 @@ ** ****************************************************************************/ - - -#include "qsgdefaultrectanglenode_p.h" - -#include <QtQuick/qsgvertexcolormaterial.h> -#include <QtQuick/qsgtexturematerial.h> - -#include <QtQuick/private/qsgcontext_p.h> +#include "qsgbasicinternalrectanglenode_p.h" #include <QtCore/qmath.h> -#include <QtCore/qvarlengtharray.h> QT_BEGIN_NAMESPACE @@ -96,95 +87,16 @@ namespace const QSGGeometry::AttributeSet &smoothAttributeSet() { static QSGGeometry::Attribute data[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), - QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE, false), - QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false) + QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), + QSGGeometry::Attribute::createWithAttributeType(1, 4, QSGGeometry::UnsignedByteType, QSGGeometry::ColorAttribute), + QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute) }; static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data }; return attrs; } } -class SmoothColorMaterialShader : public QSGMaterialShader -{ -public: - SmoothColorMaterialShader(); - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; - -private: - virtual void initialize(); - - int m_matrixLoc; - int m_opacityLoc; - int m_pixelSizeLoc; -}; - -SmoothColorMaterialShader::SmoothColorMaterialShader() - : QSGMaterialShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.frag")); -} - -void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect) -{ - if (state.isOpacityDirty()) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - - if (oldEffect == 0) { - // The viewport is constant, so set the pixel size uniform only once. - QRect r = state.viewportRect(); - program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); - } -} - -char const *const *SmoothColorMaterialShader::attributeNames() const -{ - static char const *const attributes[] = { - "vertex", - "vertexColor", - "vertexOffset", - 0 - }; - return attributes; -} - -void SmoothColorMaterialShader::initialize() -{ - m_matrixLoc = program()->uniformLocation("matrix"); - m_opacityLoc = program()->uniformLocation("opacity"); - m_pixelSizeLoc = program()->uniformLocation("pixelSize"); -} - -QSGSmoothColorMaterial::QSGSmoothColorMaterial() -{ - setFlag(RequiresFullMatrixExceptTranslate, true); - setFlag(Blending, true); -} - -int QSGSmoothColorMaterial::compare(const QSGMaterial *) const -{ - return 0; -} - -QSGMaterialType *QSGSmoothColorMaterial::type() const -{ - static QSGMaterialType type; - return &type; -} - -QSGMaterialShader *QSGSmoothColorMaterial::createShader() const -{ - return new SmoothColorMaterialShader; -} - - -QSGDefaultRectangleNode::QSGDefaultRectangleNode() +QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode() : m_radius(0) , m_pen_width(0) , m_aligned(true) @@ -194,14 +106,13 @@ QSGDefaultRectangleNode::QSGDefaultRectangleNode() , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) { setGeometry(&m_geometry); - setMaterial(&m_material); #ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("rectangle")); + qsgnode_set_description(this, QLatin1String("internalrectangle")); #endif } -void QSGDefaultRectangleNode::setRect(const QRectF &rect) +void QSGBasicInternalRectangleNode::setRect(const QRectF &rect) { if (rect == m_rect) return; @@ -209,7 +120,7 @@ void QSGDefaultRectangleNode::setRect(const QRectF &rect) m_dirty_geometry = true; } -void QSGDefaultRectangleNode::setColor(const QColor &color) +void QSGBasicInternalRectangleNode::setColor(const QColor &color) { if (color == m_color) return; @@ -218,7 +129,7 @@ void QSGDefaultRectangleNode::setColor(const QColor &color) m_dirty_geometry = true; } -void QSGDefaultRectangleNode::setPenColor(const QColor &color) +void QSGBasicInternalRectangleNode::setPenColor(const QColor &color) { if (color == m_border_color) return; @@ -227,7 +138,7 @@ void QSGDefaultRectangleNode::setPenColor(const QColor &color) m_dirty_geometry = true; } -void QSGDefaultRectangleNode::setPenWidth(qreal width) +void QSGBasicInternalRectangleNode::setPenWidth(qreal width) { if (width == m_pen_width) return; @@ -236,7 +147,7 @@ void QSGDefaultRectangleNode::setPenWidth(qreal width) } -void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops) +void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops) { if (stops.constData() == m_gradient_stops.constData()) return; @@ -249,7 +160,7 @@ void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops) m_dirty_geometry = true; } -void QSGDefaultRectangleNode::setRadius(qreal radius) +void QSGBasicInternalRectangleNode::setRadius(qreal radius) { if (radius == m_radius) return; @@ -257,24 +168,26 @@ void QSGDefaultRectangleNode::setRadius(qreal radius) m_dirty_geometry = true; } -void QSGDefaultRectangleNode::setAntialiasing(bool antialiasing) +void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing) { + if (!supportsAntialiasing()) + return; + if (antialiasing == m_antialiasing) return; m_antialiasing = antialiasing; if (m_antialiasing) { - setMaterial(&m_smoothMaterial); setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); setFlag(OwnsGeometry, true); } else { - setMaterial(&m_material); setGeometry(&m_geometry); setFlag(OwnsGeometry, false); } + updateMaterialAntialiasing(); m_dirty_geometry = true; } -void QSGDefaultRectangleNode::setAligned(bool aligned) +void QSGBasicInternalRectangleNode::setAligned(bool aligned) { if (aligned == m_aligned) return; @@ -282,30 +195,19 @@ void QSGDefaultRectangleNode::setAligned(bool aligned) m_dirty_geometry = true; } -void QSGDefaultRectangleNode::update() +void QSGBasicInternalRectangleNode::update() { if (m_dirty_geometry) { updateGeometry(); m_dirty_geometry = false; QSGNode::DirtyState state = QSGNode::DirtyGeometry; - // smoothed material is always blended, so no change in material state - if (material() == &m_material) { - bool wasBlending = (m_material.flags() & QSGMaterial::Blending); - bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) - || (m_color.alpha() < 255 && m_color.alpha() != 0) - || (m_pen_width > 0 && m_border_color.alpha() < 255); - if (wasBlending != isBlending) { - m_material.setFlag(QSGMaterial::Blending, isBlending); - state |= QSGNode::DirtyMaterial; - } - } - + updateMaterialBlending(&state); markDirty(state); } } -void QSGDefaultRectangleNode::updateGeometry() +void QSGBasicInternalRectangleNode::updateGeometry() { float width = float(m_rect.width()); float height = float(m_rect.height()); @@ -315,7 +217,7 @@ void QSGDefaultRectangleNode::updateGeometry() penWidth = qRound(penWidth); QSGGeometry *g = geometry(); - g->setDrawingMode(GL_TRIANGLE_STRIP); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); int vertexStride = g->sizeOfVertex(); union { @@ -782,5 +684,4 @@ void QSGDefaultRectangleNode::updateGeometry() } } - QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h index 4cfe921127..98e53669ce 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h @@ -38,8 +38,8 @@ ****************************************************************************/ -#ifndef QSGDEFAULTRECTANGLENODE_P_H -#define QSGDEFAULTRECTANGLENODE_P_H +#ifndef QSGBASICINTERNALRECTANGLENODE_P_H +#define QSGBASICINTERNALRECTANGLENODE_P_H // // W A R N I N G @@ -54,46 +54,31 @@ #include <private/qsgadaptationlayer_p.h> -#include <QtQuick/qsgvertexcolormaterial.h> - QT_BEGIN_NAMESPACE -class QSGContext; - -class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial +class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalRectangleNode : public QSGInternalRectangleNode { public: - QSGSmoothColorMaterial(); - - int compare(const QSGMaterial *other) const; + QSGBasicInternalRectangleNode(); + + void setRect(const QRectF &rect) override; + void setColor(const QColor &color) override; + void setPenColor(const QColor &color) override; + void setPenWidth(qreal width) override; + void setGradientStops(const QGradientStops &stops) override; + void setRadius(qreal radius) override; + void setAntialiasing(bool antialiasing) override; + void setAligned(bool aligned) override; + void update() override; protected: - virtual QSGMaterialType *type() const; - virtual QSGMaterialShader *createShader() const; -}; + virtual bool supportsAntialiasing() const { return true; } + virtual void updateMaterialAntialiasing() = 0; + virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0; -class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGRectangleNode -{ -public: - QSGDefaultRectangleNode(); - - 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); - virtual void setAligned(bool aligned); - virtual void update(); - -private: void updateGeometry(); void updateGradientTexture(); - QSGVertexColorMaterial m_material; - QSGSmoothColorMaterial m_smoothMaterial; - QRectF m_rect; QGradientStops m_gradient_stops; QColor m_color; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 842afe9dff..d52f69c7a3 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -38,27 +38,13 @@ ****************************************************************************/ #include <QtQuick/private/qsgcontext_p.h> -#include <QtQuick/private/qsgbatchrenderer_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> -#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> -#include <QtQuick/private/qsgdefaultrectanglenode_p.h> -#include <QtQuick/private/qsgdefaultimagenode_p.h> -#include <QtQuick/private/qsgdefaultpainternode_p.h> -#include <QtQuick/private/qsgdefaultglyphnode_p.h> -#include <QtQuick/private/qsgdistancefieldglyphnode_p.h> -#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h> -#include <QtQuick/private/qsgatlastexture_p.h> -#include <QtQuick/private/qsgrenderloop_p.h> -#include <QtQuick/private/qsgdefaultlayer_p.h> - #include <QtQuick/private/qsgtexture_p.h> #include <QtQuick/private/qquickpixmapcache_p.h> +#include <QtQuick/private/qsgadaptationlayer_p.h> #include <QGuiApplication> #include <QScreen> -#include <QOpenGLContext> #include <QQuickWindow> -#include <QtGui/qopenglframebufferobject.h> #include <private/qqmlglobal_p.h> @@ -69,8 +55,6 @@ #include <private/qobject_p.h> #include <qmutex.h> -DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) - /* Comments about this class from Gunnar: @@ -114,31 +98,9 @@ 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") -class QSGContextPrivate : public QObjectPrivate +bool qsg_useConsistentTiming() { -public: - QSGContextPrivate() - : antialiasingMethod(QSGContext::UndecidedAntialiasing) - , distanceFieldDisabled(qmlDisableDistanceField()) - , distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing) - , distanceFieldAntialiasingDecided(false) - { - } - - ~QSGContextPrivate() - { - } - - QMutex mutex; - QSGContext::AntialiasingMethod antialiasingMethod; - bool distanceFieldDisabled; - QSGDistanceFieldGlyphNode::AntialiasingMode distanceFieldAntialiasing; - bool distanceFieldAntialiasingDecided; -}; - -static bool qsg_useConsistentTiming() -{ - static int use = -1; + int use = -1; if (use < 0) { use = !qEnvironmentVariableIsEmpty("QSG_FIXED_ANIMATION_STEP") && qgetenv("QSG_FIXED_ANIMATION_STEP") != "no" ? 1 : 0; @@ -274,20 +236,6 @@ public: QSGTexture *texture; }; -namespace QSGMultisampleAntialiasing { - class ImageNode : public QSGDefaultImageNode { - public: - void setAntialiasing(bool) { } - }; - - - class RectangleNode : public QSGDefaultRectangleNode { - public: - void setAntialiasing(bool) { } - }; -} - - /*! \class QSGContext @@ -300,92 +248,16 @@ namespace QSGMultisampleAntialiasing { */ QSGContext::QSGContext(QObject *parent) : - QObject(*(new QSGContextPrivate), parent) + QObject(parent) { - Q_D(QSGContext); - if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QSG_DISTANCEFIELD_ANTIALIASING"))) { - const QByteArray mode = qgetenv("QSG_DISTANCEFIELD_ANTIALIASING"); - d->distanceFieldAntialiasingDecided = true; - if (mode == "subpixel") - d->distanceFieldAntialiasing = QSGGlyphNode::HighQualitySubPixelAntialiasing; - else if (mode == "subpixel-lowq") - d->distanceFieldAntialiasing = QSGGlyphNode::LowQualitySubPixelAntialiasing; - else if (mode == "gray") - d->distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing; - } - - // Adds compatibility with Qt 5.3 and earlier's QSG_RENDER_TIMING - if (qEnvironmentVariableIsSet("QSG_RENDER_TIMING")) { - const_cast<QLoggingCategory &>(QSG_LOG_TIME_GLYPH()).setEnabled(QtDebugMsg, true); - const_cast<QLoggingCategory &>(QSG_LOG_TIME_TEXTURE()).setEnabled(QtDebugMsg, true); - const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERER()).setEnabled(QtDebugMsg, true); - const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERLOOP()).setEnabled(QtDebugMsg, true); - const_cast<QLoggingCategory &>(QSG_LOG_TIME_COMPILATION()).setEnabled(QtDebugMsg, true); - } } - QSGContext::~QSGContext() { } -QSGRenderContext *QSGContext::createRenderContext() +void QSGContext::renderContextInitialized(QSGRenderContext *) { - return new QSGRenderContext(this); -} - -void QSGContext::renderContextInitialized(QSGRenderContext *renderContext) -{ - Q_D(QSGContext); - - d->mutex.lock(); - if (d->antialiasingMethod == UndecidedAntialiasing) { - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) { - const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD"); - if (aaType == "msaa") - d->antialiasingMethod = MsaaAntialiasing; - else if (aaType == "vertex") - d->antialiasingMethod = VertexAntialiasing; - } - if (d->antialiasingMethod == UndecidedAntialiasing) { - if (renderContext->openglContext()->format().samples() > 0) - d->antialiasingMethod = MsaaAntialiasing; - else - d->antialiasingMethod = VertexAntialiasing; - } - } - - // With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless - // some value had been requested explicitly. This could not be decided - // before without a context. Now the context is ready. - if (!d->distanceFieldAntialiasingDecided) { - d->distanceFieldAntialiasingDecided = true; -#ifndef Q_OS_WIN32 - if (renderContext->openglContext()->isOpenGLES()) - d->distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing; -#endif - } - - static bool dumped = false; - if (!dumped && QSG_LOG_INFO().isDebugEnabled()) { - dumped = true; - QSurfaceFormat format = renderContext->openglContext()->format(); - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - qCDebug(QSG_LOG_INFO) << "R/G/B/A Buffers: " << format.redBufferSize() << format.greenBufferSize() << format.blueBufferSize() << format.alphaBufferSize(); - qCDebug(QSG_LOG_INFO) << "Depth Buffer: " << format.depthBufferSize(); - qCDebug(QSG_LOG_INFO) << "Stencil Buffer: " << format.stencilBufferSize(); - qCDebug(QSG_LOG_INFO) << "Samples: " << format.samples(); - qCDebug(QSG_LOG_INFO) << "GL_VENDOR: " << (const char *) funcs->glGetString(GL_VENDOR); - qCDebug(QSG_LOG_INFO) << "GL_RENDERER: " << (const char *) funcs->glGetString(GL_RENDERER); - qCDebug(QSG_LOG_INFO) << "GL_VERSION: " << (const char *) funcs->glGetString(GL_VERSION); - QSet<QByteArray> exts = renderContext->openglContext()->extensions(); - QByteArray all; foreach (const QByteArray &e, exts) all += ' ' + e; - qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData(); - qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << renderContext->maxTextureSize(); - qCDebug(QSG_LOG_INFO) << "Debug context: " << format.testOption(QSurfaceFormat::DebugContext); - } - - d->mutex.unlock(); } void QSGContext::renderContextInvalidated(QSGRenderContext *) @@ -396,9 +268,9 @@ void QSGContext::renderContextInvalidated(QSGRenderContext *) /*! Convenience factory function for creating a colored rectangle with the given geometry. */ -QSGRectangleNode *QSGContext::createRectangleNode(const QRectF &rect, const QColor &c) +QSGInternalRectangleNode *QSGContext::createInternalRectangleNode(const QRectF &rect, const QColor &c) { - QSGRectangleNode *node = createRectangleNode(); + QSGInternalRectangleNode *node = createInternalRectangleNode(); node->setRect(rect); node->setColor(c); node->update(); @@ -406,139 +278,76 @@ QSGRectangleNode *QSGContext::createRectangleNode(const QRectF &rect, const QCol } /*! - Factory function for scene graph backends of the Rectangle element. - */ -QSGRectangleNode *QSGContext::createRectangleNode() -{ - Q_D(QSGContext); - return d->antialiasingMethod == MsaaAntialiasing - ? new QSGMultisampleAntialiasing::RectangleNode - : new QSGDefaultRectangleNode; -} - -/*! - Factory function for scene graph backends of the Image element. - */ -QSGImageNode *QSGContext::createImageNode() -{ - Q_D(QSGContext); - return d->antialiasingMethod == MsaaAntialiasing - ? new QSGMultisampleAntialiasing::ImageNode - : new QSGDefaultImageNode; -} - -/*! - Factory function for scene graph backends of Painter elements + Creates a new shader effect helper instance. This function is called on the + gui thread, unlike the others. This is necessary in order to provide + adaptable, backend-specific shader effect functionality to the gui thread too. */ -QSGPainterNode *QSGContext::createPainterNode(QQuickPaintedItem *item) +QSGGuiThreadShaderEffectManager *QSGContext::createGuiThreadShaderEffectManager() { - return new QSGDefaultPainterNode(item); + return nullptr; } /*! - Factory function for scene graph backends of the Text elements; + Creates a new shader effect node. The default of returning nullptr is + valid as long as the backend does not claim SupportsShaderEffectNode or + ignoring ShaderEffect elements is acceptable. */ -QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) +QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *, QSGGuiThreadShaderEffectManager *) { - Q_D(QSGContext); - - if (d->distanceFieldDisabled || preferNativeGlyphNode) { - return new QSGDefaultGlyphNode; - } else { - QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc); - node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing); - return node; - } + return nullptr; } /*! - * Factory function for scene graph backends of the QStyle stylable elements. Returns a - * null pointer if the backend doesn't provide its own node type. - */ -QSGNinePatchNode *QSGContext::createNinePatchNode() -{ - return 0; -} - -/*! - Factory function for scene graph backends of layers. + Creates a new animation driver. */ -QSGLayer *QSGContext::createLayer(QSGRenderContext *renderContext) -{ - return new QSGDefaultLayer(renderContext); -} - -QSurfaceFormat QSGContext::defaultSurfaceFormat() const +QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent) { - QSurfaceFormat format = QSurfaceFormat::defaultFormat(); - static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); - static bool useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER"); - static bool enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG"); - format.setDepthBufferSize(useDepth ? 24 : 0); - format.setStencilBufferSize(useStencil ? 8 : 0); - if (enableDebug) - format.setOption(QSurfaceFormat::DebugContext); - if (QQuickWindow::hasDefaultAlphaBuffer()) - format.setAlphaBufferSize(8); - format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - return format; + return new QSGAnimationDriver(parent); } -/*! - Returns the minimum supported framebuffer object size. - */ - QSize QSGContext::minimumFBOSize() const { return QSize(1, 1); } - - /*! - Sets whether or not the scene graph should use the distance field technique to render text - */ -void QSGContext::setDistanceFieldEnabled(bool enabled) + Returns a pointer to the (presumably) global renderer interface. + + \note This function may be called on the gui thread in order to get access + to QSGRendererInterface::graphicsApi() and other getters. + + \note it is expected that the simple queries (graphicsApi, shaderType, + etc.) are available regardless of the render context validity (i.e. + scenegraph status). This does not apply to engine-specific getters like + getResource(). In the end this means that this function must always return + a valid object in subclasses, even when renderContext->isValid() is false. + The typical pattern is to implement the QSGRendererInterface in the + QSGContext or QSGRenderContext subclass itself, whichever is more suitable. + */ +QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderContext) { - d_func()->distanceFieldDisabled = !enabled; + Q_UNUSED(renderContext); + qWarning("QSGRendererInterface not implemented"); + return nullptr; } - -/*! - Returns true if the scene graph uses the distance field technique to render text - */ -bool QSGContext::isDistanceFieldEnabled() const +QSGRenderContext::QSGRenderContext(QSGContext *context) + : m_sg(context) + , m_distanceFieldCacheManager(0) { - return !d_func()->distanceFieldDisabled; } - - -/*! - Creates a new animation driver. - */ - -QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent) +QSGRenderContext::~QSGRenderContext() { - return new QSGAnimationDriver(parent); } -QSGRenderContext::QSGRenderContext(QSGContext *context) - : m_gl(0) - , m_sg(context) - , m_atlasManager(0) - , m_depthStencilManager(0) - , m_distanceFieldCacheManager(0) - , m_maxTextureSize(0) - , m_brokenIBOs(false) - , m_serializedRender(false) - , m_attachToGLContext(true) +void QSGRenderContext::initialize(void *context) { + Q_UNUSED(context); } -QSGRenderContext::~QSGRenderContext() +void QSGRenderContext::invalidate() { - invalidate(); } void QSGRenderContext::endSync() @@ -547,49 +356,14 @@ void QSGRenderContext::endSync() m_texturesToDelete.clear(); } -static QBasicMutex qsg_framerender_mutex; - -void QSGRenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId) -{ - if (m_serializedRender) - qsg_framerender_mutex.lock(); - - renderer->renderScene(fboId); - - if (m_serializedRender) - qsg_framerender_mutex.unlock(); - -} - /*! Factory function for scene graph backends of the distance-field glyph cache. */ -QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &font) -{ - if (!m_distanceFieldCacheManager) - m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager; - - QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font); - if (!cache) { - cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font); - m_distanceFieldCacheManager->insertCache(font, cache); - } - - return cache; -} - -void QSGRenderContext::setAttachToGLContext(bool attach) +QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &) { - Q_ASSERT(!isValid()); - m_attachToGLContext = attach; + return nullptr; } -#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext" - -QSGRenderContext *QSGRenderContext::from(QOpenGLContext *context) -{ - return qobject_cast<QSGRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>()); -} void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) { @@ -598,178 +372,18 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) } /*! - Initializes the scene graph render context with the GL context \a context. This also - emits the ready() signal so that the QML graph can start building scene graph nodes. - */ -void QSGRenderContext::initialize(QOpenGLContext *context) -{ - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); - - // Sanity check the surface format, in case it was overridden by the application - QSurfaceFormat requested = m_sg->defaultSurfaceFormat(); - QSurfaceFormat actual = context->format(); - if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0) - qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors"); - if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0) - qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors"); - - if (!m_atlasManager) - m_atlasManager = new QSGAtlasTexture::Manager(); - - Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!"); - m_gl = context; - if (m_attachToGLContext) { - Q_ASSERT(!context->property(QSG_RENDERCONTEXT_PROPERTY).isValid()); - context->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this)); - } - m_sg->renderContextInitialized(this); - -#ifdef Q_OS_LINUX - const char *vendor = (const char *) funcs->glGetString(GL_VENDOR); - if (vendor && strstr(vendor, "nouveau")) - m_brokenIBOs = true; - const char *renderer = (const char *) funcs->glGetString(GL_RENDERER); - if (renderer && strstr(renderer, "llvmpipe")) - m_serializedRender = true; - if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16")) - m_brokenIBOs = true; -#endif - - emit initialized(); -} - -void QSGRenderContext::invalidate() -{ - if (!m_gl) - return; - - qDeleteAll(m_texturesToDelete); - m_texturesToDelete.clear(); - - qDeleteAll(m_textures); - m_textures.clear(); - - /* The cleanup of the atlas textures is a bit intriguing. - As part of the cleanup in the threaded render loop, we - do: - 1. call this function - 2. call QCoreApp::sendPostedEvents() to immediately process - any pending deferred deletes. - 3. delete the GL context. - - As textures need the atlas manager while cleaning up, the - manager needs to be cleaned up after the textures, so - we post a deleteLater here at the very bottom so it gets - deferred deleted last. - - Another alternative would be to use a QPointer in - QSGAtlasTexture::Texture, but this seemed simpler. - */ - m_atlasManager->invalidate(); - m_atlasManager->deleteLater(); - m_atlasManager = 0; - - // The following piece of code will read/write to the font engine's caches, - // potentially from different threads. However, this is safe because this - // code is only called from QQuickWindow's shutdown which is called - // only when the GUI is blocked, and multiple threads will call it in - // sequence. (see qsgdefaultglyphnode_p.cpp's init()) - for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(), - end = m_fontEnginesToClean.constEnd(); it != end; ++it) { - (*it)->clearGlyphCache(m_gl); - if (!(*it)->ref.deref()) - delete *it; - } - m_fontEnginesToClean.clear(); - - delete m_depthStencilManager; - m_depthStencilManager = 0; - - delete m_distanceFieldCacheManager; - m_distanceFieldCacheManager = 0; - - if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) - m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); - m_gl = 0; - - m_sg->renderContextInvalidated(this); - emit invalidated(); -} - -/*! - Returns a shared pointer to a depth stencil buffer that can be used with \a fbo. - */ -QSharedPointer<QSGDepthStencilBuffer> QSGRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo) -{ - if (!m_gl) - return QSharedPointer<QSGDepthStencilBuffer>(); - QSGDepthStencilBufferManager *manager = depthStencilBufferManager(); - QSGDepthStencilBuffer::Format format; - format.size = fbo->size(); - format.samples = fbo->format().samples(); - format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment; - QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format); - if (buffer.isNull()) { - buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format)); - manager->insertBuffer(buffer); - } - return buffer; -} - -/*! - Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom - implementations of \l depthStencilBufferForFbo(). - */ -QSGDepthStencilBufferManager *QSGRenderContext::depthStencilBufferManager() -{ - if (!m_gl) - return 0; - if (!m_depthStencilManager) - m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl); - return m_depthStencilManager; -} - - -/*! Factory function for texture objects. If \a image is a valid image, the QSGTexture::setImage function will be called with \a image as argument. */ -QSGTexture *QSGRenderContext::createTexture(const QImage &image, uint flags) const -{ - bool atlas = flags & CreateTexture_Atlas; - bool mipmap = flags & CreateTexture_Mipmap; - bool alpha = flags & CreateTexture_Alpha; - - // The atlas implementation is only supported from the render thread and - // does not support mipmaps. - if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) { - QSGTexture *t = m_atlasManager->create(image, alpha); - if (t) - return t; - } - - QSGPlainTexture *texture = new QSGPlainTexture(); - texture->setImage(image); - if (texture->hasAlphaChannel() && !alpha) - texture->setHasAlphaChannel(false); - - return texture; -} - /*! Factory function for the scene graph renderers. The renderers are used for the toplevel renderer and once for every QQuickShaderEffectSource used in the QML scene. */ -QSGRenderer *QSGRenderContext::createRenderer() -{ - return new QSGBatchRenderer::Renderer(this); -} QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window) { @@ -799,41 +413,6 @@ void QSGRenderContext::textureFactoryDestroyed(QObject *o) m_mutex.unlock(); } -/*! - Compile \a shader, optionally using \a vertexCode and \a fragmentCode as - replacement for the source code supplied by \a shader. - - If \a vertexCode or \a fragmentCode is supplied, the caller is responsible - for setting up attribute bindings. - - \a material is supplied in case the implementation needs to take the - material flags into account. - */ - -void QSGRenderContext::compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode, const char *fragmentCode) -{ - Q_UNUSED(material); - if (vertexCode || fragmentCode) { - Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0, - "QSGRenderContext::compile()", - "materials with custom compile step cannot have custom vertex/fragment code"); - QOpenGLShaderProgram *p = shader->program(); - p->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader()); - p->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader()); - p->link(); - if (!p->isLinked()) - qWarning() << "shader compilation failed:" << endl << p->log(); - } else { - shader->compile(); - } -} - -void QSGRenderContext::initialize(QSGMaterialShader *shader) -{ - shader->program()->bind(); - shader->initialize(); -} - #include "qsgcontext.moc" QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 38d0cdaccc..6b9db105e7 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -53,6 +53,7 @@ #include <QtCore/QObject> #include <QtCore/qabstractanimation.h> +#include <QtCore/QMutex> #include <QtGui/QImage> #include <QtGui/QSurfaceFormat> @@ -61,36 +62,32 @@ #include <private/qrawfont_p.h> #include <QtQuick/qsgnode.h> -#include <QtQuick/private/qsgdepthstencilbuffer_p.h> QT_BEGIN_NAMESPACE -namespace QSGAtlasTexture { - class Manager; -} - class QSGContextPrivate; -class QSGRectangleNode; -class QSGImageNode; +class QSGInternalRectangleNode; +class QSGInternalImageNode; class QSGPainterNode; class QSGGlyphNode; -class QSGNinePatchNode; class QSGRenderer; class QSGDistanceFieldGlyphCache; class QQuickWindow; class QSGTexture; class QSGMaterial; -class QSGMaterialShader; class QSGRenderLoop; class QSGLayer; - -class QOpenGLContext; -class QOpenGLFramebufferObject; - class QQuickTextureFactory; class QSGDistanceFieldGlyphCacheManager; class QSGContext; class QQuickPaintedItem; +class QSGRendererInterface; +class QSGShaderEffectNode; +class QSGGuiThreadShaderEffectManager; +class QSGRectangleNode; +class QSGImageNode; +class QSGNinePatchNode; +class QSGSpriteNode; Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP) Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION) @@ -112,39 +109,28 @@ public: }; QSGRenderContext(QSGContext *context); - ~QSGRenderContext(); + virtual ~QSGRenderContext(); - QOpenGLContext *openglContext() const { return m_gl; } QSGContext *sceneGraphContext() const { return m_sg; } - virtual bool isValid() const { return m_gl; } + virtual bool isValid() const { return true; } - virtual void initialize(QOpenGLContext *context); + virtual void initialize(void *context); virtual void invalidate(); - - virtual void renderNextFrame(QSGRenderer *renderer, GLuint fboId); + virtual void renderNextFrame(QSGRenderer *renderer, uint fboId) = 0; virtual void endSync(); - virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo); - QSGDepthStencilBufferManager *depthStencilBufferManager(); - virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font); QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window); - virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const; + virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const = 0; + virtual QSGRenderer *createRenderer() = 0; - virtual QSGRenderer *createRenderer(); + virtual void setAttachToGraphicsContext(bool attach) { Q_UNUSED(attach); } - virtual void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); - virtual void initialize(QSGMaterialShader *shader); + virtual int maxTextureSize() const = 0; - void setAttachToGLContext(bool attach); void registerFontengineForCleanup(QFontEngine *engine); - static QSGRenderContext *from(QOpenGLContext *context); - - bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; } - int maxTextureSize() const { return m_maxTextureSize; } - Q_SIGNALS: void initialized(); void invalidated(); @@ -153,29 +139,21 @@ public Q_SLOTS: void textureFactoryDestroyed(QObject *o); protected: - QOpenGLContext *m_gl; - QSGContext *m_sg; + // Hold m_sg with QPointer in the rare case it gets deleted before us. + QPointer<QSGContext> m_sg; QMutex m_mutex; QHash<QQuickTextureFactory *, QSGTexture *> m_textures; QSet<QSGTexture *> m_texturesToDelete; - QSGAtlasTexture::Manager *m_atlasManager; - - QSGDepthStencilBufferManager *m_depthStencilManager; QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager; QSet<QFontEngine *> m_fontEnginesToClean; - int m_maxTextureSize; - bool m_brokenIBOs; - bool m_serializedRender; - bool m_attachToGLContext; }; class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject { Q_OBJECT - Q_DECLARE_PRIVATE(QSGContext) public: enum AntialiasingMethod { @@ -185,30 +163,40 @@ public: }; explicit QSGContext(QObject *parent = 0); - ~QSGContext(); + virtual ~QSGContext(); virtual void renderContextInitialized(QSGRenderContext *renderContext); virtual void renderContextInvalidated(QSGRenderContext *renderContext); - virtual QSGRenderContext *createRenderContext(); - - QSGRectangleNode *createRectangleNode(const QRectF &rect, const QColor &c); - 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 QSGRenderContext *createRenderContext() = 0; + + QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c); + virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0; + virtual QSGInternalImageNode *createInternalImageNode() = 0; + virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0; + virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0; + virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0; + virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager(); + virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, + QSGGuiThreadShaderEffectManager *mgr); +#if QT_CONFIG(quick_sprite) + virtual QSGSpriteNode *createSpriteNode() = 0; +#endif virtual QAnimationDriver *createAnimationDriver(QObject *parent); virtual QSize minimumFBOSize() const; - virtual QSurfaceFormat defaultSurfaceFormat() const; + virtual QSurfaceFormat defaultSurfaceFormat() const = 0; - void setDistanceFieldEnabled(bool enabled); - bool isDistanceFieldEnabled() const; + virtual QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext); + + virtual QSGRectangleNode *createRectangleNode() = 0; + virtual QSGImageNode *createImageNode() = 0; + virtual QSGNinePatchNode *createNinePatchNode() = 0; static QSGContext *createDefaultContext(); static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); static QSGRenderLoop *createWindowManager(); + + static void setBackend(const QString &backend); }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp index f5c2b6880b..3751891455 100644 --- a/src/quick/scenegraph/qsgcontextplugin.cpp +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -43,8 +43,16 @@ #include <QtCore/private/qfactoryloader_p.h> #include <QtCore/qlibraryinfo.h> +// Built-in adaptations +#include <QtQuick/private/qsgsoftwareadaptation_p.h> +#ifndef QT_NO_OPENGL +#include <QtQuick/private/qsgdefaultcontext_p.h> +#endif + QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO) + QSGContextPlugin::QSGContextPlugin(QObject *parent) : QObject(parent) { @@ -59,61 +67,109 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QSGContextFactoryInterface_iid, QLatin1String("/scenegraph"))) #endif -struct QSGAdaptionPluginData +struct QSGAdaptationBackendData { - QSGAdaptionPluginData() - : tried(false) - , factory(0) - { - } - - ~QSGAdaptionPluginData() - { - } + QSGAdaptationBackendData(); bool tried; QSGContextFactoryInterface *factory; - QString deviceName; + QString name; + QSGContextFactoryInterface::Flags flags; + + QVector<QSGContextFactoryInterface *> builtIns; + + QString quickWindowBackendRequest; }; -Q_GLOBAL_STATIC(QSGAdaptionPluginData, qsg_adaptation_data) +QSGAdaptationBackendData::QSGAdaptationBackendData() + : tried(false) + , factory(nullptr) + , flags(0) +{ + // Fill in the table with the built-in adaptations. + builtIns.append(new QSGSoftwareAdaptation); +} + +Q_GLOBAL_STATIC(QSGAdaptationBackendData, qsg_adaptation_data) + +// This only works when the backend is loaded (contextFactory() was called), +// otherwise the return value is 0. +// +// Note that the default (OpenGL) implementation always results in 0, custom flags +// can only be returned from the other (either compiled-in or plugin-based) backends. +QSGContextFactoryInterface::Flags qsg_backend_flags() +{ + return qsg_adaptation_data()->flags; +} -QSGAdaptionPluginData *contextFactory() +QSGAdaptationBackendData *contextFactory() { - QSGAdaptionPluginData *plugin = qsg_adaptation_data(); - if (!plugin->tried) { + QSGAdaptationBackendData *backendData = qsg_adaptation_data(); + + if (!backendData->tried) { + backendData->tried = true; - plugin->tried = true; const QStringList args = QGuiApplication::arguments(); - QString device; + QString requestedBackend = backendData->quickWindowBackendRequest; // empty or set via QQuickWindow::setBackend() + for (int index = 0; index < args.count(); ++index) { if (args.at(index).startsWith(QLatin1String("--device="))) { - device = args.at(index).mid(9); + requestedBackend = args.at(index).mid(9); break; } } - if (device.isEmpty()) - device = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE")); -#ifndef QT_NO_LIBRARY - if (!device.isEmpty()) { - const int index = loader()->indexOf(device); - if (index != -1) - plugin->factory = qobject_cast<QSGContextFactoryInterface*>(loader()->instance(index)); - plugin->deviceName = device; -#ifndef QT_NO_DEBUG - if (!plugin->factory) { - qWarning("Could not create scene graph context for device '%s'" - " - check that plugins are installed correctly in %s", - qPrintable(device), - qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath))); - } + if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QMLSCENE_DEVICE")) + requestedBackend = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE")); + + // A modern alternative. Scenegraph adaptations can represent backends + // for different graphics APIs as well, instead of being specific to + // some device or platform. + if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QT_QUICK_BACKEND")) + requestedBackend = QString::fromLocal8Bit(qgetenv("QT_QUICK_BACKEND")); + +#ifdef QT_NO_OPENGL + // If this is a build without OpenGL, and no backend has been set + // default to the software renderer + if (requestedBackend.isEmpty()) + requestedBackend = QString::fromLocal8Bit("software"); #endif - } + if (!requestedBackend.isEmpty()) { + qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend; + + // First look for a built-in adaptation. + for (QSGContextFactoryInterface *builtInBackend : qAsConst(backendData->builtIns)) { + if (builtInBackend->keys().contains(requestedBackend)) { + backendData->factory = builtInBackend; + backendData->name = requestedBackend; + backendData->flags = backendData->factory->flags(requestedBackend); + break; + } + } + + // Then try the plugins. + if (!backendData->factory) { +#ifndef QT_NO_LIBRARY + const int index = loader()->indexOf(requestedBackend); + if (index != -1) + backendData->factory = qobject_cast<QSGContextFactoryInterface*>(loader()->instance(index)); + if (backendData->factory) { + backendData->name = requestedBackend; + backendData->flags = backendData->factory->flags(requestedBackend); + } + if (!backendData->factory) { + qWarning("Could not create scene graph context for backend '%s'" + " - check that plugins are installed correctly in %s", + qPrintable(requestedBackend), + qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath))); + } + } #endif // QT_NO_LIBRARY + } } - return plugin; + + return backendData; } @@ -126,10 +182,14 @@ QSGAdaptionPluginData *contextFactory() */ QSGContext *QSGContext::createDefaultContext() { - QSGAdaptionPluginData *plugin = contextFactory(); - if (plugin->factory) - return plugin->factory->create(plugin->deviceName); - return new QSGContext(); + QSGAdaptationBackendData *backendData = contextFactory(); + if (backendData->factory) + return backendData->factory->create(backendData->name); +#ifndef QT_NO_OPENGL + return new QSGDefaultContext(); +#else + return nullptr; +#endif } @@ -143,9 +203,9 @@ QSGContext *QSGContext::createDefaultContext() QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &image) { - QSGAdaptionPluginData *plugin = contextFactory(); - if (plugin->factory) - return plugin->factory->createTextureFactoryFromImage(image); + QSGAdaptationBackendData *backendData = contextFactory(); + if (backendData->factory) + return backendData->factory->createTextureFactoryFromImage(image); return 0; } @@ -157,14 +217,19 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im QSGRenderLoop *QSGContext::createWindowManager() { - QSGAdaptionPluginData *plugin = contextFactory(); - if (plugin->factory) - return plugin->factory->createWindowManager(); + QSGAdaptationBackendData *backendData = contextFactory(); + if (backendData->factory) + return backendData->factory->createWindowManager(); return 0; } +void QSGContext::setBackend(const QString &backend) +{ + QSGAdaptationBackendData *backendData = qsg_adaptation_data(); + if (backendData->tried) + qWarning("Scenegraph already initialized, setBackend() request ignored"); - - + backendData->quickWindowBackendRequest = backend; +} QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h index ca326ead07..08c3d21408 100644 --- a/src/quick/scenegraph/qsgcontextplugin_p.h +++ b/src/quick/scenegraph/qsgcontextplugin_p.h @@ -64,12 +64,20 @@ class QSGRenderLoop; struct Q_QUICK_PRIVATE_EXPORT QSGContextFactoryInterface : public QFactoryInterface { + enum Flag { + SupportsShaderEffectNode = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + virtual QSGContext *create(const QString &key) const = 0; + virtual Flags flags(const QString &key) const = 0; virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image) = 0; virtual QSGRenderLoop *createWindowManager() = 0; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGContextFactoryInterface::Flags) + #define QSGContextFactoryInterface_iid \ "org.qt-project.Qt.QSGContextFactoryInterface" Q_DECLARE_INTERFACE(QSGContextFactoryInterface, QSGContextFactoryInterface_iid) @@ -83,7 +91,6 @@ public: virtual ~QSGContextPlugin(); virtual QStringList keys() const = 0; - virtual QSGContext *create(const QString &key) const = 0; virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) { return 0; } virtual QSGRenderLoop *createWindowManager() { return 0; } diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp new file mode 100644 index 0000000000..405f1d86a4 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultcontext_p.h" + +#include <QtQuick/private/qsgdistancefieldutil_p.h> +#include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h> +#include <QtQuick/private/qsgdefaultinternalimagenode_p.h> +#include <QtQuick/private/qsgdefaultpainternode_p.h> +#include <QtQuick/private/qsgdefaultglyphnode_p.h> +#include <QtQuick/private/qsgdistancefieldglyphnode_p.h> +#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h> +#include <QtQuick/private/qsgrenderloop_p.h> +#include <QtQuick/private/qsgdefaultlayer_p.h> +#include <QtQuick/private/qsgdefaultrendercontext_p.h> +#include <QtQuick/private/qsgdefaultrectanglenode_p.h> +#include <QtQuick/private/qsgdefaultimagenode_p.h> +#include <QtQuick/private/qsgdefaultninepatchnode_p.h> +#if QT_CONFIG(quick_sprite) +#include <QtQuick/private/qsgdefaultspritenode_p.h> +#endif + +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFramebufferObject> + +#include <QtQuick/QQuickWindow> + +#include <private/qqmlglobal_p.h> + +QT_BEGIN_NAMESPACE + +namespace QSGMultisampleAntialiasing { + class ImageNode : public QSGDefaultInternalImageNode { + public: + void setAntialiasing(bool) { } + }; + + + class RectangleNode : public QSGDefaultInternalRectangleNode { + public: + void setAntialiasing(bool) { } + }; +} + +DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) + +QSGDefaultContext::QSGDefaultContext(QObject *parent) + : QSGContext (parent) + , m_antialiasingMethod(QSGContext::UndecidedAntialiasing) + , m_distanceFieldDisabled(qmlDisableDistanceField()) + , m_distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing) + , m_distanceFieldAntialiasingDecided(false) +{ + if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QSG_DISTANCEFIELD_ANTIALIASING"))) { + const QByteArray mode = qgetenv("QSG_DISTANCEFIELD_ANTIALIASING"); + m_distanceFieldAntialiasingDecided = true; + if (mode == "subpixel") + m_distanceFieldAntialiasing = QSGGlyphNode::HighQualitySubPixelAntialiasing; + else if (mode == "subpixel-lowq") + m_distanceFieldAntialiasing = QSGGlyphNode::LowQualitySubPixelAntialiasing; + else if (mode == "gray") + m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing; + } + + // Adds compatibility with Qt 5.3 and earlier's QSG_RENDER_TIMING + if (qEnvironmentVariableIsSet("QSG_RENDER_TIMING")) { + const_cast<QLoggingCategory &>(QSG_LOG_TIME_GLYPH()).setEnabled(QtDebugMsg, true); + const_cast<QLoggingCategory &>(QSG_LOG_TIME_TEXTURE()).setEnabled(QtDebugMsg, true); + const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERER()).setEnabled(QtDebugMsg, true); + const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERLOOP()).setEnabled(QtDebugMsg, true); + const_cast<QLoggingCategory &>(QSG_LOG_TIME_COMPILATION()).setEnabled(QtDebugMsg, true); + } +} + +QSGDefaultContext::~QSGDefaultContext() +{ + +} + +void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext) +{ + m_mutex.lock(); + + auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(renderContext); + if (m_antialiasingMethod == UndecidedAntialiasing) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) { + const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD"); + if (aaType == "msaa") + m_antialiasingMethod = MsaaAntialiasing; + else if (aaType == "vertex") + m_antialiasingMethod = VertexAntialiasing; + } + if (m_antialiasingMethod == UndecidedAntialiasing) { + if (openglRenderContext->openglContext()->format().samples() > 0) + m_antialiasingMethod = MsaaAntialiasing; + else + m_antialiasingMethod = VertexAntialiasing; + } + } + + // With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless + // some value had been requested explicitly. This could not be decided + // before without a context. Now the context is ready. + if (!m_distanceFieldAntialiasingDecided) { + m_distanceFieldAntialiasingDecided = true; +#ifndef Q_OS_WIN32 + if (openglRenderContext->openglContext()->isOpenGLES()) + m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing; +#endif + } + + static bool dumped = false; + if (!dumped && QSG_LOG_INFO().isDebugEnabled()) { + dumped = true; + QSurfaceFormat format = openglRenderContext->openglContext()->format(); + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + qCDebug(QSG_LOG_INFO) << "R/G/B/A Buffers: " << format.redBufferSize() << format.greenBufferSize() << format.blueBufferSize() << format.alphaBufferSize(); + qCDebug(QSG_LOG_INFO) << "Depth Buffer: " << format.depthBufferSize(); + qCDebug(QSG_LOG_INFO) << "Stencil Buffer: " << format.stencilBufferSize(); + qCDebug(QSG_LOG_INFO) << "Samples: " << format.samples(); + qCDebug(QSG_LOG_INFO) << "GL_VENDOR: " << (const char *) funcs->glGetString(GL_VENDOR); + qCDebug(QSG_LOG_INFO) << "GL_RENDERER: " << (const char *) funcs->glGetString(GL_RENDERER); + qCDebug(QSG_LOG_INFO) << "GL_VERSION: " << (const char *) funcs->glGetString(GL_VERSION); + QSet<QByteArray> exts = openglRenderContext->openglContext()->extensions(); + QByteArray all; + for (const QByteArray &e : qAsConst(exts)) + all += ' ' + e; + qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData(); + qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << openglRenderContext->maxTextureSize(); + qCDebug(QSG_LOG_INFO) << "Debug context: " << format.testOption(QSurfaceFormat::DebugContext); + } + + m_mutex.unlock(); +} + +void QSGDefaultContext::renderContextInvalidated(QSGRenderContext *) +{ +} + +QSGRenderContext *QSGDefaultContext::createRenderContext() +{ + return new QSGDefaultRenderContext(this); +} + +QSGInternalRectangleNode *QSGDefaultContext::createInternalRectangleNode() +{ + return m_antialiasingMethod == MsaaAntialiasing + ? new QSGMultisampleAntialiasing::RectangleNode + : new QSGDefaultInternalRectangleNode; +} + +QSGInternalImageNode *QSGDefaultContext::createInternalImageNode() +{ + return m_antialiasingMethod == MsaaAntialiasing + ? new QSGMultisampleAntialiasing::ImageNode + : new QSGDefaultInternalImageNode; +} + +QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item) +{ + return new QSGDefaultPainterNode(item); +} + +QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) +{ + if (m_distanceFieldDisabled || preferNativeGlyphNode) { + return new QSGDefaultGlyphNode; + } else { + QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc); + node->setPreferredAntialiasingMode(m_distanceFieldAntialiasing); + return node; + } +} + +QSGLayer *QSGDefaultContext::createLayer(QSGRenderContext *renderContext) +{ + return new QSGDefaultLayer(renderContext); +} + +QSurfaceFormat QSGDefaultContext::defaultSurfaceFormat() const +{ + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); + static bool useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER"); + static bool enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG"); + format.setDepthBufferSize(useDepth ? 24 : 0); + format.setStencilBufferSize(useStencil ? 8 : 0); + if (enableDebug) + format.setOption(QSurfaceFormat::DebugContext); + if (QQuickWindow::hasDefaultAlphaBuffer()) + format.setAlphaBufferSize(8); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + return format; +} + +void QSGDefaultContext::setDistanceFieldEnabled(bool enabled) +{ + m_distanceFieldDisabled = !enabled; +} + +bool QSGDefaultContext::isDistanceFieldEnabled() const +{ + return !m_distanceFieldDisabled; +} + +QSGRendererInterface *QSGDefaultContext::rendererInterface(QSGRenderContext *renderContext) +{ + Q_UNUSED(renderContext); + return this; +} + +QSGRectangleNode *QSGDefaultContext::createRectangleNode() +{ + return new QSGDefaultRectangleNode; +} + +QSGImageNode *QSGDefaultContext::createImageNode() +{ + return new QSGDefaultImageNode; +} + +QSGNinePatchNode *QSGDefaultContext::createNinePatchNode() +{ + return new QSGDefaultNinePatchNode; +} + +#if QT_CONFIG(quick_sprite) +QSGSpriteNode *QSGDefaultContext::createSpriteNode() +{ + return new QSGDefaultSpriteNode; +} +#endif + +QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const +{ + return OpenGL; +} + +QSGRendererInterface::ShaderType QSGDefaultContext::shaderType() const +{ + return GLSL; +} + +QSGRendererInterface::ShaderCompilationTypes QSGDefaultContext::shaderCompilationType() const +{ + return RuntimeCompilation; +} + +QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() const +{ + return ShaderSourceString | ShaderSourceFile; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h new file mode 100644 index 0000000000..ab319502ef --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultcontext_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTCONTEXT_H +#define QSGDEFAULTCONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qsgcontext_p.h> +#include <QtQuick/private/qsgdistancefieldglyphnode_p.h> +#include "qsgrendererinterface.h" + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultContext : public QSGContext, public QSGRendererInterface +{ +public: + QSGDefaultContext(QObject *parent = 0); + ~QSGDefaultContext(); + + void renderContextInitialized(QSGRenderContext *renderContext) override; + void renderContextInvalidated(QSGRenderContext *) override; + QSGRenderContext *createRenderContext() override; + QSGInternalRectangleNode *createInternalRectangleNode() override; + QSGInternalImageNode *createInternalImageNode() override; + QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override; + QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override; + QSGLayer *createLayer(QSGRenderContext *renderContext) override; + QSurfaceFormat defaultSurfaceFormat() const override; + QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; + QSGRectangleNode *createRectangleNode() override; + QSGImageNode *createImageNode() override; + QSGNinePatchNode *createNinePatchNode() override; +#if QT_CONFIG(quick_sprite) + QSGSpriteNode *createSpriteNode() override; +#endif + + void setDistanceFieldEnabled(bool enabled); + bool isDistanceFieldEnabled() const; + + GraphicsApi graphicsApi() const override; + ShaderType shaderType() const override; + ShaderCompilationTypes shaderCompilationType() const override; + ShaderSourceTypes shaderSourceType() const override; + +private: + QMutex m_mutex; + QSGContext::AntialiasingMethod m_antialiasingMethod; + bool m_distanceFieldDisabled; + QSGDistanceFieldGlyphNode::AntialiasingMode m_distanceFieldAntialiasing; + bool m_distanceFieldAntialiasingDecided; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTCONTEXT_H diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index bb4a3b04d5..f0a336e229 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -508,7 +508,7 @@ bool QSGDefaultDistanceFieldGlyphCache::useTextureUploadWorkaround() const bool QSGDefaultDistanceFieldGlyphCache::createFullSizeTextures() const { - return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT; + return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT(); } int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index 082a4f8c09..b856d99bc1 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -40,59 +40,11 @@ #include "qsgdefaultglyphnode_p.h" #include "qsgdefaultglyphnode_p_p.h" -#include <qopenglshaderprogram.h> -#include <private/qfont_p.h> - QT_BEGIN_NAMESPACE -QSGDefaultGlyphNode::QSGDefaultGlyphNode() - : m_style(QQuickText::Normal) - , m_material(0) - , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) -{ - m_geometry.setDrawingMode(GL_TRIANGLES); - setGeometry(&m_geometry); -} - -QSGDefaultGlyphNode::~QSGDefaultGlyphNode() -{ - delete m_material; -} - -void QSGDefaultGlyphNode::setColor(const QColor &color) -{ - m_color = color; - if (m_material != 0) { - m_material->setColor(color); - markDirty(DirtyMaterial); - } -} - -void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) -{ - if (m_material != 0) - delete m_material; - - m_position = position; - m_glyphs = glyphs; - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("glyphs")); -#endif -} - -void QSGDefaultGlyphNode::setStyle(QQuickText::TextStyle style) -{ - if (m_style == style) - return; - m_style = style; -} - -void QSGDefaultGlyphNode::setStyleColor(const QColor &color) +void QSGDefaultGlyphNode::setMaterialColor(const QColor &color) { - if (m_styleColor == color) - return; - m_styleColor = color; + static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color); } void QSGDefaultGlyphNode::update() @@ -120,11 +72,12 @@ void QSGDefaultGlyphNode::update() m_material = material; } - m_material->setColor(m_color); + QSGTextMaskMaterial *textMaskMaterial = static_cast<QSGTextMaskMaterial *>(m_material); + textMaskMaterial->setColor(m_color); QRectF boundingRect; - m_material->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(), - &boundingRect, &m_baseLine, margins); + textMaskMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(), + &boundingRect, &m_baseLine, margins); setBoundingRect(boundingRect); setMaterial(m_material); diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index 161373306a..b001899915 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -50,6 +50,7 @@ #include <QtQuick/qquickwindow.h> #include <QtQuick/private/qsgtexture_p.h> +#include <QtQuick/private/qsgdefaultrendercontext_p.h> #include <private/qrawfont_p.h> #include <QtCore/qmath.h> @@ -445,7 +446,7 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat) if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) { m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform); fontEngine->setGlyphCache(ctx, m_glyphCache.data()); - QSGRenderContext *sg = QSGRenderContext::from(ctx); + auto sg = QSGDefaultRenderContext::from(ctx); Q_ASSERT(sg); sg->registerFontengineForCleanup(fontEngine); } diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index ea4c0ff787..0eb7a4e4bd 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -52,38 +52,15 @@ // #include <private/qsgadaptationlayer_p.h> -#include <QtQuick/qsgnode.h> +#include <private/qsgbasicglyphnode_p.h> QT_BEGIN_NAMESPACE -class QSGTextMaskMaterial; -class QSGDefaultGlyphNode: public QSGGlyphNode +class QSGDefaultGlyphNode : public QSGBasicGlyphNode { public: - QSGDefaultGlyphNode(); - virtual ~QSGDefaultGlyphNode(); - - virtual QPointF baseLine() const { return m_baseLine; } - virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs); - virtual void setColor(const QColor &color); - - virtual void setPreferredAntialiasingMode(AntialiasingMode) { } - virtual void setStyle(QQuickText::TextStyle); - virtual void setStyleColor(const QColor &); - - virtual void update(); - -protected: - QGlyphRun m_glyphs; - QPointF m_position; - QColor m_color; - QQuickText::TextStyle m_style; - QColor m_styleColor; - - QPointF m_baseLine; - QSGTextMaskMaterial *m_material; - - QSGGeometry m_geometry; + void setMaterialColor(const QColor &color) override; + void update() override; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp deleted file mode 100644 index bb4db150c0..0000000000 --- a/src/quick/scenegraph/qsgdefaultimagenode.cpp +++ /dev/null @@ -1,671 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgdefaultimagenode_p.h" -#include <private/qsgmaterialshader_p.h> - -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qmath.h> -#include <QtGui/qopenglfunctions.h> - -#include <qsgtexturematerial.h> -#include <private/qsgtexturematerial_p.h> -#include <qsgmaterial.h> - -QT_BEGIN_NAMESPACE - -namespace -{ - struct SmoothVertex - { - float x, y, u, v; - float dx, dy, du, dv; - }; - - const QSGGeometry::AttributeSet &smoothAttributeSet() - { - static QSGGeometry::Attribute data[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), - QSGGeometry::Attribute::create(1, 2, GL_FLOAT, false), - QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false), - QSGGeometry::Attribute::create(3, 2, GL_FLOAT, false) - }; - static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data }; - return attrs; - } -} - -class SmoothTextureMaterialShader : public QSGTextureMaterialShader -{ -public: - SmoothTextureMaterialShader(); - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; - -protected: - virtual void initialize(); - - int m_pixelSizeLoc; -}; - - -QSGSmoothTextureMaterial::QSGSmoothTextureMaterial() -{ - setFlag(RequiresFullMatrixExceptTranslate, true); - setFlag(Blending, true); -} - -void QSGSmoothTextureMaterial::setTexture(QSGTexture *texture) -{ - m_texture = texture; -} - -QSGMaterialType *QSGSmoothTextureMaterial::type() const -{ - static QSGMaterialType type; - return &type; -} - -QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const -{ - return new SmoothTextureMaterialShader; -} - -SmoothTextureMaterialShader::SmoothTextureMaterialShader() - : QSGTextureMaterialShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.frag")); -} - -void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) -{ - if (oldEffect == 0) { - // The viewport is constant, so set the pixel size uniform only once. - QRect r = state.viewportRect(); - program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); - } - QSGTextureMaterialShader::updateState(state, newEffect, oldEffect); -} - -char const *const *SmoothTextureMaterialShader::attributeNames() const -{ - static char const *const attributes[] = { - "vertex", - "multiTexCoord", - "vertexOffset", - "texCoordOffset", - 0 - }; - return attributes; -} - -void SmoothTextureMaterialShader::initialize() -{ - m_pixelSizeLoc = program()->uniformLocation("pixelSize"); - QSGTextureMaterialShader::initialize(); -} - -QSGDefaultImageNode::QSGDefaultImageNode() - : m_innerSourceRect(0, 0, 1, 1) - , m_subSourceRect(0, 0, 1, 1) - , m_antialiasing(false) - , m_mirror(false) - , m_dirtyGeometry(false) - , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) -{ - setMaterial(&m_materialO); - setOpaqueMaterial(&m_material); - setGeometry(&m_geometry); - -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("image")); -#endif -} - -void QSGDefaultImageNode::setTargetRect(const QRectF &rect) -{ - if (rect == m_targetRect) - return; - m_targetRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultImageNode::setInnerTargetRect(const QRectF &rect) -{ - if (rect == m_innerTargetRect) - return; - m_innerTargetRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultImageNode::setInnerSourceRect(const QRectF &rect) -{ - if (rect == m_innerSourceRect) - return; - m_innerSourceRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultImageNode::setSubSourceRect(const QRectF &rect) -{ - if (rect == m_subSourceRect) - return; - m_subSourceRect = rect; - m_dirtyGeometry = true; -} - -void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.filtering() == filtering) - return; - - m_material.setFiltering(filtering); - m_materialO.setFiltering(filtering); - m_smoothMaterial.setFiltering(filtering); - markDirty(DirtyMaterial); -} - - -void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.mipmapFiltering() == filtering) - return; - - m_material.setMipmapFiltering(filtering); - m_materialO.setMipmapFiltering(filtering); - m_smoothMaterial.setMipmapFiltering(filtering); - markDirty(DirtyMaterial); -} - -void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) -{ - if (m_material.verticalWrapMode() == wrapMode) - return; - - m_material.setVerticalWrapMode(wrapMode); - m_materialO.setVerticalWrapMode(wrapMode); - m_smoothMaterial.setVerticalWrapMode(wrapMode); - markDirty(DirtyMaterial); -} - -void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) -{ - if (m_material.horizontalWrapMode() == wrapMode) - return; - - m_material.setHorizontalWrapMode(wrapMode); - m_materialO.setHorizontalWrapMode(wrapMode); - m_smoothMaterial.setHorizontalWrapMode(wrapMode); - markDirty(DirtyMaterial); -} - - -void QSGDefaultImageNode::setTexture(QSGTexture *texture) -{ - Q_ASSERT(texture); - - m_material.setTexture(texture); - m_materialO.setTexture(texture); - m_smoothMaterial.setTexture(texture); - m_material.setFlag(QSGMaterial::Blending, texture->hasAlphaChannel()); - - markDirty(DirtyMaterial); - - // Because the texture can be a different part of the atlas, we need to update it... - m_dirtyGeometry = true; -} - -void QSGDefaultImageNode::setAntialiasing(bool antialiasing) -{ - if (antialiasing == m_antialiasing) - return; - m_antialiasing = antialiasing; - if (m_antialiasing) { - setMaterial(&m_smoothMaterial); - setOpaqueMaterial(0); - setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); - setFlag(OwnsGeometry, true); - } else { - setMaterial(&m_materialO); - setOpaqueMaterial(&m_material); - setGeometry(&m_geometry); - setFlag(OwnsGeometry, false); - } - m_dirtyGeometry = true; -} - -void QSGDefaultImageNode::setMirror(bool mirror) -{ - if (mirror == m_mirror) - return; - m_mirror = mirror; - m_dirtyGeometry = true; -} - - -void QSGDefaultImageNode::update() -{ - if (m_dirtyGeometry) - updateGeometry(); -} - -void QSGDefaultImageNode::preprocess() -{ - bool doDirty = false; - QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture()); - if (t) { - doDirty = t->updateTexture(); - if (doDirty) - updateGeometry(); - } - bool alpha = m_material.flags() & QSGMaterial::Blending; - if (m_material.texture() && alpha != m_material.texture()->hasAlphaChannel()) { - m_material.setFlag(QSGMaterial::Blending, !alpha); - doDirty = true; - } - - if (doDirty) - markDirty(DirtyMaterial); -} - -inline static bool isPowerOfTwo(int x) -{ - // Assumption: x >= 1 - return x == (x & -x); -} - -namespace { - struct X { float x, tx; }; - struct Y { float y, ty; }; -} - -static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, - quint16 bottomLeft, quint16 bottomRight) -{ - *(*indices)++ = topLeft; - *(*indices)++ = bottomLeft; - *(*indices)++ = bottomRight; - *(*indices)++ = bottomRight; - *(*indices)++ = topRight; - *(*indices)++ = topLeft; -} - -void QSGDefaultImageNode::updateGeometry() -{ - Q_ASSERT(!m_targetRect.isEmpty()); - const QSGTexture *t = m_material.texture(); - if (!t) { - QSGGeometry *g = geometry(); - g->allocate(4); - g->setDrawingMode(GL_TRIANGLE_STRIP); - memset(g->vertexData(), 0, g->sizeOfVertex() * 4); - } else { - QRectF sourceRect = t->normalizedTextureSubRect(); - - QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(), - sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(), - m_innerSourceRect.width() * sourceRect.width(), - m_innerSourceRect.height() * sourceRect.height()); - - bool hasMargins = m_targetRect != m_innerTargetRect; - - int floorLeft = qFloor(m_subSourceRect.left()); - int ceilRight = qCeil(m_subSourceRect.right()); - int floorTop = qFloor(m_subSourceRect.top()); - int ceilBottom = qCeil(m_subSourceRect.bottom()); - int hTiles = ceilRight - floorLeft; - int vTiles = ceilBottom - floorTop; - - bool hasTiles = hTiles != 1 || vTiles != 1; - bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); - - bool wrapSupported = true; - - QOpenGLContext *ctx = QOpenGLContext::currentContext(); -#ifndef QT_OPENGL_ES_2 - if (ctx->isOpenGLES()) -#endif - { - bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); - QSize size = t->textureSize(); - const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); - wrapSupported = npotSupported || !isNpot; - } - - // An image can be rendered as a single quad if: - // - There are no margins, and either: - // - the image isn't repeated - // - the source rectangle fills the entire texture so that texture wrapping can be used, - // and NPOT is supported - if (!hasMargins && (!hasTiles || (fullTexture && wrapSupported))) { - QRectF sr; - if (!fullTexture) { - sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(), - innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(), - m_subSourceRect.width() * innerSourceRect.width(), - m_subSourceRect.height() * innerSourceRect.height()); - } else { - sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop, - m_subSourceRect.width(), m_subSourceRect.height()); - } - if (m_mirror) { - qreal oldLeft = sr.left(); - sr.setLeft(sr.right()); - sr.setRight(oldLeft); - } - - if (m_antialiasing) { - QSGGeometry *g = geometry(); - Q_ASSERT(g != &m_geometry); - g->allocate(8, 14); - g->setDrawingMode(GL_TRIANGLE_STRIP); - SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); - float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) - ? m_targetRect.width() : m_targetRect.height()) * 0.5f; - float sx = float(sr.width() / m_targetRect.width()); - float sy = float(sr.height() / m_targetRect.height()); - for (int d = -1; d <= 1; d += 2) { - for (int j = 0; j < 2; ++j) { - for (int i = 0; i < 2; ++i, ++vertices) { - vertices->x = m_targetRect.x() + i * m_targetRect.width(); - vertices->y = m_targetRect.y() + j * m_targetRect.height(); - vertices->u = sr.x() + i * sr.width(); - vertices->v = sr.y() + j * sr.height(); - vertices->dx = (i == 0 ? delta : -delta) * d; - vertices->dy = (j == 0 ? delta : -delta) * d; - vertices->du = (d < 0 ? 0 : vertices->dx * sx); - vertices->dv = (d < 0 ? 0 : vertices->dy * sy); - } - } - } - Q_ASSERT(vertices - g->vertexCount() == g->vertexData()); - static const quint16 indices[] = { - 0, 4, 1, 5, 3, 7, 2, 6, 0, 4, - 4, 6, 5, 7 - }; - Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices)); - memcpy(g->indexDataAsUShort(), indices, sizeof(indices)); - } else { - m_geometry.allocate(4); - m_geometry.setDrawingMode(GL_TRIANGLE_STRIP); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); - } - } else { - int hCells = hTiles; - int vCells = vTiles; - if (m_innerTargetRect.width() == 0) - hCells = 0; - if (m_innerTargetRect.left() != m_targetRect.left()) - ++hCells; - if (m_innerTargetRect.right() != m_targetRect.right()) - ++hCells; - if (m_innerTargetRect.height() == 0) - vCells = 0; - if (m_innerTargetRect.top() != m_targetRect.top()) - ++vCells; - if (m_innerTargetRect.bottom() != m_targetRect.bottom()) - ++vCells; - QVarLengthArray<X, 32> xData(2 * hCells); - QVarLengthArray<Y, 32> yData(2 * vCells); - X *xs = xData.data(); - Y *ys = yData.data(); - - if (m_innerTargetRect.left() != m_targetRect.left()) { - xs[0].x = m_targetRect.left(); - xs[0].tx = sourceRect.left(); - xs[1].x = m_innerTargetRect.left(); - xs[1].tx = innerSourceRect.left(); - xs += 2; - } - if (m_innerTargetRect.width() != 0) { - xs[0].x = m_innerTargetRect.left(); - xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(); - ++xs; - float b = m_innerTargetRect.width() / m_subSourceRect.width(); - float a = m_innerTargetRect.x() - m_subSourceRect.x() * b; - for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) { - xs[0].x = xs[1].x = a + b * i; - xs[0].tx = innerSourceRect.right(); - xs[1].tx = innerSourceRect.left(); - xs += 2; - } - xs[0].x = m_innerTargetRect.right(); - xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); - ++xs; - } - if (m_innerTargetRect.right() != m_targetRect.right()) { - xs[0].x = m_innerTargetRect.right(); - xs[0].tx = innerSourceRect.right(); - xs[1].x = m_targetRect.right(); - xs[1].tx = sourceRect.right(); - xs += 2; - } - Q_ASSERT(xs == xData.data() + xData.size()); - if (m_mirror) { - float leftPlusRight = m_targetRect.left() + m_targetRect.right(); - int count = xData.size(); - xs = xData.data(); - for (int i = 0; i < count >> 1; ++i) - qSwap(xs[i], xs[count - 1 - i]); - for (int i = 0; i < count; ++i) - xs[i].x = leftPlusRight - xs[i].x; - } - - if (m_innerTargetRect.top() != m_targetRect.top()) { - ys[0].y = m_targetRect.top(); - ys[0].ty = sourceRect.top(); - ys[1].y = m_innerTargetRect.top(); - ys[1].ty = innerSourceRect.top(); - ys += 2; - } - if (m_innerTargetRect.height() != 0) { - ys[0].y = m_innerTargetRect.top(); - ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(); - ++ys; - float b = m_innerTargetRect.height() / m_subSourceRect.height(); - float a = m_innerTargetRect.y() - m_subSourceRect.y() * b; - for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) { - ys[0].y = ys[1].y = a + b * i; - ys[0].ty = innerSourceRect.bottom(); - ys[1].ty = innerSourceRect.top(); - ys += 2; - } - ys[0].y = m_innerTargetRect.bottom(); - ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); - ++ys; - } - if (m_innerTargetRect.bottom() != m_targetRect.bottom()) { - ys[0].y = m_innerTargetRect.bottom(); - ys[0].ty = innerSourceRect.bottom(); - ys[1].y = m_targetRect.bottom(); - ys[1].ty = sourceRect.bottom(); - ys += 2; - } - Q_ASSERT(ys == yData.data() + yData.size()); - - if (m_antialiasing) { - QSGGeometry *g = geometry(); - Q_ASSERT(g != &m_geometry); - - g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, - hCells * vCells * 6 + (hCells + vCells) * 12); - g->setDrawingMode(GL_TRIANGLES); - SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); - memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); - quint16 *indices = g->indexDataAsUShort(); - - // The deltas are how much the fuzziness can reach into the image. - // Only the border vertices are moved by the vertex shader, so the fuzziness - // can't reach further into the image than the closest interior vertices. - float leftDx = xData.at(1).x - xData.at(0).x; - float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x; - float topDy = yData.at(1).y - yData.at(0).y; - float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y; - - float leftDu = xData.at(1).tx - xData.at(0).tx; - float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx; - float topDv = yData.at(1).ty - yData.at(0).ty; - float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty; - - if (hCells == 1) { - leftDx = rightDx *= 0.5f; - leftDu = rightDu *= 0.5f; - } - if (vCells == 1) { - topDy = bottomDy *= 0.5f; - topDv = bottomDv *= 0.5f; - } - - // This delta is how much the fuzziness can reach out from the image. - float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height()) - ? m_targetRect.width() : m_targetRect.height()) * 0.5f; - - quint16 index = 0; - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - bool isTop = j == 0; - bool isBottom = j == vCells - 1; - for (int i = 0; i < hCells; ++i, xs += 2) { - bool isLeft = i == 0; - bool isRight = i == hCells - 1; - - SmoothVertex *v = vertices + index; - - quint16 topLeft = index; - for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { - v->x = xs[0].x; - v->u = xs[0].tx; - v->y = ys[0].y; - v->v = ys[0].ty; - } - - quint16 topRight = index; - for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { - v->x = xs[1].x; - v->u = xs[1].tx; - v->y = ys[0].y; - v->v = ys[0].ty; - } - - quint16 bottomLeft = index; - for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { - v->x = xs[0].x; - v->u = xs[0].tx; - v->y = ys[1].y; - v->v = ys[1].ty; - } - - quint16 bottomRight = index; - for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { - v->x = xs[1].x; - v->u = xs[1].tx; - v->y = ys[1].y; - v->v = ys[1].ty; - } - - appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); - - if (isTop) { - vertices[topLeft].dy = vertices[topRight].dy = topDy; - vertices[topLeft].dv = vertices[topRight].dv = topDv; - vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; - appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); - } - - if (isBottom) { - vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; - vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; - vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; - appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); - } - - if (isLeft) { - vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; - vertices[topLeft].du = vertices[bottomLeft].du = leftDu; - vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; - appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); - } - - if (isRight) { - vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; - vertices[topRight].du = vertices[bottomRight].du = -rightDu; - vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; - appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); - } - } - } - - Q_ASSERT(index == g->vertexCount()); - Q_ASSERT(indices - g->indexCount() == g->indexData()); - } else { - m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6); - m_geometry.setDrawingMode(GL_TRIANGLES); - QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D(); - ys = yData.data(); - for (int j = 0; j < vCells; ++j, ys += 2) { - xs = xData.data(); - for (int i = 0; i < hCells; ++i, xs += 2) { - vertices[0].x = vertices[2].x = xs[0].x; - vertices[0].tx = vertices[2].tx = xs[0].tx; - vertices[1].x = vertices[3].x = xs[1].x; - vertices[1].tx = vertices[3].tx = xs[1].tx; - - vertices[0].y = vertices[1].y = ys[0].y; - vertices[0].ty = vertices[1].ty = ys[0].ty; - vertices[2].y = vertices[3].y = ys[1].y; - vertices[2].ty = vertices[3].ty = ys[1].ty; - - vertices += 4; - } - } - - quint16 *indices = m_geometry.indexDataAsUShort(); - for (int i = 0; i < 4 * vCells * hCells; i += 4) - appendQuad(&indices, i, i + 1, i + 2, i + 3); - } - } - } - markDirty(DirtyGeometry); - m_dirtyGeometry = false; -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp new file mode 100644 index 0000000000..1d54628acd --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultinternalimagenode_p.h" +#include <private/qsgmaterialshader_p.h> +#include <private/qsgtexturematerial_p.h> +#include <QtGui/qopenglfunctions.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +class SmoothTextureMaterialShader : public QSGTextureMaterialShader +{ +public: + SmoothTextureMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +protected: + virtual void initialize(); + + int m_pixelSizeLoc; +}; + + +QSGSmoothTextureMaterial::QSGSmoothTextureMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +void QSGSmoothTextureMaterial::setTexture(QSGTexture *texture) +{ + m_texture = texture; +} + +QSGMaterialType *QSGSmoothTextureMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const +{ + return new SmoothTextureMaterialShader; +} + +SmoothTextureMaterialShader::SmoothTextureMaterialShader() + : QSGTextureMaterialShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.frag")); +} + +void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + if (oldEffect == 0) { + // The viewport is constant, so set the pixel size uniform only once. + QRect r = state.viewportRect(); + program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); + } + QSGTextureMaterialShader::updateState(state, newEffect, oldEffect); +} + +char const *const *SmoothTextureMaterialShader::attributeNames() const +{ + static char const *const attributes[] = { + "vertex", + "multiTexCoord", + "vertexOffset", + "texCoordOffset", + 0 + }; + return attributes; +} + +void SmoothTextureMaterialShader::initialize() +{ + m_pixelSizeLoc = program()->uniformLocation("pixelSize"); + QSGTextureMaterialShader::initialize(); +} + +QSGDefaultInternalImageNode::QSGDefaultInternalImageNode() +{ + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); +} + +void QSGDefaultInternalImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_materialO.setFiltering(filtering); + m_smoothMaterial.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + m_materialO.setMipmapFiltering(filtering); + m_smoothMaterial.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.verticalWrapMode() == wrapMode) + return; + + m_material.setVerticalWrapMode(wrapMode); + m_materialO.setVerticalWrapMode(wrapMode); + m_smoothMaterial.setVerticalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) +{ + if (m_material.horizontalWrapMode() == wrapMode) + return; + + m_material.setHorizontalWrapMode(wrapMode); + m_materialO.setHorizontalWrapMode(wrapMode); + m_smoothMaterial.setHorizontalWrapMode(wrapMode); + markDirty(DirtyMaterial); +} + +void QSGDefaultInternalImageNode::updateMaterialAntialiasing() +{ + if (m_antialiasing) { + setMaterial(&m_smoothMaterial); + setOpaqueMaterial(0); + } else { + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); + } +} + +void QSGDefaultInternalImageNode::setMaterialTexture(QSGTexture *texture) +{ + m_material.setTexture(texture); + m_materialO.setTexture(texture); + m_smoothMaterial.setTexture(texture); +} + +QSGTexture *QSGDefaultInternalImageNode::materialTexture() const +{ + return m_material.texture(); +} + +bool QSGDefaultInternalImageNode::updateMaterialBlending() +{ + const bool alpha = m_material.flags() & QSGMaterial::Blending; + if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) { + m_material.setFlag(QSGMaterial::Blending, !alpha); + return true; + } + return false; +} + +inline static bool isPowerOfTwo(int x) +{ + // Assumption: x >= 1 + return x == (x & -x); +} + +bool QSGDefaultInternalImageNode::supportsWrap(const QSize &size) const +{ + bool wrapSupported = true; + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); +#ifndef QT_OPENGL_ES_2 + if (ctx->isOpenGLES()) +#endif + { + bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); + const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); + wrapSupported = npotSupported || !isNpot; + } + + return wrapSupported; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h index 2d8abc1d35..1fc7834bd1 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h @@ -38,8 +38,8 @@ ****************************************************************************/ -#ifndef QSGDEFAULTIMAGENODE_P_H -#define QSGDEFAULTIMAGENODE_P_H +#ifndef QSGDEFAULTINTERNALIMAGENODE_P_H +#define QSGDEFAULTINTERNALIMAGENODE_P_H // // W A R N I N G @@ -53,6 +53,7 @@ // #include <private/qsgadaptationlayer_p.h> +#include <private/qsgbasicinternalimagenode_p.h> #include <QtQuick/qsgtexturematerial.h> QT_BEGIN_NAMESPACE @@ -65,47 +66,30 @@ public: void setTexture(QSGTexture *texture); protected: - virtual QSGMaterialType *type() const; - virtual QSGMaterialShader *createShader() const; + QSGMaterialType *type() const override; + QSGMaterialShader *createShader() const override; }; -class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode +class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalImageNode : public QSGBasicInternalImageNode { public: - QSGDefaultImageNode(); - 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 *t); - virtual void setAntialiasing(bool antialiasing); - virtual void setMirror(bool mirror); - virtual void update(); + QSGDefaultInternalImageNode(); - virtual void setMipmapFiltering(QSGTexture::Filtering filtering); - virtual void setFiltering(QSGTexture::Filtering filtering); - virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode); - virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode); + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override; + void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override; - virtual void preprocess(); + void updateMaterialAntialiasing() override; + void setMaterialTexture(QSGTexture *texture) override; + QSGTexture *materialTexture() const override; + bool updateMaterialBlending() override; + bool supportsWrap(const QSize &size) const override; private: - void updateGeometry(); - - QRectF m_targetRect; - QRectF m_innerTargetRect; - QRectF m_innerSourceRect; - QRectF m_subSourceRect; - QSGOpaqueTextureMaterial m_material; QSGTextureMaterial m_materialO; QSGSmoothTextureMaterial m_smoothMaterial; - - uint m_antialiasing : 1; - uint m_mirror : 1; - uint m_dirtyGeometry : 1; - - QSGGeometry m_geometry; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp new file mode 100644 index 0000000000..94414444ba --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp @@ -0,0 +1,159 @@ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultinternalrectanglenode_p.h" + +#include <QtQuick/qsgvertexcolormaterial.h> +#include <QtQuick/qsgtexturematerial.h> + +#include <QtQuick/private/qsgcontext_p.h> + +#include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +class SmoothColorMaterialShader : public QSGMaterialShader +{ +public: + SmoothColorMaterialShader(); + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +private: + virtual void initialize(); + + int m_matrixLoc; + int m_opacityLoc; + int m_pixelSizeLoc; +}; + +SmoothColorMaterialShader::SmoothColorMaterialShader() + : QSGMaterialShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.frag")); +} + +void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect) +{ + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + if (oldEffect == 0) { + // The viewport is constant, so set the pixel size uniform only once. + QRect r = state.viewportRect(); + program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height()); + } +} + +char const *const *SmoothColorMaterialShader::attributeNames() const +{ + static char const *const attributes[] = { + "vertex", + "vertexColor", + "vertexOffset", + 0 + }; + return attributes; +} + +void SmoothColorMaterialShader::initialize() +{ + m_matrixLoc = program()->uniformLocation("matrix"); + m_opacityLoc = program()->uniformLocation("opacity"); + m_pixelSizeLoc = program()->uniformLocation("pixelSize"); +} + +QSGSmoothColorMaterial::QSGSmoothColorMaterial() +{ + setFlag(RequiresFullMatrixExceptTranslate, true); + setFlag(Blending, true); +} + +int QSGSmoothColorMaterial::compare(const QSGMaterial *) const +{ + return 0; +} + +QSGMaterialType *QSGSmoothColorMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +QSGMaterialShader *QSGSmoothColorMaterial::createShader() const +{ + return new SmoothColorMaterialShader; +} + +QSGDefaultInternalRectangleNode::QSGDefaultInternalRectangleNode() +{ + setMaterial(&m_material); +} + +void QSGDefaultInternalRectangleNode::updateMaterialAntialiasing() +{ + if (m_antialiasing) + setMaterial(&m_smoothMaterial); + else + setMaterial(&m_material); +} + +void QSGDefaultInternalRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state) +{ + // smoothed material is always blended, so no change in material state + if (material() == &m_material) { + bool wasBlending = (m_material.flags() & QSGMaterial::Blending); + bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) + || (m_color.alpha() < 255 && m_color.alpha() != 0) + || (m_pen_width > 0 && m_border_color.alpha() < 255); + if (wasBlending != isBlending) { + m_material.setFlag(QSGMaterial::Blending, isBlending); + *state |= QSGNode::DirtyMaterial; + } + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h new file mode 100644 index 0000000000..a3f734a7b3 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSGDEFAULTINTERNALRECTANGLENODE_P_H +#define QSGDEFAULTINTERNALRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsgadaptationlayer_p.h> +#include <private/qsgbasicinternalrectanglenode_p.h> +#include <QtQuick/qsgvertexcolormaterial.h> + +QT_BEGIN_NAMESPACE + +class QSGContext; + +class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial +{ +public: + QSGSmoothColorMaterial(); + + int compare(const QSGMaterial *other) const override; + +protected: + QSGMaterialType *type() const override; + QSGMaterialShader *createShader() const override; +}; + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalRectangleNode : public QSGBasicInternalRectangleNode +{ +public: + QSGDefaultInternalRectangleNode(); + +private: + void updateMaterialAntialiasing() override; + void updateMaterialBlending(QSGNode::DirtyState *state) override; + + QSGVertexColorMaterial m_material; + QSGSmoothColorMaterial m_smoothMaterial; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp index 2f1c1d454c..ad5b57ff83 100644 --- a/src/quick/scenegraph/qsgdefaultlayer.cpp +++ b/src/quick/scenegraph/qsgdefaultlayer.cpp @@ -40,8 +40,12 @@ #include <private/qqmlglobal_p.h> #include <private/qsgrenderer_p.h> +#include <private/qsgdefaultrendercontext_p.h> -#include <QOpenGLFramebufferObject> +#include <QtGui/QOpenGLFramebufferObject> +#include <QtGui/QOpenGLFunctions> + +#include <QtQuick/private/qsgdepthstencilbuffer_p.h> #ifdef QSG_DEBUG_FBO_OVERLAY DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) @@ -95,7 +99,6 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context) #ifdef QSG_DEBUG_FBO_OVERLAY , m_debugOverlay(0) #endif - , m_context(context) , m_mipmap(false) , m_live(true) , m_recursive(false) @@ -106,6 +109,7 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context) , m_mirrorHorizontal(false) , m_mirrorVertical(true) { + m_context = static_cast<QSGDefaultRenderContext *>(context); } QSGDefaultLayer::~QSGDefaultLayer() diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgdefaultlayer_p.h index 8bb565f845..ae39994096 100644 --- a/src/quick/scenegraph/qsgdefaultlayer_p.h +++ b/src/quick/scenegraph/qsgdefaultlayer_p.h @@ -54,8 +54,14 @@ #include <private/qsgcontext_p.h> #include <qsgsimplerectnode.h> +QT_BEGIN_NAMESPACE + #define QSG_DEBUG_FBO_OVERLAY +class QOpenGLFramebufferObject; +class QSGDepthStencilBuffer; +class QSGDefaultRenderContext; + class Q_QUICK_PRIVATE_EXPORT QSGDefaultLayer : public QSGLayer { Q_OBJECT @@ -131,7 +137,7 @@ private: QSGSimpleRectNode *m_debugOverlay; #endif - QSGRenderContext *m_context; + QSGDefaultRenderContext *m_context; uint m_mipmap : 1; uint m_live : 1; @@ -144,4 +150,6 @@ private: uint m_mirrorVertical : 1; }; +QT_END_NAMESPACE + #endif // QSGDEFAULTLAYER_P_H diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp new file mode 100644 index 0000000000..1a17453baf --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultrendercontext_p.h" + +#include <QtGui/QOpenGLFramebufferObject> + +#include <QtQuick/private/qsgbatchrenderer_p.h> +#include <QtQuick/private/qsgrenderer_p.h> +#include <QtQuick/private/qsgatlastexture_p.h> +#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> +#include <QtQuick/private/qsgdistancefieldutil_p.h> + +QT_BEGIN_NAMESPACE + +#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext" + +QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context) + : QSGRenderContext(context) + , m_gl(nullptr) + , m_depthStencilManager(nullptr) + , m_maxTextureSize(0) + , m_brokenIBOs(false) + , m_serializedRender(false) + , m_attachToGLContext(true) + , m_atlasManager(nullptr) +{ + +} + +/*! + Initializes the scene graph render context with the GL context \a context. This also + emits the ready() signal so that the QML graph can start building scene graph nodes. + */ +void QSGDefaultRenderContext::initialize(void *context) +{ + if (!m_sg) + return; + + QOpenGLContext *openglContext = static_cast<QOpenGLContext *>(context); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); + + // Sanity check the surface format, in case it was overridden by the application + QSurfaceFormat requested = m_sg->defaultSurfaceFormat(); + QSurfaceFormat actual = openglContext->format(); + if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0) + qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors"); + if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0) + qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors"); + + if (!m_atlasManager) + m_atlasManager = new QSGAtlasTexture::Manager(); + + Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!"); + m_gl = openglContext; + if (m_attachToGLContext) { + Q_ASSERT(!openglContext->property(QSG_RENDERCONTEXT_PROPERTY).isValid()); + openglContext->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this)); + } + m_sg->renderContextInitialized(this); + +#ifdef Q_OS_LINUX + const char *vendor = (const char *) funcs->glGetString(GL_VENDOR); + if (vendor && strstr(vendor, "nouveau")) + m_brokenIBOs = true; + const char *renderer = (const char *) funcs->glGetString(GL_RENDERER); + if (renderer && strstr(renderer, "llvmpipe")) + m_serializedRender = true; + if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16")) + m_brokenIBOs = true; +#endif + + emit initialized(); +} + + +void QSGDefaultRenderContext::invalidate() +{ + if (!m_gl) + return; + + qDeleteAll(m_texturesToDelete); + m_texturesToDelete.clear(); + + qDeleteAll(m_textures); + m_textures.clear(); + + /* The cleanup of the atlas textures is a bit intriguing. + As part of the cleanup in the threaded render loop, we + do: + 1. call this function + 2. call QCoreApp::sendPostedEvents() to immediately process + any pending deferred deletes. + 3. delete the GL context. + + As textures need the atlas manager while cleaning up, the + manager needs to be cleaned up after the textures, so + we post a deleteLater here at the very bottom so it gets + deferred deleted last. + + Another alternative would be to use a QPointer in + QSGAtlasTexture::Texture, but this seemed simpler. + */ + m_atlasManager->invalidate(); + m_atlasManager->deleteLater(); + m_atlasManager = nullptr; + + // The following piece of code will read/write to the font engine's caches, + // potentially from different threads. However, this is safe because this + // code is only called from QQuickWindow's shutdown which is called + // only when the GUI is blocked, and multiple threads will call it in + // sequence. (see qsgdefaultglyphnode_p.cpp's init()) + for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(), + end = m_fontEnginesToClean.constEnd(); it != end; ++it) { + (*it)->clearGlyphCache(m_gl); + if (!(*it)->ref.deref()) + delete *it; + } + m_fontEnginesToClean.clear(); + + delete m_depthStencilManager; + m_depthStencilManager = 0; + + delete m_distanceFieldCacheManager; + m_distanceFieldCacheManager = 0; + + if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) + m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); + m_gl = 0; + + if (m_sg) + m_sg->renderContextInvalidated(this); + emit invalidated(); +} + +static QBasicMutex qsg_framerender_mutex; + +void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId) +{ + if (m_serializedRender) + qsg_framerender_mutex.lock(); + + renderer->renderScene(fboId); + + if (m_serializedRender) + qsg_framerender_mutex.unlock(); +} + +/*! + Returns a shared pointer to a depth stencil buffer that can be used with \a fbo. +*/ +QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo) +{ + if (!m_gl) + return QSharedPointer<QSGDepthStencilBuffer>(); + QSGDepthStencilBufferManager *manager = depthStencilBufferManager(); + QSGDepthStencilBuffer::Format format; + format.size = fbo->size(); + format.samples = fbo->format().samples(); + format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment; + QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format); + if (buffer.isNull()) { + buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format)); + manager->insertBuffer(buffer); + } + return buffer; +} + +/*! + Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom + implementations of \l depthStencilBufferForFbo(). +*/ +QSGDepthStencilBufferManager *QSGDefaultRenderContext::depthStencilBufferManager() +{ + if (!m_gl) + return 0; + if (!m_depthStencilManager) + m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl); + return m_depthStencilManager; +} + +QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const +{ + bool atlas = flags & CreateTexture_Atlas; + bool mipmap = flags & CreateTexture_Mipmap; + bool alpha = flags & CreateTexture_Alpha; + + // The atlas implementation is only supported from the render thread and + // does not support mipmaps. + if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) { + QSGTexture *t = m_atlasManager->create(image, alpha); + if (t) + return t; + } + + QSGPlainTexture *texture = new QSGPlainTexture(); + texture->setImage(image); + if (texture->hasAlphaChannel() && !alpha) + texture->setHasAlphaChannel(false); + + return texture; +} + +QSGRenderer *QSGDefaultRenderContext::createRenderer() +{ + return new QSGBatchRenderer::Renderer(this); +} + +/*! + Compile \a shader, optionally using \a vertexCode and \a fragmentCode as + replacement for the source code supplied by \a shader. + + If \a vertexCode or \a fragmentCode is supplied, the caller is responsible + for setting up attribute bindings. + + \a material is supplied in case the implementation needs to take the + material flags into account. + */ +void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode, const char *fragmentCode) +{ + Q_UNUSED(material); + if (vertexCode || fragmentCode) { + Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0, + "QSGRenderContext::compile()", + "materials with custom compile step cannot have custom vertex/fragment code"); + QOpenGLShaderProgram *p = shader->program(); + p->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader()); + p->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader()); + p->link(); + if (!p->isLinked()) + qWarning() << "shader compilation failed:" << endl << p->log(); + } else { + shader->compile(); + } +} + +void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader) +{ + shader->program()->bind(); + shader->initialize(); +} + +void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach) +{ + Q_ASSERT(!isValid()); + m_attachToGLContext = attach; +} + +QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context) +{ + return qobject_cast<QSGDefaultRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>()); +} + +QT_END_NAMESPACE + + +QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) +{ + if (!m_distanceFieldCacheManager) + m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager; + + QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font); + if (!cache) { + cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font); + m_distanceFieldCacheManager->insertCache(font, cache); + } + + return cache; +} diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h new file mode 100644 index 0000000000..0aed46b658 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTRENDERCONTEXT_H +#define QSGDEFAULTRENDERCONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qsgcontext_p.h> +#include <QtQuick/private/qsgdepthstencilbuffer_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; +class QSGMaterialShader; +class QOpenGLFramebufferObject; + +namespace QSGAtlasTexture { + class Manager; +} + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderContext : public QSGRenderContext +{ + Q_OBJECT +public: + QSGDefaultRenderContext(QSGContext *context); + + QOpenGLContext *openglContext() const { return m_gl; } + bool isValid() const override { return m_gl; } + + void initialize(void *context) override; + void invalidate() override; + void renderNextFrame(QSGRenderer *renderer, uint fboId) override; + + QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font) override; + + virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo); + QSGDepthStencilBufferManager *depthStencilBufferManager(); + + QSGTexture *createTexture(const QImage &image, uint flags) const override; + QSGRenderer *createRenderer() override; + + virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); + virtual void initializeShader(QSGMaterialShader *shader); + + void setAttachToGraphicsContext(bool attach) override; + + static QSGDefaultRenderContext *from(QOpenGLContext *context); + + bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; } + int maxTextureSize() const override { return m_maxTextureSize; } + +protected: + QOpenGLContext *m_gl; + QSGDepthStencilBufferManager *m_depthStencilManager; + int m_maxTextureSize; + bool m_brokenIBOs; + bool m_serializedRender; + bool m_attachToGLContext; + QSGAtlasTexture::Manager *m_atlasManager; + + +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTRENDERCONTEXT_H diff --git a/src/quick/scenegraph/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp new file mode 100644 index 0000000000..5eb8fb6e08 --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultspritenode_p.h" + +#include <QtQuick/QSGMaterial> +#include <QtGui/QOpenGLShaderProgram> + +QT_BEGIN_NAMESPACE + +struct SpriteVertex { + float x; + float y; + float tx; + float ty; +}; + +struct SpriteVertices { + SpriteVertex v1; + SpriteVertex v2; + SpriteVertex v3; + SpriteVertex v4; +}; + +class QQuickSpriteMaterial : public QSGMaterial +{ +public: + QQuickSpriteMaterial(); + ~QQuickSpriteMaterial(); + QSGMaterialType *type() const override { static QSGMaterialType type; return &type; } + QSGMaterialShader *createShader() const override; + int compare(const QSGMaterial *other) const override + { + return this - static_cast<const QQuickSpriteMaterial *>(other); + } + + QSGTexture *texture; + + float animT; + float animX1; + float animY1; + float animX2; + float animY2; + float animW; + float animH; +}; + +QQuickSpriteMaterial::QQuickSpriteMaterial() + : texture(0) + , animT(0.0f) + , animX1(0.0f) + , animY1(0.0f) + , animX2(0.0f) + , animY2(0.0f) + , animW(1.0f) + , animH(1.0f) +{ + setFlag(Blending, true); +} + +QQuickSpriteMaterial::~QQuickSpriteMaterial() +{ + delete texture; +} + +class SpriteMaterialData : public QSGMaterialShader +{ +public: + SpriteMaterialData() + : QSGMaterialShader() + { + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.frag")); + } + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE + { + QQuickSpriteMaterial *m = static_cast<QQuickSpriteMaterial *>(newEffect); + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT); + program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + void initialize() Q_DECL_OVERRIDE { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_opacity_id = program()->uniformLocation("qt_Opacity"); + m_animData_id = program()->uniformLocation("animData"); + m_animPos_id = program()->uniformLocation("animPos"); + } + + char const *const *attributeNames() const Q_DECL_OVERRIDE { + static const char *attr[] = { + "vPos", + "vTex", + 0 + }; + return attr; + } + + int m_matrix_id; + int m_opacity_id; + int m_animData_id; + int m_animPos_id; +}; + +QSGMaterialShader *QQuickSpriteMaterial::createShader() const +{ + return new SpriteMaterialData; +} + +static QSGGeometry::Attribute Sprite_Attributes[] = { + QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // pos + QSGGeometry::Attribute::create(1, 2, QSGGeometry::FloatType), // tex +}; + +static QSGGeometry::AttributeSet Sprite_AttributeSet = +{ + 2, // Attribute Count + (2+2) * sizeof(float), + Sprite_Attributes +}; + +QSGDefaultSpriteNode::QSGDefaultSpriteNode() + : m_material(new QQuickSpriteMaterial) + , m_geometryDirty(true) + , m_sheetSize(QSize(64, 64)) +{ + // Setup geometry data + m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6); + m_geometry->setDrawingMode(QSGGeometry::DrawTriangles); + quint16 *indices = m_geometry->indexDataAsUShort(); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 1; + indices[4] = 3; + indices[5] = 2; + + setGeometry(m_geometry); + setMaterial(m_material); + setFlag(OwnsGeometry, true); + setFlag(OwnsMaterial, true); +} + +void QSGDefaultSpriteNode::setTexture(QSGTexture *texture) +{ + m_material->texture = texture; + m_geometryDirty = true; + markDirty(DirtyMaterial); +} + +void QSGDefaultSpriteNode::setTime(float time) +{ + m_material->animT = time; + markDirty(DirtyMaterial); +} + +void QSGDefaultSpriteNode::setSourceA(const QPoint &source) +{ + if (m_sourceA != source) { + m_sourceA = source; + m_material->animX1 = static_cast<float>(source.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast<float>(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultSpriteNode::setSourceB(const QPoint &source) +{ + if (m_sourceB != source) { + m_sourceB = source; + m_material->animX2 = static_cast<float>(source.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast<float>(source.y()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultSpriteNode::setSpriteSize(const QSize &size) +{ + if (m_spriteSize != size) { + m_spriteSize = size; + m_material->animW = static_cast<float>(size.width()) / m_sheetSize.width(); + m_material->animH = static_cast<float>(size.height()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } + +} + +void QSGDefaultSpriteNode::setSheetSize(const QSize &size) +{ + if (m_sheetSize != size) { + m_sheetSize = size; + + // Update all dependent properties + m_material->animX1 = static_cast<float>(m_sourceA.x()) / m_sheetSize.width(); + m_material->animY1 = static_cast<float>(m_sourceA.y()) / m_sheetSize.height(); + m_material->animX2 = static_cast<float>(m_sourceB.x()) / m_sheetSize.width(); + m_material->animY2 = static_cast<float>(m_sourceB.y()) / m_sheetSize.height(); + m_material->animW = static_cast<float>(m_spriteSize.width()) / m_sheetSize.width(); + m_material->animH = static_cast<float>(m_spriteSize.height()) / m_sheetSize.height(); + markDirty(DirtyMaterial); + } +} + +void QSGDefaultSpriteNode::setSize(const QSizeF &size) +{ + if (m_size != size) { + m_size = size; + m_geometryDirty = true; + } +} + +void QSGDefaultSpriteNode::setFiltering(QSGTexture::Filtering filtering) +{ + m_material->texture->setFiltering(filtering); + markDirty(DirtyMaterial); +} + +void QSGDefaultSpriteNode::update() +{ + if (m_geometryDirty) { + updateGeometry(); + m_geometryDirty = false; + } +} + +void QSGDefaultSpriteNode::updateGeometry() +{ + if (!m_material->texture) + return; + + SpriteVertices *p = (SpriteVertices *) m_geometry->vertexData(); + + QRectF texRect = m_material->texture->normalizedTextureSubRect(); + + p->v1.tx = texRect.topLeft().x(); + p->v1.ty = texRect.topLeft().y(); + + p->v2.tx = texRect.topRight().x(); + p->v2.ty = texRect.topRight().y(); + + p->v3.tx = texRect.bottomLeft().x(); + p->v3.ty = texRect.bottomLeft().y(); + + p->v4.tx = texRect.bottomRight().x(); + p->v4.ty = texRect.bottomRight().y(); + + p->v1.x = 0; + p->v1.y = 0; + + p->v2.x = m_size.width(); + p->v2.y = 0; + + p->v3.x = 0; + p->v3.y = m_size.height(); + + p->v4.x = m_size.width(); + p->v4.y = m_size.height(); + markDirty(DirtyGeometry); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultspritenode_p.h b/src/quick/scenegraph/qsgdefaultspritenode_p.h new file mode 100644 index 0000000000..78aa8cc0cf --- /dev/null +++ b/src/quick/scenegraph/qsgdefaultspritenode_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTSPRITENODE_H +#define QSGDEFAULTSPRITENODE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtquickglobal_p.h> + +QT_REQUIRE_CONFIG(quick_sprite); + +#include <private/qsgadaptationlayer_p.h> + +QT_BEGIN_NAMESPACE +class QQuickSpriteMaterial; +class QSGDefaultSpriteNode : public QSGSpriteNode +{ +public: + QSGDefaultSpriteNode(); + + void setTexture(QSGTexture *texture) override; + void setTime(float time) override; + void setSourceA(const QPoint &source) override; + void setSourceB(const QPoint &source) override; + void setSpriteSize(const QSize &size) override; + void setSheetSize(const QSize &size) override; + void setSize(const QSizeF &size) override; + void setFiltering(QSGTexture::Filtering filtering) override; + void update() override; +private: + void updateGeometry(); + + QQuickSpriteMaterial *m_material; + QSGGeometry *m_geometry; + bool m_geometryDirty; + QPoint m_sourceA; + QPoint m_sourceB; + QSize m_spriteSize; + QSize m_sheetSize; + QSizeF m_size; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTSPRITENODE_H diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index b5f149eff7..df7bbe6ceb 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -47,7 +47,6 @@ #include <QtCore/QLibraryInfo> #include <QtCore/private/qabstractanimation_p.h> -#include <QtGui/QOpenGLContext> #include <QtGui/QOffscreenSurface> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> @@ -59,7 +58,13 @@ #include <QtQuick/private/qsgcontext_p.h> #include <private/qquickprofiler_p.h> -#include <private/qquickshadereffectnode_p.h> +#ifndef QT_NO_OPENGL +# include <QtGui/QOpenGLContext> +# include <private/qsgdefaultrendercontext_p.h> +#if QT_CONFIG(quick_shadereffect) +# include <private/qquickopenglshadereffectnode_p.h> +#endif +#endif #ifdef Q_OS_WIN # include <QtCore/qt_windows.h> @@ -67,9 +72,9 @@ QT_BEGIN_NAMESPACE - +extern bool qsg_useConsistentTiming(); extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - +#ifndef QT_NO_OPENGL /*! expectations for this manager to work: - one opengl context to render multiple windows @@ -81,7 +86,7 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP); DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk - +#endif QSGRenderLoop *QSGRenderLoop::s_instance = 0; QSGRenderLoop::~QSGRenderLoop() @@ -97,7 +102,7 @@ void QSGRenderLoop::cleanup() { if (!s_instance) return; - foreach (QQuickWindow *w, s_instance->windows()) { + for (QQuickWindow *w : s_instance->windows()) { QQuickWindowPrivate *wd = QQuickWindowPrivate::get(w); if (wd->windowManager == s_instance) { s_instance->windowDestroyed(w); @@ -113,17 +118,20 @@ void QSGRenderLoop::cleanup() */ void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job) { - Q_ASSERT(window); Q_ASSERT(job); - +#ifndef QT_NO_OPENGL + Q_ASSERT(window); if (window->openglContext()) { window->openglContext()->makeCurrent(window); job->run(); } - +#else + Q_UNUSED(window) + job->run(); +#endif delete job; } - +#ifndef QT_NO_OPENGL class QSGGuiThreadRenderLoop : public QSGRenderLoop { Q_OBJECT @@ -164,7 +172,7 @@ public: QImage grabContent; }; - +#endif QSGRenderLoop *QSGRenderLoop::instance() { if (!s_instance) { @@ -174,7 +182,7 @@ QSGRenderLoop *QSGRenderLoop::instance() const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true); s_instance = QSGContext::createWindowManager(); - +#ifndef QT_NO_OPENGL if (!s_instance) { enum RenderLoopType { @@ -226,9 +234,10 @@ QSGRenderLoop *QSGRenderLoop::instance() break; } } - +#endif qAddPostRoutine(QSGRenderLoop::cleanup); } + return s_instance; } @@ -253,20 +262,24 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window, const bool signalEmitted = QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable, translatedMessage); -#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) if (!signalEmitted && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow()) { MessageBox(0, (LPCTSTR) translatedMessage.utf16(), (LPCTSTR)(QCoreApplication::applicationName().utf16()), MB_OK | MB_ICONERROR); } -#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT +#endif // Q_OS_WIN && !Q_OS_WINRT if (!signalEmitted) qFatal("%s", qPrintable(untranslatedMessage)); } - +#ifndef QT_NO_OPENGL QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop() : gl(0) { + if (qsg_useConsistentTiming()) { + QUnifiedTimer::instance(true)->setConsistentTiming(true); + qCDebug(QSG_LOG_INFO, "using fixed animation steps"); + } sg = QSGContext::createDefaultContext(); rc = sg->createRenderContext(); } @@ -315,7 +328,9 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(!current)) qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; - QQuickShaderEffectMaterial::cleanupMaterialCache(); +#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); +#endif d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { @@ -354,8 +369,10 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) cd->fireOpenGLContextCreated(gl); current = gl->makeCurrent(window); } - if (current) - cd->context->initialize(gl); + if (current) { + auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(cd->context); + openglRenderContext->initialize(gl); + } } else { current = gl->makeCurrent(window); } @@ -367,7 +384,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) return; if (!data.grabOnly) { - cd->flushDelayedTouchEvent(); + cd->flushFrameSynchronousEvents(); // Event delivery/processing triggered the window to be deleted or stop rendering. if (!m_windows.contains(window)) return; @@ -476,6 +493,8 @@ void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window) renderWindow(window); } +#endif + #include "qsgrenderloop.moc" QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h index 9159a2f01d..7bf40ecac4 100644 --- a/src/quick/scenegraph/qsgrenderloop_p.h +++ b/src/quick/scenegraph/qsgrenderloop_p.h @@ -69,6 +69,10 @@ class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject Q_OBJECT public: + enum RenderLoopFlags { + SupportsGrabWithoutExpose = 0x01 + }; + virtual ~QSGRenderLoop(); virtual void show(QQuickWindow *window) = 0; @@ -104,6 +108,8 @@ public: virtual bool interleaveIncubation() const { return false; } + virtual int flags() const { return 0; } + static void cleanup(); Q_SIGNALS: diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 96abd4267b..693012154e 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -63,7 +63,10 @@ #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmldebugconnector_p.h> -#include <private/qquickshadereffectnode_p.h> +#if QT_CONFIG(quick_shadereffect) +#include <private/qquickopenglshadereffectnode_p.h> +#endif +#include <private/qsgdefaultrendercontext_p.h> /* Overall design: @@ -268,7 +271,6 @@ public: QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext) : wm(w) , gl(0) - , sgrc(renderContext) , animatorDriver(0) , pendingUpdate(0) , sleeping(false) @@ -277,6 +279,7 @@ public: , window(0) , stopEventProcessing(false) { + sgrc = static_cast<QSGDefaultRenderContext *>(renderContext); #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); @@ -325,7 +328,7 @@ public: QSGThreadedRenderLoop *wm; QOpenGLContext *gl; - QSGRenderContext *sgrc; + QSGDefaultRenderContext *sgrc; QAnimationDriver *animatorDriver; @@ -486,7 +489,9 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); - QQuickShaderEffectMaterial::cleanupMaterialCache(); +#if QT_CONFIG(quick_shadereffect) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); +#endif // The canvas nodes must be cleaned up regardless if we are in the destructor.. if (wipeSG) { @@ -1141,7 +1146,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) } // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); // The delivery of the event might have caused the window to stop rendering w = windowFor(m_windows, window); if (!w || !w->thread || !w->thread->window) { diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 901fbbec43..941a7b3f3c 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -48,13 +48,17 @@ #include <QtQuick/private/qsgcontext_p.h> #include <QtQuick/private/qquickwindow_p.h> +#include <QtQuick/private/qsgdefaultrendercontext_p.h> #include <QtQuick/QQuickWindow> #include <private/qquickprofiler_p.h> -#include <private/qquickshadereffectnode_p.h> #include <private/qquickanimatorcontroller_p.h> +#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) +#include <private/qquickopenglshadereffectnode_p.h> +#endif + QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); @@ -78,7 +82,7 @@ QSGWindowsRenderLoop::QSGWindowsRenderLoop() , m_updateTimer(0) , m_animationTimer(0) { - m_rc = m_sg->createRenderContext(); + m_rc = static_cast<QSGDefaultRenderContext *>(m_sg->createRenderContext()); m_animationDriver = m_sg->createAnimationDriver(m_sg); m_animationDriver->install(); @@ -241,7 +245,9 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) if (Q_UNLIKELY(!current)) qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; - QQuickShaderEffectMaterial::cleanupMaterialCache(); +#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); +#endif d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { @@ -257,7 +263,7 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) bool QSGWindowsRenderLoop::anyoneShowing() const { - foreach (const WindowData &wd, m_windows) + for (const WindowData &wd : qAsConst(m_windows)) if (wd.window->isVisible() && wd.window->isExposed() && wd.window->size().isValid()) return true; return false; @@ -341,6 +347,11 @@ void QSGWindowsRenderLoop::maybeUpdate(QQuickWindow *window) maybePostUpdateTimer(); } +QSGRenderContext *QSGWindowsRenderLoop::createRenderContext(QSGContext *) const +{ + return m_rc; +} + bool QSGWindowsRenderLoop::event(QEvent *event) { switch (event->type()) { @@ -371,7 +382,7 @@ void QSGWindowsRenderLoop::render() { RLDEBUG("render"); bool rendered = false; - foreach (const WindowData &wd, m_windows) { + for (const WindowData &wd : qAsConst(m_windows)) { if (wd.pendingUpdate) { const_cast<WindowData &>(wd).pendingUpdate = false; renderWindow(wd.window); @@ -434,7 +445,7 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) } } - d->flushDelayedTouchEvent(); + d->flushFrameSynchronousEvents(); // Event delivery or processing has caused the window to stop rendering. if (!windowData(window)) return; diff --git a/src/quick/scenegraph/qsgwindowsrenderloop_p.h b/src/quick/scenegraph/qsgwindowsrenderloop_p.h index ad7986035f..9e5d7f04d3 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop_p.h +++ b/src/quick/scenegraph/qsgwindowsrenderloop_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE class QSGRenderContext; +class QSGDefaultRenderContext; class QSGWindowsRenderLoop : public QSGRenderLoop { @@ -83,7 +84,7 @@ public: QAnimationDriver *animationDriver() const { return m_animationDriver; } QSGContext *sceneGraphContext() const { return m_sg; } - QSGRenderContext *createRenderContext(QSGContext *) const { return m_rc; } + QSGRenderContext *createRenderContext(QSGContext *) const; void releaseResources(QQuickWindow *) { } @@ -113,7 +114,7 @@ private: QOpenGLContext *m_gl; QSGContext *m_sg; - QSGRenderContext *m_rc; + QSGDefaultRenderContext *m_rc; QAnimationDriver *m_animationDriver; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 84cc2ba135..edf4aa08c5 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -1,173 +1,224 @@ -!contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL - # DEFINES += QSG_SEPARATE_INDEX_BUFFER # DEFINES += QSG_DISTANCEFIELD_CACHE_DEBUG # Core API HEADERS += \ - $$PWD/coreapi/qsgbatchrenderer_p.h \ $$PWD/coreapi/qsggeometry.h \ $$PWD/coreapi/qsgmaterial.h \ + $$PWD/coreapi/qsgmaterialshader_p.h \ $$PWD/coreapi/qsgnode.h \ $$PWD/coreapi/qsgnode_p.h \ $$PWD/coreapi/qsgnodeupdater_p.h \ $$PWD/coreapi/qsgabstractrenderer.h \ $$PWD/coreapi/qsgabstractrenderer_p.h \ $$PWD/coreapi/qsgrenderer_p.h \ + $$PWD/coreapi/qsgrendernode.h \ $$PWD/coreapi/qsgrendernode_p.h \ - $$PWD/coreapi/qsggeometry_p.h \ - $$PWD/coreapi/qsgmaterialshader_p.h + $$PWD/coreapi/qsgrendererinterface.h \ + $$PWD/coreapi/qsggeometry_p.h SOURCES += \ $$PWD/coreapi/qsgabstractrenderer.cpp \ - $$PWD/coreapi/qsgbatchrenderer.cpp \ $$PWD/coreapi/qsggeometry.cpp \ $$PWD/coreapi/qsgmaterial.cpp \ $$PWD/coreapi/qsgnode.cpp \ $$PWD/coreapi/qsgnodeupdater.cpp \ $$PWD/coreapi/qsgrenderer.cpp \ $$PWD/coreapi/qsgrendernode.cpp \ - $$PWD/coreapi/qsgshaderrewriter.cpp + $$PWD/coreapi/qsgrendererinterface.cpp + +qtConfig(opengl(es1|es2)?) { + HEADERS += \ + $$PWD/coreapi/qsgbatchrenderer_p.h + SOURCES += \ + $$PWD/coreapi/qsgbatchrenderer.cpp \ + $$PWD/coreapi/qsgshaderrewriter.cpp +} # Util API HEADERS += \ $$PWD/util/qsgareaallocator_p.h \ - $$PWD/util/qsgatlastexture_p.h \ - $$PWD/util/qsgdepthstencilbuffer_p.h \ $$PWD/util/qsgengine.h \ $$PWD/util/qsgengine_p.h \ - $$PWD/util/qsgflatcolormaterial.h \ - $$PWD/util/qsgsimplematerial.h \ $$PWD/util/qsgsimplerectnode.h \ $$PWD/util/qsgsimpletexturenode.h \ - $$PWD/util/qsgtexturematerial.h \ - $$PWD/util/qsgtexturematerial_p.h \ - $$PWD/util/qsgvertexcolormaterial.h \ $$PWD/util/qsgtexture.h \ $$PWD/util/qsgtexture_p.h \ $$PWD/util/qsgtextureprovider.h \ - $$PWD/util/qsgdefaultpainternode_p.h \ $$PWD/util/qsgdistancefieldutil_p.h \ - $$PWD/util/qsgshadersourcebuilder_p.h + $$PWD/util/qsgflatcolormaterial.h \ + $$PWD/util/qsgsimplematerial.h \ + $$PWD/util/qsgtexturematerial.h \ + $$PWD/util/qsgtexturematerial_p.h \ + $$PWD/util/qsgvertexcolormaterial.h \ + $$PWD/util/qsgrectanglenode.h \ + $$PWD/util/qsgimagenode.h \ + $$PWD/util/qsgninepatchnode.h SOURCES += \ $$PWD/util/qsgareaallocator.cpp \ - $$PWD/util/qsgatlastexture.cpp \ - $$PWD/util/qsgdepthstencilbuffer.cpp \ $$PWD/util/qsgengine.cpp \ - $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplerectnode.cpp \ $$PWD/util/qsgsimpletexturenode.cpp \ - $$PWD/util/qsgtexturematerial.cpp \ - $$PWD/util/qsgvertexcolormaterial.cpp \ $$PWD/util/qsgtexture.cpp \ $$PWD/util/qsgtextureprovider.cpp \ - $$PWD/util/qsgdefaultpainternode.cpp \ $$PWD/util/qsgdistancefieldutil.cpp \ + $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplematerial.cpp \ - $$PWD/util/qsgshadersourcebuilder.cpp + $$PWD/util/qsgtexturematerial.cpp \ + $$PWD/util/qsgvertexcolormaterial.cpp \ + $$PWD/util/qsgrectanglenode.cpp \ + $$PWD/util/qsgimagenode.cpp \ + $$PWD/util/qsgninepatchnode.cpp + +qtConfig(opengl(es1|es2)?) { + HEADERS += \ + $$PWD/util/qsgdepthstencilbuffer_p.h \ + $$PWD/util/qsgshadersourcebuilder_p.h \ + $$PWD/util/qsgatlastexture_p.h + SOURCES += \ + $$PWD/util/qsgdepthstencilbuffer.cpp \ + $$PWD/util/qsgatlastexture.cpp \ + $$PWD/util/qsgshadersourcebuilder.cpp +} + # QML / Adaptations API HEADERS += \ $$PWD/qsgadaptationlayer_p.h \ $$PWD/qsgcontext_p.h \ $$PWD/qsgcontextplugin_p.h \ - $$PWD/qsgdefaultglyphnode_p.h \ - $$PWD/qsgdefaultdistancefieldglyphcache_p.h \ - $$PWD/qsgdistancefieldglyphnode_p.h \ - $$PWD/qsgdistancefieldglyphnode_p_p.h \ - $$PWD/qsgdefaultglyphnode_p_p.h \ - $$PWD/qsgdefaultimagenode_p.h \ - $$PWD/qsgdefaultrectanglenode_p.h \ - $$PWD/qsgrenderloop_p.h \ - $$PWD/qsgthreadedrenderloop_p.h \ - $$PWD/qsgwindowsrenderloop_p.h \ - $$PWD/qsgdefaultlayer_p.h + $$PWD/qsgbasicinternalrectanglenode_p.h \ + $$PWD/qsgbasicinternalimagenode_p.h \ + $$PWD/qsgbasicglyphnode_p.h \ + $$PWD/qsgrenderloop_p.h SOURCES += \ $$PWD/qsgadaptationlayer.cpp \ $$PWD/qsgcontext.cpp \ $$PWD/qsgcontextplugin.cpp \ - $$PWD/qsgdefaultglyphnode.cpp \ - $$PWD/qsgdefaultglyphnode_p.cpp \ - $$PWD/qsgdefaultdistancefieldglyphcache.cpp \ - $$PWD/qsgdistancefieldglyphnode.cpp \ - $$PWD/qsgdistancefieldglyphnode_p.cpp \ - $$PWD/qsgdefaultimagenode.cpp \ - $$PWD/qsgdefaultrectanglenode.cpp \ - $$PWD/qsgrenderloop.cpp \ - $$PWD/qsgthreadedrenderloop.cpp \ - $$PWD/qsgwindowsrenderloop.cpp \ - $$PWD/qsgdefaultlayer.cpp + $$PWD/qsgbasicinternalrectanglenode.cpp \ + $$PWD/qsgbasicinternalimagenode.cpp \ + $$PWD/qsgbasicglyphnode.cpp \ + $$PWD/qsgrenderloop.cpp + +qtConfig(opengl(es1|es2)?) { + SOURCES += \ + $$PWD/qsgdefaultglyphnode.cpp \ + $$PWD/qsgdefaultglyphnode_p.cpp \ + $$PWD/qsgdefaultdistancefieldglyphcache.cpp \ + $$PWD/qsgdistancefieldglyphnode.cpp \ + $$PWD/qsgdistancefieldglyphnode_p.cpp \ + $$PWD/qsgdefaultinternalimagenode.cpp \ + $$PWD/qsgdefaultinternalrectanglenode.cpp \ + $$PWD/qsgdefaultrendercontext.cpp \ + $$PWD/qsgdefaultcontext.cpp \ + $$PWD/util/qsgdefaultpainternode.cpp \ + $$PWD/util/qsgdefaultrectanglenode.cpp \ + $$PWD/util/qsgdefaultimagenode.cpp \ + $$PWD/util/qsgdefaultninepatchnode.cpp \ + $$PWD/qsgdefaultlayer.cpp \ + $$PWD/qsgthreadedrenderloop.cpp \ + $$PWD/qsgwindowsrenderloop.cpp + HEADERS += \ + $$PWD/qsgdefaultglyphnode_p.h \ + $$PWD/qsgdefaultdistancefieldglyphcache_p.h \ + $$PWD/qsgdistancefieldglyphnode_p.h \ + $$PWD/qsgdistancefieldglyphnode_p_p.h \ + $$PWD/qsgdefaultglyphnode_p_p.h \ + $$PWD/qsgdefaultinternalimagenode_p.h \ + $$PWD/qsgdefaultinternalrectanglenode_p.h \ + $$PWD/qsgdefaultrendercontext_p.h \ + $$PWD/qsgdefaultcontext_p.h \ + $$PWD/util/qsgdefaultpainternode_p.h \ + $$PWD/util/qsgdefaultrectanglenode_p.h \ + $$PWD/util/qsgdefaultimagenode_p.h \ + $$PWD/util/qsgdefaultninepatchnode_p.h \ + $$PWD/qsgdefaultlayer_p.h \ + $$PWD/qsgthreadedrenderloop_p.h \ + $$PWD/qsgwindowsrenderloop_p.h + + qtConfig(quick-sprite) { + SOURCES += \ + $$PWD/qsgdefaultspritenode.cpp + HEADERS += \ + $$PWD/qsgdefaultspritenode_p.h + } +} + +# Built-in, non-plugin-based adaptations +include(adaptations/adaptations.pri) RESOURCES += \ $$PWD/scenegraph.qrc -OTHER_FILES += \ - $$PWD/shaders/24bittextmask.frag \ - $$PWD/shaders/8bittextmask.frag \ - $$PWD/shaders/distancefieldoutlinetext.frag \ - $$PWD/shaders/distancefieldshiftedtext.frag \ - $$PWD/shaders/distancefieldshiftedtext.vert \ - $$PWD/shaders/distancefieldtext.frag \ - $$PWD/shaders/distancefieldtext.vert \ - $$PWD/shaders/flatcolor.frag \ - $$PWD/shaders/flatcolor.vert \ - $$PWD/shaders/hiqsubpixeldistancefieldtext.frag \ - $$PWD/shaders/hiqsubpixeldistancefieldtext.vert \ - $$PWD/shaders/loqsubpixeldistancefieldtext.frag \ - $$PWD/shaders/loqsubpixeldistancefieldtext.vert \ - $$PWD/shaders/opaquetexture.frag \ - $$PWD/shaders/opaquetexture.vert \ - $$PWD/shaders/outlinedtext.frag \ - $$PWD/shaders/outlinedtext.vert \ - $$PWD/shaders/rendernode.frag \ - $$PWD/shaders/rendernode.vert \ - $$PWD/shaders/smoothcolor.frag \ - $$PWD/shaders/smoothcolor.vert \ - $$PWD/shaders/smoothtexture.frag \ - $$PWD/shaders/smoothtexture.vert \ - $$PWD/shaders/stencilclip.frag \ - $$PWD/shaders/stencilclip.vert \ - $$PWD/shaders/styledtext.frag \ - $$PWD/shaders/styledtext.vert \ - $$PWD/shaders/textmask.frag \ - $$PWD/shaders/textmask.vert \ - $$PWD/shaders/texture.frag \ - $$PWD/shaders/vertexcolor.frag \ - $$PWD/shaders/vertexcolor.vert \ - $$PWD/shaders/24bittextmask_core.frag \ - $$PWD/shaders/8bittextmask_core.frag \ - $$PWD/shaders/distancefieldoutlinetext_core.frag \ - $$PWD/shaders/distancefieldshiftedtext_core.frag \ - $$PWD/shaders/distancefieldshiftedtext_core.vert \ - $$PWD/shaders/distancefieldtext_core.frag \ - $$PWD/shaders/distancefieldtext_core.vert \ - $$PWD/shaders/flatcolor_core.frag \ - $$PWD/shaders/flatcolor_core.vert \ - $$PWD/shaders/hiqsubpixeldistancefieldtext_core.frag \ - $$PWD/shaders/hiqsubpixeldistancefieldtext_core.vert \ - $$PWD/shaders/loqsubpixeldistancefieldtext_core.frag \ - $$PWD/shaders/loqsubpixeldistancefieldtext_core.vert \ - $$PWD/shaders/opaquetexture_core.frag \ - $$PWD/shaders/opaquetexture_core.vert \ - $$PWD/shaders/outlinedtext_core.frag \ - $$PWD/shaders/outlinedtext_core.vert \ - $$PWD/shaders/rendernode_core.frag \ - $$PWD/shaders/rendernode_core.vert \ - $$PWD/shaders/smoothcolor_core.frag \ - $$PWD/shaders/smoothcolor_core.vert \ - $$PWD/shaders/smoothtexture_core.frag \ - $$PWD/shaders/smoothtexture_core.vert \ - $$PWD/shaders/stencilclip_core.frag \ - $$PWD/shaders/stencilclip_core.vert \ - $$PWD/shaders/styledtext_core.frag \ - $$PWD/shaders/styledtext_core.vert \ - $$PWD/shaders/textmask_core.frag \ - $$PWD/shaders/textmask_core.vert \ - $$PWD/shaders/texture_core.frag \ - $$PWD/shaders/vertexcolor_core.frag \ - $$PWD/shaders/vertexcolor_core.vert \ - scenegraph/shaders/visualization.frag \ - scenegraph/shaders/visualization.vert - +# OpenGL Shaders +qtConfig(opengl(es1|es2)?) { + OTHER_FILES += \ + $$PWD/shaders/24bittextmask.frag \ + $$PWD/shaders/8bittextmask.frag \ + $$PWD/shaders/distancefieldoutlinetext.frag \ + $$PWD/shaders/distancefieldshiftedtext.frag \ + $$PWD/shaders/distancefieldshiftedtext.vert \ + $$PWD/shaders/distancefieldtext.frag \ + $$PWD/shaders/distancefieldtext.vert \ + $$PWD/shaders/flatcolor.frag \ + $$PWD/shaders/flatcolor.vert \ + $$PWD/shaders/hiqsubpixeldistancefieldtext.frag \ + $$PWD/shaders/hiqsubpixeldistancefieldtext.vert \ + $$PWD/shaders/loqsubpixeldistancefieldtext.frag \ + $$PWD/shaders/loqsubpixeldistancefieldtext.vert \ + $$PWD/shaders/opaquetexture.frag \ + $$PWD/shaders/opaquetexture.vert \ + $$PWD/shaders/outlinedtext.frag \ + $$PWD/shaders/outlinedtext.vert \ + $$PWD/shaders/rendernode.frag \ + $$PWD/shaders/rendernode.vert \ + $$PWD/shaders/smoothcolor.frag \ + $$PWD/shaders/smoothcolor.vert \ + $$PWD/shaders/smoothtexture.frag \ + $$PWD/shaders/smoothtexture.vert \ + $$PWD/shaders/stencilclip.frag \ + $$PWD/shaders/stencilclip.vert \ + $$PWD/shaders/styledtext.frag \ + $$PWD/shaders/styledtext.vert \ + $$PWD/shaders/textmask.frag \ + $$PWD/shaders/textmask.vert \ + $$PWD/shaders/texture.frag \ + $$PWD/shaders/vertexcolor.frag \ + $$PWD/shaders/vertexcolor.vert \ + $$PWD/shaders/24bittextmask_core.frag \ + $$PWD/shaders/8bittextmask_core.frag \ + $$PWD/shaders/distancefieldoutlinetext_core.frag \ + $$PWD/shaders/distancefieldshiftedtext_core.frag \ + $$PWD/shaders/distancefieldshiftedtext_core.vert \ + $$PWD/shaders/distancefieldtext_core.frag \ + $$PWD/shaders/distancefieldtext_core.vert \ + $$PWD/shaders/flatcolor_core.frag \ + $$PWD/shaders/flatcolor_core.vert \ + $$PWD/shaders/hiqsubpixeldistancefieldtext_core.frag \ + $$PWD/shaders/hiqsubpixeldistancefieldtext_core.vert \ + $$PWD/shaders/loqsubpixeldistancefieldtext_core.frag \ + $$PWD/shaders/loqsubpixeldistancefieldtext_core.vert \ + $$PWD/shaders/opaquetexture_core.frag \ + $$PWD/shaders/opaquetexture_core.vert \ + $$PWD/shaders/outlinedtext_core.frag \ + $$PWD/shaders/outlinedtext_core.vert \ + $$PWD/shaders/rendernode_core.frag \ + $$PWD/shaders/rendernode_core.vert \ + $$PWD/shaders/smoothcolor_core.frag \ + $$PWD/shaders/smoothcolor_core.vert \ + $$PWD/shaders/smoothtexture_core.frag \ + $$PWD/shaders/smoothtexture_core.vert \ + $$PWD/shaders/stencilclip_core.frag \ + $$PWD/shaders/stencilclip_core.vert \ + $$PWD/shaders/styledtext_core.frag \ + $$PWD/shaders/styledtext_core.vert \ + $$PWD/shaders/textmask_core.frag \ + $$PWD/shaders/textmask_core.vert \ + $$PWD/shaders/texture_core.frag \ + $$PWD/shaders/vertexcolor_core.frag \ + $$PWD/shaders/vertexcolor_core.vert \ + $$PWD/shaders/visualization.frag \ + $$PWD/shaders/visualization.vert +} diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc index ef6da71334..0687530be1 100644 --- a/src/quick/scenegraph/scenegraph.qrc +++ b/src/quick/scenegraph/scenegraph.qrc @@ -68,5 +68,9 @@ <file>shaders/vertexcolor_core.vert</file> <file>shaders/visualization.vert</file> <file>shaders/visualization.frag</file> + <file>shaders/sprite.frag</file> + <file>shaders/sprite.vert</file> + <file>shaders/sprite_core.frag</file> + <file>shaders/sprite_core.vert</file> </qresource> </RCC> diff --git a/src/quick/scenegraph/shaders/sprite.frag b/src/quick/scenegraph/shaders/sprite.frag new file mode 100644 index 0000000000..e1fcb0f006 --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite.frag @@ -0,0 +1,12 @@ +uniform sampler2D _qt_texture; +uniform lowp float qt_Opacity; + +varying highp vec4 fTexS; +varying lowp float progress; + +void main() +{ + gl_FragColor = mix(texture2D(_qt_texture, fTexS.xy), + texture2D(_qt_texture, fTexS.zw), + progress) * qt_Opacity; +}
\ No newline at end of file diff --git a/src/quick/scenegraph/shaders/sprite.vert b/src/quick/scenegraph/shaders/sprite.vert new file mode 100644 index 0000000000..fc826f60b4 --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite.vert @@ -0,0 +1,23 @@ +attribute highp vec2 vPos; +attribute highp vec2 vTex; + +uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress +uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation) + +uniform highp mat4 qt_Matrix; + +varying highp vec4 fTexS; +varying lowp float progress; + +void main() +{ + progress = animData.z; + + // Calculate frame location in texture + fTexS.xy = animPos.xy + vTex.xy * animData.xy; + + // Next frame is also passed, for interpolation + fTexS.zw = animPos.zw + vTex.xy * animData.xy; + + gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1); +}
\ No newline at end of file diff --git a/src/quick/scenegraph/shaders/sprite_core.frag b/src/quick/scenegraph/shaders/sprite_core.frag new file mode 100644 index 0000000000..c1087a8754 --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite_core.frag @@ -0,0 +1,16 @@ +#version 150 core + +in vec4 fTexS; +in float progress; + +out vec4 fragColor; + +uniform sampler2D _qt_texture; +uniform float qt_Opacity; + +void main() +{ + fragColor = mix(texture(_qt_texture, fTexS.xy), + texture(_qt_texture, fTexS.zw), + progress) * qt_Opacity; +}
\ No newline at end of file diff --git a/src/quick/scenegraph/shaders/sprite_core.vert b/src/quick/scenegraph/shaders/sprite_core.vert new file mode 100644 index 0000000000..5027bf03fc --- /dev/null +++ b/src/quick/scenegraph/shaders/sprite_core.vert @@ -0,0 +1,24 @@ +#version 150 core + +in vec2 vPos; +in vec2 vTex; + +out vec4 fTexS; +out float progress; + +uniform vec3 animData; // w,h(premultiplied of anim), interpolation progress +uniform vec4 animPos; // x,y, x,y (two frames for interpolation) +uniform mat4 qt_Matrix; + +void main() +{ + progress = animData.z; + + // Calculate frame location in texture + fTexS.xy = animPos.xy + vTex.xy * animData.xy; + + // Next frame is also passed, for interpolation + fTexS.zw = animPos.zw + vTex.xy * animData.xy; + + gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1); +}
\ No newline at end of file diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 27806c48ae..40c3293c7b 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -44,6 +44,7 @@ #include <QtCore/QtMath> #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> #include <QtGui/QSurface> diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h index d6c0109c98..cd24645fcf 100644 --- a/src/quick/scenegraph/util/qsgatlastexture_p.h +++ b/src/quick/scenegraph/util/qsgatlastexture_p.h @@ -104,17 +104,17 @@ public: QSize size() const { return m_size; } - GLuint internalFormat() const { return m_internalFormat; } - GLuint externalFormat() const { return m_externalFormat; } + uint internalFormat() const { return m_internalFormat; } + uint externalFormat() const { return m_externalFormat; } private: QSGAreaAllocator m_allocator; - GLuint m_texture_id; + unsigned int m_texture_id; QSize m_size; QList<Texture *> m_pending_uploads; - GLuint m_internalFormat; - GLuint m_externalFormat; + uint m_internalFormat; + uint m_externalFormat; int m_atlas_transient_image_threshold; diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp new file mode 100644 index 0000000000..6afe591dca --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultimagenode_p.h" +#include <private/qsgnode_p.h> + +QT_BEGIN_NAMESPACE + +QSGDefaultImageNode::QSGDefaultImageNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_texCoordMode(QSGDefaultImageNode::NoTransform) + , m_isAtlasTexture(false) + , m_ownsTexture(false) +{ + setGeometry(&m_geometry); + setMaterial(&m_material); + setOpaqueMaterial(&m_opaque_material); + m_material.setMipmapFiltering(QSGTexture::None); + m_opaque_material.setMipmapFiltering(QSGTexture::None); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("image")); +#endif +} + +QSGDefaultImageNode::~QSGDefaultImageNode() +{ + if (m_ownsTexture) + delete m_material.texture(); +} + +void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_opaque_material.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGDefaultImageNode::filtering() const +{ + return m_material.filtering(); +} + +void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.mipmapFiltering() == filtering) + return; + + m_material.setMipmapFiltering(filtering); + m_opaque_material.setMipmapFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QSGDefaultImageNode::mipmapFiltering() const +{ + return m_material.mipmapFiltering(); +} + +void QSGDefaultImageNode::setRect(const QRectF &r) +{ + if (m_rect == r) + return; + + m_rect = r; + rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGDefaultImageNode::rect() const +{ + return m_rect; +} + +void QSGDefaultImageNode::setSourceRect(const QRectF &r) +{ + if (m_sourceRect == r) + return; + + m_sourceRect = r; + rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyGeometry); +} + +QRectF QSGDefaultImageNode::sourceRect() const +{ + return m_sourceRect; +} + +void QSGDefaultImageNode::setTexture(QSGTexture *texture) +{ + Q_ASSERT(texture); + if (m_ownsTexture) + delete m_material.texture(); + m_material.setTexture(texture); + m_opaque_material.setTexture(texture); + rebuildGeometry(&m_geometry, texture, m_rect, m_sourceRect, m_texCoordMode); + + DirtyState dirty = DirtyMaterial; + // It would be tempting to skip the extra bit here and instead use + // m_material.texture to get the old state, but that texture could + // have been deleted in the mean time. + bool wasAtlas = m_isAtlasTexture; + m_isAtlasTexture = texture->isAtlasTexture(); + if (wasAtlas || m_isAtlasTexture) + dirty |= DirtyGeometry; + markDirty(dirty); +} + +QSGTexture *QSGDefaultImageNode::texture() const +{ + return m_material.texture(); +} + +void QSGDefaultImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) +{ + if (m_texCoordMode == mode) + return; + m_texCoordMode = mode; + rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode); + markDirty(DirtyMaterial); +} + +QSGDefaultImageNode::TextureCoordinatesTransformMode QSGDefaultImageNode::textureCoordinatesTransform() const +{ + return m_texCoordMode; +} + +void QSGDefaultImageNode::setOwnsTexture(bool owns) +{ + m_ownsTexture = owns; +} + +bool QSGDefaultImageNode::ownsTexture() const +{ + return m_ownsTexture; +} + +void QSGDefaultImageNode::rebuildGeometry(QSGGeometry *g, + QSGTexture *texture, + const QRectF &rect, + QRectF sourceRect, + TextureCoordinatesTransformMode texCoordMode) +{ + if (!texture) + return; + + if (!sourceRect.width() || !sourceRect.height()) { + QSize ts = texture->textureSize(); + sourceRect = QRectF(0, 0, ts.width(), ts.height()); + } + + // Maybe transform the texture coordinates + if (texCoordMode.testFlag(QSGImageNode::MirrorHorizontally)) { + float tmp = sourceRect.left(); + sourceRect.setLeft(sourceRect.right()); + sourceRect.setRight(tmp); + } + if (texCoordMode.testFlag(QSGImageNode::MirrorVertically)) { + float tmp = sourceRect.top(); + sourceRect.setTop(sourceRect.bottom()); + sourceRect.setBottom(tmp); + } + + QSGGeometry::updateTexturedRectGeometry(g, rect, texture->convertToNormalizedSourceRect(sourceRect)); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h new file mode 100644 index 0000000000..eb6c487c18 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTIMAGENODE_P_H +#define QSGDEFAULTIMAGENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/private/qtquickglobal_p.h> +#include <QtQuick/qsgimagenode.h> +#include <QtQuick/qsggeometry.h> +#include <QtQuick/qsgtexturematerial.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode +{ +public: + QSGDefaultImageNode(); + ~QSGDefaultImageNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setSourceRect(const QRectF &r) override; + QRectF sourceRect() const override; + + void setTexture(QSGTexture *texture) override; + QSGTexture *texture() const override; + + void setFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering filtering() const override; + + void setMipmapFiltering(QSGTexture::Filtering filtering) override; + QSGTexture::Filtering mipmapFiltering() const override; + + void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override; + TextureCoordinatesTransformMode textureCoordinatesTransform() const override; + + void setOwnsTexture(bool owns) override; + bool ownsTexture() const override; + + static void rebuildGeometry(QSGGeometry *g, + QSGTexture *texture, + const QRectF &rect, + QRectF sourceRect, + TextureCoordinatesTransformMode texCoordMode); + +private: + QSGGeometry m_geometry; + QSGOpaqueTextureMaterial m_opaque_material; + QSGTextureMaterial m_material; + QRectF m_rect; + QRectF m_sourceRect; + TextureCoordinatesTransformMode m_texCoordMode; + uint m_isAtlasTexture : 1; + uint m_ownsTexture : 1; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTIMAGENODE_P_H diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp b/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp new file mode 100644 index 0000000000..e5a53a3617 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultninepatchnode_p.h" + +QT_BEGIN_NAMESPACE + +QSGDefaultNinePatchNode::QSGDefaultNinePatchNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) +{ + m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip); + setGeometry(&m_geometry); + setMaterial(&m_material); +} + +QSGDefaultNinePatchNode::~QSGDefaultNinePatchNode() +{ + delete m_material.texture(); +} + +void QSGDefaultNinePatchNode::setTexture(QSGTexture *texture) +{ + delete m_material.texture(); + m_material.setTexture(texture); +} + +void QSGDefaultNinePatchNode::setBounds(const QRectF &bounds) +{ + m_bounds = bounds; +} + +void QSGDefaultNinePatchNode::setDevicePixelRatio(qreal ratio) +{ + m_devicePixelRatio = ratio; +} + +void QSGDefaultNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) +{ + m_padding = QVector4D(left, top, right, bottom); +} + +void QSGDefaultNinePatchNode::update() +{ + rebuildGeometry(m_material.texture(), &m_geometry, m_padding, m_bounds, m_devicePixelRatio); + markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial); +} + +void QSGDefaultNinePatchNode::rebuildGeometry(QSGTexture *texture, QSGGeometry *geometry, const QVector4D &padding, + const QRectF &bounds, qreal dpr) +{ + if (padding.x() <= 0 && padding.y() <= 0 && padding.z() <= 0 && padding.w() <= 0) { + geometry->allocate(4, 0); + QSGGeometry::updateTexturedRectGeometry(geometry, bounds, texture->normalizedTextureSubRect()); + return; + } + + QRectF tc = texture->normalizedTextureSubRect(); + QSize ts = texture->textureSize(); + ts.setHeight(ts.height() / dpr); + ts.setWidth(ts.width() / dpr); + + qreal invtw = tc.width() / ts.width(); + qreal invth = tc.height() / ts.height(); + + struct Coord { qreal p; qreal t; }; + Coord cx[4] = { { bounds.left(), tc.left() }, + { bounds.left() + padding.x(), tc.left() + padding.x() * invtw }, + { bounds.right() - padding.z(), tc.right() - padding.z() * invtw }, + { bounds.right(), tc.right() } + }; + Coord cy[4] = { { bounds.top(), tc.top() }, + { bounds.top() + padding.y(), tc.top() + padding.y() * invth }, + { bounds.bottom() - padding.w(), tc.bottom() - padding.w() * invth }, + { bounds.bottom(), tc.bottom() } + }; + + geometry->allocate(16, 28); + QSGGeometry::TexturedPoint2D *v = geometry->vertexDataAsTexturedPoint2D(); + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + v->set(cx[x].p, cy[y].p, cx[x].t, cy[y].t); + ++v; + } + } + + quint16 *i = geometry->indexDataAsUShort(); + for (int r = 0; r < 3; ++r) { + if (r > 0) + *i++ = 4 * r; + for (int c = 0; c < 4; ++c) { + i[0] = 4 * r + c; + i[1] = 4 * r + c + 4; + i += 2; + } + if (r < 2) + *i++ = 4 * r + 3 + 4; + } +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h new file mode 100644 index 0000000000..675cf48f47 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTNINEPATCHNODE_P_H +#define QSGDEFAULTNINEPATCHNODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtquickglobal_p.h> +#include <QtQuick/qsgninepatchnode.h> +#include <QtQuick/qsggeometry.h> +#include <QtQuick/qsgtexturematerial.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGDefaultNinePatchNode : public QSGNinePatchNode +{ +public: + QSGDefaultNinePatchNode(); + ~QSGDefaultNinePatchNode(); + + void setTexture(QSGTexture *texture) override; + void setBounds(const QRectF &bounds) override; + void setDevicePixelRatio(qreal ratio) override; + void setPadding(qreal left, qreal top, qreal right, qreal bottom) override; + void update() override; + + static void rebuildGeometry(QSGTexture *texture, QSGGeometry *geometry, const QVector4D &padding, + const QRectF &bounds, qreal dpr); + +private: + QRectF m_bounds; + qreal m_devicePixelRatio; + QVector4D m_padding; + QSGGeometry m_geometry; + QSGTextureMaterial m_material; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTNINEPATCHNODE_P_H diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp index 16625b889b..389b9e0b4e 100644 --- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp @@ -41,6 +41,7 @@ #include <QtQuick/private/qquickpainteditem_p.h> +#include <QtQuick/private/qsgdefaultrendercontext_p.h> #include <QtQuick/private/qsgcontext_p.h> #include <private/qopenglextensions_p.h> #include <qopenglframebufferobject.h> @@ -96,7 +97,7 @@ QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item) , m_dirtyRenderTarget(false) , m_dirtyTexture(false) { - m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext(); + m_context = static_cast<QSGDefaultRenderContext *>(static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext()); setMaterial(&m_materialO); setOpaqueMaterial(&m_material); diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h index 7e23264100..7488f7878d 100644 --- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h +++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h @@ -63,6 +63,7 @@ QT_BEGIN_NAMESPACE class QOpenGLFramebufferObject; class QOpenGLPaintDevice; +class QSGDefaultRenderContext; class Q_QUICK_PRIVATE_EXPORT QSGPainterTexture : public QSGPlainTexture { @@ -127,7 +128,7 @@ private: void updateRenderTarget(); void updateFBOSize(); - QSGRenderContext *m_context; + QSGDefaultRenderContext *m_context; QQuickPaintedItem::RenderTarget m_preferredRenderTarget; QQuickPaintedItem::RenderTarget m_actualRenderTarget; diff --git a/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp new file mode 100644 index 0000000000..e1c8672add --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdefaultrectanglenode_p.h" +#include "qsgflatcolormaterial.h" + +QT_BEGIN_NAMESPACE + +// Unlike our predecessor, QSGSimpleRectNode, use QSGVertexColorMaterial +// instead of Flat in order to allow better batching in the renderer. + +QSGDefaultRectangleNode::QSGDefaultRectangleNode() + : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 4) +{ + QSGGeometry::updateColoredRectGeometry(&m_geometry, QRectF()); + setMaterial(&m_material); + setGeometry(&m_geometry); + setColor(QColor(255, 255, 255)); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("rectangle")); +#endif +} + +void QSGDefaultRectangleNode::setRect(const QRectF &rect) +{ + QSGGeometry::updateColoredRectGeometry(&m_geometry, rect); + markDirty(QSGNode::DirtyGeometry); +} + +QRectF QSGDefaultRectangleNode::rect() const +{ + const QSGGeometry::ColoredPoint2D *pts = m_geometry.vertexDataAsColoredPoint2D(); + return QRectF(pts[0].x, + pts[0].y, + pts[3].x - pts[0].x, + pts[3].y - pts[0].y); +} + +void QSGDefaultRectangleNode::setColor(const QColor &color) +{ + if (color != m_color) { + m_color = color; + QSGGeometry::ColoredPoint2D *pts = m_geometry.vertexDataAsColoredPoint2D(); + for (int i = 0; i < 4; ++i) { + pts[i].r = uchar(qRound(m_color.redF() * m_color.alphaF() * 255)); + pts[i].g = uchar(qRound(m_color.greenF() * m_color.alphaF() * 255)); + pts[i].b = uchar(qRound(m_color.blueF() * m_color.alphaF() * 255)); + pts[i].a = uchar(qRound(m_color.alphaF() * 255)); + } + markDirty(QSGNode::DirtyGeometry); + } +} + +QColor QSGDefaultRectangleNode::color() const +{ + return m_color; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h new file mode 100644 index 0000000000..965aa8dabb --- /dev/null +++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEFAULTRECTANGLENODE_P_H +#define QSGDEFAULTRECTANGLENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qcolor.h> +#include <QtQuick/qsgrectanglenode.h> +#include <QtQuick/qsgvertexcolormaterial.h> + +QT_BEGIN_NAMESPACE + +class QSGDefaultRectangleNode : public QSGRectangleNode +{ +public: + QSGDefaultRectangleNode(); + + void setRect(const QRectF &rect) override; + QRectF rect() const override; + + void setColor(const QColor &color) override; + QColor color() const override; + +private: + QSGVertexColorMaterial m_material; + QSGGeometry m_geometry; + QColor m_color; +}; + +QT_END_NAMESPACE + +#endif // QSGDEFAULTRECTANGLENODE_P_H diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp index 5eb6a6f593..65a6bcd52c 100644 --- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp +++ b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp @@ -40,7 +40,9 @@ #include "qsgdistancefieldutil_p.h" #include <private/qsgadaptationlayer_p.h> -#include <QtGui/private/qopenglengineshadersource_p.h> +#ifndef QT_NO_OPENGL +# include <QtGui/private/qopenglengineshadersource_p.h> +#endif #include <QtQuick/private/qsgcontext_p.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index 1ef98d222d..ad1fcfa470 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -44,6 +44,11 @@ #include <private/qsgrenderer_p.h> #include <private/qsgtexture_p.h> +#ifndef QT_NO_OPENGL +# include <QtGui/QOpenGLContext> +# include <private/qsgdefaultrendercontext_p.h> +#endif + QT_BEGIN_NAMESPACE @@ -83,7 +88,7 @@ QT_BEGIN_NAMESPACE QSGEnginePrivate::QSGEnginePrivate() : sgContext(QSGContext::createDefaultContext()) - , sgRenderContext(new QSGRenderContext(sgContext.data())) + , sgRenderContext(sgContext.data()->createRenderContext()) { } @@ -111,15 +116,19 @@ QSGEngine::~QSGEngine() void QSGEngine::initialize(QOpenGLContext *context) { Q_D(QSGEngine); - if (QOpenGLContext::currentContext() != context) { +#ifndef QT_NO_OPENGL + if (context && QOpenGLContext::currentContext() != context) { qWarning("WARNING: The context must be current before calling QSGEngine::initialize."); return; } - - if (!d->sgRenderContext->isValid()) { - d->sgRenderContext->setAttachToGLContext(false); +#endif + if (d->sgRenderContext && !d->sgRenderContext->isValid()) { + d->sgRenderContext->setAttachToGraphicsContext(false); d->sgRenderContext->initialize(context); - connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate); +#ifndef QT_NO_OPENGL + if (context) + connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate); +#endif } } @@ -198,4 +207,59 @@ QSGTexture *QSGEngine::createTextureFromId(uint id, const QSize &size, CreateTex return 0; } +/*! + Returns the current renderer interface if there is one. Otherwise null is returned. + + \sa QSGRenderNode, QSGRendererInterface + \since 5.8 + */ +QSGRendererInterface *QSGEngine::rendererInterface() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() + ? d->sgRenderContext->sceneGraphContext()->rendererInterface(d->sgRenderContext.data()) + : nullptr; +} + +/*! + Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null. + + This is cross-backend alternative to constructing a QSGSimpleRectNode directly. + + \since 5.8 + \sa QSGRectangleNode + */ +QSGRectangleNode *QSGEngine::createRectangleNode() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createRectangleNode() : nullptr; +} + +/*! + Creates a simple image node. When the scenegraph is not initialized, the return value is null. + + This is cross-backend alternative to constructing a QSGSimpleTextureNode directly. + + \since 5.8 + \sa QSGImageNode + */ + +QSGImageNode *QSGEngine::createImageNode() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createImageNode() : nullptr; +} + +/*! + Creates a nine patch node. When the scenegraph is not initialized, the return value is null. + + \since 5.8 + */ + +QSGNinePatchNode *QSGEngine::createNinePatchNode() const +{ + Q_D(const QSGEngine); + return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createNinePatchNode() : nullptr; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h index 89af71fb47..3c8b61852e 100644 --- a/src/quick/scenegraph/util/qsgengine.h +++ b/src/quick/scenegraph/util/qsgengine.h @@ -49,6 +49,10 @@ class QOpenGLContext; class QSGAbstractRenderer; class QSGEnginePrivate; class QSGTexture; +class QSGRendererInterface; +class QSGRectangleNode; +class QSGImageNode; +class QSGNinePatchNode; class Q_QUICK_EXPORT QSGEngine : public QObject { @@ -72,6 +76,10 @@ public: QSGAbstractRenderer *createRenderer() const; QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options = CreateTextureOption()) const; QSGTexture *createTextureFromId(uint id, const QSize &size, CreateTextureOptions options = CreateTextureOption()) const; + QSGRendererInterface *rendererInterface() const; + QSGRectangleNode *createRectangleNode() const; + QSGImageNode *createImageNode() const; + QSGNinePatchNode *createNinePatchNode() const; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp index 836b5759a2..2ce27275cd 100644 --- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp @@ -39,8 +39,9 @@ #include "qsgflatcolormaterial.h" #include <private/qsgmaterialshader_p.h> - -#include <qopenglshaderprogram.h> +#ifndef QT_NO_OPENGL +# include <qopenglshaderprogram.h> +#endif QT_BEGIN_NAMESPACE @@ -56,9 +57,10 @@ public: private: virtual void initialize(); - +#ifndef QT_NO_OPENGL int m_matrix_id; int m_color_id; +#endif }; QSGMaterialType FlatColorMaterialShader::type; @@ -66,14 +68,16 @@ QSGMaterialType FlatColorMaterialShader::type; FlatColorMaterialShader::FlatColorMaterialShader() : QSGMaterialShader(*new QSGMaterialShaderPrivate) { +#ifndef QT_NO_OPENGL setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/flatcolor.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/flatcolor.frag")); +#endif } void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { +#ifndef QT_NO_OPENGL Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); - QSGFlatColorMaterial *oldMaterial = static_cast<QSGFlatColorMaterial *>(oldEffect); QSGFlatColorMaterial *newMaterial = static_cast<QSGFlatColorMaterial *>(newEffect); @@ -90,6 +94,11 @@ void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial if (state.isMatrixDirty()) program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +#else + Q_UNUSED(state) + Q_UNUSED(newEffect) + Q_UNUSED(oldEffect) +#endif } char const *const *FlatColorMaterialShader::attributeNames() const @@ -100,8 +109,10 @@ char const *const *FlatColorMaterialShader::attributeNames() const void FlatColorMaterialShader::initialize() { +#ifndef QT_NO_OPENGL m_matrix_id = program()->uniformLocation("matrix"); m_color_id = program()->uniformLocation("color"); +#endif } @@ -115,6 +126,9 @@ void FlatColorMaterialShader::initialize() \inmodule QtQuick \ingroup qtquick-scenegraph-materials + \warning This utility class is only functional when running with the OpenGL + backend of the Qt Quick scenegraph. + The flat color material will fill every pixel in a geometry using a solid color. The color can contain transparency. diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp new file mode 100644 index 0000000000..a78bfc1c66 --- /dev/null +++ b/src/quick/scenegraph/util/qsgimagenode.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgimagenode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGImageNode + \brief The QSGImageNode class is provided for convenience to easily draw + textured content using the QML scene graph. + + \inmodule QtQuick + \since 5.8 + + \warning The image node class must have a texture before being + added to the scene graph to be rendered. + */ + +/*! + \fn void QSGImageNode::setRect(const QRectF &rect) + + Sets the target rect of this image node to \a rect. + */ + +/*! + \fn void QSGImageNode::setRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangle of this image node to begin at (\a x, \a y) and have + width \a w and height \a h. + */ + +/*! + \fn QRectF QSGImageNode::rect() const + + Returns the target rect of this image node. + */ + +/*! + \fn void QSGImageNode::setSourceRect(const QRectF &rect) + + Sets the source rect of this image node to \a rect. + */ + +/*! + \fn void QSGImageNode::setSourceRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangle of this image node to show its texture from (\a x, \a y) and + have width \a w and height \a h relatively to the QSGTexture::textureSize. + */ + +/*! + \fn QRectF QSGImageNode::sourceRect() const + + Returns the source rect of this image node. + */ + +/*! + \fn void QSGImageNode::setTexture(QSGTexture *texture) + + Sets the texture of this image node to \a texture. + + Use setOwnsTexture() to set whether the node should take + ownership of the texture. By default, the node does not + take ownership. + + \warning An image node must have a texture before being added to the + scenegraph to be rendered. + */ + +/*! + \fn QSGTexture *QSGImageNode::texture() const + + Returns the texture for this image node. + */ + +/*! + \fn void QSGImageNode::setFiltering(QSGTexture::Filtering filtering) + + Sets the filtering to be used for this image node to \a filtering. + + For smooth scaling, use QSGTexture::Linear. For normal scaling, use + QSGTexture::Nearest. + */ + +/*! + \fn QSGTexture::Filtering QSGImageNode::filtering() const + + Returns the filtering for this image node. + */ + +/*! + \fn void QSGImageNode::setMipmapFiltering(QSGTexture::Filtering filtering) + + Sets the mipmap filtering to be used for this image node to \a filtering. + + For smooth scaling between mip maps, use QSGTexture::Linear. + For normal scaling, use QSGTexture::Nearest. + */ + +/*! + \fn QSGTexture::Filtering QSGImageNode::mipmapFiltering() const + + Returns the mipmap filtering for this image node. + */ + +/*! + \enum QSGImageNode::TextureCoordinatesTransformFlag + + The TextureCoordinatesTransformFlag enum is used to specify the mode used + to generate texture coordinates for a textured quad. + + \value NoTransform Texture coordinates are oriented with window coordinates + i.e. with origin at top-left. + + \value MirrorHorizontally Texture coordinates are inverted in the horizontal axis with + respect to window coordinates + + \value MirrorVertically Texture coordinates are inverted in the vertical axis with + respect to window coordinates + */ + +/*! + \fn void QSGImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) + + Sets the method used to generate texture coordinates to \a mode. This can + be used to obtain correct orientation of the texture. This is commonly + needed when using a third-party OpenGL library to render to texture as + OpenGL has an inverted y-axis relative to Qt Quick. + */ + +/*! + \fn QSGImageNode::TextureCoordinatesTransformMode textureCoordinatesTransform() const + + Returns the mode used to generate texture coordinates for this node. + */ + +/*! + \fn void QSGImageNode::setOwnsTexture(bool owns) + + Sets whether the node takes ownership of the texture to \a owns. + + By default, the node does not take ownership of the texture. + */ + +/*! + \fn bool QSGImageNode::ownsTexture() const + + \return \c true if the node takes ownership of the texture; otherwise \c false. + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgimagenode.h b/src/quick/scenegraph/util/qsgimagenode.h new file mode 100644 index 0000000000..7eab42c4e6 --- /dev/null +++ b/src/quick/scenegraph/util/qsgimagenode.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGIMAGENODE_H +#define QSGIMAGENODE_H + +#include <QtQuick/qsgnode.h> +#include <QtQuick/qsgtexture.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGImageNode : public QSGGeometryNode +{ +public: + virtual ~QSGImageNode() { } + + virtual void setRect(const QRectF &rect) = 0; + inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } + virtual QRectF rect() const = 0; + + virtual void setSourceRect(const QRectF &r) = 0; + inline void setSourceRect(qreal x, qreal y, qreal w, qreal h) { setSourceRect(QRectF(x, y, w, h)); } + virtual QRectF sourceRect() const = 0; + + virtual void setTexture(QSGTexture *texture) = 0; + virtual QSGTexture *texture() const = 0; + + virtual void setFiltering(QSGTexture::Filtering filtering) = 0; + virtual QSGTexture::Filtering filtering() const = 0; + + virtual void setMipmapFiltering(QSGTexture::Filtering filtering) = 0; + virtual QSGTexture::Filtering mipmapFiltering() const = 0; + + enum TextureCoordinatesTransformFlag { + NoTransform = 0x00, + MirrorHorizontally = 0x01, + MirrorVertically = 0x02 + }; + Q_DECLARE_FLAGS(TextureCoordinatesTransformMode, TextureCoordinatesTransformFlag) + + virtual void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) = 0; + virtual TextureCoordinatesTransformMode textureCoordinatesTransform() const = 0; + + virtual void setOwnsTexture(bool owns) = 0; + virtual bool ownsTexture() const = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGImageNode::TextureCoordinatesTransformMode) + +QT_END_NAMESPACE + +#endif // QSGIMAGENODE_H diff --git a/src/quick/scenegraph/util/qsgninepatchnode.cpp b/src/quick/scenegraph/util/qsgninepatchnode.cpp new file mode 100644 index 0000000000..9c167ca76f --- /dev/null +++ b/src/quick/scenegraph/util/qsgninepatchnode.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgninepatchnode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGNinePatchNode + \inmodule QtQuick + \since 5.8 + \internal + */ + +/*! + \fn void QSGNinePatchNode::setTexture(QSGTexture *texture) + \internal + */ + +/*! + \fn void QSGNinePatchNode::setBounds(const QRectF &bounds) + \internal + */ + +/*! + \fn void QSGNinePatchNode::setDevicePixelRatio(qreal ratio) + \internal + */ + +/*! + \fn void QSGNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom) + \internal + */ + + +/*! + \fn void QSGNinePatchNode::update() + \internal + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgninepatchnode.h b/src/quick/scenegraph/util/qsgninepatchnode.h new file mode 100644 index 0000000000..8677a432ba --- /dev/null +++ b/src/quick/scenegraph/util/qsgninepatchnode.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGNINEPATCHNODE_H +#define QSGNINEPATCHNODE_H + +#include <QtQuick/qsgnode.h> +#include <QtQuick/qsgtexture.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGNinePatchNode : public QSGGeometryNode +{ +public: + virtual ~QSGNinePatchNode() { } + + virtual void setTexture(QSGTexture *texture) = 0; + virtual void setBounds(const QRectF &bounds) = 0; + virtual void setDevicePixelRatio(qreal ratio) = 0; + virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom) = 0; + virtual void update() = 0; +}; + +QT_END_NAMESPACE + +#endif // QSGNINEPATCHNODE_H diff --git a/src/quick/scenegraph/util/qsgrectanglenode.cpp b/src/quick/scenegraph/util/qsgrectanglenode.cpp new file mode 100644 index 0000000000..38c1f16a63 --- /dev/null +++ b/src/quick/scenegraph/util/qsgrectanglenode.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrectanglenode.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QSGRectangleNode + + \brief The QSGRectangleNode class is a convenience class for drawing + solid filled rectangles using scenegraph. + \inmodule QtQuick + \since 5.8 + */ + +/*! + \fn void QSGRectangleNode::setRect(const QRectF &rect) + + Sets the rectangle of this rect node to \a rect. + */ + +/*! + \fn void QSGRectangleNode::setRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangle of this rect node to begin at (\a x, \a y) and have + width \a w and height \a h. + */ + +/*! + \fn QRectF QSGRectangleNode::rect() const + + Returns the rectangle that this rect node covers. + */ + +/*! + \fn void QSGRectangleNode::setColor(const QColor &color) + + Sets the color of this rectangle to \a color. The default color will be + white. + */ + +/*! + \fn QColor QSGRectangleNode::color() const + + Returns the color of this rectangle. + */ + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgrectanglenode.h b/src/quick/scenegraph/util/qsgrectanglenode.h new file mode 100644 index 0000000000..8e0da1d9c7 --- /dev/null +++ b/src/quick/scenegraph/util/qsgrectanglenode.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGRECTANGLENODE_H +#define QSGRECTANGLENODE_H + +#include <QtQuick/qsgnode.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QSGRectangleNode : public QSGGeometryNode +{ +public: + virtual ~QSGRectangleNode() { } + + virtual void setRect(const QRectF &rect) = 0; + inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } + virtual QRectF rect() const = 0; + + virtual void setColor(const QColor &color) = 0; + virtual QColor color() const = 0; +}; + +QT_END_NAMESPACE + +#endif // QSGRECTANGLENODE_H diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp index 923179071b..7214a255df 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.cpp +++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp @@ -41,11 +41,14 @@ \class QSGSimpleMaterialShader \brief The QSGSimpleMaterialShader class provides a convenient way of - building custom materials for the scene graph. + building custom OpenGL-based materials for the scene graph. \inmodule QtQuick \ingroup qtquick-scenegraph-materials + \warning This utility class is only functional when running with the OpenGL + backend of the Qt Quick scenegraph. + Where the QSGMaterial and QSGMaterialShader API requires a bit of boilerplate code to create a functioning material, the QSGSimpleMaterialShader tries to hide some of this through the use diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h index 9ebfc1f8d2..d07a68e850 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.h +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -50,7 +50,7 @@ class QSGSimpleMaterialShader : public QSGMaterialShader public: void initialize() { QSGMaterialShader::initialize(); - +#ifndef QT_NO_OPENGL m_id_matrix = program()->uniformLocation(uniformMatrixName()); if (m_id_matrix < 0) { qFatal("QSGSimpleMaterialShader does not implement 'uniform highp mat4 %s;' in its vertex shader", @@ -67,7 +67,7 @@ public: } else { m_id_opacity = -1; } - +#endif resolveUniforms(); } @@ -197,11 +197,14 @@ QSGMaterialType QSGSimpleMaterial<State>::m_type; template <typename State> Q_INLINE_TEMPLATE void QSGSimpleMaterialShader<State>::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) { +#ifndef QT_NO_OPENGL if (state.isMatrixDirty()) program()->setUniformValue(m_id_matrix, state.combinedMatrix()); if (state.isOpacityDirty() && m_id_opacity >= 0) program()->setUniformValue(m_id_opacity, state.opacity()); - +#else + Q_UNUSED(state) +#endif State *ns = static_cast<QSGSimpleMaterial<State> *>(newMaterial)->state(); State *old = 0; if (oldMaterial) diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp index 3f6b8b0eec..28b177be84 100644 --- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp +++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp @@ -49,6 +49,12 @@ QT_BEGIN_NAMESPACE solid filled rectangles using scenegraph. \inmodule QtQuick + \warning This utility class is only functional when running with the OpenGL + or software backends of the Qt Quick scenegraph. For a proper cross-platform + alternative prefer using QSGRectangleNode via + QQuickWindow::createRectangleNode() or QSGEngine::createRectangleNode(). + + \deprecated */ diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index 1208a6bc72..6ce37de7cb 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -97,6 +97,13 @@ static void qsgsimpletexturenode_update(QSGGeometry *g, \warning The simple texture node class must have a texture before being added to the scene graph to be rendered. + + \warning This utility class is only functional when running with the OpenGL + or software backends of the Qt Quick scenegraph. For a proper cross-platform + alternative prefer using QSGImageNode via + QQuickWindow::createImageNode() or QSGEngine::createImageNode(). + + \deprecated */ /*! diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 9b9c77dce4..4cf339aeb8 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qsgtexture_p.h" -#include <qopenglfunctions.h> #include <QtQuick/private/qsgcontext_p.h> #include <qthread.h> #include <qmath.h> @@ -46,9 +45,12 @@ #include <private/qqmlglobal_p.h> #include <QtGui/qguiapplication.h> #include <QtGui/qpa/qplatformnativeinterface.h> -#include <QtGui/qopenglcontext.h> -#include <QtGui/qopenglfunctions.h> - +#ifndef QT_NO_OPENGL +# include <qopenglfunctions.h> +# include <QtGui/qopenglcontext.h> +# include <QtGui/qopenglfunctions.h> +# include <private/qsgdefaultrendercontext_p.h> +#endif #include <private/qsgmaterialshader_p.h> #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(__UCLIBC__) @@ -68,7 +70,9 @@ #include <QHash> #endif +#ifndef QT_NO_OPENGL static QElapsedTimer qsg_renderer_timer; +#endif #ifndef QT_NO_DEBUG static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); @@ -82,11 +86,13 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK" QT_BEGIN_NAMESPACE +#ifndef QT_NO_OPENGL inline static bool isPowerOfTwo(int x) { // Assumption: x >= 1 return x == (x & -x); } +#endif QSGTexturePrivate::QSGTexturePrivate() : wrapChanged(false) @@ -278,6 +284,7 @@ Q_GLOBAL_STATIC(QMutex, qsg_valid_texture_mutex) bool qsg_safeguard_texture(QSGTexture *texture) { +#ifndef QT_NO_OPENGL QMutexLocker locker(qsg_valid_texture_mutex()); if (!qsg_valid_texture_set()->contains(texture)) { qWarning() << "Invalid texture accessed:" << (void *) texture; @@ -285,6 +292,9 @@ bool qsg_safeguard_texture(QSGTexture *texture) QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, 0); return false; } +#else + Q_UNUSED(texture) +#endif return true; } #endif @@ -517,6 +527,7 @@ QSGTexture::WrapMode QSGTexture::verticalWrapMode() const */ void QSGTexture::updateBindOptions(bool force) { +#ifndef QT_NO_OPENGL Q_D(QSGTexture); QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); force |= isAtlasTexture(); @@ -551,6 +562,9 @@ void QSGTexture::updateBindOptions(bool force) funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); d->wrapChanged = false; } +#else + Q_UNUSED(force) +#endif } QSGPlainTexture::QSGPlainTexture() @@ -568,8 +582,10 @@ QSGPlainTexture::QSGPlainTexture() QSGPlainTexture::~QSGPlainTexture() { +#ifndef QT_NO_OPENGL if (m_texture_id && m_owns_texture && QOpenGLContext::currentContext()) QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id); +#endif } void qsg_swizzleBGRAToRGBA(QImage *image) @@ -601,8 +617,10 @@ int QSGPlainTexture::textureId() const // or ~QSGPlainTexture so just keep it minimal here. return 0; } else if (m_texture_id == 0){ +#ifndef QT_NO_OPENGL // Generate a texture id for use later and return it. QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id); +#endif return m_texture_id; } } @@ -611,8 +629,10 @@ int QSGPlainTexture::textureId() const void QSGPlainTexture::setTextureId(int id) { +#ifndef QT_NO_OPENGL if (m_texture_id && m_owns_texture) QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id); +#endif m_texture_id = id; m_dirty_texture = false; @@ -623,6 +643,7 @@ void QSGPlainTexture::setTextureId(int id) void QSGPlainTexture::bind() { +#ifndef QT_NO_OPENGL QOpenGLContext *context = QOpenGLContext::currentContext(); QOpenGLFunctions *funcs = context->functions(); if (!m_dirty_texture) { @@ -684,7 +705,7 @@ void QSGPlainTexture::bind() // based on QSGTexture::textureSize which is updated after this, so that // should be ok. int max; - if (QSGRenderContext *rc = QSGRenderContext::from(context)) + if (auto rc = QSGDefaultRenderContext::from(context)) max = rc->maxTextureSize(); else funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); @@ -789,6 +810,7 @@ void QSGPlainTexture::bind() m_dirty_bind_options = false; if (!m_retain_image) m_image = QImage(); +#endif } diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h index 5c358aecc3..a0d7eb41e3 100644 --- a/src/quick/scenegraph/util/qsgtexture_p.h +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -53,9 +53,9 @@ #include <QtQuick/qtquickglobal.h> #include <private/qobject_p.h> - -#include <QtGui/qopengl.h> - +#ifndef QT_NO_OPENGL +# include <QtGui/qopengl.h> +#endif #include "qsgtexture.h" #include <QtQuick/private/qsgcontext_p.h> @@ -110,7 +110,7 @@ public: protected: QImage m_image; - GLuint m_texture_id; + uint m_texture_id; QSize m_texture_size; QRectF m_texture_rect; diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index 66e87a77a8..119828bc81 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -39,25 +39,30 @@ #include "qsgtexturematerial_p.h" #include "qsgtexture_p.h" - -#include <QtGui/qopenglshaderprogram.h> -#include <QtGui/qopenglfunctions.h> +#ifndef QT_NO_OPENGL +# include <QtGui/qopenglshaderprogram.h> +# include <QtGui/qopenglfunctions.h> +#endif QT_BEGIN_NAMESPACE +#ifndef QT_NO_OPENGL inline static bool isPowerOfTwo(int x) { // Assumption: x >= 1 return x == (x & -x); } +#endif QSGMaterialType QSGOpaqueTextureMaterialShader::type; QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader() : QSGMaterialShader() { +#ifndef QT_NO_OPENGL setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.frag")); +#endif } char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const @@ -68,7 +73,9 @@ char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const void QSGOpaqueTextureMaterialShader::initialize() { +#ifndef QT_NO_OPENGL m_matrix_id = program()->uniformLocation("qt_Matrix"); +#endif } void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) @@ -88,6 +95,7 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa t->setHorizontalWrapMode(tx->horizontalWrapMode()); t->setVerticalWrapMode(tx->verticalWrapMode()); +#ifndef QT_NO_OPENGL bool npotSupported = const_cast<QOpenGLContext *>(state.context()) ->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); if (!npotSupported) { @@ -98,16 +106,19 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa t->setVerticalWrapMode(QSGTexture::ClampToEdge); } } - +#else + Q_UNUSED(state) +#endif t->setMipmapFiltering(tx->mipmapFiltering()); if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId()) t->bind(); else t->updateBindOptions(); - +#ifndef QT_NO_OPENGL if (state.isMatrixDirty()) program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +#endif } @@ -118,6 +129,9 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa \inmodule QtQuick \ingroup qtquick-scenegraph-materials + \warning This utility class is only functional when running with the OpenGL + backend of the Qt Quick scenegraph. + The opaque textured material will fill every pixel in a geometry with the supplied texture. The material does not respect the opacity of the QSGMaterialShader::RenderState, so opacity nodes in the parent chain @@ -312,6 +326,9 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const \inmodule QtQuick \ingroup qtquick-scenegraph-materials + \warning This utility class is only functional when running with the OpenGL + backend of the Qt Quick scenegraph. + The textured material will fill every pixel in a geometry with the supplied texture. @@ -362,22 +379,27 @@ QSGMaterialShader *QSGTextureMaterial::createShader() const QSGTextureMaterialShader::QSGTextureMaterialShader() : QSGOpaqueTextureMaterialShader() { +#ifndef QT_NO_OPENGL setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag")); +#endif } void QSGTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); +#ifndef QT_NO_OPENGL if (state.isOpacityDirty()) program()->setUniformValue(m_opacity_id, state.opacity()); - +#endif QSGOpaqueTextureMaterialShader::updateState(state, newEffect, oldEffect); } void QSGTextureMaterialShader::initialize() { QSGOpaqueTextureMaterialShader::initialize(); +#ifndef QT_NO_OPENGL m_opacity_id = program()->uniformLocation("opacity"); +#endif } QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp index 8e86b3906f..847ec289d8 100644 --- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp @@ -38,9 +38,9 @@ ****************************************************************************/ #include "qsgvertexcolormaterial.h" - -#include <qopenglshaderprogram.h> - +#ifndef QT_NO_OPENGL +# include <qopenglshaderprogram.h> +#endif QT_BEGIN_NAMESPACE class QSGVertexColorMaterialShader : public QSGMaterialShader @@ -55,9 +55,10 @@ public: private: virtual void initialize(); - +#ifndef QT_NO_OPENGL int m_matrix_id; int m_opacity_id; +#endif }; QSGMaterialType QSGVertexColorMaterialShader::type; @@ -65,17 +66,23 @@ QSGMaterialType QSGVertexColorMaterialShader::type; QSGVertexColorMaterialShader::QSGVertexColorMaterialShader() : QSGMaterialShader() { +#ifndef QT_NO_OPENGL setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/vertexcolor.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/vertexcolor.frag")); +#endif } void QSGVertexColorMaterialShader::updateState(const RenderState &state, QSGMaterial * /*newEffect*/, QSGMaterial *) { +#ifndef QT_NO_OPENGL if (state.isOpacityDirty()) program()->setUniformValue(m_opacity_id, state.opacity()); if (state.isMatrixDirty()) program()->setUniformValue(m_matrix_id, state.combinedMatrix()); +#else + Q_UNUSED(state) +#endif } char const *const *QSGVertexColorMaterialShader::attributeNames() const @@ -86,8 +93,10 @@ char const *const *QSGVertexColorMaterialShader::attributeNames() const void QSGVertexColorMaterialShader::initialize() { +#ifndef QT_NO_OPENGL m_matrix_id = program()->uniformLocation("matrix"); m_opacity_id = program()->uniformLocation("opacity"); +#endif } @@ -99,6 +108,9 @@ void QSGVertexColorMaterialShader::initialize() \inmodule QtQuick \ingroup qtquick-scenegraph-materials + \warning This utility class is only functional when running with the OpenGL + backend of the Qt Quick scenegraph. + The vertex color material will give each vertex in a geometry a color. Pixels between vertices will be linearly interpolated. The colors can contain transparency. |